-
Notifications
You must be signed in to change notification settings - Fork 9.4k
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
tests: assert what axe checks matches our a11y audits #12699
Changes from 9 commits
ae02e61
4c28f7b
1e7146c
b5f2a30
8d82efa
10b1c49
ceefe90
b18145f
1adb599
5757e80
48f1686
477d0e0
bb7756d
da573e8
b895102
4d4f872
cf3cdd2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,13 +12,12 @@ const axeLibSource = require('../../lib/axe.js').source; | |
const pageFunctions = require('../../lib/page-functions.js'); | ||
|
||
/** | ||
* This is run in the page, not Lighthouse itself. | ||
* axe.run returns a promise which fulfills with a results object | ||
* containing any violations. | ||
* @return {Promise<LH.Artifacts.Accessibility>} | ||
* @param {Array<import('axe-core/axe').resultGroups>} resultTypes | ||
* @param {{colorContrastEnabled: boolean}} opts | ||
* @return {Promise<import('axe-core/axe').AxeResults>} | ||
*/ | ||
/* c8 ignore start */ | ||
async function runA11yChecks() { | ||
async function runAxe(resultTypes, opts = {colorContrastEnabled: true}) { | ||
/** @type {import('axe-core/axe')} */ | ||
// @ts-expect-error - axe defined by axeLibSource | ||
const axe = window.axe; | ||
|
@@ -38,8 +37,9 @@ async function runA11yChecks() { | |
'wcag2aa', | ||
], | ||
}, | ||
resultTypes: ['violations', 'inapplicable'], | ||
resultTypes, | ||
rules: { | ||
// Consider http://go/prcpg for expert review of the aXe rules. | ||
'tabindex': {enabled: true}, | ||
'accesskeys': {enabled: true}, | ||
'heading-order': {enabled: true}, | ||
|
@@ -60,9 +60,27 @@ async function runA11yChecks() { | |
// https://github.com/dequelabs/axe-core/issues/2958 | ||
'nested-interactive': {enabled: false}, | ||
'frame-focusable-content': {enabled: false}, | ||
'color-contrast': {enabled: opts.colorContrastEnabled}, // See gatherer's test for explanation | ||
'aria-roledescription': {enabled: false}, | ||
'scrollable-region-focusable': {enabled: false}, | ||
// TODO(paulirish): create audits and enable these 3. | ||
'input-button-name': {enabled: false}, | ||
'role-img-alt': {enabled: false}, | ||
'select-name': {enabled: false}, | ||
Comment on lines
+66
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. has this list changed since this pr? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. funnily enough, nope! |
||
}, | ||
}); | ||
|
||
|
||
return axeResults; | ||
} | ||
|
||
/** | ||
* Run aXe and adjust results for Lighthouse consumption | ||
* @return {Promise<LH.Artifacts.Accessibility>} | ||
*/ | ||
async function runA11yChecks() { | ||
const axeResults = await runAxe(['violations', 'inapplicable']); | ||
|
||
// axe just scrolled the page, scroll back to the top of the page so that element positions | ||
// are relative to the top of the page | ||
document.documentElement.scrollTop = 0; | ||
|
@@ -137,9 +155,17 @@ class Accessibility extends FRGatherer { | |
axeLibSource, | ||
pageFunctions.getNodeDetailsString, | ||
createAxeRuleResultArtifact, | ||
runAxe, | ||
], | ||
}); | ||
} | ||
|
||
// @ts-expect-error just sending 'em right through… | ||
static runAxeForTest(...args) { | ||
// @ts-expect-error | ||
return runAxe(...args); | ||
} | ||
} | ||
|
||
module.exports = Accessibility; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,3 +32,53 @@ describe('Accessibility gatherer', () => { | |
err => assert.ok(err.message.includes(error))); | ||
}); | ||
}); | ||
|
||
describe('axe-run', () => { | ||
/** | ||
* @jest-environment jsdom | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this for eslint? does it actually mutate jest's environment for the entire file? just this |
||
*/ | ||
const fs = require('fs'); | ||
const jsdom = require('jsdom'); | ||
const pageFunctions = require('../../../lib/page-functions.js'); | ||
|
||
const jdomObj = new jsdom.JSDOM(`<!doctype html><meta charset="utf8"><title>hi</title>valid.`); | ||
let axe; | ||
|
||
beforeAll(() =>{ | ||
// Needed by axe-core | ||
// https://github.com/dequelabs/axe-core/blob/581c441c/doc/examples/jsdom/test/a11y.js#L24 | ||
global.window = global.self = jdomObj.window; | ||
global.document = jdomObj.window.document; | ||
global.getNodeDetails = pageFunctions.getNodeDetails; | ||
|
||
// axe-core must be required after the global polyfills | ||
axe = require('axe-core'); // eslint-disable-line no-unused-vars | ||
}); | ||
|
||
afterAll(() => { | ||
global.window = undefined; | ||
global.document = undefined; | ||
global.getNodeDetails = undefined; | ||
}); | ||
|
||
it('tests only the checks we have audits defined for', async () => { | ||
// The color-contrast rule is manually disabled as jsdom doesn't support the APIs needed. https://github.com/dequelabs/axe-core/blob/581c441c/doc/examples/jsdom/test/a11y.js#L40 | ||
const axeResults = await AccessibilityGather.runAxeForTest(undefined, { | ||
colorContrastEnabled: false, | ||
}); | ||
|
||
const axeRuleIds = new Set(); | ||
for (const type of ['violations', 'incomplete', 'inapplicable', 'passes']) { | ||
if (axeResults[type]) axeResults[type].forEach(result => axeRuleIds.add(result.id)); | ||
} | ||
axeRuleIds.add('color-contrast'); | ||
const axeRuleIdsArr = Array.from(axeRuleIds).sort(); | ||
|
||
// Note: audit ids match their filenames, thx to the getAuditList test in runner-test.js | ||
const filenames = fs.readdirSync(__dirname + '/../../../audits/accessibility/') | ||
.map(f => f.replace('.js', '')).filter(f => f !== 'axe-audit' && f !== 'manual') | ||
.sort(); | ||
|
||
expect(axeRuleIdsArr).toEqual(filenames); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like
resultTypes
is a bit weird. We definitely haveincomplete
results in our artifacts and I was very confused just noticing for the first time that'incomplete'
is not in this array :)The docs say
so I think
resultTypes
can remain the same since we just need the idThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
over in #12536 (comment) we also talked about the value of having
passes
information in the artifact as well. Could start withpasses: axeResults.passes.map(result => ({id: result.id})),
likenotApplicable
currently does to get started on that for minimal artifact cost, apparently no additional axe time (sinceresultTypes
wasn't limiting that anyways), and it's forward compatible if we ever need to do the fullcreateAxeRuleResultArtifact
onpasses
since it'll still be extending{id: string}
.That would mean only
opts.colorContrastEnabled
would have to be custom between the two things (and maybe wouldn't need a separate method unless folks really want it).