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 installwas 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.
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 testCache & 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.
- uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: pw-${{ hashFiles('**/package-lock.json') }}Common errors
- Playwright runner
Executable doesn’t exist→ runplaywright install --with-deps. - Puppeteer runner
No usable sandbox!→ add--no-sandboxto launch args. - Empty results →
await page.goto(...)and wait for load beforeanalyze(). 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.