SPB Git
1.9 KB · 25 lines typescript
1/** Automated accessibility audit with axe-core over key public pages (desktop + iPhone). Writes docs/_a11y-results.json and exits 1 on serious/critical violations. */2import { chromium, devices } from "@playwright/test";3import AxeBuilder from "@axe-core/playwright";4import { writeFileSync } from "node:fs";5const base = process.env.E2E_BASE_URL ?? "http://127.0.0.1:8540";6const pages = ["/", "/tools", "/tools?q=python", "/tools/duckdb", "/categories", "/categories/analytics-engines", "/collections", "/compare?tools=duckdb,postgresql,sqlite", "/about", "/login"];7const browser = await chromium.launch(); const results: any[] = []; let serious = 0;8for (const device of [null, devices["iPhone 15"]]) {9  const ctx = await browser.newContext(device ? { ...device } : { viewport: { width: 1440, height: 900 } });10  for (const p of pages) {11    const page = await ctx.newPage(); await page.goto(base + p, { waitUntil: "networkidle" });12    const r = await new AxeBuilder({ page }).withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa", "best-practice"]).analyze();13    const v = r.violations.map((x) => ({ id: x.id, impact: x.impact, help: x.help, nodes: x.nodes.length, targets: x.nodes.slice(0, 3).map((n) => n.target.join(" ")) }));14    serious += v.filter((x) => x.impact === "serious" || x.impact === "critical").length;15    results.push({ page: p, device: device ? "iphone" : "desktop", passes: r.passes.length, violations: v });16    console.log(`${device ? "📱" : "🖥 "} ${p}: ${v.length} violation(s)${v.length ? " — " + v.map((x) => `${x.id}(${x.impact})`).join(", ") : ""}`);17    await page.close();18  }19  await ctx.close();20}21await browser.close();22writeFileSync("docs/_a11y-results.json", JSON.stringify({ at: new Date().toISOString(), base, serious, results }, null, 2));23console.log(JSON.stringify({ pages: results.length, serious }));24if (serious) process.exit(1);25
Files · f05ed656dfb02e05e9363ab635689d5774c375f6
Menu

Theme