relocation.spec.tsx 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. import {initializeOrg} from 'sentry-test/initializeOrg';
  2. import {
  3. fireEvent,
  4. render,
  5. screen,
  6. userEvent,
  7. waitFor,
  8. } from 'sentry-test/reactTestingLibrary';
  9. import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
  10. import ConfigStore from 'sentry/stores/configStore';
  11. import type {InjectedRouter} from 'sentry/types/legacyReactRouter';
  12. import Relocation from 'sentry/views/relocation/relocation';
  13. jest.mock('sentry/actionCreators/indicator');
  14. const fakeOrgSlug = 'test-org';
  15. const fakePromoCode = 'free-hugs';
  16. const fakePublicKey = `FAKE-PK-ANY`;
  17. type FakeRegion = {
  18. name: string;
  19. publicKey: string;
  20. url: string;
  21. };
  22. const fakeRegions: {[key: string]: FakeRegion} = {
  23. Earth: {
  24. name: 'earth',
  25. url: 'https://earth.example.com',
  26. publicKey: 'FAKE-PK-EARTH',
  27. },
  28. Moon: {
  29. name: 'moon',
  30. url: 'https://moon.example.com',
  31. publicKey: 'FAKE-PK-MOON',
  32. },
  33. };
  34. describe('Relocation', function () {
  35. let router: InjectedRouter;
  36. let fetchExistingRelocations: jest.Mock;
  37. let fetchPublicKeys: jest.Mock;
  38. beforeEach(function () {
  39. MockApiClient.clearMockResponses();
  40. MockApiClient.asyncDelay = undefined;
  41. sessionStorage.clear();
  42. ConfigStore.set('regions', [
  43. {name: fakeRegions.Earth.name, url: fakeRegions.Earth.url},
  44. {name: fakeRegions.Moon.name, url: fakeRegions.Moon.url},
  45. ]);
  46. ConfigStore.set('relocationConfig', {
  47. selectableRegions: [fakeRegions.Earth.name, fakeRegions.Moon.name],
  48. });
  49. // For tests that don't care about the difference between our "earth" and "moon" regions, we can
  50. // re-use the same mock responses, with the same generic public key for both.
  51. fetchExistingRelocations = MockApiClient.addMockResponse({
  52. url: '/relocations/',
  53. body: [],
  54. });
  55. fetchPublicKeys = MockApiClient.addMockResponse({
  56. url: '/publickeys/relocations/',
  57. body: {
  58. public_key: fakePublicKey,
  59. },
  60. });
  61. // The tests fail because we have a "component update was not wrapped in act" error. It should
  62. // be safe to ignore this error, but we should remove the mock once we move to react testing
  63. // library.
  64. //
  65. // eslint-disable-next-line no-console
  66. jest.spyOn(console, 'error').mockImplementation(jest.fn());
  67. });
  68. afterEach(function () {
  69. MockApiClient.clearMockResponses();
  70. MockApiClient.asyncDelay = undefined;
  71. sessionStorage.clear();
  72. });
  73. function renderPage(step: string) {
  74. const routeParams = {
  75. step,
  76. };
  77. const {routerProps, organization, ...rest} = initializeOrg({
  78. router: {
  79. params: routeParams,
  80. },
  81. });
  82. router = rest.router;
  83. return render(<Relocation {...routerProps} />, {
  84. router,
  85. organization,
  86. });
  87. }
  88. async function waitForRenderSuccess(step: string) {
  89. renderPage(step);
  90. await waitFor(() => expect(screen.getByTestId(step)).toBeInTheDocument());
  91. }
  92. async function waitForRenderError(step: string) {
  93. renderPage(step);
  94. await waitFor(() => expect(screen.getByTestId('loading-error')).toBeInTheDocument());
  95. }
  96. describe('Get Started', function () {
  97. it('renders', async function () {
  98. await waitForRenderSuccess('get-started');
  99. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  100. expect(
  101. await screen.findByText('Basic information needed to get started')
  102. ).toBeInTheDocument();
  103. expect(
  104. await screen.findByText('Organization slugs being relocated')
  105. ).toBeInTheDocument();
  106. expect(await screen.findByText('Choose a datacenter location')).toBeInTheDocument();
  107. });
  108. it('redirects to `in-progress` page if user already has active relocation', async function () {
  109. MockApiClient.clearMockResponses();
  110. fetchExistingRelocations = MockApiClient.addMockResponse({
  111. url: '/relocations/',
  112. body: [
  113. {
  114. uuid: 'ccef828a-03d8-4dd0-918a-487ffecf8717',
  115. status: 'IN_PROGRESS',
  116. },
  117. ],
  118. });
  119. fetchPublicKeys = MockApiClient.addMockResponse({
  120. url: '/publickeys/relocations/',
  121. body: {
  122. public_key: fakePublicKey,
  123. },
  124. });
  125. await waitForRenderSuccess('get-started');
  126. await waitFor(() => expect(fetchExistingRelocations).toHaveBeenCalledTimes(2));
  127. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  128. expect(router.push).toHaveBeenCalledWith('/relocation/in-progress/');
  129. });
  130. it('should prevent user from going to the next step if no org slugs or region are entered', async function () {
  131. await waitForRenderSuccess('get-started');
  132. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  133. expect(screen.getByRole('button', {name: 'Continue'})).toBeDisabled();
  134. });
  135. it('should be allowed to go to next step if org slug is entered, region is selected, and promo code is entered', async function () {
  136. await waitForRenderSuccess('get-started');
  137. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  138. const fetchPromoCode = MockApiClient.addMockResponse({
  139. url: `/promocodes-external/${fakePromoCode}`,
  140. method: 'GET',
  141. statusCode: 200,
  142. });
  143. await userEvent.type(screen.getByLabelText('org-slugs'), fakeOrgSlug);
  144. await userEvent.type(screen.getByLabelText('region'), fakeRegions.Earth.name);
  145. await userEvent.click(screen.getByRole('menuitemradio'));
  146. expect(screen.getByRole('button', {name: 'Continue'})).toBeEnabled();
  147. await userEvent.click(screen.getByText('Got a promo code?', {exact: false}));
  148. await userEvent.type(screen.getByLabelText('promocode'), fakePromoCode);
  149. await userEvent.click(screen.getByRole('button', {name: 'Continue'}));
  150. await waitFor(() => expect(fetchPromoCode).toHaveBeenCalledTimes(1));
  151. expect(addErrorMessage).not.toHaveBeenCalled();
  152. });
  153. it('should persist form data across reloads', async function () {
  154. sessionStorage.setItem(
  155. 'relocationOnboarding',
  156. JSON.stringify({
  157. orgSlugs: fakeOrgSlug,
  158. promoCode: fakePromoCode,
  159. regionUrl: fakeRegions.Earth.url,
  160. })
  161. );
  162. await waitForRenderSuccess('get-started');
  163. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  164. expect(screen.getByLabelText('org-slugs')).toHaveValue(fakeOrgSlug);
  165. expect(screen.getByLabelText('promocode')).toHaveValue(fakePromoCode);
  166. });
  167. it('should not be allowed to go to next step if org slug is entered, region is selected, and promo code is invalid', async function () {
  168. await waitForRenderSuccess('get-started');
  169. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  170. const fetchPromoCode = MockApiClient.addMockResponse({
  171. url: `/promocodes-external/${fakePromoCode}`,
  172. method: 'GET',
  173. statusCode: 403,
  174. });
  175. await userEvent.type(screen.getByLabelText('org-slugs'), fakeOrgSlug);
  176. await userEvent.type(screen.getByLabelText('region'), fakeRegions.Earth.name);
  177. await userEvent.click(screen.getByRole('menuitemradio'));
  178. expect(screen.getByRole('button', {name: 'Continue'})).toBeEnabled();
  179. await userEvent.click(screen.getByText('Got a promo code?', {exact: false}));
  180. await userEvent.type(screen.getByLabelText('promocode'), fakePromoCode);
  181. expect(screen.getByRole('button', {name: 'Continue'})).toBeEnabled();
  182. await userEvent.click(screen.getByRole('button', {name: 'Continue'}));
  183. await waitFor(() => expect(fetchPromoCode).toHaveBeenCalledTimes(1));
  184. expect(addErrorMessage).toHaveBeenCalledWith(
  185. 'That promotional code has already been claimed, does not have enough remaining uses, is no longer valid, or never existed.'
  186. );
  187. });
  188. it('should show loading indicator and error message if existing relocation retrieval failed', async function () {
  189. MockApiClient.clearMockResponses();
  190. // Note: only one fails, but that is enough.
  191. const failingFetchExistingEarthRelocation = MockApiClient.addMockResponse({
  192. host: fakeRegions.Earth.url,
  193. url: `/relocations/`,
  194. statusCode: 400,
  195. });
  196. const successfulFetchExistingMoonRelocation = MockApiClient.addMockResponse({
  197. host: fakeRegions.Moon.url,
  198. url: '/relocations/',
  199. body: [],
  200. });
  201. fetchPublicKeys = MockApiClient.addMockResponse({
  202. url: '/publickeys/relocations/',
  203. body: {
  204. public_key: fakePublicKey,
  205. },
  206. });
  207. await waitForRenderError('get-started');
  208. await waitFor(() =>
  209. expect(failingFetchExistingEarthRelocation).toHaveBeenCalledTimes(1)
  210. );
  211. await waitFor(() =>
  212. expect(successfulFetchExistingMoonRelocation).toHaveBeenCalledTimes(1)
  213. );
  214. expect(fetchPublicKeys).toHaveBeenCalledTimes(2);
  215. expect(screen.queryByRole('button', {name: 'Continue'})).not.toBeInTheDocument();
  216. expect(screen.queryByLabelText('org-slugs')).not.toBeInTheDocument();
  217. expect(screen.getByRole('button', {name: 'Retry'})).toBeInTheDocument();
  218. const successfulFetchExistingEarthRelocation = MockApiClient.addMockResponse({
  219. host: fakeRegions.Earth.url,
  220. url: '/relocations/',
  221. body: [],
  222. });
  223. await userEvent.click(screen.getByRole('button', {name: 'Retry'}));
  224. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  225. await waitFor(() => expect(screen.getByTestId('get-started')).toBeInTheDocument());
  226. await waitFor(() =>
  227. expect(successfulFetchExistingEarthRelocation).toHaveBeenCalledTimes(1)
  228. );
  229. await waitFor(() =>
  230. expect(successfulFetchExistingMoonRelocation).toHaveBeenCalledTimes(2)
  231. );
  232. expect(screen.queryByLabelText('org-slugs')).toBeInTheDocument();
  233. expect(screen.queryByRole('button', {name: 'Continue'})).toBeInTheDocument();
  234. });
  235. });
  236. describe('Public Key', function () {
  237. beforeEach(function () {
  238. sessionStorage.setItem(
  239. 'relocationOnboarding',
  240. JSON.stringify({
  241. orgSlugs: fakeOrgSlug,
  242. promoCode: fakePromoCode,
  243. regionUrl: fakeRegions.Earth.url,
  244. })
  245. );
  246. });
  247. it('should show instructions if key retrieval was successful', async function () {
  248. await waitForRenderSuccess('public-key');
  249. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  250. expect(
  251. await screen.findByText("Save Sentry's public key to your machine")
  252. ).toBeInTheDocument();
  253. expect(screen.getByText('key.pub')).toBeInTheDocument();
  254. expect(screen.getByRole('button', {name: 'Continue'})).toBeInTheDocument();
  255. });
  256. it('should show loading indicator if key retrieval still in progress', function () {
  257. MockApiClient.asyncDelay = 1;
  258. renderPage('public-key');
  259. expect(screen.queryByRole('button', {name: 'Continue'})).not.toBeInTheDocument();
  260. expect(screen.queryByText('key.pub')).not.toBeInTheDocument();
  261. });
  262. it('should show loading indicator and error message if key retrieval failed', async function () {
  263. MockApiClient.clearMockResponses();
  264. fetchExistingRelocations = MockApiClient.addMockResponse({
  265. url: '/relocations/',
  266. body: [],
  267. });
  268. // Note: only one fails, but that is enough.
  269. const failingFetchEarthPublicKey = MockApiClient.addMockResponse({
  270. host: fakeRegions.Earth.url,
  271. url: `/publickeys/relocations/`,
  272. statusCode: 400,
  273. });
  274. const successfulFetchMoonPublicKey = MockApiClient.addMockResponse({
  275. host: fakeRegions.Moon.url,
  276. url: '/publickeys/relocations/',
  277. body: {
  278. public_key: fakeRegions.Moon.publicKey,
  279. },
  280. });
  281. await waitForRenderError('public-key');
  282. await waitFor(() => expect(failingFetchEarthPublicKey).toHaveBeenCalledTimes(1));
  283. await waitFor(() => expect(successfulFetchMoonPublicKey).toHaveBeenCalledTimes(1));
  284. expect(fetchExistingRelocations).toHaveBeenCalledTimes(2);
  285. expect(screen.queryByRole('button', {name: 'Continue'})).not.toBeInTheDocument();
  286. expect(screen.queryByText('key.pub')).not.toBeInTheDocument();
  287. expect(screen.getByRole('button', {name: 'Retry'})).toBeInTheDocument();
  288. const successfulFetchEarthPublicKey = MockApiClient.addMockResponse({
  289. host: fakeRegions.Earth.url,
  290. url: '/publickeys/relocations/',
  291. body: {
  292. public_key: fakeRegions.Earth.publicKey,
  293. },
  294. });
  295. await userEvent.click(screen.getByRole('button', {name: 'Retry'}));
  296. await waitFor(() => expect(successfulFetchEarthPublicKey).toHaveBeenCalledTimes(1));
  297. await waitFor(() => expect(successfulFetchMoonPublicKey).toHaveBeenCalledTimes(2));
  298. await waitFor(() => expect(screen.getByTestId('public-key')).toBeInTheDocument());
  299. expect(fetchExistingRelocations).toHaveBeenCalledTimes(2);
  300. expect(screen.queryByText('key.pub')).toBeInTheDocument();
  301. expect(screen.queryByRole('button', {name: 'Continue'})).toBeInTheDocument();
  302. });
  303. it('redirects to `get-started` page if expected local storage data is missing', async function () {
  304. sessionStorage.setItem(
  305. 'relocationOnboarding',
  306. JSON.stringify({
  307. orgSlugs: fakeOrgSlug,
  308. // regionUrl missing
  309. })
  310. );
  311. await waitForRenderSuccess('public-key');
  312. await waitFor(() => expect(fetchExistingRelocations).toHaveBeenCalledTimes(2));
  313. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  314. expect(router.push).toHaveBeenCalledWith('/relocation/get-started/');
  315. });
  316. });
  317. describe('Encrypt Backup', function () {
  318. it('renders', async function () {
  319. await waitForRenderSuccess('encrypt-backup');
  320. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  321. expect(
  322. await screen.findByText(
  323. 'Create an encrypted backup of your current self-hosted instance'
  324. )
  325. ).toBeInTheDocument();
  326. });
  327. it('redirects to `get-started` page if expected local storage data is missing', async function () {
  328. sessionStorage.setItem(
  329. 'relocationOnboarding',
  330. JSON.stringify({
  331. // orgSlugs missing
  332. regionUrl: fakeRegions.Earth.url,
  333. })
  334. );
  335. await waitForRenderSuccess('encrypt-backup');
  336. await waitFor(() => expect(fetchExistingRelocations).toHaveBeenCalledTimes(2));
  337. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  338. expect(router.push).toHaveBeenCalledWith('/relocation/get-started/');
  339. });
  340. });
  341. describe('Upload Backup', function () {
  342. beforeEach(function () {
  343. sessionStorage.setItem(
  344. 'relocationOnboarding',
  345. JSON.stringify({
  346. orgSlugs: fakeOrgSlug,
  347. promoCode: fakePromoCode,
  348. regionUrl: fakeRegions.Earth.url,
  349. })
  350. );
  351. });
  352. it('renders', async function () {
  353. await waitForRenderSuccess('upload-backup');
  354. expect(
  355. await screen.findByText('Upload Tarball to begin the relocation process')
  356. ).toBeInTheDocument();
  357. });
  358. it('accepts a file upload', async function () {
  359. await waitForRenderSuccess('upload-backup');
  360. await userEvent.upload(
  361. screen.getByLabelText('file-upload'),
  362. new File(['hello'], 'hello.tar', {type: 'file'})
  363. );
  364. expect(await screen.findByText('hello.tar')).toBeInTheDocument();
  365. expect(await screen.findByText('Start Relocation')).toBeInTheDocument();
  366. });
  367. it('accepts a file upload through drag and drop', async function () {
  368. await waitForRenderSuccess('upload-backup');
  369. fireEvent.drop(screen.getByLabelText('dropzone'), {
  370. dataTransfer: {files: [new File(['hello'], 'hello.tar', {type: 'file'})]},
  371. });
  372. expect(await screen.findByText('hello.tar')).toBeInTheDocument();
  373. expect(await screen.findByText('Start Relocation')).toBeInTheDocument();
  374. });
  375. it('correctly removes file and prompts for file upload', async function () {
  376. await waitForRenderSuccess('upload-backup');
  377. await userEvent.upload(
  378. screen.getByLabelText('file-upload'),
  379. new File(['hello'], 'hello.tar', {type: 'file'})
  380. );
  381. await userEvent.click(screen.getByText('Remove file'));
  382. expect(screen.queryByText('hello.tar')).not.toBeInTheDocument();
  383. expect(
  384. await screen.findByText('Upload Tarball to begin the relocation process')
  385. ).toBeInTheDocument();
  386. });
  387. it('starts relocation job if form data is available from previous steps', async function () {
  388. const postRelocation = MockApiClient.addMockResponse({
  389. url: `/relocations/`,
  390. method: 'POST',
  391. responseJSON: [
  392. {
  393. uuid: 'ccef828a-03d8-4dd0-918a-487ffecf8717',
  394. status: 'IN_PROGRESS',
  395. },
  396. ],
  397. });
  398. await waitForRenderSuccess('get-started');
  399. await userEvent.type(screen.getByLabelText('org-slugs'), fakeOrgSlug);
  400. await userEvent.type(screen.getByLabelText('region'), fakeRegions.Earth.name);
  401. await userEvent.click(screen.getByRole('menuitemradio'));
  402. await userEvent.click(screen.getByRole('button', {name: 'Continue'}));
  403. await waitForRenderSuccess('upload-backup');
  404. await userEvent.upload(
  405. screen.getByLabelText('file-upload'),
  406. new File(['hello'], 'hello.tar', {type: 'file'})
  407. );
  408. await userEvent.click(await screen.findByText('Start Relocation'));
  409. await waitFor(() =>
  410. expect(postRelocation).toHaveBeenCalledWith(
  411. '/relocations/',
  412. expect.objectContaining({host: fakeRegions.Earth.url, method: 'POST'})
  413. )
  414. );
  415. expect(addSuccessMessage).toHaveBeenCalledWith(
  416. "Your relocation has started - we'll email you with updates as soon as we have 'em!"
  417. );
  418. await waitForRenderSuccess('in-progress');
  419. });
  420. it('throws error if user already has an in-progress relocation job', async function () {
  421. const postRelocation = MockApiClient.addMockResponse({
  422. url: `/relocations/`,
  423. method: 'POST',
  424. statusCode: 409,
  425. });
  426. await waitForRenderSuccess('get-started');
  427. await userEvent.type(screen.getByLabelText('org-slugs'), fakeOrgSlug);
  428. await userEvent.type(screen.getByLabelText('region'), fakeRegions.Earth.name);
  429. await userEvent.click(screen.getByRole('menuitemradio'));
  430. await userEvent.click(screen.getByRole('button', {name: 'Continue'}));
  431. await waitForRenderSuccess('upload-backup');
  432. await userEvent.upload(
  433. screen.getByLabelText('file-upload'),
  434. new File(['hello'], 'hello.tar', {type: 'file'})
  435. );
  436. await userEvent.click(await screen.findByText('Start Relocation'));
  437. await waitFor(() => expect(postRelocation).toHaveBeenCalledTimes(1));
  438. expect(addErrorMessage).toHaveBeenCalledWith(
  439. 'You already have an in-progress relocation job.'
  440. );
  441. });
  442. it('throws error if daily limit of relocations has been reached', async function () {
  443. const postRelocation = MockApiClient.addMockResponse({
  444. url: `/relocations/`,
  445. method: 'POST',
  446. statusCode: 429,
  447. });
  448. await waitForRenderSuccess('get-started');
  449. await userEvent.type(screen.getByLabelText('org-slugs'), fakeOrgSlug);
  450. await userEvent.type(screen.getByLabelText('region'), fakeRegions.Earth.name);
  451. await userEvent.click(screen.getByRole('menuitemradio'));
  452. await userEvent.click(screen.getByRole('button', {name: 'Continue'}));
  453. await waitForRenderSuccess('upload-backup');
  454. await userEvent.upload(
  455. screen.getByLabelText('file-upload'),
  456. new File(['hello'], 'hello.tar', {type: 'file'})
  457. );
  458. await userEvent.click(await screen.findByText('Start Relocation'));
  459. await waitFor(() => expect(postRelocation).toHaveBeenCalledTimes(1));
  460. expect(addErrorMessage).toHaveBeenCalledWith(
  461. 'We have reached the daily limit of relocations - please try again tomorrow, or contact support.'
  462. );
  463. });
  464. it('throws error if user session has expired', async function () {
  465. const postRelocation = MockApiClient.addMockResponse({
  466. url: `/relocations/`,
  467. method: 'POST',
  468. statusCode: 401,
  469. });
  470. await waitForRenderSuccess('get-started');
  471. await userEvent.type(screen.getByLabelText('org-slugs'), fakeOrgSlug);
  472. await userEvent.type(screen.getByLabelText('region'), fakeRegions.Earth.name);
  473. await userEvent.click(screen.getByRole('menuitemradio'));
  474. await userEvent.click(screen.getByRole('button', {name: 'Continue'}));
  475. await waitForRenderSuccess('upload-backup');
  476. await userEvent.upload(
  477. screen.getByLabelText('file-upload'),
  478. new File(['hello'], 'hello.tar', {type: 'file'})
  479. );
  480. await userEvent.click(await screen.findByText('Start Relocation'));
  481. await waitFor(() => expect(postRelocation).toHaveBeenCalledTimes(1));
  482. expect(addErrorMessage).toHaveBeenCalledWith('Your session has expired.');
  483. });
  484. it('throws error for 500 error', async function () {
  485. const postRelocation = MockApiClient.addMockResponse({
  486. url: `/relocations/`,
  487. method: 'POST',
  488. statusCode: 500,
  489. });
  490. await waitForRenderSuccess('get-started');
  491. await userEvent.type(screen.getByLabelText('org-slugs'), fakeOrgSlug);
  492. await userEvent.type(screen.getByLabelText('region'), fakeRegions.Earth.name);
  493. await userEvent.click(screen.getByRole('menuitemradio'));
  494. await userEvent.click(screen.getByRole('button', {name: 'Continue'}));
  495. await waitForRenderSuccess('upload-backup');
  496. await userEvent.upload(
  497. screen.getByLabelText('file-upload'),
  498. new File(['hello'], 'hello.tar', {type: 'file'})
  499. );
  500. await userEvent.click(await screen.findByText('Start Relocation'));
  501. await waitFor(() => expect(postRelocation).toHaveBeenCalledTimes(1));
  502. expect(addErrorMessage).toHaveBeenCalledWith(
  503. 'An error has occurred while trying to start relocation job. Please contact support for further assistance.'
  504. );
  505. });
  506. it('redirects to `get-started` page if expected local storage data is missing', async function () {
  507. sessionStorage.setItem('relocationOnboarding', JSON.stringify({}));
  508. await waitForRenderSuccess('upload-backup');
  509. await waitFor(() => expect(fetchExistingRelocations).toHaveBeenCalledTimes(2));
  510. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  511. expect(router.push).toHaveBeenCalledWith('/relocation/get-started/');
  512. });
  513. });
  514. describe('In Progress', function () {
  515. it('renders', async function () {
  516. MockApiClient.clearMockResponses();
  517. fetchExistingRelocations = MockApiClient.addMockResponse({
  518. url: '/relocations/',
  519. body: [
  520. {
  521. uuid: 'ccef828a-03d8-4dd0-918a-487ffecf8717',
  522. status: 'IN_PROGRESS',
  523. },
  524. ],
  525. });
  526. fetchPublicKeys = MockApiClient.addMockResponse({
  527. url: '/publickeys/relocations/',
  528. body: {
  529. public_key: fakePublicKey,
  530. },
  531. });
  532. await waitForRenderSuccess('in-progress');
  533. expect(
  534. await screen.findByText('Your relocation is under way!')
  535. ).toBeInTheDocument();
  536. });
  537. it('redirects to `get-started` page if there is no existing relocation', async function () {
  538. await waitForRenderSuccess('in-progress');
  539. await waitFor(() => expect(fetchExistingRelocations).toHaveBeenCalledTimes(2));
  540. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  541. expect(router.push).toHaveBeenCalledWith('/relocation/get-started/');
  542. });
  543. it('redirects to `get-started` page if there is no active relocation', async function () {
  544. MockApiClient.clearMockResponses();
  545. fetchExistingRelocations = MockApiClient.addMockResponse({
  546. url: '/relocations/',
  547. body: [
  548. {
  549. uuid: 'ccef828a-03d8-4dd0-918a-487ffecf8717',
  550. status: 'SUCCESS',
  551. },
  552. ],
  553. });
  554. fetchPublicKeys = MockApiClient.addMockResponse({
  555. url: '/publickeys/relocations/',
  556. body: {
  557. public_key: fakePublicKey,
  558. },
  559. });
  560. await waitForRenderSuccess('in-progress');
  561. await waitFor(() => expect(fetchExistingRelocations).toHaveBeenCalledTimes(2));
  562. await waitFor(() => expect(fetchPublicKeys).toHaveBeenCalledTimes(2));
  563. expect(router.push).toHaveBeenCalledWith('/relocation/get-started/');
  564. });
  565. });
  566. });