From b5133677ff9a6ae22a2de56249fa7f4517461314 Mon Sep 17 00:00:00 2001 From: Quinten Steenhuis Date: Wed, 2 Sep 2026 13:18:18 -0400 Subject: [PATCH 1/3] Add Axe accessibility checks for pull requests --- .github/workflows/accessibility.yml | 70 ++++++++++ .../commands/seed_accessibility_session.py | 129 ++++++++++++++++++ .../efile/static/css/reorganized-flow.css | 21 +++ .../templates/efile/extraction_review.html | 8 +- efile_app/eslint.config.mjs | 2 +- efile_app/package-lock.json | 24 ++++ efile_app/package.json | 4 +- efile_app/playwright.a11y.config.js | 30 ++++ efile_app/tests/accessibility.spec.js | 115 ++++++++++++++++ 9 files changed, 398 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/accessibility.yml create mode 100644 efile_app/efile/management/commands/seed_accessibility_session.py create mode 100644 efile_app/playwright.a11y.config.js create mode 100644 efile_app/tests/accessibility.spec.js diff --git a/.github/workflows/accessibility.yml b/.github/workflows/accessibility.yml new file mode 100644 index 00000000..f1abef50 --- /dev/null +++ b/.github/workflows/accessibility.yml @@ -0,0 +1,70 @@ +name: Accessibility + +on: + pull_request: + branches: [main] + workflow_dispatch: + +concurrency: + group: accessibility-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + axe: + name: Axe accessibility checks + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.12' + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + version: latest + + - name: Install Python dependencies + working-directory: efile_app + run: uv sync --group dev + + - name: Set up Node.js + uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + cache-dependency-path: efile_app/package-lock.json + + - name: Install browser dependencies + working-directory: efile_app + run: | + npm ci + npx playwright install --with-deps chromium + + - name: Seed the local accessibility fixture + working-directory: efile_app + run: | + uv run python manage.py migrate --noinput + uv run python manage.py seed_accessibility_session --output test-results/a11y-storage-state.json + + - name: Run Axe checks + working-directory: efile_app + env: + A11Y_STORAGE_STATE: test-results/a11y-storage-state.json + run: | + uv run python manage.py runserver 127.0.0.1:8000 > /tmp/litefile-a11y-server.log 2>&1 & + curl --retry 30 --retry-connrefused --retry-delay 1 --fail http://127.0.0.1:8000/choose-jurisdiction + npm run test:a11y + + - name: Upload accessibility results + if: always() + uses: actions/upload-artifact@v4 + with: + name: axe-accessibility-results + path: | + efile_app/test-results/ + /tmp/litefile-a11y-server.log + if-no-files-found: warn diff --git a/efile_app/efile/management/commands/seed_accessibility_session.py b/efile_app/efile/management/commands/seed_accessibility_session.py new file mode 100644 index 00000000..2bbd3829 --- /dev/null +++ b/efile_app/efile/management/commands/seed_accessibility_session.py @@ -0,0 +1,129 @@ +"""Create deterministic, local-only data for browser accessibility checks.""" + +import json +from pathlib import Path + +from django.conf import settings +from django.core.management.base import BaseCommand +from django.test import Client + +from efile.models import FilingDocument, FilingDraft, FilingParty, FilingPlan +from efile.services.current_drafts import CURRENT_DRAFT_SESSION_KEY +from efile.workflow import ExistingCase, WorkflowStepKey + + +class Command(BaseCommand): + help = "Seed a local browser session for the Axe accessibility suite." + + def add_arguments(self, parser): + parser.add_argument("--output", required=True, help="Path for Playwright storage state JSON.") + parser.add_argument("--origin", default="http://127.0.0.1:8000", help="Origin served to Playwright.") + + def handle(self, *args, **options): + user_model = settings.AUTH_USER_MODEL + from django.apps import apps + + user_class = apps.get_model(user_model) + user, _ = user_class.objects.update_or_create( + username="accessibility-checker", + defaults={ + "email": "accessibility-checker@example.com", + "tyler_jurisdiction": "illinois", + "tyler_username": "accessibility-checker@example.com", + "first_name": "Avery", + "last_name": "Checker", + }, + ) + user.set_unusable_password() + user.save() + + FilingDraft.objects.filter(user=user).delete() + FilingPlan.objects.filter(user=user).delete() + plan = FilingPlan.objects.create(user=user, jurisdiction="illinois", title="Accessibility test filing") + draft = FilingDraft.objects.create( + user=user, + plan=plan, + jurisdiction="illinois", + workflow_version=2, + existing_case=ExistingCase.NEW, + current_step=WorkflowStepKey.REVIEW, + court_code="cook:cvd1", + court_name="Cook County", + case_category_code="6198", + case_category_name="Small Claims", + case_type_code="183541", + case_type_name="Contract", + filing_type_code="143132", + filing_type_name="Complaint", + document_checklist_acknowledged=True, + selected_payment_account_id="a11y-payment-account", + selected_payment_account_name="Accessibility payment account", + quoted_fee_total="0.00", + ) + FilingDocument.objects.create( + draft=draft, + role=FilingDocument.Role.LEAD, + name="Accessibility complaint.pdf", + original_filename="Accessibility complaint.pdf", + filing_type_code="143132", + filing_type_name="Complaint", + document_type_code="public", + document_type_name="Public", + ) + FilingParty.objects.create( + draft=draft, + role="filer", + sort_order=0, + first_name="Avery", + last_name="Checker", + address_line_1="100 Main Street", + city="Chicago", + state="IL", + zip_code="60601", + email=user.email, + party_type="plaintiff", + party_type_name="Plaintiff", + is_filing_party=True, + ) + other_party = FilingParty.objects.create( + draft=draft, + role="other", + sort_order=1, + first_name="Jordan", + last_name="Example", + ) + + # Let Django's own test client construct the authenticated session. This + # tracks framework changes to session-auth details without duplicating + # private authentication keys in this browser-only fixture command. + client = Client() + client.force_login(user) + session = client.session + session[CURRENT_DRAFT_SESSION_KEY] = draft.pk + session["jurisdiction"] = "illinois" + session["auth_tokens"] = {"TYLER-TOKEN-ILLINOIS": "accessibility-test-token"} + session.save() + + output = Path(options["output"]) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text( + json.dumps( + { + "cookies": [ + { + "name": settings.SESSION_COOKIE_NAME, + "value": session.session_key, + "domain": "127.0.0.1", + "path": "/", + "expires": -1, + "httpOnly": True, + "secure": False, + "sameSite": "Lax", + } + ], + "origins": [], + }, + indent=2, + ) + ) + self.stdout.write(self.style.SUCCESS(f"Seeded accessibility session for party {other_party.pk}.")) diff --git a/efile_app/efile/static/css/reorganized-flow.css b/efile_app/efile/static/css/reorganized-flow.css index 47bd38b0..2566c404 100644 --- a/efile_app/efile/static/css/reorganized-flow.css +++ b/efile_app/efile/static/css/reorganized-flow.css @@ -1392,6 +1392,27 @@ margin-top: 0; } +/* Bootstrap's secondary outline is too light against the warning panel used + for this alternative action. Keep its text and border legible at AA. */ +#claim-party-instead-filing-for, +#claim-party-cancel { + --bs-btn-color: #343a40; + --bs-btn-border-color: #343a40; + --bs-btn-hover-bg: #343a40; + --bs-btn-hover-border-color: #343a40; + --bs-btn-active-bg: #212529; + --bs-btn-active-border-color: #212529; +} + +.document-plan__save { + --bs-btn-color: #343a40; + --bs-btn-border-color: #343a40; + --bs-btn-hover-bg: #343a40; + --bs-btn-hover-border-color: #343a40; + --bs-btn-active-bg: #212529; + --bs-btn-active-border-color: #212529; +} + .claim-party-dialog .workflow-actions { display: flex; gap: 0.75rem; diff --git a/efile_app/efile/templates/efile/extraction_review.html b/efile_app/efile/templates/efile/extraction_review.html index 0ef46564..05bfcd43 100644 --- a/efile_app/efile/templates/efile/extraction_review.html +++ b/efile_app/efile/templates/efile/extraction_review.html @@ -129,7 +129,7 @@

{% translate "Tell us about your case" %}

- {{ case_category_label }} +