From harness-claude
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.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Automate WCAG accessibility checks using axe-core with Playwright and jest-axe
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.
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.
> Automate accessibility testing with axe-core, jest-axe, Playwright, and CI pipeline integration
Share bugs, ideas, or general feedback.
Automate WCAG accessibility checks using axe-core with Playwright and jest-axe
npm install -D @axe-core/playwright
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('homepage has no accessibility violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});
test('login form is accessible', async ({ page }) => {
await page.goto('/login');
const results = await new AxeBuilder({ page }).include('#login-form').analyze();
expect(results.violations).toEqual([]);
});
const results = await new AxeBuilder({ page })
.exclude('#legacy-widget') // Known issue, tracked in JIRA-123
.disableRules(['color-contrast']) // Temporarily disable specific rules
.analyze();
npm install -D jest-axe @types/jest-axe
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
it('Button component has no a11y violations', async () => {
const { container } = render(
<Button onClick={() => {}}>Click me</Button>
);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
test('tab order follows visual layout', async ({ page }) => {
await page.goto('/form');
await page.keyboard.press('Tab');
await expect(page.getByLabel('Name')).toBeFocused();
await page.keyboard.press('Tab');
await expect(page.getByLabel('Email')).toBeFocused();
await page.keyboard.press('Tab');
await expect(page.getByRole('button', { name: 'Submit' })).toBeFocused();
});
test('dropdown opens with Enter and navigates with arrow keys', async ({ page }) => {
await page.goto('/select');
const select = page.getByRole('combobox', { name: 'Country' });
await select.focus();
await page.keyboard.press('Enter');
await expect(page.getByRole('listbox')).toBeVisible();
await page.keyboard.press('ArrowDown');
await page.keyboard.press('ArrowDown');
await page.keyboard.press('Enter');
await expect(select).toHaveValue('Canada');
});
test('error messages are associated with inputs', async ({ page }) => {
await page.goto('/form');
// Submit empty form to trigger validation
await page.getByRole('button', { name: 'Submit' }).click();
const emailInput = page.getByLabel('Email');
const errorId = await emailInput.getAttribute('aria-describedby');
expect(errorId).toBeTruthy();
const errorMessage = page.locator(`#${errorId}`);
await expect(errorMessage).toHaveText('Email is required');
await expect(emailInput).toHaveAttribute('aria-invalid', 'true');
});
const pages = ['/', '/about', '/contact', '/products', '/login'];
for (const path of pages) {
test(`${path} has no a11y violations`, async ({ page }) => {
await page.goto(path);
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});
}
Automated accessibility testing catches approximately 30-50% of WCAG violations. The remaining violations require manual testing (screen reader behavior, cognitive load, content clarity).
What axe-core catches:
What axe-core cannot catch:
WCAG conformance levels:
axe-core rule tags: Filter by conformance level with .withTags(['wcag2a', 'wcag2aa', 'best-practice']).
Trade-offs:
https://playwright.dev/docs/accessibility-testing