@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
@@ -19,23 +19,23 @@ describe("LoginScreen", () => {
19
19
 
20
20
  test("renders translated title + email + password labels (de)", () => {
21
21
  renderWithProviders(<LoginScreen />);
22
- expect(screen.getByText("Anmelden")).toBeTruthy();
23
- expect(screen.getByLabelText(/^E-Mail/)).toBeTruthy();
24
- expect(screen.getByLabelText(/^Passwort/)).toBeTruthy();
25
- expect(screen.getByRole("button", { name: "Einloggen" })).toBeTruthy();
22
+ expect(screen.getByRole("heading", { name: "Sign in" })).toBeTruthy();
23
+ expect(screen.getByLabelText(/^Email/)).toBeTruthy();
24
+ expect(screen.getByLabelText(/^Password/)).toBeTruthy();
25
+ expect(screen.getByRole("button", { name: "Sign in" })).toBeTruthy();
26
26
  });
27
27
 
28
28
  test("submit calls session.login with form values", async () => {
29
29
  const session = makeSessionApi({ status: "unauthenticated", user: null });
30
30
  renderWithProviders(<LoginScreen />, { session });
31
31
 
32
- fireEvent.change(screen.getByLabelText(/^E-Mail/), {
32
+ fireEvent.change(screen.getByLabelText(/^Email/), {
33
33
  target: { value: "demo@example.com" },
34
34
  });
35
- fireEvent.change(screen.getByLabelText(/^Passwort/), {
35
+ fireEvent.change(screen.getByLabelText(/^Password/), {
36
36
  target: { value: "secret" },
37
37
  });
38
- fireEvent.click(screen.getByRole("button", { name: "Einloggen" }));
38
+ fireEvent.click(screen.getByRole("button", { name: "Sign in" }));
39
39
 
40
40
  await waitFor(() => {
41
41
  expect(session.login).toHaveBeenCalledWith({
@@ -56,17 +56,17 @@ describe("LoginScreen", () => {
56
56
  });
57
57
  renderWithProviders(<LoginScreen />, { session });
58
58
 
59
- fireEvent.change(screen.getByLabelText(/^E-Mail/), {
59
+ fireEvent.change(screen.getByLabelText(/^Email/), {
60
60
  target: { value: "wrong@example.com" },
61
61
  });
62
- fireEvent.change(screen.getByLabelText(/^Passwort/), {
62
+ fireEvent.change(screen.getByLabelText(/^Password/), {
63
63
  target: { value: "x" },
64
64
  });
65
- fireEvent.click(screen.getByRole("button", { name: "Einloggen" }));
65
+ fireEvent.click(screen.getByRole("button", { name: "Sign in" }));
66
66
 
67
67
  await waitFor(() => {
68
68
  expect(screen.getByRole("alert")).toBeTruthy();
69
- expect(screen.getByRole("alert").textContent).toBe("E-Mail oder Passwort falsch.");
69
+ expect(screen.getByRole("alert").textContent).toBe("Invalid email or password.");
70
70
  });
71
71
  });
72
72
 
@@ -81,30 +81,30 @@ describe("LoginScreen", () => {
81
81
  });
82
82
  renderWithProviders(<LoginScreen />, { session });
83
83
 
84
- fireEvent.change(screen.getByLabelText(/^E-Mail/), {
84
+ fireEvent.change(screen.getByLabelText(/^Email/), {
85
85
  target: { value: "x@example.com" },
86
86
  });
87
- fireEvent.change(screen.getByLabelText(/^Passwort/), {
87
+ fireEvent.change(screen.getByLabelText(/^Password/), {
88
88
  target: { value: "x" },
89
89
  });
90
- fireEvent.click(screen.getByRole("button", { name: "Einloggen" }));
90
+ fireEvent.click(screen.getByRole("button", { name: "Sign in" }));
91
91
 
92
92
  await waitFor(() => {
93
- // 540s → 9 Minuten (Math.ceil)
94
- expect(screen.getByRole("alert").textContent).toMatch(/9 Minuten/);
93
+ // 540s → 9 minutes (Math.ceil)
94
+ expect(screen.getByRole("alert").textContent).toMatch(/9 minutes/);
95
95
  });
96
96
  });
97
97
 
98
98
  test("forgotPasswordHref-prop → Link rendert mit korrektem href", () => {
99
99
  renderWithProviders(<LoginScreen forgotPasswordHref="/forgot-password" />);
100
- const link = screen.getByRole("link", { name: /Passwort vergessen/i });
100
+ const link = screen.getByRole("link", { name: /Forgot password/i });
101
101
  expect(link).toBeTruthy();
102
102
  expect(link.getAttribute("href")).toBe("/forgot-password");
103
103
  });
104
104
 
105
105
  test("ohne forgotPasswordHref → KEIN Link (Login bleibt minimalistisch)", () => {
106
106
  renderWithProviders(<LoginScreen />);
107
- expect(screen.queryByRole("link", { name: /Passwort vergessen/i })).toBeNull();
107
+ expect(screen.queryByRole("link", { name: /Forgot password/i })).toBeNull();
108
108
  });
109
109
 
110
110
  // Bug-Bash 2026-06-07 (Bug 1): Login-Screen ist oft die einzige
@@ -133,16 +133,16 @@ describe("LoginScreen", () => {
133
133
  });
134
134
 
135
135
  // Resend-Flow: bei email_not_verified bietet der LoginScreen einen
136
- // "Bestätigungs-Mail erneut senden"-Link im Fehler-Banner an.
136
+ // "Send verification email again"-Link im Fehler-Banner an.
137
137
  describe("resend verification on email_not_verified", () => {
138
138
  async function loginUntilEmailNotVerified(): Promise<void> {
139
- fireEvent.change(screen.getByLabelText(/^E-Mail/), {
139
+ fireEvent.change(screen.getByLabelText(/^Email/), {
140
140
  target: { value: "demo@example.com" },
141
141
  });
142
- fireEvent.change(screen.getByLabelText(/^Passwort/), {
142
+ fireEvent.change(screen.getByLabelText(/^Password/), {
143
143
  target: { value: "secret" },
144
144
  });
145
- fireEvent.click(screen.getByRole("button", { name: "Einloggen" }));
145
+ fireEvent.click(screen.getByRole("button", { name: "Sign in" }));
146
146
  await waitFor(() => {
147
147
  expect(screen.getByRole("alert")).toBeTruthy();
148
148
  });
@@ -164,7 +164,7 @@ describe("LoginScreen", () => {
164
164
  // Vor Submit gibt es keinen Resend-Trigger
165
165
  expect(screen.queryByRole("button", { name: /erneut senden/i })).toBeNull();
166
166
  await loginUntilEmailNotVerified();
167
- expect(screen.getByRole("button", { name: "Bestätigungs-Mail erneut senden" })).toBeTruthy();
167
+ expect(screen.getByRole("button", { name: "Send verification email again" })).toBeTruthy();
168
168
  });
169
169
 
170
170
  test("click → calls requestEmailVerification with form email and shows success banner", async () => {
@@ -172,16 +172,14 @@ describe("LoginScreen", () => {
172
172
  renderWithProviders(<LoginScreen />, { session: unverifiedSession() });
173
173
  await loginUntilEmailNotVerified();
174
174
 
175
- fireEvent.click(screen.getByRole("button", { name: "Bestätigungs-Mail erneut senden" }));
175
+ fireEvent.click(screen.getByRole("button", { name: "Send verification email again" }));
176
176
 
177
177
  await waitFor(() => {
178
178
  expect(requestEmailVerificationMock).toHaveBeenCalledWith("demo@example.com");
179
179
  });
180
180
  // Banner variant="info" setzt kein role — wir suchen per Text
181
181
  await waitFor(() => {
182
- expect(
183
- screen.getByText("Wir haben dir eine neue Bestätigungs-Mail geschickt."),
184
- ).toBeTruthy();
182
+ expect(screen.getByText("We've sent you a new verification email.")).toBeTruthy();
185
183
  });
186
184
  // Fehler-Banner (role=alert) ist weg, Success-Banner ist da
187
185
  expect(screen.queryByRole("alert")).toBeNull();
@@ -195,15 +193,13 @@ describe("LoginScreen", () => {
195
193
  renderWithProviders(<LoginScreen />, { session: unverifiedSession() });
196
194
  await loginUntilEmailNotVerified();
197
195
 
198
- fireEvent.click(screen.getByRole("button", { name: "Bestätigungs-Mail erneut senden" }));
196
+ fireEvent.click(screen.getByRole("button", { name: "Send verification email again" }));
199
197
 
200
198
  await waitFor(() => {
201
- expect(screen.getByRole("alert").textContent).toMatch(
202
- /Bitte warte kurz und versuche es erneut/,
203
- );
199
+ expect(screen.getByRole("alert").textContent).toMatch(/Please wait a moment and try again/);
204
200
  });
205
201
  // Original-Fehler bleibt sichtbar
206
- expect(screen.getByRole("alert").textContent).toMatch(/E-Mail-Adresse noch nicht bestätigt/);
202
+ expect(screen.getByRole("alert").textContent).toMatch(/Email address not yet verified/);
207
203
  });
208
204
 
209
205
  test("network/unknown error → generischer inline hint", async () => {
@@ -211,23 +207,21 @@ describe("LoginScreen", () => {
211
207
  renderWithProviders(<LoginScreen />, { session: unverifiedSession() });
212
208
  await loginUntilEmailNotVerified();
213
209
 
214
- fireEvent.click(screen.getByRole("button", { name: "Bestätigungs-Mail erneut senden" }));
210
+ fireEvent.click(screen.getByRole("button", { name: "Send verification email again" }));
215
211
 
216
212
  await waitFor(() => {
217
- expect(screen.getByRole("alert").textContent).toMatch(
218
- /Konnte nicht senden. Bitte erneut versuchen/,
219
- );
213
+ expect(screen.getByRole("alert").textContent).toMatch(/Could not send. Please try again/);
220
214
  });
221
215
  });
222
216
 
223
217
  test("Email-Änderung nach Failure → Resend-Button verschwindet (anti-typo-Falle)", async () => {
224
218
  renderWithProviders(<LoginScreen />, { session: unverifiedSession() });
225
219
  await loginUntilEmailNotVerified();
226
- expect(screen.getByRole("button", { name: "Bestätigungs-Mail erneut senden" })).toBeTruthy();
220
+ expect(screen.getByRole("button", { name: "Send verification email again" })).toBeTruthy();
227
221
 
228
222
  // User korrigiert die Email-Eingabe — Resend-Button darf nicht mehr
229
223
  // sichtbar sein, damit kein silent-send an typo-Adresse passiert
230
- fireEvent.change(screen.getByLabelText(/^E-Mail/), {
224
+ fireEvent.change(screen.getByLabelText(/^Email/), {
231
225
  target: { value: "typo@example.com" },
232
226
  });
233
227
  expect(screen.queryByRole("button", { name: /erneut senden/i })).toBeNull();
@@ -258,24 +252,24 @@ describe("LoginScreen", () => {
258
252
  })),
259
253
  });
260
254
  renderWithProviders(<LoginScreen unlockAccountHref="/unlock" />, { session });
261
- fireEvent.change(screen.getByLabelText(/^E-Mail/), { target: { value: "a@b.c" } });
262
- fireEvent.change(screen.getByLabelText(/^Passwort/), { target: { value: "x" } });
263
- fireEvent.click(screen.getByRole("button", { name: "Einloggen" }));
255
+ fireEvent.change(screen.getByLabelText(/^Email/), { target: { value: "a@b.c" } });
256
+ fireEvent.change(screen.getByLabelText(/^Password/), { target: { value: "x" } });
257
+ fireEvent.click(screen.getByRole("button", { name: "Sign in" }));
264
258
  await waitFor(() => {
265
- expect(screen.getByRole("alert").textContent).toContain("vorübergehend gesperrt");
259
+ expect(screen.getByRole("alert").textContent).toContain("temporarily locked");
266
260
  });
267
- expect(screen.getByRole("link", { name: /Konto entsperren/i }).getAttribute("href")).toBe(
261
+ expect(screen.getByRole("link", { name: /Unlock account/i }).getAttribute("href")).toBe(
268
262
  "/unlock",
269
263
  );
270
264
  });
271
265
 
272
266
  test("reasonToKey covers no_membership, rate_limited, invalid_body, default", async () => {
273
267
  const cases: Array<{ reason: string; needle: string }> = [
274
- { reason: "no_membership", needle: "keinen Tenant-Zugang" },
275
- { reason: "rate_limited", needle: "Zu viele Login-Versuche" },
276
- { reason: "invalid_body", needle: "Ungültige Eingabe" },
277
- { reason: "weird_unknown", needle: "Login fehlgeschlagen" },
278
- { reason: "mfa_setup_required", needle: "Zwei-Faktor-Authentifizierung erforderlich" },
268
+ { reason: "no_membership", needle: "no tenant access" },
269
+ { reason: "rate_limited", needle: "Too many login attempts" },
270
+ { reason: "invalid_body", needle: "Invalid input" },
271
+ { reason: "weird_unknown", needle: "Login failed" },
272
+ { reason: "mfa_setup_required", needle: "Two-factor authentication required" },
279
273
  ];
280
274
  for (const { reason, needle } of cases) {
281
275
  const session = makeSessionApi({
@@ -287,9 +281,9 @@ describe("LoginScreen", () => {
287
281
  })),
288
282
  });
289
283
  const { unmount } = renderWithProviders(<LoginScreen />, { session });
290
- fireEvent.change(screen.getByLabelText(/^E-Mail/), { target: { value: "a@b.c" } });
291
- fireEvent.change(screen.getByLabelText(/^Passwort/), { target: { value: "x" } });
292
- fireEvent.click(screen.getByRole("button", { name: "Einloggen" }));
284
+ fireEvent.change(screen.getByLabelText(/^Email/), { target: { value: "a@b.c" } });
285
+ fireEvent.change(screen.getByLabelText(/^Password/), { target: { value: "x" } });
286
+ fireEvent.click(screen.getByRole("button", { name: "Sign in" }));
293
287
  await waitFor(() => {
294
288
  expect(screen.getByRole("alert").textContent).toContain(needle);
295
289
  });
@@ -308,9 +302,9 @@ describe("LoginScreen", () => {
308
302
  })),
309
303
  });
310
304
  renderWithProviders(<LoginScreen onMfaChallenge={onMfaChallenge} />, { session });
311
- fireEvent.change(screen.getByLabelText(/^E-Mail/), { target: { value: "a@b.c" } });
312
- fireEvent.change(screen.getByLabelText(/^Passwort/), { target: { value: "x" } });
313
- fireEvent.click(screen.getByRole("button", { name: "Einloggen" }));
305
+ fireEvent.change(screen.getByLabelText(/^Email/), { target: { value: "a@b.c" } });
306
+ fireEvent.change(screen.getByLabelText(/^Password/), { target: { value: "x" } });
307
+ fireEvent.click(screen.getByRole("button", { name: "Sign in" }));
314
308
  await waitFor(() => {
315
309
  expect(onMfaChallenge).toHaveBeenCalledWith("chal-1");
316
310
  });
@@ -327,11 +321,11 @@ describe("LoginScreen", () => {
327
321
  })),
328
322
  });
329
323
  renderWithProviders(<LoginScreen />, { session });
330
- fireEvent.change(screen.getByLabelText(/^E-Mail/), { target: { value: "a@b.c" } });
331
- fireEvent.change(screen.getByLabelText(/^Passwort/), { target: { value: "x" } });
332
- fireEvent.click(screen.getByRole("button", { name: "Einloggen" }));
324
+ fireEvent.change(screen.getByLabelText(/^Email/), { target: { value: "a@b.c" } });
325
+ fireEvent.change(screen.getByLabelText(/^Password/), { target: { value: "x" } });
326
+ fireEvent.click(screen.getByRole("button", { name: "Sign in" }));
333
327
  await waitFor(() => {
334
- expect(screen.getByRole("alert").textContent).toContain("unterstützt keine Zwei-Faktor");
328
+ expect(screen.getByRole("alert").textContent).toContain("doesn't support two-factor");
335
329
  });
336
330
  });
337
331
 
@@ -349,9 +343,9 @@ describe("LoginScreen", () => {
349
343
  <LoginScreen onMfaSetupRequired={onMfaSetupRequired} />,
350
344
  { session: sessionOk },
351
345
  );
352
- fireEvent.change(screen.getByLabelText(/^E-Mail/), { target: { value: "a@b.c" } });
353
- fireEvent.change(screen.getByLabelText(/^Passwort/), { target: { value: "x" } });
354
- fireEvent.click(screen.getByRole("button", { name: "Einloggen" }));
346
+ fireEvent.change(screen.getByLabelText(/^Email/), { target: { value: "a@b.c" } });
347
+ fireEvent.change(screen.getByLabelText(/^Password/), { target: { value: "x" } });
348
+ fireEvent.click(screen.getByRole("button", { name: "Sign in" }));
355
349
  await waitFor(() => {
356
350
  expect(onMfaSetupRequired).toHaveBeenCalledWith("setup-token-value", "a@b.c");
357
351
  });
@@ -366,19 +360,17 @@ describe("LoginScreen", () => {
366
360
  })),
367
361
  });
368
362
  renderWithProviders(<LoginScreen />, { session: sessionBare });
369
- fireEvent.change(screen.getByLabelText(/^E-Mail/), { target: { value: "a@b.c" } });
370
- fireEvent.change(screen.getByLabelText(/^Passwort/), { target: { value: "x" } });
371
- fireEvent.click(screen.getByRole("button", { name: "Einloggen" }));
363
+ fireEvent.change(screen.getByLabelText(/^Email/), { target: { value: "a@b.c" } });
364
+ fireEvent.change(screen.getByLabelText(/^Password/), { target: { value: "x" } });
365
+ fireEvent.click(screen.getByRole("button", { name: "Sign in" }));
372
366
  await waitFor(() => {
373
- expect(screen.getByRole("alert").textContent).toContain(
374
- "Zwei-Faktor-Authentifizierung erforderlich",
375
- );
367
+ expect(screen.getByRole("alert").textContent).toContain("Two-factor authentication required");
376
368
  });
377
369
  });
378
370
 
379
371
  test("signupHref renders signup link", () => {
380
372
  renderWithProviders(<LoginScreen signupHref="/signup" />);
381
- const link = screen.getByRole("link", { name: "Account erstellen" });
373
+ const link = screen.getByRole("link", { name: "Create account" });
382
374
  expect(link.getAttribute("href")).toBe("/signup");
383
375
  });
384
376
  });
@@ -9,9 +9,9 @@ installFetchMock();
9
9
  describe("RequestAccountUnlockScreen", () => {
10
10
  test("renders title + email input + submit (de)", () => {
11
11
  renderWithProviders(<RequestAccountUnlockScreen />);
12
- expect(screen.getByText("Konto entsperren")).toBeTruthy();
13
- expect(screen.getByLabelText(/^E-Mail/)).toBeTruthy();
14
- expect(screen.getByRole("button", { name: "Link anfordern" })).toBeTruthy();
12
+ expect(screen.getByText("Unlock account")).toBeTruthy();
13
+ expect(screen.getByLabelText(/^Email/)).toBeTruthy();
14
+ expect(screen.getByRole("button", { name: "Request link" })).toBeTruthy();
15
15
  });
16
16
 
17
17
  test("submit posts /api/auth/request-account-unlock with email", async () => {
@@ -19,10 +19,10 @@ describe("RequestAccountUnlockScreen", () => {
19
19
  globalThis.fetch = fetchMock as unknown as typeof fetch;
20
20
 
21
21
  renderWithProviders(<RequestAccountUnlockScreen />);
22
- fireEvent.change(screen.getByLabelText(/^E-Mail/), {
22
+ fireEvent.change(screen.getByLabelText(/^Email/), {
23
23
  target: { value: "user@example.com" },
24
24
  });
25
- fireEvent.click(screen.getByRole("button", { name: "Link anfordern" }));
25
+ fireEvent.click(screen.getByRole("button", { name: "Request link" }));
26
26
 
27
27
  await waitFor(() => {
28
28
  expect(fetchMock).toHaveBeenCalledWith(
@@ -39,15 +39,15 @@ describe("RequestAccountUnlockScreen", () => {
39
39
 
40
40
  test("success → silent-success banner + back-to-login link", async () => {
41
41
  renderWithProviders(<RequestAccountUnlockScreen loginHref="/signin" />);
42
- fireEvent.change(screen.getByLabelText(/^E-Mail/), {
42
+ fireEvent.change(screen.getByLabelText(/^Email/), {
43
43
  target: { value: "user@example.com" },
44
44
  });
45
- fireEvent.click(screen.getByRole("button", { name: "Link anfordern" }));
45
+ fireEvent.click(screen.getByRole("button", { name: "Request link" }));
46
46
 
47
47
  await waitFor(() => {
48
- expect(screen.getByText("Mail gesendet")).toBeTruthy();
48
+ expect(screen.getByText("Email sent")).toBeTruthy();
49
49
  });
50
- expect(screen.getByRole("link", { name: /Zurück zum Login/i }).getAttribute("href")).toBe(
50
+ expect(screen.getByRole("link", { name: /Back to sign in/i }).getAttribute("href")).toBe(
51
51
  "/signin",
52
52
  );
53
53
  });
@@ -61,15 +61,15 @@ describe("RequestAccountUnlockScreen", () => {
61
61
  ) as unknown as typeof fetch;
62
62
 
63
63
  renderWithProviders(<RequestAccountUnlockScreen />);
64
- fireEvent.change(screen.getByLabelText(/^E-Mail/), {
64
+ fireEvent.change(screen.getByLabelText(/^Email/), {
65
65
  target: { value: "user@example.com" },
66
66
  });
67
- fireEvent.click(screen.getByRole("button", { name: "Link anfordern" }));
67
+ fireEvent.click(screen.getByRole("button", { name: "Request link" }));
68
68
 
69
69
  await waitFor(() => {
70
- expect(screen.getByRole("alert").textContent).toContain("3 Minuten");
70
+ expect(screen.getByRole("alert").textContent).toContain("3 minutes");
71
71
  });
72
- expect(screen.queryByText("Mail gesendet")).toBeNull();
72
+ expect(screen.queryByText("Email sent")).toBeNull();
73
73
  });
74
74
 
75
75
  test("429 without retryAfterSeconds → generic rateLimited banner", async () => {
@@ -78,13 +78,13 @@ describe("RequestAccountUnlockScreen", () => {
78
78
  ) as unknown as typeof fetch;
79
79
 
80
80
  renderWithProviders(<RequestAccountUnlockScreen />);
81
- fireEvent.change(screen.getByLabelText(/^E-Mail/), {
81
+ fireEvent.change(screen.getByLabelText(/^Email/), {
82
82
  target: { value: "user@example.com" },
83
83
  });
84
- fireEvent.click(screen.getByRole("button", { name: "Link anfordern" }));
84
+ fireEvent.click(screen.getByRole("button", { name: "Request link" }));
85
85
 
86
86
  await waitFor(() => {
87
- expect(screen.getByRole("alert").textContent).toContain("Zu viele Login-Versuche");
87
+ expect(screen.getByRole("alert").textContent).toContain("Too many login attempts");
88
88
  });
89
89
  });
90
90
 
@@ -94,16 +94,16 @@ describe("RequestAccountUnlockScreen", () => {
94
94
  ) as unknown as typeof fetch;
95
95
 
96
96
  renderWithProviders(<RequestAccountUnlockScreen />);
97
- fireEvent.change(screen.getByLabelText(/^E-Mail/), {
97
+ fireEvent.change(screen.getByLabelText(/^Email/), {
98
98
  target: { value: "user@example.com" },
99
99
  });
100
- fireEvent.click(screen.getByRole("button", { name: "Link anfordern" }));
100
+ fireEvent.click(screen.getByRole("button", { name: "Request link" }));
101
101
 
102
102
  await waitFor(() => {
103
- expect(screen.getByRole("alert").textContent).toContain("schief");
103
+ expect(screen.getByRole("alert").textContent).toContain("wrong");
104
104
  });
105
- expect(screen.queryByText("Mail gesendet")).toBeNull();
106
- expect(screen.getByLabelText(/^E-Mail/)).toBeTruthy();
105
+ expect(screen.queryByText("Email sent")).toBeNull();
106
+ expect(screen.getByLabelText(/^Email/)).toBeTruthy();
107
107
  });
108
108
 
109
109
  test("fetch throw → unknownError banner", async () => {
@@ -112,13 +112,13 @@ describe("RequestAccountUnlockScreen", () => {
112
112
  }) as unknown as typeof fetch;
113
113
 
114
114
  renderWithProviders(<RequestAccountUnlockScreen />);
115
- fireEvent.change(screen.getByLabelText(/^E-Mail/), {
115
+ fireEvent.change(screen.getByLabelText(/^Email/), {
116
116
  target: { value: "user@example.com" },
117
117
  });
118
- fireEvent.click(screen.getByRole("button", { name: "Link anfordern" }));
118
+ fireEvent.click(screen.getByRole("button", { name: "Request link" }));
119
119
 
120
120
  await waitFor(() => {
121
- expect(screen.getByRole("alert").textContent).toContain("schief");
121
+ expect(screen.getByRole("alert").textContent).toContain("wrong");
122
122
  });
123
123
  });
124
124
 
@@ -10,14 +10,14 @@ describe("ResetPasswordScreen", () => {
10
10
  test("ohne Token in URL UND ohne token-Prop → missing-token-Page", () => {
11
11
  // jsdom default location is "about:blank" → search = ""
12
12
  renderWithProviders(<ResetPasswordScreen />);
13
- expect(screen.getByText(/enthält keinen Token/i)).toBeTruthy();
13
+ expect(screen.getByText(/missing a token/i)).toBeTruthy();
14
14
  });
15
15
 
16
16
  test("mit token-Prop → Form rendert", () => {
17
17
  renderWithProviders(<ResetPasswordScreen token="abc-token" />);
18
- expect(screen.getByLabelText(/^Neues Passwort/)).toBeTruthy();
19
- expect(screen.getByLabelText(/^Passwort bestätigen/)).toBeTruthy();
20
- expect(screen.getByRole("button", { name: "Passwort speichern" })).toBeTruthy();
18
+ expect(screen.getByLabelText(/^New password/)).toBeTruthy();
19
+ expect(screen.getByLabelText(/^Confirm password/)).toBeTruthy();
20
+ expect(screen.getByRole("button", { name: "Save password" })).toBeTruthy();
21
21
  });
22
22
 
23
23
  test("Passwort < 8 Zeichen → client-side error, kein fetch-Call", async () => {
@@ -25,28 +25,28 @@ describe("ResetPasswordScreen", () => {
25
25
  globalThis.fetch = fetchMock as unknown as typeof fetch;
26
26
 
27
27
  renderWithProviders(<ResetPasswordScreen token="abc" />);
28
- fireEvent.change(screen.getByLabelText(/^Neues Passwort/), { target: { value: "short" } });
29
- fireEvent.change(screen.getByLabelText(/^Passwort bestätigen/), { target: { value: "short" } });
30
- fireEvent.click(screen.getByRole("button", { name: "Passwort speichern" }));
28
+ fireEvent.change(screen.getByLabelText(/^New password/), { target: { value: "short" } });
29
+ fireEvent.change(screen.getByLabelText(/^Confirm password/), { target: { value: "short" } });
30
+ fireEvent.click(screen.getByRole("button", { name: "Save password" }));
31
31
 
32
32
  await waitFor(() => {
33
- expect(screen.getByRole("alert").textContent).toContain("8 Zeichen");
33
+ expect(screen.getByRole("alert").textContent).toContain("8 characters");
34
34
  });
35
35
  expect(fetchMock).not.toHaveBeenCalled();
36
36
  });
37
37
 
38
38
  test("mismatch zwischen Passwort und Confirm → client-side error", async () => {
39
39
  renderWithProviders(<ResetPasswordScreen token="abc" />);
40
- fireEvent.change(screen.getByLabelText(/^Neues Passwort/), {
40
+ fireEvent.change(screen.getByLabelText(/^New password/), {
41
41
  target: { value: "validpass1" },
42
42
  });
43
- fireEvent.change(screen.getByLabelText(/^Passwort bestätigen/), {
43
+ fireEvent.change(screen.getByLabelText(/^Confirm password/), {
44
44
  target: { value: "differentpass" },
45
45
  });
46
- fireEvent.click(screen.getByRole("button", { name: "Passwort speichern" }));
46
+ fireEvent.click(screen.getByRole("button", { name: "Save password" }));
47
47
 
48
48
  await waitFor(() => {
49
- expect(screen.getByRole("alert").textContent).toContain("nicht überein");
49
+ expect(screen.getByRole("alert").textContent).toContain("do not match");
50
50
  });
51
51
  });
52
52
 
@@ -55,13 +55,13 @@ describe("ResetPasswordScreen", () => {
55
55
  globalThis.fetch = fetchMock as unknown as typeof fetch;
56
56
 
57
57
  renderWithProviders(<ResetPasswordScreen token="abc-token" />);
58
- fireEvent.change(screen.getByLabelText(/^Neues Passwort/), {
58
+ fireEvent.change(screen.getByLabelText(/^New password/), {
59
59
  target: { value: "validpass1" },
60
60
  });
61
- fireEvent.change(screen.getByLabelText(/^Passwort bestätigen/), {
61
+ fireEvent.change(screen.getByLabelText(/^Confirm password/), {
62
62
  target: { value: "validpass1" },
63
63
  });
64
- fireEvent.click(screen.getByRole("button", { name: "Passwort speichern" }));
64
+ fireEvent.click(screen.getByRole("button", { name: "Save password" }));
65
65
 
66
66
  await waitFor(() => {
67
67
  expect(fetchMock).toHaveBeenCalledWith(
@@ -71,7 +71,7 @@ describe("ResetPasswordScreen", () => {
71
71
  body: JSON.stringify({ token: "abc-token", newPassword: "validpass1" }),
72
72
  }),
73
73
  );
74
- expect(screen.getByText("Passwort gesetzt")).toBeTruthy();
74
+ expect(screen.getByText("Password set")).toBeTruthy();
75
75
  });
76
76
  });
77
77
 
@@ -84,16 +84,16 @@ describe("ResetPasswordScreen", () => {
84
84
  ) as unknown as typeof fetch;
85
85
 
86
86
  renderWithProviders(<ResetPasswordScreen token="bad" />);
87
- fireEvent.change(screen.getByLabelText(/^Neues Passwort/), {
87
+ fireEvent.change(screen.getByLabelText(/^New password/), {
88
88
  target: { value: "validpass1" },
89
89
  });
90
- fireEvent.change(screen.getByLabelText(/^Passwort bestätigen/), {
90
+ fireEvent.change(screen.getByLabelText(/^Confirm password/), {
91
91
  target: { value: "validpass1" },
92
92
  });
93
- fireEvent.click(screen.getByRole("button", { name: "Passwort speichern" }));
93
+ fireEvent.click(screen.getByRole("button", { name: "Save password" }));
94
94
 
95
95
  await waitFor(() => {
96
- expect(screen.getByRole("alert").textContent).toContain("ungültig");
96
+ expect(screen.getByRole("alert").textContent).toContain("invalid");
97
97
  });
98
98
  });
99
99
  });
@@ -33,14 +33,14 @@ function fillPasswords(password: string, confirm: string): void {
33
33
  describe("SignupCompleteScreen", () => {
34
34
  test("ohne Token in URL UND ohne token-Prop → missing-token-Page", () => {
35
35
  renderWithProviders(<SignupCompleteScreen />);
36
- expect(screen.getByText(/enthält keinen Token/i)).toBeTruthy();
36
+ expect(screen.getByText(/missing a token/i)).toBeTruthy();
37
37
  });
38
38
 
39
39
  test("mit token-Prop → Form rendert", () => {
40
40
  renderWithProviders(<SignupCompleteScreen token="abc-token" />);
41
41
  expect(document.getElementById("signup-password")).toBeTruthy();
42
42
  expect(document.getElementById("signup-confirm-password")).toBeTruthy();
43
- expect(screen.getByRole("button", { name: "Account aktivieren" })).toBeTruthy();
43
+ expect(screen.getByRole("button", { name: "Activate account" })).toBeTruthy();
44
44
  });
45
45
 
46
46
  test("Passwort < 8 Zeichen → client-side error, kein fetch-Call", async () => {
@@ -49,10 +49,10 @@ describe("SignupCompleteScreen", () => {
49
49
 
50
50
  renderWithProviders(<SignupCompleteScreen token="abc" />);
51
51
  fillPasswords("short", "short");
52
- fireEvent.click(screen.getByRole("button", { name: "Account aktivieren" }));
52
+ fireEvent.click(screen.getByRole("button", { name: "Activate account" }));
53
53
 
54
54
  await waitFor(() => {
55
- expect(screen.getByRole("alert").textContent).toContain("8 Zeichen");
55
+ expect(screen.getByRole("alert").textContent).toContain("8 characters");
56
56
  });
57
57
  expect(fetchMock).not.toHaveBeenCalled();
58
58
  });
@@ -84,7 +84,7 @@ describe("SignupCompleteScreen", () => {
84
84
  />,
85
85
  );
86
86
  fillPasswords("validpass1", "validpass1");
87
- fireEvent.click(screen.getByRole("button", { name: "Account aktivieren" }));
87
+ fireEvent.click(screen.getByRole("button", { name: "Activate account" }));
88
88
 
89
89
  await waitFor(() => {
90
90
  expect(fetchMock).toHaveBeenCalledWith(
@@ -107,10 +107,10 @@ describe("SignupCompleteScreen", () => {
107
107
 
108
108
  renderWithProviders(<SignupCompleteScreen token="abc" />);
109
109
  fillPasswords("validpass1", "differentpass");
110
- fireEvent.click(screen.getByRole("button", { name: "Account aktivieren" }));
110
+ fireEvent.click(screen.getByRole("button", { name: "Activate account" }));
111
111
 
112
112
  await waitFor(() => {
113
- expect(screen.getByRole("alert").textContent).toContain("nicht überein");
113
+ expect(screen.getByRole("alert").textContent).toContain("do not match");
114
114
  });
115
115
  expect(fetchMock).not.toHaveBeenCalled();
116
116
  });
@@ -125,10 +125,10 @@ describe("SignupCompleteScreen", () => {
125
125
 
126
126
  renderWithProviders(<SignupCompleteScreen token="bad" />);
127
127
  fillPasswords("validpass1", "validpass1");
128
- fireEvent.click(screen.getByRole("button", { name: "Account aktivieren" }));
128
+ fireEvent.click(screen.getByRole("button", { name: "Activate account" }));
129
129
 
130
130
  await waitFor(() => {
131
- expect(screen.getByRole("alert").textContent).toMatch(/ungültig|abgelaufen/i);
131
+ expect(screen.getByRole("alert").textContent).toMatch(/invalid|expired/i);
132
132
  });
133
133
  });
134
134
  });