Accessibility Testing — Automated Coverage
| Field | Value |
|---|---|
| Type | Agent Reference |
| Source | ~/.copilot/agents/_refs/senior-qa/accessibility-testing.md |
| Description | Not specified |
Source Content
Accessibility Testing — Automated Coverage
axe in Component Tests
Run axe on every component. This catches ~57% of WCAG issues automatically:
// Setup — vitest.setup.tsimport { toHaveNoViolations } from 'jest-axe'expect.extend(toHaveNoViolations)// In any component testimport { render } from '@testing-library/react'import { axe } from 'jest-axe'
it('has no axe violations', async () => { const { container } = render(<MyComponent {...mockProps} />) expect(await axe(container)).toHaveNoViolations()})Run axe on every meaningful state:
describe('UserCard accessibility', () => { const defaultProps = { user: mockUser, onEdit: vi.fn(), onDelete: vi.fn() }
it('default state has no violations', async () => { const { container } = render(<UserCard {...defaultProps} />) expect(await axe(container)).toHaveNoViolations() })
it('deleting state has no violations', async () => { const { container } = render(<UserCard {...defaultProps} isDeleting />) expect(await axe(container)).toHaveNoViolations() })
it('error state has no violations', async () => { const { container } = render(<UserCard {...defaultProps} error="Failed to load" />) expect(await axe(container)).toHaveNoViolations() })})Targeting specific rules:
// Only check specific WCAG rules (for known partial test contexts)expect( await axe(container, { rules: { // Disable color-contrast in a context where background is unknown 'color-contrast': { enabled: false }, }, })).toHaveNoViolations()Never globally disable rules — always disable at the test level with a comment explaining why.
E2E accessibility checks
No prescribed E2E framework. If a project adopts one (Cypress, WebdriverIO, Vitest browser mode, etc.), run axe at the page level using that framework’s axe integration. Cover the same WCAG tag set used at the component layer (wcag2a, wcag2aa, wcag21aa, wcag22aa).
Keyboard Contract Tests
Test keyboard flows at the component layer with RTL + @testing-library/user-event — dialogs, selects, menus, comboboxes. Cover: open with Enter/Space, focus moves into the widget, Tab cycles inside (focus trap), Escape closes and returns focus to the trigger. If an E2E suite exists, repeat the same contracts there using its keyboard primitives.
Focus Ring Visual Test
Verify focus rings are visible with a Lost Pixel snapshot:
// In Storybook storyexport const KeyboardFocused: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement) // Tab to the interactive element await userEvent.tab() // Screenshot is taken in this state — verifies focus ring is visible }, parameters: { // Lost Pixel will snapshot this story showing the focused state lostpixel: { threshold: 0 }, // zero tolerance for focus ring regressions },}WCAG 2.2 Automated Coverage Map
| WCAG Criterion | Automated? | How |
|---|---|---|
| 1.1.1 Non-text content | Partial | axe checks missing alt text |
| 1.3.1 Info and relationships | Partial | axe checks semantic structure |
| 1.3.2 Meaningful sequence | Manual | Reading order requires human review |
| 1.3.3 Sensory characteristics | Manual | ”the button on the right” — manual |
| 1.4.1 Use of color | Partial | axe checks some cases; manual for complex |
| 1.4.3 Contrast (minimum) | Yes | axe color-contrast rule |
| 1.4.11 Non-text contrast | Partial | axe partially; manual for UI components |
| 2.1.1 Keyboard | Partial | RTL + userEvent keyboard tests (component layer) |
| 2.1.2 No keyboard trap | Yes | RTL focus trap tests (or E2E equivalent if present) |
| 2.4.3 Focus order | Partial | RTL tab sequence tests |
| 2.4.7 Focus visible | Partial | Lost Pixel focus ring snapshots |
| 2.5.3 Label in name | Partial | axe label checks |
| 3.3.1 Error identification | Yes | axe aria-invalid + error message tests |
| 3.3.2 Labels or instructions | Yes | axe label association checks |
| 4.1.1 Parsing | Yes | axe HTML validity checks |
| 4.1.2 Name, role, value | Partial | axe ARIA checks |
| 4.1.3 Status messages | Yes | axe aria-live region checks |
What automated testing cannot catch (manual required):
- Meaningful sequence / reading order
- Color used as the only visual signal in complex layouts
- Focus visible in non-standard themes
- Screen reader announcement quality
- Cognitive load and plain language
- Mobile touch target size (3mm minimum)