57 lines
2.7 KiB
TypeScript
57 lines
2.7 KiB
TypeScript
import AxeBuilder from "@axe-core/playwright";
|
|
import { expect, loginAs, test, type Persona } from "./fixtures";
|
|
|
|
const routes: { path: string; persona: Persona }[] = [
|
|
{ path: "/", persona: "hub-viewer" },
|
|
{ path: "/projects", persona: "hub-viewer" },
|
|
{ path: "/servers", persona: "hub-viewer" },
|
|
{ path: "/tools", persona: "hub-viewer" },
|
|
{ path: "/connectors", persona: "hub-viewer" },
|
|
{ path: "/clients", persona: "hub-viewer" },
|
|
{ path: "/policies", persona: "hub-viewer" },
|
|
{ path: "/operations", persona: "hub-viewer" },
|
|
{ path: "/audit", persona: "hub-auditor" },
|
|
{ path: "/settings", persona: "hub-viewer" },
|
|
];
|
|
|
|
test.describe("accessibility and responsive baseline", () => {
|
|
test("the unauthenticated gateway has landmarks, a skip link, and no serious axe findings", async ({ page }) => {
|
|
await page.goto("/");
|
|
await expect(page.getByRole("link", { name: "Skip to main content" })).toBeVisible();
|
|
await expect(page.getByRole("main")).toHaveCount(1);
|
|
const result = await new AxeBuilder({ page }).analyze();
|
|
expect(result.violations.filter((violation) => ["serious", "critical"].includes(violation.impact ?? ""))).toEqual([]);
|
|
});
|
|
|
|
for (const viewport of [{ width: 390, height: 844 }, { width: 1440, height: 900 }]) {
|
|
test(`authorized routes have no serious axe findings or page overflow at ${viewport.width}px`, async ({ page }) => {
|
|
test.setTimeout(90_000);
|
|
await page.setViewportSize(viewport);
|
|
for (const persona of ["hub-viewer", "hub-auditor"] as const) {
|
|
const personaRoutes = routes.filter((route) => route.persona === persona);
|
|
if (!personaRoutes.length) continue;
|
|
await loginAs(page, persona);
|
|
for (const route of personaRoutes) {
|
|
await page.goto(route.path);
|
|
await expect(page.getByRole("main")).toBeVisible();
|
|
const overflow = await page.evaluate(() => document.documentElement.scrollWidth > window.innerWidth + 1);
|
|
expect(overflow, `${route.path} must not cause page-level horizontal overflow`).toBe(false);
|
|
const result = await new AxeBuilder({ page }).analyze();
|
|
expect(result.violations.filter((violation) => ["serious", "critical"].includes(violation.impact ?? "")), route.path).toEqual([]);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
test("mobile drawer and modal preserve keyboard focus", async ({ page }) => {
|
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
await loginAs(page, "hub-viewer");
|
|
await page.goto("/");
|
|
const trigger = page.getByRole("button", { name: "Open navigation" });
|
|
await trigger.focus();
|
|
await trigger.press("Enter");
|
|
await page.getByRole("button", { name: "Close navigation" }).press("Escape");
|
|
await expect(trigger).toBeFocused();
|
|
});
|
|
});
|