Repository navigation
ci: accessibility job, Playwright + axe-core WCAG 2.1 AA scans (#168) - #222
Merged
Merged
Conversation
New Quality Gate job `accessibility`: tests/accessibility/ scans the frontend's main pages in Chromium against the Vite dev server, and only there. The API is stubbed, so the job needs no database and a backend-only change cannot alter what is scanned. Nine scenarios (sign-in, upload page empty / with history / mapping dialog / toast, audit log and its menu, review page) each wait for content that proves the page rendered before scanning. The frontend has violations today, so the scans judge against tests/accessibility/known_violations.json: a new violation, or more elements than recorded, fails; a recorded one that is fixed fails until its entry is deleted. Emptying the list makes the job strict. The integration job, make test-all and run_qa.ps1 exclude the marker. requirements-a11y.txt keeps the browser driver out of the other jobs. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VBrxqChRJtxdvSpFhiUWUy
Bringing main's newer frontend (#187) in changed what the upload page calls: GET /api/csv/uploads feeds a new 'Upload attempts' card. The scans caught it twice over, as designed: the page did not render (the content wait timed out) and the stub-gap check named the route. The stub now answers it (an empty list by default) and a new scenario scans the card with a failed, a partly imported and a rejected attempt; its one finding, the same unnamed page-size select as the other upload-page scenarios, is recorded. No other scenario changed. The test catalogue conflict was regenerated with scripts/test_catalogue.py. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VBrxqChRJtxdvSpFhiUWUy
This was referenced Nov 9, 2026
amar-python
marked this pull request as ready for review
November 9, 2026 14:45
amar-python
pushed a commit
that referenced
this pull request
Nov 9, 2026
The tests moved to tests/unit/ with the reorganisation; Git carried the control's tests across the rename. One path in them computed the repo root with parent.parent and is now parents[2]. The generated test catalogue was the only conflict: taken from main and regenerated with scripts/test_catalogue.py. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VBrxqChRJtxdvSpFhiUWUy
amar-python
pushed a commit
that referenced
this pull request
Nov 9, 2026
…ace the new tests The reorganisation of tests/ (#221) is on main. Git followed the renames of test_migrations.py and test_tenant_isolation.py; the three test files this branch adds are moved to the homes #221 suggested: test_prod_db_naming.py to unit/, test_tenant_te_migrations.py and test_type_provenance.py to system/. Paths a text merge cannot see are fixed: the repo root is parents[2] in the two moved files that compute it, and in test_tenant_isolation.py, whose _ALEMBIC_INI line (added here for #195) still pointed at tests/alembic.ini. Two references to the old path (te0002's docstring, docs/TENANT_SCHEMA_MIGRATIONS.md) are updated. The generated test catalogue was the only conflict: regenerated. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VBrxqChRJtxdvSpFhiUWUy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements #168 as the issue scopes it: a new Quality Gate job
accessibility,tests/accessibility/(conftest.pyPlaywright fixtures,test_accessibility.py),playwright install chromiumin CI, and the marker wiring so the integration job runs withnot accessibility. Frontend fixes and branch protection stay out of scope, as the issue says.Stacked on #221 (the test-folder reorganisation, which provides
tests/accessibility/and the marker). This PR's base is that branch, so the diff shows only #168. Merge #221 first; GitHub will then retarget this tomain.How it works
npm run dev(Vite) and drive Chromium through Playwright; every/api/**request is answered from fixed data (stub_api.py) and a signed-in session is planted in the browser. No PostgreSQL service and no API process, so a backend-only PR cannot change what is scanned.test_signed_out_visitors_are_sent_to_sign_inguards the redirect itself.node_modulesfails with the command to fix it. All heavy imports are inside fixtures, so collectingtests/elsewhere needs none of this.integration (postgres service)runs--markers "not accessibility"and its coverage step-m "not accessibility"; same formake test-allandscripts/run_qa.ps1. The free tier's markers cannot select these tests.requirements-a11y.txtkeeps the browser driver out of the other jobs. A frontend change now makesscripts/select_tests.pyrecommend the scans.Decision for the owner: known violations are recorded, so the job passes today
The issue asks both that the job "runs green on a trivial PR touching only backend" and that "failures are expected until the frontend fixes land". The frontend does have violations now, so those can't both hold literally. I made the job pass on today's frontend and fail on change:
tests/accessibility/known_violations.jsonrecords what is wrong now, per scenario and rule with an element count.To make the job strict instead (red until the frontend is fixed), empty the
violationslist. One change, no code.What is recorded (11 entries, all in the family the closed PR #41 bundled): unnamed select/icon buttons (
button-name), upload progress bars with no accessible name (aria-progressbar-name), low-contrast text including the error toast and status pills (color-contrast, 3.65 to 4.48:1 against 4.5:1), and a focusable control insidearia-hiddenwhile the report menu is open (aria-hidden-focus).Clean today: sign-in (both states), empty upload page, review page.
The scans caught a frontend change while this PR was open
Merging
main's newer frontend into this branch (#187 added an "Upload attempts" card fed by a newGET /api/csv/uploads) made four scenarios fail, in two independent ways, as designed: the upload page did not render (the content wait timed out), and the stub-gap check named the unknown route. The stub now answers it, and a new scenario scans the card; its one finding is the same unnamed page-size select as the other upload-page scenarios. No other scenario changed, so #187/#188 introduced no accessibility drift.Found on the way (frontend, not fixed here)
In development, React StrictMode runs the effects of
csv-table-hub-main/src/hooks/use-local-storage.tstwice: the first pass writes the hook's empty default over the stored import history and the second reads that back, so saved history is never shown after a reload. My first audit-page scan therefore scanned an empty page and passed without checking a row.stub_api.seed_scriptdrops writes of[]to that key so the page is scanned with rows in it (explained in the code and the README); remove it when the hook is fixed. Production builds don't double-run effects, so this is a dev-mode bug. I queued it as a separate suggested task.Verified
accessibilityjob is green on both of its runs (about 2 minutes each: the first commit, and the current head with the attempts scenario). That settles what I could not prove locally:actions/setup-node@v6,playwright install --with-deps chromiumon the runner, and that the scan counts on the runner's Chromium equal the local ones.python -m pytest tests/accessibility -v: 13 passed, repeated runs identical; the scans start and stop their own dev server. The CI command,python3 scripts/test_report.py --markers accessibility --strict: 0 skipped, the rest reported NOT RUN (deselected), JSON reports written for the artifact. (tests/system/test_oidc.pyexcluded locally only, viaPYTEST_ADDOPTS: an unrelatedcffiimport problem in this sandbox.)NEW label (critical); naming the progress bars fails with "aria-progressbar-name no longer fails here: delete its entry".not accessibilitydrops them.tests/unit/test_a11y_baseline.py, 14, free tier, no browser) and for the workflow wiring (tests/unit/test_ci_quality_gate.py, 4).make lintclean (flake8 + bandit over all test files),make health89/0,test_catalogue.py --checkcurrent, free tier passes.npm ci --dry-runsucceeds (lockfile in sync).Not proven / not done
Local runs used the sandbox's pre-installed Chromium (
A11Y_CHROMIUM_PATH); CI uses the oneplaywright installfetches (and matched, see above).Not covered: dark mode, mobile widths, keyboard-only flows, the Streamlit spike (Spike: rebuild M4 review screen in Streamlit (1-day time-box) #154).
Not done, per the issue: making this a required check (after a few green runs).
CI green on the current head (53b41eb):
accessibility,integration (postgres service),free-tierand the Windowspython-validator-tests.🤖 Generated with Claude Code
https://claude.ai/code/session_01VBrxqChRJtxdvSpFhiUWUy