small fixes #42
Workflow file for this run
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
| name: Accessibility Testing | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| env: | |
| TEST_BASE_URL: http://localhost:3000 | |
| TEST_PAGES: "/" | |
| jobs: | |
| setup: | |
| name: Setup and build | |
| runs-on: ubuntu-latest | |
| outputs: | |
| cache-key: ${{ steps.cache-key.outputs.key }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: "yarn" | |
| - name: Generate cache key | |
| id: cache-key | |
| run: echo "key=${{ runner.os }}-a11y-node20-${{ hashFiles('yarn.lock') }}" >> $GITHUB_OUTPUT | |
| - name: Cache dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: node_modules | |
| key: ${{ steps.cache-key.outputs.key }} | |
| restore-keys: | | |
| ${{ runner.os }}-a11y-node20- | |
| - name: Install dependencies | |
| run: yarn install --frozen-lockfile | |
| - name: Build site | |
| run: yarn build | |
| - name: Cache build output | |
| uses: actions/cache@v4 | |
| with: | |
| path: dist | |
| key: ${{ runner.os }}-build-${{ github.sha }} | |
| axe-core: | |
| name: Axe Core Accessibility Testing | |
| runs-on: ubuntu-latest | |
| needs: setup | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: "yarn" | |
| - name: Restore dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: node_modules | |
| key: ${{ needs.setup.outputs.cache-key }} | |
| - name: Restore build output | |
| uses: actions/cache@v4 | |
| with: | |
| path: dist | |
| key: ${{ runner.os }}-build-${{ github.sha }} | |
| - name: Start preview server | |
| run: | | |
| yarn preview --port 3000 & | |
| npx wait-on http://localhost:3000 --timeout 60000 | |
| - name: Install Axe CLI and browser drivers | |
| run: | | |
| npm install -g @axe-core/cli | |
| npx browser-driver-manager install chrome | |
| - name: Extract routes from Router.tsx | |
| id: extract-routes | |
| run: | | |
| echo "Extracting routes from React Router configuration..." | |
| if ROUTES=$(node scripts/extract-routes.js 2>/dev/null); then | |
| echo "Found routes: $ROUTES" | |
| echo "test_pages=$ROUTES" >> $GITHUB_OUTPUT | |
| else | |
| echo "Route extraction failed, using fallback routes: $TEST_PAGES" | |
| echo "test_pages=$TEST_PAGES" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Run Axe accessibility tests | |
| run: | | |
| # Use extracted routes | |
| IFS=' ' read -ra PAGE_PATHS <<< "${{ steps.extract-routes.outputs.test_pages }}" | |
| mkdir -p axe-results | |
| FAILED=0 | |
| for path in "${PAGE_PATHS[@]}"; do | |
| url="${TEST_BASE_URL}${path}" | |
| echo "Testing $url with Axe Core..." | |
| PAGE_NAME=$(echo $path | sed 's|/|-|g' | sed 's|^-||') | |
| if [ -z "$PAGE_NAME" ]; then PAGE_NAME="home"; fi | |
| if ! axe "$url" \ | |
| --save "axe-results/axe-$PAGE_NAME.json" \ | |
| --tags wcag2a,wcag2aa,wcag21aa; then | |
| FAILED=1 | |
| fi | |
| done | |
| # Combine all results into one file | |
| echo '{"results":[' > axe-results.json | |
| first=true | |
| for file in axe-results/axe-*.json; do | |
| if [ -f "$file" ]; then | |
| if [ "$first" = true ]; then | |
| first=false | |
| else | |
| echo ',' >> axe-results.json | |
| fi | |
| cat "$file" >> axe-results.json | |
| fi | |
| done | |
| echo ']}' >> axe-results.json | |
| if [ $FAILED -eq 1 ]; then | |
| exit 1 | |
| fi | |
| - name: Upload Axe results | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: axe-accessibility-results | |
| path: | | |
| axe-results.json | |
| axe-results/ | |
| lighthouse: | |
| name: Lighthouse Accessibility & Performance | |
| runs-on: ubuntu-latest | |
| needs: setup | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| checks: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: "yarn" | |
| - name: Restore dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: node_modules | |
| key: ${{ needs.setup.outputs.cache-key }} | |
| - name: Restore build output | |
| uses: actions/cache@v4 | |
| with: | |
| path: dist | |
| key: ${{ runner.os }}-build-${{ github.sha }} | |
| - name: Install Lighthouse CI | |
| run: npm install -g @lhci/cli@0.13.x | |
| - name: Debug Lighthouse CI setup | |
| run: | | |
| echo "Lighthouse CI version:" | |
| lhci --version | |
| echo "Current directory:" | |
| pwd | |
| echo "Lighthouse config:" | |
| cat lighthouserc.json | |
| - name: Start preview server | |
| run: | | |
| yarn preview --port 3000 & | |
| npx wait-on http://localhost:3000 --timeout 60000 | |
| - name: Verify server is responding | |
| run: | | |
| echo "Testing server response..." | |
| curl -f $TEST_BASE_URL/ || (echo "Server not responding" && exit 1) | |
| echo "Server is responding correctly!" | |
| - name: Update Lighthouse config with dynamic routes | |
| run: | | |
| echo "Updating Lighthouse configuration with routes from Router.tsx..." | |
| node scripts/update-lighthouse-config.js | |
| echo "Lighthouse config updated successfully" | |
| - name: Run Lighthouse CI | |
| env: | |
| LHCI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| LHCI_UPLOAD_TARGET: github | |
| run: | | |
| lhci autorun --debug | |
| echo "Lighthouse CI completed (exit code: $?)" | |
| - name: Collect Lighthouse artifacts | |
| if: always() | |
| run: | | |
| mkdir -p lighthouse-results | |
| if [ -d ".lighthouseci" ]; then | |
| cp -r .lighthouseci/* lighthouse-results/ 2>/dev/null || true | |
| fi | |
| if [ -d "lhci_reports" ]; then | |
| cp -r lhci_reports/* lighthouse-results/ 2>/dev/null || true | |
| fi | |
| # Create a summary file with the URLs that were tested | |
| echo "Lighthouse CI Results Summary" > lighthouse-results/summary.txt | |
| echo "=============================" >> lighthouse-results/summary.txt | |
| echo "Tested URLs:" >> lighthouse-results/summary.txt | |
| cat lighthouserc.json | grep -A 10 '"url"' >> lighthouse-results/summary.txt | |
| echo "" >> lighthouse-results/summary.txt | |
| echo "Generated at: $(date)" >> lighthouse-results/summary.txt | |
| echo "Commit: ${{ github.sha }}" >> lighthouse-results/summary.txt | |
| echo "Lighthouse artifacts collected:" | |
| ls -la lighthouse-results/ || echo "No lighthouse artifacts found" | |
| - name: Upload Lighthouse results | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: lighthouse-results | |
| path: lighthouse-results/ | |
| pa11y: | |
| name: Pa11y Accessibility Testing | |
| runs-on: ubuntu-latest | |
| needs: setup | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: "yarn" | |
| - name: Restore dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: node_modules | |
| key: ${{ needs.setup.outputs.cache-key }} | |
| - name: Restore build output | |
| uses: actions/cache@v4 | |
| with: | |
| path: dist | |
| key: ${{ runner.os }}-build-${{ github.sha }} | |
| - name: Install Pa11y | |
| run: npm install -g pa11y | |
| - name: Start preview server | |
| run: | | |
| yarn preview --port 3000 & | |
| npx wait-on http://localhost:3000 --timeout 60000 | |
| - name: Extract routes from Router.tsx | |
| id: extract-routes | |
| run: | | |
| echo "Extracting routes from React Router configuration..." | |
| if ROUTES=$(node scripts/extract-routes.js 2>/dev/null); then | |
| echo "Found routes: $ROUTES" | |
| echo "test_pages=$ROUTES" >> $GITHUB_OUTPUT | |
| else | |
| echo "Route extraction failed, using fallback routes: $TEST_PAGES" | |
| echo "test_pages=$TEST_PAGES" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Run Pa11y accessibility tests | |
| env: | |
| PUPPETEER_EXECUTABLE_PATH: /usr/bin/google-chrome-stable | |
| run: | | |
| export PUPPETEER_LAUNCH_ARGS="--no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage --disable-gpu --headless" | |
| # Use extracted routes | |
| IFS=' ' read -ra PAGE_PATHS <<< "${{ steps.extract-routes.outputs.test_pages }}" | |
| mkdir -p pa11y-results | |
| FAILED=0 | |
| echo '{"results":[' > pa11y-results.json | |
| first=true | |
| for path in "${PAGE_PATHS[@]}"; do | |
| url="${TEST_BASE_URL}${path}" | |
| echo "Testing $url with Pa11y..." | |
| PAGE_NAME=$(echo $path | sed 's|/|-|g' | sed 's|^-||') | |
| if [ -z "$PAGE_NAME" ]; then PAGE_NAME="home"; fi | |
| # Run with JSON reporter | |
| if pa11y "$url" \ | |
| --reporter json \ | |
| > "pa11y-results/pa11y-$PAGE_NAME.json"; then | |
| if [ "$first" = true ]; then | |
| first=false | |
| else | |
| echo ',' >> pa11y-results.json | |
| fi | |
| cat "pa11y-results/pa11y-$PAGE_NAME.json" >> pa11y-results.json | |
| else | |
| FAILED=1 | |
| echo "Pa11y failed for $url" | |
| fi | |
| # Also run with CLI reporter for human-readable output | |
| echo "=== Pa11y results for $url ===" | |
| pa11y "$url" \ | |
| --reporter cli \ | |
| || true | |
| echo "" | |
| done | |
| echo ']}' >> pa11y-results.json | |
| - name: Upload Pa11y results | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: pa11y-accessibility-results | |
| path: | | |
| pa11y-results.json | |
| pa11y-results/ | |
| accessibility-comment: | |
| name: Accessibility Test Summary Comment | |
| runs-on: ubuntu-latest | |
| needs: [axe-core, lighthouse, pa11y] | |
| if: always() && github.event_name == 'pull_request' | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| - name: Create accessibility summary comment | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const fs = require('fs'); | |
| // Find and delete any existing accessibility comments | |
| const comments = await github.rest.issues.listComments({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| }); | |
| const accessibilityComments = comments.data.filter(comment => | |
| comment.body.includes('🔍 Accessibility Test Results') && | |
| comment.body.includes('🤖 This comment was automatically generated by the accessibility testing workflow') | |
| ); | |
| for (const comment of accessibilityComments) { | |
| console.log(`Deleting previous accessibility comment: ${comment.id}`); | |
| try { | |
| await github.rest.issues.deleteComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: comment.id, | |
| }); | |
| } catch (error) { | |
| console.log(`Could not delete comment ${comment.id}: ${error.message}`); | |
| } | |
| } | |
| // Get job results | |
| const axeResult = '${{ needs.axe-core.result }}'; | |
| const lighthouseResult = '${{ needs.lighthouse.result }}'; | |
| const pa11yResult = '${{ needs.pa11y.result }}'; | |
| function getStatusDisplay(result) { | |
| switch(result) { | |
| case 'success': return { emoji: '✅', text: 'Passed' }; | |
| case 'failure': return { emoji: '❌', text: 'Failed' }; | |
| case 'cancelled': return { emoji: '⏭️', text: 'Cancelled' }; | |
| case 'skipped': return { emoji: '⚪', text: 'Skipped' }; | |
| default: return { emoji: '❓', text: 'Unknown' }; | |
| } | |
| } | |
| const axeStatus = getStatusDisplay(axeResult); | |
| const lighthouseStatus = getStatusDisplay(lighthouseResult); | |
| const pa11yStatus = getStatusDisplay(pa11yResult); | |
| // Read routes that were tested | |
| let testedRoutes = 'Unknown'; | |
| try { | |
| if (fs.existsSync('lighthouse-results/summary.txt')) { | |
| const summary = fs.readFileSync('lighthouse-results/summary.txt', 'utf8'); | |
| const urlMatch = summary.match(/Tested URLs:(.*?)Generated at:/s); | |
| if (urlMatch) { | |
| testedRoutes = urlMatch[1].trim().replace(/"/g, '').replace(/\[|\]/g, '').split(',').map(url => url.trim()).join(', '); | |
| } | |
| } | |
| } catch (error) { | |
| console.log('Could not read lighthouse summary:', error.message); | |
| } | |
| // Count issues if available | |
| let issueDetails = ''; | |
| try { | |
| // Try to read Axe results | |
| if (fs.existsSync('axe-accessibility-results/axe-results.json')) { | |
| const axeData = JSON.parse(fs.readFileSync('axe-accessibility-results/axe-results.json', 'utf8')); | |
| if (axeData.violations && axeData.violations.length > 0) { | |
| issueDetails += `\n**Axe Issues Found:** ${axeData.violations.length} violation(s)`; | |
| } | |
| } | |
| // Try to read Pa11y results | |
| if (fs.existsSync('pa11y-accessibility-results/pa11y-results.json')) { | |
| const pa11yData = JSON.parse(fs.readFileSync('pa11y-accessibility-results/pa11y-results.json', 'utf8')); | |
| if (pa11yData.results && pa11yData.results.length > 0) { | |
| const totalIssues = pa11yData.results.reduce((sum, result) => sum + (result.issues ? result.issues.length : 0), 0); | |
| if (totalIssues > 0) { | |
| issueDetails += `\n**Pa11y Issues Found:** ${totalIssues} issue(s)`; | |
| } | |
| } | |
| } | |
| } catch (error) { | |
| console.log('Could not parse accessibility results:', error.message); | |
| } | |
| const comment = `## 🔍 Accessibility Test Results | |
| | Tool | Status | Result | | |
| |------|--------|--------| | |
| | **Axe Core** | ${axeStatus.emoji} | ${axeStatus.text} | | |
| | **Lighthouse** | ${lighthouseStatus.emoji} | ${lighthouseStatus.text} | | |
| | **Pa11y** | ${pa11yStatus.emoji} | ${pa11yStatus.text} | | |
| Download detailed results from the **Artifacts** section of this workflow run | |
| --- | |
| <sub>🤖 This comment was automatically generated by the accessibility testing workflow</sub>`; | |
| // Post the comment | |
| await github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: comment | |
| }); | |
| accessibility-summary: | |
| name: Accessibility Test Summary | |
| runs-on: ubuntu-latest | |
| needs: [axe-core, lighthouse, pa11y] | |
| if: always() | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| - name: Create accessibility summary | |
| run: | | |
| echo "# 🔍 Accessibility Test Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Check Axe results | |
| if [ "${{ needs.axe-core.result }}" = "success" ]; then | |
| echo "✅ **Axe Core**: All accessibility tests passed" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ **Axe Core**: Accessibility issues found" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Check Lighthouse results | |
| if [ "${{ needs.lighthouse.result }}" = "success" ]; then | |
| echo "✅ **Lighthouse**: Performance and accessibility scores met thresholds" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ **Lighthouse**: Performance or accessibility scores below threshold" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Check Pa11y results | |
| if [ "${{ needs.pa11y.result }}" = "success" ]; then | |
| echo "✅ **Pa11y**: No accessibility violations detected" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ **Pa11y**: Accessibility violations found" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "📊 **Test artifacts are available for download in the workflow run**" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY |