Skip to content

Commit 61ac303

Browse files
marcysuttonMarcy Sutton
authored andcommitted
feat: allow options in aria-allowed-attr, aria-required-attr (#673)
* chore: rename lut in tests * feat: allow options in aria-allowed-attr * chore: rename options integ. tests for clarity * feat: add required-attr options, integration tests * chore: PR feedback for aria-allowed-attr * feat: use object for ARIA check options e.g. {separator: ['aria-valuenow', 'aria-valuemin', aria-valuemax']} * chore: remove duplicate to-array util The exact same code existed in axe.utils and axe.commons (as axe.utils.toArray) * chore: simplify ARIA options usage
1 parent 8016ad1 commit 61ac303

File tree

14 files changed

+190
-48
lines changed

14 files changed

+190
-48
lines changed

lib/checks/aria/allowed-attr.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
options = options || {};
2+
13
var invalid = [];
24

35
var attr, attrName, allowed,
@@ -7,12 +9,18 @@ var attr, attrName, allowed,
79
if (!role) {
810
role = axe.commons.aria.implicitRole(node);
911
}
12+
1013
allowed = axe.commons.aria.allowedAttr(role);
14+
15+
if (Array.isArray(options[role])) {
16+
allowed = axe.utils.uniqueArray(options[role].concat(allowed));
17+
}
18+
1119
if (role && allowed) {
1220
for (var i = 0, l = attrs.length; i < l; i++) {
1321
attr = attrs[i];
1422
attrName = attr.name;
15-
if (axe.commons.aria.validateAttr(attrName) && allowed.indexOf(attrName) === -1) {
23+
if (axe.commons.aria.validateAttr(attrName) && !allowed.includes(attrName)) {
1624
invalid.push(attrName + '="' + attr.nodeValue + '"');
1725
}
1826
}

lib/checks/aria/required-attr.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
options = options || {};
2+
13
var missing = [];
24

35
if (node.hasAttributes()) {
46
var attr,
57
role = node.getAttribute('role'),
68
required = axe.commons.aria.requiredAttr(role);
79

10+
if (Array.isArray(options[role])) {
11+
required = axe.utils.uniqueArray(options[role], required);
12+
}
813
if (role && required) {
914
for (var i = 0, l = required.length; i < l; i++) {
1015
attr = required[i];

lib/commons/utils/to-array.js

Lines changed: 0 additions & 14 deletions
This file was deleted.

lib/core/utils/to-array.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,17 @@
77
axe.utils.toArray = function (thing) {
88
'use strict';
99
return Array.prototype.slice.call(thing);
10-
};
10+
};
11+
12+
13+
/**
14+
* Creates an array without duplicate values from 2 array inputs
15+
* @param {Array} arr1 First array
16+
* @param {Array} arr2 Second array
17+
* @return {Array}
18+
*/
19+
axe.utils.uniqueArray = (arr1, arr2) => {
20+
return arr1.concat(arr2).filter((elem, pos, arr) => {
21+
return arr.indexOf(elem) === pos;
22+
});
23+
};

test/checks/aria/allowed-attr.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,51 @@ describe('aria-allowed-attr', function () {
119119
assert.isNull(checkContext._data);
120120
});
121121

122+
describe('options', function () {
123+
it('should allow provided attribute names for a role', function () {
124+
axe.commons.aria.lookupTable.role.mcheddarton = {
125+
type: 'widget',
126+
attributes: {
127+
allowed: ['aria-checked']
128+
},
129+
owned: null,
130+
nameFrom: ['author'],
131+
context: null
132+
};
133+
fixture.innerHTML = '<div role="mccheddarton" id="target" aria-checked="true" aria-snuggles="true"></div>';
134+
var target = fixture.children[0];
135+
assert.isTrue(checks['aria-allowed-attr'].evaluate.call(checkContext, target, {'mccheddarton': ['aria-checked', 'aria-snuggles']}));
136+
delete axe.commons.aria.lookupTable.role.mccheddarton;
137+
});
138+
139+
it('should handle multiple roles provided in options', function () {
140+
axe.commons.aria.lookupTable.role.mcheddarton = {
141+
type: 'widget',
142+
attributes: {
143+
allowed: ['aria-checked']
144+
},
145+
owned: null,
146+
nameFrom: ['author'],
147+
context: null
148+
};
149+
axe.commons.aria.lookupTable.role.bagley = {
150+
type: 'widget',
151+
attributes: {
152+
allowed: ['aria-checked']
153+
},
154+
owned: null,
155+
nameFrom: ['author'],
156+
context: null
157+
};
158+
fixture.innerHTML = '<div role="bagley" id="target" aria-snuggles2="true"></div>';
159+
var target = fixture.children[0];
160+
var options = {
161+
'mccheddarton': ['aria-snuggles'],
162+
'bagley': ['aria-snuggles2']
163+
};
164+
assert.isTrue(checks['aria-allowed-attr'].evaluate.call(checkContext, target, options));
165+
delete axe.commons.aria.lookupTable.role.mccheddarton;
166+
delete axe.commons.aria.lookupTable.role.bagley;
167+
});
168+
});
122169
});

test/checks/aria/required-attr.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,25 @@ describe('aria-required-attr', function () {
5757
axe.commons.aria.requiredAttr = orig;
5858
});
5959

60+
describe('options', function () {
61+
it('should require provided attribute names for a role', function () {
62+
axe.commons.aria.lookupTable.role.mccheddarton = {
63+
type: 'widget',
64+
attributes: {
65+
required: ['aria-valuemax']
66+
},
67+
owned: null,
68+
nameFrom: ['author'],
69+
context: null
70+
};
71+
fixture.innerHTML = '<div role="mccheddarton" id="target"></div>';
72+
var target = fixture.children[0];
73+
var options = {
74+
'mccheddarton': ['aria-snuggles']
75+
};
76+
assert.isFalse(checks['aria-required-attr'].evaluate.call(checkContext, target, options));
77+
assert.deepEqual(checkContext._data, ['aria-snuggles', 'aria-valuemax']);
78+
delete axe.commons.aria.lookupTable.role.mccheddarton;
79+
});
80+
});
6081
});

test/commons/aria/roles.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe('aria.isValidRole', function () {
1212

1313
});
1414

15-
it('should return false if role is not found in the lut', function () {
15+
it('should return false if role is not found in the lookup table', function () {
1616
assert.isFalse(axe.commons.aria.isValidRole('cats'));
1717

1818
});
@@ -57,7 +57,7 @@ describe('aria.getRolesByType', function () {
5757

5858
});
5959

60-
it('should return empty array if role is not found in the lut', function () {
60+
it('should return empty array if role is not found in the lookup table', function () {
6161
assert.deepEqual(axe.commons.aria.getRolesByType('blahblahblah'), []);
6262
});
6363
});
@@ -77,7 +77,7 @@ describe('aria.getRoleType', function () {
7777

7878
});
7979

80-
it('should return null if role is not found in the lut', function () {
80+
it('should return null if role is not found in the lookup table', function () {
8181
assert.isNull(axe.commons.aria.getRoleType('cats'));
8282
});
8383
});

test/commons/utils/to-array.js

Lines changed: 0 additions & 26 deletions
This file was deleted.

test/core/utils/to-array.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
describe('axe.utils.toArray', function () {
22
'use strict';
3-
43
it('should call Array.prototype.slice', function () {
54
var orig = Array.prototype.slice,
65
called = false,
@@ -24,4 +23,18 @@ describe('axe.utils.toArray', function () {
2423
var result = axe.utils.toArray(arrayLike);
2524
assert.isArray(result);
2625
});
26+
27+
});
28+
29+
describe('axe.utils.uniqueArray', function () {
30+
'use strict';
31+
32+
it('should filter duplicate values', function () {
33+
var array1 = [1, 2, 3, 4, 5];
34+
var array2 = [1, 3, 7];
35+
36+
var result = axe.utils.uniqueArray(array1, array2);
37+
assert.isArray(result);
38+
assert.includeMembers(result, [1, 2, 3, 4, 5, 7]);
39+
});
2740
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!doctype html>
2+
<html lang="en" id="main">
3+
<head>
4+
<title></title>
5+
<meta charset="utf8">
6+
<link rel="stylesheet" type="text/css" href="/node_modules/mocha/mocha.css" />
7+
<script src="/node_modules/mocha/mocha.js"></script>
8+
<script src="/node_modules/chai/chai.js"></script>
9+
<script src="/axe.js"></script>
10+
<script>
11+
mocha.setup({
12+
timeout: 50000,
13+
ui: 'bdd'
14+
});
15+
var assert = chai.assert;
16+
</script>
17+
</head>
18+
<body>
19+
<div id="target"></div>
20+
21+
<div id="mocha"></div>
22+
<script src="configure-options.js"></script>
23+
<script src="/test/integration/adapter.js"></script>
24+
</body>
25+
</html>

0 commit comments

Comments
 (0)