Make onboarding E2E helpers locale-aware for German UI

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-10-06 13:06:43 +00:00
parent c5b2ebc2b3
commit 1511880f1b
No known key found for this signature in database
4 changed files with 33 additions and 23 deletions

View File

@ -5,12 +5,12 @@ test("onboarding names the first Oo and shows it in chat", async ({ page }) => {
const stamp = Date.now(); const stamp = Date.now();
await signup(page, `name-oo-${stamp}@rakazo.test`, "password12", "Namer"); await signup(page, `name-oo-${stamp}@rakazo.test`, "password12", "Namer");
await page.waitForURL(/\/onboarding/, { timeout: 20_000 }); 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 expect(page.getByTestId("onboarding-name-step")).toBeVisible();
await page.getByTestId("onboarding-oo-name-input").fill("Luna"); await page.getByTestId("onboarding-oo-name-input").fill("Luna");
await page.getByTestId("onboarding-name-step").getByRole("button", { name: "Continue" }).click(); 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/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).toHaveURL(/\/app\/g\/[^/]+/, { timeout: 30_000 });
await expect( await expect(
page.getByTestId("bot-settings-trigger").getByText("Luna", { exact: true }), 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(); const stamp = Date.now();
await signup(page, `de-skip-${stamp}@rakazo.test`, "password12", "Deutsch"); await signup(page, `de-skip-${stamp}@rakazo.test`, "password12", "Deutsch");
await page.getByRole("button", { name: "Continue", exact: true }).click(); await page.getByRole("button", { name: /^(Continue|Weiter)$/i }).click();
await page.getByRole("button", { name: "Skip", exact: true }).click(); await page.getByRole("button", { name: /^(Skip|Überspringen)$/i }).click();
await expect( await expect(
page.getByRole("heading", { name: /Wähle deine Sprache|Choose your language/i }), page.getByRole("heading", { name: /Wähle deine Sprache|Choose your language/i }),
).toBeVisible(); ).toBeVisible();
@ -40,8 +40,8 @@ test("German voice step lists German voices after choosing Deutsch", async ({ pa
}); });
const stamp = Date.now(); const stamp = Date.now();
await signup(page, `de-voice-${stamp}@rakazo.test`, "password12", "Stimme"); await signup(page, `de-voice-${stamp}@rakazo.test`, "password12", "Stimme");
await page.getByRole("button", { name: "Continue", exact: true }).click(); await page.getByRole("button", { name: /^(Continue|Weiter)$/i }).click();
await page.getByRole("button", { name: "Skip", exact: true }).click(); await page.getByRole("button", { name: /^(Skip|Überspringen)$/i }).click();
await page.getByRole("button", { name: "Deutsch", exact: true }).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: "Eva", exact: true })).toBeVisible();
await expect(page.getByRole("button", { name: "Alloy", exact: true })).toHaveCount(0); await expect(page.getByRole("button", { name: "Alloy", exact: true })).toHaveCount(0);

View File

