sudoModal.spec.jsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import {mountWithTheme} from 'sentry-test/enzyme';
  2. import {Client} from 'app/api';
  3. import ConfigStore from 'app/stores/configStore';
  4. import App from 'app/views/app';
  5. describe('Sudo Modal', function () {
  6. beforeEach(function () {
  7. Client.clearMockResponses();
  8. Client.addMockResponse({
  9. url: '/internal/health/',
  10. body: {
  11. problems: [],
  12. },
  13. });
  14. Client.addMockResponse({
  15. url: '/assistant/?v2',
  16. body: [],
  17. });
  18. Client.addMockResponse({
  19. url: '/organizations/',
  20. body: [TestStubs.Organization()],
  21. });
  22. Client.addMockResponse({
  23. url: '/organizations/org-slug/',
  24. method: 'DELETE',
  25. statusCode: 401,
  26. body: {
  27. detail: {
  28. code: 'sudo-required',
  29. username: 'test@test.com',
  30. },
  31. },
  32. });
  33. Client.addMockResponse({
  34. url: '/authenticators/',
  35. body: [],
  36. });
  37. });
  38. it('can delete an org with sudo flow', async function () {
  39. ConfigStore.set('user', {
  40. ...ConfigStore.get('user'),
  41. hasPasswordAuth: true,
  42. });
  43. const wrapper = mountWithTheme(
  44. <App>{<div>placeholder content</div>}</App>,
  45. TestStubs.routerContext()
  46. );
  47. const api = new Client();
  48. const successCb = jest.fn();
  49. const errorCb = jest.fn();
  50. // No Modal
  51. expect(wrapper.find('GlobalModal[visible=true]').exists()).toBe(false);
  52. // Should return w/ `sudoRequired`
  53. api.request('/organizations/org-slug/', {
  54. method: 'DELETE',
  55. success: successCb,
  56. error: errorCb,
  57. });
  58. await tick();
  59. await tick();
  60. wrapper.update();
  61. // Should have Modal + input
  62. expect(wrapper.find('Modal input')).toHaveLength(1);
  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. Client.clearMockResponses();
  68. const orgDeleteMock = Client.addMockResponse({
  69. url: '/organizations/org-slug/',
  70. method: 'DELETE',
  71. statusCode: 200,
  72. });
  73. const sudoMock = Client.addMockResponse({
  74. url: '/auth/',
  75. method: 'PUT',
  76. statusCode: 200,
  77. });
  78. expect(sudoMock).not.toHaveBeenCalled();
  79. // "Sudo" auth
  80. wrapper
  81. .find('Modal input[name="password"]')
  82. .simulate('change', {target: {value: 'password'}});
  83. wrapper.find('Modal form').simulate('submit');
  84. wrapper.find('Modal Button[type="submit"]').simulate('click');
  85. await tick();
  86. wrapper.update();
  87. expect(sudoMock).toHaveBeenCalledWith(
  88. '/auth/',
  89. expect.objectContaining({
  90. method: 'PUT',
  91. data: {
  92. password: 'password',
  93. },
  94. })
  95. );
  96. // Retry API request
  97. expect(successCb).toHaveBeenCalled();
  98. expect(orgDeleteMock).toHaveBeenCalledWith(
  99. '/organizations/org-slug/',
  100. expect.objectContaining({
  101. method: 'DELETE',
  102. })
  103. );
  104. await tick();
  105. wrapper.update();
  106. // Sudo Modal should be closed
  107. expect(wrapper.find('GlobalModal[visible=true]').exists()).toBe(false);
  108. });
  109. it('shows button to redirect if user does not have password auth', async function () {
  110. ConfigStore.set('user', {
  111. ...ConfigStore.get('user'),
  112. hasPasswordAuth: false,
  113. });
  114. const wrapper = mountWithTheme(
  115. <App>{<div>placeholder content</div>}</App>,
  116. TestStubs.routerContext()
  117. );
  118. const api = new Client();
  119. const successCb = jest.fn();
  120. const errorCb = jest.fn();
  121. // No Modal
  122. expect(wrapper.find('GlobalModal[visible=true]').exists()).toBe(false);
  123. // Should return w/ `sudoRequired`
  124. api.request('/organizations/org-slug/', {
  125. method: 'DELETE',
  126. success: successCb,
  127. error: errorCb,
  128. });
  129. await tick();
  130. await tick();
  131. wrapper.update();
  132. // Should have Modal + input
  133. expect(wrapper.find('Modal input')).toHaveLength(0);
  134. expect(wrapper.find('Button[href]').prop('href')).toMatch('/auth/login/?next=%2F');
  135. });
  136. });