npm.io
1.0.4 • Published 1h agoCLI

@wcag-checkr/ci

Licence
UNLICENSED
Version
1.0.4
Deps
2
Size
6.4 MB
Vulns
0
Weekly
1.3K

@wcag-checkr/ci

Headless wcagcheckr accessibility audit runner for CI/CD pipelines.

Drives the wcagcheckr Chrome extension via Playwright, running full-page WCAG audits across the same 108-state matrix (hover, focus, dark mode, RTL, breakpoints, etc.) the extension uses interactively. So your CI catches what your developer sees.

Why this matters

Competing tools (axe DevTools Pro, Siteimprove Enterprise) charge for CI/CD integration. We ship it free. And ours is the only CI runner that audits at hover/focus/dark/RTL/breakpoint state combinations — competitors run a single default-state audit.

Install

npm install --save-dev @wcag-checkr/ci playwright
npx playwright install chromium

Use

npx wcagcheckr-ci audit https://your-site.com/

Default: outputs JSON to stdout, exits non-zero if any serious or critical violations.

Common flags
# Write SARIF for GitHub PR annotations
npx wcagcheckr-ci audit https://your-site.com/ --format sarif --output a11y.sarif

# Write JUnit XML for Jenkins/GitLab CI
npx wcagcheckr-ci audit https://your-site.com/ --format junit --output a11y.xml

# Strict — fail on any violation, even minor
npx wcagcheckr-ci audit https://your-site.com/ --threshold minor

# Permissive — never fail (just collect findings)
npx wcagcheckr-ci audit https://your-site.com/ --threshold none

# Audit with a license token (unlocks paid features like forensic anchoring)
npx wcagcheckr-ci audit https://your-site.com/ --license $WCAGCHECKR_LICENSE

# Use your own built extension (e.g. self-hosted fork)
npx wcagcheckr-ci audit https://your-site.com/ --extension-dir ./path/to/dist

Verifying a forensic log

The extension's Forensic tab can export your full audit log as JSON (the export JSON button). That file contains every audit's identity hash, RFC 3161 trusted-timestamp token, and ed25519 server signature. wcagcheckr-ci verify validates it offline (the server is only contacted to fetch the public key by fingerprint):

npx wcagcheckr-ci verify wcagcheckr-forensic-log.json

Per entry, the verifier:

  1. Recomputes the SHA-256 identity hash from the stored fields and checks it against the recorded hash.
  2. Fetches the server's ed25519 public key matching the receipt's fingerprint and verifies the signature over (hash, anchoredAt, tsaName, productSlug, prevAuditHash).
  3. If prevAuditHash references another entry in the same export, validates the chain link. (Deep chain links require exporting the full history.)

Exit code is 0 only when every entry's hash and signature verify cleanly.

To fully verify the RFC 3161 timestamp (which proves the hash was witnessed by a public TSA at the recorded time), save the receipt's rfc3161TokenBase64 to a .tsr file and run openssl ts -verify against FreeTSA's certificate chain. That step is out-of-scope for v0 of the CLI verifier — the ed25519 signature already commits to the same TSA token bytes, so a verified ed25519 signature is strong evidence the receipt was issued by our server in response to that TSA timestamp.

Exit codes

Code Meaning
0 Success — threshold not exceeded
1 Threshold exceeded — CI failure signal
2 Runtime error (target unreachable, extension didn't load, etc.)

GitHub Actions example

name: a11y
on: [pull_request]
jobs:
  wcagcheckr:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - run: npm ci
      - run: npx playwright install chromium --with-deps
      - run: npx wcagcheckr-ci audit ${{ env.PREVIEW_URL }} --format sarif --output a11y.sarif
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with: { sarif_file: a11y.sarif }

GitLab CI example

a11y:
  image: node:20
  before_script:
    - npm ci
    - npx playwright install chromium --with-deps
  script:
    - npx wcagcheckr-ci audit "$PREVIEW_URL" --format junit --output a11y.xml
  artifacts:
    reports:
      junit: a11y.xml
    when: always

License gating

The audit command can run without a license — free-tier audits work. To unlock paid features (forensic anchoring, AI summaries) in a CI run, pass --license <token>. The CLI activates the token via the same LICENSE_SET_REQUEST message the extension UI uses; failures are surfaced via a non-zero exit code.

For verification, no license is required. The forensic anchor verifier reads a public-key endpoint and recomputes signatures — anyone who receives a forensic-log JSON can validate it without any wcagcheckr credentials.

Programmatic API (planned)

A Node-importable API is on the roadmap:

import { audit } from '@wcag-checkr/ci';
const { violations, sarif } = await audit('https://your-site.com/');

Until then, parse the JSON output of the CLI — same shape as the extension's "Export → JSON" output.

Limitations (v0)

  • Auditing happens against the rendered DOM at the URL you pass — preview-deploy URLs work great; localhost requires the CI runner to also be the host (use npm run start & then audit http://localhost:PORT).
  • Authentication: log-in flows aren't yet supported; pass URLs that don't require auth, or fork to inject cookies/auth headers via Playwright's storageState.
  • The runner uses headless: 'new' (Chromium's modern headless mode); some sites that detect headless browsers may behave differently. Workaround: use --extension-dir with a forked runner that switches to headless: false on a virtual display.

License

UNLICENSED until commercial release. See wcagcheckr.com/license.

Keywords