]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | describe("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 |