Skip to content

Commit

Permalink
fix: Let findUp work on shadow root children (#447)
Browse files Browse the repository at this point in the history
* fix: Let findUp work on shadow root children

* fix: Solve a few tests

# Conflicts:
#	test/testutils.js

* Fix: Properly disable of shadow DOM checks

* test: Use findUp on host element
  • Loading branch information
WilcoFiers committed Jul 18, 2017
1 parent 2c0b075 commit 0f98481
Show file tree
Hide file tree
Showing 11 changed files with 70 additions and 62 deletions.
3 changes: 2 additions & 1 deletion lib/checks/label/explicit.js
@@ -1,6 +1,7 @@
if (node.getAttribute('id')) {
const root = axe.commons.dom.getRootNode(node);
const id = axe.commons.utils.escapeSelector(node.getAttribute('id'));
const label = document.querySelector(`label[for="${id}"]`);
const label = root.querySelector(`label[for="${id}"]`);

if (label) {
return !!axe.commons.text.accessibleText(label);
Expand Down
25 changes: 8 additions & 17 deletions lib/commons/dom/find-up.js
Expand Up @@ -9,33 +9,24 @@
* @return {HTMLElement|null} Either the matching HTMLElement or `null` if there was no match
*/
dom.findUp = function (element, target) {
'use strict';
/*jslint browser:true*/
let doc, matches,
parent = element;

var parent,
doc = axe.commons.dom.getRootNode(element),
matches;

matches = doc.querySelectorAll(target);
matches = axe.utils.toArray(matches);
if (doc === document && !matches.length) {
return null;
}

// recursively walk up the DOM, checking each parent node
parent = dom.getComposedParent(element);
while (parent && matches.indexOf(parent) === -1) {
parent = (parent.assignedSlot) ? parent.assignedSlot : parent.parentNode;
do {// recursively walk up the DOM, checking each parent node
parent = (parent.assignedSlot ? parent.assignedSlot : parent.parentNode);
if (parent && parent.nodeType === 11) {
matches = null;
parent = parent.host;
}
if (!matches) {
doc = axe.commons.dom.getRootNode(parent);
matches = doc.querySelectorAll(target);
matches = axe.utils.toArray(matches);
if (doc === document && !matches.length) {
return null;
}
}
}
} while (parent && !matches.includes(parent));

return parent;
};
4 changes: 2 additions & 2 deletions test/checks/lists/dlitem.js
Expand Up @@ -21,7 +21,7 @@ describe('dlitem', function () {
assert.isFalse(checks.dlitem.evaluate.apply(null, checkArgs));
});

(shadowSupport ? it : xit)('should return true in a shadow DOM pass', function () {
(shadowSupport.v1 ? it : xit)('should return true in a shadow DOM pass', function () {
var node = document.createElement('div');
node.innerHTML = '<dt>My list item </dt>';
var shadow = node.attachShadow({ mode: 'open' });
Expand All @@ -31,7 +31,7 @@ describe('dlitem', function () {
assert.isTrue(checks.dlitem.evaluate.apply(null, checkArgs));
});

(shadowSupport ? it : xit)('should return false in a shadow DOM fail', function () {
(shadowSupport.v1 ? it : xit)('should return false in a shadow DOM fail', function () {
var node = document.createElement('div');
node.innerHTML = '<dt>My list item </dt>';
var shadow = node.attachShadow({ mode: 'open' });
Expand Down
4 changes: 2 additions & 2 deletions test/checks/lists/has-listitem.js
Expand Up @@ -33,7 +33,7 @@ describe('has-listitem', function () {
assert.isFalse(checks['has-listitem'].evaluate.apply(null, checkArgs));
});

(shadowSupport ? it : xit)('should return true in a shadow DOM pass', function () {
(shadowSupport.v1 ? it : xit)('should return true in a shadow DOM pass', function () {
var node = document.createElement('div');
node.innerHTML = '<li>My list item </li>';
var shadow = node.attachShadow({ mode: 'open' });
Expand All @@ -43,7 +43,7 @@ describe('has-listitem', function () {
assert.isFalse(checks['has-listitem'].evaluate.apply(null, checkArgs));
});

(shadowSupport ? it : xit)('should return false in a shadow DOM fail', function () {
(shadowSupport.v1 ? it : xit)('should return false in a shadow DOM fail', function () {
var node = document.createElement('div');
node.innerHTML = '<p>Not a list</p>';
var shadow = node.attachShadow({ mode: 'open' });
Expand Down
4 changes: 2 additions & 2 deletions test/checks/lists/listitem.js
Expand Up @@ -33,7 +33,7 @@ describe('listitem', function () {
assert.isFalse(checks.listitem.evaluate.apply(null, checkArgs));
});

(shadowSupport ? it : xit)('should return true in a shadow DOM pass', function () {
(shadowSupport.v1 ? it : xit)('should return true in a shadow DOM pass', function () {
var node = document.createElement('div');
node.innerHTML = '<li>My list item </li>';
var shadow = node.attachShadow({ mode: 'open' });
Expand All @@ -43,7 +43,7 @@ describe('listitem', function () {
assert.isTrue(checks.listitem.evaluate.apply(null, checkArgs));
});

(shadowSupport ? it : xit)('should return false in a shadow DOM fail', function () {
(shadowSupport.v1 ? it : xit)('should return false in a shadow DOM fail', function () {
var node = document.createElement('div');
node.innerHTML = '<li>My list item </li>';
var shadow = node.attachShadow({ mode: 'open' });
Expand Down
4 changes: 2 additions & 2 deletions test/checks/lists/only-dlitems.js
Expand Up @@ -104,7 +104,7 @@ describe('only-dlitems', function () {
assert.isFalse(checks['only-dlitems'].evaluate.apply(checkContext, checkArgs));
});

(shadowSupport ? it : xit)('should return false in a shadow DOM pass', function () {
(shadowSupport.v1 ? it : xit)('should return false in a shadow DOM pass', function () {
var node = document.createElement('div');
node.innerHTML = '<dt>My list item </dt>';
var shadow = node.attachShadow({ mode: 'open' });
Expand All @@ -114,7 +114,7 @@ describe('only-dlitems', function () {
assert.isFalse(checks['only-dlitems'].evaluate.apply(checkContext, checkArgs));
});

(shadowSupport ? it : xit)('should return true in a shadow DOM fail', function () {
(shadowSupport.v1 ? it : xit)('should return true in a shadow DOM fail', function () {
var node = document.createElement('div');
node.innerHTML = '<p>Not a list</p>';
var shadow = node.attachShadow({ mode: 'open' });
Expand Down
4 changes: 2 additions & 2 deletions test/checks/lists/only-listitems.js
Expand Up @@ -97,7 +97,7 @@ describe('only-listitems', function () {
assert.isFalse(checks['only-listitems'].evaluate.apply(checkContext, checkArgs));
});

(shadowSupport ? it : xit)('should return false in a shadow DOM pass', function () {
(shadowSupport.v1 ? it : xit)('should return false in a shadow DOM pass', function () {
var node = document.createElement('div');
node.innerHTML = '<li>My list item </li>';
var shadow = node.attachShadow({ mode: 'open' });
Expand All @@ -107,7 +107,7 @@ describe('only-listitems', function () {
assert.isFalse(checks['only-listitems'].evaluate.apply(checkContext, checkArgs));
});

(shadowSupport ? it : xit)('should return true in a shadow DOM fail', function () {
(shadowSupport.v1 ? it : xit)('should return true in a shadow DOM fail', function () {
var node = document.createElement('div');
node.innerHTML = '<p>Not a list item</p>';
var shadow = node.attachShadow({ mode: 'open' });
Expand Down
4 changes: 2 additions & 2 deletions test/checks/lists/structured-dlitems.js
Expand Up @@ -49,7 +49,7 @@ describe('structured-dlitems', function () {
assert.isFalse(checks['structured-dlitems'].evaluate.apply(null, checkArgs));
});

(shadowSupport ? it : xit)('should return false in a shadow DOM pass', function () {
(shadowSupport.v1 ? it : xit)('should return false in a shadow DOM pass', function () {
var node = document.createElement('div');
node.innerHTML = '<dt>Grayhound bus</dt><dd>at dawn</dd>';
var shadow = node.attachShadow({ mode: 'open' });
Expand All @@ -59,7 +59,7 @@ describe('structured-dlitems', function () {
assert.isFalse(checks['structured-dlitems'].evaluate.apply(null, checkArgs));
});

(shadowSupport ? it : xit)('should return true in a shadow DOM fail', function () {
(shadowSupport.v1 ? it : xit)('should return true in a shadow DOM fail', function () {
var node = document.createElement('div');
node.innerHTML = '<dd>Galileo</dd><dt>Figaro</dt>';
var shadow = node.attachShadow({ mode: 'open' });
Expand Down
6 changes: 3 additions & 3 deletions test/checks/navigation/landmark.js
Expand Up @@ -24,7 +24,7 @@ describe('landmark', function () {
assert.isFalse(checks.landmark.evaluate.apply(null, checkArgs));
});

(shadowSupport ? it : xit)('should not automatically pass if there is a shadow tree', function () {
(shadowSupport.v1 ? it : xit)('should not automatically pass if there is a shadow tree', function () {
var node = document.createElement('div');
var shadow = node.attachShadow({ mode: 'open' });
shadow.innerHTML = '<div></div>';
Expand All @@ -33,7 +33,7 @@ describe('landmark', function () {
assert.isFalse(checks.landmark.evaluate.apply(null, checkArgs));
});

(shadowSupport ? it : xit)('should find elements inside shadow trees', function () {
(shadowSupport.v1 ? it : xit)('should find elements inside shadow trees', function () {
var node = document.createElement('div');
var shadow = node.attachShadow({ mode: 'open' });
shadow.innerHTML = '<main></main>';
Expand All @@ -42,7 +42,7 @@ describe('landmark', function () {
assert.isTrue(checks.landmark.evaluate.apply(null, checkArgs));
});

(shadowSupport ? it : xit)('should find elements slotted in shadow trees', function () {
(shadowSupport.v1 ? it : xit)('should find elements slotted in shadow trees', function () {
var node = document.createElement('div');
node.innerHTML = '<main></main>';
var shadow = node.attachShadow({ mode: 'open' });
Expand Down
22 changes: 9 additions & 13 deletions test/checks/shared/button-has-visible-text.js
Expand Up @@ -2,7 +2,7 @@ describe('button-has-visible-text', function () {
'use strict';

var fixture = document.getElementById('fixture');

var checkSetup = axe.testUtils.checkSetup;
var checkContext = {
_data: null,
data: function (d) {
Expand All @@ -16,32 +16,28 @@ describe('button-has-visible-text', function () {
});

it('should return false if button element is empty', function () {
fixture.innerHTML = '<button></button>';
var checkArgs = checkSetup('<button></button>', 'button');

var node = fixture.querySelector('button');
assert.isFalse(checks['button-has-visible-text'].evaluate.call(checkContext, node));
assert.isFalse(checks['button-has-visible-text'].evaluate.apply(checkContext, checkArgs));
});

it('should return true if a button element has text', function () {
fixture.innerHTML = '<button>Name</button>';
var checkArgs = checkSetup('<button>Name</button>', 'button');

var node = fixture.querySelector('button');
assert.isTrue(checks['button-has-visible-text'].evaluate.call(checkContext, node));
assert.isTrue(checks['button-has-visible-text'].evaluate.apply(checkContext, checkArgs));
assert.deepEqual(checkContext._data, 'Name');
});

it('should return true if ARIA button has text', function () {
fixture.innerHTML = '<div role="button">Text</div>';
var checkArgs = checkSetup('<div role="button">Text</div>>', '[role=button]');

var node = fixture.querySelector('div');
assert.isTrue(checks['button-has-visible-text'].evaluate.call(checkContext, node));
assert.isTrue(checks['button-has-visible-text'].evaluate.apply(checkContext, checkArgs));
assert.deepEqual(checkContext._data, 'Text');
});

it('should return false if ARIA button has no text', function () {
fixture.innerHTML = '<div role="button"></div>';
var checkArgs = checkSetup('<div role="button"></div>>', '[role=button]');

var node = fixture.querySelector('div');
assert.isFalse(checks['button-has-visible-text'].evaluate.call(checkContext, node));
assert.isFalse(checks['button-has-visible-text'].evaluate.apply(checkContext, checkArgs));
});
});
52 changes: 36 additions & 16 deletions test/commons/dom/find-up.js
Expand Up @@ -74,7 +74,18 @@ describe('dom.findUp', function () {
assert.equal(axe.commons.dom.findUp(el.actualNode, 'label'), fixture.firstChild);
});

it('should walk up the assigned slot', function () {
(shadowSupport.v0 ? it : xit)('should work on shadow root children', function () {
fixture.innerHTML = '<div role="list" id="something"></div>';
var shadow = fixture.querySelector('#something').createShadowRoot();

shadow.innerHTML = '<div role="listitem">item 1</div>';
var listItem = shadow.querySelector('[role=listitem]');

assert.equal(axe.commons.dom.findUp(listItem, '[role=list]'),
fixture.firstChild);
});

(shadowSupport.v1 ? it : xit)('should walk up the assigned slot', function () {
function createContentSlotted() {
var group = document.createElement('div');
group.innerHTML = '<div id="target" style="display:none;">Stuff<slot></slot></div>';
Expand All @@ -86,16 +97,15 @@ describe('dom.findUp', function () {
root.appendChild(div);
div.appendChild(createContentSlotted());
}
if (shadowSupport.v1) {
fixture.innerHTML = '<label><div><p><a>hello</a></p></div></label>';
makeShadowTree(fixture.querySelector('div'));
var tree = axe.utils.getFlattenedTree(fixture.firstChild);
var el = axe.utils.querySelectorAll(tree, 'a')[0];
assert.equal(axe.commons.dom.findUp(el.actualNode, 'label'), fixture.firstChild);
}

fixture.innerHTML = '<label><div><p><a>hello</a></p></div></label>';
makeShadowTree(fixture.querySelector('div'));
var tree = axe.utils.getFlattenedTree(fixture.firstChild);
var el = axe.utils.querySelectorAll(tree, 'a')[0];
assert.equal(axe.commons.dom.findUp(el.actualNode, 'label'), fixture.firstChild);
});

it('should walk up the shadow DOM', function () {
(shadowSupport.v1 ? it : xit)('should walk up the shadow DOM', function () {
function createContent() {
var group = document.createElement('div');
group.innerHTML = '<a>thing</a>';
Expand All @@ -107,12 +117,22 @@ describe('dom.findUp', function () {
root.appendChild(div);
div.appendChild(createContent());
}
if (shadowSupport.v1) {
fixture.innerHTML = '<label><div></div></label>';
makeShadowTree(fixture.querySelector('div'));
var tree = axe.utils.getFlattenedTree(fixture.firstChild);
var el = axe.utils.querySelectorAll(tree, 'a')[0];
assert.equal(axe.commons.dom.findUp(el.actualNode, 'label'), fixture.firstChild);
}

fixture.innerHTML = '<label><div></div></label>';
makeShadowTree(fixture.querySelector('div'));
var tree = axe.utils.getFlattenedTree(fixture.firstChild);
var el = axe.utils.querySelectorAll(tree, 'a')[0];
assert.equal(axe.commons.dom.findUp(el.actualNode, 'label'), fixture.firstChild);
});

(shadowSupport.v1 ? it : xit)('should work on shadow root children', function () {
fixture.innerHTML = '<div role="list" id="something"></div>';
var shadow = fixture.querySelector('#something').attachShadow({ mode: 'open' });

shadow.innerHTML = '<div role="listitem">item 1</div>';
var listItem = shadow.querySelector('[role=listitem]');

assert.equal(axe.commons.dom.findUp(listItem, '[role=list]'),
fixture.firstChild);
});
});

0 comments on commit 0f98481

Please sign in to comment.