Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions lib/checks/aria/aria-prohibited-attr-evaluate.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ export default function ariaProhibitedAttrEvaluate(
) {
const elementsAllowedAriaLabel = options?.elementsAllowedAriaLabel || [];
const { nodeName } = virtualNode.props;
const role = getRole(virtualNode, { chromium: true });
const role = getRole(virtualNode, {
chromium: true,
// this check allows fallback roles. For example, `<div role="foo img" aria-label="...">` is legal.
fallback: true
});

const prohibitedList = listProhibitedAttrs(
virtualNode,
Expand All @@ -53,7 +57,7 @@ export default function ariaProhibitedAttrEvaluate(
return false;
}

let messageKey = virtualNode.hasAttr('role') ? 'hasRole' : 'noRole';
let messageKey = role !== null ? 'hasRole' : 'noRole';
messageKey += prohibited.length > 1 ? 'Plural' : 'Singular';
this.data({ role, nodeName, messageKey, prohibited });

Expand Down
33 changes: 33 additions & 0 deletions test/checks/aria/aria-prohibited-attr.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,39 @@ describe('aria-prohibited-attr', () => {
assert.isFalse(checkEvaluate.apply(checkContext, params));
});

it('should not allow aria-label on divs that have an invalid role', function () {
const params = checkSetup(
'<div id="target" role="foo" aria-label="foo"></div>'
);
assert.isTrue(checkEvaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, {
nodeName: 'div',
role: null,
messageKey: 'noRoleSingular',
prohibited: ['aria-label']
});
});

it('should allow aria-label on divs with a valid fallback role', function () {
const params = checkSetup(
'<div id="target" role="foo dialog" aria-label="foo"></div>'
);
assert.isFalse(checkEvaluate.apply(checkContext, params));
});

it('should not allow aria-label on divs with no valid fallback roles', function () {
const params = checkSetup(
'<div id="target" role="foo bar" aria-label="foo"></div>'
);
assert.isTrue(checkEvaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, {
nodeName: 'div',
role: null,
messageKey: 'noRoleSingular',
prohibited: ['aria-label']
});
});

describe('widget ancestor', () => {
it('should allow aria-label', () => {
const params = checkSetup(`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<div role="row" aria-colcount="value" id="pass5"></div>
<div role="button"><span id="pass6" aria-label="value"></span></div>
<div role="button"><span id="pass7" aria-labelledby="value"></span></div>
<div role="foo dialog" aria-label="foo" id="pass8"></div>

<div role="caption" aria-label="value" id="fail1"></div>
<div role="caption" aria-labelledby="value" id="fail2"></div>
Expand All @@ -26,7 +27,7 @@
<div role="superscript" aria-labelledby="value" id="fail18"></div>
<div aria-label="value" id="fail19"></div>
<div aria-labelledby="value" id="fail20"></div>
<!- aria-label(ledby) is prohibited on none / presentation. Axe-core considers this to trigger presentation role
<!- aria-label(ledby) is prohibited on none / presentation. Axe-core considers this to trigger presentation role
conflict, which was true in ARIA 1.1. This changed in ARIA 1.2 but so far has only been implemented in Chomium. -->
<span aria-label="value" id="fail21"></span>
<strong aria-label="value" id="fail22"></strong>
Expand All @@ -39,6 +40,8 @@
<div role="suggestion" aria-labelledby="value" id="fail29"></div>
<div role="grid"><span id="fail30" aria-label="value"></span></div>
<div role="grid"><span id="fail31" aria-labelledby="value"></span></div>
<div role="foo" aria-label="foo" id="fail32"></div>
<div role="foo bar" aria-label="foo" id="fail33"></div>

<div id="incomplete1" aria-label="foo">Foo</div>
<div id="incomplete2" aria-labelledby="missing">Foo</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
["#pass4"],
["#pass5"],
["#pass6"],
["#pass7"]
["#pass7"],
["#pass8"]
],
"incomplete": [["#incomplete1"], ["#incomplete2"], ["#incomplete3"]],
"violations": [
Expand Down Expand Up @@ -42,6 +43,8 @@
["#fail28"],
["#fail29"],
["#fail30"],
["#fail31"]
["#fail31"],
["#fail32"],
["#fail33"]
]
}
48 changes: 48 additions & 0 deletions test/integration/virtual-rules/aria-prohibited-attr.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,52 @@ describe('aria-prohibited-attr virtual-rule', () => {
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.incomplete, 0);
});

it('should fail for invalid role', () => {
const vNode = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'foo',
'aria-label': 'foo'
}
});
vNode.children = [];

const results = axe.runVirtualRule('aria-prohibited-attr', vNode);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.incomplete, 0);
});

it('should pass for fallback roles', () => {
const results = axe.runVirtualRule('aria-prohibited-attr', {
nodeName: 'div',
attributes: {
role: 'foo dialog',
'aria-label': 'foo'
}
});

assert.lengthOf(results.passes, 1);
assert.lengthOf(results.violations, 0);
assert.lengthOf(results.incomplete, 0);
});

it('should fail for multiple invalid roles', () => {
const vNode = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'foo bar',
'aria-label': 'foo'
}
});
vNode.children = [];

const results = axe.runVirtualRule('aria-prohibited-attr', vNode);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.incomplete, 0);
});
});