import { expect, test, type Page } from "@playwright/test"; // Visual-regression lock (plan P6): pixel baselines for the §2 design // system on the primary surfaces. Baselines are PER-PLATFORM (rendering // differs across OSes); CI excludes @visual until linux baselines are // generated there — see ci.yml. Regenerate intentionally with: // npx playwright test visual --update-snapshots const OWNER_EMAIL = "owner@acme.test"; const OWNER_PASSWORD = "e2e-password"; const SCREENSHOT = { animations: "disabled" as const, maxDiffPixelRatio: 0.02, }; async function signIn(page: Page) { await page.goto("/login"); await page.getByLabel("Email address").fill(OWNER_EMAIL); await page.getByLabel("Password").fill(OWNER_PASSWORD); await page.getByRole("button", { name: "Sign in" }).click(); // Wait for the real workspace home — the login page also has a lowercase // "clawmates" heading, so confirm navigation and match case-sensitively. await page.waitForURL((url) => !url.pathname.endsWith("/login")); await expect( page.getByRole("heading", { name: "Clawmates", exact: true }), ).toBeVisible(); } test("login page looks right @visual", async ({ page }) => { await page.goto("/login"); await expect(page).toHaveScreenshot("login.png", SCREENSHOT); }); test("workspace home looks right @visual", async ({ page }) => { await signIn(page); await expect(page).toHaveScreenshot("workspace-home.png", SCREENSHOT); }); test("chat welcome and computer panel look right @visual", async ({ page, }) => { await signIn(page); await page.getByRole("link", { name: /Scout/ }).click(); await expect(page).toHaveURL(/\/claws\/.+\/chat\//); const before = page.url(); await page.getByRole("button", { name: "New" }).click(); await page.waitForURL((url) => url.toString() !== before); await expect( page.getByRole("heading", { name: /Hi, I'm Scout/ }), ).toBeVisible(); await expect(page).toHaveScreenshot("chat-welcome.png", SCREENSHOT); await page.getByRole("button", { name: "Computer" }).click(); const panel = page.getByRole("complementary", { name: "Computer" }); await expect(panel.getByText(/Scout's Computer/)).toBeVisible(); await expect(page).toHaveScreenshot("computer-home.png", SCREENSHOT); }); test("credits page looks right @visual", async ({ page }) => { await signIn(page); await page.getByRole("link", { name: "Credits" }).click(); await expect(page.getByText("Available credits")).toBeVisible(); await expect(page).toHaveScreenshot("credits.png", { ...SCREENSHOT, // Balance and usage are live numbers — mask them, lock the layout. // Balance and the whole usage card are live (and the runway line // appears/disappears with burn) — mask them, lock the layout. mask: [ page.getByTestId("credit-balance"), page.getByTestId("usage-card"), ], }); }); test("marketing landing looks right @visual", async ({ page, context }) => { await context.clearCookies(); await page.goto("/"); await expect( page.getByRole("heading", { name: /agentic systems/i }), ).toBeVisible(); // Below-the-fold art is lazy-loaded; scroll the page to trigger every // loader, then wait for each image to finish before the full-page shot so // the baseline isn't missing the deeper sections' art. await page.evaluate(async () => { for (let y = 0; y < document.body.scrollHeight; y += 600) { window.scrollTo(0, y); await new Promise((r) => setTimeout(r, 80)); } window.scrollTo(0, 0); const start = performance.now(); while (performance.now() - start < 8000) { const imgs = Array.from(document.images); if (imgs.every((i) => i.complete && i.naturalWidth > 0)) break; await new Promise((r) => setTimeout(r, 100)); } }); await page.waitForLoadState("networkidle"); await page.evaluate(() => Promise.allSettled(document.getAnimations().map((a) => a.finished)), ); await expect(page).toHaveScreenshot("marketing.png", { ...SCREENSHOT, fullPage: true, // The hero backdrop is an animated, randomized canvas — mask it so the // baseline stays deterministic (the heading is asserted separately above). mask: [page.locator("[data-hero-ambient]")], }); }); test("team org chart looks right @visual", async ({ page }) => { await signIn(page); await page.getByRole("link", { name: "Team", exact: true }).click(); await page.getByRole("tab", { name: "Claw org chart" }).click(); await expect(page.getByRole("tab", { name: "Claw org chart" })).toHaveAttribute( "aria-selected", "true", ); await expect(page).toHaveScreenshot("team-orgchart.png", SCREENSHOT); });