diff --git a/.tangled/workflows/a11y-spike.yml b/.tangled/workflows/a11y-spike.yml deleted file mode 100644 index 9cff7df..0000000 --- a/.tangled/workflows/a11y-spike.yml +++ /dev/null @@ -1,74 +0,0 @@ -# SPIKE (throwaway): can the Tangled nixery engine launch headless Chromium for -# Playwright? This is the gating unknown for the e2e/a11y harness CI. A green run -# means the strategy below — pull `chromium` from nixpkgs and drive it via -# Playwright's executablePath — works in CI, and becomes the harness's browser -# provisioning. A red run: read the "Probe environment" output + the launch error -# and iterate. Delete this workflow + test/e2e-spike once the harness lands. -# -# Runs only on the spike branch, so it never touches main and the normal ci.yml -# (push to main / PRs to main) does not fire here. -when: - - event: ["push"] - branch: ["a11y-ci-spike"] - -engine: "nixery" - -# Pull a full Chromium (brings its own shared libs — the whole point of using -# nix here) plus Node. cacert gives npm/Node a CA bundle on the minimal image. -# fontconfig + a font are required: the minimal image ships NO fonts, so headless -# Chromium lays text out at zero height (empty innerText) and color-contrast -# checks would be meaningless. -dependencies: - nixpkgs/nixpkgs-unstable: - - nodejs - - chromium - - cacert - - fontconfig - - dejavu_fonts - -environment: - # Never let `npm install` try to download Playwright's own browser binaries — - # we launch the Nix-provided Chromium instead. - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" - -steps: - # Surface what the image actually provides, so a failure is diagnosable from - # one push instead of many. - - name: "Probe environment" - command: | - set +e - echo "== node =="; node --version - echo "== npm =="; npm --version - echo "== chromium on PATH =="; command -v chromium && chromium --version - echo "== nix store chromium dirs =="; ls -d /nix/store/*chromium*/ 2>/dev/null | head - echo "== nss-cacert =="; ls /nix/store/*-nss-cacert-*/etc/ssl/certs/ca-bundle.crt 2>/dev/null | head -n1 - true - - - name: "Install Playwright (no browser download)" - command: | - set -eu - # Node bundles Mozilla CAs, but point it at the nix bundle too in case the - # registry TLS chain needs it. - export NODE_EXTRA_CA_CERTS="$(ls /nix/store/*-nss-cacert-*/etc/ssl/certs/ca-bundle.crt | head -n1)" - cd test/e2e-spike - npm install --no-audit --no-fund - - - name: "Launch headless Chromium via Playwright" - command: | - set -eu - # Make a font discoverable by fontconfig (env does not persist across - # steps, so set this up in the same step as the launch). A minimal - # fontconfig that scans our font dir + a writable cache is enough. - FONT_SRC="$(ls -d /nix/store/*-dejavu-fonts-*/share/fonts 2>/dev/null | head -n1)" - echo "font source: ${FONT_SRC:-}" - mkdir -p "$HOME/.fonts" "$HOME/.cache/fontconfig" "$HOME/.config/fontconfig" - [ -n "$FONT_SRC" ] && cp -Lr "$FONT_SRC"/. "$HOME/.fonts"/ 2>/dev/null || true - printf '\n\n\n%s/.fonts\n%s/.cache/fontconfig\n\n' "$HOME" "$HOME" > "$HOME/.config/fontconfig/fonts.conf" - export FONTCONFIG_FILE="$HOME/.config/fontconfig/fonts.conf" - fc-cache -f || true - echo "== fonts visible to fontconfig =="; fc-list | head || true - - export CHROMIUM_BIN="$(command -v chromium)" - echo "Driving Chromium at: $CHROMIUM_BIN" - cd test/e2e-spike - node probe.mjs diff --git a/.tangled/workflows/e2e.yml b/.tangled/workflows/e2e.yml index 2d19c12..e7d5dc9 100644 --- a/.tangled/workflows/e2e.yml +++ b/.tangled/workflows/e2e.yml @@ -2,10 +2,8 @@ # shadix_docs release (NOT mix dev), boots it, and runs the Playwright suite # (axe + regression) against it. Browser recipe confirmed by the a11y-ci-spike. when: - # NOTE: worktree-a11y-axe-ci is the in-development branch — remove it from this - # list before merging so the gate runs only on main + PRs. - event: ["push"] - branch: ["main", "worktree-a11y-axe-ci"] + branch: ["main"] - event: ["pull_request"] branch: ["main"] diff --git a/CLAUDE.md b/CLAUDE.md index b3a32c2..bc15acd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,8 +33,15 @@ block the commit), `tsc --noEmit` when staged `.ts` files exist, and `shadix-reg `assets/ts/*.ts` is staged. CI mirrors this: the setup step compiles with `--warnings-as-errors`, so warnings fail the build. -A11y tests are a **separate** Playwright/axe-core suite under `test/a11y/` (its own -`package.json`, not run by `mix test`). They drive the live docs site. +End-to-end tests are a **separate** Playwright suite under `test/e2e/` (its own +`package.json`, not run by `mix test`). Two Playwright projects: `a11y` runs +axe-core against every component page — a baseline scan plus per-state scenarios +(`support/scenarios/`) — scoped to the `[data-shadix-preview]` regions; `regression` +holds functional behavioral tests. Locally they drive `mix dev` (`:4001`); in CI, +`.tangled/workflows/e2e.yml` builds + boots the `shadix_docs` release and runs the +suite against it. Genuine a11y defects the gate finds are tracked in `FOLLOWUPS.md` +and `test.fixme`'d (see `test/e2e/a11y/baseline.spec.mjs` `FIXME_SLUGS`); demo-page +artifacts are allowlisted in `test/e2e/support/allowlist.mjs`. ## Architecture diff --git a/test/a11y/README.md b/test/a11y/README.md deleted file mode 100644 index 7d39e72..0000000 --- a/test/a11y/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Visual-regression + accessibility tests - -Playwright project that, for every Shadix component story, takes a screenshot -(visual regression) and runs [axe-core](https://github.com/dequelabs/axe-core) -(accessibility), scoped to the `.shadix` preview sandbox. - -## Run - -```sh -# 1. Build assets + start the storybook dev server (from the repo root): -mix tailwind storybook && mix esbuild storybook -mix dev # serves http://localhost:4001 - -# 2. In another shell: -cd test/a11y -npm install -npx playwright install chromium # first time only - -# First run — create the visual baselines: -npx playwright test --update-snapshots - -# Subsequent runs — fail on visual diffs or NEW a11y violations: -npx playwright test -``` - -## What it checks - -- **Visual regression:** `toHaveScreenshot` per component; baselines in - `screenshots/` (commit them once generated, or regenerate per environment). -- **Accessibility:** axe violations are compared against the `BASELINE` allowlist - in `components.spec.mjs` (the documented findings from `docs/ACCESSIBILITY.md`). - Any **new** rule violation fails the test. As component fixes land, remove the - corresponding entries from `BASELINE`. - -`screenshots/`, `node_modules/`, `test-results/`, and `playwright-report/` are -gitignored — baselines are environment-specific; generate them with -`--update-snapshots` in CI or locally. diff --git a/test/a11y/components.spec.mjs b/test/a11y/components.spec.mjs deleted file mode 100644 index 44f3346..0000000 --- a/test/a11y/components.spec.mjs +++ /dev/null @@ -1,40 +0,0 @@ -import { test, expect } from "@playwright/test"; -import AxeBuilder from "@axe-core/playwright"; - -// One story slug per component (the storybook URL is /components/). -const SLUGS = - "accordion alert_dialog alert aspect_ratio avatar badge breadcrumb button card carousel checkbox collapsible combobox command context_menu data_table dialog dropdown_menu empty hover_card input_otp input kbd label pagination popover progress radio_group resizable scroll_area select separator sheet skeleton slider sonner switch table tabs textarea tooltip".split( - " ", - ); - -// Accepted axe violations as of the 2026-06-18 audit (see docs/ACCESSIBILITY.md). -// Most are storybook-demo artifacts (bare controls without a <.label>); the rest are -// tracked component fixes. The test fails on any NEW rule id not listed here, so it -// gates against regressions while we work the baseline down. -const BASELINE = { - // After the 2026-06-18 a11y fix pass the only remaining finding is a storybook - // artifact: two