diff --git a/.github/workflows/axe.yaml b/.github/workflows/axe.yaml index 45f7114..ec2df6a 100644 --- a/.github/workflows/axe.yaml +++ b/.github/workflows/axe.yaml @@ -17,7 +17,25 @@ jobs: - run: npm run dev & npx wait-on http://localhost:3000 - name: Install browser drivers run: npx browser-driver-manager install chrome - - name: Run axe + - name: Get URLs to scan based on changed files + id: urls + env: + GITHUB_BASE_SHA: ${{ github.event.pull_request.base.sha }} + run: | + # Fetch base branch to ensure valid git history + git fetch origin ${{ github.event.pull_request.base.ref }} + + URLS=$(node scripts/get-changed-urls.js http://localhost:3000 --include-home) + echo "urls<> $GITHUB_OUTPUT + echo "$URLS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + - name: Run axe on changed pages run: | npm install -g @axe-core/cli - axe http://localhost:3000 --exit + + # Scan each changed URL + echo "${{ steps.urls.outputs.urls }}" | while read url; do + [ -z "$url" ] && continue + echo "Scanning: $url" + axe "$url" --exit + done diff --git a/components/ContentTemplates/ImagesTemplate.tsx b/components/ContentTemplates/ImagesTemplate.tsx index de98d28..ae423ba 100644 --- a/components/ContentTemplates/ImagesTemplate.tsx +++ b/components/ContentTemplates/ImagesTemplate.tsx @@ -1,3 +1,5 @@ +/* eslint-disable jsx-a11y/no-noninteractive-tabindex */ +/* eslint-disable jsx-a11y/alt-text */ import Image from "next/legacy/image" import { NavPage } from "../NavPage/NavPage" import { CodeBlock } from "../CodeBlock/CodeBlock" @@ -8,6 +10,12 @@ import { PageUpdated } from "../PageUpdated/PageUpdated" export const ImagesTemplate = () => { return ( <> + +
+ Testing +
123
+
+
Testing tabindex
diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..e854963 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,62 @@ +# URL Mapping Script for Axe Testing + +This script automatically detects which content pages have changed in a git diff and maps them to their corresponding URLs for accessibility testing with axe-core. + +## How it works + +1. **Detects changed files** using `git diff`: + - In PR context: compares against the base branch + - In push context: compares against the previous commit + +2. **Maps components to URLs** by: + - Reading `data/pages.ts` to get the page URL mapping + - Checking if changed files are template components (e.g., `AudioTemplate.tsx`) + - Converting template names to content slugs (e.g., `AudioTemplate` → `audio` → `/audio`) + +3. **Special cases**: + - If layout components change (Header, Footer, Nav, Layout), includes homepage + - If data files change, includes homepage + first few pages as sanity check + - If no relevant changes detected, defaults to homepage + +## Usage + +```bash +# Get URLs with homepage included +node scripts/get-changed-urls.js http://localhost:3000 --include-home + +# Get URLs without forcing homepage (only affected pages) +node scripts/get-changed-urls.js http://localhost:3000 +``` + +## Output + +The script outputs URLs (one per line): + +``` +http://localhost:3000 +http://localhost:3000/audio +http://localhost:3000/forms +``` + +## In GitHub Actions + +The workflow calls this script and pipes each URL to `axe` for accessibility testing. See `.github/workflows/axe.yaml` for the full implementation. + +## Extending the mapping + +To add new pages: +1. Add the page to `data/pages.ts` with a `content` field +2. Add the corresponding template name mapping in this script's `templateNameMap` object +3. The script will automatically detect changes to that template + +Example: +```javascript +// In data/pages.ts +{ name: "Example", href: "/example", content: "example" } + +// In scripts/get-changed-urls.js +const templateNameMap = { + // ... existing mappings + ExampleTemplate: "example", +}; +``` diff --git a/scripts/get-changed-urls.js b/scripts/get-changed-urls.js new file mode 100644 index 0000000..b6fca2d --- /dev/null +++ b/scripts/get-changed-urls.js @@ -0,0 +1,148 @@ +#!/usr/bin/env node +/* eslint-disable @typescript-eslint/no-var-requires */ + +/** + * Get list of URLs to scan based on changed files in git. + * Maps changed component templates back to their URLs using data/pages.ts + * + * Usage: + * node scripts/get-changed-urls.js [baseUrl] [--include-home] + * + * Example: + * node scripts/get-changed-urls.js http://localhost:3000 --include-home + */ + +const fs = require("fs") +const path = require("path") +const { execSync } = require("child_process") + +const baseUrl = process.argv[2] || "http://localhost:3000" +const includeHome = process.argv.includes("--include-home") + +// Get git changes +let changedFiles = [] +try { + let diff + // Check if running in PR context (GitHub Actions sets GITHUB_BASE_SHA) + if (process.env.GITHUB_BASE_SHA) { + // For PR: compare against base branch SHA + diff = execSync( + `git diff --name-only ${process.env.GITHUB_BASE_SHA}...HEAD` + ).toString() + } else { + // For push: compare against previous commit + diff = execSync("git diff --name-only HEAD~1 HEAD").toString() + } + changedFiles = diff.trim().split("\n").filter(Boolean) +} catch (error) { + console.error("Error getting changed files:", error.message) + process.exit(1) +} + +// Read pages data to build URL mapping +let pagesData +try { + const pagesContent = fs.readFileSync( + path.join(__dirname, "../data/pages.ts"), + "utf-8" + ) + + // Extract the pages array from the TypeScript file + const regex = /export const pages: IPage\[\] = \[([\s\S]*?)\]/ + const match = pagesContent.match(regex) + if (!match) { + console.error("Could not parse pages.ts") + process.exit(1) + } + + pagesData = [] + const arrayContent = match[1] + + // Parse each page entry + const pageRegex = + /{\s*name:\s*"([^"]+)",\s*href:\s*"([^"]+)",\s*content:\s*"([^"]+)"\s*}/g + let pageMatch + while ((pageMatch = pageRegex.exec(arrayContent)) !== null) { + pagesData.push({ + name: pageMatch[1], + href: pageMatch[2], + content: pageMatch[3], + }) + } +} catch (error) { + console.error("Error parsing pages.ts:", error.message) + process.exit(1) +} + +// Map component/template files to content names +const templateNameMap = { + AlertsTemplate: "alerts", + AnimationsTemplate: "animations", + AudioTemplate: "audio", + BreadcrumbsTemplate: "breadcrumbs", + ButtonsTemplate: "buttons", + CaptchasTemplate: "captchas", + ChartsTemplate: "charts", + FormsTemplate: "forms", + HeadingsTemplate: "headings", + IconsTemplate: "icons", + ImagesTemplate: "images", + LinksTemplate: "links", + ListsTemplate: "lists", + MenusTemplate: "menus", + ModalsTemplate: "modals", + NavigationTemplate: "navigation", + PaginationTemplate: "pagination", + TablesTemplate: "tables", + VideoTemplate: "video", +} + +// Find which URLs need to be scanned +const urlsToScan = new Set() + +// Always include homepage if flag is set or if certain key files change +if (includeHome) { + urlsToScan.add(baseUrl) +} + +for (const file of changedFiles) { + // Check if it's a content template file + for (const [templateName, contentName] of Object.entries(templateNameMap)) { + if (file.includes(templateName)) { + // Find the corresponding page + const page = pagesData.find((p) => p.content === contentName) + if (page) { + urlsToScan.add(`${baseUrl}${page.href}`) + } + break + } + } + + // If pages.ts or data files changed, include home and some key pages + if (file.includes("data/") || file.includes("utils.ts")) { + urlsToScan.add(baseUrl) + // Add first few pages as sanity check + pagesData.slice(0, 3).forEach((page) => { + urlsToScan.add(`${baseUrl}${page.href}`) + }) + break + } + + // If layout/core components changed, scan home + if ( + file.includes("components/Layout/") || + file.includes("components/Header/") || + file.includes("components/Footer/") || + file.includes("components/Nav/") + ) { + urlsToScan.add(baseUrl) + } +} + +// Output URLs +if (urlsToScan.size === 0) { + // No relevant changes detected, just scan homepage + console.log(baseUrl) +} else { + console.log(Array.from(urlsToScan).join("\n")) +}