diff --git a/lib/checks/aria/aria-allowed-attr-evaluate.js b/lib/checks/aria/aria-allowed-attr-evaluate.js index 59d09e7e6e..2874176873 100644 --- a/lib/checks/aria/aria-allowed-attr-evaluate.js +++ b/lib/checks/aria/aria-allowed-attr-evaluate.js @@ -29,7 +29,7 @@ import { isFocusable } from '../../commons/dom'; export default function ariaAllowedAttrEvaluate(node, options, virtualNode) { const invalid = []; const role = getRole(virtualNode); - let allowed = allowedAttr(role); + let allowed = allowedAttr(role, { vNode: virtualNode }); // @deprecated: allowed attr options to pass more attrs. // configure the standards spec instead diff --git a/lib/commons/aria/allowed-attr.js b/lib/commons/aria/allowed-attr.js index f2cd8b55b8..93da02d375 100644 --- a/lib/commons/aria/allowed-attr.js +++ b/lib/commons/aria/allowed-attr.js @@ -7,9 +7,16 @@ import getGlobalAriaAttrs from '../standards/get-global-aria-attrs'; * @memberof axe.commons.aria * @instance * @param {String} role The role to check + * @param {Object} options Optional configuration + * @param {VirtualNode} options.vNode Optional virtual node for element-level restrictions * @return {Array} */ -function allowedAttr(role) { +function allowedAttr(role, { vNode } = {}) { + // Check for br and wbr elements - only aria-hidden is allowed + if (vNode && ['br', 'wbr'].includes(vNode.props.nodeName)) { + return ['aria-hidden']; + } + const roleDef = standards.ariaRoles[role]; const attrs = [...getGlobalAriaAttrs()]; diff --git a/test/checks/aria/aria-allowed-attr.js b/test/checks/aria/aria-allowed-attr.js index b25b9df4a1..afd5999e4d 100644 --- a/test/checks/aria/aria-allowed-attr.js +++ b/test/checks/aria/aria-allowed-attr.js @@ -282,4 +282,46 @@ describe('aria-allowed-attr', () => { ); }); }); + + it('should pass for br with aria-hidden', () => { + const vNode = queryFixture(''); + + assert.isTrue( + axe.testUtils + .getCheckEvaluate('aria-allowed-attr') + .call(checkContext, null, null, vNode) + ); + }); + + it('should fail for br with global aria attribute', () => { + const vNode = queryFixture('
'); + + assert.isFalse( + axe.testUtils + .getCheckEvaluate('aria-allowed-attr') + .call(checkContext, null, null, vNode) + ); + assert.deepEqual(checkContext._data, ['aria-busy="true"']); + }); + + it('should pass for wbr with aria-hidden', () => { + const vNode = queryFixture(''); + + assert.isTrue( + axe.testUtils + .getCheckEvaluate('aria-allowed-attr') + .call(checkContext, null, null, vNode) + ); + }); + + it('should fail for wbr with global aria attribute', () => { + const vNode = queryFixture(''); + + assert.isFalse( + axe.testUtils + .getCheckEvaluate('aria-allowed-attr') + .call(checkContext, null, null, vNode) + ); + assert.deepEqual(checkContext._data, ['aria-busy="true"']); + }); }); diff --git a/test/commons/aria/allowed-attr.js b/test/commons/aria/allowed-attr.js index 575b3e427d..9d032fa187 100644 --- a/test/commons/aria/allowed-attr.js +++ b/test/commons/aria/allowed-attr.js @@ -49,4 +49,20 @@ describe('aria.allowedAttr', function () { it('should return an array with globally allowed attributes', function () { assert.deepEqual(axe.commons.aria.allowedAttr('cats'), globalAttrs); }); + + it('should return only aria-hidden for br element', function () { + const queryFixture = axe.testUtils.queryFixture; + const vNode = queryFixture('
'); + assert.deepEqual(axe.commons.aria.allowedAttr(null, { vNode }), [ + 'aria-hidden' + ]); + }); + + it('should return only aria-hidden for wbr element', function () { + const queryFixture = axe.testUtils.queryFixture; + const vNode = queryFixture(''); + assert.deepEqual(axe.commons.aria.allowedAttr(null, { vNode }), [ + 'aria-hidden' + ]); + }); }); diff --git a/test/integration/rules/aria-allowed-attr/failures.html b/test/integration/rules/aria-allowed-attr/failures.html index 21c84b0e50..4c582d7d4d 100644 --- a/test/integration/rules/aria-allowed-attr/failures.html +++ b/test/integration/rules/aria-allowed-attr/failures.html @@ -13,3 +13,6 @@
+ +
+ diff --git a/test/integration/rules/aria-allowed-attr/failures.json b/test/integration/rules/aria-allowed-attr/failures.json index 02d9579b81..03a4569f50 100644 --- a/test/integration/rules/aria-allowed-attr/failures.json +++ b/test/integration/rules/aria-allowed-attr/failures.json @@ -9,6 +9,8 @@ ["#fail5"], ["#fail6"], ["#fail7"], - ["#fail8"] + ["#fail8"], + ["#fail9"], + ["#fail10"] ] } diff --git a/test/integration/rules/aria-allowed-attr/passes.html b/test/integration/rules/aria-allowed-attr/passes.html index a21c68c3b1..7144db8c80 100644 --- a/test/integration/rules/aria-allowed-attr/passes.html +++ b/test/integration/rules/aria-allowed-attr/passes.html @@ -2173,3 +2173,7 @@
+ + + + diff --git a/test/integration/rules/aria-allowed-attr/passes.json b/test/integration/rules/aria-allowed-attr/passes.json index 0974e07250..6c68e108ab 100644 --- a/test/integration/rules/aria-allowed-attr/passes.json +++ b/test/integration/rules/aria-allowed-attr/passes.json @@ -104,6 +104,8 @@ ["#pass99"], ["#pass100"], ["#pass101"], - ["#pass102"] + ["#pass102"], + ["#pass103"], + ["#pass104"] ] }