Skip to content

Commit

Permalink
feat: allow options in aria-allowed-attr, aria-required-attr (#673)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
marcysutton authored and Marcy Sutton committed Jan 18, 2018
1 parent 8016ad1 commit 61ac303
Show file tree
Hide file tree
Showing 14 changed files with 190 additions and 48 deletions.
10 changes: 9 additions & 1 deletion lib/checks/aria/allowed-attr.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
options = options || {};

var invalid = [];

var attr, attrName, allowed,
Expand All @@ -7,12 +9,18 @@ var attr, attrName, allowed,
if (!role) {
role = axe.commons.aria.implicitRole(node);
}

allowed = axe.commons.aria.allowedAttr(role);

if (Array.isArray(options[role])) {
allowed = axe.utils.uniqueArray(options[role].concat(allowed));
}

if (role && allowed) {
for (var i = 0, l = attrs.length; i < l; i++) {
attr = attrs[i];
attrName = attr.name;
if (axe.commons.aria.validateAttr(attrName) && allowed.indexOf(attrName) === -1) {
if (axe.commons.aria.validateAttr(attrName) && !allowed.includes(attrName)) {
invalid.push(attrName + '="' + attr.nodeValue + '"');
}
}
Expand Down
5 changes: 5 additions & 0 deletions lib/checks/aria/required-attr.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
options = options || {};

var missing = [];

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

if (Array.isArray(options[role])) {
required = axe.utils.uniqueArray(options[role], required);
}
if (role && required) {
for (var i = 0, l = required.length; i < l; i++) {
attr = required[i];
Expand Down
14 changes: 0 additions & 14 deletions lib/commons/utils/to-array.js

This file was deleted.

15 changes: 14 additions & 1 deletion lib/core/utils/to-array.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,17 @@
axe.utils.toArray = function (thing) {
'use strict';
return Array.prototype.slice.call(thing);
};
};


/**
* Creates an array without duplicate values from 2 array inputs
* @param {Array} arr1 First array
* @param {Array} arr2 Second array
* @return {Array}
*/
axe.utils.uniqueArray = (arr1, arr2) => {
return arr1.concat(arr2).filter((elem, pos, arr) => {
return arr.indexOf(elem) === pos;
});
};
47 changes: 47 additions & 0 deletions test/checks/aria/allowed-attr.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,51 @@ describe('aria-allowed-attr', function () {
assert.isNull(checkContext._data);
});

describe('options', function () {
it('should allow provided attribute names for a role', function () {
axe.commons.aria.lookupTable.role.mcheddarton = {
type: 'widget',
attributes: {
allowed: ['aria-checked']
},
owned: null,
nameFrom: ['author'],
context: null
};
fixture.innerHTML = '<div role="mccheddarton" id="target" aria-checked="true" aria-snuggles="true"></div>';
var target = fixture.children[0];
assert.isTrue(checks['aria-allowed-attr'].evaluate.call(checkContext, target, {'mccheddarton': ['aria-checked', 'aria-snuggles']}));
delete axe.commons.aria.lookupTable.role.mccheddarton;
});

it('should handle multiple roles provided in options', function () {
axe.commons.aria.lookupTable.role.mcheddarton = {
type: 'widget',
attributes: {
allowed: ['aria-checked']
},
owned: null,
nameFrom: ['author'],
context: null
};
axe.commons.aria.lookupTable.role.bagley = {
type: 'widget',
attributes: {
allowed: ['aria-checked']
},
owned: null,
nameFrom: ['author'],
context: null
};
fixture.innerHTML = '<div role="bagley" id="target" aria-snuggles2="true"></div>';
var target = fixture.children[0];
var options = {
'mccheddarton': ['aria-snuggles'],
'bagley': ['aria-snuggles2']
};
assert.isTrue(checks['aria-allowed-attr'].evaluate.call(checkContext, target, options));
delete axe.commons.aria.lookupTable.role.mccheddarton;
delete axe.commons.aria.lookupTable.role.bagley;
});
});
});
21 changes: 21 additions & 0 deletions test/checks/aria/required-attr.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,25 @@ describe('aria-required-attr', function () {
axe.commons.aria.requiredAttr = orig;
});

describe('options', function () {
it('should require provided attribute names for a role', function () {
axe.commons.aria.lookupTable.role.mccheddarton = {
type: 'widget',
attributes: {
required: ['aria-valuemax']
},
owned: null,
nameFrom: ['author'],
context: null
};
fixture.innerHTML = '<div role="mccheddarton" id="target"></div>';
var target = fixture.children[0];
var options = {
'mccheddarton': ['aria-snuggles']
};
assert.isFalse(checks['aria-required-attr'].evaluate.call(checkContext, target, options));
assert.deepEqual(checkContext._data, ['aria-snuggles', 'aria-valuemax']);
delete axe.commons.aria.lookupTable.role.mccheddarton;
});
});
});
6 changes: 3 additions & 3 deletions test/commons/aria/roles.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('aria.isValidRole', function () {

});

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

});
Expand Down Expand Up @@ -57,7 +57,7 @@ describe('aria.getRolesByType', function () {

});

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

});

