Skip to content

Commit

Permalink
feat(presentation-role-conflict): create rule to flag elements with r…
Browse files Browse the repository at this point in the history
…ole conflict resolution (#2440)

* Add checks and tests for flag-role rule

* Format files

* test: add integration tests

* Add checks and tests for flag-role rule

* Format files

* test: add integration tests

* PR changes

* Fix integration test

* Enrich message with global attribute information
  • Loading branch information
jlin95 committed Sep 28, 2020
1 parent 8a699ec commit e4edffc
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 28 deletions.
57 changes: 29 additions & 28 deletions doc/rule-descriptions.md

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions lib/checks/aria/has-global-aria-attribute-evaluate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import getGlobalAriaAttrs from '../../commons/standards/get-global-aria-attrs';

function hasGlobalAriaAttributeEvaluate(node, options, virtualNode) {
const globalAttrs = getGlobalAriaAttrs().filter(attr =>
virtualNode.hasAttr(attr)
);
this.data(globalAttrs);
return globalAttrs.length > 0;
}

export default hasGlobalAriaAttributeEvaluate;
14 changes: 14 additions & 0 deletions lib/checks/aria/has-global-aria-attribute.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"id": "has-global-aria-attribute",
"evaluate": "has-global-aria-attribute-evaluate",
"metadata": {
"impact": "minor",
"messages": {
"pass": {
"singular": "Element has global ARIA attribute: ${data.values}",
"plural": "Element has global ARIA attributes: ${data.values}"
},
"fail": "Element does not have global ARIA attribute"
}
}
}
7 changes: 7 additions & 0 deletions lib/checks/aria/is-element-focusable-evaluate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { isFocusable } from '../../commons/dom';

function isElementFocusableEvaluate(node, options, virtualNode) {
return isFocusable(virtualNode);
}

export default isElementFocusableEvaluate;
11 changes: 11 additions & 0 deletions lib/checks/aria/is-element-focusable.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"id": "is-element-focusable",
"evaluate": "is-element-focusable-evaluate",
"metadata": {
"impact": "minor",
"messages": {
"pass": "Element is focusable.",
"fail": "Element is not focusable."
}
}
}
4 changes: 4 additions & 0 deletions lib/core/base/metadata-function-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import ariaUnsupportedAttrEvaluate from '../../checks/aria/aria-unsupported-attr
import ariaValidAttrEvaluate from '../../checks/aria/aria-valid-attr-evaluate';
import ariaValidAttrValueEvaluate from '../../checks/aria/aria-valid-attr-value-evaluate';
import fallbackroleEvaluate from '../../checks/aria/fallbackrole-evaluate';
import hasGlobalAriaAttributeEvaluate from '../../checks/aria/has-global-aria-attribute-evaluate';
import hasWidgetRoleEvaluate from '../../checks/aria/has-widget-role-evaluate';
import invalidroleEvaluate from '../../checks/aria/invalidrole-evaluate';
import isElementFocusableEvaluate from '../../checks/aria/is-element-focusable-evaluate';
import noImplicitExplicitLabelEvaluate from '../../checks/aria/no-implicit-explicit-label-evaluate';
import unsupportedroleEvaluate from '../../checks/aria/unsupportedrole-evaluate';
import validScrollableSemanticsEvaluate from '../../checks/aria/valid-scrollable-semantics-evaluate';
Expand Down Expand Up @@ -175,8 +177,10 @@ const metadataFunctionMap = {
'aria-valid-attr-evaluate': ariaValidAttrEvaluate,
'aria-valid-attr-value-evaluate': ariaValidAttrValueEvaluate,
'fallbackrole-evaluate': fallbackroleEvaluate,
'has-global-aria-attribute-evaluate': hasGlobalAriaAttributeEvaluate,
'has-widget-role-evaluate': hasWidgetRoleEvaluate,
'invalidrole-evaluate': invalidroleEvaluate,
'is-element-focusable-evaluate': isElementFocusableEvaluate,
'no-implicit-explicit-label-evaluate': noImplicitExplicitLabelEvaluate,
'unsupportedrole-evaluate': unsupportedroleEvaluate,
'valid-scrollable-semantics-evaluate': validScrollableSemanticsEvaluate,
Expand Down
12 changes: 12 additions & 0 deletions lib/rules/presentation-role-conflict.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"id": "presentation-role-conflict",
"selector": "[role=\"none\"], [role=\"presentation\"]",
"tags": ["cat.aria", "best-practice"],
"metadata": {
"description": "Flags elements whose role is none or presentation and which cause the role conflict resolution to trigger.",
"help": "Elements of role none or presentation should be flagged"
},
"all": [],
"any": [],
"none": ["is-element-focusable", "has-global-aria-attribute"]
}
Empty file modified package-lock.json
100644 → 100755
Empty file.
31 changes: 31 additions & 0 deletions test/checks/aria/has-global-aria-attribute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
describe('has-global-aria-attribute', function() {
'use strict';

var fixture = document.getElementById('fixture');
var checkSetup = axe.testUtils.checkSetup;

var checkContext = axe.testUtils.MockCheckContext();
var evaluate = axe.testUtils.getCheckEvaluate('has-global-aria-attribute');

afterEach(function() {
fixture.innerHTML = '';
axe._tree = undefined;
checkContext.reset();
});

it('should return true if any global ARIA attributes are found', function() {
var node = document.createElement('div');
node.id = 'test';
node.setAttribute('aria-label', 'hello');
var params = checkSetup(node);
assert.isTrue(evaluate.apply(checkContext, params));
});

it('should return false if no valid ARIA attributes are found', function() {
var node = document.createElement('div');
node.id = 'test';
node.setAttribute('aria-random', 'hello');
var params = checkSetup(node);
assert.isFalse(evaluate.apply(checkContext, params));
});
});
41 changes: 41 additions & 0 deletions test/checks/aria/is-element-focusable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
describe('is-element-focusable', function() {
'use strict';

var fixture = document.getElementById('fixture');
var checkContext = axe.testUtils.MockCheckContext();

afterEach(function() {
fixture.innerHTML = '';
checkContext.reset();
});

it('should return true for div with a tabindex', function() {
var node = document.createElement('div');
node.id = 'target';
node.tabIndex = 1;
fixture.appendChild(node);
axe._tree = axe.utils.getFlattenedTree(fixture);
var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);

assert.isTrue(
axe.testUtils
.getCheckEvaluate('is-element-focusable')
.call(checkContext, node, {}, virtualNode)
);
});

it('should return false for natively unfocusable element', function() {
var node = document.createElement('span');
node.id = 'target';
node.role = 'link';
node.href = '#';
fixture.appendChild(node);
axe._tree = axe.utils.getFlattenedTree(fixture);
var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);
assert.isFalse(
axe.testUtils
.getCheckEvaluate('is-element-focusable')
.call(checkContext, node, {}, virtualNode)
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div id="pass1" role="presentation">Test</div>
<div id="pass2" role="presentation">Test Button</div>
<li id="violation1" role="presentation" aria-label="My Heading">Hello</li>
<h1 id="violation2" role="none" aria-label="My Heading">Hello</h1>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"rule": "presentation-role-conflict",
"description": "presentation-role-conflict tests",
"violations": [["#violation1"], ["#violation2"]],
"passes": [["#pass1"], ["#pass2"]]
}

0 comments on commit e4edffc

Please sign in to comment.