@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.
Files changed (68) hide show
  1. package/package.json +9 -8
  2. package/src/admin-shell/__tests__/admin-shell.boot.test.ts +1 -1
  3. package/src/admin-shell/i18n.ts +17 -39
  4. package/src/admin-shell/web/i18n.ts +6 -11
  5. package/src/audit/i18n.ts +25 -41
  6. package/src/audit/web/i18n.ts +5 -9
  7. package/src/auth-email-password/__tests__/email-templates.test.ts +4 -0
  8. package/src/auth-email-password/email-templates.ts +97 -149
  9. package/src/auth-email-password/i18n.ts +6 -265
  10. package/src/auth-email-password/web/__tests__/confirm-account-unlock-screen.test.tsx +7 -7
  11. package/src/auth-email-password/web/__tests__/forgot-password-screen.test.tsx +14 -14
  12. package/src/auth-email-password/web/__tests__/invite-accept-screen.test.tsx +23 -25
  13. package/src/auth-email-password/web/__tests__/login-screen.test.tsx +60 -68
  14. package/src/auth-email-password/web/__tests__/request-account-unlock-screen.test.tsx +24 -24
  15. package/src/auth-email-password/web/__tests__/reset-password-screen.test.tsx +20 -20
  16. package/src/auth-email-password/web/__tests__/signup-complete-screen.test.tsx +9 -9
  17. package/src/auth-email-password/web/__tests__/signup-screen.test.tsx +11 -11
  18. package/src/auth-email-password/web/__tests__/test-utils.tsx +2 -2
  19. package/src/auth-email-password/web/__tests__/user-menu.test.tsx +3 -3
  20. package/src/auth-email-password/web/__tests__/verify-email-screen.test.tsx +3 -3
  21. package/src/auth-email-password/web/invite-accept-screen.tsx +3 -1
  22. package/src/auth-mfa/__tests__/errors-i18n-parity.test.ts +2 -4
  23. package/src/auth-mfa/i18n.ts +1 -3
  24. package/src/auth-mfa/web/__tests__/mfa-enable-screen.test.tsx +15 -15
  25. package/src/auth-mfa/web/__tests__/mfa-setup-preauth-screen.test.tsx +45 -49
  26. package/src/auth-mfa/web/__tests__/mfa-verify-screen.test.tsx +10 -10
  27. package/src/auth-mfa/web/i18n.ts +0 -126
  28. package/src/cap-counter/i18n.ts +5 -11
  29. package/src/compliance-profiles/i18n.ts +3 -11
  30. package/src/compliance-profiles/web/i18n.ts +0 -20
  31. package/src/config/i18n.ts +5 -5
  32. package/src/config/web/i18n.ts +1 -19
  33. package/src/custom-fields/web/i18n.ts +1 -18
  34. package/src/delivery/i18n.ts +3 -7
  35. package/src/delivery/web/i18n.ts +0 -20
  36. package/src/feature-toggles/i18n.ts +3 -11
  37. package/src/feature-toggles/web/i18n.ts +0 -24
  38. package/src/folders/web/i18n.ts +1 -51
  39. package/src/jobs/i18n.ts +40 -68
  40. package/src/jobs/web/i18n.ts +5 -9
  41. package/src/managed-pages/i18n.ts +23 -67
  42. package/src/managed-pages/web/i18n.ts +6 -11
  43. package/src/notes-history/i18n.ts +1 -1
  44. package/src/notes-history/web/i18n.ts +1 -17
  45. package/src/personal-access-tokens/i18n.ts +2 -6
  46. package/src/personal-access-tokens/web/i18n.ts +0 -72
  47. package/src/sessions/i18n.ts +17 -21
  48. package/src/tags/i18n.ts +2 -2
  49. package/src/tags/web/i18n.ts +1 -52
  50. package/src/template-resolver/web/__tests__/client-plugin.test.tsx +2 -2
  51. package/src/template-resolver/web/__tests__/editor-read-only.test.tsx +5 -5
  52. package/src/template-resolver/web/i18n.ts +0 -28
  53. package/src/tenant/__tests__/members-screens.boot.test.ts +0 -2
  54. package/src/tenant/i18n.ts +13 -27
  55. package/src/tenant/web/i18n.ts +0 -38
  56. package/src/tier-engine/i18n.ts +3 -39
  57. package/src/user/i18n.ts +14 -32
  58. package/src/user-data-rights/__tests__/default-mailers.test.ts +4 -0
  59. package/src/user-data-rights/__tests__/email-templates.test.ts +4 -0
  60. package/src/user-data-rights/__tests__/mail-default-bridge.integration.test.ts +2 -0
  61. package/src/user-data-rights/__tests__/run-export-jobs-cron-context.integration.test.ts +4 -0
  62. package/src/user-data-rights/email-templates.ts +55 -91
  63. package/src/user-data-rights/i18n.ts +18 -70
  64. package/src/user-data-rights/lib/default-mailers.ts +4 -1
  65. package/src/user-data-rights/web/__tests__/deletion-screens.test.tsx +11 -11
  66. package/src/user-data-rights/web/__tests__/public-deletion-gate.test.tsx +5 -5
  67. package/src/user-data-rights/web/i18n.ts +9 -157
  68. 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("Account erstellen")).toBeTruthy();
