evOo-local/apps/web/e2e/onboarding-emoji-first.spec.ts
cynarAI 0e1036b2ff
First-run onboarding UX: naming, locale, voices, Heute route, mobile drawer (#171)
* Fix first-run onboarding UX: naming, locale, voices, Heute route, mobile drawer

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix biome import order and formatting

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix biome formatting on OoStage and E2E specs

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix E2E specs for onboarding name step and Heute reload route

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix German onboarding heading matcher in E2E

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix German signup E2E, Escape menu stacking, and Heute rhythm click

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Make onboarding E2E helpers locale-aware for German UI

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix completeOnboarding language skip for Skip · locale label

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Use language-step Skip · locale button in completeOnboarding

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix German E2E: Nachricht composer, voice loading wait, Heute rhythm

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Stabilize Heute reload URL and German voice E2E assertions

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Fix Heute bootstrap redirect and German onboarding voices

When Kokoro lists no locale-matched voices, onboarding now uses offline
presets instead of showing English-only options. Shell bootstrap skips
bot redirects while the user is on /app/heute.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Relax German voice E2E name matching for voice labels

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Harden Heute route and German voice onboarding E2E

Rebased onto main with Expo SDK 57 typecheck fixes. Voice options expose
stable test ids; Shell keeps /app/heute across bootstrap and redirects
legacy /app/heute bot URLs. Heute coach E2E asserts URL after reload.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* Format Shell after Heute route helper

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-06 20:15:36 +02:00

112 lines
4.1 KiB
TypeScript

import { expect, test } from "@playwright/test";
import { captureScreenshot, signup, skipOnboardingNameStep } from "./helpers";
test.beforeEach(async ({ page }) => {
await page.route("**/rpc/integrationSetup/get", (route) =>
route.fulfill({
json: {
json: {
canConfigure: true,
needsSetup: false,
webUrl: "https://example.test/integrations/setup",
providers: [{ id: "composio", configured: true }],
},
},
}),
);
});
/**
* E2E test for the emoji-first onboarding flow:
* 1. Sign up → lands on onboarding
* 2. Design Oo: pick shape + color → live preview
* 3. Choose language, skip the voice
* 4. Skip integrations → bot created → land in chat, where the main bot asks the rest
*/
test("emoji-first onboarding: shape + color selection with live preview", async ({
page,
}, testInfo) => {
const stamp = Date.now();
await signup(page, `emoji-${stamp}@rakazo.test`, "password12", "TestUser");
// Should land on onboarding page
await page.waitForURL(/\/(onboarding|app)/, { timeout: 20_000 });
// Step 1: Design your Oo
await expect(page.getByRole("heading", { name: /Design your Oo/i })).toBeVisible();
if (testInfo) await captureScreenshot(page, testInfo, "01-design-your-oo");
await expect(page.getByTestId("onboarding-oo-designer")).toBeVisible();
await expect(page.getByTestId("onboarding-look-preview")).toBeVisible();
await page.getByTestId("onboarding-look-category-work").click();
await page.getByRole("tab", { name: /Look 3 of/i }).click();
const botAvatar = page.locator('[data-testid="onboarding-look-preview"] svg').first();
await expect(botAvatar).toBeVisible();
if (testInfo) await captureScreenshot(page, testInfo, "02-shape-color-preview");
// Click Continue → naming, then language.
await page
.getByRole("button", { name: /Continue/i })
.first()
.click();
await skipOnboardingNameStep(page);
// Step 2: Language selection
await expect(page.getByRole("heading", { name: /Choose your language/i })).toBeVisible();
if (testInfo) await captureScreenshot(page, testInfo, "05-language-step");
// Select Deutsch — choosing a language continues to the voice step.
await page.getByRole("button", { name: /Deutsch/ }).click();
await expect(page.getByRole("heading", { name: /Pick a voice/i })).toBeVisible();
await page.getByRole("button", { name: "Skip", exact: true }).click();
const chief = page.getByText("Chief").first();
const integrationsHeading = page.getByRole("heading", {
name: /Server integrations/i,
});
await integrationsHeading.or(chief).waitFor({ timeout: 20_000 });
if (testInfo) await captureScreenshot(page, testInfo, "06-integrations-or-bot");
// If integrations shown, skip
if (await integrationsHeading.isVisible().catch(() => false)) {
await page.getByRole("button", { name: "Skip", exact: true }).click();
}
await page.waitForURL(/\/app\//, { timeout: 20_000 });
await expect(chief).toBeVisible();
if (testInfo) await captureScreenshot(page, testInfo, "07-onboarding-complete");
});
test("onboarding: skip language defaults to English", async ({ page }) => {
const stamp = Date.now();
await signup(page, `lang-skip-${stamp}@rakazo.test`, "password12", "Skipper");
await page.waitForURL(/\/(onboarding|app)/, { timeout: 20_000 });
// Go through emoji step quickly (just click continue)
await expect(page.getByRole("heading", { name: /Design your Oo/i })).toBeVisible();
await page
.getByRole("button", { name: /Continue/i })
.first()
.click();
await skipOnboardingNameStep(page);
// Language step — click Skip, then skip voice.
await expect(page.getByRole("heading", { name: /Choose your language/i })).toBeVisible();
await page.getByRole("button", { name: /Skip/i }).click();
await expect(page.getByRole("heading", { name: /Pick a voice/i })).toBeVisible();
await page.getByRole("button", { name: "Skip", exact: true }).click();
// Should land in bot chat
await page.waitForURL(/\/app\//, { timeout: 20_000 });
await expect(page.getByText("Chief").first()).toBeVisible();
});