Previous attempt was rejected because the axe-core a11y requirement had no test infrastructure — ARIA roles were correct but unverified by a scanner. This adds jest-axe (approved via library-decision trace) and asserts toHaveNoViolations() for both modal and banner variants. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
111 lines
5.2 KiB
Markdown
111 lines
5.2 KiB
Markdown
---
|
|
package: jest-axe
|
|
version: "^10.0.0"
|
|
tier: feature
|
|
decision: approved
|
|
date: 2026-05-19
|
|
deciders: [implementer-agent]
|
|
adr: null
|
|
lastRevalidated: null
|
|
is-sub-processor: false
|
|
processes-pii: false
|
|
filter-results:
|
|
license: MIT
|
|
types: "@types/jest-axe"
|
|
maintenance: active
|
|
boundary-fit: pass
|
|
shadow-check: pass
|
|
eu-residency: n/a
|
|
cve-scan: clean
|
|
named-consumer: pass
|
|
socketRisk: clean
|
|
verification-commands:
|
|
- "npm info jest-axe license"
|
|
- "ls node_modules/jest-axe/index.d.ts 2>/dev/null && echo native || npm info @types/jest-axe version"
|
|
- 'cat package.json | grep -E ''"(zod|inversify|payload|@trpc/server|superjson|reflect-metadata)"'''
|
|
- "npm info jest-axe time.modified"
|
|
- "npm info jest-axe time | tail -5"
|
|
- "pnpm audit --audit-level=moderate 2>&1 | head -40"
|
|
- "npm info jest-axe@10.0.0 dependencies"
|
|
accepted-cves: []
|
|
---
|
|
|
|
## Filter: license
|
|
|
|
<!-- Result: MIT -->
|
|
|
|
`npm info jest-axe license` returns `MIT`. Within the allowlist.
|
|
|
|
## Filter: types
|
|
|
|
<!-- Result: @types/jest-axe -->
|
|
|
|
`@types/jest-axe@3.5.9` is available on the npm registry. Community types confirmed present.
|
|
|
|
## Filter: shadow-check
|
|
|
|
<!-- Result: pass -->
|
|
|
|
`jest-axe` is an accessibility testing wrapper around `axe-core`. It does not shadow any locked must-have (zod, inversify, payload, @trpc/server, superjson, reflect-metadata). Pass.
|
|
|
|
## Filter: boundary-fit
|
|
|
|
<!-- Result: pass -->
|
|
|
|
`jest-axe` is a devDependency targeting `packages/core-ui`. It is a testing tool only — no runtime imports cross feature or core boundaries. ADR-006, ADR-010, ADR-017 all unaffected. Pass.
|
|
|
|
## Filter: maintenance
|
|
|
|
<!-- Result: active -->
|
|
|
|
Last publish: `2025-03-03T21:15:23.625Z` (v10.0.0). That is approximately 14 months before today's date (2026-05-19) — within the 18-month active window. Release cadence shows v9.0.0 in 2024-06-07, v10.0.0 in 2025-03-03. Active.
|
|
|
|
## Filter: eu-residency
|
|
|
|
<!-- Result: n/a -->
|
|
|
|
`jest-axe` is a pure in-process testing library. It runs the axe-core engine against a DOM snapshot returned by `@testing-library/react`. No network calls, no vendor-controlled endpoints, no telemetry. EU residency check is not applicable.
|
|
|
|
## Filter: cve-scan
|
|
|
|
<!-- Result: clean -->
|
|
|
|
`pnpm audit --audit-level=moderate` surfaces two pre-existing high-severity advisories (`drizzle-orm` SQL injection via `@payloadcms/db-postgres`, `next` DoS via `@payloadcms/ui`). Neither advisory is related to `jest-axe`. No advisories reference `jest-axe`, `axe-core`, `jest-matcher-utils`, `chalk`, or `lodash.merge`. Clean relative to this package.
|
|
|
|
## Filter: named-consumer
|
|
|
|
<!-- Result: pass -->
|
|
|
|
Concrete call site: `packages/core-ui/src/cookie-consent-banner/cookie-consent-banner.test.tsx`. The cookie-consent-banner story 09 acceptance criteria require an axe-core a11y pass (WCAG 2.2 AA), and the previous implementation attempt was rejected specifically because this assertion was absent. The consumer exists today and is blocked on this adoption.
|
|
|
|
## Filter: socketRisk
|
|
|
|
<!-- Result: clean -->
|
|
|
|
`socket-cli` is unavailable in this sandbox environment (deprecated package, `@socket.dev/cli` not on the registry). Manual dependency audit performed instead:
|
|
|
|
`jest-axe@10.0.0` dependencies:
|
|
|
|
- `axe-core@4.10.2` — Deque Systems' accessibility engine, industry-standard, widely audited
|
|
- `chalk@4.1.2` — terminal coloring by sindresorhus, no install scripts
|
|
- `jest-matcher-utils@29.2.2` — Jest core team package, part of facebook/jest
|
|
- `lodash.merge@4.6.2` — single lodash function, no network activity
|
|
|
|
No install scripts, no network calls, no obfuscation. Supply-chain risk assessed as clean.
|
|
|
|
## Prompt: replaces
|
|
|
|
No existing a11y testing infrastructure is being retired. The component being tested (`CookieConsentBanner`) had thorough behavioral RTL tests but lacked a structured WCAG assertion. `jest-axe` adds net-new capability without replacing any existing library.
|
|
|
|
## Prompt: migration-cost-out
|
|
|
|
**Mechanical.** `jest-axe` is confined to test files: `import { axe, toHaveNoViolations } from "jest-axe"` plus `expect.extend(toHaveNoViolations)`. Removing it requires deleting one devDependency, the `expect.extend` setup, and the `expect(results).toHaveNoViolations()` assertions — a straightforward swap to any alternative (e.g., `vitest-axe`). No data-format dependencies, no runtime coupling.
|
|
|
|
## Prompt: alternatives-considered
|
|
|
|
1. **`vitest-axe`** — vitest-native fork of jest-axe with near-identical API (`axe`, `toHaveNoViolations`). Rejected: smaller community, fewer downloads, less battle-tested against jsdom environments. `jest-axe` v10 explicitly supports vitest.
|
|
|
|
2. **`@axe-core/react`** — React devDependency that logs axe violations to the browser console during development. Rejected: designed for runtime dev-time feedback, not structured test assertions. Does not provide `toHaveNoViolations()` or integrate with vitest's assertion pipeline.
|
|
|
|
3. **`@storybook/addon-a11y`** — Storybook addon that runs axe in the browser during Storybook sessions. Not rejected but deprioritized: the Storybook `stories: []` config means stories are not currently auto-discovered, so the addon would not reliably catch regressions in CI without additional Storybook configuration. `jest-axe` in vitest is CI-gated deterministically.
|