Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: include wcag22aa as part of accessibility checks #1250

Merged
merged 8 commits into from
Oct 23, 2023
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
4 changes: 4 additions & 0 deletions docs/components/ComponentLayout.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ ul.Component__list li {
margin: var(--space-half) 0;
}

ul.Component__list li a {
line-height: 1.4;
}

.Component {
grid-row: 1;
grid-column: 1;
Expand Down
171 changes: 99 additions & 72 deletions e2e/accessibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import chalk from 'chalk';
import fs from 'fs';
import path from 'path';
import assert from 'assert';
import PQueue from 'p-queue';
import { AddressInfo } from 'net';

const DIST_PATH = path.join(__dirname, '..', 'docs', 'dist');
Expand All @@ -16,23 +17,12 @@ const MAX_WIDTH = IS_CI ? 80 : process.stdout.columns - 8;
const THEMES = ['light', 'dark'];
const AXE_PATH = require.resolve('axe-core');
const AXE_SOURCE = fs.readFileSync(AXE_PATH, 'utf8');
const CONCURRENCY = parseInt(process.env.CONCURRENCY as string, 10) || 8;
scurker marked this conversation as resolved.
Show resolved Hide resolved

let foundViolations = false;

const main = async (): Promise<void> => {
assert(
fs.existsSync(DIST_PATH) && fs.existsSync(INDEX_HTML),
'Missing dist/'
);

const app = express();
app.use(express.static(DIST_PATH));
app.use((req, res) => res.sendFile(INDEX_HTML));
const server = app.listen(0);
const { port } = server.address() as AddressInfo;

const getComponentUrls = async (port: number): Promise<Set<string>> => {
const urls = new Set<string>([`http://localhost:${port}/`]);

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(`http://localhost:${port}/`);
Expand All @@ -45,69 +35,106 @@ const main = async (): Promise<void> => {
urls.add(url);
}

await browser.close();

return urls;
};

const main = async (): Promise<void> => {
assert(
fs.existsSync(DIST_PATH) && fs.existsSync(INDEX_HTML),
'Missing dist/'
);

const app = express();
app.use(express.static(DIST_PATH));
app.use((req, res) => res.sendFile(INDEX_HTML));
const server = app.listen(0);
const { port } = server.address() as AddressInfo;
const urls = await getComponentUrls(port);

// Allow multiple instances of puppeteer to run to speed up testing
const queue = new PQueue({
concurrency: CONCURRENCY
});

// Analyze each URL.
for (const url of urls) {
const component = (url.split('/').pop() as string) || 'Index';

await page.goto(url);

for (const theme of THEMES) {
await page.evaluate(
theme => {
document.body.className = '';
document.body.classList.add(`cauldron--theme-${theme}`);
},
[theme]
);

// Try to wait until there is an idle period up to a max of 5s
await Promise.race([
await page.evaluate(
() =>
new Promise(resolve =>
// Typescript does not implement experimental apis but this should exist within puppeteer
// see: https://github.com/microsoft/TypeScript/issues/21309
(window as any).requestIdleCallback(resolve)
)
),
await page.waitForTimeout(5000)
]);

const axe = new AxePuppeteer(page, AXE_SOURCE).withTags([
'wcag2a',
'wcag2aa',
'wcag21a',
'wcag21aa'
]);
const { violations } = (await axe.analyze()) as AxeResults;

let symbol = logSymbols.success;
if (violations.length) {
symbol = logSymbols.warning;
foundViolations = true;
}

const title =
chalk.cyan(chalk.bold(component)) + ' (' + chalk.italic(theme) + ')';
const dots = '.'.repeat(MAX_WIDTH - component.length - symbol.length);
console.log(title, dots, symbol);

for (const { id, help, nodes } of violations) {
console.log('↳', chalk.underline(chalk.magenta(id)), '━', help);
nodes.map(node => {
console.log(
' ↳',
chalk.bgGreen(chalk.black(`${node.target}`)),
chalk.yellowBright(node.html)
await Promise.all(
scurker marked this conversation as resolved.
Show resolved Hide resolved
Array.from(urls).map((url: string) => {
return queue.add(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(`http://localhost:${port}/`);

const component = (url.split('/').pop() as string) || 'Index';

await page.goto(url);

for (const theme of THEMES) {
await page.evaluate(
(theme) => {
document.body.className = '';
document.body.classList.add(`cauldron--theme-${theme}`);
},
[theme]
);
});
}
}
}

// Try to wait until there is an idle period up to a max of 5s
await Promise.race([
await page.evaluate(
() =>
new Promise((resolve) =>
// Typescript does not implement experimental apis but this should exist within puppeteer
// see: https://github.com/microsoft/TypeScript/issues/21309
(window as any).requestIdleCallback(resolve)
)
),
await page.waitForTimeout(5000)
]);

const axe = new AxePuppeteer(page, AXE_SOURCE).withTags([
'wcag2a',
'wcag2aa',
'wcag21a',
'wcag21aa',
'wcag22a',
'wcag22aa'
]);
const { violations } = (await axe.analyze()) as AxeResults;

let symbol = logSymbols.success;
if (violations.length) {
symbol = logSymbols.warning;
foundViolations = true;
}

const title =
chalk.cyan(chalk.bold(component)) +
' (' +
chalk.italic(theme) +
')';
const dots = '.'.repeat(MAX_WIDTH - component.length - symbol.length);
console.log(title, dots, symbol);

for (const { id, help, nodes } of violations) {
console.log('↳', chalk.underline(chalk.magenta(id)), '━', help);
nodes.map((node) => {
console.log(
' ↳',
chalk.bgGreen(chalk.black(`${node.target}`)),
chalk.yellowBright(node.html)
);
});
}
}

await page.close();
await browser.close();
});
})
);

server.close();
await page.close();
await browser.close();
};

main()
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
]
},
"devDependencies": {
"@axe-core/puppeteer": "^4.3.1",
"@axe-core/puppeteer": "^4.8.1",
"@babel/core": "^7.22.10",
"@babel/plugin-proposal-export-default-from": "^7.22.5",
"@babel/preset-env": "^7.22.10",
Expand Down Expand Up @@ -71,7 +71,7 @@
"@typescript-eslint/eslint-plugin": "^5.59.9",
"@typescript-eslint/parser": "^5.59.9",
"autoprefixer": "^9.7.6",
"axe-core": "^4.3.5-canary.0ddc00b",
"axe-core": "^4.8.2",
"babel-loader": "^8.0.5",
"babel-plugin-module-resolver": "^3.0.0",
"babel-plugin-transform-export-extensions": "^6.22.0",
Expand Down Expand Up @@ -105,6 +105,7 @@
"node-fetch": "^3.3.0",
"nyc": "^11.3.0",
"optimize-css-assets-webpack-plugin": "^6.0.1",
"p-queue": "^6",
"postcss-cli": "^6.0.1",
"postcss-import": "^12.0.1",
"postcss-loader": "^3.0.0",
Expand Down
34 changes: 27 additions & 7 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
"@jridgewell/gen-mapping" "^0.3.0"
"@jridgewell/trace-mapping" "^0.3.9"

"@axe-core/puppeteer@^4.3.1":
version "4.7.3"
resolved "https://registry.yarnpkg.com/@axe-core/puppeteer/-/puppeteer-4.7.3.tgz#46b67584b67415a32b0bb6c118bec29e23b9cb4b"
integrity sha512-a+fkO0l4hHehEqHPJBhkZv0lz7SZlDMnYE52Sx2JuX0AMfcgL+UxcPo0QrS9LaqNXXhU67xCCqQVSinlGFixcQ==
"@axe-core/puppeteer@^4.8.1":
version "4.8.1"
resolved "https://registry.yarnpkg.com/@axe-core/puppeteer/-/puppeteer-4.8.1.tgz#33ce884cc89ac24316cb984472fcf2ad65246a1b"
integrity sha512-1gAeYS/wWlEBtRkJQ0E3PqB7cvF/aMb2rAbJBfzDox0fBcYZpPS3Q5aJE6U+DnRXy5JADMHPI69YSJx3Vdlj8g==
dependencies:
axe-core "^4.7.0"
axe-core "~4.8.2"

"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.10", "@babel/code-frame@^7.22.5":
version "7.22.10"
Expand Down Expand Up @@ -2554,11 +2554,16 @@ axe-core@^3.5.5:
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.6.tgz#e762a90d7f6dbd244ceacb4e72760ff8aad521b5"
integrity sha512-LEUDjgmdJoA3LqklSTwKYqkjcZ4HKc4ddIYGSAiSkr46NTjzg2L9RNB+lekO9P7Dlpa87+hBtzc2Fzn/+GUWMQ==

axe-core@^4.3.5-canary.0ddc00b, axe-core@^4.6.2, axe-core@^4.7.0:
axe-core@^4.6.2:
version "4.7.2"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.2.tgz#040a7342b20765cb18bb50b628394c21bccc17a0"
integrity sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==

axe-core@^4.8.2, axe-core@~4.8.2:
version "4.8.2"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.8.2.tgz#2f6f3cde40935825cf4465e3c1c9e77b240ff6ae"
integrity sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g==

axobject-query@^3.1.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a"
Expand Down Expand Up @@ -4746,7 +4751,7 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==

eventemitter3@^4.0.0:
eventemitter3@^4.0.0, eventemitter3@^4.0.4:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
Expand Down Expand Up @@ -8788,6 +8793,14 @@ p-map@^2.0.0:
resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==

p-queue@^6:
version "6.6.2"
resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426"
integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==
dependencies:
eventemitter3 "^4.0.4"
p-timeout "^3.2.0"

p-retry@^4.5.0:
version "4.6.2"
resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16"
Expand All @@ -8796,6 +8809,13 @@ p-retry@^4.5.0:
"@types/retry" "0.12.0"
retry "^0.13.1"

p-timeout@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe"
integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==
dependencies:
p-finally "^1.0.0"

p-try@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
Expand Down
Loading