Skip to main content
This recipe covers accessibility spot-checking using axe-core and Lighthouse. For operationalized a11y monitoring — scheduled runs, trend tracking, aggregated reports, and stakeholder dashboards — talk to your QA Wolf team about full-service accessibility testing.

Examples

Generate a Lighthouse accessibility report:
const { lhr } = await playAudit({
  page,
  thresholds: { accessibility: 90 },
  reports: {
    formats: { html: true, json: true },
    directory: `${process.env.TEAM_STORAGE_DIR}/lighthouse`,
    name: `a11y-${Date.now()}`,
  },
  config: {
    extends: "lighthouse:default",
    settings: { onlyCategories: ["accessibility"] },
  },
});

const a11yScore = Math.round(lhr.categories.accessibility.score * 100);
console.log(`Accessibility score: ${a11yScore}`);
Lighthouse scores accessibility against its own model, which does not map 1:1 to WCAG conformance levels. Use it as a directional score and audit artifact — not as a hard compliance gate.

When to use

  • Your team wants to catch accessibility regressions introduced by new code before they reach production
  • You need a release gate that fails on critical or serious WCAG violations
  • You need a shareable accessibility report for stakeholders or compliance purposes
  • You want to establish a baseline score for a page and track it over time

Choosing the right approach

Axe-coreLighthouse
Best forCI checks, violation counts, release gatingScores, formal reports, stakeholder sharing
OutputViolation list by severityAccessibility score + full report
Use as a gateYes — throw on critical / seriousNot recommended
Report artifactNo (console / logs)Yes — HTML, JSON, PDF
These two approaches complement each other. Axe-core is precise and gateable; Lighthouse is broad and reportable. For the most complete picture, use both.

Notes

  • The gate in the full sample fails on all violations. Adjust the threshold to match your policy — some teams also gate on moderate, particularly in regulated industries.

Full sample test

import { flow, expect } from "@qawolf/flows/web";

export default flow(
  "Accessibility",
  "Web - Chrome",
   async ({ test, ...testContext }) => {
    const { launch } = testContext;
// Sample Test - Accessibility
//--------------------------------
// Arrange:
//--------------------------------
// Create user and log in
const { context } = await launch();
      context.setDefaultTimeout(8000);
      const page = await context.newPage();
      await page.goto("https://www.qawolf.com/");

// Navigate to Home page

//--------------------------------
// Act:
//--------------------------------
// Add Axe package
await page.addScriptTag({
url: "https://unpkg.com/axe-core@4.8.2/axe.min.js",
});
await page.getByRole(`button`, { name: `Accept` }).click();


// Run Axe
const violationsPage1 = await page.evaluate(async () => {
const { violations } = await window.axe.run();
return violations;
});
// grab data points
let critical = violationsPage1.filter((x) => x.impact === "critical");
let serious = violationsPage1.filter((x) => x.impact === "serious");
let moderate = violationsPage1.filter((x) => x.impact === "moderate");
let minor = violationsPage1.filter((x) => x.impact === "minor");
//--------------------------------
// Assert:
//--------------------------------
// Critical
expect(critical.length).toBe(0);
// Serious
 expect(serious.length).toBe(0);
// Moderate
expect(moderate.length).toBe(0);

// Minor
expect(minor.length).toBe(0);
  }
);
Last modified on April 17, 2026