/** * a11y-audit.mjs — Axe-core + jsdom audit sobre el HTML servidor por Next.js * Uso: node scripts/a11y-audit.mjs [url] */ import { JSDOM } from 'jsdom' import axe from 'axe-core' const url = process.argv[2] || 'http://localhost:3001' console.log(`Auditando ${url} …\n`) // 1. Fetch the rendered HTML from the dev server const res = await fetch(url) if (!res.ok) { console.error(`ERROR: servidor respondió ${res.status}`) process.exit(1) } const html = await res.text() // 2. Parse in jsdom const dom = new JSDOM(html, { url, runScripts: 'dangerously', resources: 'usable', }) const { window } = dom const { document } = window // Wait a tick for DOM to stabilize await new Promise((r) => setTimeout(r, 200)) // 3. Configure axe and run axe.configure({ allowedOrigins: [''] }) const results = await axe.run(document.body, { runOnly: { type: 'tag', values: ['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa'] }, }) const { violations, passes, inapplicable, incomplete } = results console.log(`✅ Reglas superadas: ${passes.length}`) console.log(`⚠️ Incompletas: ${incomplete.length}`) console.log(`— No aplican: ${inapplicable.length}`) console.log(`\n❌ Violaciones AA: ${violations.length}`) if (violations.length > 0) { for (const v of violations) { console.log(`\n [${v.impact?.toUpperCase()}] ${v.id} — ${v.description}`) console.log(` Help: ${v.helpUrl}`) for (const node of v.nodes.slice(0, 3)) { console.log(` HTML: ${node.html.slice(0, 120)}`) } } process.exit(1) } else { console.log('\n✅ 0 violaciones AA — PASS\n') }