/** * E2E: after the wizard the main bot gets to know the user in the main conversation, one card * after each answer, without ever blocking the composer. */ import type { Page } from "@playwright/test"; import { expect, test } from "@playwright/test"; import { captureScreenshot, completeOnboarding, rpc, signup } from "./helpers"; function card(page: Page, step: string) { return page.locator(`[data-testid="choice-card"][data-step="${step}"]`); } test("the main bot asks focus, goal, check-ins and quiet hours, then says what is next", async ({ page, }, testInfo) => { await signup(page, `intake-${Date.now()}@rakazo.test`, "password12", "Intake Tester"); await completeOnboarding(page, undefined, { mainConversation: true }); await page.getByTestId("focus-choice-day").click(); const goal = card(page, "goal"); await expect(goal).toContainText("What’s one thing you want to get done in the next few weeks?"); // Typing instead of answering goes to the main bot, and the card stays answerable. const composer = page.getByRole("combobox", { name: "Message" }); await composer.fill("Quick question first"); await composer.press("Enter"); await expect(page.getByTestId("transcript")).toContainText(/on it|handled/i, { timeout: 60_000, }); const goalInput = goal.getByRole("textbox", { name: "What’s one thing you want to get done in the next few weeks?", }); await goalInput.fill("Run a 10K"); await goal.scrollIntoViewIfNeeded(); await page.mouse.move(1, 1); await captureScreenshot(page, testInfo, "onboarding-intake-goal"); await goalInput.press("Enter"); await expect(goal.getByTestId("choice-answer")).toHaveText("Run a 10K"); const rhythm = card(page, "rhythm"); await expect(rhythm).toContainText("When should I check in with you?"); await rhythm.scrollIntoViewIfNeeded(); await page.mouse.move(1, 1); await captureScreenshot(page, testInfo, "onboarding-intake-rhythm"); await rhythm.getByTestId("rhythm-choice-morning").click(); await expect(rhythm.getByTestId("choice-answer")).toContainText("Every morning"); const quiet = card(page, "quiet_hours"); await expect(quiet).toContainText("When should I stay quiet?"); await quiet.getByTestId("quiet_hours-choice-22-07").click(); // Only a browser that can still turn push on is asked about notifications. // The web words the closing note itself, from the time of the first check-in. const closing = page .getByTestId("transcript") .getByText(/^All set\. I’ll check in (today|tomorrow) at 8:00\sAM\.$/); const notifications = card(page, "notifications"); await expect(notifications.or(closing).first()).toBeVisible(); if (await notifications.isVisible()) { await notifications.getByTestId("notifications-choice-later").click(); } await expect(closing).toBeVisible(); await expect( page.locator('[data-testid="choice-card"]:not(:has([data-testid="choice-answer"]))'), ).toHaveCount(0); await closing.scrollIntoViewIfNeeded(); await page.mouse.move(1, 1); await captureScreenshot(page, testInfo, "onboarding-intake-done"); const settings = await rpc<{ quietHours: { start: string; end: string } | null }>( page, "notifications/settings/get", null, ); expect(settings.quietHours).toEqual({ start: "22:00", end: "07:00" }); const profile = await rpc<{ facts: Array<{ value: string; source: string }> }>( page, "profile/get", null, ); expect(profile.facts).toContainEqual( expect.objectContaining({ value: "Wants help with day-to-day work first", source: "onboarding", }), ); });