sudoModal.spec.jsx 3.8 KB

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