diff --git a/lighthouse-core/audits/accessibility/axe-audit.js b/lighthouse-core/audits/accessibility/axe-audit.js
index d053031b9201..2cac0ab1a7d1 100644
--- a/lighthouse-core/audits/accessibility/axe-audit.js
+++ b/lighthouse-core/audits/accessibility/axe-audit.js
@@ -37,7 +37,7 @@ class AxeAudit extends Audit {
const isNotApplicable = notApplicables.find(result => result.id === this.meta.id);
if (isNotApplicable) {
return {
- score: 1,
+ score: null,
notApplicable: true,
};
}
@@ -66,7 +66,7 @@ class AxeAudit extends Audit {
// Since there is no score impact from informative rules, display the rule as not applicable
if (isInformative && !rule) {
return {
- score: 1,
+ score: null,
notApplicable: true,
};
}
diff --git a/lighthouse-core/gather/gatherers/accessibility.js b/lighthouse-core/gather/gatherers/accessibility.js
index 9b1a7ceb239d..9996707bae02 100644
--- a/lighthouse-core/gather/gatherers/accessibility.js
+++ b/lighthouse-core/gather/gatherers/accessibility.js
@@ -20,7 +20,7 @@ const pageFunctions = require('../../lib/page-functions.js');
/* c8 ignore start */
async function runA11yChecks() {
/** @type {import('axe-core/axe')} */
- // @ts-expect-error axe defined by axeLibSource
+ // @ts-expect-error - axe defined by axeLibSource
const axe = window.axe;
const application = `lighthouse-${Math.random()}`;
axe.configure({
@@ -65,47 +65,52 @@ async function runA11yChecks() {
// are relative to the top of the page
document.documentElement.scrollTop = 0;
- /** @param {import('axe-core/axe').Result} result */
- const augmentAxeNodes = result => {
- result.helpUrl = result.helpUrl.replace(application, 'lighthouse');
- if (axeResults.inapplicable.includes(result)) return;
+ return {
+ violations: axeResults.violations.map(createAxeRuleResultArtifact),
+ incomplete: axeResults.incomplete.map(createAxeRuleResultArtifact),
+ notApplicable: axeResults.inapplicable.map(result => ({id: result.id})),
+ version: axeResults.testEngine.version,
+ };
+}
- result.nodes.forEach(node => {
- // @ts-expect-error - getNodeDetails put into scope via stringification
- node.node = getNodeDetails(node.element);
- // @ts-expect-error - avoid circular JSON concerns
- node.element = node.any = node.all = node.none = node.html = undefined;
- });
+/**
+ * @param {import('axe-core/axe').Result} result
+ * @return {LH.Artifacts.AxeRuleResult}
+ */
+function createAxeRuleResultArtifact(result) {
+ // Simplify `nodes` and collect nodeDetails for each.
+ const nodes = result.nodes.map(node => {
+ const {target, failureSummary, element} = node;
+ // TODO: with `elementRef: true`, `element` _should_ always be defined, but need to verify.
+ // @ts-expect-error - getNodeDetails put into scope via stringification
+ const nodeDetails = getNodeDetails(/** @type {HTMLElement} */ (element));
- // Ensure errors can be serialized over the protocol
- /** @type {(Error & {message: string, errorNode: any}) | undefined} */
- // @ts-expect-error - when rules error axe sets these properties
- // see https://github.com/dequelabs/axe-core/blob/eeff122c2de11dd690fbad0e50ba2fdb244b50e8/lib/core/base/audit.js#L684-L693
- const error = result.error;
- if (error instanceof Error) {
- // @ts-expect-error
- result.error = {
- name: error.name,
- message: error.message,
- stack: error.stack,
- errorNode: error.errorNode,
- };
- }
- };
+ return {
+ target,
+ failureSummary,
+ node: nodeDetails,
+ };
+ });
- // Augment the node objects with outerHTML snippet & custom path string
- axeResults.violations.forEach(augmentAxeNodes);
- axeResults.incomplete.forEach(augmentAxeNodes);
- axeResults.inapplicable.forEach(augmentAxeNodes);
+ // Ensure errors can be serialized over the protocol.
+ /** @type {Error | undefined} */
+ // @ts-expect-error - when rules throw an error, axe saves it here.
+ // see https://github.com/dequelabs/axe-core/blob/eeff122c2de11dd690fbad0e50ba2fdb244b50e8/lib/core/base/audit.js#L684-L693
+ const resultError = result.error;
+ let error;
+ if (resultError instanceof Error) {
+ error = {
+ name: resultError.name,
+ message: resultError.message,
+ };
+ }
- // We only need violations, and circular references are possible outside of violations
return {
- // @ts-expect-error value is augmented above.
- violations: axeResults.violations,
- notApplicable: axeResults.inapplicable,
- // @ts-expect-error value is augmented above.
- incomplete: axeResults.incomplete,
- version: axeResults.testEngine.version,
+ id: result.id,
+ impact: result.impact || undefined,
+ tags: result.tags,
+ nodes,
+ error,
};
}
/* c8 ignore stop */
@@ -129,15 +134,8 @@ class Accessibility extends FRGatherer {
deps: [
axeLibSource,
pageFunctions.getNodeDetailsString,
+ createAxeRuleResultArtifact,
],
- }).then(returnedValue => {
- if (!returnedValue) {
- throw new Error('No axe-core results returned');
- }
- if (!Array.isArray(returnedValue.violations)) {
- throw new Error('Unable to parse axe results' + returnedValue);
- }
- return returnedValue;
});
}
}
diff --git a/lighthouse-core/test/audits/accessibility/axe-audit-test.js b/lighthouse-core/test/audits/accessibility/axe-audit-test.js
index 7364d98662ee..35d68d3120ab 100644
--- a/lighthouse-core/test/audits/accessibility/axe-audit-test.js
+++ b/lighthouse-core/test/audits/accessibility/axe-audit-test.js
@@ -102,10 +102,9 @@ describe('Accessibility: axe-audit', () => {
}
const artifacts = {
Accessibility: {
- passes: [{
- id: 'fake-axe-pass',
- help: 'http://example.com/',
- }],
+ violations: [],
+ notApplicable: [],
+ incomplete: [],
},
};
@@ -134,15 +133,7 @@ describe('Accessibility: axe-audit', () => {
}],
help: 'http://example.com/',
}],
- // TODO: remove: axe-core v3.3.0 backwards-compatibility test
- violations: [{
- id: 'fake-axe-failure-case',
- nodes: [{
- html: '',
- node: {},
- }],
- help: 'http://example.com/',
- }],
+ violations: [],
},
};
diff --git a/lighthouse-core/test/gather/gatherers/accessibility-test.js b/lighthouse-core/test/gather/gatherers/accessibility-test.js
index 05edeebba86e..b7b1d2a282da 100644
--- a/lighthouse-core/test/gather/gatherers/accessibility-test.js
+++ b/lighthouse-core/test/gather/gatherers/accessibility-test.js
@@ -17,34 +17,6 @@ describe('Accessibility gatherer', () => {
accessibilityGather = new AccessibilityGather();
});
- it('fails if nothing is returned', () => {
- return accessibilityGather.afterPass({
- driver: {
- executionContext: {
- async evaluate() {},
- },
- },
- }).then(
- _ => assert.ok(false),
- _ => assert.ok(true));
- });
-
- it('fails if result has no violations array', () => {
- return accessibilityGather.afterPass({
- driver: {
- executionContext: {
- async evaluate() {
- return {
- url: 'https://example.com',
- };
- },
- },
- },
- }).then(
- _ => assert.ok(false),
- _ => assert.ok(true));
- });
-
it('propagates error retrieving the results', () => {
const error = 'There was an error.';
return accessibilityGather.afterPass({
diff --git a/lighthouse-core/test/results/artifacts/artifacts.json b/lighthouse-core/test/results/artifacts/artifacts.json
index 22ddc0adce4f..751cfd552381 100644
--- a/lighthouse-core/test/results/artifacts/artifacts.json
+++ b/lighthouse-core/test/results/artifacts/artifacts.json
@@ -1587,12 +1587,8 @@
"wcag311",
"ACT"
],
- "description": "Ensures every HTML document has a lang attribute",
- "help": " element must have a lang attribute",
- "helpUrl": "https://dequeuniversity.com/rules/axe/4.1/html-has-lang?application=lighthouse",
"nodes": [
{
- "impact": "serious",
"target": [
"html"
],
@@ -1626,12 +1622,8 @@
"section508.22.a",
"ACT"
],
- "description": "Ensures elements have alternate text or a role of none or presentation",
- "help": "Images must have alternate text",
- "helpUrl": "https://dequeuniversity.com/rules/axe/4.1/image-alt?application=lighthouse",
"nodes": [
{
- "impact": "critical",
"target": [
"img[src=\"lighthouse-480x318\\.jpg\\?iar1\"]"
],
@@ -1653,7 +1645,6 @@
}
},
{
- "impact": "critical",
"target": [
"img[src=\"lighthouse-480x318\\.jpg\\?iar2\"]"
],
@@ -1675,7 +1666,6 @@
}
},
{
- "impact": "critical",
"target": [
"img[src=\"lighthouse-480x318\\.jpg\\?isr1\"]"
],
@@ -1697,7 +1687,6 @@
}
},
{
- "impact": "critical",
"target": [
"img[src=\"lighthouse-480x318\\.jpg\\?isr2\"]"
],
@@ -1719,7 +1708,6 @@
}
},
{
- "impact": "critical",
"target": [
"img[src=\"lighthouse-480x318\\.jpg\\?isr3\"]"
],
@@ -1741,7 +1729,6 @@
}
},
{
- "impact": "critical",
"target": [
"img[src=\"lighthouse-480x318\\.jpg\\?isr4\"]"
],
@@ -1763,7 +1750,6 @@
}
},
{
- "impact": "critical",
"target": [
"img[src$=\"lighthouse-rotating\\.gif\"]"
],
@@ -1785,7 +1771,6 @@
}
},
{
- "impact": "critical",
"target": [
"img:nth-child(37)"
],
@@ -1807,7 +1792,6 @@
}
},
{
- "impact": "critical",
"target": [
"img:nth-child(43)"
],
@@ -1842,12 +1826,8 @@
"section508.22.n",
"ACT"
],
- "description": "Ensures every form element has a label",
- "help": "Form elements must have labels",
- "helpUrl": "https://dequeuniversity.com/rules/axe/4.1/label?application=lighthouse",
"nodes": [
{
- "impact": "critical",
"target": [
"input[onpaste=\"event\\.preventDefault\\(\\)\\;\"]"
],
@@ -1869,7 +1849,6 @@
}
},
{
- "impact": "critical",
"target": [
"input:nth-child(35)"
],
@@ -1891,7 +1870,6 @@
}
},
{
- "impact": "critical",
"target": [
"input[onpaste=\"return\\ false\\;\"]"
],
@@ -1926,12 +1904,8 @@
"section508.22.a",
"ACT"
],
- "description": "Ensures links have discernible text",
- "help": "Links must have discernible text",
- "helpUrl": "https://dequeuniversity.com/rules/axe/4.1/link-name?application=lighthouse",
"nodes": [
{
- "impact": "serious",
"target": [
"a[href=\"javascript\\:void\\(0\\)\"]"
],
@@ -1953,7 +1927,6 @@
}
},
{
- "impact": "serious",
"target": [
"a[href$=\"mailto\\:inbox\\@email\\.com\"]"
],
@@ -1986,12 +1959,8 @@
"section508",
"section508.22.a"
],
- "description": "Ensures