From 1511880f1b07981736239ebf7b0a5706334b748a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 6 Oct 2026 13:06:43 +0000 Subject: [PATCH] Make onboarding E2E helpers locale-aware for German UI Co-authored-by: cynarAI --- apps/web/e2e/first-run-onboarding-ux.spec.ts | 12 ++++---- apps/web/e2e/helpers.ts | 29 ++++++++++++------- apps/web/e2e/heute-brief.spec.ts | 11 ++++--- .../web/src/pages/shell/use-mobile-sidebar.ts | 4 +-- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/apps/web/e2e/first-run-onboarding-ux.spec.ts b/apps/web/e2e/first-run-onboarding-ux.spec.ts index ee169b7..ac4137c 100644 --- a/apps/web/e2e/first-run-onboarding-ux.spec.ts +++ b/apps/web/e2e/first-run-onboarding-ux.spec.ts @@ -5,12 +5,12 @@ test("onboarding names the first Oo and shows it in chat", async ({ page }) => { const stamp = Date.now(); await signup(page, `name-oo-${stamp}@rakazo.test`, "password12", "Namer"); await page.waitForURL(/\/onboarding/, { timeout: 20_000 }); - await page.getByRole("button", { name: "Continue", exact: true }).click(); + await page.getByRole("button", { name: /^(Continue|Weiter)$/i }).click(); await expect(page.getByTestId("onboarding-name-step")).toBeVisible(); await page.getByTestId("onboarding-oo-name-input").fill("Luna"); await page.getByTestId("onboarding-name-step").getByRole("button", { name: "Continue" }).click(); await page.getByRole("button", { name: /Skip/i }).click(); - await page.getByRole("button", { name: "Skip", exact: true }).click(); + await page.getByRole("button", { name: /^(Skip|Überspringen)$/i }).click(); await expect(page).toHaveURL(/\/app\/g\/[^/]+/, { timeout: 30_000 }); await expect( page.getByTestId("bot-settings-trigger").getByText("Luna", { exact: true }), @@ -25,8 +25,8 @@ test("German UI keeps Deutsch on language skip label", async ({ page }) => { }); const stamp = Date.now(); await signup(page, `de-skip-${stamp}@rakazo.test`, "password12", "Deutsch"); - await page.getByRole("button", { name: "Continue", exact: true }).click(); - await page.getByRole("button", { name: "Skip", exact: true }).click(); + await page.getByRole("button", { name: /^(Continue|Weiter)$/i }).click(); + await page.getByRole("button", { name: /^(Skip|Überspringen)$/i }).click(); await expect( page.getByRole("heading", { name: /Wähle deine Sprache|Choose your language/i }), ).toBeVisible(); @@ -40,8 +40,8 @@ test("German voice step lists German voices after choosing Deutsch", async ({ pa }); const stamp = Date.now(); await signup(page, `de-voice-${stamp}@rakazo.test`, "password12", "Stimme"); - await page.getByRole("button", { name: "Continue", exact: true }).click(); - await page.getByRole("button", { name: "Skip", exact: true }).click(); + await page.getByRole("button", { name: /^(Continue|Weiter)$/i }).click(); + await page.getByRole("button", { name: /^(Skip|Überspringen)$/i }).click(); await page.getByRole("button", { name: "Deutsch", exact: true }).click(); await expect(page.getByRole("button", { name: "Eva", exact: true })).toBeVisible(); await expect(page.getByRole("button", { name: "Alloy", exact: true })).toHaveCount(0); diff --git a/apps/web/e2e/helpers.ts b/apps/web/e2e/helpers.ts index 355869d..bc3053a 100644 --- a/apps/web/e2e/helpers.ts +++ b/apps/web/e2e/helpers.ts @@ -87,6 +87,9 @@ async function visible(locator: Locator, timeout: number) { return locator.isVisible({ timeout }).catch(() => false); } +const onboardingContinueButton = /^(Continue|Weiter)$/i; +const onboardingSkipButton = /^(Skip|Überspringen)$/i; + /** After the look picker, skip the optional Oo naming step (keeps the default name). */ export async function skipOnboardingNameStep(page: Page) { await expect(page.getByTestId("onboarding-name-step")).toBeVisible(); @@ -113,11 +116,15 @@ export async function completeOnboarding( ); } - const design = page.getByRole("heading", { name: /Design your Oo/i }); + const design = page.getByRole("heading", { name: /Design your Oo|Gestalte dein Oo/i }); const naming = page.getByTestId("onboarding-name-step"); - const language = page.getByRole("heading", { name: /Choose your language/i }); - const voice = page.getByRole("heading", { name: /Pick a voice/i }); - const integrations = page.getByRole("heading", { name: "Server integrations", exact: true }); + const language = page.getByRole("heading", { + name: /Choose your language|Wähle deine Sprache/i, + }); + const voice = page.getByRole("heading", { name: /Pick a voice|Wähle eine Stimme/i }); + const integrations = page.getByRole("heading", { + name: /Server integrations|Server-Integrationen/i, + }); const expectedOoName = options.ooName ?? "Chief"; const chief = page.getByText(expectedOoName).first(); @@ -127,30 +134,30 @@ export async function completeOnboarding( await expect(entry.first()).toBeVisible({ timeout: 20_000 }); if (await design.isVisible()) { - await page.getByRole("button", { name: "Continue", exact: true }).click(); + await page.getByRole("button", { name: onboardingContinueButton }).click(); await expect(naming).toBeVisible(); if (options.ooName) { await page.getByTestId("onboarding-oo-name-input").fill(options.ooName); - await naming.getByRole("button", { name: "Continue", exact: true }).click(); + await naming.getByRole("button", { name: onboardingContinueButton }).click(); } else { - await page.getByRole("button", { name: "Skip", exact: true }).click(); + await page.getByRole("button", { name: onboardingSkipButton }).click(); } await expect(language).toBeVisible(); - await page.getByRole("button", { name: /Skip/i }).click(); + await page.getByRole("button", { name: onboardingSkipButton }).click(); await expect(voice).toBeVisible(); - await page.getByRole("button", { name: "Skip", exact: true }).click(); + await page.getByRole("button", { name: onboardingSkipButton }).click(); } if (await visible(integrations, 8_000)) { if (testInfo) await captureScreenshot(page, testInfo, "02-connect-apps"); - await page.getByRole("button", { name: "Skip", exact: true }).click(); + await page.getByRole("button", { name: onboardingSkipButton }).click(); } // The scan step, when shown, either asks first (skip it) or advances by itself. Onboarding // then lands in the main conversation. const skipScan = page .locator('[data-onboarding-step="scan"]') - .getByRole("button", { name: "Skip", exact: true }); + .getByRole("button", { name: onboardingSkipButton }); await expect(async () => { if (await skipScan.isVisible()) await skipScan.click(); expect(page.url()).toMatch(/\/app\/g\/[^/]+$/); diff --git a/apps/web/e2e/heute-brief.spec.ts b/apps/web/e2e/heute-brief.spec.ts index 5fa278b..3cf8043 100644 --- a/apps/web/e2e/heute-brief.spec.ts +++ b/apps/web/e2e/heute-brief.spec.ts @@ -85,6 +85,7 @@ test("Heute shows the coach's brief, habits and goals, checks habits off and set await captureScreenshot(page, testInfo, "heute-coach"); await page.reload(); + await openHeute(page); await expect(page).toHaveURL(/\/app\/heute$/); await expect(page.getByTestId("heute-brief")).toBeVisible(); await expect(page.getByTestId(`heute-habit-${habit.id}`).getByRole("checkbox")).toBeChecked(); @@ -92,11 +93,13 @@ test("Heute shows the coach's brief, habits and goals, checks habits off and set // Check-in times live behind one compact control. const rhythm = page.getByTestId("heute-rhythm"); await expect(rhythm).toBeVisible(); - await expect(async () => { - await rhythm.click(); - await expect(page.getByTestId("heute-rhythm-panel")).toBeVisible(); - }).toPass({ timeout: 15_000 }); + const rhythmLoaded = page.waitForResponse( + (response) => response.url().includes("/rpc/coach/rhythm/get") && response.ok(), + ); + await rhythm.click(); + await rhythmLoaded; const panel = page.getByTestId("heute-rhythm-panel"); + await expect(panel).toBeVisible(); const morning = panel.getByTestId("heute-rhythm-morning"); await expect(morning.getByRole("switch")).not.toBeChecked(); const enabled = page.waitForResponse( diff --git a/apps/web/src/pages/shell/use-mobile-sidebar.ts b/apps/web/src/pages/shell/use-mobile-sidebar.ts index 16847db..421567a 100644 --- a/apps/web/src/pages/shell/use-mobile-sidebar.ts +++ b/apps/web/src/pages/shell/use-mobile-sidebar.ts @@ -27,8 +27,8 @@ export function useMobileSidebar() { document.querySelector('[data-testid="open-navigation"]')?.focus(); }); } - window.addEventListener("keydown", onKeyDown, true); - return () => window.removeEventListener("keydown", onKeyDown, true); + window.addEventListener("keydown", onKeyDown); + return () => window.removeEventListener("keydown", onKeyDown); }, [mobileSidebarOpen]); function onMobileSidebarTouchStart(event: React.TouchEvent) {