import AxeBuilder from "@axe-core/playwright"; import { expect, test } from "../support/browser/strict-browser-test.ts"; import { ROUTE_REGISTRY } from "../../src/features/installed-feature-contracts.ts"; for (const route of Object.values(ROUTE_REGISTRY).map((definition) => { if (definition.path === "*") return "/not-found"; return definition.path.replace(":resourceId", "reference-1"); })) { test(`@a11y ${route} has no critical or serious axe violations`, async ({ page, }) => { await page.goto(route); await expect(page.getByRole("main")).toBeVisible(); const results = await new AxeBuilder({ page }) .withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"]) .analyze(); const blocking = results.violations.filter((violation) => ["critical", "serious"].includes(violation.impact ?? ""), ); expect(blocking).toEqual([]); }); } test("@a11y keyboard reaches the primary route action with visible focus", async ({ page, }) => { await page.goto("/"); const action = page.getByRole("link", { name: "플랫폼 구성 보기" }); await expect(action).toBeVisible(); await page.keyboard.press("Tab"); await expect(action).toBeFocused(); await expect(action).toHaveCSS("outline-style", "solid"); }); test("@a11y reduced-motion policy disables long animation", async ({ page }) => { await page.emulateMedia({ reducedMotion: "reduce" }); await page.goto("/"); const duration = await page .locator("body") .evaluate((body) => getComputedStyle(body).animationDuration); expect(["0s", "0.00001s", "1e-05s"]).toContain(duration); }); test("@a11y opened design-system dialog has no blocking violations", async ({ page, }) => { await page.goto("/examples/ui"); await page.getByRole("button", { name: "모달 열기" }).click(); const results = await new AxeBuilder({ page }) .include(".ui-dialog") .withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"]) .analyze(); expect( results.violations.filter((violation) => ["critical", "serious"].includes(violation.impact ?? ""), ), ).toEqual([]); });