Skip to content

Commit

Permalink
feat(new-rule): ARIA links, buttons, menuitems have an accessible name (
Browse files Browse the repository at this point in the history
#2571)

* feat(new-rule): menuitems have an accessible name

* feat(new-rule): ARIA buttons, links, menuitems have a name
  • Loading branch information
WilcoFiers authored Oct 23, 2020
1 parent c627d54 commit 9476a1f
Show file tree
Hide file tree
Showing 16 changed files with 224 additions and 108 deletions.
4 changes: 3 additions & 1 deletion build/tasks/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ function createSchemas() {
items: {
type: 'string'
},
conform: function hasCategoryTag(tags) { return tags.some(tag => tag.includes('cat.')) },
conform: function hasCategoryTag(tags) {
return tags.some(tag => tag.includes('cat.'));
},
messages: {
conform: 'must include a category tag'
}
Expand Down
1 change: 1 addition & 0 deletions doc/rule-descriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
| :------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------ | :---------------- | :------------------------------------------------------------------------------------ | :------------------------- |
| [area-alt](https://dequeuniversity.com/rules/axe/4.0/area-alt?application=RuleDescription) | Ensures <area> elements of image maps have alternate text | Critical | cat.text-alternatives, wcag2a, wcag111, wcag244, wcag412, section508, section508.22.a | failure, needs review |
| [aria-allowed-attr](https://dequeuniversity.com/rules/axe/4.0/aria-allowed-attr?application=RuleDescription) | Ensures ARIA attributes are allowed for an element's role | Critical | cat.aria, wcag2a, wcag412 | failure |
| [aria-command-name](https://dequeuniversity.com/rules/axe/4.0/aria-command-name?application=RuleDescription) | Ensures every ARIA button, link and menuitem has an accessible name | Serious | wcag2a, wcag412 | failure, needs review |
| [aria-hidden-body](https://dequeuniversity.com/rules/axe/4.0/aria-hidden-body?application=RuleDescription) | Ensures aria-hidden='true' is not present on the document body. | Critical | cat.aria, wcag2a, wcag412 | failure |
| [aria-hidden-focus](https://dequeuniversity.com/rules/axe/4.0/aria-hidden-focus?application=RuleDescription) | Ensures aria-hidden elements do not contain focusable elements | Serious | cat.name-role-value, wcag2a, wcag412, wcag131 | failure, needs review |
| [aria-input-field-name](https://dequeuniversity.com/rules/axe/4.0/aria-input-field-name?application=RuleDescription) | Ensures every ARIA input field has an accessible name | Moderate, Serious | cat.aria, wcag2a, wcag412 | failure, needs review |
Expand Down
18 changes: 18 additions & 0 deletions lib/rules/aria-command-name.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"id": "aria-command-name",
"selector": "[role=\"link\"], [role=\"button\"], [role=\"menuitem\"]",
"matches": "no-naming-method-matches",
"tags": ["wcag2a", "wcag412"],
"metadata": {
"description": "Ensures every ARIA button, link and menuitem has an accessible name",
"help": "ARIA commands must have an accessible name"
},
"all": [],
"any": [
"aria-label",
"aria-labelledby",
"non-empty-title",
"has-visible-text"
],
"none": []
}
2 changes: 1 addition & 1 deletion lib/rules/aria-tooltip-name.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"tags": ["cat.aria", "wcag2a", "wcag412"],
"metadata": {
"description": "Ensures every ARIA tooltip node has an accessible name",
"help": "ARIA tooltip ndoes must have an accessible name"
"help": "ARIA tooltip nodes must have an accessible name"
},
"all": [],
"any": [
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/button-name.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "button-name",
"selector": "button, [role=\"button\"]:not(input)",
"selector": "button",
"tags": [
"cat.name-role-value",
"wcag2a",
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/link-name.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "link-name",
"selector": "a[href]:not([role=button]), [role=link]",
"selector": "a[href]",
"tags": [
"cat.name-role-value",
"wcag2a",
Expand Down
53 changes: 53 additions & 0 deletions test/integration/rules/aria-command-name/aria-command-name.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!-- PASS -->
<div id="pass1" role="link">Home</div>
<div id="pass2" role="link" title="About"></div>
<div id="pass3" role="link" aria-label="Products"></div>
<div id="pass4" role="link" aria-labelledby="contact"></div>
<div id="contact">Contact us</div>

<div id="pass5" role="button">Submit</div>
<div id="pass6" role="button" title="Upload"></div>
<div id="pass7" role="button" aria-label="Download"></div>
<div id="pass8" role="button" aria-labelledby="print"></div>

<div role="menu">
<div id="pass9" role="menuitem">New</div>
<div id="pass10" role="menuitem" title="Open"></div>
<div id="pass11" role="menuitem" aria-label="Save"></div>
<div id="pass12" role="menuitem" aria-labelledby="print"></div>
</div>

<div id="print">print file</div>

<!-- FAIL -->
<div id="fail1" role="link"></div>
<div id="fail2" role="link" aria-labelledby="non-existant"></div>
<div id="fail3" role="link" aria-labelledby="div-empty"></div>

<div id="fail4" role="button"></div>
<div id="fail5" role="button" aria-labelledby="non-existant"></div>
<div id="fail6" role="button" aria-labelledby="div-empty"></div>

<div role="menu">
<div id="fail7" role="menuitem"></div>
<div id="fail8" role="menuitem" aria-labelledby="non-existant"></div>
<div id="fail9" role="menuitem" aria-labelledby="div-empty"></div>
</div>

<div id="div-empty"></div>

<!-- INAPPLICABLE -->
<a href="/" role="link">Home</a>
<button role="button">Save</button>
<img role="button" alt="Send" id="inapplicable1" />
<div role="menu">
<input role="menuitem" title="Label" id="inapplicable2" />
<button role="menuitem" title="Label" id="inapplicable3"></button>
<a href="#" role="menuitem" title="Label" id="inapplicable4"></a>
<select role="menuitem" title="Label" id="inapplicable5">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="opel">Opel</option>
</select>
<textarea role="menuitem" id="inapplicable6" title="Label"></textarea>
</div>
29 changes: 29 additions & 0 deletions test/integration/rules/aria-command-name/aria-command-name.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"description": "aria-command-name test",
"rule": "aria-command-name",
"passes": [
["#pass1"],
["#pass2"],
["#pass3"],
["#pass4"],
["#pass5"],
["#pass6"],
["#pass7"],
["#pass8"],
["#pass9"],
["#pass10"],
["#pass11"],
["#pass12"]
],
"violations": [
["#fail1"],
["#fail2"],
["#fail3"],
["#fail4"],
["#fail5"],
["#fail6"],
["#fail7"],
["#fail8"],
["#fail9"]
]
}
5 changes: 3 additions & 2 deletions test/integration/rules/button-name/button-name.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
<button id="buttonTitle" title="Title"></button>
<button id="buttonvalue" value="foo" tabindex="-1"></button>

<input type="submit" value="submit" role="button" id="ignored" />

<button id="fail1" role="presentation"></button>
<button id="fail2" role="none"></button>

<button id="pass1" role="presentation" disabled></button>
<button id="pass2" role="none" disabled></button>

<span id="inapplicable1" role="button">Does not apply</span>
<input type="submit" value="submit" role="button" id="inapplicable2" />
51 changes: 11 additions & 40 deletions test/integration/rules/link-name/link-name.html
Original file line number Diff line number Diff line change
@@ -1,45 +1,16 @@
<a href="#" id="violation1"></a>
<a href="#" id="pass1">This link has text</a>
<a href="#" id="pass3" aria-label="link text"></a>
<a href="#" id="pass4" aria-labelledby="linklabel"></a>
<a href="#" id="violation2" aria-labelledby="nonexistent"></a>
<a href="#" id="violation3" aria-labelledby="badlabel"></a>
<a href="#" id="pass2" aria-label="link text"></a>
<a href="#" id="pass3" aria-labelledby="linklabel"></a>
<a href="#" id="pass4" title="title text"></a>

<div id="linklabel">Text</div>
<div id="badlabel"></div>
<span role="link" href="#" tabindex="0" id="violation4"></span>
<span
role="link"
href="#"
tabindex="0"
id="pass6"
aria-labelledby="linklabel"
></span>
<span
role="link"
href="#"
tabindex="0"
id="pass7"
aria-label="Link text"
></span>
<span
role="link"
href="#"
tabindex="0"
id="violation5"
aria-labelledby="badlabel"
></span>
<span
role="link"
href="#"
tabindex="0"
id="violation6"
aria-labelledby="nonexistent"
></span>

<a href="#" id="pass8" title="title text"></a>
<span role="link" href="#" tabindex="0" id="pass9" title="title text"></span>
<a href="#" id="violation1"></a>
<a href="#" id="violation2" aria-labelledby="nonexistent"></a>
<a href="#" id="violation3" aria-labelledby="empty-label"></a>
<a href="#" id="violation4" role="none"></a>
<a href="#" id="violation5" role="presentation"></a>

<a href="#" role="button">Does not apply</a>
<div id="empty-label"></div>

<a href="#" id="violation7" role="none"></a>
<a href="#" id="violation8" role="presentation"></a>
<span id="inapplicable1" role="link">Does not apply</span>
15 changes: 2 additions & 13 deletions test/integration/rules/link-name/link-name.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,7 @@
["#violation2"],
["#violation3"],
["#violation4"],
["#violation5"],
["#violation6"],
["#violation7"],
["#violation8"]
["#violation5"]
],
"passes": [
["#pass1"],
["#pass3"],
["#pass4"],
["#pass6"],
["#pass7"],
["#pass8"],
["#pass9"]
]
"passes": [["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"]]
}
13 changes: 0 additions & 13 deletions test/integration/rules/role-button-name/role-button-name.html

This file was deleted.

6 changes: 0 additions & 6 deletions test/integration/rules/role-button-name/role-button-name.json

This file was deleted.

89 changes: 89 additions & 0 deletions test/integration/virtual-rules/aria-command-name.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
describe('aria-command-name', function() {
it('should pass for aria-label', function() {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'link',
'aria-label': 'foobar'
}
});
node.parent = null;

var results = axe.runVirtualRule('aria-command-name', node);

assert.lengthOf(results.passes, 1);
assert.lengthOf(results.violations, 0);
assert.lengthOf(results.incomplete, 0);
});

it('should incomplete for aria-labelledby', function() {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'button',
'aria-labelledby': 'foobar'
}
});
node.parent = null;

var results = axe.runVirtualRule('aria-command-name', node);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 0);
assert.lengthOf(results.incomplete, 1);
});

it('should pass for title', function() {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'menuitem',
title: 'foobar'
}
});
// children are required since titleText comes after subtree text
// in accessible name calculation
node.children = [];
node.parent = null;

var results = axe.runVirtualRule('aria-command-name', node);

assert.lengthOf(results.passes, 1);
assert.lengthOf(results.violations, 0);
assert.lengthOf(results.incomplete, 0);
});

