Skip to content

Commit bc346a3

Browse files
Initial merge
1 parent 7da6bec commit bc346a3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+766
-575
lines changed

build/cherry-pick.js

100644100755
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#!/usr/bin/env node
2+
13
const { execSync } = require('child_process');
24
const conventionalCommitsParser = require('conventional-commits-parser');
35
const chalk = require('chalk');
@@ -129,7 +131,7 @@ commitsToCherryPick.forEach(({ hash, type, scope, subject }) => {
129131

130132
try {
131133
execSync(`git cherry-pick ${hash} -X theirs`);
132-
} catch (e) {
134+
} catch {
133135
console.error(
134136
chalk.red.bold('\nAborting cherry-pick and reseting to master')
135137
);

lib/checks/aria/aria-allowed-attr-evaluate.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ export default function ariaAllowedAttrEvaluate(node, options, virtualNode) {
3939

4040
// Unknown ARIA attributes are tested in aria-valid-attr
4141
for (const attrName of virtualNode.attrNames) {
42-
if (validateAttr(attrName) && !allowed.includes(attrName)) {
42+
if (
43+
validateAttr(attrName) &&
44+
!allowed.includes(attrName) &&
45+
!ignoredAttrs(attrName, virtualNode.attr(attrName), virtualNode)
46+
) {
4347
invalid.push(attrName);
4448
}
4549
}
@@ -57,3 +61,23 @@ export default function ariaAllowedAttrEvaluate(node, options, virtualNode) {
5761
}
5862
return false;
5963
}
64+
65+
function ignoredAttrs(attrName, attrValue, vNode) {
66+
// allow aria-required=false as screen readers consistently ignore it
67+
// @see https://github.com/dequelabs/axe-core/issues/3756
68+
if (attrName === 'aria-required' && attrValue === 'false') {
69+
return true;
70+
}
71+
72+
// allow aria-multiline=false when contenteditable is set
73+
// @see https://github.com/dequelabs/axe-core/issues/4463
74+
if (
75+
attrName === 'aria-multiline' &&
76+
attrValue === 'false' &&
77+
vNode.hasAttr('contenteditable')
78+
) {
79+
return true;
80+
}
81+
82+
return false;
83+
}

lib/checks/aria/aria-errormessage.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
"hidden": "aria-errormessage value `${data.values}` cannot reference a hidden element"
1212
},
1313
"incomplete": {
14-
"singular": "ensure aria-errormessage value `${data.values}` references an existing element",
15-
"plural": "ensure aria-errormessage values `${data.values}` reference existing elements",
16-
"idrefs": "unable to determine if aria-errormessage element exists on the page: ${data.values}"
14+
"singular": "Ensure aria-errormessage value `${data.values}` references an existing element",
15+
"plural": "Ensure aria-errormessage values `${data.values}` reference existing elements",
16+
"idrefs": "Unable to determine if aria-errormessage element exists on the page: ${data.values}"
1717
}
1818
}
1919
}

lib/checks/label/explicit-evaluate.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function explicitEvaluate(node, options, virtualNode) {
3535
return !!explicitLabel;
3636
}
3737
});
38-
} catch (e) {
38+
} catch {
3939
return undefined;
4040
}
4141
}

lib/checks/label/explicit.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
"metadata": {
55
"impact": "critical",
66
"messages": {
7-
"pass": "Form element has an explicit <label>",
8-
"fail": "Form element does not have an explicit <label>",
7+
"pass": "Element has an explicit <label>",
8+
"fail": "Element does not have an explicit <label>",
99
"incomplete": "Unable to determine if form element has an explicit <label>"
1010
}
1111
}

lib/checks/label/help-same-as-label-evaluate.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { labelVirtual, accessibleText, sanitize } from '../../commons/text';
22
import { idrefs } from '../../commons/dom';
33

