import AxeBuilder from "@axe-core/playwright"; import { expect, test } from "@playwright/test"; for (const path of [ "/", "/records", "/report", "/rules", "/privacy", "/admin", "/admin/moderation", "/admin/external-sources", "/admin/media", ]) { test(`${path} has no serious accessibility violations`, async ({ page }) => { await page.goto(path); const results = await new AxeBuilder({ page }).withTags(["wcag2a", "wcag2aa", "wcag21aa"]).analyze(); expect(results.violations.filter(item => ["critical", "serious"].includes(item.impact ?? ""))).toEqual([]); }); } test("admin keyboard navigation keeps focus out of the document body", async ({ page }) => { await page.goto("/admin"); await page.keyboard.press("Tab"); await expect(page.locator(".skip-link")).toBeFocused(); const focusedLabels: string[] = []; for (let index = 0; index < 20; index += 1) { await page.keyboard.press("Tab"); focusedLabels.push(await page.evaluate(() => { const active = document.activeElement; if (active?.tagName === "BODY") return "BODY"; if (active?.matches(".admin-login input")) return "ADMIN_TOKEN_INPUT"; if (active?.matches(".admin-login button")) return "ADMIN_PANEL_BUTTON"; return (active?.getAttribute("aria-label") || active?.textContent || active?.tagName || "").trim().slice(0, 80); })); } expect(focusedLabels).not.toContain("BODY"); expect(focusedLabels).toContain("ADMIN_TOKEN_INPUT"); expect(focusedLabels).toContain("ADMIN_PANEL_BUTTON"); }); test("print media keeps a light color scheme and removes hero image filters", async ({ page }) => { await page.emulateMedia({ media: "print" }); await page.goto("/"); const printState = await page.evaluate(() => ({ colorScheme: getComputedStyle(document.documentElement).colorScheme, heroFilter: document.querySelector(".lake-card img") ? getComputedStyle(document.querySelector(".lake-card img")!).filter : "none", })); expect(printState.colorScheme).toBe("light"); expect(printState.heroFilter).toBe("none"); }); test("forced colors keeps theme controls visibly bordered", async ({ page }) => { await page.emulateMedia({ forcedColors: "active" }); await page.goto("/admin"); const forcedColorsState = await page.evaluate(() => { const control = document.querySelector(".theme-switcher button"); const pressed = document.querySelector(".theme-switcher button[aria-pressed='true']"); return { borderStyle: control ? getComputedStyle(control).borderStyle : "", borderWidth: control ? getComputedStyle(control).borderWidth : "", activeBackground: pressed ? getComputedStyle(pressed).backgroundColor : "", }; }); expect(forcedColorsState.borderStyle).toBe("solid"); expect(forcedColorsState.borderWidth).toBe("1px"); expect(forcedColorsState.activeBackground).not.toBe("transparent"); });