UNPKG

20.1 kBJavaScriptView Raw
1/*
2 taken from https://github.com/dperini/nwmatcher/blob/master/test/scotch/test.js
3*/
4
5var DomUtils = require("htmlparser2").DomUtils,
6 helper = require("../tools/helper.js"),
7 assert = require("assert"),
8 path = require("path"),
9 document = helper.getDocument(path.join(__dirname, "test.html")),
10 CSSselect = helper.CSSselect;
11
12//Prototype's `$` function
13function getById(element){
14 if(arguments.length === 1){
15 if(typeof element === "string"){
16 return DomUtils.getElementById(element, document);
17 }
18 return element;
19 }
20 else return Array.prototype.map.call(arguments, function(elem){
21 return getById(elem);
22 });
23}
24
25//NWMatcher methods
26var select = function(query, doc){
27 if(arguments.length === 1 || typeof doc === "undefined") doc = document;
28 else if(typeof doc === "string") doc = select(doc);
29 return CSSselect(query, doc);
30}, match = CSSselect.is;
31
32var validators = {
33 assert: assert,
34 assertEqual: assert.equal,
35 assertEquivalent: assert.deepEqual,
36 refute: function refute(a, msg){
37 assert(!a, msg);
38 },
39 assertThrowsException: function(){} //not implemented
40};
41
42var runner = {
43 __name: "",
44 addGroup: function(name){
45 this.__name = name;
46 return this;
47 },
48 addTests: function(_, tests){
49 if(this.__name){
50 describe(this.__name, run);
51 this.__name = "";
52 } else run();
53
54 function run(){
55 Object.keys(tests).forEach(function(name){
56 it(name, function(){
57 tests[name].call(validators);
58 });
59 });
60 }
61 }
62}
63
64var RUN_BENCHMARKS = false;
65//The tests...
66(function(runner){
67 runner.addGroup("Basic Selectors").addTests(null, {
68 "*": function(){
69 //Universal selector
70 var results = [], nodes = document.getElementsByTagName("*"), index = 0, length = nodes.length, node;
71 //Collect all element nodes, excluding comments (IE)
72 for(; index < length; index++){
73 if((node = nodes[index]).tagName !== "!"){
74 results[results.length] = node;
75 }
76 }
77 this.assertEquivalent(select("*"), results, "Comment nodes should be ignored.");
78 },
79 "E": function(){
80 //Type selector
81 var results = [], index = 0, nodes = document.getElementsByTagName("li");
82 while((results[index] = nodes[index++])){}
83 results.length--;
84 // this.assertEquivalent(select("li"), results); //TODO
85 this.assertEqual(select("strong", getById("fixtures"))[0], getById("strong"));
86 this.assertEquivalent(select("nonexistent"), []);
87 },
88 "#id": function(){
89 //ID selector
90 this.assertEqual(select("#fixtures")[0], getById("fixtures"));
91 this.assertEquivalent(select("nonexistent"), []);
92 this.assertEqual(select("#troubleForm")[0], getById("troubleForm"));
93 },
94 ".class": function(){
95 //Class selector
96 this.assertEquivalent(select(".first"), getById('p', 'link_1', 'item_1'));
97 this.assertEquivalent(select(".second"), []);
98 },
99 "E#id": function(){
100 this.assertEqual(select("strong#strong")[0], getById("strong"));
101 this.assertEquivalent(select("p#strong"), []);
102 },
103 "E.class": function(){
104 var secondLink = getById("link_2");
105 this.assertEquivalent(select('a.internal'), getById('link_1', 'link_2'));
106 this.assertEqual(select('a.internal.highlight')[0], secondLink);
107 this.assertEqual(select('a.highlight.internal')[0], secondLink);
108 this.assertEquivalent(select('a.highlight.internal.nonexistent'), []);
109 },
110 "#id.class": function(){
111 var secondLink = getById('link_2');
112 this.assertEqual(select('#link_2.internal')[0], secondLink);
113 this.assertEqual(select('.internal#link_2')[0], secondLink);
114 this.assertEqual(select('#link_2.internal.highlight')[0], secondLink);
115 this.assertEquivalent(select('#link_2.internal.nonexistent'), []);
116 },
117 "E#id.class": function(){
118 var secondLink = getById('link_2');
119 this.assertEqual(select('a#link_2.internal')[0], secondLink);
120 this.assertEqual(select('a.internal#link_2')[0], secondLink);
121 this.assertEqual(select('li#item_1.first')[0], getById("item_1"));
122 this.assertEquivalent(select('li#item_1.nonexistent'), []);
123 this.assertEquivalent(select('li#item_1.first.nonexistent'), []);
124 }
125 });
126
127 runner.addGroup("Attribute Selectors").addTests(null, {
128 "[foo]": function(){
129 this.assertEquivalent(select('[href]', document.body), select('a[href]', document.body));
130 this.assertEquivalent(select('[class~=internal]'), select('a[class~="internal"]'));
131 this.assertEquivalent(select('[id]'), select('*[id]'));
132 this.assertEquivalent(select('[type=radio]'), getById('checked_radio', 'unchecked_radio'));
133 this.assertEquivalent(select('[type=checkbox]'), select('*[type=checkbox]'));
134 this.assertEquivalent(select('[title]'), getById('with_title', 'commaParent'));
135 this.assertEquivalent(select('#troubleForm [type=radio]'), select('#troubleForm *[type=radio]'));
136 this.assertEquivalent(select('#troubleForm [type]'), select('#troubleForm *[type]'));
137 },
138 "E[foo]": function(){
139 this.assertEquivalent(select('h1[class]'), select('#fixtures h1'), "h1[class]");
140 this.assertEquivalent(select('h1[CLASS]'), select('#fixtures h1'), "h1[CLASS]");
141 this.assertEqual(select('li#item_3[class]')[0], getById('item_3'), "li#item_3[class]");
142 this.assertEquivalent(select('#troubleForm2 input[name="brackets[5][]"]'), getById('chk_1', 'chk_2'));
143 //Brackets in attribute value
144 this.assertEqual(select('#troubleForm2 input[name="brackets[5][]"]:checked')[0], getById('chk_1'));
145 //Space in attribute value
146 this.assertEqual(select('cite[title="hello world!"]')[0], getById('with_title'));
147 //Namespaced attributes
148 // this.assertEquivalent(select('[xml:lang]'), [document.documentElement, getById("item_3")]);
149 // this.assertEquivalent(select('*[xml:lang]'), [document.documentElement, getById("item_3")]);
150 },
151 'E[foo="bar"]': function(){
152 this.assertEquivalent(select('a[href="#"]'), getById('link_1', 'link_2', 'link_3'));
153 this.assertThrowsException(/Error/, function(){
154 select('a[href=#]');
155 });
156 this.assertEqual(select('#troubleForm2 input[name="brackets[5][]"][value="2"]')[0], getById('chk_2'));
157 },
158 'E[foo~="bar"]': function(){
159 this.assertEquivalent(select('a[class~="internal"]'), getById('link_1', 'link_2'), "a[class~=\"internal\"]");
160 this.assertEquivalent(select('a[class~=internal]'), getById('link_1', 'link_2'), "a[class~=internal]");
161 this.assertEqual(select('a[class~=external][href="#"]')[0], getById('link_3'), 'a[class~=external][href="#"]');
162 },/*
163 'E[foo|="en"]': function(){
164 this.assertEqual(select('*[xml:lang|="es"]')[0], getById('item_3'));
165 this.assertEqual(select('*[xml:lang|="ES"]')[0], getById('item_3'));
166 },*/
167 'E[foo^="bar"]': function(){
168 this.assertEquivalent(select('div[class^=bro]'), getById('father', 'uncle'), 'matching beginning of string');
169 this.assertEquivalent(select('#level1 *[id^="level2_"]'), getById('level2_1', 'level2_2', 'level2_3'));
170 this.assertEquivalent(select('#level1 *[id^=level2_]'), getById('level2_1', 'level2_2', 'level2_3'));
171 if(RUN_BENCHMARKS){
172 this.wait(function(){
173 this.benchmark(function(){
174 select('#level1 *[id^=level2_]');
175 }, 1000);
176 }, 500);
177 }
178 },
179 'E[foo$="bar"]': function(){
180 this.assertEquivalent(select('div[class$=men]'), getById('father', 'uncle'), 'matching end of string');
181 this.assertEquivalent(select('#level1 *[id$="_1"]'), getById('level2_1', 'level3_1'));
182 this.assertEquivalent(select('#level1 *[id$=_1]'), getById('level2_1', 'level3_1'));
183 if(RUN_BENCHMARKS){
184 this.wait(function(){
185 this.benchmark(function(){
186 select('#level1 *[id$=_1]');
187 }, 1000);
188 }, 500);
189 }
190 },
191 'E[foo*="bar"]': function(){
192 this.assertEquivalent(select('div[class*="ers m"]'), getById('father', 'uncle'), 'matching substring');
193 this.assertEquivalent(select('#level1 *[id*="2"]'), getById('level2_1', 'level3_2', 'level2_2', 'level2_3'));
194 this.assertThrowsException(/Error/, function(){
195 select('#level1 *[id*=2]');
196 });
197 if(RUN_BENCHMARKS){
198 this.wait(function(){
199 this.benchmark(function(){
200 select('#level1 *[id*=2]');
201 }, 1000);
202 }, 500);
203 }
204 },
205
206 // *** these should throw SYNTAX_ERR ***
207
208 'E[id=-1]': function(){
209 this.assertThrowsException(/Error/, function(){
210 select('#level1 *[id=-1]');
211 });
212 if(RUN_BENCHMARKS){
213 this.wait(function(){
214 this.benchmark(function(){
215 select('#level1 *[id=9]');
216 }, 1000);
217 }, 500);
218 }
219 },
220 'E[class=-45deg]': function(){
221 this.assertThrowsException(/Error/, function(){
222 select('#level1 *[class=-45deg]');
223 });
224 if(RUN_BENCHMARKS){
225 this.wait(function(){
226 this.benchmark(function(){
227 select('#level1 *[class=-45deg]');
228 }, 1000);
229 }, 500);
230 }
231 },
232 'E[class=8mm]': function(){
233 this.assertThrowsException(/Error/, function(){
234 select('#level1 *[class=8mm]');
235 });
236 if(RUN_BENCHMARKS){
237 this.wait(function(){
238 this.benchmark(function(){
239 select('#level1 *[class=8mm]');
240 }, 1000);
241 }, 500);
242 }
243 }
244
245 });
246
247 runner.addGroup("Structural pseudo-classes").addTests(null, {
248 "E:first-child": function(){
249 this.assertEqual(select('#level1>*:first-child')[0], getById('level2_1'));
250 this.assertEquivalent(select('#level1 *:first-child'), getById('level2_1', 'level3_1', 'level_only_child'));
251 this.assertEquivalent(select('#level1>div:first-child'), []);
252 this.assertEquivalent(select('#level1 span:first-child'), getById('level2_1', 'level3_1'));
253 this.assertEquivalent(select('#level1:first-child'), []);
254 if(RUN_BENCHMARKS){
255 this.wait(function(){
256 this.benchmark(function(){
257 select('#level1 *:first-child');
258 }, 1000);
259 }, 500);
260 }
261 },
262 "E:last-child": function(){
263 this.assertEqual(select('#level1>*:last-child')[0], getById('level2_3'));
264 this.assertEquivalent(select('#level1 *:last-child'), getById('level3_2', 'level_only_child', 'level2_3'));
265 this.assertEqual(select('#level1>div:last-child')[0], getById('level2_3'));
266 this.assertEqual(select('#level1 div:last-child')[0], getById('level2_3'));
267 this.assertEquivalent(select('#level1>span:last-child'), []);
268 if(RUN_BENCHMARKS){
269 this.wait(function(){
270 this.benchmark(function(){
271 select('#level1 *:last-child');
272 }, 1000);
273 }, 500);
274 }
275 },
276 "E:nth-child(n)": function(){
277 this.assertEqual(select('#p *:nth-child(3)')[0], getById('link_2'));
278 this.assertEqual(select('#p a:nth-child(3)')[0], getById('link_2'), 'nth-child');
279 this.assertEquivalent(select('#list > li:nth-child(n+2)'), getById('item_2', 'item_3'));
280 this.assertEquivalent(select('#list > li:nth-child(-n+2)'), getById('item_1', 'item_2'));
281 },
282 "E:nth-of-type(n)": function(){
283 this.assertEqual(select('#p a:nth-of-type(2)')[0], getById('link_2'), 'nth-of-type');
284 this.assertEqual(select('#p a:nth-of-type(1)')[0], getById('link_1'), 'nth-of-type');
285 },
286 "E:nth-last-of-type(n)": function(){
287 this.assertEqual(select('#p a:nth-last-of-type(1)')[0], getById('link_2'), 'nth-last-of-type');
288 },
289 "E:first-of-type": function(){
290 this.assertEqual(select('#p a:first-of-type')[0], getById('link_1'), 'first-of-type');
291 },
292 "E:last-of-type": function(){
293 this.assertEqual(select('#p a:last-of-type')[0], getById('link_2'), 'last-of-type');
294 },
295 "E:only-child": function(){
296 this.assertEqual(select('#level1 *:only-child')[0], getById('level_only_child'));
297 //Shouldn't return anything
298 this.assertEquivalent(select('#level1>*:only-child'), []);
299 this.assertEquivalent(select('#level1:only-child'), []);
300 this.assertEquivalent(select('#level2_2 :only-child:not(:last-child)'), []);
301 this.assertEquivalent(select('#level2_2 :only-child:not(:first-child)'), []);
302 if(RUN_BENCHMARKS){
303 this.wait(function(){
304 this.benchmark(function(){
305 select('#level1 *:only-child');
306 }, 1000);
307 }, 500);
308 }
309 },
310 "E:empty": function(){
311 getById('level3_1').children = [];
312 if(document.createEvent){
313 this.assertEquivalent(select('#level1 *:empty'), getById('level3_1', 'level3_2', 'level2_3'), '#level1 *:empty');
314 this.assertEquivalent(select('#level_only_child:empty'), [], 'newlines count as content!');
315 }else{
316 this.assertEqual(select('#level3_1:empty')[0], getById('level3_1'), 'IE forced empty content!');
317 //this.skip("IE forced empty content!");
318 }
319 //Shouldn't return anything
320 this.assertEquivalent(select('span:empty > *'), []);
321 }
322 });
323
324 runner.addTests(null, {
325 "E:not(s)": function(){
326 //Negation pseudo-class
327 this.assertEquivalent(select('a:not([href="#"])'), []);
328 this.assertEquivalent(select('div.brothers:not(.brothers)'), []);
329 this.assertEquivalent(select('a[class~=external]:not([href="#"])'), [], 'a[class~=external][href!="#"]');
330 this.assertEqual(select('#p a:not(:first-of-type)')[0], getById('link_2'), 'first-of-type');
331 this.assertEqual(select('#p a:not(:last-of-type)')[0], getById('link_1'), 'last-of-type');
332 this.assertEqual(select('#p a:not(:nth-of-type(1))')[0], getById('link_2'), 'nth-of-type');
333 this.assertEqual(select('#p a:not(:nth-last-of-type(1))')[0], getById('link_1'), 'nth-last-of-type');
334 this.assertEqual(select('#p a:not([rel~=nofollow])')[0], getById('link_2'), 'attribute 1');
335 this.assertEqual(select('#p a:not([rel^=external])')[0], getById('link_2'), 'attribute 2');
336 this.assertEqual(select('#p a:not([rel$=nofollow])')[0], getById('link_2'), 'attribute 3');
337 this.assertEqual(select('#p a:not([rel$="nofollow"]) > em')[0], getById('em'), 'attribute 4');
338 this.assertEqual(select('#list li:not(#item_1):not(#item_3)')[0], getById('item_2'), 'adjacent :not clauses');
339 this.assertEqual(select('#grandfather > div:not(#uncle) #son')[0], getById('son'));
340 this.assertEqual(select('#p a:not([rel$="nofollow"]) em')[0], getById('em'), 'attribute 4 + all descendants');
341 this.assertEqual(select('#p a:not([rel$="nofollow"])>em')[0], getById('em'), 'attribute 4 (without whitespace)');
342 }
343 });
344
345 runner.addGroup("UI element states pseudo-classes").addTests(null, {
346 "E:disabled": function(){
347 this.assertEqual(select('#troubleForm > p > *:disabled')[0], getById('disabled_text_field'));
348 },
349 "E:checked": function(){
350 this.assertEquivalent(select('#troubleForm *:checked'), getById('checked_box', 'checked_radio'));
351 }
352 });
353
354 runner.addGroup("Combinators").addTests(null, {
355 "E F": function(){
356 //Descendant
357 this.assertEquivalent(select('#fixtures a *'), getById('em2', 'em', 'span'));
358 this.assertEqual(select('div#fixtures p')[0], getById("p"));
359 },
360 "E + F": function(){
361 //Adjacent sibling
362 this.assertEqual(select('div.brothers + div.brothers')[0], getById("uncle"));
363 this.assertEqual(select('div.brothers + div')[0], getById('uncle'));
364 this.assertEqual(select('#level2_1+span')[0], getById('level2_2'));
365 this.assertEqual(select('#level2_1 + span')[0], getById('level2_2'));
366 this.assertEqual(select('#level2_1 + *')[0], getById('level2_2'));
367 this.assertEquivalent(select('#level2_2 + span'), []);
368 this.assertEqual(select('#level3_1 + span')[0], getById('level3_2'));
369 this.assertEqual(select('#level3_1 + *')[0], getById('level3_2'));
370 this.assertEquivalent(select('#level3_2 + *'), []);
371 this.assertEquivalent(select('#level3_1 + em'), []);
372 if(RUN_BENCHMARKS){
373 this.wait(function(){
374 this.benchmark(function(){
375 select('#level3_1 + span');
376 }, 1000);
377 }, 500);
378 }
379 },
380 "E > F": function(){
381 //Child
382 this.assertEquivalent(select('p.first > a'), getById('link_1', 'link_2'));
383 this.assertEquivalent(select('div#grandfather > div'), getById('father', 'uncle'));
384 this.assertEquivalent(select('#level1>span'), getById('level2_1', 'level2_2'));
385 this.assertEquivalent(select('#level1 > span'), getById('level2_1', 'level2_2'));
386 this.assertEquivalent(select('#level2_1 > *'), getById('level3_1', 'level3_2'));
387 this.assertEquivalent(select('div > #nonexistent'), []);
388 if(RUN_BENCHMARKS){
389 this.wait(function(){
390 this.benchmark(function(){
391 select('#level1 > span');
392 }, 1000);
393 }, 500);
394 }
395 },
396 "E ~ F": function(){
397 //General sibling
398 this.assertEqual(select('h1 ~ ul')[0], getById('list'));
399 this.assertEquivalent(select('#level2_2 ~ span'), []);
400 this.assertEquivalent(select('#level3_2 ~ *'), []);
401 this.assertEquivalent(select('#level3_1 ~ em'), []);
402 this.assertEquivalent(select('div ~ #level3_2'), []);
403 this.assertEquivalent(select('div ~ #level2_3'), []);
404 this.assertEqual(select('#level2_1 ~ span')[0], getById('level2_2'));
405 this.assertEquivalent(select('#level2_1 ~ *'), getById('level2_2', 'level2_3'));
406 this.assertEqual(select('#level3_1 ~ #level3_2')[0], getById('level3_2'));
407 this.assertEqual(select('span ~ #level3_2')[0], getById('level3_2'));
408 if(RUN_BENCHMARKS){
409 this.wait(function(){
410 this.benchmark(function(){
411 select('#level2_1 ~ span');
412 }, 1000);
413 }, 500);
414 }
415 }
416 });
417
418 runner.addTests(null, {
419 "NW.Dom.match": function(){
420 var element = getById('dupL1');
421 //Assertions
422 this.assert(match(element, 'span'));
423 this.assert(match(element, "span#dupL1"));
424 this.assert(match(element, "div > span"), "child combinator");
425 this.assert(match(element, "#dupContainer span"), "descendant combinator");
426 this.assert(match(element, "#dupL1"), "ID only");
427 this.assert(match(element, "span.span_foo"), "class name 1");
428 this.assert(match(element, "span.span_bar"), "class name 2");
429 this.assert(match(element, "span:first-child"), "first-child pseudoclass");
430 //Refutations
431 this.refute(match(element, "span.span_wtf"), "bogus class name");
432 this.refute(match(element, "#dupL2"), "different ID");
433 this.refute(match(element, "div"), "different tag name");
434 this.refute(match(element, "span span"), "different ancestry");
435 this.refute(match(element, "span > span"), "different parent");
436 this.refute(match(element, "span:nth-child(5)"), "different pseudoclass");
437 //Misc.
438 this.refute(match(getById('link_2'), 'a[rel^=external]'));
439 this.assert(match(getById('link_1'), 'a[rel^=external]'));
440 this.assert(match(getById('link_1'), 'a[rel^="external"]'));
441 this.assert(match(getById('link_1'), "a[rel^='external']"));
442 },
443 "Equivalent Selectors": function(){
444 this.assertEquivalent(select('div.brothers'), select('div[class~=brothers]'));
445 this.assertEquivalent(select('div.brothers'), select('div[class~=brothers].brothers'));
446 this.assertEquivalent(select('div:not(.brothers)'), select('div:not([class~=brothers])'));
447 this.assertEquivalent(select('li ~ li'), select('li:not(:first-child)'));
448 this.assertEquivalent(select('ul > li'), select('ul > li:nth-child(n)'));
449 this.assertEquivalent(select('ul > li:nth-child(even)'), select('ul > li:nth-child(2n)'));
450 this.assertEquivalent(select('ul > li:nth-child(odd)'), select('ul > li:nth-child(2n+1)'));
451 this.assertEquivalent(select('ul > li:first-child'), select('ul > li:nth-child(1)'));
452 this.assertEquivalent(select('ul > li:last-child'), select('ul > li:nth-last-child(1)'));
453 /* Opera 10 does not accept values > 128 as a parameter to :nth-child
454 See <http://operawiki.info/ArtificialLimits> */
455 this.assertEquivalent(select('ul > li:nth-child(n-128)'), select('ul > li'));
456 this.assertEquivalent(select('ul>li'), select('ul > li'));
457 this.assertEquivalent(select('#p a:not([rel$="nofollow"])>em'), select('#p a:not([rel$="nofollow"]) > em'));
458 },
459 "Multiple Selectors": function(){
460 //The next two assertions should return document-ordered lists of matching elements --Diego Perini
461 // this.assertEquivalent(select('#list, .first,*[xml:lang="es-us"] , #troubleForm'), getById('p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'));
462 // this.assertEquivalent(select('#list, .first, *[xml:lang="es-us"], #troubleForm'), getById('p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'));
463 this.assertEquivalent(select('form[title*="commas,"], input[value="#commaOne,#commaTwo"]'), getById('commaParent', 'commaChild'));
464 this.assertEquivalent(select('form[title*="commas,"], input[value="#commaOne,#commaTwo"]'), getById('commaParent', 'commaChild'));
465 }
466 });
467}(runner));
\No newline at end of file