import AxeBuilder from "@axe-core/playwright"; import { expect, test } from "@playwright/test"; import { newIsolatedContext } from "./helpers"; async function expectAccessible(page: import("@playwright/test").Page): Promise { const results = await new AxeBuilder({ page }).analyze(); expect( results.violations, results.violations .map( (violation) => `${violation.id}: ${violation.help}\n${violation.nodes .map((node) => ` ${node.target.join(" ")}: ${node.failureSummary}`) .join("\n")}`, ) .join("\n\n"), ).toEqual([]); } test("public pages are accessible by keyboard in light and dark themes", async ({ browser }) => { const context = await newIsolatedContext(browser); const page = await context.newPage(); await page.goto("/"); await page.keyboard.press("Tab"); await expect(page.getByRole("link", { name: "Skip to main content" })).toBeFocused(); await page.keyboard.press("Enter"); await expect(page.locator("#main-content")).toBeFocused(); for (const theme of ["light", "dark"] as const) { await page.getByRole("button", { name: `Use ${theme} theme` }).click(); await expect(page.locator("html")).toHaveAttribute("data-theme", theme); for (const path of ["/", "/safety", "/users/log-in", "/users/register"]) { await page.goto(path); await expectAccessible(page); } } await context.close(); }); test("public navigation does not overflow narrow or wide viewports", async ({ browser }) => { for (const viewport of [ { width: 360, height: 800 }, { width: 768, height: 1024 }, { width: 1030, height: 900 }, { width: 1440, height: 900 }, ]) { const context = await newIsolatedContext(browser); const page = await context.newPage(); await page.setViewportSize(viewport); await page.goto("/"); const dimensions = await page.evaluate(() => ({ clientWidth: document.documentElement.clientWidth, scrollWidth: document.documentElement.scrollWidth, })); expect(dimensions.scrollWidth).toBeLessThanOrEqual(dimensions.clientWidth); await expect(page.getByRole("navigation", { name: "Primary navigation" })).toBeVisible(); await expect(page.locator("body > header")).toHaveCount(1); await expect(page.locator("body > nav[aria-label='Account and language']")).toHaveCount(0); if (viewport.width < 1280) { const menuButton = page.locator("summary[aria-label='Primary navigation']"); await expect(menuButton).toBeVisible(); await menuButton.click(); await expect(page.getByRole("link", { name: "Requests", exact: true })).toBeVisible(); await expect(page.getByRole("link", { name: "Activities", exact: true })).toBeVisible(); await expect(page.getByRole("link", { name: "Categories", exact: true })).toBeVisible(); await expect(page.getByRole("link", { name: "Helpers", exact: true })).toBeVisible(); await expect(page.getByRole("link", { name: "English", exact: true })).toBeVisible(); await expect(page.getByRole("link", { name: "Log in", exact: true })).toBeVisible(); const openDimensions = await page.evaluate(() => ({ clientWidth: document.documentElement.clientWidth, scrollWidth: document.documentElement.scrollWidth, })); expect(openDimensions.scrollWidth).toBeLessThanOrEqual(openDimensions.clientWidth); } else { await expect(page.locator("summary[aria-label='Primary navigation']")).toBeHidden(); await expect(page.getByRole("link", { name: "Requests", exact: true })).toBeVisible(); await expect(page.getByRole("link", { name: "English", exact: true })).toBeVisible(); await expect(page.getByRole("link", { name: "Register", exact: true })).toBeVisible(); await expect(page.getByRole("link", { name: "Log in", exact: true })).toBeVisible(); } await context.close(); } });