@digitaldefiance/express-suite-react-components 5.0.2 → 5.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/package.json +5 -4
  2. package/src/auth/AdminRoute.d.ts +8 -0
  3. package/src/auth/AdminRoute.d.ts.map +1 -0
  4. package/src/auth/AdminRoute.js +28 -0
  5. package/src/auth/Private.d.ts +6 -0
  6. package/src/auth/Private.d.ts.map +1 -0
  7. package/src/auth/Private.js +14 -0
  8. package/src/auth/PrivateRoute.d.ts +8 -0
  9. package/src/auth/PrivateRoute.d.ts.map +1 -0
  10. package/src/auth/PrivateRoute.js +23 -0
  11. package/src/auth/UnAuth.d.ts +6 -0
  12. package/src/auth/UnAuth.d.ts.map +1 -0
  13. package/src/auth/UnAuth.js +14 -0
  14. package/src/auth/UnAuthRoute.d.ts +8 -0
  15. package/src/auth/UnAuthRoute.d.ts.map +1 -0
  16. package/src/auth/UnAuthRoute.js +22 -0
  17. package/src/auth/{index.ts → index.d.ts} +3 -2
  18. package/src/auth/index.d.ts.map +1 -0
  19. package/src/auth/index.js +12 -0
  20. package/src/components/ApiAccess.d.ts +16 -0
  21. package/src/components/ApiAccess.d.ts.map +1 -0
  22. package/src/components/ApiAccess.js +77 -0
  23. package/src/components/BackupCodeLoginForm.d.ts +43 -0
  24. package/src/components/BackupCodeLoginForm.d.ts.map +1 -0
  25. package/src/components/BackupCodeLoginForm.js +139 -0
  26. package/src/components/BackupCodesForm.d.ts +26 -0
  27. package/src/components/BackupCodesForm.d.ts.map +1 -0
  28. package/src/components/BackupCodesForm.js +120 -0
  29. package/src/components/CategoryMenu.d.ts +17 -0
  30. package/src/components/CategoryMenu.d.ts.map +1 -0
  31. package/src/components/CategoryMenu.js +65 -0
  32. package/src/components/ChangePasswordForm.d.ts +26 -0
  33. package/src/components/ChangePasswordForm.d.ts.map +1 -0
  34. package/src/components/ChangePasswordForm.js +78 -0
  35. package/src/components/ConfirmationDialog.d.ts +13 -0
  36. package/src/components/ConfirmationDialog.d.ts.map +1 -0
  37. package/src/components/ConfirmationDialog.js +10 -0
  38. package/src/components/CurrencyCodeSelector.d.ts +9 -0
  39. package/src/components/CurrencyCodeSelector.d.ts.map +1 -0
  40. package/src/components/CurrencyCodeSelector.js +31 -0
  41. package/src/components/CurrencyInput.d.ts +13 -0
  42. package/src/components/CurrencyInput.d.ts.map +1 -0
  43. package/src/components/CurrencyInput.js +22 -0
  44. package/src/components/DashboardPage.d.ts +8 -0
  45. package/src/components/DashboardPage.d.ts.map +1 -0
  46. package/src/components/DashboardPage.js +10 -0
  47. package/src/components/DropdownMenu.d.ts +13 -0
  48. package/src/components/DropdownMenu.d.ts.map +1 -0
  49. package/src/components/DropdownMenu.js +63 -0
  50. package/src/components/ExpirationSecondsSelector.d.ts +13 -0
  51. package/src/components/ExpirationSecondsSelector.d.ts.map +1 -0
  52. package/src/components/ExpirationSecondsSelector.js +32 -0
  53. package/src/components/FakeEmailAdminPanel.d.ts +23 -0
  54. package/src/components/FakeEmailAdminPanel.d.ts.map +1 -0
  55. package/src/components/FakeEmailAdminPanel.js +147 -0
  56. package/src/components/Flag.d.ts +20 -0
  57. package/src/components/Flag.d.ts.map +1 -0
  58. package/src/components/Flag.js +43 -0
  59. package/src/components/ForgotPasswordForm.d.ts +18 -0
  60. package/src/components/ForgotPasswordForm.d.ts.map +1 -0
  61. package/src/components/ForgotPasswordForm.js +61 -0
  62. package/src/components/LoginForm.d.ts +44 -0
  63. package/src/components/LoginForm.d.ts.map +1 -0
  64. package/src/components/LoginForm.js +123 -0
  65. package/src/components/LogoutPage.d.ts +8 -0
  66. package/src/components/LogoutPage.d.ts.map +1 -0
  67. package/src/components/LogoutPage.js +16 -0
  68. package/src/components/RegisterForm.d.ts +100 -0
  69. package/src/components/RegisterForm.d.ts.map +1 -0
  70. package/src/components/RegisterForm.js +306 -0
  71. package/src/components/ResetPasswordForm.d.ts +23 -0
  72. package/src/components/ResetPasswordForm.d.ts.map +1 -0
  73. package/src/components/ResetPasswordForm.js +78 -0
  74. package/src/components/SideMenu.d.ts +8 -0
  75. package/src/components/SideMenu.d.ts.map +1 -0
  76. package/src/components/SideMenu.js +25 -0
  77. package/src/components/SideMenuListItem.d.ts +13 -0
  78. package/src/components/SideMenuListItem.d.ts.map +1 -0
  79. package/src/components/SideMenuListItem.js +44 -0
  80. package/src/components/T.d.ts +7 -0
  81. package/src/components/T.d.ts.map +1 -0
  82. package/src/components/T.js +13 -0
  83. package/src/components/TDiv.d.ts +9 -0
  84. package/src/components/TDiv.d.ts.map +1 -0
  85. package/src/components/TDiv.js +14 -0
  86. package/src/components/TImgAlt.d.ts +10 -0
  87. package/src/components/TImgAlt.d.ts.map +1 -0
  88. package/src/components/TImgAlt.js +14 -0
  89. package/src/components/TopMenu.d.ts +50 -0
  90. package/src/components/TopMenu.d.ts.map +1 -0
  91. package/src/components/TopMenu.js +79 -0
  92. package/src/components/TotpSetupForm.d.ts +24 -0
  93. package/src/components/TotpSetupForm.d.ts.map +1 -0
  94. package/src/components/TotpSetupForm.js +82 -0
  95. package/src/components/TotpVerificationForm.d.ts +23 -0
  96. package/src/components/TotpVerificationForm.d.ts.map +1 -0
  97. package/src/components/TotpVerificationForm.js +66 -0
  98. package/src/components/TranslatedTitle.d.ts +8 -0
  99. package/src/components/TranslatedTitle.d.ts.map +1 -0
  100. package/src/components/TranslatedTitle.js +15 -0
  101. package/src/components/UserLanguageSelector.d.ts +4 -0
  102. package/src/components/UserLanguageSelector.d.ts.map +1 -0
  103. package/src/components/UserLanguageSelector.js +31 -0
  104. package/src/components/UserMenu.d.ts +4 -0
  105. package/src/components/UserMenu.d.ts.map +1 -0
  106. package/src/components/UserMenu.js +12 -0
  107. package/src/components/UserSettingsForm.d.ts +113 -0
  108. package/src/components/UserSettingsForm.d.ts.map +1 -0
  109. package/src/components/UserSettingsForm.js +269 -0
  110. package/src/components/VerifyEmailPage.d.ts +24 -0
  111. package/src/components/VerifyEmailPage.d.ts.map +1 -0
  112. package/src/components/VerifyEmailPage.js +81 -0
  113. package/src/components/{index.ts → index.d.ts} +1 -1
  114. package/src/components/index.d.ts.map +1 -0
  115. package/src/components/index.js +35 -0
  116. package/src/contexts/AuthProvider.d.ts +186 -0
  117. package/src/contexts/AuthProvider.d.ts.map +1 -0
  118. package/src/contexts/AuthProvider.js +515 -0
  119. package/src/contexts/I18nProvider.d.ts +27 -0
  120. package/src/contexts/I18nProvider.d.ts.map +1 -0
  121. package/src/contexts/I18nProvider.js +60 -0
  122. package/src/contexts/MenuContext.d.ts +28 -0
  123. package/src/contexts/MenuContext.d.ts.map +1 -0
  124. package/src/contexts/MenuContext.js +292 -0
  125. package/src/contexts/SuiteConfigProvider.d.ts +44 -0
  126. package/src/contexts/SuiteConfigProvider.d.ts.map +1 -0
  127. package/src/contexts/SuiteConfigProvider.js +43 -0
  128. package/src/contexts/ThemeProvider.d.ts +15 -0
  129. package/src/contexts/ThemeProvider.d.ts.map +1 -0
  130. package/src/contexts/ThemeProvider.js +36 -0
  131. package/src/contexts/{index.ts → index.d.ts} +1 -0
  132. package/src/contexts/index.d.ts.map +1 -0
  133. package/src/contexts/index.js +8 -0
  134. package/src/hooks/{index.ts → index.d.ts} +1 -0
  135. package/src/hooks/index.d.ts.map +1 -0
  136. package/src/hooks/index.js +9 -0
  137. package/src/hooks/useAuthenticatedApi.d.ts +34 -0
  138. package/src/hooks/useAuthenticatedApi.d.ts.map +1 -0
  139. package/src/hooks/useAuthenticatedApi.js +46 -0
  140. package/src/hooks/useBackupCodes.d.ts +15 -0
  141. package/src/hooks/useBackupCodes.d.ts.map +1 -0
  142. package/src/hooks/useBackupCodes.js +74 -0
  143. package/src/hooks/useEmailVerification.d.ts +10 -0
  144. package/src/hooks/useEmailVerification.d.ts.map +1 -0
  145. package/src/hooks/useEmailVerification.js +40 -0
  146. package/src/hooks/useExpiringValue.d.ts +14 -0
  147. package/src/hooks/useExpiringValue.d.ts.map +1 -0
  148. package/src/hooks/useExpiringValue.js +53 -0
  149. package/src/hooks/useLocalStorage.d.ts +2 -0
  150. package/src/hooks/useLocalStorage.d.ts.map +1 -0
  151. package/src/hooks/useLocalStorage.js +15 -0
  152. package/src/hooks/useUserSettings.d.ts +48 -0
  153. package/src/hooks/useUserSettings.d.ts.map +1 -0
  154. package/src/hooks/useUserSettings.js +171 -0
  155. package/src/{index.ts → index.d.ts} +1 -1
  156. package/src/index.d.ts.map +1 -0
  157. package/src/index.js +12 -0
  158. package/src/interfaces/IAppConfig.d.ts +8 -0
  159. package/src/interfaces/IAppConfig.d.ts.map +1 -0
  160. package/src/interfaces/IAppConfig.js +2 -0
  161. package/src/interfaces/ICategoryConfig.d.ts +41 -0
  162. package/src/interfaces/ICategoryConfig.d.ts.map +1 -0
  163. package/src/interfaces/ICategoryConfig.js +2 -0
  164. package/src/interfaces/IMenuConfig.d.ts +28 -0
  165. package/src/interfaces/IMenuConfig.d.ts.map +1 -0
  166. package/src/interfaces/IMenuConfig.js +2 -0
  167. package/src/interfaces/IMenuOption.d.ts +82 -0
  168. package/src/interfaces/IMenuOption.d.ts.map +1 -0
  169. package/src/interfaces/IMenuOption.js +2 -0
  170. package/src/interfaces/{index.ts → index.d.ts} +1 -0
  171. package/src/interfaces/index.d.ts.map +1 -0
  172. package/src/interfaces/index.js +7 -0
  173. package/src/services/__mocks__/authService.d.ts +21 -0
  174. package/src/services/__mocks__/authService.d.ts.map +1 -0
  175. package/src/services/__mocks__/authService.js +15 -0
  176. package/src/services/api.d.ts +3 -0
  177. package/src/services/api.d.ts.map +1 -0
  178. package/src/services/api.js +14 -0
  179. package/src/services/authService.d.ts +126 -0
  180. package/src/services/authService.d.ts.map +1 -0
  181. package/src/services/authService.js +471 -0
  182. package/src/services/authenticatedApi.d.ts +3 -0
  183. package/src/services/authenticatedApi.d.ts.map +1 -0
  184. package/src/services/authenticatedApi.js +18 -0
  185. package/src/services/index.d.ts +4 -0
  186. package/src/services/index.d.ts.map +1 -0
  187. package/src/services/index.js +6 -0
  188. package/src/types/MenuType.d.ts +11 -0
  189. package/src/types/MenuType.d.ts.map +1 -0
  190. package/src/types/MenuType.js +12 -0
  191. package/src/types/expirationSeconds.d.ts +3 -0
  192. package/src/types/expirationSeconds.d.ts.map +1 -0
  193. package/src/types/expirationSeconds.js +17 -0
  194. package/src/types/index.d.ts +2 -0
  195. package/src/types/index.d.ts.map +1 -0
  196. package/src/types/index.js +4 -0
  197. package/src/types/translation.d.ts +10 -0
  198. package/src/types/translation.d.ts.map +1 -0
  199. package/src/types/translation.js +9 -0
  200. package/src/wrappers/BackupCodeLoginWrapper.d.ts +8 -0
  201. package/src/wrappers/BackupCodeLoginWrapper.d.ts.map +1 -0
  202. package/src/wrappers/BackupCodeLoginWrapper.js +20 -0
  203. package/src/wrappers/BackupCodesWrapper.d.ts +7 -0
  204. package/src/wrappers/BackupCodesWrapper.d.ts.map +1 -0
  205. package/src/wrappers/BackupCodesWrapper.js +17 -0
  206. package/src/wrappers/ChangePasswordFormWrapper.d.ts +8 -0
  207. package/src/wrappers/ChangePasswordFormWrapper.d.ts.map +1 -0
  208. package/src/wrappers/ChangePasswordFormWrapper.js +21 -0
  209. package/src/wrappers/ForgotPasswordFormWrapper.d.ts +8 -0
  210. package/src/wrappers/ForgotPasswordFormWrapper.d.ts.map +1 -0
  211. package/src/wrappers/ForgotPasswordFormWrapper.js +29 -0
  212. package/src/wrappers/LoginFormWrapper.d.ts +21 -0
  213. package/src/wrappers/LoginFormWrapper.d.ts.map +1 -0
  214. package/src/wrappers/LoginFormWrapper.js +72 -0
  215. package/src/wrappers/LogoutPageWrapper.d.ts +9 -0
  216. package/src/wrappers/LogoutPageWrapper.d.ts.map +1 -0
  217. package/src/wrappers/LogoutPageWrapper.js +21 -0
  218. package/src/wrappers/RegisterFormWrapper.d.ts +20 -0
  219. package/src/wrappers/RegisterFormWrapper.d.ts.map +1 -0
  220. package/src/wrappers/RegisterFormWrapper.js +51 -0
  221. package/src/wrappers/ResetPasswordFormWrapper.d.ts +9 -0
  222. package/src/wrappers/ResetPasswordFormWrapper.d.ts.map +1 -0
  223. package/src/wrappers/ResetPasswordFormWrapper.js +34 -0
  224. package/src/wrappers/UserSettingsFormWrapper.d.ts +29 -0
  225. package/src/wrappers/UserSettingsFormWrapper.d.ts.map +1 -0
  226. package/src/wrappers/UserSettingsFormWrapper.js +95 -0
  227. package/src/wrappers/VerifyEmailPageWrapper.d.ts +8 -0
  228. package/src/wrappers/VerifyEmailPageWrapper.d.ts.map +1 -0
  229. package/src/wrappers/VerifyEmailPageWrapper.js +21 -0
  230. package/src/wrappers/{index.tsx → index.d.ts} +1 -10
  231. package/src/wrappers/index.d.ts.map +1 -0
  232. package/src/wrappers/index.js +24 -0
  233. package/LICENSE +0 -21
  234. package/src/auth/AdminRoute.tsx +0 -54
  235. package/src/auth/Private.tsx +0 -17
  236. package/src/auth/PrivateRoute.tsx +0 -28
  237. package/src/auth/UnAuth.tsx +0 -16
  238. package/src/auth/UnAuthRoute.tsx +0 -30
  239. package/src/components/ApiAccess.tsx +0 -175
  240. package/src/components/BackupCodeLoginForm.tsx +0 -489
  241. package/src/components/BackupCodesForm.tsx +0 -287
  242. package/src/components/CategoryMenu.tsx +0 -155
  243. package/src/components/ChangePasswordForm.tsx +0 -273
  244. package/src/components/ConfirmationDialog.tsx +0 -48
  245. package/src/components/CurrencyCodeSelector.tsx +0 -60
  246. package/src/components/CurrencyInput.tsx +0 -80
  247. package/src/components/DashboardPage.tsx +0 -24
  248. package/src/components/DropdownMenu.tsx +0 -105
  249. package/src/components/ExpirationSecondsSelector.tsx +0 -60
  250. package/src/components/FakeEmailAdminPanel.tsx +0 -374
  251. package/src/components/Flag.tsx +0 -52
  252. package/src/components/ForgotPasswordForm.tsx +0 -174
  253. package/src/components/LoginForm.tsx +0 -468
  254. package/src/components/LogoutPage.tsx +0 -21
  255. package/src/components/RegisterForm.tsx +0 -1169
  256. package/src/components/ResetPasswordForm.tsx +0 -247
  257. package/src/components/SideMenu.tsx +0 -46
  258. package/src/components/SideMenuListItem.tsx +0 -75
  259. package/src/components/T.tsx +0 -24
  260. package/src/components/TDiv.tsx +0 -26
  261. package/src/components/TImgAlt.tsx +0 -28
  262. package/src/components/TopMenu.tsx +0 -252
  263. package/src/components/TotpSetupForm.tsx +0 -188
  264. package/src/components/TotpVerificationForm.tsx +0 -148
  265. package/src/components/TranslatedTitle.tsx +0 -31
  266. package/src/components/UserLanguageSelector.tsx +0 -45
  267. package/src/components/UserMenu.tsx +0 -15
  268. package/src/components/UserSettingsForm.tsx +0 -904
  269. package/src/components/VerifyEmailPage.tsx +0 -222
  270. package/src/components/__tests__/RegisterForm.totp.spec.tsx +0 -377
  271. package/src/components/__tests__/TotpSetupForm.spec.tsx +0 -258
  272. package/src/components/__tests__/TotpVerificationForm.spec.tsx +0 -259
  273. package/src/components/__tests__/UserSettingsForm.totp.spec.tsx +0 -396
  274. package/src/contexts/AuthProvider.spec.tsx +0 -1284
  275. package/src/contexts/AuthProvider.tsx +0 -977
  276. package/src/contexts/I18nProvider.tsx +0 -164
  277. package/src/contexts/MenuContext.tsx +0 -426
  278. package/src/contexts/SuiteConfigProvider.tsx +0 -93
  279. package/src/contexts/ThemeProvider.tsx +0 -67
  280. package/src/hooks/useAuthenticatedApi.tsx +0 -67
  281. package/src/hooks/useBackupCodes.ts +0 -105
  282. package/src/hooks/useEmailVerification.ts +0 -54
  283. package/src/hooks/useExpiringValue.ts +0 -78
  284. package/src/hooks/useLocalStorage.ts +0 -18
  285. package/src/hooks/useUserSettings.ts +0 -271
  286. package/src/interfaces/IAppConfig.ts +0 -7
  287. package/src/interfaces/ICategoryConfig.ts +0 -41
  288. package/src/interfaces/IMenuConfig.ts +0 -28
  289. package/src/interfaces/IMenuOption.ts +0 -79
  290. package/src/services/__mocks__/authService.ts +0 -14
  291. package/src/services/__tests__/authService.totp.property.spec.ts +0 -360
  292. package/src/services/__tests__/authService.totp.spec.ts +0 -439
  293. package/src/services/api.ts +0 -13
  294. package/src/services/authService.ts +0 -692
  295. package/src/services/authenticatedApi.ts +0 -17
  296. package/src/services/index.ts +0 -3
  297. package/src/types/MenuType.ts +0 -15
  298. package/src/types/expirationSeconds.ts +0 -18
  299. package/src/types/index.ts +0 -1
  300. package/src/types/translation.ts +0 -20
  301. package/src/wrappers/BackupCodeLoginWrapper.tsx +0 -34
  302. package/src/wrappers/BackupCodesWrapper.tsx +0 -28
  303. package/src/wrappers/ChangePasswordFormWrapper.tsx +0 -34
  304. package/src/wrappers/ForgotPasswordFormWrapper.tsx +0 -45
  305. package/src/wrappers/LoginFormWrapper.tsx +0 -108
  306. package/src/wrappers/LogoutPageWrapper.tsx +0 -30
  307. package/src/wrappers/RegisterFormWrapper.tsx +0 -109
  308. package/src/wrappers/ResetPasswordFormWrapper.tsx +0 -57
  309. package/src/wrappers/UserSettingsFormWrapper.tsx +0 -140
  310. package/src/wrappers/VerifyEmailPageWrapper.tsx +0 -27
  311. package/src/wrappers/__tests__/LoginFormWrapper.totp.spec.tsx +0 -339
