diff --git a/lib/checks/aria/required-parent.js b/lib/checks/aria/required-parent.js index d227f0635d..5a4ef3614f 100644 --- a/lib/checks/aria/required-parent.js +++ b/lib/checks/aria/required-parent.js @@ -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; diff --git a/test/checks/aria/required-parent.js b/test/checks/aria/required-parent.js index aef8f598ae..e105231c9f 100644 --- a/test/checks/aria/required-parent.js +++ b/test/checks/aria/required-parent.js @@ -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, @@ -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 = '

Nothing here.

'; - var node = fixture.querySelector('#target'); - assert.isFalse(checks['aria-required-parent'].evaluate.call(checkContext, node)); + var params = checkSetup('

Nothing here.

'); + 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 = '
'; + + var shadowRoot = document.querySelector('#target').attachShadow({ mode: 'open' }); + shadowRoot.innerHTML = '

Nothing here.

'; + + 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 = '

Nothing here.

'; - var node = fixture.querySelector('#target'); - assert.isTrue(checks['aria-required-parent'].evaluate.call(checkContext, node)); + var snippet = '
'+ + '

Nothing here.

'; + 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 = '

Nothing here.

'; - var node = fixture.querySelector('#target'); - assert.isFalse(checks['aria-required-parent'].evaluate.call(checkContext, node)); + var params = checkSetup( + '
' + + '

Nothing here.

' + ); + 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 = '

Nothing here.

'; - var node = fixture.querySelector('#target'); - assert.isTrue(checks['aria-required-parent'].evaluate.call(checkContext, node)); + var params = checkSetup('

Nothing here.

'); + 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 = '

Nothing here.

'; - var node = fixture.querySelector('#target'); - assert.isTrue(checks['aria-required-parent'].evaluate.call(checkContext, node)); + var params = checkSetup('

Nothing here.

'); + assert.isTrue(checks['aria-required-parent'].evaluate.apply(checkContext, params)); }); + it('should pass when required parent is present', function () { - fixture.innerHTML = '

Nothing here.

'; - var node = fixture.querySelector('#target'); - assert.isTrue(checks['aria-required-parent'].evaluate.call(checkContext, node)); + var params = checkSetup('

Nothing here.

'); + 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 = '
'; + + var shadowRoot = document.querySelector('#parent').attachShadow({ mode: 'open' }); + shadowRoot.innerHTML = '

Nothing here.

'; + + 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 = '
'; + + var shadowRoot = document.querySelector('#parent').attachShadow({ mode: 'open' }); + shadowRoot.innerHTML = '

Nothing here.

'; + + 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)); + }); });