]> git.proxmox.com Git - extjs.git/blame - extjs/packages/core/test/specs/ComponentQuery.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / test / specs / ComponentQuery.js
CommitLineData
6527f429
DM
1describe("Ext.ComponentQuery", function() {\r
2 var cq,\r
3 cm,\r
4 EA,\r
5 result,\r
6 root,\r
7 child1,\r
8 child2,\r
9 child3,\r
10 child4,\r
11 child5,\r
12 child6,\r
13 child7,\r
14 child8,\r
15 child9,\r
16 child10,\r
17 child11,\r
18 child12,\r
19 setup = function(o, parent) {\r
20 if (o.items) {\r
21 for (var i = 0; i < o.items.length; i++) {\r
22 setup(o.items[i], o);\r
23 }\r
24 }\r
25 \r
26 Ext.apply(o, {\r
27 getItemId: function() {\r
28 return this.itemId !== undefined ? this.itemId : this.id;\r
29 },\r
30\r
31 getId: function() {\r
32 return this.id;\r
33 },\r
34\r
35 getRefItems: function(deep) {\r
36 var items = this.items || [],\r
37 len = items.length,\r
38 i = 0,\r
39 item;\r
40 \r
41 if (deep) {\r
42 for (; i < len; i++) {\r
43 item = items[i];\r
44 if (item.getRefItems) {\r
45 items = items.concat(item.getRefItems(true));\r
46 }\r
47 }\r
48 }\r
49 \r
50 return items;\r
51 },\r
52\r
53 getReference: function() {\r
54 return null;\r
55 },\r
56 \r
57 getRefOwner: function() {\r
58 return this.ownerCt;\r
59 },\r
60 \r
61 hasCls: function(cls) {\r
62 return this.cls == cls;\r
63 },\r
64 \r
65 isHidden: function() {\r
66 return this.hidden;\r
67 },\r
68 \r
69 isXType: function(type) {\r
70 return EA.contains(this.type.split('/'), type);\r
71 },\r
72 \r
73 ownerCt: parent,\r
74\r
75 self: {\r
76 $config: {\r
77 configs: {}\r
78 }\r
79 }\r
80 });\r
81 \r
82 cm.register(o);\r
83 };\r
84 \r
85 function expectChildren() {\r
86 var args = Array.prototype.slice.apply(arguments),\r
87 result = args.shift(),\r
88 len = args.length,\r
89 i, expected, actual;\r
90 \r
91 expect(result.length).toBe(len);\r
92 \r
93 for (i = 0, len = args.length; i < len; i++) {\r
94 expected = args[i];\r
95 actual = result[i];\r
96 \r
97 expect(actual.id).toBe(expected.id);\r
98 }\r
99 };\r
100\r
101 beforeEach(function() {\r
102 cq = Ext.ComponentQuery;\r
103 cm = Ext.ComponentManager;\r
104 EA = Ext.Array;\r
105\r
106 root = {\r
107 id: 'root',\r
108 cls: 'root-cls',\r
109 type: 'A',\r
110 items: [child1 = {\r
111 $className: 'Foo',\r
112 id: 'child1',\r
113 cls: 'child1-cls',\r
114 type: 'B/G/Z',\r
115 foo: 'bar,baz',\r
116 scrollable: false\r
117 }, child2 = {\r
118 $className: 'Bar.Baz.Qux',\r
119 id: 'child2',\r
120 cls: 'child2-cls',\r
121 type: 'B/G/Z',\r
122 bar: 'foo,bar,baz',\r
123 scrollable: true\r
124 }, child3 = {\r
125 $className: 'Foo',\r
126 id: 'child3',\r
127 cls: 'child3-cls',\r
128 type: 'B/C/D',\r
129 layout: 'card',\r
130 items: [child4 = {\r
131 id: 'child4',\r
132 cls: 'child4-cls',\r
133 type: 'B/C/E',\r
134 layout: 'hbox',\r
135 items: [child5 = {\r
136 id: 'child5',\r
137 cls: 'child5-cls',\r
138 type: 'B/C/F',\r
139 items: [child7 = {\r
140 id: 'child7',\r
141 cls: 'child7-cls',\r
142 type: 'B/G/H',\r
143 scrollable: null\r
144 }, child8 = {\r
145 id: 'child8',\r
146 cls: 'child8-cls',\r
147 type: 'B/G/I',\r
148 scrollable: 'x'\r
149 }, child9 = {\r
150 id: 'child9',\r
151 cls: 'child9-cls',\r
152 type: 'B/G/J'\r
153 }]\r
154 }, child6 = {\r
155 id: 'child6',\r
156 cls: 'child6-cls',\r
157 type: 'B/G/Z',\r
158 hidden: true\r
159 }, child10 = {\r
160 id : 'child10',\r
161 cls : 'child10-cls my-foo-cls',\r
162 type : 'B'\r
163 }, child11 = {\r
164 id : 'child.11',\r
165 cls : 'child11-cls my-foo-cls-test',\r
166 type : 'B',\r
167 scrollable: 'y'\r
168 }, child12 = {\r
169 id: 'child.12',\r
170 itemId: 'bobby.brown.goes.down',\r
171 type: 'E.2-E.4',\r
172 foo: '[foo(bar)!baz#qux\\fred*]',\r
173 sq: "'single' 'quotes'",\r
174 dq: '"double" "quotes"'\r
175 }]\r
176 }]\r
177 }]\r
178 };\r
179 setup(root);\r
180 });\r
181\r
182 afterEach(function() {\r
183 cm.all = {};\r
184 });\r
185 \r
186 describe("parser", function() {\r
187 it("should blow up if the intra-selector comma is escaped", function() {\r
188 expect(function() {\r
189 cq.query('#child3\\,F', root);\r
190 }).toThrow('Invalid ComponentQuery selector: ",F"');\r
191 });\r
192 \r
193 it("should blow up if a selector ends with unescaped comma", function() {\r
194 expect(function() {\r
195 cq.query('#child3,', root);\r
196 }).toThrow('Invalid ComponentQuery selector: ""');\r
197 });\r
198 \r
199 it("should blow up if a selector ends with an escaped comma", function() {\r
200 expect(function() {\r
201 cq.query('#child3\\,', root);\r
202 }).toThrow('Invalid ComponentQuery selector: ","');\r
203 });\r
204 \r
205 describe("missing quotes", function() {\r
206 var warning;\r
207 \r
208 beforeEach(function() {\r
209 spyOn(Ext.log, 'warn').andCallFake(function(msg) {\r
210 warning = msg;\r
211 });\r
212 \r
213 warning = null;\r
214 });\r
215 \r
216 it("should warn on missing opening double quote", function() {\r
217 cq.query('[foo=bar"]');\r
218 expect(warning).toMatch(/^ComponentQuery selector.*?unescaped \("\).*?end/);\r
219 });\r
220 \r
221 it("should warn on missing closing double quote", function() {\r
222 cq.query('[foo="bar]');\r
223 expect(warning).toMatch(/^ComponentQuery selector.*?unescaped \("\).*?beginning/);\r
224 });\r
225 \r
226 it("should warn on missing opening single quote", function() {\r
227 cq.query("[foo=bar']");\r
228 expect(warning).toMatch(/^ComponentQuery selector.*?unescaped \('\).*end/);\r
229 });\r
230 \r
231 it("should warn on missing closing single quote", function() {\r
232 cq.query("[foo='bar]");\r
233 expect(warning).toMatch(/^ComponentQuery selector.*?unescaped \('\).*beginning/);\r
234 });\r
235 });\r
236 });\r
237 \r
238 describe("Query object", function() {\r
239 describe("is", function() {\r
240 it("should return true if the selector is empty", function() {\r
241 var q = cq.parse('');\r
242 \r
243 expect(q.is(root)).toBe(true);\r
244 });\r
245 });\r
246 });\r
247 \r
248 describe("is", function(){\r
249 var item;\r
250 beforeEach(function() {\r
251 item = Ext.getCmp('root');\r
252 });\r
253 \r
254 afterEach(function(){\r
255 item = null;\r
256 });\r
257 \r
258 it("should return true if there is no selector", function(){\r
259 expect(cq.is(root)).toBe(true);\r
260 });\r
261 \r
262 it("should return true if component matches the selector", function(){\r
263 expect(cq.is(root, '[type=A]')).toBe(true);\r
264 }); \r
265 \r
266 it("should return true if component matches any selector", function(){\r
267 expect(cq.is(root, 'button, #foo, #root, [type=A]')).toBe(true);\r
268 }); \r
269 \r
270 it("should return false if the component doesn't match the selector", function(){\r
271 expect(cq.is(root, '#foo')).toBe(false);\r
272 });\r
273 \r
274 it("should work with the :not pseudo", function() {\r
275 var comp = new Ext.Component({\r
276 foo: 1\r
277 });\r
278 \r
279 expect(comp.is('[foo]:not([bar])')).toBe(true);\r
280 });\r
281 \r
282 it("should be able to run on destroyed components", function(){\r
283 var comp = new Ext.Component({\r
284 foo: 1\r
285 });\r
286 \r
287 comp.destroy();\r
288 expect(comp.is('[foo]:not([bar])')).toBe(true);\r
289 });\r
290 \r
291 describe("hierarchy selectors", function() {\r
292 it("should match a direct child", function(){\r
293 expect(cq.is(child6, '#child4 > #child6')).toBe(true); \r
294 }); \r
295 \r
296 it("should return false if it's not a direct child", function() {\r
297 expect(cq.is(child6, '#child3 > #child6')).toBe(false); \r
298 });\r
299 \r
300 it("should match deep children", function() {\r
301 expect(cq.is(child6, '#child3 #child6')).toBe(true); \r
302 });\r
303 \r
304 it("should match an upward selector", function() {\r
305 expect(cq.is(child3, '#child6 ^ #child3')).toBe(true); \r
306 });\r
307 });\r
308 });\r
309 \r
310 describe("simple query by xtype", function() {\r
311 it("should select all six items of type G", function() {\r
312 result = cq.query('G', root);\r
313 expect(result.length).toEqual(6);\r
314 expect(result[2].id).toEqual(child6.id);\r
315 });\r
316 \r
317 it("should allow escaped dots in xtype selector", function() {\r
318 result = cq.query('E\\.2-E\\.4', root);\r
319 expect(result.length).toBe(1);\r
320 expect(result[0].id).toBe(child12.id);\r
321 });\r
322 });\r
323 \r
324 describe("simple query by xtype prefixed with dot", function() {\r
325 beforeEach(function() {\r
326 // Silence console warnings\r
327 spyOn(Ext.log, 'warn');\r
328 });\r
329 \r
330 it("should select all six items of type G", function() {\r
331 result = cq.query('.G', root);\r
332 expect(result.length).toEqual(6);\r
333 expect(result[2].id).toEqual(child6.id);\r
334 });\r
335\r
336 it("should allow escaped dots in xtype selector", function() {\r
337 result = cq.query('.E\\.2-E\\.4', root);\r
338 expect(result.length).toBe(1);\r
339 expect(result[0].id).toBe(child12.id);\r
340 });\r
341 });\r
342 \r
343 describe("attributes starting with $", function(){\r
344 it("should match $className variable", function(){\r
345 result = cq.query('[$className=Foo]'); \r
346 expect(result.length).toBe(2);\r
347 expect(result[0].id).toBe('child1');\r
348 expect(result[1].id).toBe('child3');\r
349 }); \r
350\r
351 it("should allow dots in attribute values", function() {\r
352 result = cq.query('[$className=Bar.Baz.Qux]', root);\r
353 expectChildren(result, child2);\r
354 });\r
355 });\r
356 \r
357 describe("query by id", function() {\r
358 it("should select the second child", function() {\r
359 result = cq.query('G#child2', root);\r
360 expect(result.length).toEqual(1);\r
361 expect(result[0].id).toEqual(child2.id);\r
362 });\r
363 \r
364 it("should select the fifth child", function() {\r
365 result = cq.query('#child5', root);\r
366 expect(result.length).toEqual(1);\r
367 expect(result[0].id).toEqual(child5.id);\r
368 });\r
369 \r
370 it("should allow escaped dots in query-by-id selectors", function() {\r
371 result = cq.query('#child\\.11', root);\r
372 expectChildren(result, child11);\r
373 });\r
374 \r
375 it("should allow multiple escaped commas in #itemId selectors", function() {\r
376 result = cq.query('#bobby\\.brown\\.goes\\.down', root);\r
377 expectChildren(result, child12);\r
378 });\r
379 });\r
380 \r
381 describe("query by property", function() {\r
382 it("should select the second child", function() {\r
383 result = cq.query('G[cls=child2-cls]', root);\r
384 expect(result.length).toEqual(1);\r
385 expect(result[0].id).toEqual(child2.id);\r
386 });\r
387\r
388 it("should select the sixth child", function () {\r
389 result = cq.query('[hidden]', root);\r
390 expect(result.length).toEqual(1);\r
391 expect(result[0].id).toEqual(child6.id);\r
392 });\r
393 \r
394 describe("property value quotes", function() {\r
395 it("should allow single quoted value", function() {\r
396 result = cq.query("[id='child.12']", root);\r
397 expectChildren(result, child12);\r
398 });\r
399 \r
400 it("should allow double quoted value", function() {\r
401 result = cq.query('[id="child.12"]', root);\r
402 expectChildren(result, child12);\r
403 });\r
404 \r
405 it("should allow double quotes in single quoted value", function() {\r
406 result = cq.query('[dq=\'"double" "quotes"\']', root);\r
407 expectChildren(result, child12);\r
408 });\r
409 \r
410 it("should allow single quotes in double quoted value", function() {\r
411 result = cq.query("[sq=\"'single' 'quotes'\"]", root);\r
412 expectChildren(result, child12);\r
413 });\r
414 });\r
415 \r
416 describe("matchers", function(){\r
417 it("should select the tenth child", function () {\r
418 result = cq.query('[cls~=my-foo-cls]', root);\r
419 expect(result.length).toEqual(1);\r
420 expect(result[0].id).toEqual(child10.id);\r
421 }); \r
422 \r
423 it("should select items where id starts with child1", function(){\r
424 result = cq.query('[id^=child1]', root);\r
425 expectChildren(result, child1, child10);\r
426 });\r
427 \r
428 it("should select items where cls ends with 9-cls", function(){\r
429 result = cq.query('[cls$=9-cls]', root);\r
430 expect(result.length).toBe(1);\r
431 expect(result[0].cls).toBe('child9-cls'); \r
432 });\r
433\r
434 it("should select items with commas in properties", function() {\r
435 result = cq.query('[foo=bar\\,baz]');\r
436 expect(result.length).toEqual(1);\r
437 expect(result[0].id).toEqual(child1.id);\r
438 });\r
439 \r
440 it("should allow multiple escaped commas", function() {\r
441 result = cq.query('[bar=foo\\,bar\\,baz]', root);\r
442 expectChildren(result, child2);\r
443 });\r
444 \r
445 it("should allow escaped metacharacters", function() {\r
446 result = cq.query('[foo=\\[foo\\(bar\\)\\!baz\\#qux\\\\fred\\*\\]]', root);\r
447 expectChildren(result, child12);\r
448 });\r
449 \r
450 describe("regexen", function() {\r
451 it("should match everything with an empty regex", function() {\r
452 result = cq.query('[cls/=]');\r
453 expect(result.length).toBe(12);\r
454 });\r
455 \r
456 describe("simple regexen", function() {\r
457 it("should match regexen with text as pattern", function() {\r
458 result = cq.query('[cls/=my-foo]');\r
459 expectChildren(result, child10, child11);\r
460 });\r
461 \r
462 it("should match regexen with simple alternation", function() {\r
463 result = cq.query('[cls/=child3-cls|child4-cls|child5-cls]');\r
464 expectChildren(result, child5, child4, child3);\r
465 });\r
466 });\r
467 \r
468 describe("complex regexen", function() {\r
469 it("should match regexen with pattern quantifiers", function() {\r
470 result = cq.query('[cls/="child.{2}-cls"]');\r
471 expectChildren(result, child10, child11);\r
472 });\r
473 \r
474 it("should match regexen with grouping and alternating", function() {\r
475 result = cq.query('[cls/="child(?:7|8)-cls"]');\r
476 expectChildren(result, child7, child8);\r
477 });\r
478 \r
479 it("should match regexen with character classes", function() {\r
480 result = cq.query('[cls/="child\\[5-7\\]-cls"]');\r
481 expectChildren(result, child7, child5, child6);\r
482 });\r
483 });\r
484 });\r
485 });\r
486 });\r
487 \r
488 describe("query using mode ^", function() {\r
489 it("should select the fourth child", function() {\r
490 result = cq.query('G[cls=child8-cls]^#child4', root);\r
491 expect(result.length).toEqual(1);\r
492 expect(result[0].id).toEqual(child4.id);\r
493 });\r
494 });\r
495\r
496 describe("query using mode ^ and >", function() {\r
497 it("should select the sixth child", function() {\r
498 result = cq.query('G[cls=child8-cls]^#child4>G', root);\r
499 expect(result.length).toEqual(1);\r
500 expect(result[0].id).toEqual(child6.id);\r
501 });\r
502 });\r
503\r
504 describe("query using multiple selectors", function() {\r
505 it("should select the third and fifth child", function() {\r
506 result = cq.query('#child3,F', root);\r
507 expect(result.length).toEqual(2);\r
508 expect(result[0].id).toEqual(child3.id);\r
509 expect(result[1].id).toEqual(child5.id);\r
510 });\r
511 });\r
512\r
513 describe("query using member function", function() {\r
514 it("should select the sixth child that is hidden", function() {\r
515 result = cq.query('{isHidden()}', root);\r
516 expect(result.length).toEqual(1);\r
517 expect(result[0].id).toEqual(child6.id);\r
518 });\r
519 });\r
520\r
521 describe("query using pseudo-class", function() {\r
522 beforeEach(function() {\r
523 cq.pseudos.cardLayout = function(items) {\r
524 var result = [], c, i = 0, l = items.length;\r
525 for (; i < l; i++) {\r
526 if ((c = items[i]).layout === 'card') {\r
527 result.push(c);\r
528 }\r
529 }\r
530 return result;\r
531 };\r
532 });\r
533\r
534 it("should select the third child with layout == 'card'", function() {\r
535 result = cq.query('C:cardLayout', root);\r
536 delete cq.pseudos.cardLayout;\r
537 expect(result.length).toEqual(1);\r
538 expect(result[0].id).toEqual(child3.id);\r
539 });\r
540 \r
541 it("should not select the sixth child which is filtered by :not()", function(){\r
542 result = cq.query(':not([hidden])', root);\r
543 var all = root.getRefItems(true),\r
544 getId = function(o){ return o.id; },\r
545 allIds = EA.map(all, getId),\r
546 resultIds = EA.map(result, getId),\r
547 diffIds = EA.difference(allIds, resultIds);\r
548 expect(result.length).toEqual(all.length - 1);\r
549 expect(diffIds.length).toEqual(1);\r
550 expect(diffIds[0]).toEqual(child6.id);\r
551 });\r
552 \r
553 it("should accept back-to-back pseudo-class selectors with cumulative results", function(){\r
554 result = cq.query(':not(G):not(F)', root);\r
555 expect(result.length).toEqual(5);\r
556 expect(result[0].id).toEqual(child3.id);\r
557 expect(result[1].id).toEqual(child4.id);\r
558 expect(result[2].id).toEqual(child10.id);\r
559 expect(result[3].id).toEqual(child11.id);\r
560 expect(result[4].id).toEqual(child12.id);\r
561 });\r
562 \r
563 it("should accept member expression selectors", function() {\r
564 result = cq.query(':not({isHidden()})', root);\r
565 var all = root.getRefItems(true),\r
566 getId = function(o){ return o.id; },\r
567 allIds = EA.map(all, getId),\r
568 resultIds = EA.map(result, getId),\r
569 diffIds = EA.difference(allIds, resultIds);\r
570 expect(result.length).toEqual(all.length - 1);\r
571 expect(diffIds.length).toEqual(1);\r
572 expect(diffIds[0]).toEqual(child6.id);\r
573 });\r
574 \r
575 describe("focusable", function() {\r
576 // https://sencha.jira.com/browse/EXTJS-16758\r
577 it("should not blow up when card item is not a component", function() {\r
578 var container = new Ext.container.Container({\r
579 renderTo: Ext.getBody(),\r
580 items: [new Ext.Widget()]\r
581 });\r
582 \r
583 expect(function() {\r
584 container.query(':focusable');\r
585 }).not.toThrow();\r
586 \r
587 container.destroy();\r
588 });\r
589 });\r
590 \r
591 describe("first/last", function() {\r
592 var items;\r
593 beforeEach(function(){\r
594 items = [\r
595 new Ext.Component({\r
596 action: 'type1',\r
597 id: 'id1'\r
598 }),\r
599 new Ext.container.Container({\r
600 action: 'type1',\r
601 id: 'id2'\r
602 }),\r
603 new Ext.container.Container({\r
604 action: 'type2',\r
605 id: 'id3'\r
606 }),\r
607 new Ext.Component({\r
608 action: 'type2',\r
609 id: 'id4'\r
610 }),\r
611 new Ext.container.Container({\r
612 action: 'type2',\r
613 id: 'id5'\r
614 })\r
615 ];\r
616 });\r
617 \r
618 afterEach(function(){\r
619 Ext.Array.forEach(items, function(item){\r
620 item.destroy();\r
621 });\r
622 items = null;\r
623 });\r
624 \r
625 describe("first", function(){\r
626 it("should return an empty array if no items match", function(){\r
627 var result = cq.query('button:first', items);\r
628 expect(result).toEqual([]); \r
629 });\r
630 \r
631 it("should return the first matching component by type", function(){\r
632 var result = cq.query('container:first', items);\r
633 expect(result).toEqual([items[1]]); \r
634 });\r
635 \r
636 it("should return the last matching component by attribute", function(){\r
637 var result = cq.query('[action=type2]:first', items);\r
638 expect(result).toEqual([items[2]]); \r
639 });\r
640 \r
641 it("should return the first component", function(){\r
642 var result = cq.query('*:first', items);\r
643 expect(result).toEqual([items[0]]); \r
644 });\r
645 \r
646 describe("no items/single item", function(){\r
647 it("should return an empty array if there are no items", function(){\r
648 var result = cq.query('*:first', []);\r
649 expect(result).toEqual([]); \r
650 });\r
651 \r
652 it("should return an a single item if it matches", function(){\r
653 var c = new Ext.Component();\r
654 var result = cq.query('component:first', [c]);\r
655 expect(result).toEqual([c]); \r
656 c.destroy(); \r
657 }); \r
658 });\r
659 });\r
660 \r
661 describe("last", function(){\r
662 it("should return an empty array if no items match", function(){\r
663 var result = cq.query('button:last', items);\r
664 expect(result).toEqual([]); \r
665 });\r
666 \r
667 it("should return the last matching component by type", function(){\r
668 var result = cq.query('component:last', items);\r
669 expect(result).toEqual([items[4]]); \r
670 });\r
671 \r
672 it("should return the first matching component by attribute", function(){\r
673 var result = cq.query('[action=type1]:last', items);\r
674 expect(result).toEqual([items[1]]); \r
675 });\r
676 \r
677 it("should return the first component", function(){\r
678 var result = cq.query('*:last', items);\r
679 expect(result).toEqual([items[4]]); \r
680 });\r
681 \r
682 describe("no items/single item", function(){\r
683 it("should return an empty array if there are no items", function(){\r
684 var result = cq.query('*:last', []);\r
685 expect(result).toEqual([]); \r
686 });\r
687 \r
688 it("should return an a single item if it matches", function(){\r
689 var c = new Ext.Component();\r
690 var result = cq.query('component:last', [c]);\r
691 expect(result).toEqual([c]); \r
692 c.destroy(); \r
693 }); \r
694 });\r
695 });\r
696 });\r
697\r
698 describe(':scrollable', function () {\r
699 it('should find all valid scrollable items no matter how deeply nested', function () {\r
700 expect(cq.query(':scrollable', root).length).toBe(3);\r
701 });\r
702\r
703 it('should only find non-valid scrollable items (with a null or false value) if explicitly specified', function () {\r
704 expect(cq.query('[scrollable=null]', root).length).toBe(1);\r
705 expect(cq.query('[scrollable=false]', root).length).toBe(1);\r
706 });\r
707\r
708 it('should return an empty array if no items match', function () {\r
709 expect(cq.query(':scrollable[type=B/C/D]', root)).toEqual([]);\r
710 });\r
711\r
712 it('should return an a single item if it matches', function () {\r
713 expect(cq.query(':scrollable:first', root)[0]).toEqual(Ext.getCmp('child2'));\r
714 });\r
715\r
716 it('should not blow up when card item is not a component', function () {\r
717 var container = new Ext.container.Container({\r
718 renderTo: Ext.getBody(),\r
719 items: [new Ext.Widget()]\r
720 });\r
721\r
722 expect(function () {\r
723 container.query(':scrollable');\r
724 }).not.toThrow();\r
725\r
726 container.destroy();\r
727 });\r
728 });\r
729 });\r
730\r
731 describe('attribute value coercion', function() {\r
732 var candidates = [{\r
733 att1: 0,\r
734 att2: 0,\r
735 att3: 0,\r
736 att4: 0\r
737 }, {\r
738 att1: null,\r
739 att2: false,\r
740 att3: true,\r
741 att4: undefined\r
742 }, {\r
743 att1: 0,\r
744 att2: 0,\r
745 att3: 0,\r
746 att4: 0\r
747 }];\r
748\r
749 if('should coerce "null" to match a null property value', function() {\r
750 expect(cq.query('[att1=null]', candidates)).toBe(candidates[1]);\r
751 });\r
752\r
753 if('should coerce "false" to match a Boolean property value', function() {\r
754 expect(cq.query('[att2=false]', candidates)).toBe(candidates[1]);\r
755 });\r
756\r
757 if('should coerce "true" to match a Boolean property value', function() {\r
758 expect(cq.query('[att3=true]', candidates)).toBe(candidates[1]);\r
759 });\r
760\r
761 if('should coerce "undefined" to match an undefined property value', function() {\r
762 expect(cq.query('[att4=undefined]', candidates)).toBe(candidates[1]);\r
763 });\r
764 });\r
765 \r
766 describe('ownProperty tests', function() {\r
767 var TestClass = Ext.define(null, {\r
768 extend: 'Ext.Component',\r
769 foo: 'bar',\r
770 bletch: 0\r
771 }),\r
772 candidates;\r
773 \r
774 beforeEach(function() {\r
775 // Only candidates[1] has *ownProperties* foo and bletch\r
776 // And the value of bletch is zero, so by [bletch] will never match.\r
777 // Test that [?bletch] tests for just *presence* of property in object.\r
778 candidates = [new TestClass(), new TestClass({\r
779 foo: 'bar',\r
780 bletch: 0\r
781 })];\r
782 });\r
783 \r
784 afterEach(function() {\r
785 Ext.destroy(candidates[0], candidates[1]);\r
786 candidates = null;\r
787 });\r
788\r
789 it('should only match candidates [@foo=bar] with ownProperty "foo" equal to "bar"', function() {\r
790 expect(Ext.ComponentQuery.query('[@foo=bar]', candidates).length).toBe(1);\r
791 expect(Ext.ComponentQuery.query('[@foo=bar]', candidates)[0]).toBe(candidates[1])\r
792 expect(Ext.ComponentQuery.is(candidates[0], '[@foo=bar]')).toBe(false);\r
793 expect(Ext.ComponentQuery.is(candidates[1], '[@foo=bar]')).toBe(true);\r
794 });\r
795\r
796 it('should not match candidates [bletch] where bletch is a falsy property', function() {\r
797 expect(Ext.ComponentQuery.query('[bletch]', candidates).length).toBe(0);\r
798 expect(Ext.ComponentQuery.is(candidates[0], '[bletch]')).toBe(false);\r
799 expect(Ext.ComponentQuery.is(candidates[1], '[bletch]')).toBe(false);\r
800 });\r
801\r
802 it('should match candidates [?bletch] where bletch is a falsy property', function() {\r
803 expect(Ext.ComponentQuery.query('[?bletch]', candidates).length).toBe(1);\r
804 expect(Ext.ComponentQuery.query('[?bletch]', candidates)[0]).toBe(candidates[1]);\r
805 expect(Ext.ComponentQuery.is(candidates[0], '[?bletch]')).toBe(false);\r
806 expect(Ext.ComponentQuery.is(candidates[1], '[?bletch]')).toBe(true);\r
807 });\r
808 });\r
809 \r
810 describe('Querying floating descendants', function() {\r
811 var c;\r
812 \r
813 beforeEach(function() {\r
814 c = new Ext.container.Container({\r
815 items: {\r
816 xtype: 'container',\r
817 floating: true,\r
818 id: 'floating-cq-child',\r
819 items: {\r
820 xtype: 'container',\r
821 floating: true,\r
822 id: 'floating-cq-grandchild',\r
823 items: {\r
824 floating: true,\r
825 id: 'floating-cq-great-grandchild'\r
826 }\r
827 }\r
828 },\r
829 renderTo: document.body\r
830 });\r
831 });\r
832 afterEach(function() {\r
833 c.destroy();\r
834 });\r
835\r
836 it('should find all descendants', function() {\r
837 var d = c.query();\r
838 expect(d.length).toEqual(3);\r
839 expect(d[0]).toBe(Ext.getCmp('floating-cq-child'));\r
840 expect(d[1]).toBe(Ext.getCmp('floating-cq-grandchild'));\r
841 expect(d[2]).toBe(Ext.getCmp('floating-cq-great-grandchild'));\r
842 });\r
843 it('should find individual descendants', function() {\r
844 var d = c.query('>*');\r
845 expect(d.length).toEqual(1);\r
846 expect(d[0]).toBe(Ext.getCmp('floating-cq-child'));\r
847\r
848 d = c.query('>>*');\r
849 expect(d.length).toEqual(1);\r
850 expect(d[0]).toBe(Ext.getCmp('floating-cq-grandchild'));\r
851\r
852 d = c.query('>>>*');\r
853 expect(d.length).toEqual(1);\r
854 expect(d[0]).toBe(Ext.getCmp('floating-cq-great-grandchild'));\r
855 });\r
856 });\r
857\r
858 describe('trimming spaces', function () {\r
859 var c;\r
860\r
861 beforeEach(function () {\r
862 c = new Ext.container.Container({\r
863 items: {\r
864 xtype: 'button',\r
865 text: 'Test',\r
866 action: 'selectVendors'\r
867 },\r
868 renderTo: document.body\r
869 });\r
870 });\r
871\r
872 afterEach(function () {\r
873 c.destroy();\r
874 c = null;\r
875 });\r
876\r
877 describe('single space', function () {\r
878 describe("attribute matching expressions", function() {\r
879 it('should trim leading space', function () {\r
880 result = cq.query('[action =selectVendors]', c);\r
881 expect(result.length).toBe(1);\r
882 expect(result[0].action).toBe('selectVendors');\r
883\r
884 result = cq.query('[action ^=selectVendors]', c);\r
885 expect(result.length).toBe(1);\r
886 expect(result[0].action).toBe('selectVendors');\r
887\r
888 result = cq.query('[action $=selectVendors]', c);\r
889 expect(result.length).toBe(1);\r
890 expect(result[0].action).toBe('selectVendors');\r
891 });\r
892\r
893 it('should trim trailing space', function () {\r
894 result = cq.query('[action= selectVendors]', c);\r
895 expect(result.length).toBe(1);\r
896 expect(result[0].action).toBe('selectVendors');\r
897\r
898 result = cq.query('[action*= selectVendors]', c);\r
899 expect(result.length).toBe(1);\r
900 expect(result[0].action).toBe('selectVendors');\r
901\r
902 result = cq.query('[action~= selectVendors]', c);\r
903 expect(result.length).toBe(1);\r
904 expect(result[0].action).toBe('selectVendors');\r
905 });\r
906\r
907 it('should trim both spaces', function () {\r
908 result = cq.query('[action = selectVendors]', c);\r
909 expect(result.length).toBe(1);\r
910 expect(result[0].action).toBe('selectVendors');\r
911\r
912 result = cq.query('[action *= selectVendors]', c);\r
913 expect(result.length).toBe(1);\r
914 expect(result[0].action).toBe('selectVendors');\r
915\r
916 result = cq.query('[action ~= selectVendors]', c);\r
917 expect(result.length).toBe(1);\r
918 expect(result[0].action).toBe('selectVendors');\r
919 });\r
920 });\r
921 });\r
922\r
923 describe('multiple spaces', function () {\r
924 describe("attribute matching expressions", function() {\r
925 it('should trim multiple leading spaces', function () {\r
926 result = cq.query('[action =selectVendors]', c);\r
927 expect(result.length).toBe(1);\r
928 expect(result[0].action).toBe('selectVendors');\r
929\r
930 result = cq.query('[action ^=selectVendors]', c);\r
931 expect(result.length).toBe(1);\r
932 expect(result[0].action).toBe('selectVendors');\r
933\r
934 result = cq.query('[action $=selectVendors]', c);\r
935 expect(result.length).toBe(1);\r
936 expect(result[0].action).toBe('selectVendors');\r
937 });\r
938\r
939 it('should trim multiple trailing spaces', function () {\r
940 result = cq.query('[action= selectVendors]', c);\r
941 expect(result.length).toBe(1);\r
942 expect(result[0].action).toBe('selectVendors');\r
943\r
944 result = cq.query('[action*= selectVendors]', c);\r
945 expect(result.length).toBe(1);\r
946 expect(result[0].action).toBe('selectVendors');\r
947\r
948 result = cq.query('[action~= selectVendors]', c);\r
949 expect(result.length).toBe(1);\r
950 expect(result[0].action).toBe('selectVendors');\r
951 });\r
952\r
953 it('should trim multiple spaces on both sides', function () {\r
954 result = cq.query('[action = selectVendors]', c);\r
955 expect(result.length).toBe(1);\r
956 expect(result[0].action).toBe('selectVendors');\r
957\r
958 result = cq.query('[action *= selectVendors]', c);\r
959 expect(result.length).toBe(1);\r
960 expect(result[0].action).toBe('selectVendors');\r
961\r
962 result = cq.query('[action ~= selectVendors]', c);\r
963 expect(result.length).toBe(1);\r
964 expect(result[0].action).toBe('selectVendors');\r
965 });\r
966 });\r
967 \r
968 describe("id matching expressions", function() {\r
969 it("should trim leading spaces", function() {\r
970 result = cq.query(' #child9', root);\r
971 \r
972 expect(result.length).toBe(1);\r
973 expect(result[0].id).toBe(child9.id);\r
974 });\r
975 \r
976 it("should trim trailing spaces", function() {\r
977 result = cq.query('#child9 ', root);\r
978 \r
979 expect(result.length).toBe(1);\r
980 expect(result[0].id).toBe(child9.id);\r
981 });\r
982 \r
983 it("should trim spaces on both sides", function() {\r
984 result = cq.query(' #child9 ', root);\r
985 \r
986 expect(result.length).toBe(1);\r
987 expect(result[0].id).toBe(child9.id);\r
988 });\r
989 });\r
990 \r
991 describe("descendancy expressions", function() {\r
992 it("should trim leading spaces", function() {\r
993 result = cq.query(' [layout=card] [type=B/G/H]', root);\r
994 \r
995 expect(result.length).toBe(1);\r
996 expect(result[0].id).toBe(child7.id);\r
997 });\r
998 \r
999 it("should trim trailing spaces", function() {\r
1000 result = cq.query('[type=B/G/J] ^ [layout=hbox] ', root);\r
1001 \r
1002 expect(result.length).toBe(1);\r
1003 expect(result[0].id).toBe(child4.id);\r
1004 });\r
1005 \r
1006 it("should trim spaces on both sides", function() {\r
1007 result = cq.query(' #child4 > [type=B/C/F] ', root);\r
1008 \r
1009 expect(result.length).toBe(1);\r
1010 expect(result[0].id).toBe(child5.id);\r
1011 });\r
1012 });\r
1013 });\r
1014 });\r
1015\r
1016 describe('pre- and postOrder', function () {\r
1017 var foo = false;\r
1018\r
1019 afterEach(function () {\r
1020 foo = false;\r
1021 cq.cache.clear();\r
1022 });\r
1023\r
1024 describe('preOrder', function () {\r
1025 it('should call the fn regardless of whether the selector has been cached', function () {\r
1026 expect(cq.cache.get('')).toBeUndefined();\r
1027\r
1028 cq.visitPreOrder('', this, function () {\r
1029 foo = true;\r
1030 });\r
1031\r
1032 expect(foo).toBe(true);\r
1033 });\r
1034 });\r
1035\r
1036 describe('postOrder', function () {\r
1037 it('should call the fn regardless of whether the selector has been cached', function () {\r
1038 expect(cq.cache.get('')).toBeUndefined();\r
1039\r
1040 cq.visitPostOrder('', this, function () {\r
1041 foo = true;\r
1042 });\r
1043\r
1044 expect(foo).toBe(true);\r
1045 });\r
1046 });\r
1047 });\r
1048\r
1049 describe("selecting by attribute", function(){\r
1050\r
1051 var foo, bar;\r
1052 beforeEach(function(){\r
1053 Ext.define('spec.Foo', {\r
1054 extend: 'Ext.Component',\r
1055 config: {\r
1056 bar: 1\r
1057 },\r
1058 baz: 2\r
1059 });\r
1060\r
1061 foo = new spec.Foo({\r
1062 jaz:3\r
1063 });\r
1064\r
1065 Ext.define('spec.Bar', {\r
1066 extend: 'Ext.Component',\r
1067 config: {\r
1068 bar: 4\r
1069 },\r
1070 baz: 5\r
1071 });\r
1072\r
1073 bar = new spec.Bar({\r
1074 jaz: 6\r
1075 });\r
1076 });\r
1077\r
1078 afterEach(function() {\r
1079 Ext.undefine('spec.Foo');\r
1080 Ext.undefine('spec.Bar');\r
1081 });\r
1082 it("should match instance config", function(){\r
1083 result = cq.query('[bar=1]');\r
1084 expect(result.length).toBe(1);\r
1085 expect(result[0]).toBe(foo);\r
1086 });\r
1087\r
1088 it("should match a property on the instance", function() {\r
1089 result = cq.query('[baz=2]');\r
1090 expect(result.length).toBe(1);\r
1091 expect(result[0]).toBe(foo);\r
1092 });\r
1093\r
1094 it("should match an arbitrary property on the config object", function() {\r
1095 result = cq.query('[jaz=3]');\r
1096 expect(result.length).toBe(1);\r
1097 expect(result[0]).toBe(foo);\r
1098 });\r
1099 });\r
1100 \r
1101 describe('querying non Ext classes', function() {\r
1102 it('should be able to query on raw objects', function() {\r
1103 var target = {foo: 'bar'},\r
1104 candidates = [{foo: 'ik'}, {foo: 'screeble'}, target, {foo: 'razz'}, {foo: 'poot'}];\r
1105\r
1106 expect(Ext.ComponentQuery.query('[foo=bar]', candidates)[0]).toBe(target);\r
1107 });\r
1108 });\r
1109});\r