@ -87,6 +87,9 @@ async function visible(locator: Locator, timeout: number) {
return locator.isVisible({ timeout }).catch(() => false); 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). */ /** After the look picker, skip the optional Oo naming step (keeps the default name). */
export async function skipOnboardingNameStep(page: Page) { export async function skipOnboardingNameStep(page: Page) {
await expect(page.getByTestId("onboarding-name-step")).toBeVisible(); 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 naming = page.getByTestId("onboarding-name-step");
const language = page.getByRole("heading", { name: /Choose your language/i }); const language = page.getByRole("heading", {
const voice = page.getByRole("heading", { name: /Pick a voice/i }); name: /Choose your language|Wähle deine Sprache/i,
const integrations = page.getByRole("heading", { name: "Server integrations", exact: true }); });
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 expectedOoName = options.ooName ?? "Chief";
const chief = page.getByText(expectedOoName).first(); const chief = page.getByText(expectedOoName).first();
@ -127,30 +134,30 @@ export async function completeOnboarding(
await expect(entry.first()).toBeVisible({ timeout: 20_000 }); await expect(entry.first()).toBeVisible({ timeout: 20_000 });
if (await design.isVisible()) { if (await design.isVisible()) {
await page.getByRole("button", { name: "Continue", exact: true }).click(); await page.getByRole("button", { name: onboardingContinueButton }).click();
await expect(naming).toBeVisible(); await expect(naming).toBeVisible();
if (options.ooName) { if (options.ooName) {
await page.getByTestId("onboarding-oo-name-input").fill(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 { } else {
await page.getByRole("button", { name: "Skip", exact: true }).click(); await page.getByRole("button", { name: onboardingSkipButton }).click();
} }
await expect(language).toBeVisible(); await expect(language).toBeVisible();
await page.getByRole("button", { name: /Skip/i }).click(); await page.getByRole("button", { name: onboardingSkipButton }).click();
await expect(voice).toBeVisible(); 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 (await visible(integrations, 8_000)) {
if (testInfo) await captureScreenshot(page, testInfo, "02-connect-apps"); 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 // The scan step, when shown, either asks first (skip it) or advances by itself. Onboarding
// then lands in the main conversation. // then lands in the main conversation.
const skipScan = page const skipScan = page
.locator('[data-onboarding-step="scan"]') .locator('[data-onboarding-step="scan"]')
.getByRole("button", { name: "Skip", exact: true }); .getByRole("button", { name: onboardingSkipButton });
await expect(async () => { await expect(async () => {
if (await skipScan.isVisible()) await skipScan.click(); if (await skipScan.isVisible()) await skipScan.click();
expect(page.url()).toMatch(/\/app\/g\/[^/]+$/); expect(page.url()).toMatch(/\/app\/g\/[^/]+$/);

View File

@ -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 captureScreenshot(page, testInfo, "heute-coach");
await page.reload(); await page.reload();
await openHeute(page);
await expect(page).toHaveURL(/\/app\/heute$/); await expect(page).toHaveURL(/\/app\/heute$/);
await expect(page.getByTestId("heute-brief")).toBeVisible(); await expect(page.getByTestId("heute-brief")).toBeVisible();
await expect(page.getByTestId(`heute-habit-${habit.id}`).getByRole("checkbox")).toBeChecked(); 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. // Check-in times live behind one compact control.
const rhythm = page.getByTestId("heute-rhythm"); const rhythm = page.getByTestId("heute-rhythm");
await expect(rhythm).toBeVisible(); await expect(rhythm).toBeVisible();
await expect(async () => { const rhythmLoaded = page.waitForResponse(
await rhythm.click(); (response) => response.url().includes("/rpc/coach/rhythm/get") && response.ok(),
await expect(page.getByTestId("heute-rhythm-panel")).toBeVisible(); );
}).toPass({ timeout: 15_000 }); await rhythm.click();
await rhythmLoaded;
const panel = page.getByTestId("heute-rhythm-panel"); const panel = page.getByTestId("heute-rhythm-panel");
await expect(panel).toBeVisible();
const morning = panel.getByTestId("heute-rhythm-morning"); const morning = panel.getByTestId("heute-rhythm-morning");
await expect(morning.getByRole("switch")).not.toBeChecked(); await expect(morning.getByRole("switch")).not.toBeChecked();
const enabled = page.waitForResponse( const enabled = page.waitForResponse(

View File

@ -27,8 +27,8 @@ export function useMobileSidebar() {
document.querySelector<HTMLElement>('[data-testid="open-navigation"]')?.focus(); document.querySelector<HTMLElement>('[data-testid="open-navigation"]')?.focus();
}); });
} }
window.addEventListener("keydown", onKeyDown, true); window.addEventListener("keydown", onKeyDown);
return () => window.removeEventListener("keydown", onKeyDown, true); return () => window.removeEventListener("keydown", onKeyDown);
}, [mobileSidebarOpen]); }, [mobileSidebarOpen]);
function onMobileSidebarTouchStart(event: React.TouchEvent<HTMLDivElement>) { function onMobileSidebarTouchStart(event: React.TouchEvent<HTMLDivElement>) {