From harness-claude
> Automate accessibility testing with axe-core, jest-axe, Playwright, and CI pipeline integration
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Automate accessibility testing with axe-core, jest-axe, Playwright, and CI pipeline integration
Automates WCAG accessibility checks using axe-core with Playwright for E2E tests and jest-axe for React components. Tests keyboard navigation, ARIA attributes, and screen reader compatibility.
Automates accessibility testing with Playwright (@playwright/test), TypeScript, and axe-core for WCAG 2.1 AA compliance, keyboard navigation, focus management, ARIA validations, and semantic checks.
Tests web apps for WCAG compliance, ARIA attributes, keyboard navigation, screen reader compatibility, and a11y issues using axe-core with Playwright, Cypress, Jest, and Selenium.
Share bugs, ideas, or general feedback.
Automate accessibility testing with axe-core, jest-axe, Playwright, and CI pipeline integration
npm install -D jest-axe @types/jest-axe
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
describe('LoginForm', () => {
it('should have no accessibility violations', async () => {
const { container } = render(<LoginForm />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
it('should have no violations in error state', async () => {
const { container } = render(<LoginForm errors={{ email: 'Required' }} />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
});
const states = [
{ name: 'default', props: {} },
{ name: 'loading', props: { isLoading: true } },
{ name: 'error', props: { error: 'Something went wrong' } },
{ name: 'empty', props: { items: [] } },
];
states.forEach(({ name, props }) => {
it(`should have no a11y violations in ${name} state`, async () => {
const { container } = render(<DataTable {...props} />);
expect(await axe(container)).toHaveNoViolations();
});
});
npm install -D @axe-core/playwright
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('homepage should have no a11y violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.analyze();
expect(results.violations).toEqual([]);
});
test('checkout flow should have no a11y violations', async ({ page }) => {
await page.goto('/checkout');
// Fill form to trigger validation states
await page.click('button[type="submit"]');
await page.waitForSelector('[role="alert"]');
const results = await new AxeBuilder({ page })
.exclude('.third-party-widget') // Exclude content you do not control
.analyze();
expect(results.violations).toEqual([]);
});
npm install -D eslint-plugin-jsx-a11y
{
"extends": ["plugin:jsx-a11y/recommended"],
"plugins": ["jsx-a11y"]
}
Key rules this enables:
alt-text — images must have altanchor-is-valid — <a> must have hrefclick-events-have-key-events — onClick must have onKeyDown/onKeyUplabel-has-associated-control — labels must be linked to inputsno-noninteractive-element-interactions — no click handlers on divsgetByRole, getByLabelText, and getByAltText verify accessibility as a side effect of writing tests.import { render, screen } from '@testing-library/react';
// These queries fail if accessibility is broken
const submitButton = screen.getByRole('button', { name: 'Submit' });
const emailInput = screen.getByLabelText('Email address');
const logo = screen.getByAltText('Company Logo');
const nav = screen.getByRole('navigation', { name: 'Main' });
const alert = screen.getByRole('alert');
# GitHub Actions
- name: Run a11y tests
run: npx playwright test --grep @a11y
- name: Run component a11y tests
run: npx vitest run --grep "accessibility"
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.disableRules(['color-contrast']) // Disable if you have a separate contrast testing workflow
.analyze();
import { createHtmlReport } from 'axe-html-reporter';
const results = await new AxeBuilder({ page }).analyze();
createHtmlReport({
results,
options: { outputDir: 'a11y-reports', reportFileName: 'homepage.html' },
});
What automated testing catches (~30-40% of issues):
What automated testing misses (~60-70% of issues):
Testing pyramid for accessibility:
Handling existing violations: When adding axe to an existing project, you may find hundreds of violations. Use disableRules or exclude selectively to establish a baseline, then fix violations incrementally and remove exclusions over time.
https://github.com/dequelabs/axe-core