GdzieWjade/scripts/axe.mjs

47 lines
2.5 KiB
JavaScript
Raw Normal View History

2026-10-03 14:29:28 +02:00
// 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();
2026-10-03 17:08:38 +02:00
// VIEWPORT=390x844 runs the same scan with a phone-sized (touch) viewport
const [vw, vh] = (process.env.VIEWPORT ?? '1280x900').split('x').map(Number);
const page = await browser.newPage({ viewport: { width: vw, height: vh }, isMobile: vw < 700, hasTouch: vw < 700, locale: 'pl-PL' });
2026-10-03 14:29:28 +02:00
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');
2026-10-03 17:08:38 +02:00
await page.waitForSelector('[role=tablist]');
for (const [tab, name] of [['Zgłoszenia', 'dashboard: reports'], ['Przejścia', 'dashboard: crossings'], ['Jakość', 'dashboard: quality'], ['Eksport', 'dashboard: export']]) {
await page.click(`[role=tab]:has-text("${tab}")`);
await scan(name);
}
2026-10-03 14:29:28 +02:00
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');
2026-10-03 19:14:41 +02:00
await page.fill('#route-from', 'Sukiennice');
await page.fill('#route-to', 'Zamek Królewski na Wawelu');
await page.click('button[type=submit]:has-text("Wyznacz")');
2026-10-03 14:29:28 +02:00
await page.waitForSelector('text=Opis trasy tekstem');
2026-10-03 19:14:41 +02:00
await page.waitForTimeout(1500); // let the map settle: pins that are still moving overlap and trip the target-size rule
await page.evaluate(() => document.querySelector('.kbb-panel')?.scrollTo(0, 0)); // the result scrolls under the phone sheet's sticky handle; scan from the top
2026-10-03 14:29:28 +02:00
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;