Skip to content

Commit

Permalink
feat(rules): add new rule aria-dpub-role-fallback
Browse files Browse the repository at this point in the history
Ensures that DPUB ARIA roles that are unsupported and may have a negative
impact are only used on elements which have a compatible implicit ARIA
role fallback.
  • Loading branch information
rdeltour committed Mar 5, 2018
1 parent 70b48f6 commit 9470c02
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 0 deletions.
1 change: 1 addition & 0 deletions doc/rule-descriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
| accesskeys | Ensures every accesskey attribute value is unique | wcag2a, wcag211, cat.keyboard | true |
| area-alt | Ensures <area> elements of image maps have alternate text | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | true |
| aria-allowed-attr | Ensures ARIA attributes are allowed for an element's role | cat.aria, wcag2a, wcag411, wcag412 | true |
| aria-dpub-role-fallback | Ensures unsupported DPUB roles are only used on elements with implicit fallback roles | cat.aria, wcag2a, wcag131 | true |
| aria-hidden-body | Ensures aria-hidden='true' is not present on the document body. | cat.aria, wcag2a, wcag412 | true |
| aria-required-attr | Ensures elements with ARIA roles have all required ARIA attributes | cat.aria, wcag2a, wcag411, wcag412 | true |
| aria-required-children | Ensures elements with an ARIA role that require child roles contain them | cat.aria, wcag2a, wcag131 | true |
Expand Down
6 changes: 6 additions & 0 deletions lib/checks/aria/implicit-role-fallback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
var role = node.getAttribute('role');
if (role === null || !axe.commons.aria.isValidRole(role)) {
return true;
}
var roleType = axe.commons.aria.getRoleType(role);
return axe.commons.aria.implicitRole(node) === roleType;
11 changes: 11 additions & 0 deletions lib/checks/aria/implicit-role-fallback.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"id": "implicit-role-fallback",
"evaluate": "implicit-role-fallback.js",
"metadata": {
"impact": "moderate",
"messages": {
"pass": "Element’s implicit ARIA role is an appropriate fallback",
"fail": "Element’s implicit ARIA role is not a good fallback for the (unsupported) role"
}
}
}
10 changes: 10 additions & 0 deletions lib/rules/aria-dpub-role-fallback-matches.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var role = node.getAttribute('role');
return [
'doc-backlink',
'doc-biblioentry',
'doc-biblioref',
'doc-cover',
'doc-endnote',
'doc-glossref',
'doc-noteref'
].includes(role);
19 changes: 19 additions & 0 deletions lib/rules/aria-dpub-role-fallback.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"id": "aria-dpub-role-fallback",
"selector": "[role]",
"matches": "aria-dpub-role-fallback-matches.js",
"tags": [
"cat.aria",
"wcag2a",
"wcag131"
],
"metadata": {
"description": "Ensures unsupported DPUB roles are only used on elements with implicit fallback roles",
"help": "Unsupported DPUB ARIA roles should be used on elements with implicit fallback roles"
},
"all": [
"implicit-role-fallback"
],
"any": [],
"none": []
}
76 changes: 76 additions & 0 deletions test/checks/aria/implicit-role-fallback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
describe('implicit-role-fallback', function () {
'use strict';

var fixture = document.getElementById('fixture');
var node;
var checkContext = axe.testUtils.MockCheckContext();


afterEach(function () {
node.innerHTML = '';
checkContext._data = null;
});

it('should return true for elements with no role', function() {
node = document.createElement('div');
fixture.appendChild(node);
assert.isTrue(checks['implicit-role-fallback'].evaluate.call(checkContext, node));
});

it('should return true for elements with nonsensical roles', function() {
fixture.innerHTML = '<a role="awesomelink" id="target" href="#">text</a>';
var target = fixture.children[0];
assert.isTrue(checks['implicit-role-fallback'].evaluate.call(checkContext, target));
});

it('should return true if the provided role’s parent is the element’s implicit role', function () {

axe.commons.aria.lookupTable.role.awesomelink = {
type: 'link',
attributes: {
allowed: ['aria-expanded']
},
owned: null,
namefrom: ['author', 'contents'],
context: null,
};
fixture.innerHTML = '<a role="awesomelink" id="target" href="#">text</a>';
var target = fixture.children[0];
assert.isTrue(checks['implicit-role-fallback'].evaluate.call(checkContext, target));
delete axe.commons.aria.lookupTable.role.awesomelink;
});

it('should return false if the provided role’s type is not the element’s implicit role', function () {

axe.commons.aria.lookupTable.role.awesomelink = {
type: 'link',
attributes: {
allowed: ['aria-expanded']
},
owned: null,
namefrom: ['author', 'contents'],
context: null,
};
fixture.innerHTML = '<article role="awesomelink" id="target"></article>';
var target = fixture.children[0];
assert.isFalse(checks['implicit-role-fallback'].evaluate.call(checkContext, target));
delete axe.commons.aria.lookupTable.role.awesomelink;
});

it('should return false if the element has no implicit role', function () {

axe.commons.aria.lookupTable.role.awesomelink = {
type: 'link',
attributes: {
allowed: ['aria-expanded']
},
owned: null,
namefrom: ['author', 'contents'],
context: null,
};
fixture.innerHTML = '<div role="awesomelink" id="target"></div>';
var target = fixture.children[0];
assert.isFalse(checks['implicit-role-fallback'].evaluate.call(checkContext, target));
delete axe.commons.aria.lookupTable.role.awesomelink;
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

<div id="ok">
<!-- links -->
<a id="pass1" role="doc-backlink" href="#">ok</a>
<a id="pass2" role="doc-biblioref" href="#">ok</a>
<a id="pass3" role="doc-glossref" href="#">ok</a>
<a id="pass4" role="doc-noteref" href="#">ok</a>
<!-- images -->
<img id="pass5" role="doc-cover"/>
<!-- listitems -->
<ul><li id="pass6" role="doc-biblioentry">ok</li></ul>
<ul><li id="pass7" role="doc-endnote">ok</li></ul>
</div>
<div id="violation">
<!-- links -->
<div id="fail1" role="doc-backlink">ok</div>
<div id="fail2" role="doc-biblioref">ok</div>
<div id="fail3" role="doc-glossref">ok</div>
<div id="fail4" role="doc-noteref">ok</div>
<!-- images -->
<div id="fail5" role="doc-cover">ok</div>
<!-- listitems -->
<div id="fail6" role="doc-biblioentry">ok</div>
<div id="fail7" role="doc-endnote">ok</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"description": "aria-dpub-role-fallback tests",
"rule": "aria-dpub-role-fallback",
"violations": [
["#fail1"], ["#fail2"], ["#fail3"], ["#fail4"], ["#fail5"], ["#fail6"], ["#fail7"]
],
"passes": [
["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"], ["#pass5"], ["#pass6"], ["#pass7"]
]
}
55 changes: 55 additions & 0 deletions test/rule-matches/aria-dpub-role-fallback-matches.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
describe('aria-dpub-role-fallback-matches', function () {
'use strict';

var fixture = document.getElementById('fixture');
var rule;

beforeEach(function () {
rule = axe._audit.rules.find(function (rule) {
return rule.id === 'aria-dpub-role-fallback';
});
});

afterEach(function () {
fixture.innerHTML = '';
});

it('is a function', function () {
assert.isFunction(rule.matches);
});

it('should not match elements with no role', function () {
fixture.innerHTML = '<div></div>';
var target = fixture.children[0];

assert.isFalse(rule.matches(target));
});

it('should not match elements with a nonsensical role', function () {
fixture.innerHTML = '<div role="yay"></div>';
var target = fixture.children[0];

assert.isFalse(rule.matches(target));
});

it('should not match elements with a non-DPUB role', function () {
fixture.innerHTML = '<div role="main"></div>';
var target = fixture.children[0];

assert.isFalse(rule.matches(target));
});

it('should not match elements with a "harmless" DPUB role', function () {
fixture.innerHTML = '<div role="doc-bibliography"></div>';
var target = fixture.children[0];

assert.isFalse(rule.matches(target));
});

it('should match elements with one of the targeted DPUB roles"', function () {
fixture.innerHTML = '<div role="doc-backlink"></div>';
var target = fixture.children[0];

assert.isTrue(rule.matches(target));
});
});

0 comments on commit 9470c02

Please sign in to comment.