Skip to content
Latchkey

How to Run axe-core Accessibility Tests in CI

axe-core is a JS rules engine that runs inside a real browser page - so in CI it inherits the browser-install requirements of whatever runner you inject it through.

axe-core analyzes the accessibility of a rendered DOM, but it must run inside a real browser page. You inject it via a runner like @axe-core/playwright, @axe-core/puppeteer, or cypress-axe. The CI requirements come from that browser layer: install the browser, run headless, and disable the sandbox in containers.

Why it fails in CI

axe-core itself is pure JS, but the browser it runs in is not. With the Playwright runner, browsers must be installed; with Puppeteer, the container needs --no-sandbox and the Chromium libraries. The scan also needs the page loaded before analyze() runs.

  • Playwright runner: Executable doesn’t exist - playwright install was not run.
  • Puppeteer runner: No usable sandbox! / missing libnss3.so on a slim runner.
  • Empty/partial results - axe ran before the page finished loading.

Install & run it reliably

Install the browser for your injection runner, run headless with the sandbox disabled in containers, and navigate to the page before calling axe. The example uses @axe-core/playwright.

.github/workflows/a11y.yml
npm ci
npx playwright install --with-deps chromium

// in the test (@axe-core/playwright):
//   await page.goto('http://localhost:3000')
//   const results = await new AxeBuilder({ page }).analyze()
//   expect(results.violations).toEqual([])
npx playwright test

Cache & speed

Cache the browser binaries for your runner - ~/.cache/ms-playwright (Playwright) or ~/.cache/puppeteer (Puppeteer) - keyed on the tool version. axe-core adds no binary to cache; it ships as JS in node_modules.

.github/workflows/ci.yml
- uses: actions/cache@v4
  with:
    path: ~/.cache/ms-playwright
    key: pw-${{ hashFiles('**/package-lock.json') }}

Common errors

  • Playwright runner Executable doesn’t exist → run playwright install --with-deps.
  • Puppeteer runner No usable sandbox! → add --no-sandbox to launch args.
  • Empty results → await page.goto(...) and wait for load before analyze().
  • libnss3.so/shared-library errors → install Chromium system libs or use a browser image.

Key takeaways

  • axe-core is pure JS but runs inside a real browser - install that browser in CI.
  • It inherits your Playwright/Puppeteer setup: install, --no-sandbox, system libs.
  • Load the page before calling analyze(); cache the browser binaries.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →