# Frontend Standards - Accessibility Testing > **Module:** testing-accessibility.md | **Sections:** 5 | **Parent:** [frontend.md](../frontend.md) This module covers automated accessibility testing patterns for React/Next.js applications. WCAG 2.1 AA compliance is verified through axe-core, keyboard navigation testing, and focus management validation. > **Gate Reference:** This module is loaded by `ring:qa-analyst-frontend` at Gate 2 (Accessibility Testing). --- ## Table of Contents | # | [Section Name](#anchor-link) | Description | |---|------------------------------|-------------| | 1 | [axe-core Integration](#axe-core-integration-mandatory) | Automated WCAG scanning setup | | 2 | [Semantic HTML Verification](#semantic-html-verification-mandatory) | HTML element correctness | | 3 | [Keyboard Navigation](#keyboard-navigation-mandatory) | Tab order and key handling | | 4 | [Focus Management](#focus-management-mandatory) | Focus trap, auto-focus, restoration | | 5 | [Color Contrast](#color-contrast-mandatory) | Contrast ratio verification | **Meta-sections:** [Output Format (Gate 2 - Accessibility Testing)](#output-format-gate-2---accessibility-testing), [Anti-Rationalization Table](#anti-rationalization-table-accessibility-testing) --- ## axe-core Integration (MANDATORY) **HARD GATE:** All components MUST pass axe-core automated scans with zero WCAG 2.1 AA violations. ### Required Tools | Tool | Purpose | Install | |------|---------|---------| | `@axe-core/playwright` | E2E accessibility scanning | `npm i -D @axe-core/playwright` | | `jest-axe` | Unit-level accessibility testing | `npm i -D jest-axe` | | `axe-core` | Core engine (peer dependency) | `npm i -D axe-core` | ### Unit Test Pattern (jest-axe + Vitest) ```tsx import { render } from '@testing-library/react'; import { axe, toHaveNoViolations } from 'jest-axe'; expect.extend(toHaveNoViolations); describe('LoginForm accessibility', () => { it('MUST have no WCAG AA violations', async () => { const { container } = render(); const results = await axe(container); expect(results).toHaveNoViolations(); }); it('MUST have no violations in error state', async () => { const { container } = render(); const results = await axe(container); expect(results).toHaveNoViolations(); }); }); ``` ### E2E Pattern (@axe-core/playwright) ```typescript import { test, expect } from '@playwright/test'; import AxeBuilder from '@axe-core/playwright'; test('login page MUST be accessible', async ({ page }) => { await page.goto('/login'); const accessibilityScanResults = await new AxeBuilder({ page }) .withTags(['wcag2a', 'wcag2aa']) .analyze(); expect(accessibilityScanResults.violations).toEqual([]); }); ``` ### States to Test | State | Why | Example | |-------|-----|---------| | Default | Baseline compliance | Component in normal state | | Loading | Spinners need ARIA | `aria-busy="true"`, live region | | Error | Error messages need association | `aria-describedby`, `role="alert"` | | Empty | Empty states need content | Descriptive empty message | | Disabled | Disabled controls need indication | `aria-disabled`, visual cue | ### FORBIDDEN Patterns ```tsx // FORBIDDEN: Suppressing violations const results = await axe(container, { rules: { 'color-contrast': { enabled: false } } // NEVER suppress }); // FORBIDDEN: Testing only happy path // MUST test error, loading, empty, disabled states too ``` --- ## Semantic HTML Verification (MANDATORY) **HARD GATE:** All interactive elements MUST use correct semantic HTML elements. See [frontend.md Section 12 - Accessibility Anti-Patterns](../frontend.md#forbidden-patterns). ### Required Checks | Element | Correct | FORBIDDEN | Test Pattern | |---------|---------|-----------|--------------| | Buttons | `