Files
tech-log-frontend/tests/e2e/tech-log-accessibility.spec.ts

78 lines
2.9 KiB
TypeScript

import AxeBuilder from "@axe-core/playwright";
import { expect, test } from "../support/browser/strict-browser-test.ts";
import { gotoTechLog } from "../support/browser/tech-log-fixtures.ts";
function blockingViolations(results: Awaited<ReturnType<AxeBuilder["analyze"]>>) {
return results.violations.filter((violation) =>
["critical", "serious"].includes(violation.impact ?? ""),
);
}
test("Public shell exposes one main landmark, one h1, labelled navigation, and current link", async ({
page,
}) => {
await gotoTechLog(page, "/projects");
await expect(page.getByRole("main")).toHaveCount(1);
await expect(page.getByRole("heading", { level: 1 })).toHaveCount(1);
await expect(page.getByRole("navigation", { name: "주요 탐색" })).toBeVisible();
await expect(page.getByRole("link", { name: "프로젝트", exact: true })).toHaveAttribute(
"aria-current",
"page",
);
});
test("Studio mobile navigation is keyboard reachable and exposes its expanded state", async ({
page,
}) => {
await page.setViewportSize({ width: 390, height: 844 });
await gotoTechLog(page, "/studio");
const trigger = page.locator(".studio-menu-trigger");
await trigger.focus();
await page.keyboard.press("Enter");
await expect(trigger).toHaveAttribute("aria-expanded", "true");
await expect(page.getByRole("navigation", { name: "Studio 모바일 탐색" })).toBeVisible();
});
test("dirty-leave dialog is labelled, traps focus, and restores the invoking link", async ({
page,
}) => {
await gotoTechLog(
page,
"/studio/documents/11111111-1111-4111-8111-111111111111/edit",
);
await page.getByLabel("요약").fill("저장하지 않은 접근성 검증 변경");
const trigger = page.getByRole("link", { name: "게시 기록", exact: true }).first();
await trigger.click();
const dialog = page.getByRole("dialog", { name: "저장하지 않은 변경" });
await expect(dialog).toBeVisible();
await expect(dialog.getByRole("button", { name: "이 페이지에 머무르기" })).toBeFocused();
await page.keyboard.press("Escape");
await expect(dialog).toBeHidden();
await expect(trigger).toBeFocused();
});
test("open Public search and Studio leave dialogs have no blocking Axe findings", async ({
page,
}) => {
await gotoTechLog(page, "/");
await page.getByRole("button", { name: "검색 열기" }).click();
let results = await new AxeBuilder({ page })
.include("dialog")
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
.analyze();
expect(blockingViolations(results)).toEqual([]);
await gotoTechLog(
page,
"/studio/documents/11111111-1111-4111-8111-111111111111/edit",
);
await page.getByLabel("요약").fill("dialog axe state");
await page.getByRole("link", { name: "게시 기록", exact: true }).first().click();
results = await new AxeBuilder({ page })
.include("dialog")
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
.analyze();
expect(blockingViolations(results)).toEqual([]);
});