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

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 (314) 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 +19 -0
  117. package/src/contexts/AuthProvider.d.ts.map +1 -0
  118. package/src/contexts/AuthProvider.js +514 -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 +307 -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/auth-context.d.ts +172 -0
  132. package/src/contexts/auth-context.d.ts.map +1 -0
  133. package/src/contexts/auth-context.js +13 -0
  134. package/src/contexts/{index.ts → index.d.ts} +1 -0
  135. package/src/contexts/index.d.ts.map +1 -0
  136. package/src/contexts/index.js +8 -0
  137. package/src/hooks/{index.ts → index.d.ts} +1 -0
  138. package/src/hooks/index.d.ts.map +1 -0
  139. package/src/hooks/index.js +9 -0
  140. package/src/hooks/useAuthenticatedApi.d.ts +34 -0
  141. package/src/hooks/useAuthenticatedApi.d.ts.map +1 -0
  142. package/src/hooks/useAuthenticatedApi.js +46 -0
  143. package/src/hooks/useBackupCodes.d.ts +15 -0
  144. package/src/hooks/useBackupCodes.d.ts.map +1 -0
  145. package/src/hooks/useBackupCodes.js +74 -0
  146. package/src/hooks/useEmailVerification.d.ts +10 -0
  147. package/src/hooks/useEmailVerification.d.ts.map +1 -0
  148. package/src/hooks/useEmailVerification.js +40 -0
  149. package/src/hooks/useExpiringValue.d.ts +14 -0
  150. package/src/hooks/useExpiringValue.d.ts.map +1 -0
  151. package/src/hooks/useExpiringValue.js +53 -0
  152. package/src/hooks/useLocalStorage.d.ts +2 -0
  153. package/src/hooks/useLocalStorage.d.ts.map +1 -0
  154. package/src/hooks/useLocalStorage.js +15 -0
  155. package/src/hooks/useUserSettings.d.ts +48 -0
  156. package/src/hooks/useUserSettings.d.ts.map +1 -0
  157. package/src/hooks/useUserSettings.js +171 -0
  158. package/src/{index.ts → index.d.ts} +1 -1
  159. package/src/index.d.ts.map +1 -0
  160. package/src/index.js +12 -0
  161. package/src/interfaces/IAppConfig.d.ts +8 -0
  162. package/src/interfaces/IAppConfig.d.ts.map +1 -0
  163. package/src/interfaces/IAppConfig.js +2 -0
  164. package/src/interfaces/ICategoryConfig.d.ts +41 -0
  165. package/src/interfaces/ICategoryConfig.d.ts.map +1 -0
  166. package/src/interfaces/ICategoryConfig.js +2 -0
  167. package/src/interfaces/IMenuConfig.d.ts +28 -0
  168. package/src/interfaces/IMenuConfig.d.ts.map +1 -0
  169. package/src/interfaces/IMenuConfig.js +2 -0
  170. package/src/interfaces/IMenuOption.d.ts +82 -0
  171. package/src/interfaces/IMenuOption.d.ts.map +1 -0
  172. package/src/interfaces/IMenuOption.js +2 -0
  173. package/src/interfaces/{index.ts → index.d.ts} +1 -0
  174. package/src/interfaces/index.d.ts.map +1 -0
  175. package/src/interfaces/index.js +7 -0
  176. package/src/services/__mocks__/authService.d.ts +21 -0
  177. package/src/services/__mocks__/authService.d.ts.map +1 -0
  178. package/src/services/__mocks__/authService.js +15 -0
  179. package/src/services/api.d.ts +3 -0
  180. package/src/services/api.d.ts.map +1 -0
  181. package/src/services/api.js +14 -0
  182. package/src/services/authService.d.ts +126 -0
  183. package/src/services/authService.d.ts.map +1 -0
  184. package/src/services/authService.js +471 -0
  185. package/src/services/authenticatedApi.d.ts +3 -0
  186. package/src/services/authenticatedApi.d.ts.map +1 -0
  187. package/src/services/authenticatedApi.js +18 -0
  188. package/src/services/index.d.ts +4 -0
  189. package/src/services/index.d.ts.map +1 -0
  190. package/src/services/index.js +6 -0
  191. package/src/types/MenuType.d.ts +11 -0
  192. package/src/types/MenuType.d.ts.map +1 -0
  193. package/src/types/MenuType.js +12 -0
  194. package/src/types/expirationSeconds.d.ts +3 -0
  195. package/src/types/expirationSeconds.d.ts.map +1 -0
  196. package/src/types/expirationSeconds.js +17 -0
  197. package/src/types/index.d.ts +2 -0
  198. package/src/types/index.d.ts.map +1 -0
  199. package/src/types/index.js +4 -0
  200. package/src/types/translation.d.ts +10 -0
  201. package/src/types/translation.d.ts.map +1 -0
  202. package/src/types/translation.js +9 -0
  203. package/src/wrappers/BackupCodeLoginWrapper.d.ts +8 -0
  204. package/src/wrappers/BackupCodeLoginWrapper.d.ts.map +1 -0
  205. package/src/wrappers/BackupCodeLoginWrapper.js +20 -0
  206. package/src/wrappers/BackupCodesWrapper.d.ts +7 -0
  207. package/src/wrappers/BackupCodesWrapper.d.ts.map +1 -0
  208. package/src/wrappers/BackupCodesWrapper.js +17 -0
  209. package/src/wrappers/ChangePasswordFormWrapper.d.ts +8 -0
  210. package/src/wrappers/ChangePasswordFormWrapper.d.ts.map +1 -0
  211. package/src/wrappers/ChangePasswordFormWrapper.js +21 -0
  212. package/src/wrappers/ForgotPasswordFormWrapper.d.ts +8 -0
  213. package/src/wrappers/ForgotPasswordFormWrapper.d.ts.map +1 -0
  214. package/src/wrappers/ForgotPasswordFormWrapper.js +29 -0
  215. package/src/wrappers/LoginFormWrapper.d.ts +21 -0
  216. package/src/wrappers/LoginFormWrapper.d.ts.map +1 -0
  217. package/src/wrappers/LoginFormWrapper.js +72 -0
  218. package/src/wrappers/LogoutPageWrapper.d.ts +9 -0
  219. package/src/wrappers/LogoutPageWrapper.d.ts.map +1 -0
  220. package/src/wrappers/LogoutPageWrapper.js +21 -0
  221. package/src/wrappers/RegisterFormWrapper.d.ts +20 -0
  222. package/src/wrappers/RegisterFormWrapper.d.ts.map +1 -0
  223. package/src/wrappers/RegisterFormWrapper.js +51 -0
  224. package/src/wrappers/ResetPasswordFormWrapper.d.ts +9 -0
  225. package/src/wrappers/ResetPasswordFormWrapper.d.ts.map +1 -0
  226. package/src/wrappers/ResetPasswordFormWrapper.js +34 -0
  227. package/src/wrappers/UserSettingsFormWrapper.d.ts +29 -0
  228. package/src/wrappers/UserSettingsFormWrapper.d.ts.map +1 -0
  229. package/src/wrappers/UserSettingsFormWrapper.js +95 -0
  230. package/src/wrappers/VerifyEmailPageWrapper.d.ts +8 -0
  231. package/src/wrappers/VerifyEmailPageWrapper.d.ts.map +1 -0
  232. package/src/wrappers/VerifyEmailPageWrapper.js +21 -0
  233. package/src/wrappers/{index.tsx → index.d.ts} +1 -10
  234. package/src/wrappers/index.d.ts.map +1 -0
  235. package/src/wrappers/index.js +24 -0
  236. package/LICENSE +0 -21
  237. package/src/auth/AdminRoute.tsx +0 -54
  238. package/src/auth/Private.tsx +0 -17
  239. package/src/auth/PrivateRoute.tsx +0 -28
  240. package/src/auth/UnAuth.tsx +0 -16
  241. package/src/auth/UnAuthRoute.tsx +0 -30
  242. package/src/components/ApiAccess.tsx +0 -175
  243. package/src/components/BackupCodeLoginForm.tsx +0 -489
  244. package/src/components/BackupCodesForm.tsx +0 -287
  245. package/src/components/CategoryMenu.tsx +0 -155
  246. package/src/components/ChangePasswordForm.tsx +0 -273
  247. package/src/components/ConfirmationDialog.tsx +0 -48
  248. package/src/components/CurrencyCodeSelector.tsx +0 -60
  249. package/src/components/CurrencyInput.tsx +0 -80
  250. package/src/components/DashboardPage.tsx +0 -24
  251. package/src/components/DropdownMenu.tsx +0 -105
  252. package/src/components/ExpirationSecondsSelector.tsx +0 -60
  253. package/src/components/FakeEmailAdminPanel.tsx +0 -374
  254. package/src/components/Flag.tsx +0 -52
  255. package/src/components/ForgotPasswordForm.tsx +0 -174
  256. package/src/components/LoginForm.tsx +0 -468
  257. package/src/components/LogoutPage.tsx +0 -21
  258. package/src/components/RegisterForm.tsx +0 -1169
  259. package/src/components/ResetPasswordForm.tsx +0 -247
  260. package/src/components/SideMenu.tsx +0 -46
  261. package/src/components/SideMenuListItem.tsx +0 -75
  262. package/src/components/T.tsx +0 -24
  263. package/src/components/TDiv.tsx +0 -26
  264. package/src/components/TImgAlt.tsx +0 -28
  265. package/src/components/TopMenu.tsx +0 -252
  266. package/src/components/TotpSetupForm.tsx +0 -188
  267. package/src/components/TotpVerificationForm.tsx +0 -148
  268. package/src/components/TranslatedTitle.tsx +0 -31
  269. package/src/components/UserLanguageSelector.tsx +0 -45
  270. package/src/components/UserMenu.tsx +0 -15
  271. package/src/components/UserSettingsForm.tsx +0 -904
  272. package/src/components/VerifyEmailPage.tsx +0 -222
  273. package/src/components/__tests__/RegisterForm.totp.spec.tsx +0 -377
  274. package/src/components/__tests__/TotpSetupForm.spec.tsx +0 -258
  275. package/src/components/__tests__/TotpVerificationForm.spec.tsx +0 -259
  276. package/src/components/__tests__/UserSettingsForm.totp.spec.tsx +0 -396
  277. package/src/contexts/AuthProvider.spec.tsx +0 -1284
  278. package/src/contexts/AuthProvider.tsx +0 -977
  279. package/src/contexts/I18nProvider.tsx +0 -164
  280. package/src/contexts/MenuContext.tsx +0 -426
  281. package/src/contexts/SuiteConfigProvider.tsx +0 -93
  282. package/src/contexts/ThemeProvider.tsx +0 -67
  283. package/src/hooks/useAuthenticatedApi.tsx +0 -67
  284. package/src/hooks/useBackupCodes.ts +0 -105
  285. package/src/hooks/useEmailVerification.ts +0 -54
  286. package/src/hooks/useExpiringValue.ts +0 -78
  287. package/src/hooks/useLocalStorage.ts +0 -18
  288. package/src/hooks/useUserSettings.ts +0 -271
  289. package/src/interfaces/IAppConfig.ts +0 -7
  290. package/src/interfaces/ICategoryConfig.ts +0 -41
  291. package/src/interfaces/IMenuConfig.ts +0 -28
  292. package/src/interfaces/IMenuOption.ts +0 -79
  293. package/src/services/__mocks__/authService.ts +0 -14
  294. package/src/services/__tests__/authService.totp.property.spec.ts +0 -360
  295. package/src/services/__tests__/authService.totp.spec.ts +0 -439
  296. package/src/services/api.ts +0 -13
  297. package/src/services/authService.ts +0 -692
  298. package/src/services/authenticatedApi.ts +0 -17
  299. package/src/services/index.ts +0 -3
  300. package/src/types/MenuType.ts +0 -15
  301. package/src/types/expirationSeconds.ts +0 -18
  302. package/src/types/index.ts +0 -1
  303. package/src/types/translation.ts +0 -20
  304. package/src/wrappers/BackupCodeLoginWrapper.tsx +0 -34
  305. package/src/wrappers/BackupCodesWrapper.tsx +0 -28
  306. package/src/wrappers/ChangePasswordFormWrapper.tsx +0 -34
  307. package/src/wrappers/ForgotPasswordFormWrapper.tsx +0 -45
  308. package/src/wrappers/LoginFormWrapper.tsx +0 -108
  309. package/src/wrappers/LogoutPageWrapper.tsx +0 -30
  310. package/src/wrappers/RegisterFormWrapper.tsx +0 -109
  311. package/src/wrappers/ResetPasswordFormWrapper.tsx +0 -57
  312. package/src/wrappers/UserSettingsFormWrapper.tsx +0 -140
  313. package/src/wrappers/VerifyEmailPageWrapper.tsx +0 -27
  314. 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
- });