Skip to content
Draft
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# MacOS
.DS_Store

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
76 changes: 76 additions & 0 deletions a11y/docs/a11y_page_titles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# a11y_page_titles

Django management command that generates a markdown report comparing page titles across the OLH, Material, and Clean themes for a list of URLs.

## Purpose
To assit with finding areas of non-compliance with [WCAG 2.4.2 Page Titled](https://www.w3.org/WAI/WCAG22/Understanding/page-titled)
> Web pages have titles that describe topic or purpose.

"Describe" is subjective. This command cannot determine whether the title is descriptive. It is a helper command, to generate list of titles for a developer to review." A check is included as to whether all three themes have the same title, but this has no direct bearing on whether the title passes the requirement. Where the same title is expected across all three themes this provides a quick way to review the results.

## What it does

The command:

1. Loads a list of URLs (from a JSON file)
2. Fetches each URL once per theme (olh, material, clean) via the Django test client
3. Extracts the `<title>` from each response
4. Writes a markdown table to a file, with one row per URL and columns for each theme’s title and whether all three match

## How to use

### Defaults
- Input [`page_title_urls.json`](../playwright/tests/test_inputs/page_title_urls.json)
- Output [`results/markdown/page_titles.md`](../results/markdown/page_titles.md)

> **Note:** This command uses `page_title_urls.json` as its default, not `front_of_house.json` (which is the default for the Playwright tests). The page title check deliberately spans multiple journals (OLH, ANE, Glossa) so that titles which should include the journal name can be verified across different journals — a single-journal list would not catch errors where the journal name is missing or incorrect.

### Options

| Option | Default | Description |
|--------|---------|-------------|
| `--output PATH` | `a11y/results/page_titles.md` | Path to the output markdown file. The directory is created if it doesn’t exist. |
| `--urls-json PATH` | `a11y/test_inputs/localhost_urls.json` | Path to a JSON file containing an array of URL strings to check. |

### Examples

**Default behaviour** — use default URL list and write to default output:

```bash
python manage.py a11y_page_titles
```

**Both options** — custom URLs and custom output:

```bash
python manage.py a11y_page_titles --urls-json path/to/urls.json --output path/to/report.md
```

### URL list format

The `--urls-json` file must be valid JSON and contain a **single array of URL strings**. Order is preserved and determines the order of rows in the report. Duplicates are removed while keeping the first occurrence.

Example:

```json
[
"http://localhost:8000/",
"http://localhost:8000/contact",
"http://localhost:8000/olh/"
]
```

## Results

The results can be found at [`scripts/results/markdown/page_titles.md`](../../results/markdown/page_titles.md). There is only a markdown file, no json. The generated results table includes a final blank column for human review.

| URL | OLH Title | Material Title | Clean Title | All Identical | Human Review |
|-----|-----------|----------------|-------------|---------------|--------------|
| [http://localhost:8000/](http://localhost:8000/) | Open Library of Humanities | Open Library of Humanities | Open Library of Humanities | :white_check_mark: | |
| ...| ... | ... | ... | ... | |

This should be filled out by a **human** after the table has been generated, to show which titles satisfy the requirement of a descriptive title, and which do not.

Line 4 must also be added afterwards, to note which commit the test was run against, e.g.

> Test run on tag a11y-audit-1.9, 17 March 2026.
33 changes: 33 additions & 0 deletions a11y/docs/a11y_scripts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Accessiblity Testing Helper Scripts

## Management Commands
These are in the same directory as the other managment commands.
1. [a11y_page_titles](docs/a11y_page_titles.md)


## Playwright Scripts
These are in the `playwright/` directory. Run from there with `npx playwright test`.

1. [Accessibility (axe/WCAG)](docs/playwright_accessibility_testing.md) — `tests/accessibility.spec.js`
Runs axe-core against a list of URLs and checks for WCAG 2.2 Level A/AA violations.
2. [Target size](docs/target_size.md) — `tests/target_size.js`
Records the pixel dimensions of every focusable element against WCAG 2.2 AA (24 px) and AAA (44 px) thresholds. Outputs a markdown table and CSV to `test-results/`.
3. [Axe teardown](docs/axe_teardown.md) — `axe-teardown.js`
Global teardown that runs automatically after all tests. Merges per-URL axe results into a single timestamped report (`.md`, `.tsv`, `.json`) in `test-results/`.

## URL input files

All URL lists live in `playwright/tests/test_inputs/`. Pass a different file to any test with the `URL_LIST` environment variable, or run a single URL with `A11Y_URL`.

| File | Default for | Description |
|------|------------|-------------|
| `front_of_house.json` | Accessibility test, Target size test | Broad front-of-house URL list covering the three main themes (clean, OLH, material) across a representative set of page types. Used as the default for most automated tests. |
| `page_title_urls.json` | `a11y_page_titles` management command | Multi-journal URL list (OLH, ANE, Glossa) used to verify page titles include the correct journal name. Intentionally spans multiple journals — a single-journal list would not catch errors where the journal name is missing or wrong. |
| `clarity.json` | — | URLs for the Clarity theme. Use with `URL_LIST` when testing Clarity specifically. |
| `clean.json` | — | URLs for the Clean theme. |
| `hourglass.json` | — | URLs for the Hourglass theme. |
| `material.json` | — | URLs for the Material theme. |
| `olh.json` | — | URLs for the OLH theme. |

## Results
Results are git-tracked so we can evaluate progress between audits.
39 changes: 39 additions & 0 deletions a11y/docs/axe_teardown.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Axe teardown

Global teardown script (`axe-teardown.js`) that runs automatically after all Playwright tests complete. It merges the per-URL axe results written during the test run into a single combined report.

You do not invoke this directly — Playwright runs it via the `globalTeardown` setting in `playwright.config.js`.

## What it does

1. Reads all `axe-raw/axe-*.json` files from `test-results/` (one per URL, written by `accessibility.spec.js` during the run).
2. Merges them into a combined report.
3. Writes three output files to `test-results/`, each with a timestamp in the filename:
- `axe-violations-<timestamp>.md` — full human-readable report (see structure below)
- `axe-violations-<timestamp>.tsv` — one row per failing element, for spreadsheet analysis
- `axe-violations-<timestamp>.json` — same data as the TSV, structured as a JSON array

## Report structure (`.md`)

| Section | Description |
|---------|-------------|
| **Header** | Total URLs tested, URLs passing, URLs with violations, total rule violations, total failing elements |
| **Summary by URL** | Table of every URL with total error count and pass/fail status |
| **Rules run and results** | Table of every axe rule executed, with description, WCAG success criteria, error count, and pass/fail status |
| **Failure summary reference** | Deduplicated list of failure messages, each with a stable anchor link and related WCAG criteria. Per-element rows in the results table link back here to avoid repetition |
| **Results** | Full table of every failing element: page, rule, impact, CSS selector, HTML snippet, and link to the relevant failure summary |

## TSV / JSON columns

| Column | Description |
|--------|-------------|
| `url` | Page the violation was found on |
| `rule_id` | axe rule identifier (e.g. `color-contrast`) |
| `impact` | `critical`, `serious`, `moderate`, or `minor` |
| `Selector` | CSS selector of the failing element |
| `html_snippet` | First 200 characters of the element's outer HTML |
| `failure_summary` | Full axe failure summary text for that element |

## Timestamps

Each run produces new files rather than overwriting previous ones, so reports from multiple runs are preserved in `test-results/`. The `test-results/` folder is listed in `.gitignore` — copy reports you want to keep into `results/` where they will be tracked.
74 changes: 74 additions & 0 deletions a11y/docs/playwright_accessibility_testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Accessibility testing with Playwright and Axe (WCAG 2.2)

This project runs automated accessibility checks using [axe-core](https://github.com/dequelabs/axe-core) via [@axe-core/playwright](https://www.npmjs.com/package/@axe-core/playwright). The test runs all applicable axe rule sets: **WCAG 2.x Level A and AA**, **ACT** (W3C Accessibility Conformance Testing), **best practices**, and **EN 301 549** (tags: `wcag2a`, `wcag2aa`, `wcag21a`, `wcag21aa`, `wcag22aa`, `best-practice`, `EN-301-549`, `ACT`).

## Prerequisites

Install dependencies (including **@axe-core/playwright** and **@playwright/test**) and Playwright browsers once:

```bash
npm install
npx playwright install
```

`npm install` installs the axe and Playwright packages; `npx playwright install` downloads the browser binaries needed to run the tests.

Note: the `playwright/test-results` folder is overwritten each time by playwright, any data to be kept must be copied outside of that folder.

## URL list

Both the accessibility and target size tests use the same URL list mechanism:

- **`tests/test_inputs/front_of_house.json`** – default list (edit this file to add or change pages).
- **`URL_LIST`** – environment variable to point at another JSON file (array of URL strings).
- **`A11Y_URL`** – if set, the test runs against this single URL only (ignores the list).

### Run against the URL list (default)

From the `playwright` directory:

```bash
npx playwright test accessibility --project=chromium
```

The test visits each URL in `tests/test_inputs/front_of_house.json`, runs axe on that page, and collects violations. If any page has violations, the test fails and the report lists violations per URL.

### Run against a single URL

```bash
A11Y_URL=http://localhost:3000 npx playwright test accessibility --project=chromium
```

### Use a different URL list

```bash
URL_LIST=/path/to/my-urls.json npx playwright test accessibility --project=chromium
```

### Run in headed mode (see the browser)

```bash
npx playwright test accessibility --project=chromium --headed
```

### Run in all configured browsers

```bash
npx playwright test accessibility
```

## What the test does

1. Loads URLs specified.
2. For each URL: opens the page, injects axe-core, and runs WCAG 2.2 Level A and AA plus ACT rules.
3. **Passes** if no page has violations.
4. **Fails** if any page has violations; the report and attachments list violations per URL.

When there are violations, two attachments are added to the run (visible in the HTML report): **axe-violations.md** (markdown) and **axe-violations.tsv** (tab-separated values, one row per failing element: url, rule_id, impact, Selector, html_snippet).

## Reading the results

- **Pass:** No violations were reported.
- **Fail:** The test output lists each failing rule. After the run, a timestamped report is written to `test-results/` by the global teardown (`axe-teardown.js`): an `axe-violations-<timestamp>.md` with a full summary and per-element details, an `axe-violations-<timestamp>.tsv` for spreadsheet analysis, and an `axe-violations-<timestamp>.json`. Review the `.md` file, fix the reported issues, and re-run.

Automated checks cannot cover every accessibility requirement (e.g. some need manual review). Use this test as part of a broader a11y strategy.
86 changes: 86 additions & 0 deletions a11y/docs/target_size.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Target size test

Playwright test that records the pixel dimensions of every focusable element on each tested page and checks them against the WCAG 2.2 target size thresholds.

## Purpose

To assist with finding areas of non-compliance with [WCAG 2.5.8 Target Size (Minimum)](https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum) (Level AA) and [WCAG 2.5.5 Target Size (Enhanced)](https://www.w3.org/WAI/WCAG22/Understanding/target-size-enhanced) (Level AAA):

| Level | Minimum size |
|-------|-------------|
| AA | 24 × 24 px |
| AAA | 44 × 44 px |

The test collects data for all focusable elements — it does not fail on small targets. Use the output report to identify elements that fall below the thresholds.

**Text links** (`<a href>` with no `img` or `svg` child) are flagged separately in the report. WCAG 2.2 provides a height exception for inline text links because their height is determined by the surrounding line height rather than the author.

## Prerequisites

Install dependencies and browsers once from the `playwright/` directory:

```bash
npm install
npx playwright install
```

## URL list

Defaults to `tests/test_inputs/front_of_house.json`. This is the same default as the accessibility test — see [playwright_accessibility_testing.md](playwright_accessibility_testing.md) for details on the URL list format and environment variable overrides.

| Environment variable | Effect |
|---------------------|--------|
| `URL_LIST=/path/to/urls.json` | Use a different JSON file (array of URL strings) |
| `A11Y_URL=http://localhost:8000/olh/` | Run against a single URL only |

## How to run

From the `playwright/` directory:

```bash
npx playwright test target_size --project=chromium
```

### Run against a single URL

```bash
A11Y_URL=http://localhost:8000/olh/ npx playwright test target_size --project=chromium
```

### Use a different URL list

```bash
URL_LIST=/path/to/my-urls.json npx playwright test target_size --project=chromium
```

### Run in headed mode

```bash
npx playwright test target_size --project=chromium --headed
```

## Output

After the run, two files are written to the test's output directory inside `test-results/`:

- **`target-size-report.md`** — a markdown table with one row per focusable element, showing URL, tag, accessible name, width, height, whether it is a text link, and whether it meets AA and AAA thresholds.
- **`target-size-report.csv`** — the same data in CSV format for spreadsheet analysis.

The report header also shows the total number of URLs tested and total focusable elements found.

## Reading the results

Each row in the report shows:

| Column | Description |
|--------|-------------|
| URL | Page the element was found on |
| # | Element index on that page |
| Tag | HTML tag (`a`, `button`, `input`, etc.) |
| Name / Label | Accessible name (aria-label, title, placeholder, or text content) |
| Width / Height (px) | Rendered size |
| Text link | ✓ if this is an inline text `<a href>` with no img/svg |
| ≥24×24 (AA) | ✓ meets WCAG 2.2 Level AA minimum |
| ≥44×44 (AAA) | ✓ meets WCAG 2.2 Level AAA enhanced |

Elements where **Text link** is ✓ and the AA column is ✗ may still pass WCAG 2.2 due to the height exception — review these manually.
8 changes: 8 additions & 0 deletions a11y/playwright/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

# Playwright
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/.auth/
Loading