@cosmicdrift/kumiko-bundled-features 0.207.0 → 0.208.0
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 +9 -8
- package/src/admin-shell/__tests__/admin-shell.boot.test.ts +1 -1
- package/src/admin-shell/i18n.ts +17 -39
- package/src/admin-shell/web/i18n.ts +6 -11
- package/src/audit/i18n.ts +25 -41
- package/src/audit/web/i18n.ts +5 -9
- package/src/auth-email-password/__tests__/email-templates.test.ts +4 -0
- package/src/auth-email-password/email-templates.ts +97 -149
- package/src/auth-email-password/i18n.ts +6 -265
- package/src/auth-email-password/web/__tests__/confirm-account-unlock-screen.test.tsx +7 -7
- package/src/auth-email-password/web/__tests__/forgot-password-screen.test.tsx +14 -14
- package/src/auth-email-password/web/__tests__/invite-accept-screen.test.tsx +23 -25
- package/src/auth-email-password/web/__tests__/login-screen.test.tsx +60 -68
- package/src/auth-email-password/web/__tests__/request-account-unlock-screen.test.tsx +24 -24
- package/src/auth-email-password/web/__tests__/reset-password-screen.test.tsx +20 -20
- package/src/auth-email-password/web/__tests__/signup-complete-screen.test.tsx +9 -9
- package/src/auth-email-password/web/__tests__/signup-screen.test.tsx +11 -11
- package/src/auth-email-password/web/__tests__/test-utils.tsx +2 -2
- package/src/auth-email-password/web/__tests__/user-menu.test.tsx +3 -3
- package/src/auth-email-password/web/__tests__/verify-email-screen.test.tsx +3 -3
- package/src/auth-email-password/web/invite-accept-screen.tsx +3 -1
- package/src/auth-mfa/__tests__/errors-i18n-parity.test.ts +2 -4
- package/src/auth-mfa/i18n.ts +1 -3
- package/src/auth-mfa/web/__tests__/mfa-enable-screen.test.tsx +15 -15
- package/src/auth-mfa/web/__tests__/mfa-setup-preauth-screen.test.tsx +45 -49
- package/src/auth-mfa/web/__tests__/mfa-verify-screen.test.tsx +10 -10
- package/src/auth-mfa/web/i18n.ts +0 -126
- package/src/cap-counter/i18n.ts +5 -11
- package/src/compliance-profiles/i18n.ts +3 -11
- package/src/compliance-profiles/web/i18n.ts +0 -20
- package/src/config/i18n.ts +5 -5
- package/src/config/web/i18n.ts +1 -19
- package/src/custom-fields/web/i18n.ts +1 -18
- package/src/delivery/i18n.ts +3 -7
- package/src/delivery/web/i18n.ts +0 -20
- package/src/feature-toggles/i18n.ts +3 -11
- package/src/feature-toggles/web/i18n.ts +0 -24
- package/src/folders/web/i18n.ts +1 -51
- package/src/jobs/i18n.ts +40 -68
- package/src/jobs/web/i18n.ts +5 -9
- package/src/managed-pages/i18n.ts +23 -67
- package/src/managed-pages/web/i18n.ts +6 -11
- package/src/notes-history/i18n.ts +1 -1
- package/src/notes-history/web/i18n.ts +1 -17
- package/src/personal-access-tokens/i18n.ts +2 -6
- package/src/personal-access-tokens/web/i18n.ts +0 -72
- package/src/sessions/i18n.ts +17 -21
- package/src/tags/i18n.ts +2 -2
- package/src/tags/web/i18n.ts +1 -52
- package/src/template-resolver/web/__tests__/client-plugin.test.tsx +2 -2
- package/src/template-resolver/web/__tests__/editor-read-only.test.tsx +5 -5
- package/src/template-resolver/web/i18n.ts +0 -28
- package/src/tenant/__tests__/members-screens.boot.test.ts +0 -2
- package/src/tenant/i18n.ts +13 -27
- package/src/tenant/web/i18n.ts +0 -38
- package/src/tier-engine/i18n.ts +3 -39
- package/src/user/i18n.ts +14 -32
- package/src/user-data-rights/__tests__/default-mailers.test.ts +4 -0
- package/src/user-data-rights/__tests__/email-templates.test.ts +4 -0
- package/src/user-data-rights/__tests__/mail-default-bridge.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/run-export-jobs-cron-context.integration.test.ts +4 -0
- package/src/user-data-rights/email-templates.ts +55 -91
- package/src/user-data-rights/i18n.ts +18 -70
- package/src/user-data-rights/lib/default-mailers.ts +4 -1
- package/src/user-data-rights/web/__tests__/deletion-screens.test.tsx +11 -11
- package/src/user-data-rights/web/__tests__/public-deletion-gate.test.tsx +5 -5
- package/src/user-data-rights/web/i18n.ts +9 -157
- package/src/user-profile/i18n.ts +1 -71
|
@@ -23,9 +23,9 @@ function fillEmail(value: string): void {
|
|
|
23
23
|
describe("SignupScreen", () => {
|
|
24
24
|
test("rendert title + email-input + submit-button (de)", () => {
|
|
25
25
|
renderWithProviders(<SignupScreen />);
|
|
26
|
-
expect(screen.getByText("
|
|
26
|
+
expect(screen.getByText("Create account")).toBeTruthy();
|
|
27
27
|
expect(document.getElementById("signup-email")).toBeTruthy();
|
|
28
|
-
expect(screen.getByRole("button", { name: "
|
|
28
|
+
expect(screen.getByRole("button", { name: "Send activation link" })).toBeTruthy();
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
test("submit ruft /api/auth/signup-request mit der Email", async () => {
|
|
@@ -34,7 +34,7 @@ describe("SignupScreen", () => {
|
|
|
34
34
|
|
|
35
35
|
renderWithProviders(<SignupScreen />);
|
|
36
36
|
fillEmail("new@example.com");
|
|
37
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
37
|
+
fireEvent.click(screen.getByRole("button", { name: "Send activation link" }));
|
|
38
38
|
|
|
39
39
|
await waitFor(() => {
|
|
40
40
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
@@ -50,12 +50,12 @@ describe("SignupScreen", () => {
|
|
|
50
50
|
test("nach erfolgreichem Submit: success-Banner + Resend", async () => {
|
|
51
51
|
renderWithProviders(<SignupScreen />);
|
|
52
52
|
fillEmail("new@example.com");
|
|
53
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
53
|
+
fireEvent.click(screen.getByRole("button", { name: "Send activation link" }));
|
|
54
54
|
|
|
55
55
|
await waitFor(() => {
|
|
56
|
-
expect(screen.getByText("
|
|
56
|
+
expect(screen.getByText("Email sent")).toBeTruthy();
|
|
57
57
|
});
|
|
58
|
-
expect(screen.getByRole("button", { name: /
|
|
58
|
+
expect(screen.getByRole("button", { name: /Send email again/i })).toBeTruthy();
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
test("rate_limited mit retryAfterSeconds → interpolated minutes", async () => {
|
|
@@ -68,12 +68,12 @@ describe("SignupScreen", () => {
|
|
|
68
68
|
|
|
69
69
|
renderWithProviders(<SignupScreen />);
|
|
70
70
|
fillEmail("new@example.com");
|
|
71
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
71
|
+
fireEvent.click(screen.getByRole("button", { name: "Send activation link" }));
|
|
72
72
|
|
|
73
73
|
await waitFor(() => {
|
|
74
74
|
expect(screen.getByRole("alert").textContent).toMatch(/2/);
|
|
75
75
|
});
|
|
76
|
-
expect(screen.queryByText("
|
|
76
|
+
expect(screen.queryByText("Email sent")).toBeNull();
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
test("server 5xx → error-banner statt Success-State", async () => {
|
|
@@ -83,11 +83,11 @@ describe("SignupScreen", () => {
|
|
|
83
83
|
|
|
84
84
|
renderWithProviders(<SignupScreen />);
|
|
85
85
|
fillEmail("new@example.com");
|
|
86
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
86
|
+
fireEvent.click(screen.getByRole("button", { name: "Send activation link" }));
|
|
87
87
|
|
|
88
88
|
await waitFor(() => {
|
|
89
|
-
expect(screen.getByRole("alert").textContent).toContain("
|
|
89
|
+
expect(screen.getByRole("alert").textContent).toContain("wrong");
|
|
90
90
|
});
|
|
91
|
-
expect(screen.queryByText("
|
|
91
|
+
expect(screen.queryByText("Email sent")).toBeNull();
|
|
92
92
|
});
|
|
93
93
|
});
|
|
@@ -21,7 +21,7 @@ import { SessionContext } from "../session";
|
|
|
21
21
|
// ihn pro Mount sonst neu konstruiert (~0.5ms × N Tests). Tests die
|
|
22
22
|
// einen *anderen* Locale brauchen, übergeben ihren eigenen Resolver
|
|
23
23
|
// über options.resolver.
|
|
24
|
-
const
|
|
24
|
+
const sharedEnResolver = createStaticLocaleResolver({ locale: "en" });
|
|
25
25
|
export type MakeSessionApiOptions = Partial<SessionState> & {
|
|
26
26
|
readonly login?: SessionApi["login"];
|
|
27
27
|
readonly logout?: SessionApi["logout"];
|
|
@@ -66,7 +66,7 @@ export function renderWithProviders(
|
|
|
66
66
|
readonly session?: SessionApi;
|
|
67
67
|
} = {},
|
|
68
68
|
): RenderResult & { readonly session: SessionApi } {
|
|
69
|
-
const resolver = options.resolver ??
|
|
69
|
+
const resolver = options.resolver ?? sharedEnResolver;
|
|
70
70
|
const session = options.session ?? makeSessionApi();
|
|
71
71
|
const result = _render(
|
|
72
72
|
<PrimitivesProvider value={defaultPrimitives}>
|
|
@@ -49,7 +49,7 @@ describe("UserMenu", () => {
|
|
|
49
49
|
const session = makeSessionApi();
|
|
50
50
|
renderWithProviders(<UserMenu />, { session });
|
|
51
51
|
await user.click(screen.getByRole("button", { name: /Test User/ }));
|
|
52
|
-
expect(screen.getByText("
|
|
52
|
+
expect(screen.getByText("Sign out")).toBeTruthy();
|
|
53
53
|
expect(screen.getByText("user@example.com")).toBeTruthy();
|
|
54
54
|
});
|
|
55
55
|
test("logout-click triggers session.logout", async () => {
|
|
@@ -57,7 +57,7 @@ describe("UserMenu", () => {
|
|
|
57
57
|
const session = makeSessionApi();
|
|
58
58
|
renderWithProviders(<UserMenu />, { session });
|
|
59
59
|
await user.click(screen.getByRole("button", { name: /Test User/ }));
|
|
60
|
-
await user.click(screen.getByText("
|
|
60
|
+
await user.click(screen.getByText("Sign out"));
|
|
61
61
|
expect(session.logout).toHaveBeenCalledTimes(1);
|
|
62
62
|
});
|
|
63
63
|
test("sidebar variant: NavUser-Row zeigt Name + Email, Dropdown trägt Logout", async () => {
|
|
@@ -75,6 +75,6 @@ describe("UserMenu", () => {
|
|
|
75
75
|
expect(screen.getByText("Alice Wonder")).toBeTruthy();
|
|
76
76
|
expect(screen.getByText("alice@example.com")).toBeTruthy();
|
|
77
77
|
await user.click(screen.getByRole("button", { name: /Alice Wonder/ }));
|
|
78
|
-
expect(screen.getByText("
|
|
78
|
+
expect(screen.getByText("Sign out")).toBeTruthy();
|
|
79
79
|
});
|
|
80
80
|
});
|
|
@@ -9,7 +9,7 @@ installFetchMock();
|
|
|
9
9
|
describe("VerifyEmailScreen", () => {
|
|
10
10
|
test("ohne Token → missing-token-Page", () => {
|
|
11
11
|
renderWithProviders(<VerifyEmailScreen />);
|
|
12
|
-
expect(screen.getByText(/
|
|
12
|
+
expect(screen.getByText(/missing a token/i)).toBeTruthy();
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
test("mit Token + 200 → success-state nach auto-submit", async () => {
|
|
@@ -26,7 +26,7 @@ describe("VerifyEmailScreen", () => {
|
|
|
26
26
|
body: JSON.stringify({ token: "t-abc" }),
|
|
27
27
|
}),
|
|
28
28
|
);
|
|
29
|
-
expect(screen.getByText("
|
|
29
|
+
expect(screen.getByText("Email verified")).toBeTruthy();
|
|
30
30
|
});
|
|
31
31
|
});
|
|
32
32
|
|
|
@@ -44,7 +44,7 @@ describe("VerifyEmailScreen", () => {
|
|
|
44
44
|
renderWithProviders(<VerifyEmailScreen token="bad" />);
|
|
45
45
|
|
|
46
46
|
await waitFor(() => {
|
|
47
|
-
expect(screen.getByText("
|
|
47
|
+
expect(screen.getByText("Verification failed")).toBeTruthy();
|
|
48
48
|
});
|
|
49
49
|
});
|
|
50
50
|
});
|
|
@@ -157,7 +157,9 @@ export function InviteAcceptScreen({
|
|
|
157
157
|
|
|
158
158
|
{mode === "loggedin" ? (
|
|
159
159
|
<Form onSubmit={onSubmit}>
|
|
160
|
-
<p className="text-sm">
|
|
160
|
+
<p className="text-sm">
|
|
161
|
+
{t("auth.inviteAccept.loggedInAs", { email: session.user?.email ?? "" })}
|
|
162
|
+
</p>
|
|
161
163
|
{error !== null && <Banner variant="error">{error}</Banner>}
|
|
162
164
|
<Button type="submit" loading={submitting} disabled={submitting}>
|
|
163
165
|
{submitting ? t("auth.inviteAccept.submitting") : t("auth.inviteAccept.acceptButton")}
|
|
@@ -22,18 +22,16 @@ const failures = {
|
|
|
22
22
|
|
|
23
23
|
describe("auth-mfa errors.ts i18nKey / client-bundle parity", () => {
|
|
24
24
|
for (const [name, failure] of Object.entries(failures)) {
|
|
25
|
-
test(`${name}'s i18nKey is registered in en
|
|
25
|
+
test(`${name}'s i18nKey is registered in en translations`, () => {
|
|
26
26
|
const key = failure.error.i18nKey;
|
|
27
27
|
expect(defaultTranslations["en"]?.[key]).toBeDefined();
|
|
28
|
-
expect(defaultTranslations["de"]?.[key]).toBeDefined();
|
|
29
28
|
});
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
// Client-only key: mfa-enable-screen.tsx sets this directly (not via a
|
|
33
32
|
// DispatcherError), so it isn't covered by the failures loop above.
|
|
34
|
-
test("client-only auth.mfa.errors.setupFailed is registered in en
|
|
33
|
+
test("client-only auth.mfa.errors.setupFailed is registered in en translations", () => {
|
|
35
34
|
const key = "auth.mfa.errors.setupFailed";
|
|
36
35
|
expect(defaultTranslations["en"]?.[key]).toBeDefined();
|
|
37
|
-
expect(defaultTranslations["de"]?.[key]).toBeDefined();
|
|
38
36
|
});
|
|
39
37
|
});
|
package/src/auth-mfa/i18n.ts
CHANGED
|
@@ -2,12 +2,10 @@
|
|
|
2
2
|
// engine requires every registered r.screen to declare. Distinct from
|
|
3
3
|
// web/i18n.ts (the client's free-form UI-string bundle) — see
|
|
4
4
|
// personal-access-tokens/i18n.ts for the same split.
|
|
5
|
-
type LocalizedString = { readonly
|
|
5
|
+
type LocalizedString = { readonly en: string };
|
|
6
6
|
|
|
7
7
|
export const AUTH_MFA_FEATURE_I18N: Readonly<Record<string, LocalizedString>> = {
|
|
8
8
|
"screen:auth-mfa-enable.title": {
|
|
9
|
-
de: "Zwei-Faktor-Authentifizierung",
|
|
10
9
|
en: "Two-factor authentication",
|
|
11
|
-
es: "Verificación en dos pasos",
|
|
12
10
|
},
|
|
13
11
|
};
|
|
@@ -51,7 +51,7 @@ function Wrapper({ children }: { readonly children: ReactNode }): ReactNode {
|
|
|
51
51
|
return (
|
|
52
52
|
<PrimitivesProvider value={defaultPrimitives}>
|
|
53
53
|
<LocaleProvider
|
|
54
|
-
resolver={createStaticLocaleResolver({ locale: "
|
|
54
|
+
resolver={createStaticLocaleResolver({ locale: "en" })}
|
|
55
55
|
fallbackBundles={[defaultTranslations]}
|
|
56
56
|
>
|
|
57
57
|
<SessionContext.Provider value={session}>{children}</SessionContext.Provider>
|
|
@@ -71,8 +71,8 @@ describe("MfaEnableScreen", () => {
|
|
|
71
71
|
<MfaEnableScreen />
|
|
72
72
|
</Wrapper>,
|
|
73
73
|
);
|
|
74
|
-
expect(screen.getByText("
|
|
75
|
-
expect(screen.getByRole("button", { name: "
|
|
74
|
+
expect(screen.getByText("Two-factor authentication")).toBeTruthy();
|
|
75
|
+
expect(screen.getByRole("button", { name: "Start setup" })).toBeTruthy();
|
|
76
76
|
expect(screen.queryByLabelText("Code aus der Authenticator-App")).toBeNull();
|
|
77
77
|
});
|
|
78
78
|
|
|
@@ -83,7 +83,7 @@ describe("MfaEnableScreen", () => {
|
|
|
83
83
|
</Wrapper>,
|
|
84
84
|
);
|
|
85
85
|
|
|
86
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
86
|
+
fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
|
|
87
87
|
|
|
88
88
|
await waitFor(() => {
|
|
89
89
|
expect(dispatchSpy).toHaveBeenCalledWith(
|
|
@@ -104,18 +104,18 @@ describe("MfaEnableScreen", () => {
|
|
|
104
104
|
<MfaEnableScreen />
|
|
105
105
|
</Wrapper>,
|
|
106
106
|
);
|
|
107
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
107
|
+
fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
|
|
108
108
|
await waitFor(() => {
|
|
109
109
|
expect(screen.getByText("ABCD1234")).toBeTruthy();
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
const confirm = screen.getByRole("button", { name: "
|
|
112
|
+
const confirm = screen.getByRole("button", { name: "Enable" }) as HTMLButtonElement;
|
|
113
113
|
expect(confirm.disabled).toBe(true);
|
|
114
114
|
|
|
115
|
-
fireEvent.click(screen.getByLabelText("
|
|
115
|
+
fireEvent.click(screen.getByLabelText("I've saved my recovery codes."));
|
|
116
116
|
expect(confirm.disabled).toBe(true);
|
|
117
117
|
|
|
118
|
-
fireEvent.change(screen.getByLabelText(/^Code
|
|
118
|
+
fireEvent.change(screen.getByLabelText(/^Code from your authenticator app/), {
|
|
119
119
|
target: { value: "123456" },
|
|
120
120
|
});
|
|
121
121
|
expect(confirm.disabled).toBe(false);
|
|
@@ -138,16 +138,16 @@ describe("MfaEnableScreen", () => {
|
|
|
138
138
|
<MfaEnableScreen onEnabled={onEnabled} />
|
|
139
139
|
</Wrapper>,
|
|
140
140
|
);
|
|
141
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
141
|
+
fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
|
|
142
142
|
await waitFor(() => {
|
|
143
143
|
expect(screen.getByText("ABCD1234")).toBeTruthy();
|
|
144
144
|
});
|
|
145
|
-
fireEvent.click(screen.getByLabelText("
|
|
146
|
-
fireEvent.change(screen.getByLabelText(/^Code
|
|
145
|
+
fireEvent.click(screen.getByLabelText("I've saved my recovery codes."));
|
|
146
|
+
fireEvent.change(screen.getByLabelText(/^Code from your authenticator app/), {
|
|
147
147
|
target: { value: "123456" },
|
|
148
148
|
});
|
|
149
149
|
|
|
150
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
150
|
+
fireEvent.click(screen.getByRole("button", { name: "Enable" }));
|
|
151
151
|
|
|
152
152
|
await waitFor(() => {
|
|
153
153
|
expect(dispatchSpy).toHaveBeenCalledWith(
|
|
@@ -156,7 +156,7 @@ describe("MfaEnableScreen", () => {
|
|
|
156
156
|
);
|
|
157
157
|
});
|
|
158
158
|
await waitFor(() => {
|
|
159
|
-
expect(screen.getByText("
|
|
159
|
+
expect(screen.getByText("Two-factor authentication is now enabled.")).toBeTruthy();
|
|
160
160
|
expect(onEnabled).toHaveBeenCalled();
|
|
161
161
|
});
|
|
162
162
|
});
|
|
@@ -172,10 +172,10 @@ describe("MfaEnableScreen", () => {
|
|
|
172
172
|
</Wrapper>,
|
|
173
173
|
);
|
|
174
174
|
|
|
175
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
175
|
+
fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
|
|
176
176
|
|
|
177
177
|
await waitFor(() => {
|
|
178
|
-
expect(screen.getByText("
|
|
178
|
+
expect(screen.getByText("Two-factor authentication is already enabled.")).toBeTruthy();
|
|
179
179
|
});
|
|
180
180
|
expect(screen.queryByText("ABCD1234")).toBeNull();
|
|
181
181
|
});
|
|
@@ -19,7 +19,7 @@ function Wrapper({ children }: { readonly children: ReactNode }): ReactNode {
|
|
|
19
19
|
return (
|
|
20
20
|
<PrimitivesProvider value={defaultPrimitives}>
|
|
21
21
|
<LocaleProvider
|
|
22
|
-
resolver={createStaticLocaleResolver({ locale: "
|
|
22
|
+
resolver={createStaticLocaleResolver({ locale: "en" })}
|
|
23
23
|
fallbackBundles={[defaultTranslations]}
|
|
24
24
|
>
|
|
25
25
|
{children}
|
|
@@ -60,9 +60,9 @@ describe("MfaSetupPreauthScreen", () => {
|
|
|
60
60
|
<MfaSetupPreauthScreen preauthSetupToken="preauth-1" accountLabel="user@example.com" />
|
|
61
61
|
</Wrapper>,
|
|
62
62
|
);
|
|
63
|
-
expect(screen.getByText("
|
|
64
|
-
expect(screen.getByRole("button", { name: "
|
|
65
|
-
expect(screen.queryByLabelText(/^Code
|
|
63
|
+
expect(screen.getByText("Two-factor authentication required")).toBeTruthy();
|
|
64
|
+
expect(screen.getByRole("button", { name: "Start setup" })).toBeTruthy();
|
|
65
|
+
expect(screen.queryByLabelText(/^Code from your authenticator app/)).toBeNull();
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
test("Start-Klick → postet preauthSetupToken+accountLabel, danach QR + Recovery-Codes", async () => {
|
|
@@ -74,7 +74,7 @@ describe("MfaSetupPreauthScreen", () => {
|
|
|
74
74
|
</Wrapper>,
|
|
75
75
|
);
|
|
76
76
|
|
|
77
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
77
|
+
fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
|
|
78
78
|
|
|
79
79
|
await waitFor(() => {
|
|
80
80
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
@@ -101,20 +101,20 @@ describe("MfaSetupPreauthScreen", () => {
|
|
|
101
101
|
<MfaSetupPreauthScreen preauthSetupToken="preauth-1" accountLabel="user@example.com" />
|
|
102
102
|
</Wrapper>,
|
|
103
103
|
);
|
|
104
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
104
|
+
fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
|
|
105
105
|
await waitFor(() => {
|
|
106
106
|
expect(screen.getByText("ABCD1234")).toBeTruthy();
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
const confirm = screen.getByRole("button", {
|
|
110
|
-
name: "
|
|
110
|
+
name: "Complete setup",
|
|
111
111
|
}) as HTMLButtonElement;
|
|
112
112
|
expect(confirm.disabled).toBe(true);
|
|
113
113
|
|
|
114
|
-
fireEvent.click(screen.getByLabelText("
|
|
114
|
+
fireEvent.click(screen.getByLabelText("I've saved my recovery codes."));
|
|
115
115
|
expect(confirm.disabled).toBe(true);
|
|
116
116
|
|
|
117
|
-
fireEvent.change(screen.getByLabelText(/^Code
|
|
117
|
+
fireEvent.change(screen.getByLabelText(/^Code from your authenticator app/), {
|
|
118
118
|
target: { value: "123456" },
|
|
119
119
|
});
|
|
120
120
|
expect(confirm.disabled).toBe(false);
|
|
@@ -131,17 +131,17 @@ describe("MfaSetupPreauthScreen", () => {
|
|
|
131
131
|
/>
|
|
132
132
|
</Wrapper>,
|
|
133
133
|
);
|
|
134
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
134
|
+
fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
|
|
135
135
|
await waitFor(() => {
|
|
136
136
|
expect(screen.getByText("ABCD1234")).toBeTruthy();
|
|
137
137
|
});
|
|
138
|
-
fireEvent.click(screen.getByLabelText("
|
|
139
|
-
fireEvent.change(screen.getByLabelText(/^Code
|
|
138
|
+
fireEvent.click(screen.getByLabelText("I've saved my recovery codes."));
|
|
139
|
+
fireEvent.change(screen.getByLabelText(/^Code from your authenticator app/), {
|
|
140
140
|
target: { value: "123456" },
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
const fetchMock = globalThis.fetch as unknown as ReturnType<typeof mock>;
|
|
144
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
144
|
+
fireEvent.click(screen.getByRole("button", { name: "Complete setup" }));
|
|
145
145
|
|
|
146
146
|
await waitFor(() => {
|
|
147
147
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
@@ -167,15 +167,15 @@ describe("MfaSetupPreauthScreen", () => {
|
|
|
167
167
|
</Wrapper>,
|
|
168
168
|
);
|
|
169
169
|
|
|
170
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
170
|
+
fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
|
|
171
171
|
|
|
172
172
|
await waitFor(() => {
|
|
173
|
-
expect(screen.getByText("
|
|
173
|
+
expect(screen.getByText("Two-factor authentication is already enabled.")).toBeTruthy();
|
|
174
174
|
});
|
|
175
175
|
expect(screen.queryByText("ABCD1234")).toBeNull();
|
|
176
176
|
});
|
|
177
177
|
|
|
178
|
-
test("onCancel gesetzt → '
|
|
178
|
+
test("onCancel gesetzt → 'Back to login'-Button vorhanden, klick ruft onCancel", () => {
|
|
179
179
|
const onCancel = mock(() => {});
|
|
180
180
|
render(
|
|
181
181
|
<Wrapper>
|
|
@@ -187,17 +187,17 @@ describe("MfaSetupPreauthScreen", () => {
|
|
|
187
187
|
</Wrapper>,
|
|
188
188
|
);
|
|
189
189
|
|
|
190
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
190
|
+
fireEvent.click(screen.getByRole("button", { name: "Back to login" }));
|
|
191
191
|
expect(onCancel).toHaveBeenCalled();
|
|
192
192
|
});
|
|
193
193
|
|
|
194
|
-
test("ohne onCancel → kein '
|
|
194
|
+
test("ohne onCancel → kein 'Back to login'-Button", () => {
|
|
195
195
|
render(
|
|
196
196
|
<Wrapper>
|
|
197
197
|
<MfaSetupPreauthScreen preauthSetupToken="preauth-1" accountLabel="user@example.com" />
|
|
198
198
|
</Wrapper>,
|
|
199
199
|
);
|
|
200
|
-
expect(screen.queryByRole("button", { name: "
|
|
200
|
+
expect(screen.queryByRole("button", { name: "Back to login" })).toBeNull();
|
|
201
201
|
});
|
|
202
202
|
|
|
203
203
|
test("confirm schlägt mit invalid_totp_code fehl → übersetztes Banner, onSuccess NICHT gefeuert, Formular weiter bedienbar", async () => {
|
|
@@ -220,24 +220,24 @@ describe("MfaSetupPreauthScreen", () => {
|
|
|
220
220
|
/>
|
|
221
221
|
</Wrapper>,
|
|
222
222
|
);
|
|
223
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
223
|
+
fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
|
|
224
224
|
await waitFor(() => {
|
|
225
225
|
expect(screen.getByText("ABCD1234")).toBeTruthy();
|
|
226
226
|
});
|
|
227
|
-
fireEvent.click(screen.getByLabelText("
|
|
228
|
-
fireEvent.change(screen.getByLabelText(/^Code
|
|
227
|
+
fireEvent.click(screen.getByLabelText("I've saved my recovery codes."));
|
|
228
|
+
fireEvent.change(screen.getByLabelText(/^Code from your authenticator app/), {
|
|
229
229
|
target: { value: "000000" },
|
|
230
230
|
});
|
|
231
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
231
|
+
fireEvent.click(screen.getByRole("button", { name: "Complete setup" }));
|
|
232
232
|
|
|
233
233
|
await waitFor(() => {
|
|
234
|
-
expect(screen.getByText("
|
|
234
|
+
expect(screen.getByText("Invalid code. Please try again.")).toBeTruthy();
|
|
235
235
|
});
|
|
236
236
|
expect(onSuccess).not.toHaveBeenCalled();
|
|
237
237
|
// Form stays usable — QR/recovery section is still there, a retry is possible.
|
|
238
238
|
expect(screen.getByText("ABCD1234")).toBeTruthy();
|
|
239
239
|
const confirm = screen.getByRole("button", {
|
|
240
|
-
name: "
|
|
240
|
+
name: "Complete setup",
|
|
241
241
|
}) as HTMLButtonElement;
|
|
242
242
|
expect(confirm.disabled).toBe(false);
|
|
243
243
|
});
|
|
@@ -261,24 +261,22 @@ describe("MfaSetupPreauthScreen", () => {
|
|
|
261
261
|
<MfaSetupPreauthScreen preauthSetupToken="preauth-1" accountLabel="user@example.com" />
|
|
262
262
|
</Wrapper>,
|
|
263
263
|
);
|
|
264
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
264
|
+
fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
|
|
265
265
|
await waitFor(() => {
|
|
266
266
|
expect(screen.getByText("ABCD1234")).toBeTruthy();
|
|
267
267
|
});
|
|
268
|
-
fireEvent.click(screen.getByLabelText("
|
|
269
|
-
fireEvent.change(screen.getByLabelText(/^Code
|
|
268
|
+
fireEvent.click(screen.getByLabelText("I've saved my recovery codes."));
|
|
269
|
+
fireEvent.change(screen.getByLabelText(/^Code from your authenticator app/), {
|
|
270
270
|
target: { value: "123456" },
|
|
271
271
|
});
|
|
272
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
272
|
+
fireEvent.click(screen.getByRole("button", { name: "Complete setup" }));
|
|
273
273
|
|
|
274
274
|
await waitFor(() => {
|
|
275
|
-
expect(
|
|
276
|
-
screen.getByText("Die Einrichtung ist abgelaufen. Bitte erneut starten."),
|
|
277
|
-
).toBeTruthy();
|
|
275
|
+
expect(screen.getByText("Setup expired. Please start again.")).toBeTruthy();
|
|
278
276
|
});
|
|
279
277
|
// The actual fix: intro re-appears with a working start button, the QR/
|
|
280
278
|
// recovery-codes section from the dead setup is gone.
|
|
281
|
-
expect(screen.getByRole("button", { name: "
|
|
279
|
+
expect(screen.getByRole("button", { name: "Start setup" })).toBeTruthy();
|
|
282
280
|
expect(screen.queryByText("ABCD1234")).toBeNull();
|
|
283
281
|
});
|
|
284
282
|
|
|
@@ -297,18 +295,18 @@ describe("MfaSetupPreauthScreen", () => {
|
|
|
297
295
|
<MfaSetupPreauthScreen preauthSetupToken="preauth-1" accountLabel="user@example.com" />
|
|
298
296
|
</Wrapper>,
|
|
299
297
|
);
|
|
300
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
298
|
+
fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
|
|
301
299
|
await waitFor(() => {
|
|
302
300
|
expect(screen.getByText("ABCD1234")).toBeTruthy();
|
|
303
301
|
});
|
|
304
|
-
fireEvent.click(screen.getByLabelText("
|
|
305
|
-
fireEvent.change(screen.getByLabelText(/^Code
|
|
302
|
+
fireEvent.click(screen.getByLabelText("I've saved my recovery codes."));
|
|
303
|
+
fireEvent.change(screen.getByLabelText(/^Code from your authenticator app/), {
|
|
306
304
|
target: { value: "123456" },
|
|
307
305
|
});
|
|
308
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
306
|
+
fireEvent.click(screen.getByRole("button", { name: "Complete setup" }));
|
|
309
307
|
|
|
310
308
|
await waitFor(() => {
|
|
311
|
-
expect(screen.getByRole("button", { name: "
|
|
309
|
+
expect(screen.getByRole("button", { name: "Start setup" })).toBeTruthy();
|
|
312
310
|
});
|
|
313
311
|
});
|
|
314
312
|
|
|
@@ -334,21 +332,19 @@ describe("MfaSetupPreauthScreen", () => {
|
|
|
334
332
|
<MfaSetupPreauthScreen preauthSetupToken="preauth-1" accountLabel="user@example.com" />
|
|
335
333
|
</Wrapper>,
|
|
336
334
|
);
|
|
337
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
335
|
+
fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
|
|
338
336
|
await waitFor(() => {
|
|
339
337
|
expect(screen.getByText("ABCD1234")).toBeTruthy();
|
|
340
338
|
});
|
|
341
|
-
fireEvent.click(screen.getByLabelText("
|
|
342
|
-
fireEvent.change(screen.getByLabelText(/^Code
|
|
339
|
+
fireEvent.click(screen.getByLabelText("I've saved my recovery codes."));
|
|
340
|
+
fireEvent.change(screen.getByLabelText(/^Code from your authenticator app/), {
|
|
343
341
|
target: { value: "123456" },
|
|
344
342
|
});
|
|
345
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
343
|
+
fireEvent.click(screen.getByRole("button", { name: "Complete setup" }));
|
|
346
344
|
|
|
347
345
|
await waitFor(() => {
|
|
348
346
|
expect(
|
|
349
|
-
screen.getByText(
|
|
350
|
-
"Zu viele Fehlversuche. Bitte in 42 Sekunden erneut versuchen oder neu einloggen.",
|
|
351
|
-
),
|
|
347
|
+
screen.getByText("Too many failed attempts. Try again in 42 seconds, or sign in again."),
|
|
352
348
|
).toBeTruthy();
|
|
353
349
|
});
|
|
354
350
|
});
|
|
@@ -362,17 +358,17 @@ describe("MfaSetupPreauthScreen", () => {
|
|
|
362
358
|
<MfaSetupPreauthScreen preauthSetupToken="preauth-1" accountLabel="user@example.com" />
|
|
363
359
|
</Wrapper>,
|
|
364
360
|
);
|
|
365
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
361
|
+
fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
|
|
366
362
|
await waitFor(() => {
|
|
367
363
|
expect(screen.getByText("ABCD1234")).toBeTruthy();
|
|
368
364
|
});
|
|
369
|
-
fireEvent.click(screen.getByLabelText("
|
|
370
|
-
fireEvent.change(screen.getByLabelText(/^Code
|
|
365
|
+
fireEvent.click(screen.getByLabelText("I've saved my recovery codes."));
|
|
366
|
+
fireEvent.change(screen.getByLabelText(/^Code from your authenticator app/), {
|
|
371
367
|
target: { value: "1234567" },
|
|
372
368
|
});
|
|
373
369
|
|
|
374
370
|
const confirm = screen.getByRole("button", {
|
|
375
|
-
name: "
|
|
371
|
+
name: "Complete setup",
|
|
376
372
|
}) as HTMLButtonElement;
|
|
377
373
|
expect(confirm.disabled).toBe(true);
|
|
378
374
|
});
|
|
@@ -17,7 +17,7 @@ function renderScreen(ui: ReactElement, session = makeSessionApi()): RenderResul
|
|
|
17
17
|
return render(
|
|
18
18
|
<PrimitivesProvider value={defaultPrimitives}>
|
|
19
19
|
<LocaleProvider
|
|
20
|
-
resolver={createStaticLocaleResolver({ locale: "
|
|
20
|
+
resolver={createStaticLocaleResolver({ locale: "en" })}
|
|
21
21
|
fallbackBundles={[defaultTranslations]}
|
|
22
22
|
>
|
|
23
23
|
<SessionContext.Provider value={session}>{ui}</SessionContext.Provider>
|
|
@@ -31,9 +31,9 @@ installFetchMock();
|
|
|
31
31
|
describe("MfaVerifyScreen", () => {
|
|
32
32
|
test("rendert Titel + Code-Feld + Submit-Button (de)", () => {
|
|
33
33
|
void renderScreen(<MfaVerifyScreen challengeToken="challenge-1" />);
|
|
34
|
-
expect(screen.getByText("
|
|
34
|
+
expect(screen.getByText("Two-factor verification")).toBeTruthy();
|
|
35
35
|
expect(screen.getByLabelText(/^Code/)).toBeTruthy();
|
|
36
|
-
expect(screen.getByRole("button", { name: "
|
|
36
|
+
expect(screen.getByRole("button", { name: "Verify" })).toBeTruthy();
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
test("richtiger Code → ruft session.refresh() + onSuccess()", async () => {
|
|
@@ -56,7 +56,7 @@ describe("MfaVerifyScreen", () => {
|
|
|
56
56
|
);
|
|
57
57
|
|
|
58
58
|
fireEvent.change(screen.getByLabelText(/^Code/), { target: { value: "123456" } });
|
|
59
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
59
|
+
fireEvent.click(screen.getByRole("button", { name: "Verify" }));
|
|
60
60
|
|
|
61
61
|
await waitFor(() => {
|
|
62
62
|
expect(session.refresh).toHaveBeenCalled();
|
|
@@ -74,23 +74,23 @@ describe("MfaVerifyScreen", () => {
|
|
|
74
74
|
void renderScreen(<MfaVerifyScreen challengeToken="challenge-1" />);
|
|
75
75
|
|
|
76
76
|
fireEvent.change(screen.getByLabelText(/^Code/), { target: { value: "000000" } });
|
|
77
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
77
|
+
fireEvent.click(screen.getByRole("button", { name: "Verify" }));
|
|
78
78
|
|
|
79
79
|
await waitFor(() => {
|
|
80
|
-
expect(screen.getByText("
|
|
80
|
+
expect(screen.getByText("Invalid code. Please try again.")).toBeTruthy();
|
|
81
81
|
});
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
test("onCancel gesetzt → '
|
|
84
|
+
test("onCancel gesetzt → 'Back to login'-Button vorhanden, klick ruft onCancel", () => {
|
|
85
85
|
const onCancel = mock(() => {});
|
|
86
86
|
void renderScreen(<MfaVerifyScreen challengeToken="challenge-1" onCancel={onCancel} />);
|
|
87
87
|
|
|
88
|
-
fireEvent.click(screen.getByRole("button", { name: "
|
|
88
|
+
fireEvent.click(screen.getByRole("button", { name: "Back to login" }));
|
|
89
89
|
expect(onCancel).toHaveBeenCalled();
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
-
test("ohne onCancel → kein '
|
|
92
|
+
test("ohne onCancel → kein 'Back to login'-Button", () => {
|
|
93
93
|
void renderScreen(<MfaVerifyScreen challengeToken="challenge-1" />);
|
|
94
|
-
expect(screen.queryByRole("button", { name: "
|
|
94
|
+
expect(screen.queryByRole("button", { name: "Back to login" })).toBeNull();
|
|
95
95
|
});
|
|
96
96
|
});
|