AppDetailsContainer.test.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. import React from 'react';
  2. import { faker } from '@faker-js/faker';
  3. import { fireEvent, render, screen, waitFor } from '../../../../../../tests/test-utils';
  4. import { createAppEntity } from '../../../../mocks/fixtures/app.fixtures';
  5. import { getTRPCMock, getTRPCMockError } from '../../../../mocks/getTrpcMock';
  6. import { server } from '../../../../mocks/server';
  7. import { AppDetailsContainer } from './AppDetailsContainer';
  8. describe('Test: AppDetailsContainer', () => {
  9. describe('Test: UI', () => {
  10. it('should render', async () => {
  11. // Arrange
  12. const app = createAppEntity({});
  13. render(<AppDetailsContainer app={app} />);
  14. // Assert
  15. expect(screen.getByText(app.info.short_desc)).toBeInTheDocument();
  16. });
  17. it('should display update button when update is available', async () => {
  18. // Arrange
  19. const app = createAppEntity({ overrides: { version: 2, latestVersion: 3 } });
  20. render(<AppDetailsContainer app={app} />);
  21. // Assert
  22. expect(screen.getByRole('button', { name: 'Update' })).toBeInTheDocument();
  23. });
  24. it('should display install button when app is not installed', async () => {
  25. // Arrange
  26. const app = createAppEntity({ overrides: { status: 'missing' } });
  27. render(<AppDetailsContainer app={app} />);
  28. // Assert
  29. expect(screen.getByRole('button', { name: 'Install' })).toBeInTheDocument();
  30. });
  31. it('should display uninstall and start button when app is stopped', async () => {
  32. // Arrange
  33. const app = createAppEntity({ overrides: { status: 'stopped' } });
  34. render(<AppDetailsContainer app={app} />);
  35. // Assert
  36. expect(screen.getByRole('button', { name: 'Remove' })).toBeInTheDocument();
  37. expect(screen.getByRole('button', { name: 'Start' })).toBeInTheDocument();
  38. });
  39. it('should display stop, open and settings buttons when app is running', async () => {
  40. // Arrange
  41. const app = createAppEntity({ overrides: { status: 'running' } });
  42. render(<AppDetailsContainer app={app} />);
  43. // Assert
  44. expect(screen.getByRole('button', { name: 'Stop' })).toBeInTheDocument();
  45. expect(screen.getByRole('button', { name: 'Open' })).toBeInTheDocument();
  46. expect(screen.getByRole('button', { name: 'Settings' })).toBeInTheDocument();
  47. });
  48. it('should not display update button when update is not available', async () => {
  49. // Arrange
  50. const app = createAppEntity({ overrides: { version: 3 }, overridesInfo: { tipi_version: 3 } });
  51. render(<AppDetailsContainer app={app} />);
  52. // Assert
  53. expect(screen.queryByRole('button', { name: 'Update' })).not.toBeInTheDocument();
  54. });
  55. it('should not display open button when app has no_gui set to true', async () => {
  56. // Arrange
  57. const app = createAppEntity({ overridesInfo: { no_gui: true } });
  58. render(<AppDetailsContainer app={app} />);
  59. // Assert
  60. expect(screen.queryByRole('button', { name: 'Open' })).not.toBeInTheDocument();
  61. });
  62. });
  63. describe('Test: Open app', () => {
  64. it('should call window.open with the correct url when open button is clicked', async () => {
  65. // Arrange
  66. const app = createAppEntity({});
  67. const spy = jest.spyOn(window, 'open').mockImplementation(() => null);
  68. render(<AppDetailsContainer app={app} />);
  69. // Act
  70. const openButton = screen.getByRole('button', { name: 'Open' });
  71. openButton.click();
  72. // Assert
  73. expect(spy).toHaveBeenCalledWith(`http://localhost:${app.info.port}`, '_blank', 'noreferrer');
  74. });
  75. it('should open with https when app info has https set to true', async () => {
  76. // Arrange
  77. const app = createAppEntity({ overridesInfo: { https: true } });
  78. const spy = jest.spyOn(window, 'open').mockImplementation(() => null);
  79. render(<AppDetailsContainer app={app} />);
  80. // Act
  81. const openButton = screen.getByRole('button', { name: 'Open' });
  82. openButton.click();
  83. // Assert
  84. expect(spy).toHaveBeenCalledWith(`https://localhost:${app.info.port}`, '_blank', 'noreferrer');
  85. });
  86. });
  87. describe('Test: Install app', () => {
  88. it('should display toast success when install success', async () => {
  89. // Arrange
  90. const app = createAppEntity({ overrides: { status: 'missing' } });
  91. server.use(getTRPCMock({ path: ['app', 'installApp'], type: 'mutation', response: app }));
  92. render(<AppDetailsContainer app={app} />);
  93. const openModalButton = screen.getByRole('button', { name: 'Install' });
  94. fireEvent.click(openModalButton);
  95. // Act
  96. const installButton = screen.getByRole('button', { name: 'Install' });
  97. fireEvent.click(installButton);
  98. await waitFor(() => {
  99. expect(screen.getByText('App installed successfully')).toBeInTheDocument();
  100. });
  101. });
  102. it('should display a toast error when install mutation fails', async () => {
  103. // Arrange
  104. const error = faker.lorem.sentence();
  105. server.use(
  106. getTRPCMockError({
  107. path: ['app', 'installApp'],
  108. type: 'mutation',
  109. message: error,
  110. }),
  111. );
  112. const app = createAppEntity({ overrides: { status: 'missing' } });
  113. render(<AppDetailsContainer app={app} />);
  114. const openModalButton = screen.getByRole('button', { name: 'Install' });
  115. fireEvent.click(openModalButton);
  116. // Act
  117. const installButton = screen.getByRole('button', { name: 'Install' });
  118. fireEvent.click(installButton);
  119. await waitFor(() => {
  120. expect(screen.getByText(error)).toBeInTheDocument();
  121. });
  122. });
  123. });
  124. describe('Test: Update app', () => {
  125. it('should display toast success when update success', async () => {
  126. // Arrange
  127. const app = createAppEntity({ overrides: { version: 2, latestVersion: 3 } });
  128. server.use(getTRPCMock({ path: ['app', 'updateApp'], type: 'mutation', response: app }));
  129. render(<AppDetailsContainer app={app} />);
  130. const openModalButton = screen.getByRole('button', { name: 'Update' });
  131. fireEvent.click(openModalButton);
  132. // Act
  133. const modalUpdateButton = screen.getByRole('button', { name: 'Update' });
  134. modalUpdateButton.click();
  135. await waitFor(() => {
  136. expect(screen.getByText('App updated successfully')).toBeInTheDocument();
  137. });
  138. });
  139. it('should display a toast error when update mutation fails', async () => {
  140. // Arrange
  141. const error = faker.lorem.sentence();
  142. server.use(getTRPCMockError({ path: ['app', 'updateApp'], type: 'mutation', message: error }));
  143. const app = createAppEntity({ overrides: { version: 2, latestVersion: 3 }, overridesInfo: { tipi_version: 3 } });
  144. render(<AppDetailsContainer app={app} />);
  145. const openModalButton = screen.getByRole('button', { name: 'Update' });
  146. fireEvent.click(openModalButton);
  147. // Act
  148. const modalUpdateButton = screen.getByRole('button', { name: 'Update' });
  149. modalUpdateButton.click();
  150. // Assert
  151. await waitFor(() => {
  152. expect(screen.getByText(error)).toBeInTheDocument();
  153. });
  154. });
  155. });
  156. describe('Test: Uninstall app', () => {
  157. it('should display toast success when uninstall success', async () => {
  158. // Arrange
  159. const app = createAppEntity({ status: 'stopped' });
  160. server.use(getTRPCMock({ path: ['app', 'uninstallApp'], type: 'mutation', response: { id: app.id, config: {}, status: 'missing' } }));
  161. render(<AppDetailsContainer app={app} />);
  162. const openModalButton = screen.getByRole('button', { name: 'Remove' });
  163. fireEvent.click(openModalButton);
  164. // Act
  165. const modalUninstallButton = screen.getByRole('button', { name: 'Uninstall' });
  166. modalUninstallButton.click();
  167. // Assert
  168. await waitFor(() => {
  169. expect(screen.getByText('App uninstalled successfully')).toBeInTheDocument();
  170. });
  171. });
  172. it('should display a toast error when uninstall mutation fails', async () => {
  173. // Arrange
  174. const error = faker.lorem.sentence();
  175. server.use(getTRPCMockError({ path: ['app', 'uninstallApp'], type: 'mutation', message: error }));
  176. const app = createAppEntity({ status: 'stopped' });
  177. render(<AppDetailsContainer app={app} />);
  178. const openModalButton = screen.getByRole('button', { name: 'Remove' });
  179. fireEvent.click(openModalButton);
  180. // Act
  181. const modalUninstallButton = screen.getByRole('button', { name: 'Uninstall' });
  182. modalUninstallButton.click();
  183. // Assert
  184. await waitFor(() => {
  185. expect(screen.getByText(error)).toBeInTheDocument();
  186. });
  187. });
  188. });
  189. describe('Test: Start app', () => {
  190. it('should display toast success when start success', async () => {
  191. // Arrange
  192. const app = createAppEntity({ status: 'stopped' });
  193. server.use(getTRPCMock({ path: ['app', 'startApp'], type: 'mutation', response: app }));
  194. render(<AppDetailsContainer app={app} />);
  195. // Act
  196. const startButton = screen.getByRole('button', { name: 'Start' });
  197. startButton.click();
  198. // Assert
  199. await waitFor(() => {
  200. expect(screen.getByText('App started successfully')).toBeInTheDocument();
  201. });
  202. });
  203. it('should display a toast error when start mutation fails', async () => {
  204. // Arrange
  205. const error = faker.lorem.sentence();
  206. server.use(getTRPCMockError({ path: ['app', 'startApp'], type: 'mutation', message: error }));
  207. const app = createAppEntity({ status: 'stopped' });
  208. render(<AppDetailsContainer app={app} />);
  209. // Act
  210. const startButton = screen.getByRole('button', { name: 'Start' });
  211. startButton.click();
  212. // Assert
  213. await waitFor(() => {
  214. expect(screen.getByText(error)).toBeInTheDocument();
  215. });
  216. });
  217. });
  218. describe('Test: Stop app', () => {
  219. it('should display toast success when stop success', async () => {
  220. // Arrange
  221. const app = createAppEntity({ status: 'running' });
  222. server.use(getTRPCMock({ path: ['app', 'stopApp'], type: 'mutation', response: app }));
  223. render(<AppDetailsContainer app={app} />);
  224. const openModalButton = screen.getByRole('button', { name: 'Stop' });
  225. fireEvent.click(openModalButton);
  226. // Act
  227. const modalStopButton = screen.getByRole('button', { name: 'Stop' });
  228. modalStopButton.click();
  229. // Assert
  230. await waitFor(() => {
  231. expect(screen.getByText('App stopped successfully')).toBeInTheDocument();
  232. });
  233. });
  234. it('should display a toast error when stop mutation fails', async () => {
  235. // Arrange
  236. const error = faker.lorem.sentence();
  237. server.use(getTRPCMockError({ path: ['app', 'stopApp'], type: 'mutation', message: error }));
  238. const app = createAppEntity({ status: 'running' });
  239. render(<AppDetailsContainer app={app} />);
  240. const openModalButton = screen.getByRole('button', { name: 'Stop' });
  241. fireEvent.click(openModalButton);
  242. // Act
  243. const modalStopButton = screen.getByRole('button', { name: 'Stop' });
  244. modalStopButton.click();
  245. // Assert
  246. await waitFor(() => {
  247. expect(screen.getByText(error)).toBeInTheDocument();
  248. });
  249. });
  250. });
  251. describe('Test: Update app config', () => {
  252. it('should display toast success when update config success', async () => {
  253. // Arrange
  254. const app = createAppEntity({ status: 'running', overridesInfo: { exposable: true } });
  255. server.use(getTRPCMock({ path: ['app', 'updateAppConfig'], type: 'mutation', response: app }));
  256. render(<AppDetailsContainer app={app} />);
  257. const openModalButton = screen.getByRole('button', { name: 'Settings' });
  258. fireEvent.click(openModalButton);
  259. // Act
  260. const configButton = screen.getByRole('button', { name: 'Update' });
  261. configButton.click();
  262. // Assert
  263. await waitFor(() => {
  264. expect(screen.getByText('App config updated successfully. Restart the app to apply the changes')).toBeInTheDocument();
  265. });
  266. });
  267. it('should display a toast error when update config mutation fails', async () => {
  268. // Arrange
  269. const error = faker.lorem.sentence();
  270. server.use(getTRPCMockError({ path: ['app', 'updateAppConfig'], type: 'mutation', message: error }));
  271. const app = createAppEntity({ status: 'running', overridesInfo: { exposable: true } });
  272. render(<AppDetailsContainer app={app} />);
  273. const openModalButton = screen.getByRole('button', { name: 'Settings' });
  274. fireEvent.click(openModalButton);
  275. // Act
  276. const configButton = screen.getByRole('button', { name: 'Update' });
  277. configButton.click();
  278. // Assert
  279. await waitFor(() => {
  280. expect(screen.getByText(error)).toBeInTheDocument();
  281. });
  282. });
  283. });
  284. });