Accessibility Testing with axe-core
axe-core is a library from Deque Systems for automated accessibility testing (WCAG 2.1/2.2). It runs in the browser and Node.js, integrates with Jest, Playwright, Cypress, and Storybook. It automatically detects approximately 57% of accessibility violations.
Integration with Jest + Testing Library
npm install --save-dev jest-axe @testing-library/react
// __tests__/accessibility.test.tsx
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
describe('Component accessibility', () => {
it('Button has no violations', async () => {
const { container } = render(<Button>Submit</Button>);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
it('Form has no violations', async () => {
const { container } = render(
<form>
<label htmlFor="email">Email</label>
<input id="email" type="email" />
<button type="submit">Sign In</button>
</form>
);
const results = await axe(container, {
rules: {
'color-contrast': { enabled: true },
'label': { enabled: true },
},
});
expect(results).toHaveNoViolations();
});
});
Integration with Playwright
// tests/accessibility.spec.ts
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('Homepage passes WCAG 2.1 AA', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.exclude('.cookie-banner') // Exclude third-party components
.analyze();
expect(results.violations).toEqual([]);
});
test('Registration form accessibility', async ({ page }) => {
await page.goto('/register');
const results = await new AxeBuilder({ page })
.include('#registration-form')
.analyze();
// Detailed report on failure
if (results.violations.length > 0) {
console.table(results.violations.map(v => ({
id: v.id,
impact: v.impact,
description: v.description,
nodes: v.nodes.length,
})));
}
expect(results.violations).toEqual([]);
});
Integration with Storybook
npm install --save-dev @storybook/addon-a11y
// .storybook/main.js
module.exports = {
addons: ['@storybook/addon-a11y'],
};
// In stories
export default {
component: Button,
parameters: {
a11y: {
config: {
rules: [{ id: 'color-contrast', enabled: true }],
},
},
},
};
Programmatic Browser Run
// For auditing arbitrary page via console
import axe from 'axe-core';
axe.run(document, {
runOnly: { type: 'tag', values: ['wcag2aa'] },
}).then(results => {
console.log('Violations:', results.violations.length);
results.violations.forEach(v => {
console.group(`[${v.impact.toUpperCase()}] ${v.id}`);
console.log(v.description);
v.nodes.forEach(n => console.log(' Element:', n.target[0]));
console.groupEnd();
});
});
Violation Levels
| Impact | Meaning | Example |
|---|---|---|
| critical | Blocks access | Image without alt |
| serious | Significantly impairs | Form without label |
| moderate | Difficult to use | Insufficient contrast |
| minor | Minor inconvenience | Incorrect lang |
Timeline
Setting up axe-core in Jest + Playwright, covering key pages and components: 2–3 business days.







