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"]
]
}