Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add shadow DOM support to list checks #439

Merged
merged 4 commits into from
Jul 16, 2017
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/checks/lists/dlitem.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
return node.parentNode.tagName === 'DL';

var parent = axe.commons.dom.getComposedParent(node);
return parent.nodeName.toUpperCase() === 'DL';
11 changes: 2 additions & 9 deletions lib/checks/lists/has-listitem.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,2 @@
var children = node.children;
if (children.length === 0) { return true; }

for (var i = 0; i < children.length; i++) {
if (children[i].nodeName.toUpperCase() === 'LI') { return false; }
}

return true;

return virtualNode.children.every(({ actualNode }) =>
actualNode.nodeName.toUpperCase() !== 'LI');
10 changes: 4 additions & 6 deletions lib/checks/lists/listitem.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@

if (['UL', 'OL'].indexOf(node.parentNode.nodeName.toUpperCase()) !== -1) {
return true;
}

return node.parentNode.getAttribute('role') === 'list';
var parent = axe.commons.dom.getComposedParent(node);
return (['UL', 'OL'].includes(parent.nodeName.toUpperCase()) ||
(parent.getAttribute('role') || '').toLowerCase() === 'list');

19 changes: 8 additions & 11 deletions lib/checks/lists/only-dlitems.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
var child,
nodeName,
bad = [],
children = node.childNodes,
var bad = [],
permitted = ['STYLE', 'META', 'LINK', 'MAP', 'AREA', 'SCRIPT', 'DATALIST', 'TEMPLATE'],
hasNonEmptyTextNode = false;

