Skip to content

small fixes

small fixes #42

Workflow file for this run

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