Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions .github/workflows/axe.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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<<EOF" >> $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
8 changes: 8 additions & 0 deletions components/ContentTemplates/ImagesTemplate.tsx
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -8,6 +10,12 @@ import { PageUpdated } from "../PageUpdated/PageUpdated"
export const ImagesTemplate = () => {
return (
<>
<img src="/images/imagesTemplate/oldPaperTexture.jpg"></img>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WCAG 1.1.1: Image element missing alt attribute.

Images must have alternate text. Add an alt attribute to <img> elements. Decorative images may use an empty alt attribute (alt=""), role='none', or role='presentation'.

Details

Adjacent text: /* eslint-disable jsx-a11y/no-noninteractive-tabindex /
/
eslint-disable jsx-a11y/alt-text */
impo

Every image needs an alt attribute. For informative images, describe the content or function concisely. For decorative images (backgrounds, spacers, purely visual flourishes), use alt='' to hide them from screen readers. Never omit alt entirely—screen readers may read the filename instead.

<div role="button">
Testing
<div role="menuitem">123</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WCAG 1.3.1: Role "menuitem" must be contained within: menu, menubar, group.

Certain ARIA roles must be contained within specific parent roles.

Details

Some ARIA roles represent items that must exist within specific container roles. For example, a listitem must be within a list, a tab must be within a tablist. Wrap the element in the appropriate parent, or use native HTML elements that provide this structure (e.g., <li> inside <ul>).

</div>
<div tabIndex={0}>Testing tabindex</div>
<NavPage pageNavigation={imagePageNavigation} />
<article>
<TemplateSection sectionName="introduction" title="Introduction">
Expand Down
62 changes: 62 additions & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
@@ -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",
};
```
148 changes: 148 additions & 0 deletions scripts/get-changed-urls.js
Original file line number Diff line number Diff line change
@@ -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"))
}
Loading