Skip to content
Merged
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
46 changes: 46 additions & 0 deletions .github/workflows/a11y.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Accessibility Checks

on:
push:
branches:
- master
pull_request:

jobs:
axe-check:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Installs
run: npm install jupyter-book selenium-webdriver @axe-core/webdriverjs chromedriver http-server wait-on

- name: Build Jupyter Book
run: npx jupyter-book build --html

- name: Run Axe Checks
run: |
# A. Start a local server in the background
npx http-server ./_build/html -p 8080 -s &
npx wait-on http://localhost:8080

# B. Generate the URL list from the actual build artifacts
cd _build/html
URLS=$(find . -name "*.html" -not -path "*/build/*" | sed 's|^\./||' | sed 's|^|http://localhost:8080/|' | tr '\n' ' ')
cd ../..

# C. Run custom script
node axe-scan.js $URLS

- name: Upload Accessibility Report
if: always()
uses: actions/upload-artifact@v4
with:
name: axe-report
path: axe-report.json
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# textbook
Textbook for Data 88E: Economic Models at UC Berkeley
# Textbook for Data 88E: Economic Models at UC Berkeley

Content is stored in the content folder. Order of textbook can be changed from _toc.yml file.
[![Jupyter Book (via myst) GitHub Pages Deploy](https://github.com/data-88e/textbook/actions/workflows/deploy.yml/badge.svg)](https://github.com/data-88e/textbook/actions/workflows/deploy.yml) [![Accessibility Checks](https://github.com/data-88e/textbook/actions/workflows/a11y.yml/badge.svg)](https://github.com/data-88e/textbook/actions/workflows/a11y.yml)

Content is stored in the content folder. Order of textbook can be changed from the `myst.yml` file.

To build the textbook, run
```
jupyter-book build .
```

This will output HTML to `_build/html`, which can be copied to `docs` in order to be served on GitHub Pages.
This will output HTML to `_build/html`.


# NOTE: Chapter 4 - shifts.ipynb
Expand Down
90 changes: 90 additions & 0 deletions axe-scan.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
const { Builder } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const AxeBuilder = require('@axe-core/webdriverjs');
const fs = require('fs');

// Get URLs from command line args
const urls = process.argv.slice(2);
console.log(`\nStarting Axe scan on ${urls.length} pages...\n`);

(async function scan() {
const options = new chrome.Options();
options.addArguments('--headless=new');
options.addArguments('--no-sandbox');
options.addArguments('--disable-dev-shm-usage');
options.addArguments('--disable-gpu');
// see if these help with the production/shifts page crashing
options.addArguments('--window-size=1920,1080');
options.addArguments('--disable-extensions');
options.addArguments('--disable-infobars');
options.addArguments('--disable-software-rasterizer');

// Initialize Driver
const driver = await new Builder()
.forBrowser('chrome')
.setChromeOptions(options)
.build();

const fullReport = [];
let hasErrors = false;

try {
// Set a very generous timeout for page loads (3 minutes)
await driver.manage().setTimeouts({ pageLoad: 180000, script: 180000 });

for (let i = 0; i < urls.length; i++) {
const url = urls[i];
console.log(`[${i + 1}/${urls.length}] Testing ${url} ...`);

try {
await driver.get(url);

const results = await new AxeBuilder(driver)
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze();

if (results.violations.length > 0) {
console.log(` FAILED: ${results.violations.length} violations found.`);
results.violations.forEach((violation) => {
console.log(` [${violation.impact.toUpperCase()}] ${violation.id}: ${violation.help}`);
console.log(` Help URL: ${violation.helpUrl}`);

violation.nodes.forEach((node) => {
console.log(` - Selector: ${node.target.join(' ')}`);
});
console.log(''); // Empty line for readability
});

fullReport.push({
url: url,
violations: results.violations
});
hasErrors = true;
} else {
console.log(` PASSED`);
}

} catch (e) {
console.error(` CRASHED: Could not scan ${url}. Skipping.`);
console.error(` Reason: ${e.message}`);
fullReport.push({
url: url,
error: "Browser crashed or timed out on this page",
details: e.message
});
hasErrors = true;
}
}

// Save JSON report
fs.writeFileSync('axe-report.json', JSON.stringify(fullReport, null, 2));
console.log('\nReport saved to axe-report.json');

} finally {
await driver.quit();
}

if (hasErrors) {
process.exit(1);
}
})();
7 changes: 1 addition & 6 deletions myst.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,4 @@ site:
favicon: content/images/favicon.ico
folders: true
hide_authors: true
template: book-theme
sphinx:
config:
# This overrides the default theme (Sphinx Book Theme)
# to use your custom theme.
html_theme: quantecon_book_theme
template: book-theme
Loading