import { expect, test, type Page } from "@playwright/test"; async function ensureSignedIn(page: Page) { await page.goto("/"); const devLogin = page.getByRole("button", { name: "Dev login" }); if (await devLogin.isVisible().catch(() => false)) { await devLogin.click(); await page.waitForURL((url) => !url.pathname.startsWith("/login")); } await expect(page.getByText("Upcoming events", { exact: true })).toBeVisible(); } test("dashboard happy path", async ({ page }) => { await ensureSignedIn(page); // Page title is present await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible(); // Quick-add FAB is present await expect(page.getByRole("button", { name: "Quick add" })).toBeVisible(); // Widget card titles from registered manifests should appear (exact match inside main) const main = page.getByRole("main"); await expect(main.getByText("Upcoming events", { exact: true })).toBeVisible(); await expect(main.getByText("List items", { exact: true })).toBeVisible(); await expect(main.getByText("Notes", { exact: true }).first()).toBeVisible(); await expect(main.getByText("Recent activity", { exact: true })).toBeVisible(); // No horizontal scroll on mobile viewport await page.setViewportSize({ width: 375, height: 812 }); await page.goto("/"); await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible(); const bodyWidth = await page.evaluate(() => document.body.scrollWidth); const viewportWidth = await page.evaluate(() => window.innerWidth); expect(bodyWidth).toBeLessThanOrEqual(viewportWidth + 2); // 2px tolerance for sub-pixel rendering }); test("dashboard edit mode shows live widget content", async ({ page }) => { await ensureSignedIn(page); await page.goto("/d/home?edit=1"); await expect(page.getByText("Drag to reorder")).toBeVisible(); await expect(page.getByRole("button", { name: "Save" })).toBeVisible(); const main = page.getByRole("main"); await expect(main.getByText("Recent activity", { exact: true })).toBeVisible(); // Server-rendered widget body (empty-state copy), not the meta description placeholder await expect( main.getByText(/No recent activity|No events in the next|No pinned notes|No notes/i).first(), ).toBeVisible(); await expect(main.getByText("Latest changes across your household.")).not.toBeVisible(); });