// Automated a11y scan (axe-core, WCAG 2.2 AA rules) of the main views. Run against `pnpm preview`. import { readFile } from 'node:fs/promises'; import { chromium } from 'playwright-core'; const axeSrc = await readFile('node_modules/axe-core/axe.min.js', 'utf8'); const URL = process.env.URL ?? 'http://localhost:4173/'; const browser = await chromium.launch(); const page = await browser.newPage({ viewport: { width: 1280, height: 900 }, locale: 'pl-PL' }); let bad = 0; async function scan(name) { await page.addScriptTag({ content: axeSrc }); const r = await page.evaluate(() => axe.run(document, { runOnly: { type: 'tag', values: ['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa', 'best-practice'] } })); console.log(`${name}: ${r.violations.length} violation(s)`); for (const v of r.violations) { bad++; console.log(` [${v.impact}] ${v.id}: ${v.help} — ${v.nodes.length} node(s), e.g. ${v.nodes[0].target.join(' ')}`); } } await page.goto(URL, { waitUntil: 'networkidle' }); await scan('login'); await page.click('text=Zaloguj jako Maria Wiśniewska'); await page.waitForSelector('.kbb-kpis'); await scan('dashboard'); await page.click('nav >> text=Miejsca'); await page.waitForSelector('#q'); await scan('places'); await page.fill('#q', 'Sukiennice'); await page.click('button[id^=place-]'); await page.waitForSelector('text=Szczegóły dostępności'); await scan('place detail'); await page.click('nav >> text=Trasa'); await page.click('text=Sukiennice → Zamek Królewski na Wawelu'); await page.waitForSelector('text=Opis trasy tekstem'); await scan('route'); await page.click('nav >> text=Profil'); await page.waitForSelector('#st1'); await scan('profile + settings'); await page.click('nav >> text=Dane'); await scan('about'); await browser.close(); process.exitCode = bad ? 1 : 0;