Skip to content

Commit

Permalink
feat(aria-required-parent): add Shadow DOM support
Browse files Browse the repository at this point in the history
Closes #422
  • Loading branch information
Marcy Sutton authored and marcysutton committed Jul 19, 2017
1 parent 9bf2870 commit 6ed29f0
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 20 deletions.
3 changes: 2 additions & 1 deletion lib/checks/aria/required-parent.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ function getAriaOwners(element) {
while (element) {
if (element.getAttribute('id')) {
const id = axe.commons.utils.escapeSelector(element.getAttribute('id'));
o = document.querySelector(`[aria-owns~=${id}]`);
let doc = axe.commons.dom.getRootNode(element);
o = doc.querySelector(`[aria-owns~=${id}]`);
if (o) { owners.push(o); }
}
element = element.parentElement;
Expand Down
85 changes: 66 additions & 19 deletions test/checks/aria/required-parent.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ describe('aria-required-parent', function () {
'use strict';

var fixture = document.getElementById('fixture');
var shadowSupported = axe.testUtils.shadowSupport.v1;

var checkContext = {
_data: null,
Expand All @@ -10,47 +11,93 @@ describe('aria-required-parent', function () {
}
};

var checkSetup = axe.testUtils.checkSetup;

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

it('should detect missing required parent', function () {
fixture.innerHTML = '<div><p role="listitem" id="target">Nothing here.</p></div>';
var node = fixture.querySelector('#target');
assert.isFalse(checks['aria-required-parent'].evaluate.call(checkContext, node));
var params = checkSetup('<div><p role="listitem" id="target">Nothing here.</p></div>');
assert.isFalse(checks['aria-required-parent'].evaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, ['list']);
});

(shadowSupported ? it : xit)
('should detect missing required parent across shadow boundary', function () {
fixture.innerHTML = '<div id="target"></div>';

var shadowRoot = document.querySelector('#target').attachShadow({ mode: 'open' });
shadowRoot.innerHTML = '<p role="listitem" id="target">Nothing here.</p>';

var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
var shadowContent = shadowRoot.querySelector('#target');
var virtualTarget = axe.utils.getNodeFromTree(tree[0], shadowContent);

var params = [shadowContent, undefined, virtualTarget];
assert.isFalse(checks['aria-required-parent'].evaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, ['list']);
});

it('should pass when required parent is present in an ancestral aria-owns context', function () {
fixture.innerHTML = '<div role="list" ><div aria-owns="parent"></div></div><div id="parent"><p role="listitem" id="target">Nothing here.</p></div>';
var node = fixture.querySelector('#target');
assert.isTrue(checks['aria-required-parent'].evaluate.call(checkContext, node));
var snippet = '<div role="list"><div aria-owns="parent"></div></div>'+
'<div id="parent"><p role="listitem" id="target">Nothing here.</p></div>';
var params = checkSetup(snippet);
assert.isTrue(checks['aria-required-parent'].evaluate.apply(checkContext, params));
});

it('should fail when wrong role is present in an aria-owns context', function () {
fixture.innerHTML = '<div role="menu" ><div aria-owns="target"></div></div><div><p role="listitem" id="target">Nothing here.</p></div>';
var node = fixture.querySelector('#target');
assert.isFalse(checks['aria-required-parent'].evaluate.call(checkContext, node));
var params = checkSetup(
'<div role="menu"><div aria-owns="target"></div></div>' +
'<div><p role="listitem" id="target">Nothing here.</p></div>'
);
assert.isFalse(checks['aria-required-parent'].evaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, ['list']);
});


it('should pass when required parent is present in an aria-owns context', function () {
fixture.innerHTML = '<div role="list" aria-owns="target"></div><div><p role="listitem" id="target">Nothing here.</p></div>';
var node = fixture.querySelector('#target');
assert.isTrue(checks['aria-required-parent'].evaluate.call(checkContext, node));
var params = checkSetup('<div role="list" aria-owns="target"></div><div><p role="listitem" id="target">Nothing here.</p></div>');
assert.isTrue(checks['aria-required-parent'].evaluate.apply(checkContext, params));
});

it('should pass when at least one required parent of multiple is present', function () {
fixture.innerHTML = '<div role="grid" ><p role="row" id="target">Nothing here.</p></div>';
var node = fixture.querySelector('#target');
assert.isTrue(checks['aria-required-parent'].evaluate.call(checkContext, node));
var params = checkSetup('<div role="grid"><p role="row" id="target">Nothing here.</p></div>');
assert.isTrue(checks['aria-required-parent'].evaluate.apply(checkContext, params));
});

it('should pass when required parent is present', function () {
fixture.innerHTML = '<div role="list" ><p role="listitem" id="target">Nothing here.</p></div>';
var node = fixture.querySelector('#target');
assert.isTrue(checks['aria-required-parent'].evaluate.call(checkContext, node));
var params = checkSetup('<div role="list"><p role="listitem" id="target">Nothing here.</p></div>');
assert.isTrue(checks['aria-required-parent'].evaluate.apply(checkContext, params));
});

(shadowSupported ? it : xit)
('should pass when required parent is present across shadow boundary', function () {
fixture.innerHTML = '<div role="list" id="parent"></div>';

var shadowRoot = document.querySelector('#parent').attachShadow({ mode: 'open' });
shadowRoot.innerHTML = '<p role="listitem" id="target">Nothing here.</p>';

var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
var shadowContent = shadowRoot.querySelector('#target');
var virtualTarget = axe.utils.getNodeFromTree(tree[0], shadowContent);

var params = [shadowContent, undefined, virtualTarget];
assert.isTrue(checks['aria-required-parent'].evaluate.apply(checkContext, params));
});

(shadowSupported ? it : xit)
('should fail when aria-owns context crosses shadow boundary', function () {
fixture.innerHTML = '<div id="parent"><div role="list" aria-owns="target"></div></div>';

var shadowRoot = document.querySelector('#parent').attachShadow({ mode: 'open' });
shadowRoot.innerHTML = '<p role="listitem" id="target">Nothing here.</p>';

var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
var shadowContent = shadowRoot.querySelector('#target');
var virtualTarget = axe.utils.getNodeFromTree(tree[0], shadowContent);

var params = [shadowContent, undefined, virtualTarget];
assert.isFalse(checks['aria-required-parent'].evaluate.apply(checkContext, params));
});
});

0 comments on commit 6ed29f0

Please sign in to comment.