Skip to content

Commit f0fe551

Browse files
author
Marcy Sutton
committed
fix: complete shadow support for color matches
1 parent 6a5f3f1 commit f0fe551

File tree

2 files changed

+132
-43
lines changed

2 files changed

+132
-43
lines changed

lib/rules/color-contrast-matches.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
var nodeName = node.nodeName.toUpperCase(),
44
nodeType = node.type,
5-
doc = document;
5+
doc = axe.commons.dom.getRootNode(node);
66

77
if (node.getAttribute('aria-disabled') === 'true' || axe.commons.dom.findUp(node, '[aria-disabled="true"]')) {
88
return false;
@@ -45,9 +45,9 @@ if (nodeName === 'LABEL' || nodeParentLabel) {
4545
return false;
4646
}
4747

48-
var candidate = node.querySelector('input:not([type="hidden"]):not([type="image"])' +
48+
var candidate = axe.utils.querySelectorAll(virtualNode, 'input:not([type="hidden"]):not([type="image"])' +
4949
':not([type="button"]):not([type="submit"]):not([type="reset"]), select, textarea');
50-
if (candidate && candidate.disabled) {
50+
if (candidate.length && candidate[0].actualNode.disabled) {
5151
return false;
5252
}
5353

@@ -66,15 +66,15 @@ if (axe.commons.text.visible(virtualNode, false, true) === '') {
6666
}
6767

6868
var range = document.createRange(),
69-
childNodes = node.childNodes,
69+
childNodes = virtualNode.children,
7070
length = childNodes.length,
7171
child, index;
7272

7373
for (index = 0; index < length; index++) {
7474
child = childNodes[index];
7575

76-
if (child.nodeType === 3 && axe.commons.text.sanitize(child.nodeValue) !== '') {
77-
range.selectNodeContents(child);
76+
if (child.actualNode.nodeType === 3 && axe.commons.text.sanitize(child.actualNode.nodeValue) !== '') {
77+
range.selectNodeContents(child.actualNode);
7878
}
7979
}
8080

test/rule-matches/color-contrast-matches.js

Lines changed: 126 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* global xit */
21
describe('color-contrast-matches', function () {
32
'use strict';
43

@@ -72,86 +71,99 @@ describe('color-contrast-matches', function () {
7271
it('should match <input type="text">', function () {
7372
fixture.innerHTML = '<input type="text">';
7473
var target = fixture.querySelector('input');
75-
assert.isTrue(rule.matches(target));
74+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
75+
assert.isTrue(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
7676
});
7777

7878
it('should not match <input type="hidden">', function () {
7979
fixture.innerHTML = '<input type="hidden">';
8080
var target = fixture.querySelector('input');
81-
assert.isFalse(rule.matches(target));
81+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
82+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
8283
});
8384

8485
it('should not match <input type="checkbox">', function () {
8586
fixture.innerHTML = '<input type="checkbox">';
8687
var target = fixture.querySelector('input');
87-
assert.isFalse(rule.matches(target));
88+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
89+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
8890
});
8991

9092
it('should not match <input type="radio">', function () {
9193
fixture.innerHTML = '<input type="radio">';
9294
var target = fixture.querySelector('input');
93-
assert.isFalse(rule.matches(target));
95+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
96+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
9497
});
9598

9699
it('should not match <input type="color">', function () {
97100
fixture.innerHTML = '<input type="color">';
98101
var target = fixture.querySelector('input');
102+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
99103
// Some browsers will fallback to type=text for unknown input types (looking at you IE)
100104
if (target.type === 'color') {
101-
assert.isFalse(rule.matches(target));
105+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
102106
}
103107
});
104108

105109
it('should not match <input type="range">', function () {
106110
fixture.innerHTML = '<input type="range">';
107111
var target = fixture.querySelector('input');
112+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
108113
// Some browsers will fallback to type=text for unknown input types (looking at you IE)
109114
if (target.type === 'range') {
110-
assert.isFalse(rule.matches(target));
115+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
111116
}
112117
});
113118

114119
it('should match <select> with options', function () {
115120
fixture.innerHTML = '<select><option>Hello</option></select>';
116121
var target = fixture.querySelector('select');
117-
assert.isTrue(rule.matches(target));
122+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
123+
assert.isTrue(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
118124
});
119125

120126
it('should not match <select> with no options', function () {
121127
fixture.innerHTML = '<select></select>';
122128
var target = fixture.querySelector('select');
123-
assert.isFalse(rule.matches(target));
129+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
130+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
124131
});
125132

126133
it('should match <textarea>', function () {
127134
fixture.innerHTML = '<textarea></textarea>';
128135
var target = fixture.querySelector('textarea');
129-
assert.isTrue(rule.matches(target));
136+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
137+
assert.isTrue(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
130138
});
131139

132140
it('should not match <option>', function () {
133141
fixture.innerHTML = '<select><option>hi</option></select>';
134142
var target = fixture.querySelector('option');
135-
assert.isFalse(rule.matches(target));
143+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
144+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
136145
});
137146

138147
it('should not match inputs that are disabled', function () {
139148
fixture.innerHTML = '<input type="text" disabled>';
140149
var target = fixture.querySelector('input');
141-
assert.isFalse(rule.matches(target));
150+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
151+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
142152

143153
});
144154

145155
it('should not match <textarea disabled>', function () {
146156
fixture.innerHTML = '<textarea disabled></textarea>';
147157
var target = fixture.querySelector('textarea');
148-
assert.isFalse(rule.matches(target));
158+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
159+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
149160
});
150161

151162
it('should not match <select> with options', function () {
152163
fixture.innerHTML = '<select disabled><option>Hello</option></select>';
153164
var target = fixture.querySelector('select');
154-
assert.isFalse(rule.matches(target));
165+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
166+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
155167
});
156168

157169
it('should match <button>', function () {
@@ -164,97 +176,174 @@ describe('color-contrast-matches', function () {
164176
it('should not match <button disabled>', function () {
165177
fixture.innerHTML = '<button disabled>hi</button>';
166178
var target = fixture.querySelector('button');
167-
assert.isFalse(rule.matches(target));
179+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
180+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
168181
});
169182

170183
it('should not match <button disabled><span></span></button>', function () {
171184
fixture.innerHTML = '<button disabled><span>Hi</span></button>';
172185
var target = fixture.querySelector('button');
173-
assert.isFalse(rule.matches(target.querySelector('span')));
186+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
187+
assert.isFalse(rule.matches(target.querySelector('span'), axe.utils.getNodeFromTree(tree[0], target.querySelector('span'))));
174188
});
175189

176190
it('should not match <button disabled><span><i></i></span></button>', function () {
177191
fixture.innerHTML = '<button disabled><span><i>Hi</i></span></button>';
178192
var target = fixture.querySelector('button');
179-
assert.isFalse(rule.matches(target.querySelector('i')));
193+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
194+
assert.isFalse(rule.matches(target.querySelector('i'), axe.utils.getNodeFromTree(tree[0], target.querySelector('i'))));
180195
});
181196

182197
it('should not match <input type=image>', function () {
183198
fixture.innerHTML = '<input type="image">';
184199
var target = fixture.querySelector('input');
185-
assert.isFalse(rule.matches(target));
200+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
201+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
186202
});
187203

188204
it('should not match a disabled input\'s label - explicit label', function () {
189205
fixture.innerHTML = '<label for="t1">Test</label><input type="text" id="t1" disabled>';
190206
var target = fixture.querySelector('label');
191-
assert.isFalse(rule.matches(target));
207+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
208+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
192209
});
193210

194211
it('should not match a disabled input\'s label - implicit label (input)', function () {
195212
fixture.innerHTML = '<label>Test<input type="text" disabled></label>';
196213
var target = fixture.querySelector('label');
197-
assert.isFalse(rule.matches(target));
214+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
215+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
198216
});
199217

200218
it('should not match a disabled input\'s label - implicit label (textarea)', function () {
201219
fixture.innerHTML = '<label>Test<textarea disabled>Hi</textarea></label>';
202220
var target = fixture.querySelector('label');
203-
assert.isFalse(rule.matches(target));
221+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
222+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
204223
});
205224

206225
it('should not match a disabled input\'s label - implicit label (select)', function () {
207226
fixture.innerHTML = '<label>Test<select disabled><option>Test</option></select></label>';
208227
var target = fixture.querySelector('label');
209-
assert.isFalse(rule.matches(target));
228+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
229+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
210230
});
211231

212232
it('should not match a disabled input\'s label - aria-labelledby', function () {
213233
fixture.innerHTML = '<div id="t1">Test</div><input type="text" aria-labelledby="bob t1 fred" disabled>';
214234
var target = fixture.querySelector('div');
215-
assert.isFalse(rule.matches(target));
235+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
236+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
216237
});
217238

218239
it('should not match aria-disabled=true', function () {
219240
fixture.innerHTML = '<div aria-disabled="true">hi</div>';
220241
var target = fixture.querySelector('div');
221-
assert.isFalse(rule.matches(target));
242+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
243+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
222244
});
223245

224246
it('should not match a descendant of aria-disabled=true', function () {
225247
fixture.innerHTML = '<div aria-disabled="true"><span>hi</span></div>';
226248
var target = fixture.querySelector('span');
227-
assert.isFalse(rule.matches(target));
249+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
250+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
228251
});
229252

230253
it('should not match a descendant of a disabled fieldset', function () {
231254
fixture.innerHTML = '<fieldset disabled><label>hi <input type="checkbox"></label></fieldset>';
232255
var target = fixture.querySelector('label');
233-
assert.isFalse(rule.matches(target));
256+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
257+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
234258
});
235259

236260
it('should not match a descendant of an explicit label for a disabled input', function () {
237261
fixture.innerHTML = '<input id="input" type="checkbox" disabled><label for="input"><span>hi</span></label>';
238262
var target = fixture.querySelector('span');
239-
assert.isFalse(rule.matches(target));
263+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
264+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
240265
});
241266

242267
it('should not match a descendant of an implicit label for a disabled input', function () {
243268
fixture.innerHTML = '<label for="input"><span>hi</span><input id="input" type="checkbox" disabled></label>';
244269
var target = fixture.querySelector('span');
245-
assert.isFalse(rule.matches(target));
270+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
271+
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(tree[0], target)));
246272
});
247273

248-
(shadowSupport ? it : xit)
249-
('should match a descendant of an element across a shadow boundary', function () {
250-
fixture.innerHTML = '<div id="parent" style="background-color: #000;">' +
274+
if (shadowSupport) {
275+
it('should match a descendant of an element across a shadow boundary', function () {
276+
fixture.innerHTML = '<div id="parent" style="background-color: #000;">' +
277+
'</div>';
278+
279+
var shadowRoot = document.getElementById('parent').attachShadow({ mode: 'open' });
280+
shadowRoot.innerHTML = '<div id="shadowTarget" style="color: #333">Text</div>';
281+
282+
var shadowTarget = fixture.firstChild.shadowRoot.querySelector('#shadowTarget');
283+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
284+
assert.isTrue(rule.matches(shadowTarget, axe.utils.getNodeFromTree(tree[0], shadowTarget)));
285+
});
286+
287+
it('should look at the correct root node when looking up an explicit label and disabled input', function () {
288+
fixture.innerHTML = '<div id="parent">'+
289+
'<input id="input">' +
251290
'</div>';
252291

253-
var shadowRoot = document.getElementById('parent').attachShadow({ mode: 'open' });
254-
shadowRoot.innerHTML = '<div id="shadowTarget" style="color: #333">Text</div>';
292+
var shadowRoot = document.getElementById('parent').attachShadow({ mode: 'open' });
293+
shadowRoot.innerHTML = '<div id="shadowParent">' +
294+
'<label for="input" id="shadowLabel">Label</label>' +
295+
'<input id="input" disabled>' +
296+
'</div>';
255297

256-
var shadowTarget = fixture.firstChild.shadowRoot.querySelector('#shadowTarget');
257-
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
258-
assert.isTrue(rule.matches(shadowTarget, axe.utils.getNodeFromTree(tree[0], shadowTarget)));
259-
});
298+
var shadowLabel = fixture.firstChild.shadowRoot.querySelector('#shadowLabel');
299+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
300+
assert.isFalse(rule.matches(shadowLabel, axe.utils.getNodeFromTree(tree[0], shadowLabel)));
301+
});
302+
303+
it('should look at the correct root node when looking up implicit label and disabled input', function () {
304+
fixture.innerHTML = '<div id="parent">'+
305+
'<input id="input">' +
306+
'</div>';
307+
308+
var shadowRoot = document.getElementById('parent').attachShadow({ mode: 'open' });
309+
shadowRoot.innerHTML = '<div id="shadowParent">' +
310+
'<label for="input" id="shadowLabel">Label' +
311+
'<input id="input" disabled></label>' +
312+
'</div>';
313+
314+
var shadowLabel = fixture.firstChild.shadowRoot.querySelector('#shadowLabel');
315+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
316+
assert.isFalse(rule.matches(shadowLabel, axe.utils.getNodeFromTree(tree[0], shadowLabel)));
317+
});
318+
319+
it('should look at the correct root node for a disabled control\'s label associated w/ aria-labelledby', function () {
320+
fixture.innerHTML = '<div id="parent">'+
321+
'<input id="input">' +
322+
'</div>';
323+
324+
var shadowRoot = document.getElementById('parent').attachShadow({ mode: 'open' });
325+
shadowRoot.innerHTML = '<div id="shadowParent">' +
326+
'<label id="shadowLabel">Label</label>' +
327+
'<input aria-labelledby="shadowLabel" disabled>' +
328+
'</div>';
329+
330+
var shadowLabel = fixture.firstChild.shadowRoot.querySelector('#shadowLabel');
331+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
332+
assert.isFalse(rule.matches(shadowLabel, axe.utils.getNodeFromTree(tree[0], shadowLabel)));
333+
});
334+
335+
it('should look at the children of a virtual node for overlap', function () {
336+
fixture.innerHTML = '<div id="parent">'+
337+
'<div id="firstChild" style="background-color: #ccc; color: #fff;"></div>' +
338+
'</div>';
339+
340+
var shadowRoot = document.getElementById('firstChild').attachShadow({ mode: 'open' });
341+
shadowRoot.innerHTML = 'Some text' +
342+
'<p style="color: #fff;" id="shadowTarget">Other text</p>';
343+
344+
var firstChild = fixture.querySelector('#firstChild');
345+
var tree = axe._tree = axe.utils.getFlattenedTree(fixture);
346+
assert.isTrue(rule.matches(firstChild, axe.utils.getNodeFromTree(tree[0], firstChild)));
347+
});
348+
}
260349
});

0 commit comments

Comments
 (0)