Skip to content

Commit c96f58c

Browse files
AutoSpongemarcysutton
authored andcommitted
fix(aria-errormessage): adds support for aria-errormessage (#517)
* fix(aria-errormessage): adds support for aria-errormessage * fix(aria-valid-attr-value): switches from `any` mode to `all`
1 parent 6b81a50 commit c96f58c

File tree

9 files changed

+110
-10
lines changed

9 files changed

+110
-10
lines changed

lib/checks/aria/errormessage.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
options = Array.isArray(options) ? options : [];
2+
3+
var attr = node.getAttribute('aria-errormessage'),
4+
hasAttr = node.hasAttribute('aria-errormessage');
5+
6+
var doc = axe.commons.dom.getRootNode(node);
7+
8+
function validateAttrValue() {
9+
var idref = attr && doc.getElementById(attr);
10+
if (idref) {
11+
return idref.getAttribute('role') === 'alert' ||
12+
idref.getAttribute('aria-live') === 'assertive' ||
13+
axe.utils.tokenList(node.getAttribute('aria-describedby') || '').indexOf(attr) > -1;
14+
}
15+
}
16+
17+
// limit results to elements that actually have this attribute
18+
if (options.indexOf(attr) === -1 && hasAttr) {
19+
if (!validateAttrValue()) {
20+
this.data(attr);
21+
return false;
22+
}
23+
}
24+
25+
return true;

lib/checks/aria/errormessage.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"id": "aria-errormessage",
3+
"evaluate": "errormessage.js",
4+
"metadata": {
5+
"impact": "critical",
6+
"messages": {
7+
"pass": "Uses a supported aria-errormessage technique",
8+
"fail": "aria-errormessage value{{=it.data && it.data.length > 1 ? 's' : ''}} {{~it.data:value}} `{{=value}}{{~}}` must use a technique to announce the message (e.g., aria-live, aria-describedby, role=alert, etc.)"
9+
}
10+
}
11+
}

lib/checks/aria/valid-attr-value.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@ var invalid = [],
66
var attr, attrName,
77
attrs = node.attributes;
88

9+
var skipAttrs = ['aria-errormessage'];
10+
911
for (var i = 0, l = attrs.length; i < l; i++) {
1012
attr = attrs[i];
1113
attrName = attr.name;
12-
if (options.indexOf(attrName) === -1 && aria.test(attrName) &&
13-
!axe.commons.aria.validateAttrValue(node, attrName)) {
14+
// skip any attributes handled elsewhere
15+
if (!skipAttrs.includes(attrName)) {
16+
if (options.indexOf(attrName) === -1 && aria.test(attrName) &&
17+
!axe.commons.aria.validateAttrValue(node, attrName)) {
1418

15-
invalid.push(attrName + '="' + attr.nodeValue + '"');
19+
invalid.push(attrName + '="' + attr.nodeValue + '"');
20+
}
1621
}
1722
}
1823

lib/commons/aria/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ lookupTables.attributes = {
5454
type: 'nmtokens',
5555
values: ['copy', 'move', 'reference', 'execute', 'popup', 'none']
5656
},
57+
'aria-errormessage': {
58+
type: 'idref'
59+
},
5760
'aria-expanded': {
5861
type: 'nmtoken',
5962
values: ['true', 'false', 'undefined']

lib/rules/aria-valid-attr-value.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
"description": "Ensures all ARIA attributes have valid values",
1313
"help": "ARIA attributes must conform to valid values"
1414
},
15-
"all": [],
16-
"any": [
17-
"aria-valid-attr-value"
15+
"all": [
16+
"aria-valid-attr-value",
17+
"aria-errormessage"
1818
],
19+
"any": [],
1920
"none": []
2021
}

test/checks/aria/errormessage.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
describe('aria-errormessage', function () {
2+
'use strict';
3+
4+
var fixture = document.getElementById('fixture');
5+
6+
var checkContext = axe.testUtils.MockCheckContext();
7+
8+
afterEach(function () {
9+
fixture.innerHTML = '';
10+
checkContext._data = null;
11+
});
12+
13+
it('should return false if aria-errormessage value is invalid', function () {
14+
var testHTML = '<div></div>';
15+
testHTML += '<div id="plain"></div>';
16+
fixture.innerHTML = testHTML;
17+
var target = fixture.children[0];
18+
target.setAttribute('aria-errormessage', 'plain');
19+
assert.isFalse(checks['aria-errormessage'].evaluate.call(checkContext, target));
20+
});
21+
22+
it('should return true if aria-errormessage id is alert', function () {
23+
var testHTML = '<div></div>';
24+
testHTML += '<div id="alert" role="alert"></div>';
25+
fixture.innerHTML = testHTML;
26+
var target = fixture.children[0];
27+
target.setAttribute('aria-errormessage', 'alert');
28+
assert.isTrue(checks['aria-errormessage'].evaluate.call(checkContext, target));
29+
});
30+
31+
it('should return true if aria-errormessage id is aria-live=assertive', function () {
32+
var testHTML = '<div></div>';
33+
testHTML += '<div id="live" aria-live="assertive"></div>';
34+
fixture.innerHTML = testHTML;
35+
var target = fixture.children[0];
36+
target.setAttribute('aria-errormessage', 'live');
37+
assert.isTrue(checks['aria-errormessage'].evaluate.call(checkContext, target));
38+
});
39+
40+
it('should return true if aria-errormessage id is aria-describedby', function () {
41+
var testHTML = '<div></div>';
42+
testHTML += '<div id="plain"></div>';
43+
fixture.innerHTML = testHTML;
44+
var target = fixture.children[0];
45+
target.setAttribute('aria-errormessage', 'plain');
46+
target.setAttribute('aria-describedby', 'plain');
47+
assert.isTrue(checks['aria-errormessage'].evaluate.call(checkContext, target));
48+
});
49+
});

test/integration/rules/aria-allowed-attr/passes.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111
["#pass43"], ["#pass44"], ["#pass45"], ["#pass46"], ["#pass47"], ["#pass48"], ["#pass49"],
1212
["#pass50"], ["#pass51"], ["#pass52"], ["#pass53"], ["#pass54"], ["#pass55"], ["#pass56"],
1313
["#pass57"], ["#pass58"], ["#pass59"], ["#pass60"], ["#pass61"], ["#pass62"], ["#pass63"],
14-
["#pass64"], ["#pass65"], ["#pass66"], ["#pass67"], ["#pass68"]
14+
["#pass64"], ["#pass65"], ["#pass66"], ["#pass67"], ["#pass68"]
1515
]
16-
}
16+
}

