-
Notifications
You must be signed in to change notification settings - Fork 9.4k
/
axe-audit.js
113 lines (100 loc) · 4.42 KB
/
axe-audit.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/**
* @license Copyright 2017 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
/**
* @fileoverview Base class for all aXe audits. Provides a consistent way to
* generate audit results using aXe rule names.
*/
import {Audit} from '../audit.js';
import * as i18n from '../../lib/i18n/i18n.js';
const UIStrings = {
/** Label of a table column that identifies HTML elements that have failed an audit. */
failingElementsHeader: 'Failing Elements',
};
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
class AxeAudit extends Audit {
/**
* Base class for audit rules which reflect assessment performed by the aXe accessibility library
* See https://github.com/dequelabs/axe-core/blob/6b444546cff492a62a70a74a8fc3c62bd4729400/doc/API.md#results-object for result type and format details
*
* @param {LH.Artifacts} artifacts Accessibility gatherer artifacts. Note that AxeAudit
* expects the meta name for the class to match the rule id from aXe.
* @return {LH.Audit.Product}
*/
static audit(artifacts) {
// Indicate if a test is not applicable.
// This means aXe did not find any nodes which matched these checks.
// Note in Lighthouse we use the phrasing "Not Applicable" (aXe uses "inapplicable", which sounds weird).
const notApplicables = artifacts.Accessibility.notApplicable || [];
const isNotApplicable = notApplicables.find(result => result.id === this.meta.id);
if (isNotApplicable) {
return {
score: null,
notApplicable: true,
};
}
// Detect errors reported within aXe 'incomplete' results
// aXe uses this result type to indicate errors, or rules which require manual investigation
// If aXe reports an error, then bubble it up to the caller
const incomplete = artifacts.Accessibility.incomplete || [];
const incompleteResult = incomplete.find(result => result.id === this.meta.id);
if (incompleteResult?.error) {
return {
score: null,
errorMessage: `axe-core Error: ${incompleteResult.error.message || 'Unknown error'}`,
};
}
const isInformative = this.meta.scoreDisplayMode === Audit.SCORING_MODES.INFORMATIVE;
const violations = artifacts.Accessibility.violations || [];
const failureCases = isInformative ? violations.concat(incomplete) : violations;
const rule = failureCases.find(result => result.id === this.meta.id);
const impact = rule?.impact;
const tags = rule?.tags;
// Handle absence of aXe failure results for informative rules as 'not applicable'
// This scenario indicates that no action is required by the web property owner
// Since there is no score impact from informative rules, display the rule as not applicable
if (isInformative && !rule) {
return {
score: null,
notApplicable: true,
};
}
/** @type {LH.Audit.Details.Table['items']}>} */
let items = [];
if (rule?.nodes) {
items = rule.nodes.map(axeNode => ({
node: {
...Audit.makeNodeItem(axeNode.node),
explanation: axeNode.failureSummary,
},
subItems: axeNode.relatedNodes.length ? {
type: 'subitems',
items: axeNode.relatedNodes.map(node => ({relatedNode: Audit.makeNodeItem(node)})),
} : undefined,
}));
}
/** @type {LH.Audit.Details.Table['headings']} */
const headings = [
/* eslint-disable max-len */
{key: 'node', itemType: 'node', subItemsHeading: {key: 'relatedNode', itemType: 'node'}, text: str_(UIStrings.failingElementsHeader)},
/* eslint-enable max-len */
];
/** @type {LH.Audit.Details.DebugData|undefined} */
let debugData;
if (impact || tags) {
debugData = {
type: 'debugdata',
impact,
tags,
};
}
return {
score: Number(rule === undefined),
details: {...Audit.makeTableDetails(headings, items), debugData},
};
}
}
export default AxeAudit;
export {UIStrings};