Skip to content

Commit 125449b

Browse files
author
Olayinka Adelakun
committed
Merge remote-tracking branch 'origin/LE-1568' into LE-1573
2 parents 93efc07 + bac8a7b commit 125449b

73 files changed

Lines changed: 3920 additions & 161 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/a11y-scan.yml

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
name: A11y / Playwright Scans
2+
3+
on:
4+
# TEMP: remove before merging — allows testing the workflow from this
5+
# branch, since workflow_dispatch only registers once the file is on main.
6+
push:
7+
branches: [feat/a11y]
8+
# TEMP: remove before merging — run on PRs while validating the workflow.
9+
pull_request:
10+
schedule:
11+
# Run nightly at 02:00 UTC, after the nightly build window
12+
- cron: "0 2 * * *"
13+
workflow_dispatch:
14+
inputs:
15+
ref:
16+
description: "(Optional) ref to checkout"
17+
required: false
18+
type: string
19+
assert:
20+
description: "Fail the run when scans differ from committed baselines"
21+
required: false
22+
type: boolean
23+
default: false
24+
25+
env:
26+
NODE_VERSION: "22"
27+
PYTHON_VERSION: "3.13"
28+
# Define the directory where Playwright browsers will be installed.
29+
# This path is used for caching across workflows
30+
PLAYWRIGHT_BROWSERS_PATH: "ms-playwright"
31+
PLAYWRIGHT_VERSION: "1.59.1"
32+
33+
jobs:
34+
a11y-scan:
35+
name: IBM Equal Access
36+
runs-on: ubuntu-latest
37+
permissions:
38+
contents: read
39+
steps:
40+
# Cron only fires from the default branch, so scheduled runs resolve the
41+
# latest release-* branch and scan that (same pattern as nightly_build).
42+
- name: Resolve Ref To Scan
43+
id: resolve_ref
44+
shell: bash
45+
run: |
46+
if [ -n "${{ inputs.ref }}" ]; then
47+
REF="${{ inputs.ref }}"
48+
elif [ "${{ github.event_name }}" = "schedule" ]; then
49+
REF=$(git ls-remote --heads https://github.com/${{ github.repository }} 'refs/heads/release-*' \
50+
| awk '{print $2}' \
51+
| sed 's|refs/heads/||' \
52+
| grep -E '^release-[0-9]+\.[0-9]+\.[0-9]+$' \
53+
| sort -V \
54+
| tail -n 1)
55+
if [ -z "$REF" ]; then
56+
echo "No release-* branch found in ${{ github.repository }}"
57+
exit 1
58+
fi
59+
else
60+
REF="${{ github.ref }}"
61+
fi
62+
echo "ref=$REF" >> "$GITHUB_OUTPUT"
63+
echo "Scanning ref: $REF"
64+
65+
- name: Checkout Repository
66+
uses: actions/checkout@v6
67+
with:
68+
ref: ${{ steps.resolve_ref.outputs.ref }}
69+
70+
- name: Setup Node.js Environment
71+
uses: actions/setup-node@v6
72+
with:
73+
node-version: ${{ env.NODE_VERSION }}
74+
75+
- name: Get npm cache directory
76+
id: npm-cache-dir
77+
shell: bash
78+
run: echo "dir=$(npm config get cache)" >> "$GITHUB_OUTPUT"
79+
80+
- name: Cache npm dependencies
81+
uses: actions/cache@v5
82+
continue-on-error: true
83+
with:
84+
path: ${{ steps.npm-cache-dir.outputs.dir }}
85+
key: ${{ runner.os }}-npm-${{ env.NODE_VERSION }}-${{ hashFiles('src/frontend/package-lock.json') }}
86+
restore-keys: |
87+
${{ runner.os }}-npm-${{ env.NODE_VERSION }}-
88+
89+
- name: Install Frontend Dependencies
90+
run: npm ci
91+
working-directory: ./src/frontend
92+
93+
- name: Cache Playwright Browsers
94+
id: cache-playwright
95+
uses: actions/cache@v5
96+
continue-on-error: true
97+
with:
98+
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
99+
key: playwright-${{ env.PLAYWRIGHT_VERSION }}-chromium-${{ runner.os }}
100+
restore-keys: |
101+
playwright-${{ env.PLAYWRIGHT_VERSION }}-chromium-${{ runner.os }}
102+
103+
- name: Install Playwright Browser Dependencies
104+
if: steps.cache-playwright.outputs.cache-hit != 'true'
105+
shell: bash
106+
run: |
107+
cd ./src/frontend
108+
npx playwright install --with-deps chromium
109+
110+
- name: "Setup Environment"
111+
uses: astral-sh/setup-uv@v6
112+
with:
113+
enable-cache: true
114+
cache-dependency-glob: "uv.lock"
115+
python-version: ${{ env.PYTHON_VERSION }}
116+
prune-cache: false
117+
118+
- name: Install Python Dependencies
119+
run: uv sync
120+
121+
- name: Run Playwright Specs With IBM Scans
122+
shell: bash
123+
env:
124+
RUN_A11Y: "true"
125+
RUN_A11Y_ASSERT: ${{ github.event_name == 'workflow_dispatch' && inputs.assert && 'true' || 'false' }}
126+
LANGFLOW_DEACTIVATE_TRACING: "true"
127+
run: |
128+
cd src/frontend
129+
# Only specs that call runA11yScan produce reports; discover them
130+
# so new scan hosts are picked up without editing this workflow.
131+
SCAN_SPECS=$(grep -rl "runA11yScan(" tests --include="*.spec.ts" | sort)
132+
echo "Specs with a11y scans:"
133+
echo "$SCAN_SPECS"
134+
test -n "$SCAN_SPECS"
135+
npx playwright test $SCAN_SPECS --project=chromium --workers=1 --retries=2
136+
137+
- name: Build Aggregated A11y Summary
138+
if: always()
139+
shell: bash
140+
run: |
141+
cd src/frontend
142+
if ls coverage/accessibility-reports/*.json > /dev/null 2>&1; then
143+
npm run a11y:report --silent
144+
{
145+
echo '```text'
146+
npm run a11y:report --silent
147+
echo '```'
148+
} >> "$GITHUB_STEP_SUMMARY"
149+
else
150+
echo "No accessibility reports were generated." | tee -a "$GITHUB_STEP_SUMMARY"
151+
fi
152+
153+
- name: Upload Accessibility Reports
154+
if: always()
155+
uses: actions/upload-artifact@v6
156+
with:
157+
name: accessibility-reports-${{ github.run_attempt }}
158+
path: src/frontend/coverage/accessibility-reports
159+
retention-days: 30
160+
overwrite: true
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: A11y / Unit Tests
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- "src/frontend/**"
7+
- ".github/workflows/a11y-unit-tests.yml"
8+
- "Makefile.frontend"
9+
workflow_call:
10+
inputs:
11+
ref:
12+
description: "(Optional) ref to checkout"
13+
required: false
14+
type: string
15+
workflow_dispatch:
16+
inputs:
17+
ref:
18+
description: "(Optional) ref to checkout"
19+
required: false
20+
type: string
21+
22+
env:
23+
NODE_VERSION: "22"
24+
25+
jobs:
26+
a11y-unit-tests:
27+
name: Frontend jest-axe
28+
runs-on: ubuntu-latest
29+
permissions:
30+
contents: read
31+
checks: write
32+
steps:
33+
- name: Checkout Repository
34+
uses: actions/checkout@v6
35+
with:
36+
ref: ${{ inputs.ref || github.ref }}
37+
38+
- name: Setup Node.js Environment
39+
uses: actions/setup-node@v6
40+
with:
41+
node-version: ${{ env.NODE_VERSION }}
42+
cache: "npm"
43+
cache-dependency-path: ./src/frontend/package-lock.json
44+
45+
# These are regression locks: every a11y unit test passes as of the
46+
# component fixes in feat/a11y-unit-tests, so a failure here means a
47+
# real a11y regression and blocks the PR.
48+
- name: Run Frontend A11y Unit Tests
49+
run: make test_frontend_a11y_ci
50+
51+
- name: Publish A11y Test Results
52+
uses: mikepenz/action-junit-report@v5
53+
if: always()
54+
with:
55+
report_paths: "src/frontend/test-results/junit.xml"
56+
check_name: "A11y / Unit Test Results"
57+
fail_on_failure: true
58+
require_tests: true

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@ jobs:
260260
(
261261
inputs.run-all-tests ||
262262
(needs.path-filter.result != 'skipped' &&
263-
needs.path-filter.outputs.docs-only != 'true')
263+
needs.path-filter.outputs.docs-only != 'true' &&
264+
needs.path-filter.outputs.python == 'true')
264265
)
265266
uses: ./.github/workflows/python_test.yml
266267
with:

Makefile.frontend

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
FRONTEND_DIR = src/frontend
66
NPM = npm
77

8-
.PHONY: install_frontend install_frontendci install_frontendc frontend_deps_check build_frontend run_frontend frontend frontendc format_frontend tests_frontend test_frontend test_frontend_watch test_frontend_coverage test_frontend_verbose test_frontend_ci test_frontend_clean test_frontend_file test_frontend_pattern test_frontend_snapshots test_frontend_config test_frontend_bail test_frontend_silent test_frontend_coverage_open help_frontend storybook storybook_build storybook_network
8+
.PHONY: install_frontend install_frontendci install_frontendc frontend_deps_check build_frontend run_frontend frontend frontendc format_frontend tests_frontend test_frontend_a11y_e2e test_frontend_a11y_e2e_blocking test_frontend_a11y_e2e_update test_frontend test_frontend_watch test_frontend_coverage test_frontend_verbose test_frontend_ci test_frontend_clean test_frontend_file test_frontend_pattern test_frontend_snapshots test_frontend_config test_frontend_bail test_frontend_silent test_frontend_coverage_open help_frontend storybook storybook_build storybook_network
99

1010
######################
1111
# FRONTEND DEPENDENCIES
@@ -77,6 +77,17 @@ else
7777
@cd $(FRONTEND_DIR) && npx playwright test --project=chromium
7878
endif
7979

80+
test_frontend_a11y_e2e: frontend_deps_check ## run non-blocking Playwright a11y scans and reports
81+
@cd $(FRONTEND_DIR) && SCAN_SPECS="$$(grep -rl "runA11yScan(" tests --include="*.spec.ts" | sort)" && echo "Specs with a11y scans:" && echo "$$SCAN_SPECS" && test -n "$$SCAN_SPECS" && RUN_A11Y=true RUN_A11Y_ASSERT=false npx playwright test $$SCAN_SPECS --project=chromium --workers=1
82+
83+
test_frontend_a11y_e2e_blocking: frontend_deps_check ## run blocking Playwright a11y scans against baselines
84+
@cd $(FRONTEND_DIR) && SCAN_SPECS="$$(grep -rl "runA11yScan(" tests --include="*.spec.ts" | sort)" && echo "Specs with a11y scans:" && echo "$$SCAN_SPECS" && test -n "$$SCAN_SPECS" && RUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test $$SCAN_SPECS --project=chromium --workers=1
85+
86+
test_frontend_a11y_e2e_update: frontend_deps_check ## refresh Playwright a11y reports used as baselines
87+
@cd $(FRONTEND_DIR) && rm -rf coverage/accessibility-reports && SCAN_SPECS="$$(grep -rl "runA11yScan(" tests --include="*.spec.ts" | sort)" && echo "Specs with a11y scans:" && echo "$$SCAN_SPECS" && test -n "$$SCAN_SPECS" && RUN_A11Y=true RUN_A11Y_ASSERT=false npx playwright test $$SCAN_SPECS --project=chromium --workers=1
88+
@mkdir -p $(FRONTEND_DIR)/tests/baselines
89+
@cp $(FRONTEND_DIR)/coverage/accessibility-reports/chromium__*.json $(FRONTEND_DIR)/tests/baselines/
90+
8091
######################
8192
# FRONTEND UNIT TESTS (JEST)
8293
######################
@@ -102,9 +113,16 @@ test_frontend_verbose: frontend_deps_check ## run frontend tests with verbose ou
102113
@cd $(FRONTEND_DIR) && npx jest --verbose
103114

104115
# Run frontend tests in CI mode (no watch, with coverage)
116+
# Excludes *.a11y.test.* files - those run in their own blocking
117+
# workflow (a11y-unit-tests.yml) with a dedicated junit check.
105118
test_frontend_ci: frontend_deps_check ## run frontend tests in CI mode
106119
@echo "Running frontend tests in CI mode..."
107-
@cd $(FRONTEND_DIR) && CI=true npx jest --ci --coverage --watchAll=false
120+
@cd $(FRONTEND_DIR) && CI=true npx jest --ci --coverage --watchAll=false --testPathIgnorePatterns="/node_modules/" "test-utils.tsx" "\.a11y\.test\."
121+
122+
# Run only the a11y component tests (jest-axe)
123+
test_frontend_a11y_ci: frontend_deps_check ## run frontend a11y unit tests in CI mode
124+
@echo "Running frontend a11y unit tests in CI mode..."
125+
@cd $(FRONTEND_DIR) && CI=true npx jest --ci --watchAll=false --testPathPatterns="\.a11y\.test\."
108126

109127
# Clean test cache and run tests
110128
test_frontend_clean: frontend_deps_check ## clean test cache and run tests
@@ -268,4 +286,4 @@ help_frontend: ## show frontend help
268286
@echo " $(GREEN)make storybook_network$(NC) - Run Storybook accessible on network"
269287
@echo ''
270288
@echo "$(GREEN)═══════════════════════════════════════════════════════════════════$(NC)"
271-
@echo ''
289+
@echo ''

0 commit comments

Comments
 (0)