evOo-local/apps/web/e2e/first-conversation.spec.ts
Claude 84b56157d0
fix(web): land new accounts in onboarding and show how to connect a model
Signing up refreshes the session, which briefly routes /onboarding to
/sign-in without a return path; the sign-in route then sent the new
account to /app. The shell's own redirect to onboarding was skipped
whenever a focus refresh landed before the bootstrap response, so a
fresh account could stay on an empty Today view.

/onboarding now sends signed-out visitors to /sign-in?next=/onboarding
and sign-in honours it, and the shell redirects an account without
any bots or conversations regardless of which refresh landed first.

Without a connected model nothing can reply, so the composer shows a
"Connect model" action that opens model settings instead of a send
error after the fact.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HKhzQ65ctE5qmkiF9Apxo
2026-10-04 15:53:07 +00:00

42 lines
1.9 KiB
TypeScript

/**
* E2E: a new account reaches onboarding and, without a model, sees how to connect one.
*/
import { expect, test } from "@playwright/test";
import { captureScreenshot, completeOnboarding, routePatchRpc, signup } from "./helpers";
test("a signed-out visit to onboarding returns there after sign-in", async ({ page }) => {
const email = `return-onboarding-${Date.now()}@rakazo.test`;
await signup(page, email, "password12", "Return");
await page.waitForURL(/\/onboarding$/, { timeout: 20_000 });
await page.context().clearCookies();
await page.goto("/onboarding", { waitUntil: "domcontentloaded" });
await page.waitForURL((url) => url.pathname === "/sign-in");
expect(new URL(page.url()).searchParams.get("next")).toBe("/onboarding");
await page.getByPlaceholder("Your email address").fill(email);
await page.getByPlaceholder("Password").fill("password12");
await page.getByRole("button", { name: "Continue with email" }).click();
await page.waitForURL(/\/onboarding$/, { timeout: 20_000 });
await expect(page.locator('[data-rakazo-surface="onboarding"]')).toBeVisible();
});
test("without a model the composer offers to connect one", async ({ page }) => {
const testInfo = test.info();
await signup(page, `model-needed-${Date.now()}@rakazo.test`, "password12", "Model needed");
await completeOnboarding(page, undefined, { mainConversation: true });
await routePatchRpc(page, "bootstrap", (json) => ({
...json,
me: { ...(json.me as Record<string, unknown>), needsModel: true },
}));
await page.reload({ waitUntil: "domcontentloaded" });
const notice = page.getByTestId("composer-model-needed");
await expect(notice).toContainText("Connect a model so your Oo can reply.");
await captureScreenshot(page, testInfo, "first-conversation-model-needed");
await notice.getByRole("button", { name: "Connect model" }).click();
await expect(page.getByTestId("model-settings")).toBeVisible();
});