26
+ expect(screen.getByText("Create account")).toBeTruthy();
27
27
  expect(document.getElementById("signup-email")).toBeTruthy();
28
- expect(screen.getByRole("button", { name: "Aktivierungs-Link senden" })).toBeTruthy();
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: "Aktivierungs-Link senden" }));
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: "Aktivierungs-Link senden" }));
53
+ fireEvent.click(screen.getByRole("button", { name: "Send activation link" }));
54
54
 
55
55
  await waitFor(() => {
56
- expect(screen.getByText("Mail gesendet")).toBeTruthy();
56
+ expect(screen.getByText("Email sent")).toBeTruthy();
57
57
  });
58
- expect(screen.getByRole("button", { name: /Mail erneut senden/i })).toBeTruthy();
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: "Aktivierungs-Link senden" }));
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("Mail gesendet")).toBeNull();
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: "Aktivierungs-Link senden" }));
86
+ fireEvent.click(screen.getByRole("button", { name: "Send activation link" }));
87
87
 
88
88
  await waitFor(() => {
89
- expect(screen.getByRole("alert").textContent).toContain("schief");
89
+ expect(screen.getByRole("alert").textContent).toContain("wrong");
90
90
  });
91
- expect(screen.queryByText("Mail gesendet")).toBeNull();
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 sharedDeResolver = createStaticLocaleResolver({ locale: "de" });
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 ?? sharedDeResolver;
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("Abmelden")).toBeTruthy();
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("Abmelden"));
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("Abmelden")).toBeTruthy();
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(/enthält keinen Token/i)).toBeTruthy();
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("E-Mail bestätigt")).toBeTruthy();
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("Bestätigung fehlgeschlagen")).toBeTruthy();
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">{t("auth.inviteAccept.loggedInAs")}</p>
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/de translations`, () => {
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/de translations", () => {
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
  });
@@ -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 de: string; readonly en: string; readonly es: string };
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: "de" })}
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("Zwei-Faktor-Authentifizierung")).toBeTruthy();
75
- expect(screen.getByRole("button", { name: "Einrichtung starten" })).toBeTruthy();
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: "Einrichtung starten" }));
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: "Einrichtung starten" }));
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: "Aktivieren" }) as HTMLButtonElement;
112
+ const confirm = screen.getByRole("button", { name: "Enable" }) as HTMLButtonElement;
113
113
  expect(confirm.disabled).toBe(true);
114
114
 
115
- fireEvent.click(screen.getByLabelText("Ich habe die Recovery-Codes gespeichert."));
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 aus der Authenticator-App/), {
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: "Einrichtung starten" }));
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("Ich habe die Recovery-Codes gespeichert."));
146
- fireEvent.change(screen.getByLabelText(/^Code aus der Authenticator-App/), {
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: "Aktivieren" }));
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("Zwei-Faktor-Authentifizierung ist jetzt aktiv.")).toBeTruthy();
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: "Einrichtung starten" }));
175
+ fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
176
176
 
177
177
  await waitFor(() => {
178
- expect(screen.getByText("Zwei-Faktor-Authentifizierung ist bereits aktiv.")).toBeTruthy();
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: "de" })}
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("Zwei-Faktor-Authentifizierung erforderlich")).toBeTruthy();
64
- expect(screen.getByRole("button", { name: "Einrichtung starten" })).toBeTruthy();
65
- expect(screen.queryByLabelText(/^Code aus der Authenticator-App/)).toBeNull();
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: "Einrichtung starten" }));
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: "Einrichtung starten" }));
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: "Einrichtung abschließen",
110
+ name: "Complete setup",
111
111
  }) as HTMLButtonElement;
112
112
  expect(confirm.disabled).toBe(true);
113
113
 
114
- fireEvent.click(screen.getByLabelText("Ich habe die Recovery-Codes gespeichert."));
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 aus der Authenticator-App/), {
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: "Einrichtung starten" }));
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("Ich habe die Recovery-Codes gespeichert."));
139
- fireEvent.change(screen.getByLabelText(/^Code aus der Authenticator-App/), {
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: "Einrichtung abschließen" }));
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: "Einrichtung starten" }));
170
+ fireEvent.click(screen.getByRole("button", { name: "Start setup" }));
171
171
 
172
172
  await waitFor(() => {
173
- expect(screen.getByText("Zwei-Faktor-Authentifizierung ist bereits aktiv.")).toBeTruthy();
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 → 'Zurück zum Login'-Button vorhanden, klick ruft onCancel", () => {
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: "Zurück zum Login" }));
190
+ fireEvent.click(screen.getByRole("button", { name: "Back to login" }));
191
191
  expect(onCancel).toHaveBeenCalled();
192
192
  });
193
193
 
194
- test("ohne onCancel → kein 'Zurück zum Login'-Button", () => {
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: "Zurück zum Login" })).toBeNull();
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: "Einrichtung starten" }));
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("Ich habe die Recovery-Codes gespeichert."));
228
- fireEvent.change(screen.getByLabelText(/^Code aus der Authenticator-App/), {
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: "Einrichtung abschließen" }));
231
+ fireEvent.click(screen.getByRole("button", { name: "Complete setup" }));
232
232
 
233
233
  await waitFor(() => {
234
- expect(screen.getByText("Ungültiger Code. Bitte erneut versuchen.")).toBeTruthy();
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: "Einrichtung abschließen",
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: "Einrichtung starten" }));
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("Ich habe die Recovery-Codes gespeichert."));
269
- fireEvent.change(screen.getByLabelText(/^Code aus der Authenticator-App/), {
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: "Einrichtung abschließen" }));
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: "Einrichtung starten" })).toBeTruthy();
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: "Einrichtung starten" }));
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("Ich habe die Recovery-Codes gespeichert."));
305
- fireEvent.change(screen.getByLabelText(/^Code aus der Authenticator-App/), {
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: "Einrichtung abschließen" }));
306
+ fireEvent.click(screen.getByRole("button", { name: "Complete setup" }));
309
307
 
310
308
  await waitFor(() => {
311
- expect(screen.getByRole("button", { name: "Einrichtung starten" })).toBeTruthy();
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: "Einrichtung starten" }));
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("Ich habe die Recovery-Codes gespeichert."));
342
- fireEvent.change(screen.getByLabelText(/^Code aus der Authenticator-App/), {
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: "Einrichtung abschließen" }));
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: "Einrichtung starten" }));
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("Ich habe die Recovery-Codes gespeichert."));
370
- fireEvent.change(screen.getByLabelText(/^Code aus der Authenticator-App/), {
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: "Einrichtung abschließen",
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: "de" })}
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("Zwei-Faktor-Bestätigung")).toBeTruthy();
34
+ expect(screen.getByText("Two-factor verification")).toBeTruthy();
35
35
  expect(screen.getByLabelText(/^Code/)).toBeTruthy();
36
- expect(screen.getByRole("button", { name: "Bestätigen" })).toBeTruthy();
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: "Bestätigen" }));
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: "Bestätigen" }));
77
+ fireEvent.click(screen.getByRole("button", { name: "Verify" }));
78
78
 
79
79
  await waitFor(() => {
80
- expect(screen.getByText("Ungültiger Code. Bitte erneut versuchen.")).toBeTruthy();
80
+ expect(screen.getByText("Invalid code. Please try again.")).toBeTruthy();
81
81
  });
82
82
  });
83
83
 
84
- test("onCancel gesetzt → 'Zurück zum Login'-Button vorhanden, klick ruft onCancel", () => {
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: "Zurück zum Login" }));
88
+ fireEvent.click(screen.getByRole("button", { name: "Back to login" }));
89
89
  expect(onCancel).toHaveBeenCalled();
90
90
  });
91
91
 
92
- test("ohne onCancel → kein 'Zurück zum Login'-Button", () => {
92
+ test("ohne onCancel → kein 'Back to login'-Button", () => {
93
93
  void renderScreen(<MfaVerifyScreen challengeToken="challenge-1" />);
94
- expect(screen.queryByRole("button", { name: "Zurück zum Login" })).toBeNull();
94
+ expect(screen.queryByRole("button", { name: "Back to login" })).toBeNull();
95
95
  });
96
96
  });