Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aria-prohibited-attr): add support fallback role #4325

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 6 additions & 2 deletions lib/checks/aria/aria-prohibited-attr-evaluate.js
Expand Up @@ -33,7 +33,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(
role,
Expand All @@ -51,7 +55,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
Expand Up @@ -138,4 +138,37 @@ describe('aria-prohibited-attr', function () {
var params = checkSetup('<svg id="target" aria-label="hello world"></svg>');
assert.isFalse(checkEvaluate.apply(checkContext, params));
});

it('should not allow aria-label on divs that have an invalid role', function () {
var 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 () {
var 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 () {
var 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']
});
});
});
Expand Up @@ -3,6 +3,7 @@
<div id="pass3" aria-labelledby=" ">Foo</div>
<div role="alert" aria-selected="true" id="pass4"></div>
<div role="row" aria-colcount="value" id="pass5"></div>
<div role="foo dialog" aria-label="foo" id="pass6"></div>

<div role="caption" aria-label="value" id="fail1"></div>
<div role="caption" aria-labelledby="value" id="fail2"></div>
Expand Down Expand Up @@ -35,6 +36,8 @@
<div role="mark" aria-labelledby="value" id="fail27"></div>
<div role="suggestion" aria-label="value" id="fail28"></div>
<div role="suggestion" aria-labelledby="value" id="fail29"></div>
<div role="foo" aria-label="foo" id="fail30"></div>
<div role="foo bar" aria-label="foo" id="fail31"></div>

<div id="incomplete1" aria-label="foo">Foo</div>
<div id="incomplete2" aria-labelledby="missing">Foo</div>
Expand Down
@@ -1,7 +1,14 @@
{
"description": "aria-prohibited-attr tests",
"rule": "aria-prohibited-attr",
"passes": [["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"], ["#pass5"]],
"passes": [
["#pass1"],
["#pass2"],
["#pass3"],
["#pass4"],
["#pass5"],
["#pass6"]
],
"incomplete": [["#incomplete1"], ["#incomplete2"], ["#incomplete3"]],
"violations": [
["#fail1"],
Expand Down Expand Up @@ -32,6 +39,8 @@
["#fail26"],
["#fail27"],
["#fail28"],
["#fail29"]
["#fail29"],
["#fail30"],
["#fail31"]
]
}
48 changes: 48 additions & 0 deletions test/integration/virtual-rules/aria-prohibited-attr.js
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);
});
});