/** Mobile (iPhone 14, 390×844) end-to-end walk of the AKOS dashboard against a live deployment. * Env: AKOS_BASE_URL, AKOS_E2E_EMAIL, AKOS_E2E_PASSWORD. Asserts real data renders (no "undefined"/"NaN"), no console errors, drawer works. */ import { test, expect, type Page } from "@playwright/test"; const EMAIL = process.env.AKOS_E2E_EMAIL ?? ""; const PASSWORD = process.env.AKOS_E2E_PASSWORD ?? ""; async function noJunk(page: Page) { await page.waitForLoadState("networkidle"); await expect(page.locator(".loading")).toHaveCount(0, { timeout: 20_000 }); const text = await page.locator("main").innerText(); expect(text).not.toMatch(/\bundefined\b/); expect(text).not.toMatch(/\bNaN\b/); expect(text).not.toMatch(/\[object Object\]/); } test.describe("AKOS mobile dashboard", () => { const errors: string[] = []; test.beforeEach(async ({ page }) => { // the app probes /api/v1/auth/me on load; a 401 before login is expected and not an error page.on("console", (m) => { if (m.type() === "error" && !/status of 401/.test(m.text())) errors.push(m.text()); }); page.on("pageerror", (e) => errors.push(String(e))); await page.goto("/login"); await page.getByLabel("Email").fill(EMAIL); await page.getByLabel("Password").fill(PASSWORD); await page.getByRole("button", { name: "Sign in" }).click(); await expect(page).toHaveURL(/\/$/); }); test.afterEach(() => { expect(errors, errors.join("\n")).toEqual([]); errors.length = 0; }); test("overview → apis → api detail", async ({ page }) => { await noJunk(page); await expect(page.getByRole("heading", { name: "Overview" })).toBeVisible(); await expect(page.locator(".tile").first()).toBeVisible(); await page.getByRole("button", { name: "Open navigation" }).click(); await expect(page.locator("#drawer.open")).toBeVisible(); await page.locator("#drawer").getByRole("link", { name: "API Atlas" }).click(); await expect(page.getByRole("heading", { name: "API Atlas" })).toBeVisible(); await noJunk(page); const first = page.locator("table.tbl tbody tr a").first(); await expect(first).toBeVisible(); await first.click(); await noJunk(page); await expect(page.getByRole("tab", { name: "Endpoints" })).toBeVisible(); await page.getByRole("tab", { name: "Evidence" }).click(); await noJunk(page); }); test("executions → detail", async ({ page }) => { await page.goto("/executions"); await noJunk(page); const first = page.locator("table.tbl tbody tr a").first(); await expect(first).toBeVisible(); await first.click(); await noJunk(page); await expect(page.getByRole("heading", { name: /Execution/ })).toBeVisible(); await expect(page.getByText("Policy decision")).toBeVisible(); }); test("lineage, debt, policy, operations, settings", async ({ page }) => { for (const [path, heading] of [["/lineage", "Research Lineage"], ["/debt", "Knowledge Debt"], ["/policy", "Policy Center"], ["/operations", "Operations"], ["/settings", "Settings"]] as const) { await page.goto(path); await expect(page.getByRole("heading", { name: heading, level: 1 })).toBeVisible(); await noJunk(page); } await page.getByRole("tab", { name: "Audit" }).click().catch(() => {}); await page.goto("/operations"); await page.getByRole("tab", { name: "Audit" }).click(); await page.getByRole("button", { name: "Verify chain" }).click(); await expect(page.getByText(/Chain intact/)).toBeVisible({ timeout: 20_000 }); }); test("dark mode toggle persists", async ({ page }) => { await page.getByRole("button", { name: "Toggle dark mode" }).click(); const theme = await page.evaluate(() => document.documentElement.getAttribute("data-theme")); expect(["dark", "light"]).toContain(theme); await page.reload(); expect(await page.evaluate(() => document.documentElement.getAttribute("data-theme"))).toBe(theme); }); });