44
function helpSameAsLabelEvaluate(node, options, virtualNode) {
5-
var labelText = labelVirtual(virtualNode),
6-
check = node.getAttribute('title');
5+
const labelText = labelVirtual(virtualNode);
6+
let check = node.getAttribute('title');
77

88
if (!labelText) {
99
return false;
@@ -13,7 +13,7 @@ function helpSameAsLabelEvaluate(node, options, virtualNode) {
1313
check = '';
1414

1515
if (node.getAttribute('aria-describedby')) {
16-
var ref = idrefs(node, 'aria-describedby');
16+
const ref = idrefs(node, 'aria-describedby');
1717
check = ref
1818
.map(thing => {
1919
return thing ? accessibleText(thing) : '';

lib/checks/label/hidden-explicit-label-evaluate.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function hiddenExplicitLabelEvaluate(node, options, virtualNode) {
1616
let name;
1717
try {
1818
name = accessibleTextVirtual(virtualNode).trim();
19-
} catch (e) {
19+
} catch {
2020
return undefined;
2121
}
2222

lib/core/base/audit.js

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import {
99
preload,
1010
findBy,
1111
ruleShouldRun,
12-
performanceTimer
12+
performanceTimer,
13+
serializeError
1314
} from '../utils';
14-
import doT from '@deque/dot';
15+
import { doT } from '../imports';
1516
import constants from '../constants';
1617

1718
const dotRegex = /\{\{.+?\}\}/g;
@@ -183,15 +184,15 @@ export default class Audit {
183184
* Initializes the rules and checks
184185
*/
185186
_init() {
186-
var audit = getDefaultConfiguration(this.defaultConfig);
187+
const audit = getDefaultConfiguration(this.defaultConfig);
187188
this.lang = audit.lang || 'en';
188189
this.reporter = audit.reporter;
189190
this.commands = {};
190191
this.rules = [];
191192
this.checks = {};
192193
this.brand = 'axe';
193194
this.application = 'axeAPI';
194-
this.tagExclude = ['experimental'];
195+
this.tagExclude = ['experimental', 'deprecated'];
195196
this.noHtml = audit.noHtml;
196197
this.allowedOrigins = audit.allowedOrigins;
197198
unpackToObject(audit.rules, this, 'addRule');
@@ -366,16 +367,26 @@ export default class Audit {
366367
* @param {Mixed} options Options object to pass into rules and/or disable rules or checks
367368
*/
368369
after(results, options) {
369-
var rules = this.rules;
370+
const rules = this.rules;
370371
return results.map(ruleResult => {
371-
var rule = findBy(rules, 'id', ruleResult.id);
372+
if (ruleResult.error) {
373+
return ruleResult;
374+
}
375+
const rule = findBy(rules, 'id', ruleResult.id);
372376
if (!rule) {
373377
// If you see this, you're probably running the Mocha tests with the axe extension installed
374378
throw new Error(
375379
'Result for unknown rule. You may be running mismatch axe-core versions'
376380
);
377381
}
378-
return rule.after(ruleResult, options);
382+
try {
383+
return rule.after(ruleResult, options);
384+
} catch (err) {
385+
if (options.debug) {
386+
throw err;
387+
}
388+
return createIncompleteErrorResult(rule, err);
389+
}
379390
});
380391
}
381392
/**
@@ -393,7 +404,7 @@ export default class Audit {
393404
* @return {Object} Validated options object
394405
*/
395406
normalizeOptions(options) {
396-
var audit = this;
407+
const audit = this;
397408
const tags = [];
398409
const ruleIds = [];
399410
audit.rules.forEach(rule => {
@@ -498,7 +509,7 @@ export default class Audit {
498509
}
499510
_constructHelpUrls(previous = null) {
500511
// TODO: es-modules-version
501-
var version = (axe.version.match(/^[1-9][0-9]*\.[0-9]+/) || ['x.y'])[0];
512+
const version = (axe.version.match(/^[1-9][0-9]*\.[0-9]+/) || ['x.y'])[0];
502513
this.rules.forEach(rule => {
503514
if (!this.data.rules[rule.id]) {
504515
this.data.rules[rule.id] = {};
@@ -732,36 +743,37 @@ function getDefferedRule(rule, context, options) {
732743
rule.run(
733744
context,
734745
options,
735-
// resolve callback for rule `run`
736-
ruleResult => {
737-
// resolve
738-
resolve(ruleResult);
739-
},
740-
// reject callback for rule `run`
746+
ruleResult => resolve(ruleResult),
741747
err => {
742-
// if debug - construct error details
743-
if (!options.debug) {
744-
const errResult = Object.assign(new RuleResult(rule), {
745-
result: constants.CANTTELL,
746-
description: 'An error occured while running this rule',
747-
message: err.message,
748-
stack: err.stack,
749-
error: err,
750-
// Add a serialized reference to the node the rule failed on for easier debugging.
751-
// See https://github.com/dequelabs/axe-core/issues/1317.
752-
errorNode: err.errorNode
753-
});
754-
// resolve
755-
resolve(errResult);
756-
} else {
757-
// reject
748+
if (options.debug) {
758749
reject(err);
750+
} else {
751+
resolve(createIncompleteErrorResult(rule, err));
759752
}
760753
}
761754
);
762755
};
763756
}
764757

758+
function createIncompleteErrorResult(rule, error) {
759+
const { errorNode } = error;
760+
const serialError = serializeError(error);
761+
const none = [
762+
{
763+
id: 'error-occurred',
764+
result: undefined,
765+
data: serialError,
766+
relatedNodes: []
767+
}
768+
];
769+
const node = errorNode || new DqElement(document.documentElement);
770+
return Object.assign(new RuleResult(rule), {
771+
error: serialError,
772+
result: constants.CANTTELL,
773+
nodes: [{ any: [], all: [], none, node }]
774+
});
775+
}
776+
765777
/**
766778
* For all the rules, create the helpUrl and add it to the data for that rule
767779
*/

test/integration/rules/runner.js

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -110,34 +110,39 @@
110110
before(done => {
111111
fixture.innerHTML = testObj.content;
112112
waitForFrames(fixture, () => {
113-
axe.run(
114-
fixture,
115-
{
116-
/**
117-
* The debug flag helps log errors in a fairly detailed fashion,
118-
* when tests fail in webdriver
119-
*/
120-
debug: true,
121-
performanceTimer: false,
122-
runOnly: { type: 'rule', values: [ruleId] }
123-
},
124-
(err, r) => {
125-
// assert that there are no errors - if error exists a stack trace is logged.
126-
const errStack = err && err.stack ? err.stack : '';
127-
assert.isNull(err, 'Error should be null. ' + errStack);
128-
// assert that result is defined
129-
assert.isDefined(r, 'Results are defined.');
130-
// assert that result has certain keys
131-
assert.hasAnyKeys(r, ['incomplete', 'violations', 'passes']);
132-
// assert incomplete(s) does not have error
133-
r.incomplete.forEach(incomplete => {
134-
assert.isUndefined(incomplete.error);
135-
});
136-
// flatten results
137-
results = flattenResult(r);
138-
done();
139-
}
140-
);
113+
// The setTimeout is a workaround for a Firefox bug. See:
114+
// - https://github.com/dequelabs/axe-core/issues/4556
115+
// - https://bugzilla.mozilla.org/show_bug.cgi?id=1912115
116+
setTimeout(() => {
117+
axe.run(
118+
fixture,
119+
{
120+
/**
121+
* The debug flag helps log errors in a fairly detailed fashion,
122+
* when tests fail in webdriver
123+
*/
124+
debug: true,
125+
performanceTimer: false,
126+
runOnly: { type: 'rule', values: [ruleId] }
127+
},
128+
(err, r) => {
129+
// assert that there are no errors - if error exists a stack trace is logged.
130+
const errStack = err && err.stack ? err.stack : '';
131+
assert.isNull(err, 'Error should be null. ' + errStack);
132+
// assert that result is defined
133+
assert.isDefined(r, 'Results are defined.');
134+
// assert that result has certain keys
135+
assert.hasAnyKeys(r, ['incomplete', 'violations', 'passes']);
136+
// assert incomplete(s) does not have error
137+
r.incomplete.forEach(incomplete => {
138+
assert.isUndefined(incomplete.error);
139+
});
140+
// flatten results
141+
results = flattenResult(r);
142+
done();
143+
}
144+
);
145+
}, 0);
141146
});
142147
});
143148
runTest(testObj, 'passes');

test/rule-matches/aria-allowed-attr-matches.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
describe('aria-allowed-attr-matches', function () {
22
'use strict';
33

4-
var queryFixture = axe.testUtils.queryFixture;
5-
var rule;
4+
const queryFixture = axe.testUtils.queryFixture;
5+
let rule;
66

77
beforeEach(function () {
88
rule = axe.utils.getRule('aria-allowed-attr');
@@ -13,15 +13,15 @@ describe('aria-allowed-attr-matches', function () {
1313
});
1414

1515
it('should return true on elements that have aria attributes', function () {
16-
var vNode = queryFixture(
16+
const vNode = queryFixture(
1717
'<div role="button" id="target" aria-label="Thing 1" aria-mccheddarton="Unsupported thing 2"></div>'
1818
);
1919

2020
assert.isTrue(rule.matches(null, vNode));
2121
});
2222

2323
it('should return false on elements that have no aria attributes', function () {
24-
var vNode = queryFixture('<div role="button" id="target"></div>');
24+
const vNode = queryFixture('<div role="button" id="target"></div>');
2525

2626
assert.isFalse(rule.matches(null, vNode));
2727
});

0 commit comments

Comments
 (0)