import { expect, test, type Page } from "@playwright/test"; import path from "node:path"; const updateAuditEvidence = process.env.UPDATE_AUDIT_EVIDENCE === "1"; async function login(page: Page) { await page.goto("/login"); await page.getByLabel("Email").fill("e2e@example.test"); await page.getByLabel("Current password").fill("E2ePassword123!"); await page.getByRole("button", { name: "Sign in", exact: true }).click(); await expect(page).toHaveURL(/\/dashboard$/); } test("kanban columns use dark theme surfaces", async ({ page }) => { let moveRequests = 0; await page.addInitScript(() => { window.localStorage.setItem("uiLanguage", "en"); window.localStorage.setItem("themeMode:anon", "dark"); }); await page.route("**/api/jobapplications/board", async (route) => { await route.fulfill({ json: [{ id: 71, jobTitle: "Synthetic platform engineer", companyId: 1, company: { id: 1, name: "Synthetic AS" }, status: "Saved", dateApplied: null, savedAt: "2026-08-10T10:00:00Z", daysSince: null, tags: "[\"C#\",\"Azure\"]", }, { id: 72, jobTitle: "Custom status role", companyId: 1, company: { id: 1, name: "Synthetic AS" }, status: "Take-home assignment", dateApplied: "2026-08-09T10:00:00Z", savedAt: "2026-08-09T10:00:00Z", daysSince: 1, }] }); }); await page.route("**/api/jobapplications/71/status", async (route) => { moveRequests += 1; await route.fulfill({ json: {} }); }); await login(page); await page.setViewportSize({ width: 1440, height: 900 }); await page.goto("/kanban"); await expect(page.getByText("Synthetic platform engineer")).toBeVisible(); const column = page.getByText("Not Applied", { exact: true }).locator("xpath=ancestor::*[contains(@class,'MuiPaper-root')][1]"); expect(await column.evaluate((element) => getComputedStyle(element).backgroundColor)).not.toBe("rgb(245, 242, 250)"); if (updateAuditEvidence) await page.screenshot({ path: path.resolve("..", "docs", "audits", "evidence", "ux-003-kanban-dark-after.png"), fullPage: true }); const card = page.getByText("Synthetic platform engineer", { exact: true }).locator("xpath=ancestor::*[contains(@class,'MuiCard-root')][1]"); await card.focus(); await page.keyboard.press("Space"); await expect(card).toHaveAttribute("aria-pressed", "true"); const active = page.getByRole("group", { name: "Active column" }); await expect(active).toHaveAttribute("data-drop-state", "valid"); await expect(page.getByRole("group", { name: "Other column, not a drop target" })).toHaveAttribute("aria-disabled", "true"); await active.focus(); await page.keyboard.press("Enter"); await expect.poll(() => moveRequests).toBe(1); await expect(page.getByText("Job moved to Applied.")).toBeAttached(); await page.setViewportSize({ width: 768, height: 900 }); await page.evaluate(() => (document.activeElement as HTMLElement | null)?.blur()); const overflow = await page.evaluate(() => ({ documentWidth: document.documentElement.scrollWidth, viewportWidth: window.innerWidth, offenders: Array.from(document.querySelectorAll("*")).map((element) => { const rect = element.getBoundingClientRect(); return { tag: element.tagName, className: element.className?.toString().slice(0, 80), left: rect.left, right: rect.right, width: rect.width }; }).filter((element) => (element.right > window.innerWidth + 1 && element.right < window.innerWidth + 40) || element.left < -1).slice(0, 12), })); const pageScroll = await page.evaluate(() => { document.documentElement.scrollLeft = 999; const scrollLeft = document.documentElement.scrollLeft; document.documentElement.scrollLeft = 0; return scrollLeft; }); expect(pageScroll, JSON.stringify(overflow)).toBe(0); await page.evaluate(() => { const userKey = window.localStorage.getItem("authUserKey") || "anon"; window.localStorage.setItem(`themeMode:${userKey}`, "light"); }); await page.reload(); await expect(page.locator("html")).toHaveAttribute("data-color-scheme", "light"); const lightColumn = page.getByText("Not Applied", { exact: true }).locator("xpath=ancestor::*[contains(@class,'MuiPaper-root')][1]"); expect(await lightColumn.evaluate((element) => getComputedStyle(element).backgroundColor)).toBe("rgb(245, 242, 250)"); await page.setViewportSize({ width: 1440, height: 900 }); const lightCard = page.getByText("Synthetic platform engineer", { exact: true }).locator("xpath=ancestor::*[contains(@class,'MuiCard-root')][1]"); await lightCard.hover(); expect(await lightCard.evaluate((element) => getComputedStyle(element).transform)).not.toBe("none"); await lightCard.dragTo(page.getByRole("group", { name: "Active column" })); await expect.poll(() => moveRequests).toBe(2); await page.setViewportSize({ width: 375, height: 812 }); const mobileScroll = await page.evaluate(() => { document.documentElement.scrollLeft = 999; const scrollLeft = document.documentElement.scrollLeft; document.documentElement.scrollLeft = 0; return scrollLeft; }); expect(mobileScroll).toBe(0); });