for (var i = 0; i < children.length; i++) {
child = children[i];
var nodeName = child.nodeName.toUpperCase();
if (child.nodeType === 1 && nodeName !== 'DT' && nodeName !== 'DD' && permitted.indexOf(nodeName) === -1) {
bad.push(child);
} else if (child.nodeType === 3 && child.nodeValue.trim() !== '') {
virtualNode.children.forEach(({ actualNode }) => {
var nodeName = actualNode.nodeName.toUpperCase();
if (actualNode.nodeType === 1 && nodeName !== 'DT' && nodeName !== 'DD' && permitted.indexOf(nodeName) === -1) {
bad.push(actualNode);
} else if (actualNode.nodeType === 3 && actualNode.nodeValue.trim() !== '') {
hasNonEmptyTextNode = true;
}
}
});

if (bad.length) {
this.relatedNodes(bad);
}
Expand Down
19 changes: 8 additions & 11 deletions lib/checks/lists/only-listitems.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
var child,
nodeName,
bad = [],
children = node.childNodes,
var bad = [],
permitted = ['STYLE', 'META', 'LINK', 'MAP', 'AREA', 'SCRIPT', 'DATALIST', 'TEMPLATE'],
hasNonEmptyTextNode = false;

for (var i = 0; i < children.length; i++) {
child = children[i];
nodeName = child.nodeName.toUpperCase();
if (child.nodeType === 1 && nodeName !== 'LI' && permitted.indexOf(nodeName) === -1) {
bad.push(child);
} else if (child.nodeType === 3 && child.nodeValue.trim() !== '') {
virtualNode.children.forEach(({ actualNode }) => {
var nodeName = actualNode.nodeName.toUpperCase();
if (actualNode.nodeType === 1 && nodeName !== 'LI' && permitted.indexOf(nodeName) === -1) {
bad.push(actualNode);
} else if (actualNode.nodeType === 3 && actualNode.nodeValue.trim() !== '') {
hasNonEmptyTextNode = true;
}
}
});

if (bad.length) {
this.relatedNodes(bad);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/checks/lists/structured-dlitems.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
var children = node.children;
var children = virtualNode.children;
if ( !children || !children.length) { return false; }

var hasDt = false, hasDd = false, nodeName;
for (var i = 0; i < children.length; i++) {
nodeName = children[i].nodeName.toUpperCase();
nodeName = children[i].actualNode.nodeName.toUpperCase();
if (nodeName === 'DT') { hasDt = true; }
if (hasDt && nodeName === 'DD') { return false; }
if (nodeName === 'DD') { hasDd = true; }
Expand Down
5 changes: 4 additions & 1 deletion lib/commons/dom/get-composed-parent.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
*/
dom.getComposedParent = function getComposedParent (element) {
if (element.assignedSlot) {
return element.assignedSlot; // content of a shadow DOM slot
// NOTE: If the display of a slot element isn't 'contents',
// the slot shouldn't be ignored. Chrome does not support this (yet) so,
// we'll skip this part for now.
return getComposedParent(element.assignedSlot); // content of a shadow DOM slot
} else if (element.parentNode) {
var parentNode = element.parentNode;
if (parentNode.nodeType === 1) {
Expand Down
1 change: 1 addition & 0 deletions test/.jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"globals": {
"describe": true,
"it": true,
"xit": true,
"before": true,
"beforeEach": true,
"after": true,
Expand Down
22 changes: 14 additions & 8 deletions test/checks/lists/dlitem.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,32 @@ describe('dlitem', function () {
'use strict';

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

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

it('should pass if the dlitem has a parent <dl>', function () {
fixture.innerHTML = '<dl><dt id="target">My list item</dl>';
var node = fixture.querySelector('#target');

assert.isTrue(checks.dlitem.evaluate(node));

var checkArgs = checkSetup('<dl><dt id="target">My list item</dl>');

assert.isTrue(checks.dlitem.evaluate.apply(null, checkArgs));
});

it('should fail if the dlitem has an incorrect parent', function () {
fixture.innerHTML = '<video><dt id="target">My list item</video>';
var node = fixture.querySelector('#target');
var checkArgs = checkSetup('<video><dt id="target">My list item</video>');

assert.isFalse(checks.dlitem.evaluate(node));
assert.isFalse(checks.dlitem.evaluate.apply(null, checkArgs));
});

(shadowSupport ? it : xit)('should work with shadow DOM', function () {
var node = document.createElement('div');
node.innerHTML = '<dt>My list item </dt>';
var shadow = node.attachShadow({ mode: 'open' });
shadow.innerHTML = '<dl><slot></slot></dl>';

var checkArgs = checkSetup(node, 'dt');
assert.isTrue(checks.dlitem.evaluate.apply(null, checkArgs));
});
});
36 changes: 18 additions & 18 deletions test/checks/lists/has-listitem.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,45 @@ describe('has-listitem', function () {
'use strict';

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

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

it('should return true if the list has no contents', function () {
fixture.innerHTML = '<ol id="target"></ol>';
var node = fixture.querySelector('#target');

assert.isTrue(checks['has-listitem'].evaluate(node));

var checkArgs = checkSetup('<ol id="target"></ol>');

assert.isTrue(checks['has-listitem'].evaluate.apply(null, checkArgs));
});

it('should return true if the list has non-li contents with li children', function () {
fixture.innerHTML = '<ol id="target"><p>Not a list <ul><li>item</li></ul></p></ol>';
var node = fixture.querySelector('#target');

assert.isTrue(checks['has-listitem'].evaluate(node));

var checkArgs = checkSetup('<ol id="target"><p>Not a list <ul><li>item</li></ul></p></ol>');

assert.isTrue(checks['has-listitem'].evaluate.apply(null, checkArgs));
});

it('should return true if the list has non-li contents', function () {
fixture.innerHTML = '<ol id="target"><p>Not a list</p></ol>';
var node = fixture.querySelector('#target');

assert.isTrue(checks['has-listitem'].evaluate(node));

var checkArgs = checkSetup('<ol id="target"><p>Not a list</p></ol>');

assert.isTrue(checks['has-listitem'].evaluate.apply(null, checkArgs));
});

it('should return false if the list has at least one li', function () {
fixture.innerHTML = '<ol id="target"><li>A list</li><p>Not a list</p></ol>';
var node = fixture.querySelector('#target');
var checkArgs = checkSetup('<ol id="target"><li>A list</li><p>Not a list</p></ol>');

assert.isFalse(checks['has-listitem'].evaluate(node));
assert.isFalse(checks['has-listitem'].evaluate.apply(null, checkArgs));
});

(shadowSupport ? it : xit)('should work with shadow DOM', function () {
var node = document.createElement('div');
node.innerHTML = '<li>My list item </li>';
var shadow = node.attachShadow({ mode: 'open' });
shadow.innerHTML = '<ul><slot></slot></ul>';

var checkArgs = checkSetup(node, 'ul');
assert.isFalse(checks['has-listitem'].evaluate.apply(null, checkArgs));
});

});
34 changes: 19 additions & 15 deletions test/checks/lists/listitem.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,44 @@ describe('listitem', function () {
'use strict';

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

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

it('should pass if the listitem has a parent <ol>', function () {
fixture.innerHTML = '<ol><li id="target">My list item</li></ol>';
var node = fixture.querySelector('#target');

assert.isTrue(checks.listitem.evaluate(node));
var checkArgs = checkSetup('<ol><li id="target">My list item</li></ol>');

assert.isTrue(checks.listitem.evaluate.apply(null, checkArgs));
});

it('should pass if the listitem has a parent <ul>', function () {
fixture.innerHTML = '<ul><li id="target">My list item</li></ul>';
var node = fixture.querySelector('#target');

assert.isTrue(checks.listitem.evaluate(node));
var checkArgs = checkSetup('<ul><li id="target">My list item</li></ul>');

assert.isTrue(checks.listitem.evaluate.apply(null, checkArgs));
});

it('should pass if the listitem has a parent role=list', function () {
fixture.innerHTML = '<div role="list"><li id="target">My list item</li></div>';
var node = fixture.querySelector('#target');

assert.isTrue(checks.listitem.evaluate(node));
var checkArgs = checkSetup('<div role="list"><li id="target">My list item</li></div>');

assert.isTrue(checks.listitem.evaluate.apply(null, checkArgs));
});

it('should fail if the listitem has an incorrect parent', function () {
fixture.innerHTML = '<div><li id="target">My list item</li></div>';
var node = fixture.querySelector('#target');
var checkArgs = checkSetup('<div><li id="target">My list item</li></div>');

assert.isFalse(checks.listitem.evaluate.apply(null, checkArgs));
});

assert.isFalse(checks.listitem.evaluate(node));
(shadowSupport ? it : xit)('should work with shadow DOM', function () {
var node = document.createElement('div');
node.innerHTML = '<li>My list item </li>';
var shadow = node.attachShadow({ mode: 'open' });
shadow.innerHTML = '<ul><slot></slot></ul>';

var checkArgs = checkSetup(node, 'li');
assert.isTrue(checks.listitem.evaluate.apply(null, checkArgs));
});
});
Loading