sudoModal.spec.tsx 4.2 KB

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