test/integration/rules/aria-valid-attr-value/aria-valid-attr-value.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ <h2>Violations</h2>
4040
<div aria-valuemax="stuff" id="violation32">hi</div>
4141
<div aria-valuemin="stuff" id="violation33">hi</div>
4242
<div aria-valuenow="stuff" id="violation34">hi</div>
43+
<div aria-errormessage="violation35-ref" id="violation35">hi</div><div id="violation35-ref"></div>
4344
</div>
4445
<h2>Possible False Positives</h2>
4546
<div>
@@ -245,6 +246,10 @@ <h2>Possible False Positives</h2>
245246
<div aria-valuenow="-.1" id="pass165">hi</div>
246247
<div aria-valuenow="-412" id="pass166">hi</div>
247248
<div aria-valuetext="stuff" id="pass167">hi</div>
249+
<div aria-errormessage="pass168-ref" id="pass168">hi</div><div id="pass168-ref" role="alert"></div>
250+
<div aria-errormessage="pass68" id="pass169">hi</div>
251+
<div aria-errormessage="ref" aria-describedby="ref" id="pass170">hi</div>
252+
248253

249254
<div id="ref">Hi</div>
250255
<div id="ref2">Hi2</div>

test/integration/rules/aria-valid-attr-value/aria-valid-attr-value.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
["#violation12"], ["#violation13"], ["#violation14"], ["#violation15"], ["#violation16"], ["#violation17"],
88
["#violation18"], ["#violation19"], ["#violation20"], ["#violation21"], ["#violation22"], ["#violation23"],
99
["#violation24"], ["#violation25"], ["#violation26"], ["#violation27"], ["#violation28"], ["#violation29"],
10-
["#violation30"], ["#violation31"], ["#violation32"], ["#violation33"], ["#violation34"]
10+
["#violation30"], ["#violation31"], ["#violation32"], ["#violation33"], ["#violation34"],["#violation35"]
1111
],
1212
"passes": [
1313
["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"], ["#pass5"], ["#pass6"], ["#pass7"], ["#pass8"], ["#pass9"],
@@ -31,6 +31,7 @@
3131
["#pass140"], ["#pass141"], ["#pass142"], ["#pass143"], ["#pass144"], ["#pass145"], ["#pass146"],
3232
["#pass147"], ["#pass148"], ["#pass149"], ["#pass150"], ["#pass151"], ["#pass152"], ["#pass153"],
3333
["#pass154"], ["#pass155"], ["#pass156"], ["#pass157"], ["#pass158"], ["#pass159"], ["#pass160"],
34-
["#pass161"], ["#pass162"], ["#pass163"], ["#pass164"], ["#pass165"], ["#pass166"], ["#pass167"]
34+
["#pass161"], ["#pass162"], ["#pass163"], ["#pass164"], ["#pass165"], ["#pass166"], ["#pass167"],
35+
["#pass168"], ["#pass169"], ["#pass170"]
3536
]
3637
}

0 commit comments

Comments
 (0)