Skip to content

Commit

Permalink
fix: check for hidden elements on aria-errormessage (#3156)
Browse files Browse the repository at this point in the history
* add visible checks and tests

* fix typo

* enable screenreader on isVisible

* add tests

* revert playground

* add aria-hidden tests

* add test and clean up

* fix test to pass if aria-invalid is false on hidden element
  • Loading branch information
Zidious authored Sep 24, 2021
1 parent 1ee88cb commit 95d37dd
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 4 deletions.
10 changes: 9 additions & 1 deletion lib/checks/aria/aria-errormessage-evaluate.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import standards from '../../standards';
import { idrefs } from '../../commons/dom';
import { tokenList } from '../../core/utils';

import { isVisible } from '../../commons/dom';
/**
* Check if `aria-errormessage` references an element that also uses a technique to announce the message (aria-live, aria-describedby, etc.).
*
Expand Down Expand Up @@ -55,13 +55,21 @@ function ariaErrormessageEvaluate(node, options, virtualNode) {
}

if (idref) {
if (!isVisible(idref, true)) {
this.data({
messageKey: 'hidden',
values: tokenList(attr)
});
return false;
}
return (
idref.getAttribute('role') === 'alert' ||
idref.getAttribute('aria-live') === 'assertive' ||
idref.getAttribute('aria-live') === 'polite' ||
tokenList(virtualNode.attr('aria-describedby')).indexOf(attr) > -1
);
}

return;
}

Expand Down
3 changes: 2 additions & 1 deletion lib/checks/aria/aria-errormessage.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"pass": "aria-errormessage exists and references elements visible to screen readers that use a supported aria-errormessage technique",
"fail": {
"singular": "aria-errormessage value `${data.values}` must use a technique to announce the message (e.g., aria-live, aria-describedby, role=alert, etc.)",
"plural": "aria-errormessage values `${data.values}` must use a technique to announce the message (e.g., aria-live, aria-describedby, role=alert, etc.)"
"plural": "aria-errormessage values `${data.values}` must use a technique to announce the message (e.g., aria-live, aria-describedby, role=alert, etc.)",
"hidden": "aria-errormessage value `${data.values}` cannot reference a hidden element"
},
"incomplete": {
"singular": "ensure aria-errormessage value `${data.values}` references an existing element",
Expand Down
88 changes: 88 additions & 0 deletions test/checks/aria/errormessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,94 @@ describe('aria-errormessage', function() {
);
});

it('should return false when hidden attribute is used', function() {
var vNode = queryFixture(
'<input type="text" id="target" aria-invalid="true" aria-errormessage="id-message-1">' +
'<div id="id-message-1" hidden>Error message 1</div>'
);
assert.isFalse(
axe.testUtils
.getCheckEvaluate('aria-errormessage')
.call(checkContext, null, null, vNode)
);
assert.deepEqual(checkContext._data, {
messageKey: 'hidden',
values: ['id-message-1']
});
});

it('should return false when display: "none" is used', function() {
var vNode = queryFixture(
'<input type="text" id="target" aria-invalid="true" aria-errormessage="id-message-1">' +
'<div id="id-message-1" style="display: none">Error message 1</div>'
);
assert.isFalse(
axe.testUtils
.getCheckEvaluate('aria-errormessage')
.call(checkContext, null, null, vNode)
);
assert.deepEqual(checkContext._data, {
messageKey: 'hidden',
values: ['id-message-1']
});
});

it('should return false when visibility: "hidden" is used', function() {
var vNode = queryFixture(
'<input type="text" id="target" aria-invalid="true" aria-errormessage="id-message-1">' +
'<div id="id-message-1" style="visibility: hidden">Error message 1</div>'
);
assert.isFalse(
axe.testUtils
.getCheckEvaluate('aria-errormessage')
.call(checkContext, null, null, vNode)
);
assert.deepEqual(checkContext._data, {
messageKey: 'hidden',
values: ['id-message-1']
});
});

it('should return false when aria-hidden=true is used', function() {
var vNode = queryFixture(
'<input type="text" id="target" aria-invalid="true" aria-errormessage="id-message-1">' +
'<div id="id-message-1" aria-hidden="true">Error message 1</div>'
);
assert.isFalse(
axe.testUtils
.getCheckEvaluate('aria-errormessage')
.call(checkContext, null, null, vNode)
);
assert.deepEqual(checkContext._data, {
messageKey: 'hidden',
values: ['id-message-1']
});
});

it('should return true when aria-hidden=false is used', function() {
var vNode = queryFixture(
'<input type="text" id="target" aria-invalid="true" aria-errormessage="id-message-1">' +
'<div id="id-message-1" aria-live="assertive" aria-hidden="false">Error message 1</div>'
);
assert.isTrue(
axe.testUtils
.getCheckEvaluate('aria-errormessage')
.call(checkContext, null, null, vNode)
);
});

it('should return true when no hidden functionality is used', function() {
var vNode = queryFixture(
'<input type="text" id="target" aria-invalid="true" aria-errormessage="id-message-1">' +
'<div id="id-message-1" aria-live="assertive">Error message 1</div>'
);
assert.isTrue(
axe.testUtils
.getCheckEvaluate('aria-errormessage')
.call(checkContext, null, null, vNode)
);
});

(shadowSupported ? it : xit)(
'should return undefined if aria-errormessage value crosses shadow boundary',
function() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,46 @@ <h2>Violations</h2>
<div aria-posinset="-22" id="violation42">hi</div>

<div aria-setsize="-22" id="violation43">hi</div>

<div>
<input
type="text"
id="violation44"
aria-invalid="true"
aria-errormessage="violation44-ref"
/>
<div id="violation44-ref" hidden>Error message 1</div>
</div>

<div>
<input
type="text"
id="violation45"
aria-invalid="true"
aria-errormessage="violation45-ref"
/>
<div id="violation45-ref" style="display: none">Error message 1</div>
</div>

<div>
<input
type="text"
id="violation46"
aria-invalid="true"
aria-errormessage="violation46-ref"
/>
<div id="violation46-ref" style="visibility: hidden">Error message 1</div>
</div>

<div>
<input
type="text"
id="violation47"
aria-invalid="true"
aria-errormessage="violation47-ref"
/>
<div id="violation47-ref" aria-hidden="true">Error message 1</div>
</div>
</div>
<h2>Possible False Positives</h2>
<div>
Expand Down Expand Up @@ -276,6 +316,35 @@ <h2>Possible False Positives</h2>
<div aria-errormessage="invalidId" id="pass185">hi</div>
<div aria-errormessage="invalidId" aria-invalid="false" id="pass186">hi</div>

<div>
<input
type="text"
id="pass187"
aria-invalid="true"
aria-errormessage="pass68"
/>
</div>

<div aria-live="assertive" aria-hidden="false" id="pass188">hi</div>

<div>
<input
type="text"
id="pass189"
aria-invalid="true"
aria-errormessage="pass188"
/>
</div>

<div>
<input
type="text"
id="pass190"
aria-invalid="false"
aria-errormessage="violation44-ref"
/>
</div>

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@
["#violation40"],
["#violation41"],
["#violation42"],
["#violation43"]
["#violation43"],
["#violation44"],
["#violation45"],
["#violation46"],
["#violation47"]
],
"passes": [
["#pass1"],
Expand Down Expand Up @@ -220,7 +224,11 @@
["#pass183"],
["#pass184"],
["#pass185"],
["#pass186"]
["#pass186"],
["#pass187"],
["#pass188"],
["#pass189"],
["#pass190"]
],
"incomplete": [
["#incomplete1"],
Expand Down

0 comments on commit 95d37dd

Please sign in to comment.