it('should fail when aria-label contains only whitespace', function() {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'link',
'aria-label': ' \t \n '
}
});
node.children = [];

var results = axe.runVirtualRule('aria-command-name', node);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.incomplete, 0);
});

it('should fail when title is empty', function() {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'button',
title: ''
}
});
node.children = [];

var results = axe.runVirtualRule('aria-command-name', node);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.incomplete, 0);
});
});
21 changes: 12 additions & 9 deletions test/integration/virtual-rules/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,24 @@
</head>
<body>
<div id="mocha"></div>
<script src="area-alt.js"></script>
<script src="aria-command-name.js"></script>
<script src="aria-input-field-name.js"></script>
<script src="aria-progressbar-name.js"></script>
<script src="aria-toggle-field-name.js"></script>
<script src="aria-tooltip-name.js"></script>
<script src="autocomplete-valid.js"></script>
<script src="image-alt.js"></script>
<script src="button-name.js"></script>
<script src="frame-title.js"></script>
<script src="image-alt.js"></script>
<script src="input-button-name.js"></script>
<script src="input-image-alt.js"></script>
<script src="object-alt.js"></script>
<script src="label.js"></script>
<script src="button-name.js"></script>
<script src="input-button-name.js"></script>
<script src="aria-toggle-field-name.js"></script>
<script src="aria-input-field-name.js"></script>
<script src="area-alt.js"></script>
<script src="svg-img-alt.js"></script>
<script src="role-img-alt.js"></script>
<script src="link-name.js"></script>
<script src="object-alt.js"></script>
<script src="role-img-alt.js"></script>
<script src="select-name.js"></script>
<script src="svg-img-alt.js"></script>
<script src="/test/integration/adapter.js"></script>
</body>
</html>
Loading

0 comments on commit 9476a1f

Please sign in to comment.