A production-grade automated accessibility testing suite demonstrating WCAG 2.1 AA compliance across an ecommerce checkout flow. Built with a multi-layer toolchain that catches violations at every level — from unit components to full page audits — with a CI gate that fails the build on any accessibility error.
Accessibility testing is one of the most underrepresented skills in QA portfolios. This repo demonstrates:
- Automated WCAG scanning at unit, integration, and E2E levels
- CI enforcement — axe violations fail the build before merge
- Real compliance coverage — ARIA, keyboard navigation, color contrast, screen reader semantics
- Multiple toolchain layers — each tool catches different classes of violations
| Tool | Layer | Purpose |
|---|---|---|
| axe-core | Foundation | WCAG rule engine powering all automated scans |
| jest-axe | Unit | Component-level axe assertions in Jest |
| @axe-core/playwright | E2E | Full-page WCAG scans in browser via Playwright |
| Pa11y | CLI Audit | Standalone WCAG 2.1 AA audits with JSON reports |
| Storybook + addon-a11y | Component | Visual a11y panel with axe on every story (see setup below) |
| GitHub Actions | CI/CD | Accessibility gate on every PR |
| TypeScript | DX | Full type safety across all test files |
accessibility-test-strategy/
├── .github/
│ └── workflows/
│ └── accessibility-ci.yml # CI pipeline with a11y gate
├── .storybook/
│ ├── main.ts # Storybook config with addon-a11y
│ └── preview.ts # Global axe config for all stories
├── public/
│ ├── index.html # Home page (WCAG 2.1 AA compliant)
│ ├── products.html # Product listing page
│ ├── checkout.html # Checkout form page
│ ├── checkout.js # Accessible form validation
│ └── styles.css # WCAG-compliant styles (4.5:1+ contrast)
├── scripts/
│ └── pa11y-audit.ts # Pa11y CLI audit runner
├── src/
│ └── components/
│ ├── CheckoutForm/
│ │ ├── CheckoutForm.tsx # Accessible React checkout form
│ │ ├── CheckoutForm.stories.tsx
│ │ └── index.ts
│ ├── ProductCard/
│ │ ├── ProductCard.tsx # Accessible product card
│ │ ├── ProductCard.stories.tsx
│ │ └── index.ts
│ └── NavigationMenu/
│ ├── NavigationMenu.tsx # Skip link + aria-current nav
│ ├── NavigationMenu.stories.tsx
│ └── index.ts
├── tests/
│ ├── __mocks__/
│ │ └── styleMock.ts
│ ├── setup.ts # Jest global setup (jest-dom)
│ ├── unit/
│ │ ├── CheckoutForm.a11y.test.tsx # jest-axe unit tests
│ │ ├── ProductCard.a11y.test.tsx
│ │ └── NavigationMenu.a11y.test.tsx
│ └── e2e/
│ ├── checkout.a11y.spec.ts # Playwright + axe E2E tests
│ └── product-listing.a11y.spec.ts
├── types/
│ ├── jest-axe.d.ts # Type declarations for jest-axe
│ └── pa11y.d.ts # Type declarations for pa11y
├── jest.config.ts
├── playwright.config.ts
├── tsconfig.json
├── tsconfig.test.json
└── package.json
- Node.js 18+
- npm 9+
npm install
npx playwright install --with-deps chromium firefox# Unit tests (jest-axe)
npm test
# E2E tests (Playwright + axe) — requires server running
npx serve -s public -l 3000 &
npm run test:e2e
# Pa11y CLI audit — requires server running
npm run test:pa11y
# All tests
npm run test:allStorybook requires a separate initialisation step. Run the following once to install it:
npx storybook initThen start it with:
npm run storybookOpen http://localhost:6006 and click the Accessibility tab on any story to see live axe results.
Each React component has a dedicated *.a11y.test.tsx file that:
- Runs
axe()on the rendered component and assertstoHaveNoViolations() - Tests all ARIA attributes (
aria-required,aria-invalid,aria-describedby) - Verifies label associations, landmark roles, and keyboard semantics
- Covers both default and error/edge-case states
it('has no axe violations on initial render', async () => {
const { container } = render(<CheckoutForm />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});Full-page WCAG 2.1 AA scans in real Chromium and Firefox:
- Scans entire rendered page with
AxeBuilder.withTags(['wcag2a', 'wcag2aa', 'wcag21aa']) - Targeted rule checks: color contrast, heading order, image alt, landmark structure
- Keyboard-only navigation flows (Tab, Enter, form submission)
- Screen reader announcement verification via
role="alert"andaria-live
const results = await new AxeBuilder({ page: axePage(page) })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.analyze();
expect(results.violations).toEqual([]);Standalone WCAG 2.1 AA audit against all pages:
- Runs against live server on
localhost:3000 - Outputs JSON reports to
pa11y-reports/ - Exits with code
1if any errors found (CI gate) - Covers errors, warnings, and notices separately
Every component story is pre-configured to run axe automatically once Storybook is initialised:
- Violations shown in the Accessibility panel
- Configured for
wcag2a,wcag2aa,wcag21aatags - Color contrast rule explicitly enabled
- All inputs have associated
<label>elements viahtmlFor/id - Required fields use
aria-required="true" - Validation errors linked via
aria-describedby - Invalid fields marked with
aria-invalid="true" - Logical grouping with
<fieldset>and<legend> autocompleteattributes for all personal data fields
- Skip-to-content link as first focusable element
<nav>landmark witharia-label- Active page indicated with
aria-current="page" - Mobile toggle with
aria-expandedandaria-controls
- All buttons have accessible names
- Disabled states use both
disabledandaria-disabled - Out-of-stock status communicated via
role="status"andaria-live - Focus visible on all interactive elements (3px outline)
- All text meets 4.5:1 contrast ratio (WCAG AA)
- Error color
#c81e1e— 5.8:1 on white - Primary blue
#1a56db— 5.9:1 on white - Hint text
#4b5563— 7.0:1 on white
The GitHub Actions workflow runs on every push and PR:
typecheck → unit-a11y ─┐
e2e-a11y ─┼→ a11y-gate (blocks merge on failure)
pa11y-audit ─┘
Artifacts uploaded on every run:
jest-coverage/— unit test coverage reportplaywright-report/— HTML report with screenshotspa11y-reports/— JSON audit results per page
- WCAG 2.1 Quick Reference
- axe-core Rules
- ARIA Authoring Practices Guide
- Playwright Accessibility Testing
- Pa11y Documentation
Darrius Jones — QA Engineer
This project is licensed under the MIT License.
Note: Full WCAG compliance validation requires manual testing with assistive technologies (NVDA, JAWS, VoiceOver) and expert accessibility review. Automated tools catch approximately 30–40% of accessibility issues.