25 / 74 · 10 Visual & Accessibility Testing · axe-core Integration← prev⊞ allnext →☰ Read as one page
5.2Playwright + axe-core
// tests/accessibility/axe-audit.spec.ts
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test.describe('Accessibility Audit', () => {
test('homepage has no critical accessibility violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa']) // WCAG 2.1 AA
.exclude('#third-party-widget') // Exclude content you don't control
.analyze();
// Report all violations with details
if (results.violations.length > 0) {
const report = results.violations.map(v => ({
rule: v.id,
impact: v.impact,
description: v.description,
helpUrl: v.helpUrl,
nodes: v.nodes.length,
elements: v.nodes.map(n => n.html).slice(0, 3),
}));
console.log('Accessibility violations:', JSON.stringify(report, null, 2));
}
expect(results.violations).toEqual([]);
});
test('form page meets WCAG 2.1 AA', async ({ page }) => {
await page.goto('/checkout');
const results = await new AxeBuilder({ page })
.withTags(['wcag2aa'])
.analyze();
// Separate critical from minor violations
const critical = results.violations.filter(v =>
v.impact === 'critical' || v.impact === 'serious'
);
const minor = results.violations.filter(v =>
v.impact === 'moderate' || v.impact === 'minor'
);
// Critical violations fail the build
expect(critical).toEqual([]);
// Minor violations are logged as warnings
if (minor.length > 0) {
console.warn(`${minor.length} minor accessibility issues found`);
minor.forEach(v => console.warn(` - ${v.id}: ${v.description}`));
}
});
test('all pages pass accessibility audit', async ({ page }) => {
const pages = [
'/', '/login', '/register', '/products',
'/products/1', '/cart', '/checkout', '/account',
'/help', '/about', '/privacy', '/terms'
];
const allViolations: Record<string, any[]> = {};
for (const pagePath of pages) {
await page.goto(pagePath);
const results = await new AxeBuilder({ page })
.withTags(['wcag2aa'])
.analyze();
if (results.violations.length > 0) {
allViolations[pagePath] = results.violations;
}
}
const totalViolations = Object.values(allViolations)
.reduce((sum, v) => sum + v.length, 0);
if (totalViolations > 0) {
console.error('Accessibility violations by page:');
for (const [pagePath, violations] of Object.entries(allViolations)) {
console.error(`\n${pagePath}:`);
violations.forEach(v =>
console.error(` [${v.impact}] ${v.id}: ${v.description} (${v.nodes.length} elements)`)
);
}
}
expect(totalViolations).toBe(0);
});
});