From c92fa77c1de43832a3a7db63f1c1ba746e4f7337 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Mon, 2 Nov 2020 11:56:03 +0100 Subject: [PATCH] feat(new-rule): check that treeitem role has an accessible name --- doc/rule-descriptions.md | 1 + lib/rules/aria-treeitem-name.json | 18 ++++ .../aria-treeitem-name.html | 32 +++++++ .../aria-treeitem-name.json | 6 ++ .../virtual-rules/aria-treeitem-name.js | 89 +++++++++++++++++++ test/integration/virtual-rules/index.html | 1 + 6 files changed, 147 insertions(+) create mode 100644 lib/rules/aria-treeitem-name.json create mode 100644 test/integration/rules/aria-treeitem-name/aria-treeitem-name.html create mode 100644 test/integration/rules/aria-treeitem-name/aria-treeitem-name.json create mode 100644 test/integration/virtual-rules/aria-treeitem-name.js diff --git a/doc/rule-descriptions.md b/doc/rule-descriptions.md index 29786f47cc..47076f03cb 100644 --- a/doc/rule-descriptions.md +++ b/doc/rule-descriptions.md @@ -79,6 +79,7 @@ Rules that do not necessarily conform to WCAG success criterion but are industry | :----------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------- | :----------------- | :---------------------------------------------- | :------------------------- | | [accesskeys](https://dequeuniversity.com/rules/axe/4.0/accesskeys?application=RuleDescription) | Ensures every accesskey attribute value is unique | Serious | cat.keyboard, best-practice | failure | | [aria-allowed-role](https://dequeuniversity.com/rules/axe/4.0/aria-allowed-role?application=RuleDescription) | Ensures role attribute has an appropriate value for the element | Minor | cat.aria, best-practice | failure, needs review | +| [aria-treeitem-name](https://dequeuniversity.com/rules/axe/4.0/aria-treeitem-name?application=RuleDescription) | Ensures every ARIA treeitem node has an accessible name | Serious | cat.aria, best-practice | failure, needs review | | [empty-heading](https://dequeuniversity.com/rules/axe/4.0/empty-heading?application=RuleDescription) | Ensures headings have discernible text | Minor | cat.name-role-value, best-practice | failure, needs review | | [frame-tested](https://dequeuniversity.com/rules/axe/4.0/frame-tested?application=RuleDescription) | Ensures <iframe> and <frame> elements contain the axe-core script | Critical | cat.structure, review-item, best-practice | failure, needs review | | [frame-title-unique](https://dequeuniversity.com/rules/axe/4.0/frame-title-unique?application=RuleDescription) | Ensures <iframe> and <frame> elements contain a unique title attribute | Serious | cat.text-alternatives, best-practice | failure | diff --git a/lib/rules/aria-treeitem-name.json b/lib/rules/aria-treeitem-name.json new file mode 100644 index 0000000000..5d0c6001c9 --- /dev/null +++ b/lib/rules/aria-treeitem-name.json @@ -0,0 +1,18 @@ +{ + "id": "aria-treeitem-name", + "selector": "[role=\"treeitem\"]", + "matches": "no-naming-method-matches", + "tags": ["cat.aria", "best-practice"], + "metadata": { + "description": "Ensures every ARIA treeitem node has an accessible name", + "help": "ARIA treeitem nodes must have an accessible name" + }, + "all": [], + "any": [ + "has-visible-text", + "aria-labelledby", + "aria-label", + "non-empty-title" + ], + "none": [] +} diff --git a/test/integration/rules/aria-treeitem-name/aria-treeitem-name.html b/test/integration/rules/aria-treeitem-name/aria-treeitem-name.html new file mode 100644 index 0000000000..3c16898b88 --- /dev/null +++ b/test/integration/rules/aria-treeitem-name/aria-treeitem-name.html @@ -0,0 +1,32 @@ + +
+
Item
+
+
+
+
+ +
Item
+ + +
+
+
+
+
+ +
+ + +
+ Label + + + + + +
diff --git a/test/integration/rules/aria-treeitem-name/aria-treeitem-name.json b/test/integration/rules/aria-treeitem-name/aria-treeitem-name.json new file mode 100644 index 0000000000..d81556d51c --- /dev/null +++ b/test/integration/rules/aria-treeitem-name/aria-treeitem-name.json @@ -0,0 +1,6 @@ +{ + "description": "aria-treeitem-name test", + "rule": "aria-treeitem-name", + "passes": [["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"]], + "violations": [["#fail1"], ["#fail2"], ["#fail3"]] +} diff --git a/test/integration/virtual-rules/aria-treeitem-name.js b/test/integration/virtual-rules/aria-treeitem-name.js new file mode 100644 index 0000000000..e5fee8be81 --- /dev/null +++ b/test/integration/virtual-rules/aria-treeitem-name.js @@ -0,0 +1,89 @@ +describe('aria-treeitem-name', function() { + it('should pass for aria-label', function() { + var node = new axe.SerialVirtualNode({ + nodeName: 'div', + attributes: { + role: 'treeitem', + 'aria-label': 'foobar' + } + }); + node.parent = null; + + var results = axe.runVirtualRule('aria-treeitem-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: 'treeitem', + 'aria-labelledby': 'foobar' + } + }); + node.parent = null; + + var results = axe.runVirtualRule('aria-treeitem-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: 'treeitem', + 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-treeitem-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: 'treeitem', + 'aria-label': ' \t \n ' + } + }); + node.children = []; + + var results = axe.runVirtualRule('aria-treeitem-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: 'treeitem', + title: '' + } + }); + node.children = []; + + var results = axe.runVirtualRule('aria-treeitem-name', node); + + assert.lengthOf(results.passes, 0); + assert.lengthOf(results.violations, 1); + assert.lengthOf(results.incomplete, 0); + }); +}); diff --git a/test/integration/virtual-rules/index.html b/test/integration/virtual-rules/index.html index 4fe25f4bd5..88993e169a 100644 --- a/test/integration/virtual-rules/index.html +++ b/test/integration/virtual-rules/index.html @@ -27,6 +27,7 @@ +