it('should return null if role is not found in the lut', function () {
it('should return null if role is not found in the lookup table', function () {
assert.isNull(axe.commons.aria.getRoleType('cats'));
});
});
Expand Down
26 changes: 0 additions & 26 deletions test/commons/utils/to-array.js

This file was deleted.

15 changes: 14 additions & 1 deletion test/core/utils/to-array.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
describe('axe.utils.toArray', function () {
'use strict';

it('should call Array.prototype.slice', function () {
var orig = Array.prototype.slice,
called = false,
Expand All @@ -24,4 +23,18 @@ describe('axe.utils.toArray', function () {
var result = axe.utils.toArray(arrayLike);
assert.isArray(result);
});

});

describe('axe.utils.uniqueArray', function () {
'use strict';

it('should filter duplicate values', function () {
var array1 = [1, 2, 3, 4, 5];
var array2 = [1, 3, 7];

var result = axe.utils.uniqueArray(array1, array2);
assert.isArray(result);
assert.includeMembers(result, [1, 2, 3, 4, 5, 7]);
});
});
25 changes: 25 additions & 0 deletions test/integration/full/configure-options/configure-options.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<html lang="en" id="main">
<head>
<title></title>
<meta charset="utf8">
<link rel="stylesheet" type="text/css" href="/node_modules/mocha/mocha.css" />
<script src="/node_modules/mocha/mocha.js"></script>
<script src="/node_modules/chai/chai.js"></script>
<script src="/axe.js"></script>
<script>
mocha.setup({
timeout: 50000,
ui: 'bdd'
});
var assert = chai.assert;
</script>
</head>
<body>
<div id="target"></div>

<div id="mocha"></div>
<script src="configure-options.js"></script>
<script src="/test/integration/adapter.js"></script>
</body>
</html>
50 changes: 50 additions & 0 deletions test/integration/full/configure-options/configure-options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
describe('Check Configure Options', function() {
'use strict';

var target = document.querySelector('#target');

describe('aria-allowed-attr', function() {
it('should allow an attribute supplied in options', function(done) {
target.setAttribute('role', 'separator');
target.setAttribute('aria-valuenow', '0');

axe.configure({
checks: [{
id: 'aria-allowed-attr',
options: {'separator': ['aria-valuenow']}
}]
});
axe.run(target, {
runOnly: {
type: 'rule',
values: [ 'aria-allowed-attr' ]
}
}, function(error, results) {
assert.lengthOf(results.violations, 0, 'violations');
done();
});
});
});

describe('aria-required-attr', function() {
it('should report unique attributes when supplied from options', function(done) {
target.setAttribute('role', 'slider');
axe.configure({
checks: [{
id: 'aria-required-attr',
options: {slider: ['aria-snuggles']}
}]
});
axe.run('#target', {
runOnly: {
type: 'rule',
values: [ 'aria-required-attr' ]
}
}, function(error, results) {
assert.lengthOf(results.violations, 1, 'violations');
assert.sameMembers(results.violations[0].nodes[0].any[0].data, ['aria-valuemax', 'aria-valuemin', 'aria-snuggles']);
done();
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
</div>

<div id="mocha"></div>
<script src="options.js"></script>
<script src="options-parameter.js"></script>
<script src="/test/integration/adapter.js"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
describe('Options', function() {
describe('Options parameter', function() {
'use strict';

before(function (done) {
Expand Down

0 comments on commit 61ac303

Please sign in to comment.