Created
November 26, 2018 11:44
-
-
Save donaldpipowitch/898854e2f71f64d0d91e199537b3ee1a to your computer and use it in GitHub Desktop.
aXe based a11y checks in your CI for Storybook
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const puppeteer = require('puppeteer'); | |
const chalk = require('chalk'); | |
const { green, red, cyan, grey, bold } = chalk; | |
// we assume storybook can visited at http://localhost:9001 | |
const url = 'http://localhost:9001/iframe.html'; | |
const runAxe = () => | |
new Promise((resolve, reject) => | |
axe.run('#root', { runOnly: ['wcag2a'] }, (error, result) => | |
error ? reject(error) : resolve(result) | |
) | |
); | |
puppeteer.launch().then(async (browser) => { | |
// setup | |
const page = await browser.newPage(); | |
await page.goto(url); | |
const stories = await page.evaluate( | |
({ innerText }) => JSON.parse(innerText), | |
await page.$('#sitemap') | |
); | |
// collect errors | |
const errors = []; | |
for (const { kind, name } of stories) { | |
const selectedKind = `selectedKind=${encodeURIComponent(kind)}`; | |
const selectedStory = `selectedStory=${encodeURIComponent(name)}`; | |
await page.goto(`${url}?${selectedKind}&${selectedStory}`); | |
const { violations } = await page.evaluate(runAxe); | |
if (violations.length) { | |
errors.push({ kind, name, violations }); | |
} | |
} | |
await browser.close(); | |
// result | |
if (errors.length) { | |
errors.forEach(({ kind, name, violations }) => { | |
console.error(bold(`Error(s) in ${red(`${kind}/${name}`)}:`)); | |
violations.forEach(({ description, nodes }) => { | |
console.error(`${description} (${grey(`${nodes.length} times`)})`); | |
}); | |
console.error(''); | |
}); | |
const sum = errors.reduce((acc, error) => acc + error.violations.length, 0); | |
console.error(`${red('✖')} Found ${red(sum)} error(s)!\n`); | |
const page = cyan('http://localhost:9001'); | |
console.error( | |
`Open ${page} and visit the mentioned stories to find out more about where the problems happen and how they can be fixed.\n` | |
); | |
process.exitCode = 1; | |
} else { | |
console.log(`${green('✔')} Everything is WCAG2A compatible!`); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ususally .storybook/config.js: | |
import { getStorybook } from '@storybook/react'; | |
// ...your usual config is here... | |
// create "sitemap" which can be queried by other tools | |
const myStories = []; | |
getStorybook().forEach(({ kind, stories }) => | |
stories.forEach(({ name }) => myStories.push({ kind, name })) | |
); | |
const data = document.createElement('script'); | |
data.type = 'application/json'; | |
data.id = 'sitemap'; | |
data.innerText = JSON.stringify(myStories); | |
document.head.append(data); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
BTW, I found a way to get all the examples without injecting any code into the storybook config ( https://github.com/happo/happo-plugin-storybook/blob/master/register.es6.js#L18-L41 ).
This and also an option to filter out some of your stories with storybook parameter, I placed in my fork: https://gist.github.com/jtomaszewski/c3e033c1672908b0b2de703a0a1fe340 . Feel free to use it.