accountSecurityDetails.spec.tsx 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. import {initializeOrg} from 'sentry-test/initializeOrg';
  2. import {
  3. render,
  4. renderGlobalModal,
  5. screen,
  6. userEvent,
  7. } from 'sentry-test/reactTestingLibrary';
  8. import AccountSecurityDetails from 'sentry/views/settings/account/accountSecurity/accountSecurityDetails';
  9. import AccountSecurityWrapper from 'sentry/views/settings/account/accountSecurity/accountSecurityWrapper';
  10. const ENDPOINT = '/users/me/authenticators/';
  11. const ACCOUNT_EMAILS_ENDPOINT = '/users/me/emails/';
  12. const ORG_ENDPOINT = '/organizations/';
  13. describe('AccountSecurityDetails', function () {
  14. beforeEach(() => {
  15. MockApiClient.clearMockResponses();
  16. });
  17. describe('Totp', function () {
  18. beforeEach(function () {
  19. MockApiClient.addMockResponse({
  20. url: ENDPOINT,
  21. body: TestStubs.AllAuthenticators(),
  22. });
  23. MockApiClient.addMockResponse({
  24. url: ORG_ENDPOINT,
  25. body: TestStubs.Organizations(),
  26. });
  27. MockApiClient.addMockResponse({
  28. url: `${ENDPOINT}15/`,
  29. body: TestStubs.Authenticators().Totp(),
  30. });
  31. MockApiClient.addMockResponse({
  32. url: ACCOUNT_EMAILS_ENDPOINT,
  33. body: TestStubs.AccountEmails(),
  34. });
  35. });
  36. it('has enrolled circle indicator', function () {
  37. const params = {
  38. authId: '15',
  39. };
  40. const {routerProps, routerContext} = initializeOrg({
  41. router: {
  42. params,
  43. },
  44. });
  45. render(
  46. <AccountSecurityWrapper {...routerProps}>
  47. <AccountSecurityDetails
  48. {...routerProps}
  49. onRegenerateBackupCodes={jest.fn()}
  50. deleteDisabled={false}
  51. />
  52. </AccountSecurityWrapper>,
  53. {context: routerContext}
  54. );
  55. expect(screen.getByTestId('auth-status-enabled')).toBeInTheDocument();
  56. // has created and last used dates
  57. expect(screen.getByText('Created at')).toBeInTheDocument();
  58. expect(screen.getByText('Last used')).toBeInTheDocument();
  59. });
  60. it('can remove method', async function () {
  61. const deleteMock = MockApiClient.addMockResponse({
  62. url: `${ENDPOINT}15/`,
  63. method: 'DELETE',
  64. });
  65. const params = {
  66. authId: '15',
  67. };
  68. const {routerProps, routerContext} = initializeOrg({
  69. router: {
  70. params,
  71. },
  72. });
  73. render(
  74. <AccountSecurityWrapper {...routerProps}>
  75. <AccountSecurityDetails
  76. {...routerProps}
  77. onRegenerateBackupCodes={jest.fn()}
  78. deleteDisabled={false}
  79. />
  80. </AccountSecurityWrapper>,
  81. {context: routerContext}
  82. );
  83. await userEvent.click(screen.getByRole('button', {name: 'Remove'}));
  84. renderGlobalModal();
  85. await userEvent.click(await screen.findByRole('button', {name: 'Confirm'}));
  86. expect(deleteMock).toHaveBeenCalled();
  87. });
  88. it('can remove one of multiple 2fa methods when org requires 2fa', async function () {
  89. MockApiClient.addMockResponse({
  90. url: ORG_ENDPOINT,
  91. body: TestStubs.Organizations({require2FA: true}),
  92. });
  93. const deleteMock = MockApiClient.addMockResponse({
  94. url: `${ENDPOINT}15/`,
  95. method: 'DELETE',
  96. });
  97. const params = {
  98. authId: '15',
  99. };
  100. const {routerProps, routerContext} = initializeOrg({
  101. router: {
  102. params,
  103. },
  104. });
  105. render(
  106. <AccountSecurityWrapper {...routerProps}>
  107. <AccountSecurityDetails
  108. {...routerProps}
  109. onRegenerateBackupCodes={jest.fn()}
  110. deleteDisabled={false}
  111. />
  112. </AccountSecurityWrapper>,
  113. {context: routerContext}
  114. );
  115. await userEvent.click(screen.getByRole('button', {name: 'Remove'}));
  116. renderGlobalModal();
  117. await userEvent.click(await screen.findByRole('button', {name: 'Confirm'}));
  118. expect(deleteMock).toHaveBeenCalled();
  119. });
  120. it('can not remove last 2fa method when org requires 2fa', function () {
  121. MockApiClient.addMockResponse({
  122. url: ORG_ENDPOINT,
  123. body: TestStubs.Organizations({require2FA: true}),
  124. });
  125. MockApiClient.addMockResponse({
  126. url: ENDPOINT,
  127. body: [TestStubs.Authenticators().Totp()],
  128. });
  129. const params = {
  130. authId: '15',
  131. };
  132. const {routerContext, routerProps} = initializeOrg({
  133. router: {
  134. params,
  135. },
  136. });
  137. render(
  138. <AccountSecurityWrapper {...routerProps}>
  139. <AccountSecurityDetails
  140. {...routerProps}
  141. onRegenerateBackupCodes={jest.fn()}
  142. deleteDisabled={false}
  143. />
  144. </AccountSecurityWrapper>,
  145. {context: routerContext}
  146. );
  147. expect(screen.getByRole('button', {name: 'Remove'})).toBeDisabled();
  148. });
  149. });
  150. describe('Recovery', function () {
  151. beforeEach(function () {
  152. MockApiClient.addMockResponse({
  153. url: ENDPOINT,
  154. body: TestStubs.AllAuthenticators(),
  155. });
  156. MockApiClient.addMockResponse({
  157. url: ORG_ENDPOINT,
  158. body: TestStubs.Organizations(),
  159. });
  160. MockApiClient.addMockResponse({
  161. url: `${ENDPOINT}16/`,
  162. body: TestStubs.Authenticators().Recovery(),
  163. });
  164. MockApiClient.addMockResponse({
  165. url: ACCOUNT_EMAILS_ENDPOINT,
  166. body: TestStubs.AccountEmails(),
  167. });
  168. });
  169. it('has enrolled circle indicator', function () {
  170. const params = {
  171. authId: '16',
  172. };
  173. const {routerProps, routerContext} = initializeOrg({
  174. router: {
  175. params,
  176. },
  177. });
  178. render(
  179. <AccountSecurityWrapper {...routerProps}>
  180. <AccountSecurityDetails
  181. {...routerProps}
  182. onRegenerateBackupCodes={jest.fn()}
  183. deleteDisabled={false}
  184. />
  185. </AccountSecurityWrapper>,
  186. {context: routerContext}
  187. );
  188. // does not have remove button
  189. expect(screen.queryByRole('button', {name: 'Remove'})).not.toBeInTheDocument();
  190. });
  191. it('regenerates codes', async function () {
  192. const deleteMock = MockApiClient.addMockResponse({
  193. url: `${ENDPOINT}16/`,
  194. method: 'PUT',
  195. });
  196. const params = {
  197. authId: '16',
  198. };
  199. const {routerProps, routerContext} = initializeOrg({
  200. router: {
  201. params,
  202. },
  203. });
  204. render(
  205. <AccountSecurityWrapper {...routerProps}>
  206. <AccountSecurityDetails
  207. {...routerProps}
  208. onRegenerateBackupCodes={jest.fn()}
  209. deleteDisabled={false}
  210. />
  211. </AccountSecurityWrapper>,
  212. {context: routerContext}
  213. );
  214. await userEvent.click(screen.getByRole('button', {name: 'Regenerate Codes'}));
  215. renderGlobalModal();
  216. expect(
  217. await screen.findByText(
  218. 'Are you sure you want to regenerate recovery codes? Your old codes will no longer work.'
  219. )
  220. ).toBeInTheDocument();
  221. await userEvent.click(screen.getByRole('button', {name: 'Confirm'}));
  222. expect(deleteMock).toHaveBeenCalled();
  223. });
  224. it('has copy, print and download buttons', function () {
  225. const params = {
  226. authId: '16',
  227. };
  228. const {routerProps, routerContext} = initializeOrg({
  229. router: {
  230. params,
  231. },
  232. });
  233. Object.defineProperty(document, 'queryCommandSupported', {
  234. value: () => true,
  235. });
  236. render(
  237. <AccountSecurityWrapper {...routerProps}>
  238. <AccountSecurityDetails
  239. {...routerProps}
  240. onRegenerateBackupCodes={jest.fn()}
  241. deleteDisabled={false}
  242. />
  243. </AccountSecurityWrapper>,
  244. {context: routerContext}
  245. );
  246. expect(screen.getByRole('button', {name: 'print'})).toBeInTheDocument();
  247. expect(screen.getByRole('button', {name: 'download'})).toHaveAttribute(
  248. 'href',
  249. 'data:text/plain;charset=utf-8,ABCD-1234 \nEFGH-5678'
  250. );
  251. expect(screen.getByTestId('frame')).toBeInTheDocument();
  252. expect(screen.getByRole('button', {name: 'Copy'})).toBeInTheDocument();
  253. });
  254. });
  255. });