diff --git a/lib/checks/aria/required-children.js b/lib/checks/aria/required-children.js
index 6d61ef5db5..73e30dd1e3 100644
--- a/lib/checks/aria/required-children.js
+++ b/lib/checks/aria/required-children.js
@@ -3,7 +3,7 @@ implicitNodes = axe.commons.aria.implicitNodes,
matchesSelector = axe.commons.utils.matchesSelector,
idrefs = axe.commons.dom.idrefs;
-function owns(node, role, ariaOwned) {
+function owns(node, virtualTree, role, ariaOwned) {
if (node === null) { return false; }
var implicit = implicitNodes(role),
selector = ['[role="' + role + '"]'];
@@ -13,9 +13,8 @@ function owns(node, role, ariaOwned) {
}
selector = selector.join(',');
-
- return ariaOwned ? (matchesSelector(node, selector) || !!node.querySelector(selector)) :
- !!node.querySelector(selector);
+ return ariaOwned ? (matchesSelector(node, selector) || !!axe.utils.querySelectorAll(virtualTree, selector)[0]) :
+ !!axe.utils.querySelectorAll(virtualTree, selector)[0];
}
function ariaOwns(nodes, role) {
@@ -23,7 +22,8 @@ function ariaOwns(nodes, role) {
for (index = 0, length = nodes.length; index < length; index++) {
if (nodes[index] === null) { continue; }
- if (owns(nodes[index], role, true)) {
+ let virtualTree = axe.utils.getFlattenedTree(nodes[index]);
+ if (owns(nodes[index], virtualTree, role, true)) {
return true;
}
}
@@ -39,7 +39,7 @@ function missingRequiredChildren(node, childRoles, all) {
for (i = 0; i < l; i++) {
var r = childRoles[i];
- if (owns(node, r) || ariaOwns(ownedElements, r)) {
+ if (owns(node, virtualNode, r) || ariaOwns(ownedElements, r)) {
if (!all) { return null; }
} else {
if (all) { missing.push(r); }
diff --git a/test/checks/aria/required-children.js b/test/checks/aria/required-children.js
index 57cdb5046c..46296572c4 100644
--- a/test/checks/aria/required-children.js
+++ b/test/checks/aria/required-children.js
@@ -2,6 +2,7 @@ describe('aria-required-children', function () {
'use strict';
var fixture = document.getElementById('fixture');
+ var shadowSupported = axe.testUtils.shadowSupport.v1;
var checkContext = {
_data: null,
@@ -10,97 +11,141 @@ describe('aria-required-children', function () {
}
};
+ function checkSetup (html, options, target) {
+ fixture.innerHTML = html;
+ axe._tree = axe.utils.getFlattenedTree(fixture);
+ var node = fixture.querySelector(target || '#target');
+ var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);
+ return [node, options, virtualNode];
+ }
+
afterEach(function () {
fixture.innerHTML = '';
+ axe._tree = undefined;
checkContext._data = null;
});
it('should detect missing sole required child', function () {
- fixture.innerHTML = '
';
- var node = fixture.querySelector('#target');
- assert.isFalse(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('');
+
+ assert.isFalse(checks['aria-required-children'].evaluate.apply(checkContext, params));
+ assert.deepEqual(checkContext._data, ['listitem']);
+ });
+
+ (shadowSupported ? it : xit)
+ ('should detect missing sole required child in shadow tree', function () {
+ fixture.innerHTML = '';
+
+ var target = document.querySelector('#target');
+ var shadowRoot = target.attachShadow({ mode: 'open' });
+ shadowRoot.innerHTML = 'Nothing here.
';
+
+ var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
+ var virtualTarget = axe.utils.getNodeFromTree(tree[0], target);
+
+ var params = [target, undefined, virtualTarget];
+ assert.isFalse(checks['aria-required-children'].evaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, ['listitem']);
});
it('should detect multiple missing required children when one required', function () {
- fixture.innerHTML = '';
- var node = fixture.querySelector('#target');
- assert.isFalse(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('');
+
+ assert.isFalse(checks['aria-required-children'].evaluate.apply(checkContext, params));
+ assert.deepEqual(checkContext._data, ['rowgroup', 'row']);
+ });
+
+ (shadowSupported ? it : xit)
+ ('should detect missing multiple required children in shadow tree when one required', function () {
+ fixture.innerHTML = '';
+
+ var target = document.querySelector('#target');
+ var shadowRoot = target.attachShadow({ mode: 'open' });
+ shadowRoot.innerHTML = 'Nothing here.
';
+
+ var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
+ var virtualTarget = axe.utils.getNodeFromTree(tree[0], target);
+
+ var params = [target, undefined, virtualTarget];
+ assert.isFalse(checks['aria-required-children'].evaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, ['rowgroup', 'row']);
});
it('should detect multiple missing required children when all required', function () {
- fixture.innerHTML = '';
- var node = fixture.querySelector('#target');
- assert.isFalse(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('');
+ assert.isFalse(checks['aria-required-children'].evaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, ['listbox', 'textbox']);
});
it('should detect single missing required child when all required', function () {
- fixture.innerHTML = '';
- var node = fixture.querySelector('#target');
- assert.isFalse(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('');
+ assert.isFalse(checks['aria-required-children'].evaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, ['textbox']);
});
it('should pass all existing required children when all required', function () {
- fixture.innerHTML = '';
- var node = fixture.querySelector('#target');
- assert.isTrue(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('');
+ assert.isTrue(checks['aria-required-children'].evaluate.apply(checkContext, params));
+ });
+
+ (shadowSupported ? it : xit)
+ ('should pass all existing required children in shadow tree when all required', function () {
+ fixture.innerHTML = '';
+
+ var target = document.querySelector('#target');
+ var shadowRoot = target.attachShadow({ mode: 'open' });
+ shadowRoot.innerHTML = 'Nothing here.
Textbox
';
+
+ var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
+ var virtualTarget = axe.utils.getNodeFromTree(tree[0], target);
+
+ var params = [target, undefined, virtualTarget];
+ assert.isTrue(checks['aria-required-children'].evaluate.apply(checkContext, params));
});
it('should pass one indirectly aria-owned child when one required', function () {
- fixture.innerHTML = '';
- var node = fixture.querySelector('#target');
- assert.isTrue(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('');
+ assert.isTrue(checks['aria-required-children'].evaluate.apply(checkContext, params));
});
it('should not break if aria-owns points to non-existent node', function () {
- fixture.innerHTML = '';
- var node = fixture.querySelector('#target');
- assert.isFalse(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('');
+ assert.isFalse(checks['aria-required-children'].evaluate.apply(checkContext, params));
});
it('should pass one existing aria-owned child when one required', function () {
- fixture.innerHTML = 'Nothing here.
';
- var node = fixture.querySelector('#target');
- assert.isTrue(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('Nothing here.
');
+ assert.isTrue(checks['aria-required-children'].evaluate.apply(checkContext, params));
});
it('should pass one existing required child when one required', function () {
- fixture.innerHTML = '';
- var node = fixture.querySelector('#target');
- assert.isTrue(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('');
+ assert.isTrue(checks['aria-required-children'].evaluate.apply(checkContext, params));
});
it('should pass one existing required child when one required because of implicit role', function () {
- fixture.innerHTML = '';
- var node = fixture.querySelector('#target');
- assert.isTrue(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('');
+ assert.isTrue(checks['aria-required-children'].evaluate.apply(checkContext, params));
});
it('should pass when a child with an implicit role is present', function () {
- fixture.innerHTML = '';
- var node = fixture.querySelector('#target');
- assert.isTrue(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('');
+ assert.isTrue(checks['aria-required-children'].evaluate.apply(checkContext, params));
});
it('should pass direct existing required children', function () {
- fixture.innerHTML = '';
- var node = fixture.querySelector('#target');
- assert.isTrue(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('');
+ assert.isTrue(checks['aria-required-children'].evaluate.apply(checkContext, params));
});
it('should pass indirect required children', function () {
- fixture.innerHTML = 'Just a regular ol p that contains a...
Nothing here.
';
- var node = fixture.querySelector('#target');
- assert.isTrue(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('Just a regular ol p that contains a...
Nothing here.
');
+ assert.isTrue(checks['aria-required-children'].evaluate.apply(checkContext, params));
});
it('should return true when a role has no required owned', function () {
- fixture.innerHTML = '';
- var node = fixture.querySelector('#target');
- assert.isTrue(checks['aria-required-children'].evaluate.call(checkContext, node));
+ var params = checkSetup('');
+ assert.isTrue(checks['aria-required-children'].evaluate.apply(checkContext, params));
});
});