feat(atomic-a11y): add vitest a11y reporter#7123
feat(atomic-a11y): add vitest a11y reporter#7123y-lakhdar wants to merge 21 commits intofeat/a11y-shared-foundationfrom
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: af3262b8f9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| await mkdir(this.outputDir, {recursive: true}); | ||
| await Promise.all( | ||
| outputPaths.map((outputPath) => | ||
| writeFile(outputPath, serializedReport, 'utf8') |
There was a problem hiding this comment.
Create parent directories for resolved output paths
The reporter resolves each output file path but only creates this.outputDir, so a custom outputFilename containing subdirectories (e.g. reports/a11y.json) causes writeFile to fail with ENOENT and the report is silently skipped after warning. Directory creation should target path.dirname(outputPath) for each file being written.
Useful? React with 👍 / 👎.
TL;DR
Custom Vitest Reporter that captures axe-core accessibility results from Storybook tests and produces a structured JSON report per component.
Context
This PR adds the core reporting engine: a custom Vitest Reporter that hooks into test results, extracts axe-core accessibility violations, and builds a structured JSON report per component.
Shard merging (for parallel CI runs) was extracted to a separate PR (#7137) to keep this focused on the reporter itself.
What this PR does
Wiring
The reporter is wired into
packages/atomic/vitest.config.jsas a custom reporter for the Storybook test project.How to review
Start from the consumer, then trace into the engine.
Step 1 — Wiring (1 min)
Read
packages/atomic/vitest.config.jsfirst. This is whereVitestA11yReporteris instantiated with its options (outputDir,packageJsonPath). This shows how the reporter plugs into Vitest — it's just a reporter in thereportersarray.Step 2 — Reporter lifecycle (core, ~10 min)
Open
vitest-a11y-reporter.ts. This is the entrypoint class — everything else serves it. Follow these two methods:onTestCaseResult(testCase)— called by Vitest for each test. Read top-to-bottom:project.name.startsWith('storybook'))meta.reports)storybook-extraction.ts)onTestRunEnd(...)— called once at the end. Delegates tobuildA11yReport()(→report-builder.ts), serializes to JSON, writes to disk.Step 3 — Report assembly (~5 min)
Open
report-builder.ts. This transforms theMap<string, ComponentAccumulator>into the finalA11yReport:buildComponents()— converts mutable accumulators → sortedA11yComponentReport[]buildCriteria()— inverts the component→criteria mapping into criteria→componentsStep 4 — Supporting modules (skim, ~5 min)
These are leaf dependencies — review as needed:
axe-integration.tsstorybook-extraction.tsshard-resolution.ts--shardflag parsingsummary.tsreporter-utils.tsStep 5 — Types & shared (~2 min)
src/shared/types.ts— all report interfaces (A11yReport,A11yComponentReport,A11yCriterionReport,A11ySummary)src/shared/constants.ts— defaults and sentinel valuessrc/index.ts— public API surface (what gets exported from the package)Key design decisions to look for
onTestCaseResultandonTestRunEndcatch all errors andconsole.warn— a reporter crash must never break the test runComponentAccumulatorusesSet<string>for deduplication, converted to plain arrays at report build timewcag143→1.4.3(positional digit extraction, not a lookup table)Try it
View the JSON report in
packages/atomic-a11y/reports/a11y-report.jsonPR Chain (3 of 7)
feat/a11y-package-scaffoldfeat/a11y-shared-foundationfeat/a11y-reporterfeat/a11y-merge-shardsfeat/a11y-openacrfeat/a11y-scriptsfeat/a11y-ci-integrationKIT-5469