]> git.proxmox.com Git - extjs.git/blame - extjs/packages/core/test/specs/data/TreeStore.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / test / specs / data / TreeStore.js
CommitLineData
6527f429
DM
1describe("Ext.data.TreeStore", function() {\r
2 var store,\r
3 loadStore,\r
4 dummyData,\r
5 NodeModel = Ext.define(null, {\r
6 extend: 'Ext.data.Model',\r
7 fields: ['name'],\r
8 proxy: {\r
9 type: 'ajax',\r
10 url: 'foo.json',\r
11 reader: {\r
12 type: 'json'\r
13 }\r
14 }\r
15 }),\r
16 TaskModel = Ext.define(null, {\r
17 extend: 'Ext.data.Model',\r
18 idProperty : 'id',\r
19 fields: [\r
20 {name: 'id', type: 'int', allowNull: true},\r
21 {name: 'task', type: 'string'},\r
22 {name: 'duration', type: 'string'}\r
23 ]\r
24 });\r
25\r
26 function spyOnEvent(object, eventName, fn) {\r
27 var obj = {\r
28 fn: fn || Ext.emptyFn\r
29 },\r
30 spy = spyOn(obj, "fn");\r
31 object.addListener(eventName, obj.fn);\r
32 return spy;\r
33 }\r
34\r
35 function expandify(nodes) {\r
36 if (Ext.isNumber(nodes[0])) {\r
37 nodes = Ext.Array.map(nodes, function(id) {\r
38 return {\r
39 id: id,\r
40 leaf: true\r
41 };\r
42 });\r
43 }\r
44 Ext.Array.forEach(nodes, function(node) {\r
45 if (node.children || node.leaf === false) {\r
46 node.expanded = true;\r
47 if (node.children) {\r
48 node.children = expandify(node.children);\r
49 } else {\r
50 node.children = [];\r
51 }\r
52 } else {\r
53 node.leaf = true;\r
54 }\r
55 });\r
56 return nodes;\r
57 }\r
58\r
59 function makeStore(nodes, cfg) {\r
60 store = new Ext.data.TreeStore(Ext.apply({\r
61 asynchronousLoad: false,\r
62 root: {\r
63 expanded: true,\r
64 children: expandify(nodes)\r
65 }\r
66 }, cfg));\r
67 }\r
68\r
69 function expectOrder(parent, ids) {\r
70 var childNodes = parent.childNodes,\r
71 i, len;\r
72\r
73 expect((childNodes || []).length).toBe(ids.length);\r
74\r
75 if (childNodes) {\r
76 for (i = 0, len = childNodes.length; i < len; ++i) {\r
77 expect(childNodes[i].id).toBe(ids[i]);\r
78 }\r
79 }\r
80 }\r
81\r
82 beforeEach(function() {\r
83 dummyData = {\r
84 success: true,\r
85 children: [{\r
86 id: 1,\r
87 name: "aaa"\r
88 },{\r
89 id: 2,\r
90 name: "bbb", \r
91 children: [{\r
92 id: 3, \r
93 name: "ccc"\r
94 },{\r
95 id: 4, \r
96 name: "ddd", \r
97 children: [{\r
98 id: 5, \r
99 name: "eee",\r
100 leaf: true\r
101 }]\r
102 }]\r
103 },{\r
104 id: 6, \r
105 name: "fff", \r
106 children: [{id: 7, \r
107 name: "ggg"\r
108 }]\r
109 }]\r
110 };\r
111\r
112 MockAjaxManager.addMethods();\r
113\r
114 loadStore = function(store, options) {\r
115 store.load(options);\r
116 completeWithData(dummyData);\r
117 };\r
118\r
119 });\r
120 \r
121 afterEach(function() {\r
122 store = Ext.destroy(store);\r
123 MockAjaxManager.removeMethods();\r
124 });\r
125\r
126 function completeWithData(data) {\r
127 Ext.Ajax.mockComplete({\r
128 status: 200,\r
129 responseText: Ext.encode(data)\r
130 });\r
131 }\r
132\r
133 function completeWithFailure() {\r
134 Ext.Ajax.mockComplete({\r
135 status: 200,\r
136 responseText: Ext.encode({\r
137 success: false\r
138 })\r
139 });\r
140 }\r
141\r
142 function byId(id) {\r
143 return store.getNodeById(id);\r
144 }\r
145\r
146 describe('success: false in return packet', function() {\r
147 // Set to bug condition to ensure event fires as expected.\r
148 var wasSuccessful = true;\r
149\r
150 it("should fire the load event with the success parameter false", function() {\r
151 store = new Ext.data.TreeStore({\r
152 model: NodeModel,\r
153 root: {\r
154 expanded: true\r
155 },\r
156 listeners: {\r
157 load: function(store, records, successful, operation, node) {\r
158 wasSuccessful = successful;\r
159 }\r
160 }\r
161 });\r
162 completeWithFailure();\r
163 expect(wasSuccessful).toBe(false);\r
164 });\r
165 });\r
166\r
167 describe("the model", function() {\r
168 it("should be able to use a non TreeModel", function() {\r
169 var Model = Ext.define(null, {\r
170 extend: 'Ext.data.Model',\r
171 fields: ['foo']\r
172 });\r
173\r
174 // Important that the proxy gets applied first here\r
175 store = new Ext.data.TreeStore({\r
176 proxy: {\r
177 type: 'ajax',\r
178 url: 'fake'\r
179 },\r
180 model: Model\r
181 });\r
182 expect(store.getModel()).toBe(Model);\r
183 expect(Model.prototype.isNode).toBe(true);\r
184 });\r
185\r
186 describe("using an implicit model", function() {\r
187 it("should use the model's memory proxy when no proxy is defined on the store", function() {\r
188 store = new Ext.data.TreeStore({\r
189 fields: ['id', 'height', 'width']\r
190 });\r
191 expect(store.getProxy().isMemoryProxy).toBe(true);\r
192 expect(store.getProxy()).toBe(store.getModel().getProxy());\r
193 });\r
194\r
195 it("should set the store's proxy on the model", function() {\r
196 store = new Ext.data.TreeStore({\r
197 fields: ['id', 'height', 'width'],\r
198 proxy: {\r
199 type: 'ajax',\r
200 url: 'foo'\r
201 }\r
202 });\r
203 expect(store.getProxy().isAjaxProxy).toBe(true);\r
204 expect(store.getProxy().url).toBe('foo');\r
205 expect(store.getProxy()).toBe(store.getModel().getProxy());\r
206 });\r
207\r
208 it("should have the model set on the proxy & the reader", function() {\r
209 store = new Ext.data.TreeStore({\r
210 fields: ['id', 'height', 'width'],\r
211 proxy: {\r
212 type: 'ajax',\r
213 url: 'foo'\r
214 }\r
215 });\r
216 expect(store.getProxy().getModel()).toBe(store.getModel());\r
217 expect(store.getProxy().getReader().getModel()).toBe(store.getModel());\r
218 });\r
219\r
220 it("should extend Ext.data.Model", function() {\r
221 store = new Ext.data.TreeStore({\r
222 fields: ['id', 'height', 'width']\r
223 });\r
224 expect(store.getModel().superclass.self).toBe(Ext.data.TreeModel);\r
225 });\r
226 });\r
227 });\r
228\r
229 describe("sorting", function() {\r
230 function expectStoreOrder(ids) {\r
231 var len = ids.length,\r
232 i;\r
233\r
234 expect(store.getCount()).toBe(len);\r
235\r
236 for (i = 0; i < len; ++i) {\r
237 expect(store.getAt(i).id).toBe(ids[i]);\r
238 }\r
239\r
240\r
241 }\r
242\r
243 describe("with local data", function() {\r
244 describe("with folderSort: true", function() {\r
245 it("should sort when setting folderSort dynamically", function() {\r
246 store = new Ext.data.TreeStore({\r
247 model: NodeModel,\r
248 root: {\r
249 expanded: true,\r
250 children: [{\r
251 id: 'l1',\r
252 leaf: true\r
253 }, {\r
254 id: 'f1'\r
255 }, {\r
256 id: 'l2',\r
257 leaf: true\r
258 }, {\r
259 id: 'f2'\r
260 }]\r
261 }\r
262 });\r
263 store.setFolderSort(true);\r
264 expectOrder(store.getRoot(), ['f1', 'f2', 'l1', 'l2']);\r
265 });\r
266\r
267 it("should leave the original sort order if there are no other sorters", function() {\r
268 store = new Ext.data.TreeStore({\r
269 model: NodeModel,\r
270 folderSort: true,\r
271 root: {\r
272 expanded: true,\r
273 children: [{\r
274 id: 'l3',\r
275 leaf: true\r
276 }, {\r
277 id: 'l2',\r
278 leaf: true\r
279 }, {\r
280 id: 'f3'\r
281 }, {\r
282 id: 'l1',\r
283 leaf: true\r
284 }, {\r
285 id: 'f2'\r
286 }, {\r
287 id: 'f1'\r
288 }]\r
289 }\r
290 });\r
291 expectOrder(store.getRoot(), ['f3', 'f2', 'f1', 'l3', 'l2', 'l1']);\r
292 });\r
293\r
294 it("should do a deep sort", function() {\r
295 store = new Ext.data.TreeStore({\r
296 model: NodeModel,\r
297 folderSort: true,\r
298 root: {\r
299 expanded: true,\r
300 children: [{\r
301 id: 'p1',\r
302 children: [{\r
303 id: 'l1',\r
304 leaf: true\r
305 }, {\r
306 id: 'f1'\r
307 }]\r
308 }, {\r
309 id: 'p2',\r
310 children: [{\r
311 id: 'l2',\r
312 leaf: true\r
313 }, {\r
314 id: 'f2'\r
315 }]\r
316 }]\r
317 }\r
318 });\r
319 expectOrder(byId('p1'), ['f1', 'l1']);\r
320 expectOrder(byId('p2'), ['f2', 'l2']);\r
321 });\r
322\r
323 it("should sort folder/non folder groups by any additional sorters", function() {\r
324 store = new Ext.data.TreeStore({\r
325 model: NodeModel,\r
326 folderSort: true,\r
327 sorters: ['id'],\r
328 root: {\r
329 expanded: true,\r
330 children: [{\r
331 id: 'f4'\r
332 }, {\r
333 id: 'l3'\r
334 }, {\r
335 id: 'f1'\r
336 }, {\r
337 id: 'l1'\r
338 }, {\r
339 id: 'l2'\r
340 }, {\r
341 id: 'f3'\r
342 }, {\r
343 id: 'l4'\r
344 }, {\r
345 id: 'f2'\r
346 }]\r
347 }\r
348 });\r
349 expectOrder(store.getRoot(), ['f1', 'f2', 'f3', 'f4', 'l1', 'l2', 'l3', 'l4']);\r
350 });\r
351 });\r
352\r
353 describe("with folderSort: false", function() {\r
354 it("should sort by existing sorters when setting folderSort: false", function() {\r
355 store = new Ext.data.TreeStore({\r
356 model: NodeModel,\r
357 folderSort: false,\r
358 sorters: ['id'],\r
359 root: {\r
360 expanded: true,\r
361 children: [{\r
362 id: 'a',\r
363 leaf: true\r
364 }, {\r
365 id: 'b'\r
366 }, {\r
367 id: 'c',\r
368 leaf: true\r
369 }, {\r
370 id: 'd'\r
371 }]\r
372 }\r
373 });\r
374 store.setFolderSort(false);\r
375 expectOrder(store.getRoot(), ['a', 'b', 'c', 'd']);\r
376 });\r
377\r
378 it("should do a deep sort", function() {\r
379 store = new Ext.data.TreeStore({\r
380 model: NodeModel,\r
381 folderSort: false,\r
382 sorters: ['id'],\r
383 root: {\r
384 expanded: true,\r
385 children: [{\r
386 id: 'p1',\r
387 expanded: true,\r
388 children: [{\r
389 id: 'b',\r
390 leaf: true\r
391 }, {\r
392 id: 'c',\r
393 leaf: true\r
394 }, {\r
395 id: 'a',\r
396 leaf: true\r
397 }, {\r
398 id: 'd',\r
399 leaf: true\r
400 }]\r
401\r
402 }, {\r
403 id: 'p2',\r
404 expanded: true,\r
405 children: [{\r
406 id: 'g',\r
407 leaf: true\r
408 }, {\r
409 id: 'e',\r
410 leaf: true\r
411 }, {\r
412 id: 'h',\r
413 leaf: true\r
414 }, {\r
415 id: 'f',\r
416 leaf: true\r
417 }]\r
418 }]\r
419 }\r
420 });\r
421 store.setFolderSort(false);\r
422 expectOrder(byId('p1'), ['a', 'b', 'c', 'd']);\r
423 expectOrder(byId('p2'), ['e', 'f', 'g', 'h']);\r
424 });\r
425 });\r
426 });\r
427\r
428 describe("with remote data", function() {\r
429 describe("with folderSort: true", function() {\r
430 it("should sort when setting folderSort dynamically", function() {\r
431 store = new Ext.data.TreeStore({\r
432 model: NodeModel,\r
433 root: {\r
434 expanded: true\r
435 }\r
436 });\r
437 completeWithData([{\r
438 id: 'l1',\r
439 leaf: true\r
440 }, {\r
441 id: 'f1'\r
442 }, {\r
443 id: 'l2',\r
444 leaf: true\r
445 }, {\r
446 id: 'f2'\r
447 }]);\r
448 store.setFolderSort(true);\r
449 expectOrder(store.getRoot(), ['f1', 'f2', 'l1', 'l2']);\r
450 });\r
451\r
452 it("should leave the original sort order if there are no other sorters", function() {\r
453 store = new Ext.data.TreeStore({\r
454 model: NodeModel,\r
455 folderSort: true,\r
456 root: {\r
457 expanded: true\r
458 }\r
459 });\r
460 completeWithData([{\r
461 id: 'l3',\r
462 leaf: true\r
463 }, {\r
464 id: 'l2',\r
465 leaf: true\r
466 }, {\r
467 id: 'f3'\r
468 }, {\r
469 id: 'l1',\r
470 leaf: true\r
471 }, {\r
472 id: 'f2'\r
473 }, {\r
474 id: 'f1'\r
475 }]);\r
476 expectOrder(store.getRoot(), ['f3', 'f2', 'f1', 'l3', 'l2', 'l1']);\r
477 });\r
478\r
479 it("should do a deep sort", function() {\r
480 store = new Ext.data.TreeStore({\r
481 model: NodeModel,\r
482 folderSort: true,\r
483 root: {\r
484 expanded: true\r
485 }\r
486 });\r
487 completeWithData([{\r
488 id: 'p1',\r
489 children: [{\r
490 id: 'l1',\r
491 leaf: true\r
492 }, {\r
493 id: 'f1'\r
494 }]\r
495 }, {\r
496 id: 'p2',\r
497 children: [{\r
498 id: 'l2',\r
499 leaf: true\r
500 }, {\r
501 id: 'f2'\r
502 }]\r
503 }]);\r
504 expectOrder(byId('p1'), ['f1', 'l1']);\r
505 expectOrder(byId('p2'), ['f2', 'l2']);\r
506 });\r
507\r
508 it("should sort folder/non folder groups by any additional sorters", function() {\r
509 store = new Ext.data.TreeStore({\r
510 model: NodeModel,\r
511 folderSort: true,\r
512 sorters: ['id'],\r
513 root: {\r
514 expanded: true\r
515 }\r
516 });\r
517 completeWithData([{\r
518 id: 'f4'\r
519 }, {\r
520 id: 'l3'\r
521 }, {\r
522 id: 'f1'\r
523 }, {\r
524 id: 'l1'\r
525 }, {\r
526 id: 'l2'\r
527 }, {\r
528 id: 'f3'\r
529 }, {\r
530 id: 'l4'\r
531 }, {\r
532 id: 'f2'\r
533 }]);\r
534 expectOrder(store.getRoot(), ['f1', 'f2', 'f3', 'f4', 'l1', 'l2', 'l3', 'l4']);\r
535 });\r
536 });\r
537\r
538 describe("with folderSort: false", function() {\r
539 it("should sort by existing sorters when setting folderSort: false", function() {\r
540 store = new Ext.data.TreeStore({\r
541 model: NodeModel,\r
542 folderSort: false,\r
543 sorters: ['id'],\r
544 root: {\r
545 expanded: true\r
546 }\r
547 });\r
548 completeWithData([{\r
549 id: 'a',\r
550 leaf: true\r
551 }, {\r
552 id: 'b'\r
553 }, {\r
554 id: 'c',\r
555 leaf: true\r
556 }, {\r
557 id: 'd'\r
558 }]);\r
559 store.setFolderSort(false);\r
560 expectOrder(store.getRoot(), ['a', 'b', 'c', 'd']);\r
561 });\r
562\r
563 it("should do a deep sort", function() {\r
564 store = new Ext.data.TreeStore({\r
565 model: NodeModel,\r
566 folderSort: false,\r
567 sorters: ['id'],\r
568 root: {\r
569 expanded: true\r
570 }\r
571 });\r
572 completeWithData([{\r
573 id: 'p1',\r
574 expanded: true,\r
575 children: [{\r
576 id: 'b',\r
577 leaf: true\r
578 }, {\r
579 id: 'c',\r
580 leaf: true\r
581 }, {\r
582 id: 'a',\r
583 leaf: true\r
584 }, {\r
585 id: 'd',\r
586 leaf: true\r
587 }]\r
588\r
589 }, {\r
590 id: 'p2',\r
591 expanded: true,\r
592 children: [{\r
593 id: 'g',\r
594 leaf: true\r
595 }, {\r
596 id: 'e',\r
597 leaf: true\r
598 }, {\r
599 id: 'h',\r
600 leaf: true\r
601 }, {\r
602 id: 'f',\r
603 leaf: true\r
604 }]\r
605 }]);\r
606 store.setFolderSort(false);\r
607 expectOrder(byId('p1'), ['a', 'b', 'c', 'd']);\r
608 expectOrder(byId('p2'), ['e', 'f', 'g', 'h']);\r
609 });\r
610 });\r
611 });\r
612\r
613 describe("adding/expanding nodes", function() {\r
614 it("should sort nodes correctly on expand", function() {\r
615 store = new Ext.data.TreeStore({\r
616 model: NodeModel,\r
617 sorters: ['id'],\r
618 root: {\r
619 expanded: true,\r
620 children: [{\r
621 id: 'a',\r
622 children: [{\r
623 id: 'z'\r
624 }, {\r
625 id: 'y'\r
626 }]\r
627 }, {\r
628 id: 'b',\r
629 children: [{\r
630 id: 'x'\r
631 }, {\r
632 id: 'w'\r
633 }]\r
634 }, {\r
635 id: 'c',\r
636 children: [{\r
637 id: 'v'\r
638 }, {\r
639 id: 'u'\r
640 }]\r
641 }]\r
642 }\r
643 });\r
644\r
645 byId('a').expand();\r
646 expectOrder(byId('a'), ['y', 'z']);\r
647 expectStoreOrder(['a', 'y', 'z', 'b', 'c']);\r
648\r
649 byId('b').expand();\r
650 expectOrder(byId('b'), ['w', 'x']);\r
651 expectStoreOrder(['a', 'y', 'z', 'b', 'w', 'x', 'c']);\r
652\r
653 byId('c').expand();\r
654 expectOrder(byId('c'), ['u', 'v']);\r
655 expectStoreOrder(['a', 'y', 'z', 'b', 'w', 'x', 'c', 'u', 'v']);\r
656 });\r
657\r
658 it("should sort nodes correctly on add", function() {\r
659 store = new Ext.data.TreeStore({\r
660 model: NodeModel,\r
661 sorters: ['id'],\r
662 root: {\r
663 expanded: true,\r
664 children: [{\r
665 id: 'a',\r
666 expanded: true,\r
667 children: []\r
668 }, {\r
669 id: 'b',\r
670 expanded: true,\r
671 children: []\r
672 }, {\r
673 id: 'c',\r
674 expanded: true,\r
675 children: []\r
676 }]\r
677 }\r
678 });\r
679\r
680 byId('a').appendChild([{\r
681 id: 'y'\r
682 }, {\r
683 id: 'z'\r
684 }]);\r
685 expectOrder(byId('a'), ['y', 'z']);\r
686 expectStoreOrder(['a', 'y', 'z', 'b', 'c']);\r
687\r
688 byId('b').appendChild([{\r
689 id: 'w'\r
690 }, {\r
691 id: 'x'\r
692 }]);\r
693 expectOrder(byId('b'), ['w', 'x']);\r
694 expectStoreOrder(['a', 'y', 'z', 'b', 'w', 'x', 'c']);\r
695\r
696 byId('c').appendChild([{\r
697 id: 'u'\r
698 }, {\r
699 id: 'v'\r
700 }]);\r
701 expectOrder(byId('c'), ['u', 'v']);\r
702 expectStoreOrder(['a', 'y', 'z', 'b', 'w', 'x', 'c', 'u', 'v']);\r
703\r
704 });\r
705 });\r
706 });\r
707\r
708 describe("getNodeById", function() {\r
709 it("should return null if there is no matching id", function() {\r
710 store = new Ext.data.TreeStore({\r
711 model: NodeModel,\r
712 root: {\r
713 expanded: true,\r
714 text: 'Root'\r
715 }\r
716 });\r
717 expect(store.getNodeById('foo')).toBeNull();\r
718 });\r
719\r
720 it("should be able to return the root", function() {\r
721 store = new Ext.data.TreeStore({\r
722 model: NodeModel,\r
723 root: {\r
724 expanded: true,\r
725 id: 'root'\r
726 }\r
727 });\r
728 expect(store.getNodeById('root')).toBe(store.getRoot());\r
729 });\r
730\r
731 it("should be able to return a deep node", function() {\r
732 store = new Ext.data.TreeStore({\r
733 model: NodeModel,\r
734 root: {\r
735 expanded: true,\r
736 children: [{\r
737 expanded: true,\r
738 children: [{\r
739 expanded: true,\r
740 children: [{\r
741 expanded: true,\r
742 children: [{\r
743 id: 'deep'\r
744 }]\r
745 }]\r
746 }]\r
747 }]\r
748 }\r
749 });\r
750\r
751 var idNode;\r
752\r
753 store.getRoot().cascadeBy(function(node) {\r
754 if (node.id === 'deep') {\r
755 idNode = node;\r
756 }\r
757 });\r
758\r
759 expect(store.getNodeById('deep')).toBe(idNode);\r
760 });\r
761\r
762 it('should be usable during nodeappend event', function () {\r
763 var ids = [];\r
764\r
765 store = new Ext.data.TreeStore({\r
766 model: NodeModel,\r
767 listeners: {\r
768 nodeappend: function (parent, child, index) {\r
769 ids.push(child.id);\r
770 var treeStore = child.getTreeStore();\r
771 var c = treeStore.getNodeById(child.id);\r
772\r
773 // easy to read output:\r
774 expect(c && c.id).toBe(child.id);\r
775\r
776 // nearly useless output on failure (but not infinite expansion):\r
777 expect(c === child).toBe(true);\r
778 }\r
779 },\r
780 root: {\r
781 expanded: true,\r
782 id: 'root',\r
783 children: [{\r
784 id: 'child',\r
785 expanded: false,\r
786 children: [{\r
787 id: 'leaf'\r
788 }]\r
789 }]\r
790 }\r
791 });\r
792\r
793 expect(ids.join(' ')).toBe('root child leaf');\r
794 });\r
795\r
796 it("should find loaded children of collapsed nodes", function() {\r
797 store = new Ext.data.TreeStore({\r
798 model: NodeModel,\r
799 root: {\r
800 expanded: true,\r
801 children: [{\r
802 expanded: false,\r
803 children: [{\r
804 id: 'leaf'\r
805 }]\r
806 }]\r
807 }\r
808 });\r
809 expect(store.getNodeById('leaf')).toBe(store.getRoot().firstChild.firstChild);\r
810 });\r
811\r
812 it("should find nodes that are filtered out", function() {\r
813 store = new Ext.data.TreeStore({\r
814 model: NodeModel,\r
815 root: {\r
816 expanded: true,\r
817 children: [{\r
818 text: 'A'\r
819 }, {\r
820 text: 'A'\r
821 }, {\r
822 text: 'A'\r
823 }, {\r
824 id: 'bNode',\r
825 text: 'B'\r
826 }]\r
827 }\r
828 });\r
829 expect(store.getCount()).toBe(4);\r
830 store.filter('text', 'A');\r
831 expect(store.getCount()).toBe(3);\r
832 expect(store.getNodeById('bNode')).toBe(store.getRoot().lastChild);\r
833 });\r
834 });\r
835 \r
836 describe("loading data", function() {\r
837 describe("isLoaded", function() {\r
838 it("should be false by default", function() {\r
839 store = new Ext.data.TreeStore({\r
840 root: {\r
841 text: 'Root'\r
842 }\r
843 });\r
844 expect(store.isLoaded()).toBe(false);\r
845 });\r
846\r
847 it("should be true after a load", function() {\r
848 store = new Ext.data.TreeStore({\r
849 root: {\r
850 text: 'Root'\r
851 }\r
852 });\r
853 store.load();\r
854 expect(store.isLoaded()).toBe(true);\r
855 });\r
856 });\r
857\r
858 describe("when loading asynchronously from a url", function() {\r
859 describe("if the root node is expanded", function() {\r
860 it("should load the TreeStore automatically", function() {\r
861 spyOn(Ext.data.TreeStore.prototype, 'load').andCallThrough();\r
862 \r
863 store = new Ext.data.TreeStore({\r
864 model: NodeModel,\r
865 asynchronousLoad: true,\r
866 root: {\r
867 expanded: true,\r
868 id: 0,\r
869 name: 'Root Node'\r
870 }\r
871 });\r
872\r
873 expect(store.load.callCount).toBe(1);\r
874 });\r
875\r
876 describe("with autoLoad: true", function() {\r
877 it("should not load twice with a root defined", function() {\r
878 spyOn(Ext.data.TreeStore.prototype, 'flushLoad').andCallThrough();\r
879\r
880 runs(function() {\r
881 store = Ext.create('Ext.data.TreeStore', {\r
882 model: NodeModel,\r
883 autoLoad: true,\r
884 asynchronousLoad: true,\r
885 root: {\r
886 expanded: true,\r
887 id: 0,\r
888 name: 'Root Node'\r
889 }\r
890 });\r
891 });\r
892 // autoLoad runs on a timer, can't use waitsFor here\r
893 waits(10);\r
894 runs(function() {\r
895 expect(store.flushLoad.callCount).toBe(1);\r
896 });\r
897 });\r
898\r
899 it("should not load twice without a root defined", function() {\r
900 spyOn(Ext.data.TreeStore.prototype, 'flushLoad').andCallThrough();\r
901\r
902 runs(function() {\r
903 store = Ext.create('Ext.data.TreeStore', {\r
904 model: NodeModel,\r
905 autoLoad: true,\r
906 asynchronousLoad: true\r
907 });\r
908 });\r
909\r
910 // autoLoad runs on a timer, can't use waitsFor here\r
911 waits(10);\r
912 runs(function() {\r
913 expect(store.flushLoad.callCount).toBe(1);\r
914 });\r
915 });\r
916 });\r
917 });\r
918 \r
919 describe("if the root node is not expanded", function() {\r
920 beforeEach(function() {\r
921 store = new Ext.data.TreeStore({\r
922 model: NodeModel,\r
923 autoLoad: false,\r
924 asynchronousLoad: true,\r
925 root: {\r
926 expanded: false,\r
927 id: 0,\r
928 name: 'Root Node'\r
929 }\r
930 });\r
931 });\r
932 \r
933 it("should not be loading before load is called", function() {\r
934 expect(store.isLoading()).toBe(false);\r
935 });\r
936\r
937 it("should be loading while the request is still in progress", function() {\r
938 store.load();\r
939 store.flushLoad();\r
940 expect(store.isLoading()).toBe(true);\r
941 });\r
942\r
943 it("should not be loading after the request has finished", function() {\r
944 loadStore(store);\r
945\r
946 expect(store.isLoading()).toBe(false);\r
947 });\r
948 \r
949 describe("if autoLoad is set to true", function() {\r
950 beforeEach(function() {\r
951 spyOn(Ext.data.TreeStore.prototype, 'load').andCallThrough();\r
952\r
953 store = new Ext.data.TreeStore({\r
954 model: NodeModel,\r
955 autoLoad: true,\r
956 asynchronousLoad: true,\r
957 root: {\r
958 expanded: false,\r
959 id: 0,\r
960 name: 'Root Node'\r
961 }\r
962 });\r
963 });\r
964\r
965 it("should load the TreeStore automatically", function() {\r
966 expect(store.load).toHaveBeenCalled();\r
967 });\r
968 });\r
969 });\r
970\r
971 describe("when reloading a store that already contains records", function() {\r
972 beforeEach(function() {\r
973 store = new Ext.data.TreeStore({\r
974 model: NodeModel,\r
975 autoLoad: false,\r
976 asynchronousLoad: false,\r
977 root: {\r
978 expanded: false,\r
979 id: 0,\r
980 name: 'Root Node'\r
981 }\r
982 });\r
983\r
984 store.fillNode(store.getRootNode(), store.getProxy().getReader().readRecords(dummyData.children).getRecords());\r
985 });\r
986\r
987 describe("if records have been removed from the store", function() {\r
988 beforeEach(function() {\r
989 store.getNodeById(1).remove();\r
990 store.getNodeById(5).remove();\r
991 store.getNodeById(4).remove();\r
992 });\r
993 describe("if the node being loaded is the root node", function() {\r
994 beforeEach(function() {\r
995 loadStore(store);\r
996 });\r
997 it("should reset the store's removed array", function() {\r
998 expect(store.getRemovedRecords().length).toBe(0);\r
999 });\r
1000 });\r
1001 describe("if the node being loaded is not the root node", function() {\r
1002 var removed;\r
1003\r
1004 beforeEach(function() {\r
1005 loadStore(store, {node: store.getNodeById(2)});\r
1006 });\r
1007 it("should only remove records from the removed array that were previously descendants of the node being reloaded", function() {\r
1008 removed = store.getRemovedRecords();\r
1009\r
1010 expect(removed.length).toBe(1);\r
1011 expect(removed[0].getId()).toBe(1);\r
1012 });\r
1013 });\r
1014 describe("if clearRemovedOnLoad is false", function() {\r
1015 var removed;\r
1016\r
1017 beforeEach(function() {\r
1018 store.clearRemovedOnLoad = false;\r
1019 loadStore(store);\r
1020 });\r
1021 afterEach(function() {\r
1022 store.clearRemovedOnLoad = true;\r
1023 });\r
1024 it("should not alter the store's removed array", function() {\r
1025 removed = store.getRemovedRecords();\r
1026\r
1027 expect(removed.length).toBe(3);\r
1028 expect(removed[0].getId()).toBe(1);\r
1029 expect(removed[1].getId()).toBe(5);\r
1030 expect(removed[2].getId()).toBe(4);\r
1031 });\r
1032 });\r
1033\r
1034 });\r
1035\r
1036 });\r
1037\r
1038 describe("when the records in the response data have an index field", function() {\r
1039 beforeEach(function() {\r
1040 dummyData = {\r
1041 success: true,\r
1042 children: [{\r
1043 id: 1, \r
1044 name: "aaa", \r
1045 index: 2\r
1046 },{\r
1047 id: 2, \r
1048 name: "bbb", \r
1049 index: 0, \r
1050 children: [{\r
1051 id: 3, \r
1052 name: "ccc", \r
1053 index: 1\r
1054 },{\r
1055 id: 4, \r
1056 name: "ddd", \r
1057 index: 0\r
1058 }],\r
1059 expanded: true\r
1060 },{\r
1061 id: 5, \r
1062 name: "eee", \r
1063 index: 1\r
1064 }]\r
1065 };\r
1066\r
1067 store = new Ext.data.TreeStore({\r
1068 model: NodeModel,\r
1069 root: {\r
1070 expanded: true,\r
1071 id: 0,\r
1072 name: 'Root Node'\r
1073 }\r
1074 });\r
1075\r
1076 loadStore(store);\r
1077 });\r
1078\r
1079 it("should sort the root level nodes by index", function() {\r
1080 // use getRootNode (as opposed to new getter getRoot) to test backward compatibilty.\r
1081 expect(store.getRootNode().childNodes[0].getId()).toBe(2);\r
1082 expect(store.getRootNode().childNodes[1].getId()).toBe(5);\r
1083 expect(store.getRootNode().childNodes[2].getId()).toBe(1);\r
1084 });\r
1085\r
1086 it("should sort descendants by index", function() {\r
1087 expect(store.getNodeById(2).firstChild.getId()).toBe(4);\r
1088 expect(store.getNodeById(2).lastChild.getId()).toBe(3);\r
1089 });\r
1090\r
1091 it("should sort folders first, then in index order", function() {\r
1092 expect(store.getAt(0).getId()).toBe(2);\r
1093 expect(store.getAt(1).getId()).toBe(4);\r
1094 expect(store.getAt(2).getId()).toBe(3);\r
1095 expect(store.getAt(3).getId()).toBe(5);\r
1096 expect(store.getAt(4).getId()).toBe(1);\r
1097 });\r
1098 });\r
1099 });\r
1100 \r
1101 describe("clearOnLoad", function(){\r
1102 \r
1103 beforeEach(function(){\r
1104 store = new Ext.data.TreeStore({\r
1105 model: NodeModel,\r
1106 asynchronousLoad: false,\r
1107 root: {\r
1108 expanded: true,\r
1109 id: 0,\r
1110 name: 'Root Node'\r
1111 }\r
1112 });\r
1113 completeWithData({\r
1114 children: []\r
1115 });\r
1116 });\r
1117 \r
1118 it("should remove existing nodes with clearOnLoad: true", function(){\r
1119 dummyData = {\r
1120 children: []\r
1121 };\r
1122 var root = store.getRootNode();\r
1123 root.appendChild({\r
1124 id: 'node1',\r
1125 text: 'A'\r
1126 });\r
1127 \r
1128 root.appendChild({\r
1129 id: 'node2',\r
1130 text: 'B'\r
1131 });\r
1132 loadStore(store);\r
1133 expect(store.getRootNode().childNodes.length).toBe(0);\r
1134 expect(store.getNodeById('node1')).toBeNull();\r
1135 expect(store.getNodeById('node2')).toBeNull();\r
1136 });\r
1137 \r
1138 it("should leave existing nodes with clearOnLoad: false", function(){\r
1139 store.clearOnLoad = false;\r
1140 dummyData = {\r
1141 children: []\r
1142 }; \r
1143 var root = store.getRootNode(),\r
1144 childNodes = root.childNodes,\r
1145 node1, node2;\r
1146\r
1147 root.appendChild({\r
1148 id: 'node1',\r
1149 text: 'A'\r
1150 });\r
1151 node1 = childNodes[0];\r
1152 \r
1153 root.appendChild({\r
1154 id: 'node2',\r
1155 text: 'B'\r
1156 });\r
1157 node2 = childNodes[1];\r
1158\r
1159 loadStore(store);\r
1160 expect(childNodes.length).toBe(2);\r
1161 expect(store.getNodeById('node1')).toBe(node1);\r
1162 expect(store.getNodeById('node2')).toBe(node2);\r
1163 });\r
1164 \r
1165 it("should ignore dupes with clearOnLoad: false", function(){\r
1166 store.clearOnLoad = false;\r
1167 dummyData = {\r
1168 children: [{\r
1169 id: 'node1',\r
1170 text: 'A'\r
1171 }, {\r
1172 id: 'node3',\r
1173 text: 'C'\r
1174 }]\r
1175 }; \r
1176 var root = store.getRootNode();\r
1177 root.appendChild({\r
1178 id: 'node1',\r
1179 text: 'A'\r
1180 });\r
1181 \r
1182 root.appendChild({\r
1183 id: 'node2',\r
1184 text: 'B'\r
1185 });\r
1186 loadStore(store);\r
1187 expect(store.getRootNode().childNodes.length).toBe(3);\r
1188 });\r
1189 });\r
1190 });\r
1191\r
1192 describe('adding data', function () {\r
1193 // See EXTJS-13509.\r
1194 var root, child;\r
1195\r
1196 afterEach(function () {\r
1197 Ext.destroy(store);\r
1198 root = child = null;\r
1199 });\r
1200\r
1201 describe('adding non-leaf nodes with children', function () {\r
1202 var root, child;\r
1203\r
1204 function doIt(desc, method) {\r
1205 describe(desc + ' an existing node', function () {\r
1206 doAdd(method, false);\r
1207 doAdd(method, true);\r
1208 });\r
1209 }\r
1210\r
1211 function doAdd(method, expanded) {\r
1212 describe('expanded: ' + expanded.toString(), function () {\r
1213 it('should add the node and create its child nodes', function () {\r
1214 root[method]({\r
1215 text: 'child',\r
1216 expanded: expanded,\r
1217 children: [{\r
1218 text: 'detention',\r
1219 expanded: expanded,\r
1220 children: [{\r
1221 text: 'ben',\r
1222 leaf: true\r
1223 }, {\r
1224 text: 'bill',\r
1225 leaf: true\r
1226 }]\r
1227 }]\r
1228 });\r
1229\r
1230 child = store.getNewRecords()[0];\r
1231 expect(child.childNodes.length).toBe(1);\r
1232 expect(child.firstChild.childNodes.length).toBe(2);\r
1233 expect(store.getNewRecords().length).toBe(4);\r
1234 });\r
1235\r
1236 it('should mark the new nodes as "loaded"', function () {\r
1237 expect(child.get('loaded')).toBe(true);\r
1238 expect(child.firstChild.get('loaded')).toBe(true);\r
1239 });\r
1240 });\r
1241 }\r
1242\r
1243 beforeEach(function () {\r
1244 store = new Ext.data.TreeStore({\r
1245 root: {\r
1246 name: 'Root Node'\r
1247 }\r
1248 });\r
1249\r
1250 root = store.getRootNode();\r
1251 });\r
1252\r
1253 doIt('appending to', 'appendChild');\r
1254 doIt('inserting before', 'insertBefore');\r
1255 });\r
1256\r
1257 describe('adding childless non-leaf nodes', function () {\r
1258 beforeEach(function () {\r
1259 spyOn(Ext.data.TreeStore.prototype, 'load').andCallThrough();\r
1260\r
1261 store = new Ext.data.TreeStore({\r
1262 model: NodeModel,\r
1263 root: {\r
1264 name: 'Root Node'\r
1265 }\r
1266 });\r
1267\r
1268 root = store.getRootNode();\r
1269\r
1270 root.appendChild({\r
1271 text: 'child2',\r
1272 expanded: false\r
1273 });\r
1274 });\r
1275\r
1276 it('should not make a request for data when expanded', function () {\r
1277 root.firstChild.expand();\r
1278 expect(store.load).not.toHaveBeenCalled();\r
1279 });\r
1280 });\r
1281 });\r
1282\r
1283 describe("modifying records", function() {\r
1284 it("should fire the update event and pass the store, record, type & modified fields", function() {\r
1285 store = new Ext.data.TreeStore({\r
1286 model: NodeModel,\r
1287 root: {\r
1288 expanded: true,\r
1289 text: 'Root',\r
1290 children: [{\r
1291 text: 'A child',\r
1292 someProp: 'a'\r
1293 }]\r
1294 }\r
1295 });\r
1296\r
1297 var rec = store.getRoot().firstChild,\r
1298 spy = jasmine.createSpy();\r
1299\r
1300 store.on('update', spy);\r
1301 rec.set('someProp', 'b');\r
1302 expect(spy).toHaveBeenCalled();\r
1303 var args = spy.mostRecentCall.args;\r
1304 expect(args[0]).toBe(store);\r
1305 expect(args[1]).toBe(rec);\r
1306 expect(args[2]).toBe(Ext.data.Model.EDIT);\r
1307 expect(args[3]).toEqual(['someProp']);\r
1308 });\r
1309\r
1310 it("should fire the update event and pass the store, record, type & modified fields when attached to another store", function() {\r
1311 store = new Ext.data.TreeStore({\r
1312 model: NodeModel,\r
1313 root: {\r
1314 expanded: true,\r
1315 text: 'Root',\r
1316 children: [{\r
1317 text: 'A child',\r
1318 someProp: 'a'\r
1319 }]\r
1320 }\r
1321 });\r
1322\r
1323 var rec = store.getRoot().firstChild,\r
1324 spy = jasmine.createSpy();\r
1325\r
1326 var other = new Ext.data.Store({\r
1327 model: NodeModel,\r
1328 data: [rec]\r
1329 });\r
1330\r
1331 store.on('update', spy);\r
1332 rec.set('someProp', 'b');\r
1333 expect(spy).toHaveBeenCalled();\r
1334 var args = spy.mostRecentCall.args;\r
1335 expect(args[0]).toBe(store);\r
1336 expect(args[1]).toBe(rec);\r
1337 expect(args[2]).toBe(Ext.data.Model.EDIT);\r
1338 expect(args[3]).toEqual(['someProp']);\r
1339 });\r
1340 });\r
1341\r
1342 describe("saving data", function() {\r
1343 var record, records, syncSpy;\r
1344\r
1345 beforeEach(function() {\r
1346 store = new Ext.data.TreeStore({\r
1347 model: NodeModel,\r
1348 asynchronousLoad: false,\r
1349 root: {\r
1350 expanded: true,\r
1351 name: 'Root Node'\r
1352 }\r
1353 });\r
1354\r
1355 loadStore(store);\r
1356\r
1357 // If overriding the sync, we need to clear the needsSync flag so that future endUpdate calls do not sync again\r
1358 syncSpy = spyOn(store, 'sync').andCallFake(function() {\r
1359 this.needsSync = false;\r
1360 });\r
1361 });\r
1362\r
1363 describe("creating records", function() {\r
1364 describe("appending a single node", function() {\r
1365 beforeEach(function() {\r
1366 record = new NodeModel({name: 'Phil'});\r
1367 store.getRootNode().appendChild(record);\r
1368 });\r
1369\r
1370 it("should add the node to getNewRecords", function() {\r
1371 records = store.getNewRecords();\r
1372 expect(records.length).toBe(1);\r
1373 expect(records[0]).toBe(record);\r
1374 });\r
1375\r
1376 it("should not add anything to getUpdatedRecords", function() {\r
1377 expect(store.getUpdatedRecords().length).toBe(0);\r
1378 });\r
1379\r
1380 it("should not sync the store", function() {\r
1381 expect(syncSpy).not.toHaveBeenCalled();\r
1382 });\r
1383 });\r
1384\r
1385 describe("inserting a single node", function() {\r
1386 beforeEach(function() {\r
1387 record = new NodeModel({name: 'Phil'});\r
1388 store.getNodeById(2).insertBefore(record, store.getNodeById(4));\r
1389 });\r
1390\r
1391 it("should add the node to getNewRecords", function() {\r
1392 records = store.getNewRecords();\r
1393 expect(records.length).toBe(1);\r
1394 expect(records[0]).toBe(record);\r
1395 });\r
1396\r
1397 it("should not add any records to getUpdatedRecords", function() {\r
1398 expect(store.getUpdatedRecords().length).toBe(0);\r
1399 });\r
1400\r
1401 it("should not sync the store", function() {\r
1402 expect(syncSpy).not.toHaveBeenCalled();\r
1403 });\r
1404 });\r
1405\r
1406 describe("appending and inserting multiple nodes", function() {\r
1407 var record1, record2, record3;\r
1408\r
1409 beforeEach(function() {\r
1410 record1 = new NodeModel({name: '1'});\r
1411 record2 = new NodeModel({name: '2'});\r
1412 record3 = new NodeModel({name: '3'});\r
1413\r
1414\r
1415 store.getRootNode().appendChild(record1);\r
1416 store.getNodeById(2).insertBefore(record2, store.getNodeById(4));\r
1417 record2.appendChild(record3);\r
1418 });\r
1419\r
1420 it("should add the nodes to getNewRecords", function() {\r
1421 var newRecords = store.getNewRecords();\r
1422 expect(newRecords.length).toBe(3);\r
1423 expect(Ext.Array.contains(newRecords, record1)).toBe(true);\r
1424 expect(Ext.Array.contains(newRecords, record2)).toBe(true);\r
1425 expect(Ext.Array.contains(newRecords, record3)).toBe(true);\r
1426 });\r
1427\r
1428 it("should not add any records to getUpdatedRecords", function() {\r
1429 expect(store.getUpdatedRecords().length).toBe(0);\r
1430 });\r
1431\r
1432 it("should not sync the store", function() {\r
1433 expect(syncSpy).not.toHaveBeenCalled();\r
1434 });\r
1435 });\r
1436\r
1437 describe("when the index field is persistent", function() {\r
1438 beforeEach(function() {\r
1439 NodeModel.getField('index').persist = true;\r
1440 });\r
1441 afterEach(function() {\r
1442 NodeModel.getField('index').persist = false;\r
1443 });\r
1444\r
1445 describe("appending a single node", function() {\r
1446 beforeEach(function() {\r
1447 record = new NodeModel({name: 'Phil'});\r
1448 store.getRootNode().appendChild(record);\r
1449 });\r
1450\r
1451 it("should add the node to getNewRecords", function() {\r
1452 records = store.getNewRecords();\r
1453 expect(records.length).toBe(1);\r
1454 expect(records[0]).toBe(record);\r
1455 });\r
1456\r
1457 it("should not add any records to getUpdatedRecords", function() {\r
1458 expect(store.getUpdatedRecords().length).toBe(0);\r
1459 });\r
1460 });\r
1461\r
1462 describe("inserting a single node", function() {\r
1463 beforeEach(function() {\r
1464 record = new NodeModel({name: 'Phil'});\r
1465 store.getNodeById(2).insertBefore(record, store.getNodeById(3));\r
1466 });\r
1467\r
1468 it("should add the node to getNewRecords", function() {\r
1469 records = store.getNewRecords();\r
1470 expect(records.length).toBe(1);\r
1471 expect(records[0]).toBe(record);\r
1472 });\r
1473\r
1474 it("should add all of its sibling nodes that come after the insertion point to getUpdatedRecords", function() {\r
1475 records = store.getUpdatedRecords();\r
1476 expect(records.length).toBe(2);\r
1477 expect(Ext.Array.contains(records, store.getNodeById(3))).toBe(true);\r
1478 expect(Ext.Array.contains(records, store.getNodeById(4))).toBe(true);\r
1479 });\r
1480 });\r
1481 });\r
1482\r
1483 describe("when autoSync is true", function() {\r
1484 beforeEach(function() {\r
1485 store.autoSync = true;\r
1486 });\r
1487\r
1488 describe("appending a single node", function() {\r
1489 beforeEach(function() {\r
1490 record = new NodeModel({name: 'Phil'});\r
1491 store.getRootNode().appendChild(record);\r
1492 });\r
1493\r
1494 it("should sync the store", function() {\r
1495 expect(syncSpy.callCount).toBe(1);\r
1496 });\r
1497 });\r
1498\r
1499 describe("inserting a single node", function() {\r
1500 beforeEach(function() {\r
1501 record = new NodeModel({name: 'Phil'});\r
1502 store.getNodeById(2).insertBefore(record, store.getNodeById(4));\r
1503 });\r
1504\r
1505 it("should sync the store", function() {\r
1506 expect(syncSpy.callCount).toBe(1);\r
1507 });\r
1508 });\r
1509 });\r
1510 });\r
1511\r
1512 describe("updating records", function() {\r
1513 describe("updating multiple records", function() {\r
1514 beforeEach(function() {\r
1515 store.getNodeById(2).set('name', '222');\r
1516 store.getNodeById(3).set('name', '333');\r
1517 });\r
1518\r
1519 it("should add the nodes to getUpdatedRecords", function() {\r
1520 records = store.getUpdatedRecords();\r
1521 expect(records.length).toBe(2);\r
1522 expect(Ext.Array.contains(records, store.getNodeById(2))).toBe(true);\r
1523 expect(Ext.Array.contains(records, store.getNodeById(3))).toBe(true);\r
1524 });\r
1525\r
1526 it("should not sync the store", function() {\r
1527 expect(syncSpy).not.toHaveBeenCalled();\r
1528 });\r
1529 });\r
1530\r
1531 describe("moving records", function() {\r
1532 describe("within the same parent node", function() {\r
1533 beforeEach(function() {\r
1534 store.getRootNode().insertBefore(store.getNodeById(6), store.getNodeById(1));\r
1535 });\r
1536\r
1537 it("should not add any records to getUpdatedRecords", function() {\r
1538 expect(store.getUpdatedRecords().length).toBe(0);\r
1539 });\r
1540\r
1541 it("should not sync the store", function() {\r
1542 expect(syncSpy).not.toHaveBeenCalled();\r
1543 });\r
1544 });\r
1545\r
1546 describe("to a different parent node", function() {\r
1547 beforeEach(function() {\r
1548 store.getNodeById(4).insertBefore(store.getNodeById(1), store.getNodeById(5));\r
1549 });\r
1550\r
1551 it("should add the node to getUpdatedRecords", function() {\r
1552 records = store.getUpdatedRecords();\r
1553 expect(records.length).toBe(1);\r
1554 expect(records[0]).toBe(store.getNodeById(1));\r
1555 });\r
1556\r
1557 it("should not sync the store", function() {\r
1558 expect(syncSpy).not.toHaveBeenCalled();\r
1559 });\r
1560 });\r
1561 });\r
1562\r
1563 describe("moving records when the index field is persistent", function() {\r
1564 beforeEach(function() {\r
1565 NodeModel.getField('index').persist = true;\r
1566 });\r
1567 afterEach(function() {\r
1568 NodeModel.getField('index').persist = false;\r
1569 });\r
1570\r
1571 describe("within the same parent node", function() {\r
1572 beforeEach(function() {\r
1573 store.getRootNode().insertBefore(store.getNodeById(6), store.getNodeById(1));\r
1574 });\r
1575\r
1576 it("should add the node and all sibling nodes after it to getUpdatedRecords", function() {\r
1577 records = store.getUpdatedRecords();\r
1578 expect(records.length).toBe(3);\r
1579 expect(Ext.Array.contains(records, store.getNodeById(1))).toBe(true);\r
1580 expect(Ext.Array.contains(records, store.getNodeById(2))).toBe(true);\r
1581 expect(Ext.Array.contains(records, store.getNodeById(6))).toBe(true);\r
1582 });\r
1583 });\r
1584\r
1585 describe("to a different parent node", function() {\r
1586 beforeEach(function() {\r
1587 store.getNodeById(4).insertBefore(store.getNodeById(1), store.getNodeById(5));\r
1588 });\r
1589\r
1590 it("should add the node, all sibling nodes after it's insertion point, and all siblings after its removal point to getUpdatedRecords", function() {\r
1591 records = store.getUpdatedRecords();\r
1592 expect(records.length).toBe(4);\r
1593 expect(Ext.Array.contains(records, store.getNodeById(1))).toBe(true);\r
1594 expect(Ext.Array.contains(records, store.getNodeById(2))).toBe(true);\r
1595 expect(Ext.Array.contains(records, store.getNodeById(5))).toBe(true);\r
1596 expect(Ext.Array.contains(records, store.getNodeById(6))).toBe(true);\r
1597 });\r
1598 });\r
1599 });\r
1600\r
1601 describe("moving records when autoSync is true", function() {\r
1602 beforeEach(function() {\r
1603 store.autoSync = true;\r
1604 });\r
1605\r
1606 describe("within the same parent node", function() {\r
1607 beforeEach(function() {\r
1608 store.getRootNode().insertBefore(store.getNodeById(6), store.getNodeById(1));\r
1609 });\r
1610\r
1611 // The parentId field is persistent. Has not been changed in this case.\r
1612 it("should not sync the store", function() {\r
1613 expect(syncSpy).not.toHaveBeenCalled();\r
1614 });\r
1615 });\r
1616\r
1617 describe("to a different parent node", function() {\r
1618 beforeEach(function() {\r
1619 store.getNodeById(4).insertBefore(store.getNodeById(1), store.getNodeById(5));\r
1620 });\r
1621\r
1622 // The parentId field is persistent. Has been changed, so store is dirty\r
1623 it("should sync the store", function() {\r
1624 expect(syncSpy.callCount).toBe(1);\r
1625 });\r
1626 });\r
1627\r
1628 describe("to a different TreeStore", function() {\r
1629 var otherStore,\r
1630 otherSyncSpy;\r
1631\r
1632 beforeEach(function() {\r
1633 otherStore =new Ext.data.TreeStore({\r
1634 model: NodeModel,\r
1635 root: {\r
1636 expanded: true,\r
1637 name: 'Root Node'\r
1638 },\r
1639 autoSync: true\r
1640 });\r
1641 otherSyncSpy = spyOn(otherStore, 'sync').andCallFake(function() {\r
1642 this.needsSync = false;\r
1643 });\r
1644 otherStore.getRootNode().appendChild(store.getNodeById(1));\r
1645 });\r
1646 afterEach(function() {\r
1647 otherStore.destroy();\r
1648 });\r
1649\r
1650 it("should sync both the stores", function() {\r
1651 expect(syncSpy.callCount).toBe(1);\r
1652 expect(otherSyncSpy.callCount).toBe(1);\r
1653 });\r
1654 });\r
1655 \r
1656 });\r
1657 });\r
1658\r
1659 describe("removing records", function() {\r
1660 describe("removing a single record", function() {\r
1661 beforeEach(function() {\r
1662 record = store.getNodeById(1).remove();\r
1663 });\r
1664\r
1665 it("should add the node to getRemovedRecords", function() {\r
1666 records = store.getRemovedRecords();\r
1667 expect(records.length).toBe(1);\r
1668 expect(records[0]).toBe(record);\r
1669 });\r
1670\r
1671 it("should not add any records to getUpdatedRecords", function() {\r
1672 expect(store.getUpdatedRecords().length).toBe(0);\r
1673 });\r
1674\r
1675 it("should not sync the store", function() {\r
1676 expect(syncSpy).not.toHaveBeenCalled();\r
1677 });\r
1678 \r
1679 it("should not add phantom records to the removed collection", function(){\r
1680 var node = new NodeModel(),\r
1681 root = store.getRootNode();\r
1682 \r
1683 root.appendChild(node);\r
1684 root.removeChild(node);\r
1685 expect(Ext.Array.contains(store.getRemovedRecords(), node)).toBe(false); \r
1686 });\r
1687 });\r
1688\r
1689 describe("removing multiple records", function() {\r
1690 var record2;\r
1691\r
1692 beforeEach(function() {\r
1693 record = store.getNodeById(1).remove();\r
1694 record2 = store.getNodeById(4).remove();\r
1695 });\r
1696\r
1697 it("should add the nodes to getRemovedRecords", function() {\r
1698 records = store.getRemovedRecords();\r
1699\r
1700 // 1, 4, and 4's sole child 5 should be in the removed list.\r
1701 expect(records.length).toBe(3);\r
1702 expect(Ext.Array.contains(records, record)).toBe(true);\r
1703 expect(Ext.Array.contains(records, record2)).toBe(true);\r
1704 });\r
1705\r
1706 it("should not add any records to getUpdatedRecords", function() {\r
1707 expect(store.getUpdatedRecords().length).toBe(0);\r
1708 });\r
1709\r
1710 it("should not sync the store", function() {\r
1711 expect(syncSpy).not.toHaveBeenCalled();\r
1712 });\r
1713 });\r
1714\r
1715\r
1716 describe("when the index field is persistent", function() {\r
1717 beforeEach(function() {\r
1718 NodeModel.getField('index').persist = true;\r
1719 });\r
1720 afterEach(function() {\r
1721 NodeModel.getField('index').persist = false;\r
1722 });\r
1723\r
1724 describe("removing a single record", function() {\r
1725 beforeEach(function() {\r
1726 record = store.getNodeById(1).remove();\r
1727 });\r
1728\r
1729 it("should add the node to getRemovedRecords", function() {\r
1730 records = store.getRemovedRecords();\r
1731 expect(records.length).toBe(1);\r
1732 expect(records[0]).toBe(record);\r
1733 });\r
1734\r
1735 it("should add all siblings after the node's removal point to getUpdatedRecords", function() {\r
1736 records = store.getUpdatedRecords();\r
1737 expect(records.length).toBe(2);\r
1738 expect(Ext.Array.contains(records, store.getNodeById(2))).toBe(true);\r
1739 expect(Ext.Array.contains(records, store.getNodeById(6))).toBe(true);\r
1740 });\r
1741 });\r
1742 });\r
1743\r
1744 describe("when autoSync is true", function() {\r
1745 beforeEach(function() {\r
1746 store.autoSync = true;\r
1747 });\r
1748\r
1749 describe("removing a single record", function() {\r
1750 beforeEach(function() {\r
1751 store.getNodeById(1).remove();\r
1752 });\r
1753\r
1754 it("should sync the store", function() {\r
1755 expect(syncSpy.callCount).toBe(1);\r
1756 });\r
1757 });\r
1758 });\r
1759 });\r
1760\r
1761 describe("sorting", function() {\r
1762 var sortByNameDesc = function(node1, node2) {\r
1763 var name1 = node1.data.name,\r
1764 name2 = node2.data.name;\r
1765\r
1766 return name1 < name2 ? 1 : node1 === node2 ? 0 : -1;\r
1767 };\r
1768\r
1769 describe("when sorting the TreeStore", function() {\r
1770 var beforeSortSpy,\r
1771 sortSpy;\r
1772\r
1773 beforeEach(function() {\r
1774 beforeSortSpy = spyOnEvent(store, 'beforesort');\r
1775 sortSpy = spyOnEvent(store, 'sort');\r
1776 store.sort(sortByNameDesc);\r
1777 });\r
1778\r
1779 it("should not add any records to getUpdatedRecords", function() {\r
1780 expect(store.getUpdatedRecords().length).toBe(0);\r
1781\r
1782 // Expected events must have fired.\r
1783 expect(beforeSortSpy.callCount).toBe(1);\r
1784 expect(sortSpy.callCount).toBe(1);\r
1785 });\r
1786 });\r
1787\r
1788 describe("when sorting recursively", function() {\r
1789 beforeEach(function() {\r
1790 store.getRootNode().sort(sortByNameDesc, true);\r
1791 });\r
1792\r
1793 it("should not add any records to getUpdatedRecords", function() {\r
1794 expect(store.getUpdatedRecords().length).toBe(0);\r
1795 });\r
1796 });\r
1797\r
1798 describe("when sorting non-recursively", function() {\r
1799 beforeEach(function() {\r
1800 store.getRootNode().sort(sortByNameDesc);\r
1801 });\r
1802\r
1803 it("should not add any records to getUpdatedRecords", function() {\r
1804 expect(store.getUpdatedRecords().length).toBe(0);\r
1805 });\r
1806 });\r
1807\r
1808 describe("when the index field is persistent and autoSync is true", function() {\r
1809 beforeEach(function() {\r
1810 NodeModel.getField('index').persist = true;\r
1811 store.autoSync = true;\r
1812 });\r
1813 afterEach(function() {\r
1814 NodeModel.getField('index').persist = false;\r
1815 });\r
1816\r
1817 describe("when sorting recursively", function() {\r
1818 beforeEach(function() {\r
1819 store.getRootNode().sort(sortByNameDesc, true);\r
1820 });\r
1821\r
1822 it("should add all nodes at all levels that had an index change to getUpdatedRecords", function() {\r
1823 records = store.getUpdatedRecords();\r
1824 expect(records.length).toBe(4);\r
1825 expect(Ext.Array.contains(records, store.getNodeById(1))).toBe(true);\r
1826 expect(Ext.Array.contains(records, store.getNodeById(3))).toBe(true);\r
1827 expect(Ext.Array.contains(records, store.getNodeById(4))).toBe(true);\r
1828 expect(Ext.Array.contains(records, store.getNodeById(6))).toBe(true);\r
1829 });\r
1830\r
1831 it("should sync the store", function() {\r
1832 expect(syncSpy.callCount).toBe(1);\r
1833 });\r
1834 });\r
1835\r
1836 describe("when sorting non-recursively", function() {\r
1837 beforeEach(function() {\r
1838 store.getRootNode().sort(sortByNameDesc);\r
1839 });\r
1840\r
1841 it("should add all nodes at depth 1 that had an index change to getUpdatedRecords", function() {\r
1842 records = store.getUpdatedRecords();\r
1843 expect(records.length).toBe(2);\r
1844 expect(Ext.Array.contains(records, store.getNodeById(1))).toBe(true);\r
1845 expect(Ext.Array.contains(records, store.getNodeById(6))).toBe(true);\r
1846 });\r
1847\r
1848 it("should sync the store", function() {\r
1849 expect(syncSpy.callCount).toBe(1);\r
1850 });\r
1851 });\r
1852 });\r
1853 });\r
1854 });\r
1855\r
1856 describe('Loading TreeStore using root config', function() {\r
1857 it('should load the root nodes children using Proxy\'s "root" config', function() {\r
1858 // Suppress console error\r
1859 spyOn(Ext.log, 'error');\r
1860 var store = new Ext.data.TreeStore({\r
1861 root: {\r
1862 expanded: true,\r
1863 CHILDREN: [\r
1864 { text: "detention", leaf: true },\r
1865 { text: "homework", expanded: true, CHILDREN: [\r
1866 { text: "book report", leaf: true },\r
1867 { text: "alegrbra", leaf: true}\r
1868 ] },\r
1869 { text: "buy lottery tickets", leaf: true }\r
1870 ]\r
1871 },\r
1872 proxy: {\r
1873 type: "memory",\r
1874 reader: {\r
1875 type: "json",\r
1876 rootProperty: "CHILDREN"\r
1877 }\r
1878 }\r
1879 });\r
1880 var cn = store.getRootNode().childNodes;\r
1881 expect(cn.length).toBe(3);\r
1882 expect(cn[0].childNodes.length).toBe(0);\r
1883 expect(cn[1].childNodes.length).toBe(2);\r
1884 expect(cn[2].childNodes.length).toBe(0);\r
1885 });\r
1886 });\r
1887 \r
1888 describe("default node id", function() {\r
1889 it('Should use generate an ID if the idProperty is null in the incoming data', function() {\r
1890 store = new Ext.data.TreeStore({\r
1891 model: TaskModel,\r
1892 defaultRootId: null,\r
1893 root : {\r
1894 }\r
1895 });\r
1896 expect(store.getRootNode().getId()).not.toBeNull();\r
1897 });\r
1898 it('Should use "root" as the defaultRootId, and parse that according to the idProperty field type', function() {\r
1899 // The idProperty field is an int, so this should raise an error\r
1900 expect(function() {\r
1901 store = new Ext.data.TreeStore({\r
1902 model: TaskModel,\r
1903 root : {\r
1904 }\r
1905 });\r
1906 }).toRaiseExtError();\r
1907 });\r
1908\r
1909 it('Should use the configured defaultRootId, and parse that according to the idProperty field type', function() {\r
1910 store = new Ext.data.TreeStore({\r
1911 model: TaskModel,\r
1912 defaultRootId: -1,\r
1913 root : {\r
1914 }\r
1915 });\r
1916 expect(store.getRootNode().getId()).toBe(-1);\r
1917 });\r
1918 });\r
1919 \r
1920 describe('moving root node between trees', function() {\r
1921 it('should move root and all descendants from source tree into destination tree', function() {\r
1922 store = new Ext.data.TreeStore({\r
1923 root: {\r
1924 expanded: true, \r
1925 children: [{\r
1926 text: "Test",\r
1927 leaf: true,\r
1928 id: 'testId'\r
1929 }]\r
1930 },\r
1931 listeners: {\r
1932 rootchange: function(newRoot, oldRoot) {\r
1933 oldStoreRootChangeArgs = [newRoot, oldRoot];\r
1934 },\r
1935 refresh: function() {\r
1936 storeRefreshed++;\r
1937 },\r
1938 add: function() {\r
1939 added++;\r
1940 },\r
1941 remove: function() {\r
1942 removed++;\r
1943 }\r
1944 }\r
1945 });\r
1946\r
1947 var rootNode = store.getRootNode(),\r
1948 childNode = rootNode.firstChild,\r
1949 store2 = new Ext.data.TreeStore({\r
1950 listeners: {\r
1951 rootchange: function(newRoot, oldRoot) {\r
1952 newStoreRootChangeArgs = [newRoot, oldRoot];\r
1953 },\r
1954 refresh: function() {\r
1955 store2Refreshed++;\r
1956 },\r
1957 add: function() {\r
1958 added++;\r
1959 },\r
1960 remove: function() {\r
1961 removed++;\r
1962 }\r
1963 },\r
1964 root: {\r
1965 }\r
1966 }),\r
1967 storeRefreshed = 0,\r
1968 store2Refreshed = 0,\r
1969 added = 0,\r
1970 removed = 0,\r
1971 store2Root = store2.getRootNode(),\r
1972 oldStoreRootChangeArgs = [],\r
1973 newStoreRootChangeArgs = [];\r
1974\r
1975 // TreeStore set up as expected\r
1976 expect(rootNode.rootOf === store.tree).toBe(true);\r
1977 expect(store.getNodeById('testId') === childNode).toBe(true);\r
1978\r
1979 // Move the root to a new TreeStore and check it's set up as expected.\r
1980 store2.setRootNode(rootNode);\r
1981\r
1982 // Old store has gone from rootNode to null\r
1983 expect(oldStoreRootChangeArgs[0]).toEqual(null);\r
1984 expect(oldStoreRootChangeArgs[1]).toEqual(rootNode);\r
1985\r
1986 // Second store has gone from store2Root to rootNode\r
1987 expect(newStoreRootChangeArgs[0]).toEqual(rootNode);\r
1988 expect(newStoreRootChangeArgs[1]).toEqual(store2Root);\r
1989\r
1990 // Both stores should fire a refresh event\r
1991 expect(storeRefreshed).toBe(1);\r
1992 expect(store2Refreshed).toBe(1);\r
1993\r
1994 // Add and remove events should be suspended for the root change operation\r
1995 expect(added).toBe(0);\r
1996 expect(removed).toBe(0);\r
1997\r
1998 expect(rootNode.rootOf === store2.tree).toBe(true);\r
1999 expect(store2.getRootNode() === rootNode).toBe(true);\r
2000 expect(store2.getNodeById('testId') === childNode).toBe(true);\r
2001\r
2002 // Child node must not be registered with the old TreeStore\r
2003 expect(store.getNodeById('testId')).toBeFalsy();\r
2004\r
2005 // Old TreeStore must not have a root\r
2006 expect(store.getRootNode()).toBeFalsy();\r
2007 store2.destroy();\r
2008 });\r
2009 });\r
2010\r
2011 describe('Node events bubbled to the root node', function() {\r
2012\r
2013 var spy,\r
2014 root,\r
2015 newNode,\r
2016 removedNode,\r
2017 firstChild,\r
2018 spyArgs;\r
2019\r
2020 beforeEach(function() {\r
2021 store = new Ext.data.TreeStore({\r
2022 root: {\r
2023 text: 'Root 1',\r
2024 expanded: true,\r
2025 children: [{\r
2026 text: 'Child 1',\r
2027 leaf: true\r
2028 }, {\r
2029 text: 'Child 2',\r
2030 leaf: true\r
2031 }, {\r
2032 text: 'Child 3',\r
2033 leaf: true\r
2034 }, {\r
2035 text: 'Child 4',\r
2036 leaf: true\r
2037 }]\r
2038 }\r
2039 });\r
2040 root = store.getRootNode();\r
2041 });\r
2042\r
2043 it('should fire insert event', function() {\r
2044\r
2045 // Node events are NOT bubbled up to the TreeStore level, only as far as the root\r
2046 spy = spyOnEvent(root, "insert").andCallThrough();\r
2047 firstChild = root.firstChild;\r
2048 newNode = root.insertBefore({\r
2049 text: 'New First'\r
2050 }, firstChild);\r
2051 spyArgs = spy.calls[0].args;\r
2052 expect(spy.calls.length).toBe(1);\r
2053 expect(spyArgs[0]).toBe(root);\r
2054 expect(spyArgs[1]).toBe(newNode);\r
2055 expect(spyArgs[2]).toBe(firstChild);\r
2056 });\r
2057\r
2058 it('should fire append event', function() {\r
2059\r
2060 // Node events are NOT bubbled up to the TreeStore level, only as far as the root\r
2061 spy = spyOnEvent(root, "append").andCallThrough();\r
2062 newNode = root.appendChild({\r
2063 text: 'New Last'\r
2064 });\r
2065 spyArgs = spy.calls[0].args;\r
2066 expect(spy.calls.length).toBe(1);\r
2067 expect(spyArgs[0]).toBe(root);\r
2068 expect(spyArgs[1]).toBe(newNode);\r
2069 expect(spyArgs[2]).toBe(4);\r
2070 });\r
2071\r
2072 it('should fire remove event', function() {\r
2073 var context;\r
2074\r
2075 // Node events are NOT bubbled up to the TreeStore level, only as far as the root\r
2076 spy = spyOnEvent(root, "remove").andCallThrough();\r
2077 removedNode = root.removeChild(root.childNodes[1]);\r
2078 spyArgs = spy.calls[0].args;\r
2079 expect(spy.calls.length).toBe(1);\r
2080 expect(spyArgs[0]).toBe(root);\r
2081 expect(spyArgs[1]).toBe(removedNode);\r
2082 expect(spyArgs[2]).toBe(false);\r
2083\r
2084 // Context arguments: where the removed node came from\r
2085 context = spyArgs[3];\r
2086 expect(context.parentNode).toBe(root);\r
2087 expect(context.previousSibling).toBe(root.childNodes[0]);\r
2088 expect(context.nextSibling).toBe(root.childNodes[1]);\r
2089 });\r
2090\r
2091 it('should fire update event', function() {\r
2092 spy = spyOnEvent(store, "update").andCallThrough();\r
2093 root.firstChild.set('text', 'New Text');\r
2094 spyArgs = spy.calls[0].args;\r
2095 expect(spy.calls.length).toBe(1);\r
2096 expect(spyArgs[0]).toBe(store);\r
2097 expect(spyArgs[1]).toBe(root.firstChild);\r
2098 expect(spyArgs[2]).toBe("edit");\r
2099 expect(spyArgs[3]).toEqual(["text"]);\r
2100 });\r
2101\r
2102\r
2103 it('should fire "load" event with valid 5-argument signature', function() {\r
2104 spy = spyOnEvent(store, "load").andCallThrough();\r
2105 store.load();\r
2106 spyArgs = spy.calls[0].args;\r
2107 expect(spy.calls.length).toBe(1);\r
2108 expect(spyArgs.length).toBe(5);\r
2109\r
2110 // validating args: [ store, records[], success, operation, node]\r
2111 expect(spyArgs[0]).toBe(store);\r
2112 expect(Ext.isArray(spyArgs[1])).toBe(true);\r
2113 expect(typeof spyArgs[2]).toBe('boolean');\r
2114 expect(spyArgs[3].isReadOperation).toBe(true);\r
2115 expect(spyArgs[4]).toBe(root);\r
2116\r
2117 });\r
2118\r
2119 it('should fire "beforeload" event with valid 2-argument signature', function() {\r
2120 spy = spyOnEvent(store, "beforeload").andCallThrough();\r
2121 store.load();\r
2122 spyArgs = spy.calls[0].args;\r
2123 expect(spy.calls.length).toBe(1);\r
2124 expect(spyArgs.length).toBe(2);\r
2125\r
2126 // validating args: [ store, data.Operation, object, eOptsObject ]\r
2127 expect(spyArgs[0]).toBe(store);\r
2128 expect(spyArgs[1] && spyArgs[1].isReadOperation).toBe(true);\r
2129 });\r
2130\r
2131 describe('event ordering', function() {\r
2132 it('should fire events in the correct order', function() {\r
2133 store = new Ext.data.TreeStore({\r
2134 root: {\r
2135 text: 'Root 1',\r
2136 expanded: true,\r
2137 children: []\r
2138 }\r
2139 });\r
2140 root = store.getRoot();\r
2141\r
2142 var result = [],\r
2143 nodeData = {\r
2144 id: 'A',\r
2145 leaf: false,\r
2146 expanded: true,\r
2147 children: [{\r
2148 id: 'A.A',\r
2149 leaf: true\r
2150 }, {\r
2151 id: 'A.B',\r
2152 leaf: true\r
2153 }, {\r
2154 id: 'A.C',\r
2155 leaf: false,\r
2156 expanded: true,\r
2157 children: [{\r
2158 id: 'A.C.A',\r
2159 leaf: true\r
2160 }, {\r
2161 id: 'A.C.B',\r
2162 leaf: true\r
2163 }]\r
2164 }, {\r
2165 id: 'A.D',\r
2166 leaf: true\r
2167 }]\r
2168 };\r
2169\r
2170 // Node events are NOT bubbled up to the TreeStore level, only as far as the root\r
2171 root.on('append', function(thisNode, newChildNode, index) {\r
2172 result.push(newChildNode.getPath() + " | " + thisNode.getPath());\r
2173 });\r
2174 root.appendChild(nodeData);\r
2175 result = result.join(', ');\r
2176 expect(result).toBe("/root/A | /root, /root/A/A.A | /root/A, /root/A/A.B | /root/A, /root/A/A.C | /root/A, /root/A/A.C/A.C.A | /root/A/A.C, /root/A/A.C/A.C.B | /root/A/A.C, /root/A/A.D | /root/A");\r
2177 store.destroy();\r
2178 });\r
2179 });\r
2180 });\r
2181 \r
2182 describe('Node events bubbled to the TreeStore', function() {\r
2183\r
2184 var spy,\r
2185 root,\r
2186 newNode,\r
2187 removedNode,\r
2188 firstChild,\r
2189 spyArgs;\r
2190\r
2191 beforeEach(function() {\r
2192 store = new Ext.data.TreeStore({\r
2193 root: {\r
2194 text: 'Root 1',\r
2195 expanded: true,\r
2196 children: [{\r
2197 text: 'Child 1',\r
2198 leaf: true\r
2199 }, {\r
2200 text: 'Child 2',\r
2201 leaf: true\r
2202 }, {\r
2203 text: 'Child 3',\r
2204 leaf: true\r
2205 }, {\r
2206 text: 'Child 4',\r
2207 leaf: true\r
2208 }]\r
2209 }\r
2210 });\r
2211 root = store.getRootNode();\r
2212 });\r
2213\r
2214 // Node events fired through the TreeStore are prepended with "node"\r
2215 it('should fire insert event', function() {\r
2216\r
2217 spy = spyOnEvent(store, "nodeinsert").andCallThrough();\r
2218 firstChild = root.firstChild;\r
2219 newNode = root.insertBefore({\r
2220 text: 'New First'\r
2221 }, firstChild);\r
2222 spyArgs = spy.calls[0].args;\r
2223 expect(spy.calls.length).toBe(1);\r
2224 expect(spyArgs[0]).toBe(root);\r
2225 expect(spyArgs[1]).toBe(newNode);\r
2226 expect(spyArgs[2]).toBe(firstChild);\r
2227 });\r
2228\r
2229 // Node events fired through the TreeStore are prepended with "node"\r
2230 it('should fire append event', function() {\r
2231\r
2232 spy = spyOnEvent(store, "nodeappend").andCallThrough();\r
2233 newNode = root.appendChild({\r
2234 text: 'New Last'\r
2235 });\r
2236 spyArgs = spy.calls[0].args;\r
2237 expect(spy.calls.length).toBe(1);\r
2238 expect(spyArgs[0]).toBe(root);\r
2239 expect(spyArgs[1]).toBe(newNode);\r
2240 expect(spyArgs[2]).toBe(4);\r
2241 });\r
2242\r
2243 // Node events fired through the TreeStore are prepended with "node"\r
2244 it('should fire remove event', function() {\r
2245\r
2246 spy = spyOnEvent(store, "noderemove").andCallThrough();\r
2247 removedNode = root.removeChild(root.firstChild);\r
2248 spyArgs = spy.calls[0].args;\r
2249 expect(spy.calls.length).toBe(1);\r
2250 expect(spyArgs[0]).toBe(root);\r
2251 expect(spyArgs[1]).toBe(removedNode);\r
2252 expect(spyArgs[2]).toBe(false);\r
2253 });\r
2254\r
2255 describe('event ordering', function() {\r
2256 it('should fire events in the correct order', function() {\r
2257\r
2258 store = new Ext.data.TreeStore({\r
2259 root: {\r
2260 text: 'Root 1',\r
2261 expanded: true,\r
2262 children: []\r
2263 }\r
2264 });\r
2265 root = store.getRoot();\r
2266\r
2267 var result = [],\r
2268 nodeData = {\r
2269 id: 'A',\r
2270 leaf: false,\r
2271 expanded: true,\r
2272 children: [{\r
2273 id: 'A.A',\r
2274 leaf: true\r
2275 }, {\r
2276 id: 'A.B',\r
2277 leaf: true\r
2278 }, {\r
2279 id: 'A.C',\r
2280 leaf: false,\r
2281 expanded: true,\r
2282 children: [{\r
2283 id: 'A.C.A',\r
2284 leaf: true\r
2285 }, {\r
2286 id: 'A.C.B',\r
2287 leaf: true\r
2288 }]\r
2289 }, {\r
2290 id: 'A.D',\r
2291 leaf: true\r
2292 }]\r
2293 };\r
2294\r
2295 // Node events fired through the TreeStore are prepended with "node"\r
2296 store.on('nodeappend', function(thisNode, newChildNode, index) {\r
2297 result.push(newChildNode.getPath() + " | " + thisNode.getPath());\r
2298 });\r
2299\r
2300 root.appendChild(nodeData);\r
2301 result = result.join(', ');\r
2302 expect(result).toBe("/root/A | /root, /root/A/A.A | /root/A, /root/A/A.B | /root/A, /root/A/A.C | /root/A, /root/A/A.C/A.C.A | /root/A/A.C, /root/A/A.C/A.C.B | /root/A/A.C, /root/A/A.D | /root/A");\r
2303 store.destroy();\r
2304 });\r
2305 });\r
2306 });\r
2307\r
2308 describe('events from descendants of collapsed nodes', function() {\r
2309 beforeEach(function() {\r
2310 store = new Ext.data.TreeStore({\r
2311 model: NodeModel,\r
2312 autoLoad: true,\r
2313 root: {\r
2314 expanded: false,\r
2315 id: 0,\r
2316 name: 'Root Node',\r
2317 autoLoad: true,\r
2318 children: dummyData.children\r
2319 }\r
2320 });\r
2321 });\r
2322 it('should fire update events from descendants of collapsed nodes', function() {\r
2323 var updateSpy = spyOnEvent(store, 'update');\r
2324 \r
2325 waitsFor(function() {\r
2326 return !!store.getNodeById(5);\r
2327 });\r
2328 runs(function() {\r
2329 store.getNodeById(5).set('name', 'modified');\r
2330\r
2331 // Data notifications take precedance over filering\r
2332 expect(updateSpy).toHaveBeenCalled();\r
2333 });\r
2334 });\r
2335 });\r
2336\r
2337 describe('beforeload', function() {\r
2338 \r
2339 it('should not clear node descendants if a function bound to beforeload returns false', function() {\r
2340 var beforeLoadComplete = false;\r
2341\r
2342 store = new Ext.data.TreeStore({\r
2343 model: NodeModel,\r
2344 autoLoad: false,\r
2345 root: {\r
2346 expanded: false,\r
2347 id: 0,\r
2348 name: 'Root Node',\r
2349 children: [{\r
2350 id: 1\r
2351 }]\r
2352 }\r
2353 });\r
2354 \r
2355 store.on('beforeload', function(store) {\r
2356 expect(store.getRootNode().firstChild).not.toBeNull();\r
2357 beforeLoadComplete = true;\r
2358 return false; \r
2359 });\r
2360 \r
2361 store.load();\r
2362 \r
2363 waitsFor(function() {\r
2364 return beforeLoadComplete;\r
2365 });\r
2366 });\r
2367 });\r
2368\r
2369 describe('appending to leaf nodes', function() {\r
2370 beforeEach(function() {\r
2371 store = new Ext.data.TreeStore({\r
2372 model: NodeModel,\r
2373 root: {\r
2374 expanded: true,\r
2375 id: 0,\r
2376 name: 'Root Node'\r
2377 }\r
2378 });\r
2379 store.fillNode(store.getRootNode(), store.getProxy().getReader().readRecords(dummyData.children).records);\r
2380 });\r
2381 it('should convert leaf nodes to branch nodes.', function() {\r
2382 var leaf = store.getNodeById(5);\r
2383\r
2384 expect(leaf.isLeaf()).toBe(true);\r
2385 leaf.appendChild({\r
2386 name: 'eee-child'\r
2387 });\r
2388 expect(leaf.isLeaf()).toBe(false);\r
2389 });\r
2390 });\r
2391\r
2392 describe("filtering", function() {\r
2393 function vis(node) {\r
2394 if (Ext.isNumber(node)) {\r
2395 node = byId(node);\r
2396 }\r
2397 return store.isVisible(node);\r
2398 }\r
2399\r
2400 function idFilter(ids) {\r
2401 store.filter({\r
2402 filterFn: function(node) {\r
2403 return Ext.Array.indexOf(ids, node.id) > -1;\r
2404 }\r
2405 });\r
2406 }\r
2407\r
2408 describe("basic filtering", function() {\r
2409 it("should be able to provide a filter in the constructor", function() {\r
2410 makeStore([{\r
2411 id: 1\r
2412 }, {\r
2413 id: 2\r
2414 }], {\r
2415 filters: [{\r
2416 fn: function(rec) {\r
2417 return rec.get('id') === 1;\r
2418 }\r
2419 }]\r
2420 });\r
2421 expect(vis(1)).toBe(true);\r
2422 expect(vis(2)).toBe(false);\r
2423 });\r
2424\r
2425 it("should not show children of non matching nodes", function() {\r
2426 makeStore([{\r
2427 id: 1,\r
2428 children: [2, 3]\r
2429 }, {\r
2430 id: 4,\r
2431 children: [5, 6]\r
2432 }]);\r
2433 idFilter([2, 3, 4, 5, 6]);\r
2434 expect(vis(1)).toBe(false);\r
2435 expect(vis(2)).toBe(false);\r
2436 expect(vis(3)).toBe(false);\r
2437 expect(vis(4)).toBe(true);\r
2438 expect(vis(5)).toBe(true);\r
2439 expect(vis(6)).toBe(true);\r
2440 });\r
2441\r
2442 it("should hide non-matching leaves", function() {\r
2443 makeStore([{\r
2444 id: 1,\r
2445 children: [2, 3]\r
2446 }, {\r
2447 id: 4,\r
2448 children: [5, 6]\r
2449 }]);\r
2450 idFilter([1, 4]);\r
2451 expect(vis(1)).toBe(true);\r
2452 expect(vis(2)).toBe(false);\r
2453 expect(vis(3)).toBe(false);\r
2454 expect(vis(4)).toBe(true);\r
2455 expect(vis(5)).toBe(false);\r
2456 expect(vis(6)).toBe(false);\r
2457 });\r
2458\r
2459 it("should hide non-matching nodes at all levels", function() {\r
2460 makeStore([{\r
2461 id: 1,\r
2462 children: [{\r
2463 id: 2,\r
2464 children: [{\r
2465 id: 3,\r
2466 children: [{\r
2467 id: 4,\r
2468 children: [{\r
2469 id: 5\r
2470 }]\r
2471 }]\r
2472 }]\r
2473 }]\r
2474 }]);\r
2475 idFilter([1, 2]);\r
2476 expect(vis(1)).toBe(true);\r
2477 expect(vis(2)).toBe(true);\r
2478 expect(vis(3)).toBe(false);\r
2479 expect(vis(4)).toBe(false);\r
2480 expect(vis(5)).toBe(false);\r
2481 });\r
2482\r
2483 it("should run the filters on all nodes (even if the parent is not visible) bottom up", function() {\r
2484 makeStore([{\r
2485 id: 'n',\r
2486 children: [{\r
2487 id: 'h',\r
2488 children: [{\r
2489 id: 'c',\r
2490 children: [{\r
2491 id: 'a'\r
2492 }, {\r
2493 id: 'b'\r
2494 }]\r
2495 }, {\r
2496 id: 'f',\r
2497 children: [{\r
2498 id: 'd'\r
2499 }, {\r
2500 id: 'e'\r
2501 }]\r
2502 }, {\r
2503 id: 'g'\r
2504 }]\r
2505 }, {\r
2506 id: 'm',\r
2507 children: [{\r
2508 id: 'i'\r
2509 }, {\r
2510 id: 'l',\r
2511 children: [{\r
2512 id: 'j'\r
2513 }, {\r
2514 id: 'k'\r
2515 }]\r
2516 }]\r
2517 }]\r
2518 }, {\r
2519 id: 'v',\r
2520 children: [{\r
2521 id: 'r',\r
2522 children: [{\r
2523 id: 'p',\r
2524 children: [{\r
2525 id: 'o'\r
2526 }]\r
2527 }, {\r
2528 id: 'q'\r
2529 }]\r
2530 }, {\r
2531 id: 'u',\r
2532 children: [{\r
2533 id: 's'\r
2534 }, {\r
2535 id: 't'\r
2536 }]\r
2537 }]\r
2538 }, {\r
2539 id: 'z',\r
2540 children: [{\r
2541 id: 'x',\r
2542 children: [{\r
2543 id: 'w'\r
2544 }]\r
2545 }, {\r
2546 id: 'y'\r
2547 }]\r
2548 }]);\r
2549\r
2550 var order = [];\r
2551 store.getFilters().add({\r
2552 filterFn: function(node) {\r
2553 if (!node.isRoot()) {\r
2554 order.push(node.id);\r
2555 }\r
2556 return node.id !== 'h';\r
2557 }\r
2558 });\r
2559 expect(order.join('')).toBe('abcdefghijklmnopqrstuvwxyz');\r
2560 });\r
2561 });\r
2562\r
2563 describe("clearing filters", function() {\r
2564 it("should reset node visibility after clearing filters", function() {\r
2565 makeStore([{\r
2566 id: 1,\r
2567 children: [{\r
2568 id: 2,\r
2569 children: [3, 4]\r
2570 }, {\r
2571 id: 5\r
2572 }, {\r
2573 id: 6,\r
2574 children: [{\r
2575 id: 7,\r
2576 children: [8, 9]\r
2577 }]\r
2578 }]\r
2579 }]);\r
2580 idFilter([1, 6]);\r
2581 expect(vis(1)).toBe(true);\r
2582 expect(vis(2)).toBe(false);\r
2583 expect(vis(3)).toBe(false);\r
2584 expect(vis(4)).toBe(false);\r
2585 expect(vis(5)).toBe(false);\r
2586 expect(vis(6)).toBe(true);\r
2587 expect(vis(7)).toBe(false);\r
2588 expect(vis(8)).toBe(false);\r
2589 expect(vis(9)).toBe(false);\r
2590 store.getFilters().removeAll();\r
2591 expect(vis(1)).toBe(true);\r
2592 expect(vis(2)).toBe(true);\r
2593 expect(vis(3)).toBe(true);\r
2594 expect(vis(4)).toBe(true);\r
2595 expect(vis(5)).toBe(true);\r
2596 expect(vis(6)).toBe(true);\r
2597 expect(vis(7)).toBe(true);\r
2598 expect(vis(8)).toBe(true);\r
2599 expect(vis(9)).toBe(true);\r
2600 });\r
2601\r
2602 it("should not fire refresh or datachanged when passing suppressEvent", function() {\r
2603 makeStore([{\r
2604 id: 1,\r
2605 children: [{\r
2606 id: 2,\r
2607 children: [3, 4]\r
2608 }, {\r
2609 id: 5\r
2610 }, {\r
2611 id: 6,\r
2612 children: [{\r
2613 id: 7,\r
2614 children: [8, 9]\r
2615 }]\r
2616 }]\r
2617 }]);\r
2618 idFilter([1, 6]);\r
2619 var spy = jasmine.createSpy();\r
2620 store.on('refresh', spy);\r
2621 store.on('datachanged', spy);\r
2622 store.clearFilter(true);\r
2623 expect(spy).not.toHaveBeenCalled();\r
2624 });\r
2625 });\r
2626\r
2627 describe("root visibility", function() {\r
2628 describe("with rootVisible: true", function() {\r
2629 it("should show the root if any root childNodes are visible", function() {\r
2630 makeStore([{\r
2631 id: 1\r
2632 }, {\r
2633 id: 2\r
2634 }, {\r
2635 id: 3\r
2636 }], {rootVisible: true});\r
2637 idFilter([2]);\r
2638 expect(vis(store.getRoot())).toBe(true);\r
2639 });\r
2640\r
2641 it("should not show the root if no children match", function() {\r
2642 makeStore([{\r
2643 id: 1\r
2644 }, {\r
2645 id: 2\r
2646 }], {rootVisible: true});\r
2647 idFilter([3]);\r
2648 expect(vis(store.getRoot())).toBe(false);\r
2649 });\r
2650 });\r
2651 });\r
2652\r
2653 describe("dynamic manipulation", function() {\r
2654 describe("adding", function() {\r
2655 it("should not show nodes that are added to a filtered out node", function() {\r
2656 makeStore([{\r
2657 id: 1,\r
2658 leaf: false\r
2659 }]);\r
2660 idFilter([2]);\r
2661 byId(1).appendChild({\r
2662 id: 2\r
2663 });\r
2664 expect(vis(2)).toBe(false);\r
2665 });\r
2666\r
2667 it("should not show a node that does match the filter", function() {\r
2668 makeStore([{\r
2669 id: 1,\r
2670 leaf: false\r
2671 }]);\r
2672 idFilter([1]);\r
2673 byId(1).appendChild({\r
2674 id: 2\r
2675 });\r
2676 expect(vis(2)).toBe(false);\r
2677 });\r
2678\r
2679 it("should show if the added node matches the filter", function() {\r
2680 makeStore([{\r
2681 id: 1,\r
2682 leaf: false\r
2683 }]);\r
2684 idFilter([1, 2]);\r
2685 byId(1).appendChild({\r
2686 id: 2\r
2687 });\r
2688 expect(vis(2)).toBe(true);\r
2689 });\r
2690\r
2691 it("should filter out deep nodes that do not match", function() {\r
2692 makeStore([{\r
2693 id: 1,\r
2694 leaf: false\r
2695 }]);\r
2696 idFilter([1, 2, 3, 4]);\r
2697\r
2698 var main = new Ext.data.TreeModel({\r
2699 id: 2,\r
2700 leaf: false,\r
2701 expanded: true,\r
2702 children: []\r
2703 });\r
2704 main.appendChild({\r
2705 id: 3,\r
2706 leaf: false,\r
2707 expanded: true,\r
2708 children: []\r
2709 }).appendChild({\r
2710 id: 4,\r
2711 leaf: false,\r
2712 expanded: true,\r
2713 children: []\r
2714 }).appendChild({\r
2715 id: 5,\r
2716 leaf: true\r
2717 });\r
2718\r
2719 byId(1).appendChild(main);\r
2720 expect(vis(2)).toBe(true);\r
2721 expect(vis(3)).toBe(true);\r
2722 expect(vis(4)).toBe(true);\r
2723 expect(vis(5)).toBe(false);\r
2724 });\r
2725 });\r
2726\r
2727 describe("updating", function() {\r
2728 it("should exclude a node when modifying it to not match the filter", function() {\r
2729 makeStore([{\r
2730 id: 1,\r
2731 text: 'Foo'\r
2732 }]);\r
2733 store.getFilters().add({\r
2734 property: 'text',\r
2735 value: 'Foo'\r
2736 });\r
2737 byId(1).set('text', 'Bar');\r
2738 expect(vis(1)).toBe(false);\r
2739 });\r
2740\r
2741 it("should exclude children when the parent is filtered out", function() {\r
2742 makeStore([{\r
2743 id: 1,\r
2744 text: 'Foo',\r
2745 children: [{\r
2746 id: 2,\r
2747 text: 'Leaf'\r
2748 }]\r
2749 }]);\r
2750 store.getFilters().add({\r
2751 filterFn: function(node) {\r
2752 if (node.isLeaf()) {\r
2753 return true;\r
2754 } else {\r
2755 return node.data.text === 'Foo';\r
2756 }\r
2757 }\r
2758 });\r
2759 byId(1).set('text', 'Bar');\r
2760 expect(vis(1)).toBe(false);\r
2761 expect(vis(2)).toBe(false);\r
2762 });\r
2763\r
2764 it("should include a node when modifying it to match the filter", function() {\r
2765 makeStore([{\r
2766 id: 1,\r
2767 text: 'Foo'\r
2768 }]);\r
2769 store.getFilters().add({\r
2770 property: 'text',\r
2771 value: 'Bar'\r
2772 });\r
2773 byId(1).set('text', 'Bar');\r
2774 expect(vis(1)).toBe(true);\r
2775 });\r
2776\r
2777 it("should include children when the parent is filtered in", function() {\r
2778 makeStore([{\r
2779 id: 1,\r
2780 text: 'Bar',\r
2781 children: [{\r
2782 id: 2,\r
2783 text: 'Leaf'\r
2784 }]\r
2785 }]);\r
2786 store.getFilters().add({\r
2787 filterFn: function(node) {\r
2788 if (node.isLeaf()) {\r
2789 return true;\r
2790 } else {\r
2791 return node.data.text === 'Foo';\r
2792 }\r
2793 }\r
2794 });\r
2795 byId(1).set('text', 'Foo');\r
2796 expect(vis(1)).toBe(true);\r
2797 expect(vis(2)).toBe(true);\r
2798 });\r
2799 });\r
2800 });\r
2801 });\r
2802 \r
2803 describe('heterogeneous TreeStores', function() {\r
2804 var treeData,\r
2805 schema;\r
2806\r
2807 beforeEach(function() {\r
2808 schema = Ext.data.Model.schema;\r
2809 schema.setNamespace('spec');\r
2810\r
2811 Ext.define('spec.Territory', {\r
2812 extend: 'Ext.data.TreeModel',\r
2813 idProperty: 'territoryName',\r
2814 fields: [{\r
2815 name: 'territoryName',\r
2816 mapping: 'territoryName',\r
2817 convert: undefined\r
2818 }]\r
2819 });\r
2820 Ext.define('spec.Country', {\r
2821 extend: 'Ext.data.TreeModel',\r
2822 idProperty: 'countryName',\r
2823 fields: [{\r
2824 name: 'countryName',\r
2825 mapping: 'countryName',\r
2826 convert: undefined\r
2827 }]\r
2828 });\r
2829 Ext.define('spec.City', {\r
2830 extend: 'Ext.data.TreeModel',\r
2831 idProperty: 'cityName',\r
2832 fields: [{\r
2833 name: 'cityName',\r
2834 mapping: 'cityName',\r
2835 convert: undefined\r
2836 }]\r
2837 });\r
2838\r
2839 // Must renew the data each time. Because TreeStore mutates input data object by deleting\r
2840 // the childNodes in onBeforeNodeExpand and onNodeAdded. TODO: it shouldn't do that.\r
2841 // The heterogeneous models MUST have disparate, non-overlapping field names\r
2842 // so that we test that a correct, record-specific data extraction function\r
2843 // has been run on the different mtypes on the dataset.\r
2844 treeData = {\r
2845 children: [{\r
2846 mtype: 'Territory',\r
2847 territoryName: 'North America',\r
2848 children :[{\r
2849 mtype: 'Country',\r
2850 countryName: 'USA',\r
2851\r
2852 // Test using both forms of classname, defaultNamespaced "City".\r
2853 children: [{\r
2854 mtype: 'spec.City',\r
2855 cityName: 'Redwood City',\r
2856 leaf: true\r
2857 }, {\r
2858 mtype: 'City',\r
2859 cityName: 'Frederick, MD',\r
2860 leaf: true\r
2861 }]\r
2862 }, {\r
2863 mtype: 'Country',\r
2864 countryName: 'Canada',\r
2865 children: [{\r
2866 mtype: 'spec.City',\r
2867 cityName: 'Vancouver',\r
2868 leaf: true\r
2869 }, {\r
2870 mtype: 'City',\r
2871 cityName: 'Toronto',\r
2872 leaf: true\r
2873 }]\r
2874 }]\r
2875 }, {\r
2876 mtype: 'Territory',\r
2877 territoryName: 'Europe, ME, Africa',\r
2878 expanded: true,\r
2879 children :[{\r
2880 mtype: 'Country',\r
2881 countryName: 'England',\r
2882 children: [{\r
2883 mtype: 'spec.City',\r
2884 cityName: 'Nottingham',\r
2885 leaf: true\r
2886 }, {\r
2887 mtype: 'City',\r
2888 cityName: 'London',\r
2889 leaf: true\r
2890 }]\r
2891 }, {\r
2892 mtype: 'Country',\r
2893 countryName: 'Netherlands',\r
2894 children: [{\r
2895 mtype: 'spec.City',\r
2896 cityName: 'Amsterdam',\r
2897 leaf: true\r
2898 }, {\r
2899 mtype: 'City',\r
2900 cityName: 'Haaksbergen',\r
2901 leaf: true\r
2902 }]\r
2903 }]\r
2904 }]\r
2905 };\r
2906 });\r
2907 afterEach(function() {\r
2908 Ext.undefine('spec.Territory');\r
2909 Ext.undefine('spec.Country');\r
2910 Ext.undefine('spec.City');\r
2911 schema.clear(true);\r
2912 });\r
2913\r
2914 it("should use the parentNode's childType to resolve child node models if no typeProperty is used on Reader", function() {\r
2915\r
2916 // Need a special root type which knows about the first level\r
2917 Ext.define('spec.World', {\r
2918 extend: 'Ext.data.TreeModel',\r
2919 childType: 'Territory'\r
2920 });\r
2921 // Set the childType on the prototypes.\r
2922 // So Territory chould always produce Country childNodes and Country should always produce City childNodes.\r
2923 spec.Territory.prototype.childType = 'Country';\r
2924 spec.Country.prototype.childType = 'City';\r
2925\r
2926 store = new Ext.data.TreeStore({\r
2927 root: treeData,\r
2928 model: 'spec.World',\r
2929 proxy: {\r
2930 type: 'memory'\r
2931 }\r
2932 });\r
2933 var root = store.getRootNode(),\r
2934 na = root.childNodes[0],\r
2935 emea = root.childNodes[1],\r
2936 spain,\r
2937 madrid,\r
2938 usa = na.childNodes[0],\r
2939 rwc = usa.childNodes[0],\r
2940 frederick = usa.childNodes[1],\r
2941 canada = na.childNodes[1],\r
2942 vancouver = canada.childNodes[0],\r
2943 toronto = canada.childNodes[1],\r
2944 sacramento = usa.appendChild({\r
2945 cityName: 'Sacramento',\r
2946 leaf: true\r
2947 });\r
2948\r
2949 // Two top level nodes are North America and Europe, ME, Africa"\r
2950 expect(na instanceof spec.Territory).toBe(true);\r
2951 expect(emea instanceof spec.Territory).toBe(true);\r
2952 expect(na.get('territoryName')).toBe('North America');\r
2953 expect(emea.get('territoryName')).toBe('Europe, ME, Africa');\r
2954\r
2955 expect(usa instanceof spec.Country).toBe(true);\r
2956 expect(canada instanceof spec.Country).toBe(true);\r
2957 expect(usa.get('countryName')).toBe('USA');\r
2958 expect(canada.get('countryName')).toBe('Canada');\r
2959\r
2960 expect(rwc instanceof spec.City).toBe(true);\r
2961 expect(frederick instanceof spec.City).toBe(true);\r
2962 expect(sacramento instanceof spec.City).toBe(true);\r
2963 expect(vancouver instanceof spec.City).toBe(true);\r
2964 expect(toronto instanceof spec.City).toBe(true);\r
2965 expect(rwc.get('cityName')).toBe('Redwood City');\r
2966 expect(frederick.get('cityName')).toBe('Frederick, MD');\r
2967 expect(sacramento.get('cityName')).toBe('Sacramento');\r
2968 expect(vancouver.get('cityName')).toBe('Vancouver');\r
2969 expect(toronto.get('cityName')).toBe('Toronto');\r
2970\r
2971 // Check that the Model converts raw configs correctly according to the\r
2972 // typeProperty in the TreeStore\r
2973 spain = emea.appendChild({\r
2974 mtype: 'Country',\r
2975 countryName: 'Spain'\r
2976 });\r
2977 expect(spain instanceof spec.Country).toBe(true);\r
2978 expect(spain.get('countryName')).toBe('Spain');\r
2979\r
2980 madrid = spain.appendChild({\r
2981 mtype: 'City',\r
2982 cityName: 'Madrid'\r
2983 });\r
2984 expect(madrid instanceof spec.City).toBe(true);\r
2985 expect(madrid.get('cityName')).toBe('Madrid');\r
2986 });\r
2987\r
2988 it("should use the store's model namespace to resolve child node models if short form typeProperty is used", function() {\r
2989 store = new Ext.data.TreeStore({\r
2990 model: 'spec.Territory',\r
2991 root: treeData,\r
2992 proxy: {\r
2993 type: 'memory',\r
2994 reader: {\r
2995 typeProperty: 'mtype'\r
2996 }\r
2997 }\r
2998 });\r
2999 var root = store.getRootNode(),\r
3000 na = root.childNodes[0],\r
3001 emea = root.childNodes[1],\r
3002 spain,\r
3003 madrid,\r
3004 usa = na.childNodes[0],\r
3005 rwc = usa.childNodes[0],\r
3006 frederick = usa.childNodes[1],\r
3007 canada = na.childNodes[1],\r
3008 vancouver = canada.childNodes[0],\r
3009 toronto = canada.childNodes[1];\r
3010\r
3011 // Two top level nodes are North America and Europe, ME, Africa"\r
3012 expect(na instanceof spec.Territory).toBe(true);\r
3013 expect(emea instanceof spec.Territory).toBe(true);\r
3014 expect(na.get('territoryName')).toBe('North America');\r
3015 expect(emea.get('territoryName')).toBe('Europe, ME, Africa');\r
3016\r
3017 expect(usa instanceof spec.Country).toBe(true);\r
3018 expect(canada instanceof spec.Country).toBe(true);\r
3019 expect(usa.get('countryName')).toBe('USA');\r
3020 expect(canada.get('countryName')).toBe('Canada');\r
3021\r
3022 expect(rwc instanceof spec.City).toBe(true);\r
3023 expect(frederick instanceof spec.City).toBe(true);\r
3024 expect(vancouver instanceof spec.City).toBe(true);\r
3025 expect(toronto instanceof spec.City).toBe(true);\r
3026 expect(rwc.get('cityName')).toBe('Redwood City');\r
3027 expect(frederick.get('cityName')).toBe('Frederick, MD');\r
3028 expect(vancouver.get('cityName')).toBe('Vancouver');\r
3029 expect(toronto.get('cityName')).toBe('Toronto');\r
3030\r
3031 // Check that the Model converts raw configs correctly according to the\r
3032 // typeProperty in the TreeStore\r
3033 spain = emea.appendChild({\r
3034 mtype: 'Country',\r
3035 countryName: 'Spain'\r
3036 });\r
3037 expect(spain instanceof spec.Country).toBe(true);\r
3038 expect(spain.get('countryName')).toBe('Spain');\r
3039\r
3040 madrid = spain.appendChild({\r
3041 mtype: 'City',\r
3042 cityName: 'Madrid'\r
3043 });\r
3044 expect(madrid instanceof spec.City).toBe(true);\r
3045 expect(madrid.get('cityName')).toBe('Madrid');\r
3046 });\r
3047\r
3048 it("should use the typeProperty's namespace property to resolve model class names", function() {\r
3049 var data = Ext.clone(treeData);\r
3050\r
3051 // Remove all usages of namespace.\r
3052 // It gets added.\r
3053 data.children[0].children[0].children[0].mtype = 'City';\r
3054 data.children[0].children[1].children[0].mtype = 'City';\r
3055 data.children[1].children[0].children[0].mtype = 'City';\r
3056 data.children[1].children[1].children[0].mtype = 'City';\r
3057 \r
3058 store = new Ext.data.TreeStore({\r
3059 root: data,\r
3060 proxy: {\r
3061 type: 'memory',\r
3062 reader: {\r
3063 typeProperty: {\r
3064 name: 'mtype',\r
3065 namespace: 'spec'\r
3066 }\r
3067 }\r
3068 }\r
3069 });\r
3070 var root = store.getRootNode(),\r
3071 na = root.childNodes[0],\r
3072 emea = root.childNodes[1],\r
3073 spain,\r
3074 madrid,\r
3075 usa = na.childNodes[0],\r
3076 rwc = usa.childNodes[0],\r
3077 frederick = usa.childNodes[1],\r
3078 canada = na.childNodes[1],\r
3079 vancouver = canada.childNodes[0],\r
3080 toronto = canada.childNodes[1];\r
3081\r
3082 expect(na instanceof spec.Territory).toBe(true);\r
3083 expect(emea instanceof spec.Territory).toBe(true);\r
3084 expect(na.get('territoryName')).toBe('North America');\r
3085 expect(emea.get('territoryName')).toBe('Europe, ME, Africa');\r
3086\r
3087 expect(usa instanceof spec.Country).toBe(true);\r
3088 expect(canada instanceof spec.Country).toBe(true);\r
3089 expect(usa.get('countryName')).toBe('USA');\r
3090 expect(canada.get('countryName')).toBe('Canada');\r
3091\r
3092 expect(rwc instanceof spec.City).toBe(true);\r
3093 expect(frederick instanceof spec.City).toBe(true);\r
3094 expect(vancouver instanceof spec.City).toBe(true);\r
3095 expect(toronto instanceof spec.City).toBe(true);\r
3096 expect(rwc.get('cityName')).toBe('Redwood City');\r
3097 expect(frederick.get('cityName')).toBe('Frederick, MD');\r
3098 expect(vancouver.get('cityName')).toBe('Vancouver');\r
3099 expect(toronto.get('cityName')).toBe('Toronto');\r
3100\r
3101 // Check that the Model converts raw configs correctly according to the\r
3102 // typeProperty in the TreeStore\r
3103 spain = emea.appendChild({\r
3104 mtype: 'Country',\r
3105 countryName: 'Spain'\r
3106 });\r
3107 expect(spain instanceof spec.Country).toBe(true);\r
3108 expect(spain.get('countryName')).toBe('Spain');\r
3109\r
3110 madrid = spain.appendChild({\r
3111 mtype: 'City',\r
3112 cityName: 'Madrid'\r
3113 });\r
3114 expect(madrid instanceof spec.City).toBe(true);\r
3115 expect(madrid.get('cityName')).toBe('Madrid');\r
3116 });\r
3117\r
3118 it("should use the typeProperty's map property to resolve model class names", function() {\r
3119 store = new Ext.data.TreeStore({\r
3120 root: treeData,\r
3121 proxy: {\r
3122 type: 'memory',\r
3123 reader: {\r
3124 typeProperty: {\r
3125 name: 'mtype',\r
3126 map: {\r
3127 Territory: 'Territory',\r
3128 Country: 'Country',\r
3129 City: 'City'\r
3130 }\r
3131 }\r
3132 }\r
3133 }\r
3134 });\r
3135 var root = store.getRootNode(),\r
3136 na = root.childNodes[0],\r
3137 emea = root.childNodes[1],\r
3138 spain,\r
3139 madrid,\r
3140 usa = na.childNodes[0],\r
3141 rwc = usa.childNodes[0],\r
3142 frederick = usa.childNodes[1],\r
3143 canada = na.childNodes[1],\r
3144 vancouver = canada.childNodes[0],\r
3145 toronto = canada.childNodes[1];\r
3146\r
3147 expect(na instanceof spec.Territory).toBe(true);\r
3148 expect(emea instanceof spec.Territory).toBe(true);\r
3149 expect(na.get('territoryName')).toBe('North America');\r
3150 expect(emea.get('territoryName')).toBe('Europe, ME, Africa');\r
3151\r
3152 expect(usa instanceof spec.Country).toBe(true);\r
3153 expect(canada instanceof spec.Country).toBe(true);\r
3154 expect(usa.get('countryName')).toBe('USA');\r
3155 expect(canada.get('countryName')).toBe('Canada');\r
3156\r
3157 expect(rwc instanceof spec.City).toBe(true);\r
3158 expect(frederick instanceof spec.City).toBe(true);\r
3159 expect(vancouver instanceof spec.City).toBe(true);\r
3160 expect(toronto instanceof spec.City).toBe(true);\r
3161 expect(rwc.get('cityName')).toBe('Redwood City');\r
3162 expect(frederick.get('cityName')).toBe('Frederick, MD');\r
3163 expect(vancouver.get('cityName')).toBe('Vancouver');\r
3164 expect(toronto.get('cityName')).toBe('Toronto');\r
3165\r
3166 // Check that the Model converts raw configs correctly according to the\r
3167 // typeProperty in the TreeStore\r
3168 spain = emea.appendChild({\r
3169 mtype: 'Country',\r
3170 countryName: 'Spain'\r
3171 });\r
3172 expect(spain instanceof spec.Country).toBe(true);\r
3173 expect(spain.get('countryName')).toBe('Spain');\r
3174\r
3175 madrid = spain.appendChild({\r
3176 mtype: 'City',\r
3177 cityName: 'Madrid'\r
3178 });\r
3179 expect(madrid instanceof spec.City).toBe(true);\r
3180 expect(madrid.get('cityName')).toBe('Madrid');\r
3181 });\r
3182\r
3183 it("should CALL the typeProperty to resolve model class names if it is a function", function() {\r
3184 var typePropertyScope;\r
3185\r
3186 store = new Ext.data.TreeStore({\r
3187 root: treeData,\r
3188 proxy: {\r
3189 type: 'memory',\r
3190 reader: {\r
3191 typeProperty: function(rawData) {\r
3192 typePropertyScope = this;\r
3193 return Ext.String.startsWith(rawData.mtype, 'spec.') ? rawData.mtype : 'spec.' + rawData.mtype;\r
3194 }\r
3195 }\r
3196 }\r
3197 });\r
3198 var root = store.getRootNode(),\r
3199 na = root.childNodes[0],\r
3200 emea = root.childNodes[1],\r
3201 spain,\r
3202 madrid,\r
3203 usa = na.childNodes[0],\r
3204 rwc = usa.childNodes[0],\r
3205 frederick = usa.childNodes[1],\r
3206 canada = na.childNodes[1],\r
3207 vancouver = canada.childNodes[0],\r
3208 toronto = canada.childNodes[1];\r
3209\r
3210 // The typeProperty function must be called in the scope of the Reader\r
3211 expect(typePropertyScope === store.getProxy().getReader());\r
3212\r
3213 expect(na instanceof spec.Territory).toBe(true);\r
3214 expect(emea instanceof spec.Territory).toBe(true);\r
3215 expect(na.get('territoryName')).toBe('North America');\r
3216 expect(emea.get('territoryName')).toBe('Europe, ME, Africa');\r
3217\r
3218 expect(usa instanceof spec.Country).toBe(true);\r
3219 expect(canada instanceof spec.Country).toBe(true);\r
3220 expect(usa.get('countryName')).toBe('USA');\r
3221 expect(canada.get('countryName')).toBe('Canada');\r
3222\r
3223 expect(rwc instanceof spec.City).toBe(true);\r
3224 expect(frederick instanceof spec.City).toBe(true);\r
3225 expect(vancouver instanceof spec.City).toBe(true);\r
3226 expect(toronto instanceof spec.City).toBe(true);\r
3227 expect(rwc.get('cityName')).toBe('Redwood City');\r
3228 expect(frederick.get('cityName')).toBe('Frederick, MD');\r
3229 expect(vancouver.get('cityName')).toBe('Vancouver');\r
3230 expect(toronto.get('cityName')).toBe('Toronto');\r
3231\r
3232 // Check that the Model converts raw configs correctly according to the\r
3233 // typeProperty in the TreeStore\r
3234 spain = emea.appendChild({\r
3235 mtype: 'Country',\r
3236 countryName: 'Spain'\r
3237 });\r
3238 expect(spain instanceof spec.Country).toBe(true);\r
3239 expect(spain.get('countryName')).toBe('Spain');\r
3240\r
3241 madrid = spain.appendChild({\r
3242 mtype: 'City',\r
3243 cityName: 'Madrid'\r
3244 });\r
3245 expect(madrid instanceof spec.City).toBe(true);\r
3246 expect(madrid.get('cityName')).toBe('Madrid');\r
3247 });\r
3248 });\r
3249\r
3250 describe('Filtering, and isLastVisible status', function() {\r
3251 var rec0, rec1, rec2;\r
3252\r
3253 beforeEach(function() {\r
3254 store = new Ext.data.TreeStore({\r
3255 model: NodeModel,\r
3256 root: {\r
3257 expanded: true,\r
3258 id: 0,\r
3259 name: 'Root Node',\r
3260 children: [{\r
3261 name: 'Foo'\r
3262 }, {\r
3263 name: 'Bar'\r
3264 }, {\r
3265 name: 'Bletch'\r
3266 }]\r
3267 }\r
3268 });\r
3269 rec0 = store.getAt(0);\r
3270 rec1 = store.getAt(1);\r
3271 rec2 = store.getAt(2);\r
3272\r
3273 });\r
3274 it('should correctly ascertain whether a node is the last visible node.', function() {\r
3275\r
3276 // Verify initial conditions\r
3277 expect(store.getCount()).toEqual(3);\r
3278 expect(rec0.isLastVisible()).toBe(false);\r
3279 expect(rec1.isLastVisible()).toBe(false);\r
3280 expect(rec2.isLastVisible()).toBe(true);\r
3281\r
3282 // Only first node should now be visible\r
3283 store.filter({\r
3284 property: 'name',\r
3285 value: 'Foo'\r
3286 });\r
3287\r
3288 // Now there's only 1, and it should report that it is the last visible\r
3289 expect(store.getCount()).toEqual(1);\r
3290 expect(rec0.isLastVisible()).toBe(true);\r
3291 });\r
3292 });\r
3293\r
3294 describe('TreeNode drop with locally created (phantom) nodes', function() {\r
3295 var n1, n2, n3;\r
3296\r
3297 beforeEach(function() {\r
3298 store = new Ext.data.TreeStore({\r
3299 model: NodeModel,\r
3300 root: {\r
3301 expanded: true,\r
3302 id: 0,\r
3303 name: 'Root Node',\r
3304 children: [{\r
3305 name: 'Foo',\r
3306 expanded: true,\r
3307 children: []\r
3308 }, {\r
3309 name: 'Bar'\r
3310 }, {\r
3311 name: 'Bletch'\r
3312 }]\r
3313 }\r
3314 });\r
3315\r
3316 n1 = store.getAt(0);\r
3317 });\r
3318\r
3319 it('should remove all descendants. All nodes are phantom, so there should be an empty removed list', function() {\r
3320 var records;\r
3321\r
3322 // "Foo", "Bar" and "Bletch" present\r
3323 expect(store.getCount()).toBe(3);\r
3324\r
3325 // Append to expanded node "Foo"\r
3326 n2 = n1.appendChild({\r
3327 name: 'Zarg',\r
3328 expanded: true\r
3329 });\r
3330 n3 = n2.appendChild({\r
3331 name: 'Blivit',\r
3332 leaf: true\r
3333 });\r
3334\r
3335 // The added nodes should be in the store; they are added to expanded nodes.\r
3336 expect(store.getCount()).toBe(5);\r
3337\r
3338 // n1, its child n2("zarg"), and grandchild n3("blivit") will all be removed by this operation.\r
3339 n1.drop();\r
3340\r
3341 records = store.getRemovedRecords();\r
3342\r
3343 // NO records should appear in the removed list because they are all phantom\r
3344 // having been defined using client-side data.\r
3345 expect(records.length).toBe(0);\r
3346\r
3347 // n1("Foo") and its descendants, "Zarg" and "Blivit" should be removed.\r
3348 // Only "Bar" and "Bletch" present now.\r
3349 expect(store.getCount()).toBe(2);\r
3350 });\r
3351 });\r
3352 \r
3353 describe('TreeNode drop', function() {\r
3354 var n1, n2, n3;\r
3355\r
3356 beforeEach(function() {\r
3357 store = new Ext.data.TreeStore({\r
3358 model: NodeModel,\r
3359 root: {\r
3360 expanded: true,\r
3361 id: 0,\r
3362 name: 'Root Node'\r
3363 },\r
3364 // Read these through a Proxy because we are expecting them NOT to be phantom\r
3365 proxy: {\r
3366 type: 'memory',\r
3367 data: [{\r
3368 name: 'Foo',\r
3369 expanded: true,\r
3370 children: []\r
3371 }, {\r
3372 name: 'Bar'\r
3373 }, {\r
3374 name: 'Bletch'\r
3375 }]\r
3376 }\r
3377 });\r
3378\r
3379 n1 = store.getAt(0);\r
3380 });\r
3381\r
3382 it('should remove all descendants, and add non-phantom descendants to removed list', function() {\r
3383 var records;\r
3384\r
3385 // "Foo", "Bar" and "Bletch" present\r
3386 expect(store.getCount()).toBe(3);\r
3387\r
3388 // Append to expanded node "Foo"\r
3389 n2 = n1.appendChild({\r
3390 name: 'Zarg',\r
3391 expanded: true\r
3392 });\r
3393 n3 = n2.appendChild({\r
3394 name: 'Blivit',\r
3395 leaf: true\r
3396 });\r
3397\r
3398 // The added nodes should be in the store; they are added to expanded nodes.\r
3399 expect(store.getCount()).toBe(5);\r
3400\r
3401 // n1, its child n2("zarg"), and grandchild n3("blivit") will all be removed by this operation.\r
3402 n1.drop();\r
3403\r
3404 records = store.getRemovedRecords();\r
3405\r
3406 // Only the non-phantom node "Foo" should be in the removed list.\r
3407 // The two newly added phantoms just disappear.\r
3408 // Only "Bar" and "Bletch" present now.\r
3409 expect(records.length).toBe(1);\r
3410 expect(records[0] === n1).toBe(true);\r
3411\r
3412 // n1("Foo") and its descendants, "Zarg" and "Blivit" should be removed.\r
3413 // Only "Bar" and "Bletch" present now.\r
3414 expect(store.getCount()).toBe(2);\r
3415 });\r
3416\r
3417 it('should remove deleted records from removed list if they get added back', function() {\r
3418 var bletchNode = store.findNode('name', 'Bletch'),\r
3419 bletchParent = bletchNode.parentNode;\r
3420\r
3421 // Queue the node for destruction upon the next store sync.\r
3422 bletchNode.drop();\r
3423\r
3424 // Should be in destruction queue\r
3425 expect(Ext.Array.contains(store.getRemovedRecords(), bletchNode)).toBe(true);\r
3426\r
3427 // Change ourt mind, add it back\r
3428 bletchParent.appendChild(bletchNode);\r
3429 \r
3430 // Should NOT be in destruction queue\r
3431 expect(Ext.Array.contains(store.getRemovedRecords(), bletchNode)).toBe(false); \r
3432 });\r
3433 });\r
3434\r
3435 describe("parentIdProperty", function() {\r
3436 var root;\r
3437\r
3438 beforeEach(function() {\r
3439 store = new Ext.data.TreeStore({\r
3440 model: NodeModel,\r
3441 root: {},\r
3442 parentIdProperty: 'foo'\r
3443 });\r
3444 root = store.getRoot();\r
3445 });\r
3446\r
3447 afterEach(function() {\r
3448 root = null;\r
3449 });\r
3450\r
3451 function makeNode(id, parent) {\r
3452 var o = {\r
3453 id: id,\r
3454 expanded: true\r
3455 };\r
3456 if (arguments.length > 1) {\r
3457 o.foo = parent;\r
3458 }\r
3459 return o;\r
3460 }\r
3461\r
3462 it("should append items without a parentId to the loaded item", function() {\r
3463 root.expand();\r
3464 completeWithData([\r
3465 makeNode(1),\r
3466 makeNode(2),\r
3467 makeNode(3)\r
3468 ]);\r
3469\r
3470 var childNodes = root.childNodes;\r
3471 expect(byId(1)).toBe(childNodes[0]);\r
3472 expect(byId(2)).toBe(childNodes[1]);\r
3473 expect(byId(3)).toBe(childNodes[2]);\r
3474 });\r
3475\r
3476 it("should allow a parentId of 0", function() {\r
3477 root.expand();\r
3478 completeWithData([\r
3479 makeNode(0),\r
3480 makeNode(1, 0)\r
3481 ]);\r
3482\r
3483 expect(byId(1)).toBe(byId(0).childNodes[0]);\r
3484 });\r
3485\r
3486 it("should throw an exception if a matching parent is not found", function() {\r
3487 root.expand();\r
3488 expect(function() {\r
3489 completeWithData([\r
3490 makeNode(1),\r
3491 makeNode(2, 100)\r
3492 ]);\r
3493 }).toThrow();\r
3494 });\r
3495\r
3496 it("should add children to their parent nodes, retaining any implied order", function() {\r
3497 root.expand();\r
3498 completeWithData([\r
3499 makeNode('c21', 'c2'),\r
3500 makeNode('a'),\r
3501 makeNode('c2', 'c'),\r
3502 makeNode('a1', 'a'),\r
3503 makeNode('c'),\r
3504 makeNode('b'),\r
3505 makeNode('b1', 'b'),\r
3506 makeNode('a2', 'a'),\r
3507 makeNode('c1', 'c'),\r
3508 makeNode('c22', 'c2'),\r
3509 makeNode('a32', 'a3'),\r
3510 makeNode('a31', 'a3'),\r
3511 makeNode('a21', 'a2'),\r
3512 makeNode('b12', 'b1'),\r
3513 makeNode('b11', 'b1'),\r
3514 makeNode('a3', 'a'),\r
3515 makeNode('a211', 'a21')\r
3516 ]);\r
3517\r
3518 expectOrder(root, ['a', 'c', 'b']);\r
3519\r
3520 expectOrder(byId('a'), ['a1', 'a2', 'a3']);\r
3521 expectOrder(byId('a2'), ['a21']);\r
3522 expectOrder(byId('a21'), ['a211']);\r
3523\r
3524 expectOrder(byId('b'), ['b1']);\r
3525 expectOrder(byId('b1'), ['b12', 'b11']);\r
3526\r
3527 expectOrder(byId('c'), ['c2', 'c1']);\r
3528 expectOrder(byId('c2'), ['c21', 'c22']);\r
3529 });\r
3530\r
3531 describe("sorting", function() {\r
3532 it("should sort nodes via sorter", function() {\r
3533 store.getSorters().add('id');\r
3534 root.expand();\r
3535 completeWithData([\r
3536 makeNode('c'),\r
3537 makeNode('a'),\r
3538 makeNode('b'),\r
3539 makeNode('c3', 'c'),\r
3540 makeNode('b3', 'b'),\r
3541 makeNode('a3', 'a'),\r
3542 makeNode('c2', 'c'),\r
3543 makeNode('b2', 'b'),\r
3544 makeNode('a2', 'a'),\r
3545 makeNode('c1', 'c'),\r
3546 makeNode('b1', 'b'),\r
3547 makeNode('a1', 'a')\r
3548 ]);\r
3549\r
3550 expectOrder(root, ['a', 'b', 'c']);\r
3551\r
3552 expectOrder(byId('a'), ['a1', 'a2', 'a3']);\r
3553 expectOrder(byId('b'), ['b1', 'b2', 'b3']);\r
3554 expectOrder(byId('c'), ['c1', 'c2', 'c3']);\r
3555 });\r
3556\r
3557 it("should do an index sort if required", function() {\r
3558 root.expand();\r
3559 completeWithData([\r
3560 {id: 'a', index: 2},\r
3561 {id: 'b', index: 1},\r
3562 {id: 'c', index: 0},\r
3563 {id: 'a1', foo: 'a', index: 2},\r
3564 {id: 'a2', foo: 'a', index: 1},\r
3565 {id: 'a3', foo: 'a', index: 0}\r
3566 ]);\r
3567\r
3568 expectOrder(root, ['c', 'b', 'a']);\r
3569 expectOrder(byId('a'), ['a3', 'a2', 'a1']);\r
3570 });\r
3571 });\r
3572\r
3573 describe("filtering", function() {\r
3574 it("should apply filters", function() {\r
3575 var allowed = ['a', 'c', 'a2', 'c1', 'c11', 'c13'];\r
3576 store.getFilters().add({\r
3577 filterFn: function(node) {\r
3578 return Ext.Array.indexOf(allowed, node.id) > -1;\r
3579 }\r
3580 });\r
3581\r
3582 root.expand();\r
3583 completeWithData([\r
3584 makeNode('a'),\r
3585 makeNode('b'),\r
3586 makeNode('c'),\r
3587 makeNode('a1', 'a'),\r
3588 makeNode('a2', 'a'),\r
3589 makeNode('a3', 'a'),\r
3590 makeNode('b1', 'b'),\r
3591 makeNode('b2', 'b'),\r
3592 makeNode('b3', 'b'),\r
3593 makeNode('c1', 'c'),\r
3594 makeNode('c11', 'c1'),\r
3595 makeNode('c12', 'c1'),\r
3596 makeNode('c13', 'c1'),\r
3597 makeNode('c2', 'c'),\r
3598 makeNode('c3', 'c')\r
3599 ]);\r
3600\r
3601 expect(store.isVisible(byId('a'))).toBe(true);\r
3602 expect(store.isVisible(byId('a1'))).toBe(false);\r
3603 expect(store.isVisible(byId('a2'))).toBe(true);\r
3604 expect(store.isVisible(byId('a3'))).toBe(false);\r
3605\r
3606 expect(store.isVisible(byId('b'))).toBe(false);\r
3607 expect(store.isVisible(byId('b1'))).toBe(false);\r
3608 expect(store.isVisible(byId('b2'))).toBe(false);\r
3609 expect(store.isVisible(byId('b3'))).toBe(false);\r
3610\r
3611 expect(store.isVisible(byId('c'))).toBe(true);\r
3612 expect(store.isVisible(byId('c1'))).toBe(true);\r
3613 expect(store.isVisible(byId('c11'))).toBe(true);\r
3614 expect(store.isVisible(byId('c12'))).toBe(false);\r
3615 expect(store.isVisible(byId('c13'))).toBe(true);\r
3616 expect(store.isVisible(byId('c2'))).toBe(false);\r
3617 expect(store.isVisible(byId('c3'))).toBe(false);\r
3618 });\r
3619 });\r
3620 });\r
3621 \r
3622 describe('loading inline data with no configured root node', function() {\r
3623 it('should run without throwing an error', function() {\r
3624 expect(function() {\r
3625 new Ext.data.TreeStore({\r
3626 fields: ['name', 'text', 'id', 'parentId'],\r
3627 parentIdProperty: 'parentId',\r
3628 data: [{\r
3629 id: 1,\r
3630 name: 'A',\r
3631 value: 10,\r
3632 parentId: null\r
3633 }, {\r
3634 id: 2,\r
3635 name: 'B',\r
3636 value: 12,\r
3637 parentId: 1,\r
3638 leaf: true\r
3639 }]\r
3640 }).load();\r
3641 }).not.toThrow();\r
3642 });\r
3643 });\r
3644\r
3645 describe("setting the root", function() {\r
3646 describe("via configuration", function() {\r
3647 describe("with a model config", function() {\r
3648 beforeEach(function() {\r
3649 store = new Ext.data.TreeStore({\r
3650 model: NodeModel,\r
3651 root: {}\r
3652 });\r
3653 });\r
3654\r
3655 it("should set the root property", function() {\r
3656 expect(store.getRoot().get('root')).toBe(true);\r
3657 });\r
3658\r
3659 it("should have the treeStore available", function() {\r
3660 var root = store.getRoot();\r
3661 expect(root.getTreeStore()).toBe(store);\r
3662 });\r
3663 });\r
3664\r
3665 describe("with a model instance", function() {\r
3666 var root;\r
3667\r
3668 beforeEach(function() {\r
3669 root = new NodeModel();\r
3670 store = new Ext.data.TreeStore({\r
3671 model: NodeModel,\r
3672 root: root\r
3673 });\r
3674 });\r
3675\r
3676 afterEach(function() {\r
3677 root = null;\r
3678 });\r
3679\r
3680 it("should set the root property", function() {\r
3681 expect(root.get('root')).toBe(true);\r
3682 });\r
3683\r
3684 it("should have the treeStore available", function() {\r
3685 expect(root.getTreeStore()).toBe(store);\r
3686 });\r
3687 });\r
3688 });\r
3689\r
3690 describe("after creation", function() {\r
3691 describe("with a model config", function() {\r
3692 beforeEach(function() {\r
3693 store = new Ext.data.TreeStore({\r
3694 model: NodeModel,\r
3695 root: {}\r
3696 });\r
3697 });\r
3698\r
3699 it("should set the root property", function() {\r
3700 var oldRoot = store.getRoot();\r
3701 store.setRoot({\r
3702 id: 'foo'\r
3703 });\r
3704 expect(oldRoot.get('root')).toBe(false);\r
3705 expect(store.getRoot().get('root')).toBe(true);\r
3706 expect(store.getRoot().id).toBe('foo');\r
3707 });\r
3708\r
3709 it("should have the treeStore available", function() {\r
3710 var oldRoot = store.getRoot();\r
3711 store.setRoot({\r
3712 id: 'foo'\r
3713 });\r
3714 expect(oldRoot.getTreeStore()).toBeNull();\r
3715 expect(store.getRoot().getTreeStore()).toBe(store);\r
3716 });\r
3717 });\r
3718\r
3719 describe("with a model instance", function() {\r
3720 var root, oldRoot;\r
3721\r
3722 beforeEach(function() {\r
3723 root = new NodeModel();\r
3724 store = new Ext.data.TreeStore({\r
3725 model: NodeModel,\r
3726 root: {}\r
3727 });\r
3728 oldRoot = store.getRoot();\r
3729 });\r
3730\r
3731 afterEach(function() {\r
3732 oldRoot = root = null;\r
3733 });\r
3734\r
3735 it("should set the root property", function() {\r
3736 store.setRoot(root);\r
3737 expect(oldRoot.get('root')).toBe(false);\r
3738 expect(store.getRoot().get('root')).toBe(true);\r
3739 expect(store.getRoot()).toBe(root);\r
3740 });\r
3741\r
3742 it("should have the treeStore available", function() {\r
3743 store.setRoot(root);\r
3744 expect(oldRoot.getTreeStore()).toBeNull();\r
3745 expect(store.getRoot().getTreeStore()).toBe(store);\r
3746 });\r
3747 });\r
3748 });\r
3749 });\r
3750\r
3751 describe('Changing root node', function() {\r
3752 it('should clear the root property', function() {\r
3753 store = new Ext.data.TreeStore({\r
3754 root: {\r
3755 text: 'Root',\r
3756 expanded: true,\r
3757 children: [{\r
3758 text: 'A',\r
3759 leaf: true\r
3760 }, {\r
3761 text: 'B',\r
3762 leaf: true\r
3763 }]\r
3764 }\r
3765 });\r
3766\r
3767 var oldRoot = store.getRootNode();\r
3768\r
3769 expect(oldRoot.get('root')).toBe(true);\r
3770\r
3771 store.setRoot({\r
3772 text: 'NewRoot',\r
3773 expanded: true,\r
3774 children: [{\r
3775 text: 'New A',\r
3776 leaf: true\r
3777 }, {\r
3778 text: 'New B',\r
3779 leaf: true\r
3780 }]\r
3781 });\r
3782\r
3783 expect(oldRoot.get('root')).toBe(false);\r
3784\r
3785 });\r
3786 });\r
3787\r
3788 describe('commitChanges', function() {\r
3789 beforeEach(function() {\r
3790 makeStore([{\r
3791 text: 'Foo',\r
3792 leaf: true\r
3793 }]);\r
3794 });\r
3795\r
3796 it('should clear the removed collection', function() {\r
3797 var root = store.getRoot();\r
3798\r
3799 root.removeChild(root.getChildAt(0));\r
3800\r
3801 store.commitChanges();\r
3802\r
3803 expect(store.removedNodes.length).toBe(0);\r
3804 });\r
3805 });\r
3806\r
3807 describe("proxy", function() {\r
3808 it("should use the model's memory proxy when no proxy is defined on the store", function() {\r
3809 store = new Ext.data.TreeStore({\r
3810 root: { text: 'Foo' }\r
3811 });\r
3812 expect(store.getProxy().isMemoryProxy).toBe(true);\r
3813 expect(store.getProxy()).toBe(store.model.getProxy());\r
3814 });\r
3815\r
3816 it("should set the store's proxy on the model", function() {\r
3817 store = new Ext.data.TreeStore({\r
3818 root: { text: 'Foo' },\r
3819 proxy: {\r
3820 type: 'ajax',\r
3821 url: 'foo'\r
3822 }\r
3823 });\r
3824 expect(store.getProxy().isAjaxProxy).toBe(true);\r
3825 expect(store.getProxy().url).toBe('foo');\r
3826 expect(store.getProxy()).toBe(store.model.getProxy());\r
3827 });\r
3828 });\r
3829\r
3830 describe('rejected changes', function () {\r
3831 // Note that we don't actually need to remove a node to test this.\r
3832 function doTests(rootVisible) {\r
3833 describe('rootVisible = ' + rootVisible, function () {\r
3834 it('should not include the root node', function () {\r
3835 makeStore([{\r
3836 children: [2, 3]\r
3837 }], {\r
3838 rootVisible: rootVisible\r
3839 });\r
3840\r
3841 expect(Ext.Array.contains(store.getRejectRecords(), store.getRoot())).toBe(false);\r
3842 });\r
3843 });\r
3844 }\r
3845\r
3846 doTests(true);\r
3847 doTests(false);\r
3848 });\r
3849});\r
3850\r