@@ -1,396 +0,0 @@
1
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
2
- import { UserSettingsForm, UserSettingsFormProps } from '../UserSettingsForm';
3
- import { I18nProvider } from '../../contexts';
4
- import { I18nEngine } from '@digitaldefiance/i18n-lib';
5
-
6
- // Mock qrcode.react to avoid SVG rendering complexity in jsdom
7
- jest.mock('qrcode.react', () => ({
8
- QRCodeSVG: (props: Record<string, unknown>) => (
9
- <svg
10
- data-testid="qr-code-svg"
11
- role={props.role as string}
12
- aria-label={props['aria-label'] as string}
13
- />
14
- ),
15
- }));
16
-
17
- const defaultInitialValues = {
18
- email: 'user@example.com',
19
- timezone: 'UTC',
20
- siteLanguage: 'en',
21
- currency: 'USD',
22
- darkMode: false,
23
- directChallenge: false,
24
- };
25
-
26
- const defaultLanguages = [{ code: 'en', label: 'English' }];
27
-
28
- const baseProps: Pick<UserSettingsFormProps, 'initialValues' | 'onSubmit' | 'languages'> = {
29
- initialValues: defaultInitialValues,
30
- onSubmit: jest.fn().mockResolvedValue({ success: true, message: 'Saved' }),
31
- languages: defaultLanguages,
32
- };
33
-
34
- const renderWithI18n = (component: React.ReactElement) => {
35
- const engine = I18nEngine.getInstance('default');
36
- return render(<I18nProvider i18nEngine={engine}>{component}</I18nProvider>);
37
- };
38
-
39
- describe('UserSettingsForm TOTP integration', () => {
40
- // Suppress act() warnings from Formik's internal state management
41
- const originalError = console.error;
42
- beforeAll(() => {
43
- console.error = (...args: unknown[]) => {
44
- if (
45
- typeof args[0] === 'string' &&
46
- (args[0].includes('not wrapped in act') ||
47
- args[0].includes('cannot be a descendant'))
48
- ) {
49
- return;
50
- }
51
- originalError.call(console, ...args);
52
- };
53
- });
54
-
55
- afterAll(() => {
56
- console.error = originalError;
57
- });
58
-
59
- beforeEach(() => {
60
- jest.clearAllMocks();
61
- });
62
-
63
- /**
64
- * Validates: Requirement 14.5
65
- * No TOTP controls when totpEnabled prop is undefined (backward compatible).
66
- */
67
- it('does not render TOTP controls when totpEnabled is undefined', () => {
68
- renderWithI18n(<UserSettingsForm {...baseProps} />);
69
-
70
- expect(
71
- screen.queryByText(/two-factor authentication/i)
72
- ).not.toBeInTheDocument();
73
- expect(
74
- screen.queryByTestId('enable-totp-button')
75
- ).not.toBeInTheDocument();
76
- expect(
77
- screen.queryByTestId('disable-totp-button')
78
- ).not.toBeInTheDocument();
79
- expect(
80
- screen.queryByTestId('reset-totp-button')
81
- ).not.toBeInTheDocument();
82
- });
83
-
84
- /**
85
- * Validates: Requirement 10.1
86
- * TOTP status is displayed when totpEnabled is provided.
87
- */
88
- it('displays TOTP status as disabled when totpEnabled=false', () => {
89
- renderWithI18n(
90
- <UserSettingsForm {...baseProps} totpEnabled={false} />
91
- );
92
-
93
- expect(
94
- screen.getByText(/two-factor authentication/i)
95
- ).toBeInTheDocument();
96
- expect(
97
- screen.getByText(/2fa is currently disabled/i)
98
- ).toBeInTheDocument();
99
- });
100
-
101
- /**
102
- * Validates: Requirement 10.1
103
- * TOTP status is displayed as enabled when totpEnabled=true.
104
- */
105
- it('displays TOTP status as enabled when totpEnabled=true', () => {
106
- renderWithI18n(
107
- <UserSettingsForm
108
- {...baseProps}
109
- totpEnabled={true}
110
- onTotpDisable={jest.fn()}
111
- onTotpReset={jest.fn()}
112
- />
113
- );
114
-
115
- expect(
116
- screen.getByText(/two-factor authentication/i)
117
- ).toBeInTheDocument();
118
- expect(
119
- screen.getByText(/2fa is currently enabled/i)
120
- ).toBeInTheDocument();
121
- });
122
-
123
- /**
124
- * Validates: Requirement 10.2
125
- * "Enable 2FA" button is shown when totpEnabled=false.
126
- */
127
- it('renders "Enable 2FA" button when totpEnabled=false', () => {
128
- renderWithI18n(
129
- <UserSettingsForm
130
- {...baseProps}
131
- totpEnabled={false}
132
- onTotpSetup={jest.fn()}
133
- />
134
- );
135
-
136
- const enableButton = screen.getByTestId('enable-totp-button');
137
- expect(enableButton).toBeInTheDocument();
138
- expect(enableButton).toHaveTextContent(/enable 2fa/i);
139
- });
140
-
141
- /**
142
- * Validates: Requirement 10.3
143
- * "Disable 2FA" and "Reset 2FA" buttons are shown when totpEnabled=true.
144
- */
145
- it('renders "Disable 2FA" and "Reset 2FA" buttons when totpEnabled=true', () => {
146
- renderWithI18n(
147
- <UserSettingsForm
148
- {...baseProps}
149
- totpEnabled={true}
150
- onTotpDisable={jest.fn()}
151
- onTotpReset={jest.fn()}
152
- />
153
- );
154
-
155
- const disableButton = screen.getByTestId('disable-totp-button');
156
- expect(disableButton).toBeInTheDocument();
157
- expect(disableButton).toHaveTextContent(/disable 2fa/i);
158
-
159
- const resetButton = screen.getByTestId('reset-totp-button');
160
- expect(resetButton).toBeInTheDocument();
161
- expect(resetButton).toHaveTextContent(/reset 2fa/i);
162
- });
163
-
164
- /**
165
- * Validates: Requirement 10.2
166
- * Clicking "Enable 2FA" calls onTotpSetup and renders TotpSetupForm inline.
167
- */
168
- it('shows TotpSetupForm inline after clicking "Enable 2FA"', async () => {
169
- const mockOnTotpSetup = jest.fn().mockResolvedValue({
170
- provisioningUri:
171
- 'otpauth://totp/TestIssuer:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=TestIssuer',
172
- secret: 'JBSWY3DPEHPK3PXP',
173
- });
174
-
175
- renderWithI18n(
176
- <UserSettingsForm
177
- {...baseProps}
178
- totpEnabled={false}
179
- onTotpSetup={mockOnTotpSetup}
180
- onTotpConfirm={jest.fn()}
181
- />
182
- );
183
-
184
- fireEvent.click(screen.getByTestId('enable-totp-button'));
185
-
186
- await waitFor(() => {
187
- expect(mockOnTotpSetup).toHaveBeenCalled();
188
- });
189
-
190
- // TotpSetupForm should be rendered inline with QR code and secret
191
- await waitFor(() => {
192
- expect(screen.getByTestId('qr-code-svg')).toBeInTheDocument();
193
- });
194
- expect(screen.getByText('JBSWY3DPEHPK3PXP')).toBeInTheDocument();
195
- });
196
-
197
- /**
198
- * Validates: Requirement 10.6
199
- * Clicking "Disable 2FA" shows a 6-digit code prompt.
200
- */
201
- it('shows code prompt when clicking "Disable 2FA"', () => {
202
- renderWithI18n(
203
- <UserSettingsForm
204
- {...baseProps}
205
- totpEnabled={true}
206
- onTotpDisable={jest.fn()}
207
- onTotpReset={jest.fn()}
208
- />
209
- );
210
-
211
- fireEvent.click(screen.getByTestId('disable-totp-button'));
212
-
213
- // Code input should appear
214
- expect(
215
- screen.getByText(/enter your current 2fa code to disable/i)
216
- ).toBeInTheDocument();
217
- expect(
218
- screen.getByTestId('disable-totp-submit-button')
219
- ).toBeInTheDocument();
220
- expect(
221
- screen.getByTestId('cancel-disable-totp-button')
222
- ).toBeInTheDocument();
223
- });
224
-
225
- /**
226
- * Validates: Requirement 10.6
227
- * Disable flow: entering code and submitting calls onTotpDisable.
228
- */
229
- it('calls onTotpDisable with the entered code', async () => {
230
- const mockOnTotpDisable = jest
231
- .fn()
232
- .mockResolvedValue({ success: true });
233
-
234
- renderWithI18n(
235
- <UserSettingsForm
236
- {...baseProps}
237
- totpEnabled={true}
238
- onTotpDisable={mockOnTotpDisable}
239
- onTotpReset={jest.fn()}
240
- />
241
- );
242
-
243
- // Open disable prompt
244
- fireEvent.click(screen.getByTestId('disable-totp-button'));
245
-
246
- // Enter code
247
- const codeInput = screen.getByLabelText(/totp code/i);
248
- fireEvent.change(codeInput, { target: { value: '654321' } });
249
-
250
- // Submit
251
- fireEvent.click(screen.getByTestId('disable-totp-submit-button'));
252
-
253
- await waitFor(() => {
254
- expect(mockOnTotpDisable).toHaveBeenCalledWith('654321');
255
- });
256
-
257
- // After successful disable, status should update to disabled
258
- await waitFor(() => {
259
- expect(
260
- screen.getByText(/2fa is currently disabled/i)
261
- ).toBeInTheDocument();
262
- });
263
- });
264
-
265
- /**
266
- * Validates: Requirement 15.8
267
- * Clicking "Reset 2FA" shows a 6-digit code prompt.
268
- */
269
- it('shows code prompt when clicking "Reset 2FA"', () => {
270
- renderWithI18n(
271
- <UserSettingsForm
272
- {...baseProps}
273
- totpEnabled={true}
274
- onTotpDisable={jest.fn()}
275
- onTotpReset={jest.fn()}
276
- />
277
- );
278
-
279
- fireEvent.click(screen.getByTestId('reset-totp-button'));
280
-
281
- // Code input should appear
282
- expect(
283
- screen.getByText(/enter your current 2fa code to reset/i)
284
- ).toBeInTheDocument();
285
- expect(
286
- screen.getByTestId('reset-totp-submit-button')
287
- ).toBeInTheDocument();
288
- expect(
289
- screen.getByTestId('cancel-reset-totp-button')
290
- ).toBeInTheDocument();
291
- });
292
-
293
- /**
294
- * Validates: Requirement 15.8
295
- * Reset flow: entering code and submitting calls onTotpReset, then shows TotpSetupForm.
296
- */
297
- it('calls onTotpReset with code and shows TotpSetupForm on success', async () => {
298
- const mockOnTotpReset = jest.fn().mockResolvedValue({
299
- provisioningUri:
300
- 'otpauth://totp/TestIssuer:user@example.com?secret=NEWBASE32SECRET&issuer=TestIssuer',
301
- secret: 'NEWBASE32SECRET',
302
- });
303
-
304
- renderWithI18n(
305
- <UserSettingsForm
306
- {...baseProps}
307
- totpEnabled={true}
308
- onTotpDisable={jest.fn()}
309
- onTotpReset={mockOnTotpReset}
310
- onTotpConfirm={jest.fn()}
311
- />
312
- );
313
-
314
- // Open reset prompt
315
- fireEvent.click(screen.getByTestId('reset-totp-button'));
316
-
317
- // Enter code
318
- const codeInput = screen.getByLabelText(/totp code/i);
319
- fireEvent.change(codeInput, { target: { value: '123456' } });
320
-
321
- // Submit
322
- fireEvent.click(screen.getByTestId('reset-totp-submit-button'));
323
-
324
- await waitFor(() => {
325
- expect(mockOnTotpReset).toHaveBeenCalledWith('123456');
326
- });
327
-
328
- // TotpSetupForm should appear with the new provisioning URI and secret
329
- await waitFor(() => {
330
- expect(screen.getByTestId('qr-code-svg')).toBeInTheDocument();
331
- });
332
- expect(screen.getByText('NEWBASE32SECRET')).toBeInTheDocument();
333
- });
334
-
335
- /**
336
- * Validates: Requirement 10.6
337
- * Disable flow: displays error when onTotpDisable returns an error.
338
- */
339
- it('displays error when disable fails', async () => {
340
- const mockOnTotpDisable = jest
341
- .fn()
342
- .mockResolvedValue({ error: 'Invalid TOTP code' });
343
-
344
- renderWithI18n(
345
- <UserSettingsForm
346
- {...baseProps}
347
- totpEnabled={true}
348
- onTotpDisable={mockOnTotpDisable}
349
- onTotpReset={jest.fn()}
350
- />
351
- );
352
-
353
- // Open disable prompt
354
- fireEvent.click(screen.getByTestId('disable-totp-button'));
355
-
356
- // Enter code and submit
357
- const codeInput = screen.getByLabelText(/totp code/i);
358
- fireEvent.change(codeInput, { target: { value: '000000' } });
359
- fireEvent.click(screen.getByTestId('disable-totp-submit-button'));
360
-
361
- await waitFor(() => {
362
- expect(screen.getByText('Invalid TOTP code')).toBeInTheDocument();
363
- });
364
- });
365
-
366
- /**
367
- * Validates: Requirement 15.8
368
- * Reset flow: displays error when onTotpReset returns an error.
369
- */
370
- it('displays error when reset fails', async () => {
371
- const mockOnTotpReset = jest
372
- .fn()
373
- .mockResolvedValue({ error: 'Invalid TOTP code' });
374
-
375
- renderWithI18n(
376
- <UserSettingsForm
377
- {...baseProps}
378
- totpEnabled={true}
379
- onTotpDisable={jest.fn()}
380
- onTotpReset={mockOnTotpReset}
381
- />
382
- );
383
-
384
- // Open reset prompt
385
- fireEvent.click(screen.getByTestId('reset-totp-button'));
386
-
387
- // Enter code and submit
388
- const codeInput = screen.getByLabelText(/totp code/i);
389
- fireEvent.change(codeInput, { target: { value: '000000' } });
390
- fireEvent.click(screen.getByTestId('reset-totp-submit-button'));
391
-
392
- await waitFor(() => {
393
- expect(screen.getByText('Invalid TOTP code')).toBeInTheDocument();
394
- });
395
- });
396
- });