57 lines
1.6 KiB
JavaScript
57 lines
1.6 KiB
JavaScript
/**
|
|
* 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: ['<unsafe_all_origins>'] })
|
|
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')
|
|
}
|