sudoModal.spec.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {initializeOrg} from 'sentry-test/initializeOrg';
  3. import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  4. import ConfigStore from 'sentry/stores/configStore';
  5. import App from 'sentry/views/app';
  6. describe('Sudo Modal', function () {
  7. const setHasPasswordAuth = hasPasswordAuth =>
  8. ConfigStore.set('user', {...ConfigStore.get('user'), hasPasswordAuth});
  9. beforeEach(function () {
  10. window.__initialData = {
  11. ...window.__initialData,
  12. links: {
  13. organizationUrl: 'https://albertos-apples.sentry.io',
  14. regionUrl: 'https://albertos-apples.sentry.io',
  15. sentryUrl: 'https://sentry.io',
  16. },
  17. };
  18. MockApiClient.clearMockResponses();
  19. MockApiClient.addMockResponse({
  20. url: '/assistant/',
  21. body: [],
  22. });
  23. MockApiClient.addMockResponse({
  24. url: '/organizations/',
  25. body: [OrganizationFixture()],
  26. });
  27. MockApiClient.addMockResponse({
  28. url: '/organizations/org-slug/',
  29. method: 'DELETE',
  30. statusCode: 401,
  31. body: {
  32. detail: {
  33. code: 'sudo-required',
  34. username: 'test@test.com',
  35. },
  36. },
  37. });
  38. MockApiClient.addMockResponse({
  39. url: '/authenticators/',
  40. body: [],
  41. });
  42. });
  43. it('can delete an org with sudo flow', async function () {
  44. const {routerProps} = initializeOrg({router: {params: {}}});
  45. setHasPasswordAuth(true);
  46. render(
  47. <App {...routerProps}>
  48. <div>placeholder content</div>
  49. </App>
  50. );
  51. const successCb = jest.fn();
  52. const errorCb = jest.fn();
  53. // No Modal
  54. expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
  55. // Should return w/ `sudoRequired`
  56. new MockApiClient().request('/organizations/org-slug/', {
  57. method: 'DELETE',
  58. success: successCb,
  59. error: errorCb,
  60. });
  61. // Should have Modal + input
  62. expect(await screen.findByRole('dialog')).toBeInTheDocument();
  63. // Original callbacks should not have been called
  64. expect(successCb).not.toHaveBeenCalled();
  65. expect(errorCb).not.toHaveBeenCalled();
  66. // Clear mocks and allow DELETE
  67. MockApiClient.clearMockResponses();
  68. const orgDeleteMock = MockApiClient.addMockResponse({
  69. url: '/organizations/org-slug/',
  70. method: 'DELETE',
  71. statusCode: 200,
  72. });
  73. const sudoMock = MockApiClient.addMockResponse({
  74. url: '/auth/',
  75. method: 'PUT',
  76. statusCode: 200,
  77. });
  78. expect(sudoMock).not.toHaveBeenCalled();
  79. // "Sudo" auth
  80. await userEvent.type(screen.getByRole('textbox', {name: 'Password'}), 'password');
  81. await userEvent.click(screen.getByRole('button', {name: 'Confirm Password'}));
  82. expect(sudoMock).toHaveBeenCalledWith(
  83. '/auth/',
  84. expect.objectContaining({
  85. method: 'PUT',
  86. data: {isSuperuserModal: false, password: 'password'},
  87. })
  88. );
  89. // Retry API request
  90. await waitFor(() => expect(successCb).toHaveBeenCalled());
  91. expect(orgDeleteMock).toHaveBeenCalledWith(
  92. '/organizations/org-slug/',
  93. expect.objectContaining({
  94. method: 'DELETE',
  95. })
  96. );
  97. // Sudo Modal should be closed
  98. await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument());
  99. });
  100. it('shows button to redirect if user does not have password auth', async function () {
  101. const {routerProps} = initializeOrg({router: {params: {}}});
  102. setHasPasswordAuth(false);
  103. render(
  104. <App {...routerProps}>
  105. <div>placeholder content</div>
  106. </App>
  107. );
  108. // No Modal
  109. expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
  110. // Should return w/ `sudoRequired` and trigger the the modal to open
  111. new MockApiClient().request('/organizations/org-slug/', {method: 'DELETE'});
  112. // Should have Modal + input
  113. expect(await screen.findByRole('dialog')).toBeInTheDocument();
  114. expect(screen.queryByLabelText('Password')).not.toBeInTheDocument();
  115. expect(screen.getByRole('button', {name: 'Continue'})).toHaveAttribute(
  116. 'href',
  117. '/auth/login/?next=http%3A%2F%2Flocalhost%2F'
  118. );
  119. });
  120. });