relocation.spec.tsx 24 KB

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