@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.
- package/package.json +5 -4
- package/src/auth/AdminRoute.d.ts +8 -0
- package/src/auth/AdminRoute.d.ts.map +1 -0
- package/src/auth/AdminRoute.js +28 -0
- package/src/auth/Private.d.ts +6 -0
- package/src/auth/Private.d.ts.map +1 -0
- package/src/auth/Private.js +14 -0
- package/src/auth/PrivateRoute.d.ts +8 -0
- package/src/auth/PrivateRoute.d.ts.map +1 -0
- package/src/auth/PrivateRoute.js +23 -0
- package/src/auth/UnAuth.d.ts +6 -0
- package/src/auth/UnAuth.d.ts.map +1 -0
- package/src/auth/UnAuth.js +14 -0
- package/src/auth/UnAuthRoute.d.ts +8 -0
- package/src/auth/UnAuthRoute.d.ts.map +1 -0
- package/src/auth/UnAuthRoute.js +22 -0
- package/src/auth/{index.ts → index.d.ts} +3 -2
- package/src/auth/index.d.ts.map +1 -0
- package/src/auth/index.js +12 -0
- package/src/components/ApiAccess.d.ts +16 -0
- package/src/components/ApiAccess.d.ts.map +1 -0
- package/src/components/ApiAccess.js +77 -0
- package/src/components/BackupCodeLoginForm.d.ts +43 -0
- package/src/components/BackupCodeLoginForm.d.ts.map +1 -0
- package/src/components/BackupCodeLoginForm.js +139 -0
- package/src/components/BackupCodesForm.d.ts +26 -0
- package/src/components/BackupCodesForm.d.ts.map +1 -0
- package/src/components/BackupCodesForm.js +120 -0
- package/src/components/CategoryMenu.d.ts +17 -0
- package/src/components/CategoryMenu.d.ts.map +1 -0
- package/src/components/CategoryMenu.js +65 -0
- package/src/components/ChangePasswordForm.d.ts +26 -0
- package/src/components/ChangePasswordForm.d.ts.map +1 -0
- package/src/components/ChangePasswordForm.js +78 -0
- package/src/components/ConfirmationDialog.d.ts +13 -0
- package/src/components/ConfirmationDialog.d.ts.map +1 -0
- package/src/components/ConfirmationDialog.js +10 -0
- package/src/components/CurrencyCodeSelector.d.ts +9 -0
- package/src/components/CurrencyCodeSelector.d.ts.map +1 -0
- package/src/components/CurrencyCodeSelector.js +31 -0
- package/src/components/CurrencyInput.d.ts +13 -0
- package/src/components/CurrencyInput.d.ts.map +1 -0
- package/src/components/CurrencyInput.js +22 -0
- package/src/components/DashboardPage.d.ts +8 -0
- package/src/components/DashboardPage.d.ts.map +1 -0
- package/src/components/DashboardPage.js +10 -0
- package/src/components/DropdownMenu.d.ts +13 -0
- package/src/components/DropdownMenu.d.ts.map +1 -0
- package/src/components/DropdownMenu.js +63 -0
- package/src/components/ExpirationSecondsSelector.d.ts +13 -0
- package/src/components/ExpirationSecondsSelector.d.ts.map +1 -0
- package/src/components/ExpirationSecondsSelector.js +32 -0
- package/src/components/FakeEmailAdminPanel.d.ts +23 -0
- package/src/components/FakeEmailAdminPanel.d.ts.map +1 -0
- package/src/components/FakeEmailAdminPanel.js +147 -0
- package/src/components/Flag.d.ts +20 -0
- package/src/components/Flag.d.ts.map +1 -0
- package/src/components/Flag.js +43 -0
- package/src/components/ForgotPasswordForm.d.ts +18 -0
- package/src/components/ForgotPasswordForm.d.ts.map +1 -0
- package/src/components/ForgotPasswordForm.js +61 -0
- package/src/components/LoginForm.d.ts +44 -0
- package/src/components/LoginForm.d.ts.map +1 -0
- package/src/components/LoginForm.js +123 -0
- package/src/components/LogoutPage.d.ts +8 -0
- package/src/components/LogoutPage.d.ts.map +1 -0
- package/src/components/LogoutPage.js +16 -0
- package/src/components/RegisterForm.d.ts +100 -0
- package/src/components/RegisterForm.d.ts.map +1 -0
- package/src/components/RegisterForm.js +306 -0
- package/src/components/ResetPasswordForm.d.ts +23 -0
- package/src/components/ResetPasswordForm.d.ts.map +1 -0
- package/src/components/ResetPasswordForm.js +78 -0
- package/src/components/SideMenu.d.ts +8 -0
- package/src/components/SideMenu.d.ts.map +1 -0
- package/src/components/SideMenu.js +25 -0
- package/src/components/SideMenuListItem.d.ts +13 -0
- package/src/components/SideMenuListItem.d.ts.map +1 -0
- package/src/components/SideMenuListItem.js +44 -0
- package/src/components/T.d.ts +7 -0
- package/src/components/T.d.ts.map +1 -0
- package/src/components/T.js +13 -0
- package/src/components/TDiv.d.ts +9 -0
- package/src/components/TDiv.d.ts.map +1 -0
- package/src/components/TDiv.js +14 -0
- package/src/components/TImgAlt.d.ts +10 -0
- package/src/components/TImgAlt.d.ts.map +1 -0
- package/src/components/TImgAlt.js +14 -0
- package/src/components/TopMenu.d.ts +50 -0
- package/src/components/TopMenu.d.ts.map +1 -0
- package/src/components/TopMenu.js +79 -0
- package/src/components/TotpSetupForm.d.ts +24 -0
- package/src/components/TotpSetupForm.d.ts.map +1 -0
- package/src/components/TotpSetupForm.js +82 -0
- package/src/components/TotpVerificationForm.d.ts +23 -0
- package/src/components/TotpVerificationForm.d.ts.map +1 -0
- package/src/components/TotpVerificationForm.js +66 -0
- package/src/components/TranslatedTitle.d.ts +8 -0
- package/src/components/TranslatedTitle.d.ts.map +1 -0
- package/src/components/TranslatedTitle.js +15 -0
- package/src/components/UserLanguageSelector.d.ts +4 -0
- package/src/components/UserLanguageSelector.d.ts.map +1 -0
- package/src/components/UserLanguageSelector.js +31 -0
- package/src/components/UserMenu.d.ts +4 -0
- package/src/components/UserMenu.d.ts.map +1 -0
- package/src/components/UserMenu.js +12 -0
- package/src/components/UserSettingsForm.d.ts +113 -0
- package/src/components/UserSettingsForm.d.ts.map +1 -0
- package/src/components/UserSettingsForm.js +269 -0
- package/src/components/VerifyEmailPage.d.ts +24 -0
- package/src/components/VerifyEmailPage.d.ts.map +1 -0
- package/src/components/VerifyEmailPage.js +81 -0
- package/src/components/{index.ts → index.d.ts} +1 -1
- package/src/components/index.d.ts.map +1 -0
- package/src/components/index.js +35 -0
- package/src/contexts/AuthProvider.d.ts +186 -0
- package/src/contexts/AuthProvider.d.ts.map +1 -0
- package/src/contexts/AuthProvider.js +515 -0
- package/src/contexts/I18nProvider.d.ts +27 -0
- package/src/contexts/I18nProvider.d.ts.map +1 -0
- package/src/contexts/I18nProvider.js +60 -0
- package/src/contexts/MenuContext.d.ts +28 -0
- package/src/contexts/MenuContext.d.ts.map +1 -0
- package/src/contexts/MenuContext.js +292 -0
- package/src/contexts/SuiteConfigProvider.d.ts +44 -0
- package/src/contexts/SuiteConfigProvider.d.ts.map +1 -0
- package/src/contexts/SuiteConfigProvider.js +43 -0
- package/src/contexts/ThemeProvider.d.ts +15 -0
- package/src/contexts/ThemeProvider.d.ts.map +1 -0
- package/src/contexts/ThemeProvider.js +36 -0
- package/src/contexts/{index.ts → index.d.ts} +1 -0
- package/src/contexts/index.d.ts.map +1 -0
- package/src/contexts/index.js +8 -0
- package/src/hooks/{index.ts → index.d.ts} +1 -0
- package/src/hooks/index.d.ts.map +1 -0
- package/src/hooks/index.js +9 -0
- package/src/hooks/useAuthenticatedApi.d.ts +34 -0
- package/src/hooks/useAuthenticatedApi.d.ts.map +1 -0
- package/src/hooks/useAuthenticatedApi.js +46 -0
- package/src/hooks/useBackupCodes.d.ts +15 -0
- package/src/hooks/useBackupCodes.d.ts.map +1 -0
- package/src/hooks/useBackupCodes.js +74 -0
- package/src/hooks/useEmailVerification.d.ts +10 -0
- package/src/hooks/useEmailVerification.d.ts.map +1 -0
- package/src/hooks/useEmailVerification.js +40 -0
- package/src/hooks/useExpiringValue.d.ts +14 -0
- package/src/hooks/useExpiringValue.d.ts.map +1 -0
- package/src/hooks/useExpiringValue.js +53 -0
- package/src/hooks/useLocalStorage.d.ts +2 -0
- package/src/hooks/useLocalStorage.d.ts.map +1 -0
- package/src/hooks/useLocalStorage.js +15 -0
- package/src/hooks/useUserSettings.d.ts +48 -0
- package/src/hooks/useUserSettings.d.ts.map +1 -0
- package/src/hooks/useUserSettings.js +171 -0
- package/src/{index.ts → index.d.ts} +1 -1
- package/src/index.d.ts.map +1 -0
- package/src/index.js +12 -0
- package/src/interfaces/IAppConfig.d.ts +8 -0
- package/src/interfaces/IAppConfig.d.ts.map +1 -0
- package/src/interfaces/IAppConfig.js +2 -0
- package/src/interfaces/ICategoryConfig.d.ts +41 -0
- package/src/interfaces/ICategoryConfig.d.ts.map +1 -0
- package/src/interfaces/ICategoryConfig.js +2 -0
- package/src/interfaces/IMenuConfig.d.ts +28 -0
- package/src/interfaces/IMenuConfig.d.ts.map +1 -0
- package/src/interfaces/IMenuConfig.js +2 -0
- package/src/interfaces/IMenuOption.d.ts +82 -0
- package/src/interfaces/IMenuOption.d.ts.map +1 -0
- package/src/interfaces/IMenuOption.js +2 -0
- package/src/interfaces/{index.ts → index.d.ts} +1 -0
- package/src/interfaces/index.d.ts.map +1 -0
- package/src/interfaces/index.js +7 -0
- package/src/services/__mocks__/authService.d.ts +21 -0
- package/src/services/__mocks__/authService.d.ts.map +1 -0
- package/src/services/__mocks__/authService.js +15 -0
- package/src/services/api.d.ts +3 -0
- package/src/services/api.d.ts.map +1 -0
- package/src/services/api.js +14 -0
- package/src/services/authService.d.ts +126 -0
- package/src/services/authService.d.ts.map +1 -0
- package/src/services/authService.js +471 -0
- package/src/services/authenticatedApi.d.ts +3 -0
- package/src/services/authenticatedApi.d.ts.map +1 -0
- package/src/services/authenticatedApi.js +18 -0
- package/src/services/index.d.ts +4 -0
- package/src/services/index.d.ts.map +1 -0
- package/src/services/index.js +6 -0
- package/src/types/MenuType.d.ts +11 -0
- package/src/types/MenuType.d.ts.map +1 -0
- package/src/types/MenuType.js +12 -0
- package/src/types/expirationSeconds.d.ts +3 -0
- package/src/types/expirationSeconds.d.ts.map +1 -0
- package/src/types/expirationSeconds.js +17 -0
- package/src/types/index.d.ts +2 -0
- package/src/types/index.d.ts.map +1 -0
- package/src/types/index.js +4 -0
- package/src/types/translation.d.ts +10 -0
- package/src/types/translation.d.ts.map +1 -0
- package/src/types/translation.js +9 -0
- package/src/wrappers/BackupCodeLoginWrapper.d.ts +8 -0
- package/src/wrappers/BackupCodeLoginWrapper.d.ts.map +1 -0
- package/src/wrappers/BackupCodeLoginWrapper.js +20 -0
- package/src/wrappers/BackupCodesWrapper.d.ts +7 -0
- package/src/wrappers/BackupCodesWrapper.d.ts.map +1 -0
- package/src/wrappers/BackupCodesWrapper.js +17 -0
- package/src/wrappers/ChangePasswordFormWrapper.d.ts +8 -0
- package/src/wrappers/ChangePasswordFormWrapper.d.ts.map +1 -0
- package/src/wrappers/ChangePasswordFormWrapper.js +21 -0
- package/src/wrappers/ForgotPasswordFormWrapper.d.ts +8 -0
- package/src/wrappers/ForgotPasswordFormWrapper.d.ts.map +1 -0
- package/src/wrappers/ForgotPasswordFormWrapper.js +29 -0
- package/src/wrappers/LoginFormWrapper.d.ts +21 -0
- package/src/wrappers/LoginFormWrapper.d.ts.map +1 -0
- package/src/wrappers/LoginFormWrapper.js +72 -0
- package/src/wrappers/LogoutPageWrapper.d.ts +9 -0
- package/src/wrappers/LogoutPageWrapper.d.ts.map +1 -0
- package/src/wrappers/LogoutPageWrapper.js +21 -0
- package/src/wrappers/RegisterFormWrapper.d.ts +20 -0
- package/src/wrappers/RegisterFormWrapper.d.ts.map +1 -0
- package/src/wrappers/RegisterFormWrapper.js +51 -0
- package/src/wrappers/ResetPasswordFormWrapper.d.ts +9 -0
- package/src/wrappers/ResetPasswordFormWrapper.d.ts.map +1 -0
- package/src/wrappers/ResetPasswordFormWrapper.js +34 -0
- package/src/wrappers/UserSettingsFormWrapper.d.ts +29 -0
- package/src/wrappers/UserSettingsFormWrapper.d.ts.map +1 -0
- package/src/wrappers/UserSettingsFormWrapper.js +95 -0
- package/src/wrappers/VerifyEmailPageWrapper.d.ts +8 -0
- package/src/wrappers/VerifyEmailPageWrapper.d.ts.map +1 -0
- package/src/wrappers/VerifyEmailPageWrapper.js +21 -0
- package/src/wrappers/{index.tsx → index.d.ts} +1 -10
- package/src/wrappers/index.d.ts.map +1 -0
- package/src/wrappers/index.js +24 -0
- package/LICENSE +0 -21
- package/src/auth/AdminRoute.tsx +0 -54
- package/src/auth/Private.tsx +0 -17
- package/src/auth/PrivateRoute.tsx +0 -28
- package/src/auth/UnAuth.tsx +0 -16
- package/src/auth/UnAuthRoute.tsx +0 -30
- package/src/components/ApiAccess.tsx +0 -175
- package/src/components/BackupCodeLoginForm.tsx +0 -489
- package/src/components/BackupCodesForm.tsx +0 -287
- package/src/components/CategoryMenu.tsx +0 -155
- package/src/components/ChangePasswordForm.tsx +0 -273
- package/src/components/ConfirmationDialog.tsx +0 -48
- package/src/components/CurrencyCodeSelector.tsx +0 -60
- package/src/components/CurrencyInput.tsx +0 -80
- package/src/components/DashboardPage.tsx +0 -24
- package/src/components/DropdownMenu.tsx +0 -105
- package/src/components/ExpirationSecondsSelector.tsx +0 -60
- package/src/components/FakeEmailAdminPanel.tsx +0 -374
- package/src/components/Flag.tsx +0 -52
- package/src/components/ForgotPasswordForm.tsx +0 -174
- package/src/components/LoginForm.tsx +0 -468
- package/src/components/LogoutPage.tsx +0 -21
- package/src/components/RegisterForm.tsx +0 -1169
- package/src/components/ResetPasswordForm.tsx +0 -247
- package/src/components/SideMenu.tsx +0 -46
- package/src/components/SideMenuListItem.tsx +0 -75
- package/src/components/T.tsx +0 -24
- package/src/components/TDiv.tsx +0 -26
- package/src/components/TImgAlt.tsx +0 -28
- package/src/components/TopMenu.tsx +0 -252
- package/src/components/TotpSetupForm.tsx +0 -188
- package/src/components/TotpVerificationForm.tsx +0 -148
- package/src/components/TranslatedTitle.tsx +0 -31
- package/src/components/UserLanguageSelector.tsx +0 -45
- package/src/components/UserMenu.tsx +0 -15
- package/src/components/UserSettingsForm.tsx +0 -904
- package/src/components/VerifyEmailPage.tsx +0 -222
- package/src/components/__tests__/RegisterForm.totp.spec.tsx +0 -377
- package/src/components/__tests__/TotpSetupForm.spec.tsx +0 -258
- package/src/components/__tests__/TotpVerificationForm.spec.tsx +0 -259
- package/src/components/__tests__/UserSettingsForm.totp.spec.tsx +0 -396
- package/src/contexts/AuthProvider.spec.tsx +0 -1284
- package/src/contexts/AuthProvider.tsx +0 -977
- package/src/contexts/I18nProvider.tsx +0 -164
- package/src/contexts/MenuContext.tsx +0 -426
- package/src/contexts/SuiteConfigProvider.tsx +0 -93
- package/src/contexts/ThemeProvider.tsx +0 -67
- package/src/hooks/useAuthenticatedApi.tsx +0 -67
- package/src/hooks/useBackupCodes.ts +0 -105
- package/src/hooks/useEmailVerification.ts +0 -54
- package/src/hooks/useExpiringValue.ts +0 -78
- package/src/hooks/useLocalStorage.ts +0 -18
- package/src/hooks/useUserSettings.ts +0 -271
- package/src/interfaces/IAppConfig.ts +0 -7
- package/src/interfaces/ICategoryConfig.ts +0 -41
- package/src/interfaces/IMenuConfig.ts +0 -28
- package/src/interfaces/IMenuOption.ts +0 -79
- package/src/services/__mocks__/authService.ts +0 -14
- package/src/services/__tests__/authService.totp.property.spec.ts +0 -360
- package/src/services/__tests__/authService.totp.spec.ts +0 -439
- package/src/services/api.ts +0 -13
- package/src/services/authService.ts +0 -692
- package/src/services/authenticatedApi.ts +0 -17
- package/src/services/index.ts +0 -3
- package/src/types/MenuType.ts +0 -15
- package/src/types/expirationSeconds.ts +0 -18
- package/src/types/index.ts +0 -1
- package/src/types/translation.ts +0 -20
- package/src/wrappers/BackupCodeLoginWrapper.tsx +0 -34
- package/src/wrappers/BackupCodesWrapper.tsx +0 -28
- package/src/wrappers/ChangePasswordFormWrapper.tsx +0 -34
- package/src/wrappers/ForgotPasswordFormWrapper.tsx +0 -45
- package/src/wrappers/LoginFormWrapper.tsx +0 -108
- package/src/wrappers/LogoutPageWrapper.tsx +0 -30
- package/src/wrappers/RegisterFormWrapper.tsx +0 -109
- package/src/wrappers/ResetPasswordFormWrapper.tsx +0 -57
- package/src/wrappers/UserSettingsFormWrapper.tsx +0 -140
- package/src/wrappers/VerifyEmailPageWrapper.tsx +0 -27
- 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
|
-
});
|