]> git.proxmox.com Git - extjs.git/blob - extjs/packages/core/test/specs/data/NodeInterface.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / test / specs / data / NodeInterface.js
1 describe('Ext.data.NodeInterface', function() {
2 function spyOnEvent(object, eventName, fn) {
3 var obj = {
4 fn: fn || Ext.emptyFn
5 },
6 spy = spyOn(obj, "fn");
7 object.addListener(eventName, obj.fn);
8 return spy;
9 }
10
11 beforeEach(function() {
12 Ext.define('spec.TreeNode', {
13 extend: 'Ext.data.TreeModel',
14 fields: [
15 {name: 'text', type: 'string'}
16 ],
17 proxy: {
18 type: 'memory'
19 }
20 });
21 });
22
23 afterEach(function() {
24 Ext.undefine('spec.TreeNode');
25 Ext.data.Model.schema.clear();
26 });
27
28 describe('decorating', function() {
29 var fields;
30
31 beforeEach(function() {
32 fields = spec.TreeNode.prototype.fieldsMap;
33 });
34
35 it('should decorate the Model with a parentId field that has the same type as the idProperty', function() {
36 var field = fields.parentId,
37 type = fields[spec.TreeNode.idProperty].getType();
38
39 expect(field.getPersist()).toBe(true);
40 expect(field.getType()).toBe(type);
41 expect(field.getDefaultValue()).toBeNull();
42 });
43 it('should decorate the Model with an index field', function() {
44 var field = fields.index;
45
46 expect(field.getPersist()).toBe(false);
47 expect(field.getType()).toBe('int');
48 expect(field.getDefaultValue()).toBe(-1);
49 });
50 it('should decorate the Model with a depth field', function() {
51 var field = fields.depth;
52
53 expect(field.getPersist()).toBe(false);
54 expect(field.getType()).toBe('int');
55 expect(field.getDefaultValue()).toBe(0);
56 });
57 it('should decorate the Model with an expanded field', function() {
58 var field = fields.expanded;
59
60 expect(field.getPersist()).toBe(false);
61 expect(field.getType()).toBe('bool');
62 expect(field.getDefaultValue()).toBe(false);
63 });
64 it('should decorate the Model with an expandable field', function() {
65 var field = fields.expandable;
66
67 expect(field.getPersist()).toBe(false);
68 expect(field.getType()).toBe('bool');
69 expect(field.getDefaultValue()).toBe(true);
70 });
71 it('should decorate the Model with a checked field', function() {
72 var field = fields.checked;
73
74 expect(field.getPersist()).toBe(false);
75 expect(field.getType()).toBe('auto');
76 expect(field.getDefaultValue()).toBe(null);
77 });
78 it('should decorate the Model with a leaf field', function() {
79 var field = fields.leaf;
80
81 expect(field.getPersist()).toBe(true);
82 expect(field.getType()).toBe('bool');
83 expect(field.getDefaultValue()).toBe(false);
84 });
85 it('should decorate the Model with a cls field', function() {
86 var field = fields.cls;
87
88 expect(field.getPersist()).toBe(false);
89 expect(field.getType()).toBe('string');
90 expect(field.getDefaultValue()).toBe('');
91 });
92 it('should decorate the Model with an iconCls field', function() {
93 var field = fields.iconCls;
94
95 expect(field.getPersist()).toBe(false);
96 expect(field.getType()).toBe('string');
97 expect(field.getDefaultValue()).toBe('');
98 });
99 it('should decorate the Model with an icon field', function() {
100 var field = fields.icon;
101
102 expect(field.getPersist()).toBe(false);
103 expect(field.getType()).toBe('string');
104 expect(field.getDefaultValue()).toBe('');
105 });
106 it('should decorate the Model with a root field', function() {
107 var field = fields.root;
108
109 expect(field.getPersist()).toBe(false);
110 expect(field.getType()).toBe('bool');
111 expect(field.getDefaultValue()).toBe(false);
112 });
113 it('should decorate the Model with an isLast field', function() {
114 var field = fields.isLast;
115
116 expect(field.getPersist()).toBe(false);
117 expect(field.getType()).toBe('bool');
118 expect(field.getDefaultValue()).toBe(false);
119 });
120 it('should decorate the Model with an isFirst field', function() {
121 var field = fields.isFirst;
122
123 expect(field.getPersist()).toBe(false);
124 expect(field.getType()).toBe('bool');
125 expect(field.getDefaultValue()).toBe(false);
126 });
127 it('should decorate the Model with an allowDrop field', function() {
128 var field = fields.allowDrop;
129
130 expect(field.getPersist()).toBe(false);
131 expect(field.getType()).toBe('bool');
132 expect(field.getDefaultValue()).toBe(true);
133 });
134
135 it('should decorate the Model with an allowDrag field', function() {
136 var field = fields.allowDrag;
137
138 expect(field.getPersist()).toBe(false);
139 expect(field.getType()).toBe('bool');
140 expect(field.getDefaultValue()).toBe(true);
141 });
142 it('should decorate the Model with a loaded field', function() {
143 var field = fields.loaded;
144
145 expect(field.getPersist()).toBe(false);
146 expect(field.getType()).toBe('bool');
147 expect(field.getDefaultValue()).toBe(false);
148 });
149 it('should decorate the Model with a loading field', function() {
150 var field = fields.loading;
151
152 expect(field.getPersist()).toBe(false);
153 expect(field.getType()).toBe('bool');
154 expect(field.getDefaultValue()).toBe(false);
155 });
156 it('should decorate the Model with an href field', function() {
157 var field = fields.href;
158
159 expect(field.getPersist()).toBe(false);
160 expect(field.getType()).toBe('string');
161 expect(field.getDefaultValue()).toBe('');
162 });
163 it('should decorate the Model with an hrefTarget field', function() {
164 var field = fields.hrefTarget;
165
166 expect(field.getPersist()).toBe(false);
167 expect(field.getType()).toBe('string');
168 expect(field.getDefaultValue()).toBe('');
169 });
170 it('should decorate the Model with a qtip field', function() {
171 var field = fields.qtip;
172
173 expect(field.getPersist()).toBe(false);
174 expect(field.getType()).toBe('string');
175 expect(field.getDefaultValue()).toBe('');
176 });
177 it('should decorate the Model with a qtitle field', function() {
178 var field = fields.qtitle;
179
180 expect(field.getPersist()).toBe(false);
181 expect(field.getType()).toBe('string');
182 expect(field.getDefaultValue()).toBe('');
183 });
184 it('should decorate the Model with a children field', function() {
185 var field = fields.children;
186
187 expect(field.getPersist()).toBe(false);
188 expect(field.getType()).toBe('auto');
189 expect(field.getDefaultValue()).toBe(null);
190 });
191 it('should decorate Model class of a given record', function() {
192 var MyModel, record1, record2;
193 MyModel = Ext.define('spec.MyModel', {
194 extend: 'Ext.data.Model',
195 fields: [
196 {name: 'text', type: 'string'}
197 ],
198 proxy: {
199 type: 'memory'
200 }
201 });
202 record1 = MyModel.create({text: 'record1'});
203 record2 = MyModel.create({text: 'record2'});
204 expect(MyModel.prototype.isNode).toBeUndefined();
205 expect(record1.isNode).toBeUndefined();
206 expect(record2.isNode).toBeUndefined();
207
208 Ext.data.NodeInterface.decorate(record1);
209 expect(MyModel.prototype.isNode).toBeTruthy();
210 expect(record1.isNode).toBeTruthy();
211 expect(record2.isNode).toBeTruthy();
212
213 Ext.undefine('spec.MyModel');
214 });
215
216 });
217
218
219 describe('methods', function() {
220 var leftChild, rightChild, rootNode, spareNode, spy;
221
222 function insertDefaultChildren() {
223 rootNode.appendChild(leftChild);
224 rootNode.appendChild(rightChild);
225 rootNode.updateInfo(false, {
226 isFirst: true,
227 isLast: true,
228 depth: 0,
229 index: 0,
230 parentId: null
231 });
232 }
233
234 beforeEach(function() {
235 leftChild = new spec.TreeNode({
236 id: 'left'
237 });
238
239 rightChild = new spec.TreeNode({
240 id: 'right'
241 });
242
243 rootNode = new spec.TreeNode({
244 id: 'root'
245 });
246
247 //we use this in several tests as an example node to add
248 spareNode = new spec.TreeNode({
249 id: 'spare'
250 });
251
252 });
253
254 describe("isFirst", function() {
255 beforeEach(function() {
256 insertDefaultChildren.call(this);
257 });
258
259 it("should have rootNode which is first", function() {
260 expect(rootNode.isFirst()).toBe(true);
261 });
262 it("should have leftChild which is first", function() {
263 expect(leftChild.isFirst()).toBe(true);
264 });
265 it("should have rightChild which is not first", function() {
266 expect(rightChild.isFirst()).toBe(false);
267 });
268 });
269
270 describe("isLast", function() {
271 beforeEach(function(){
272 insertDefaultChildren.call(this);
273 });
274
275 it("should have rootNode which is last", function() {
276 expect(rootNode.isLast()).toBe(true);
277 });
278 it("should have leftChild which is not last", function() {
279 expect(leftChild.isLast()).toBe(false);
280 });
281 it("should have rightChild which is last", function() {
282 expect(rightChild.isLast()).toBe(true);
283 });
284 });
285
286 describe("hasChildNodes", function() {
287 beforeEach(function() {
288 rootNode.appendChild(leftChild);
289 });
290
291 it("should have rootNode with children", function() {
292 expect(rootNode.hasChildNodes()).toBe(true);
293 });
294 it("should have leftChild whithout children", function() {
295 expect(leftChild.hasChildNodes()).toBe(false);
296 });
297 });
298
299 describe("isExpandable", function() {
300 it("should have node expandable if it has children", function() {
301 spareNode.appendChild(leftChild);
302 expect(spareNode.isExpandable()).toBe(true);
303 });
304
305 it("should have node expandable if has no children", function() {
306 expect(spareNode.isExpandable()).toBe(true);
307 });
308 it("should have node not expandable if it is a leaf node", function() {
309 spareNode.set('leaf', true);
310 expect(spareNode.isExpandable()).toBe(false);
311 });
312 });
313
314 describe("append", function(){
315 describe("appending children", function() {
316
317 it("should fire beforeappend", function() {
318 spy = spyOnEvent(rootNode, "beforeappend").andCallThrough();
319
320 rootNode.appendChild(leftChild);
321
322 expect(spy).toHaveBeenCalledWith(rootNode, leftChild);
323 });
324
325 it("should cancel append if beforeappend return false", function() {
326 spy = spyOnEvent(rootNode, "beforeappend").andReturn(false);
327 expect(rootNode.appendChild(leftChild)).toBe(false);
328
329 expect(spy.callCount).toEqual(1);
330 });
331
332 it("should set firstChild", function() {
333
334 rootNode.appendChild(leftChild);
335
336 expect(rootNode.firstChild).toEqual(leftChild);
337 });
338
339 it("should set lastChild", function() {
340
341 rootNode.appendChild(leftChild);
342
343 expect(rootNode.lastChild).toEqual(leftChild);
344 });
345
346 it("should add node to childnodes", function() {
347 var childNodes;
348
349 rootNode.appendChild(leftChild);
350
351 childNodes = rootNode.childNodes;
352
353 expect(childNodes.length).toEqual(1);
354 expect(childNodes[0]).toEqual(leftChild);
355 });
356
357 it("should fire append event", function() {
358 spy = spyOnEvent(rootNode, "append").andCallThrough();
359
360 rootNode.appendChild(leftChild);
361
362 expect(spy).toHaveBeenCalledWith(rootNode, leftChild, 0);
363 });
364
365 it("should return node", function() {
366 var ret = rootNode.appendChild(leftChild);
367
368 expect(ret).toEqual(leftChild);
369 });
370
371 it("should append array of nodes", function() {
372 rootNode.appendChild([leftChild, rightChild]);
373
374 var childNodes = rootNode.childNodes;
375
376 expect(childNodes[0]).toEqual(leftChild);
377 expect(childNodes[1]).toEqual(rightChild);
378 expect(childNodes.length).toEqual(2);
379 });
380 });
381
382 describe("appending with existing siblings", function() {
383 beforeEach(function() {
384 insertDefaultChildren.call(this);
385 });
386
387 it("should set next sibling", function() {
388 expect(leftChild.nextSibling).toEqual(rightChild);
389 expect(rightChild.nextSibling).toBeNull();
390 });
391
392 it("should set previous sibling", function() {
393 expect(rightChild.previousSibling).toEqual(leftChild);
394 expect(leftChild.previousSibling).toBeNull();
395 });
396 });
397
398 describe("appending children from an existing node", function() {
399 var oldParent, spy;
400
401 beforeEach(function() {
402 oldParent = new spec.TreeNode({id: 'oldparent'});
403 oldParent.appendChild(spareNode);
404 });
405
406 it("should remove from existing node", function() {
407 spy = spyOn(oldParent, "removeChild").andCallThrough();
408
409 rootNode.appendChild(spareNode);
410
411 expect(spy).toHaveBeenCalledWith(spareNode, false, undefined, true);
412 });
413
414 it("should fire beforeremove event", function(){
415 spy = spyOnEvent(oldParent, "beforeremove").andCallThrough();
416
417 rootNode.appendChild(spareNode);
418
419 expect(spy).toHaveBeenCalledWith(oldParent, spareNode, true);
420 });
421
422 it("should fire remove event", function(){
423 spy = spyOnEvent(oldParent, "remove").andCallThrough();
424
425 rootNode.appendChild(spareNode);
426
427 // Just use the context argumemt from the args as the last arg
428 expect(spy).toHaveBeenCalledWith(oldParent, spareNode, true, spy.mostRecentCall.args[3]);
429 });
430
431 it("should fire beforemove event", function() {
432 spy = spyOnEvent(spareNode, "beforemove").andCallThrough();
433
434 rootNode.appendChild(spareNode);
435
436 expect(spy).toHaveBeenCalledWith(spareNode, oldParent, rootNode, 0);
437 });
438 it("should fire move event", function() {
439 spy = spyOnEvent(spareNode, "move").andCallThrough();
440
441 rootNode.appendChild(spareNode);
442
443 expect(spy).toHaveBeenCalledWith(spareNode, oldParent, rootNode, 0);
444 });
445 });
446 });
447
448 describe("insert", function(){
449
450 beforeEach(function(){
451 rootNode.appendChild(rightChild);
452 });
453
454 describe("inserting children", function() {
455 it("should call appendChild if the node to insert before is null", function() {
456 spy = spyOn(rootNode, "appendChild");
457
458 rootNode.insertBefore(leftChild);
459
460 expect(spy).toHaveBeenCalledWith(leftChild);
461 });
462
463 it("should do nothing if the node to insert before is equal to the node to insert", function() {
464 expect(rootNode.insertBefore(leftChild, leftChild)).toBe(false);
465 });
466
467 it("should fire beforeinsert", function() {
468 spy = spyOnEvent(rootNode, "beforeinsert").andCallThrough();
469
470 rootNode.insertBefore(leftChild, rightChild);
471
472 expect(spy).toHaveBeenCalledWith(rootNode, leftChild, rightChild);
473 });
474
475 it("should cancel insert if beforeinsert return false", function() {
476 spy = spyOnEvent(rootNode, "beforeinsert").andReturn(false);
477
478 expect(rootNode.insertBefore(leftChild, rightChild)).toBe(false);
479
480 expect(spy.callCount).toEqual(1);
481 });
482
483 it("should set firstChild", function() {
484 rootNode.insertBefore(leftChild, rightChild);
485
486 expect(rootNode.firstChild).toEqual(leftChild);
487 });
488
489 it("should set lastChild", function() {
490 rootNode.insertBefore(leftChild, rightChild);
491
492 expect(rootNode.lastChild).toEqual(rightChild);
493 });
494
495 it("should fire insert", function() {
496 spy = spyOnEvent(rootNode, "insert").andCallThrough();
497
498 rootNode.insertBefore(leftChild, rightChild);
499
500 expect(spy).toHaveBeenCalledWith(rootNode, leftChild, rightChild);
501 });
502
503 it("should update indexes for all siblings after the position where the node was inserted", function() {
504 rootNode.insertBefore(spareNode, rightChild);
505
506 rootNode.insertBefore(leftChild, spareNode);
507
508 expect(spareNode.get('index')).toEqual(1);
509 expect(rightChild.get('index')).toEqual(2);
510 });
511
512
513 it("should handle siblings", function(){
514 expect(leftChild.previousSibling).toBeNull();
515 expect(leftChild.nextSibling).toBeNull();
516 expect(rightChild.previousSibling).toBeNull();
517 expect(rightChild.nextSibling).toBeNull();
518
519 rootNode.insertBefore(leftChild, rightChild);
520
521 expect(leftChild.previousSibling).toBeNull();
522 expect(leftChild.nextSibling).toEqual(rightChild);
523 expect(rightChild.previousSibling).toEqual(leftChild);
524 expect(rightChild.nextSibling).toBeNull();
525 });
526
527 describe("move", function() {
528 beforeEach(function() {
529 rootNode.appendChild(leftChild);
530 });
531
532 it("should fire beforemove", function() {
533 spy = spyOnEvent(leftChild, "beforemove").andCallThrough();
534
535 rootNode.insertBefore(leftChild, rightChild);
536
537 expect(spy).toHaveBeenCalledWith(leftChild, rootNode, rootNode, 0, rightChild);
538 });
539
540 it("should cancel insert if beforemove return false", function() {
541 spy = spyOnEvent(leftChild, "beforemove").andReturn(false);
542
543 expect(rootNode.insertBefore(leftChild, rightChild)).toBe(false);
544
545 expect(spy.callCount).toEqual(1);
546 });
547
548 it("should fire move", function() {
549 spy = spyOnEvent(leftChild, "move").andCallThrough();
550
551 rootNode.insertBefore(leftChild, rightChild);
552
553 expect(spy).toHaveBeenCalledWith(leftChild, rootNode, rootNode, 0, rightChild);
554 });
555
556 });
557 });
558 });
559
560 describe("removing children", function() {
561 it("should return false when removing bad node", function(){
562 expect(rootNode.removeChild(leftChild)).toBe(false);
563 });
564
565 it("should fire beforeremove event", function(){
566 insertDefaultChildren.call(this);
567
568 spy = spyOnEvent(rootNode, "beforeremove").andCallThrough();
569
570 rootNode.removeChild(leftChild);
571
572 expect(spy).toHaveBeenCalledWith(rootNode, leftChild, false);
573 });
574
575 it("should cancel remove if beforeremove returns false", function() {
576 insertDefaultChildren.call(this);
577
578 spy = spyOnEvent(rootNode, "beforeremove").andReturn(false);
579
580 expect(rootNode.removeChild(leftChild)).toBe(false);
581
582 expect(spy.callCount).toEqual(1);
583 });
584
585 it("should fire remove event", function() {
586 insertDefaultChildren.call(this);
587
588 spy = spyOnEvent(rootNode, "remove").andCallThrough();
589
590 rootNode.removeChild(leftChild);
591
592 // Just use the context argumemt from the args as the last arg
593 expect(spy).toHaveBeenCalledWith(rootNode, leftChild, false, spy.mostRecentCall.args[3]);
594 });
595
596 it("should remove child from childNodes", function() {
597 var childNodes, count;
598
599 insertDefaultChildren.call(this);
600
601 childNodes = rootNode.childNodes;
602 count = childNodes.length;
603
604 rootNode.removeChild(leftChild);
605
606 expect(childNodes.length).toEqual(count - 1);
607 expect(childNodes[0]).toEqual(rightChild);
608 });
609
610 it("should manage siblings", function() {
611 insertDefaultChildren.call(this);
612
613 //this gives us a third child - 'right' is actually now center
614 rootNode.appendChild(spareNode);
615
616 rootNode.removeChild(rightChild);
617
618 expect(leftChild.nextSibling, spareNode);
619
620 expect(spareNode.previousSibling, leftChild);
621 });
622
623 it("should erase node if asked", function() {
624 insertDefaultChildren.call(this);
625
626 spy = spyOn(leftChild, "erase").andCallThrough();
627
628 rootNode.removeChild(leftChild, true);
629
630 expect(spy).toHaveBeenCalled();
631 });
632
633 it("should clear node if asked", function() {
634 insertDefaultChildren.call(this);
635
636 spy = spyOn(leftChild, "clear").andCallThrough();
637
638 rootNode.removeChild(leftChild, false);
639
640 expect(spy).toHaveBeenCalled();
641 });
642 it("should update indexes for all siblings after the node's old position", function() {
643 insertDefaultChildren.call(this);
644
645 rootNode.appendChild(spareNode);
646
647 rootNode.removeChild(leftChild);
648
649 expect(rightChild.get('index')).toEqual(0);
650 expect(spareNode.get('index')).toEqual(1);
651 });
652 });
653
654 describe("clearing references", function() {
655 beforeEach(function(){
656 insertDefaultChildren.call(this);
657 rootNode.appendChild(spareNode);
658 });
659
660 it("should nullify parentNode", function() {
661 expect(rightChild.parentNode).not.toBeNull();
662
663 rightChild.clear();
664
665 expect(rightChild.parentNode).toBeNull();
666 });
667
668 it("should nullifies nextSibling", function() {
669 expect(rightChild.nextSibling).not.toBeNull();
670
671 rightChild.clear();
672
673 expect(rightChild.nextSibling).toBeNull();
674 });
675
676 it("should nullifies previousSibling", function() {
677 expect(rightChild.previousSibling).not.toBeNull();
678
679 rightChild.clear();
680
681 expect(rightChild.previousSibling).toBeNull();
682 });
683
684 it("should remove lastChild and firstChild references", function() {
685 rightChild.clear(true);
686
687 expect(rightChild.firstChild).toBeNull();
688 expect(rightChild.lastChild).toBeNull();
689 });
690 });
691
692 describe("item", function() {
693 it("should return the child node at the specified index", function() {
694 rootNode.appendChild(leftChild);
695 rootNode.appendChild(rightChild);
696 rootNode.appendChild(spareNode);
697
698 expect(rootNode.getChildAt(0)).toEqual(leftChild);
699 expect(rootNode.getChildAt(1)).toEqual(rightChild);
700 expect(rootNode.getChildAt(2)).toEqual(spareNode);
701 });
702 });
703
704 describe("silent destroy", function() {
705 it("should purge node listeners", function() {
706 spy = spyOn(leftChild.mixins.observable, "clearListeners").andCallThrough();
707
708 leftChild.destroy(true);
709
710 expect(spy).toHaveBeenCalled();
711 });
712
713 it("should erase children", function() {
714 var spy2;
715
716 insertDefaultChildren.call(this);
717
718 spy = spyOn(leftChild, "erase").andCallThrough();
719 spy2 = spyOn(rightChild, "erase").andCallThrough();
720
721 rootNode.erase();
722
723 expect(spy).toHaveBeenCalled();
724 expect(spy2).toHaveBeenCalled();
725 });
726
727 it("should nullify childNodes", function() {
728 insertDefaultChildren.call(this);
729
730 expect(rootNode.childNodes).not.toBeNull();
731
732 rootNode.erase(true);
733
734 expect(rootNode.childNodes).toBeNull();
735 });
736 });
737
738 describe("non-silent destroy", function() {
739 it("should remove node", function() {
740 insertDefaultChildren.call(this);
741
742 spy = spyOn(leftChild, "remove").andCallThrough();
743
744 leftChild.erase(false);
745
746 expect(spy).toHaveBeenCalled();
747 });
748 });
749
750 describe("remove", function() {
751 it("should remove from parent", function() {
752 spy = spyOn(rootNode, "removeChild").andCallThrough();
753
754 rootNode.appendChild(leftChild);
755
756 leftChild.remove();
757
758 expect(spy).toHaveBeenCalledWith(leftChild, undefined, undefined);
759 });
760
761 it("should return node", function() {
762 expect(leftChild.remove()).toEqual(leftChild);
763 });
764 });
765
766 describe("removeAll", function() {
767 it("should remove all children", function() {
768 rootNode.appendChild([leftChild, rightChild, spareNode]);
769 rootNode.removeAll();
770 expect(rootNode.childNodes.length).toBe(0);
771 });
772 });
773
774 describe("replacing children", function() {
775 beforeEach(function() {
776 insertDefaultChildren.call(this);
777 });
778
779 it("should keep the same childNodes length", function() {
780 var count = rootNode.childNodes.length;
781
782 rootNode.replaceChild(spareNode, leftChild);
783
784 expect(rootNode.childNodes.length).toEqual(count);
785 });
786
787 it("should replace node", function() {
788 rootNode.replaceChild(spareNode, leftChild);
789
790 expect(rootNode.childNodes[0], spareNode);
791 });
792 });
793
794 describe("getting depth", function() {
795 beforeEach(function() {
796 insertDefaultChildren.call(this);
797 leftChild.appendChild(spareNode);
798 });
799
800 it("should have a depth of 0 for rootNode", function(){
801 expect(rootNode.getDepth()).toEqual(0);
802 });
803
804 it("should have a depth of 1 for leftChild and rightChild", function(){
805 expect(rightChild.getDepth()).toEqual(1);
806 expect(leftChild.getDepth()).toEqual(1);
807 });
808
809 it("should have a depth of 2 for spareNode", function(){
810 expect(spareNode.getDepth()).toEqual(2);
811 });
812 });
813
814 describe("getting path", function() {
815 beforeEach(function() {
816 insertDefaultChildren.call(this);
817 leftChild.appendChild(spareNode);
818 });
819
820 it("should set root path", function() {
821 expect(rootNode.getPath()).toEqual("/root");
822 });
823
824 it("should set middle path", function() {
825 expect(leftChild.getPath()).toEqual("/root/left");
826 expect(rightChild.getPath()).toEqual("/root/right");
827 });
828
829 it("should set leaf path", function() {
830 expect(spareNode.getPath()).toEqual("/root/left/spare");
831 });
832 });
833
834 describe("indexOf", function(){
835 it("should always return -1 when the node is empty", function(){
836 expect(rootNode.indexOf(spareNode)).toBe(-1);
837 });
838
839 it("should return -1 when the passed node is not a child", function(){
840 rootNode.appendChild(leftChild);
841 expect(rootNode.indexOf(spareNode)).toBe(-1);
842 });
843
844 it("should return the correct index when the node exists", function(){
845 rootNode.appendChild([leftChild, spareNode, rightChild]);
846 expect(rootNode.indexOf(spareNode)).toBe(1);
847 });
848 });
849
850 describe("indexOfId", function(){
851 it("should always return -1 when the node is empty", function(){
852 expect(rootNode.indexOfId('spare')).toBe(-1);
853 });
854
855 it("should return -1 when the passed node is not a child", function(){
856 rootNode.appendChild(leftChild);
857 expect(rootNode.indexOfId('spare')).toBe(-1);
858 });
859
860 it("should return the correct index when the node exists", function(){
861 rootNode.appendChild([leftChild, spareNode, rightChild]);
862 expect(rootNode.indexOfId('spare')).toBe(1);
863 });
864 });
865
866 describe("bubbling", function() {
867 var bubbleFn;
868
869 beforeEach(function() {
870 insertDefaultChildren.call(this);
871 leftChild.appendChild(spareNode);
872 bubbleFn = jasmine.createSpy();
873 });
874
875 it("should call bubbleFn 3 times", function() {
876 spareNode.bubble(bubbleFn);
877
878 expect(bubbleFn.callCount).toEqual(3);
879 });
880
881 it("should call bubbleFn with node spare, left, root", function() {
882 spareNode.bubble(bubbleFn);
883
884 expect(bubbleFn.calls[0].args).toEqual([spareNode]);
885 expect(bubbleFn.calls[1].args).toEqual([leftChild]);
886 expect(bubbleFn.calls[2].args).toEqual([rootNode]);
887 });
888
889 it("should call bubbleFn with a defined scope", function() {
890 spareNode.bubble(bubbleFn, fakeScope);
891
892 expect(bubbleFn.calls[0].object).toBe(fakeScope);
893 expect(bubbleFn.calls[1].object).toBe(fakeScope);
894 expect(bubbleFn.calls[2].object).toBe(fakeScope);
895 });
896
897 it("should call bubbleFn with customs arguments", function() {
898 var customArgs = ['some', 'args'];
899
900 spareNode.bubble(bubbleFn, spareNode, customArgs);
901
902 expect(bubbleFn.calls[0].args).toEqual(customArgs);
903 expect(bubbleFn.calls[1].args).toEqual(customArgs);
904 expect(bubbleFn.calls[2].args).toEqual(customArgs);
905 });
906
907 it("should stop when bubbleFn return false", function() {
908 bubbleFn.andCallFake(function(node) {
909 if (node.getId() == 'left') {
910 return false;
911 }
912 });
913
914 spareNode.bubble(bubbleFn);
915
916 expect(bubbleFn.callCount).toEqual(2);
917 });
918 });
919
920 describe("cascading", function() {
921 var cascadeFn;
922
923 beforeEach(function(){
924 insertDefaultChildren.call(this);
925 leftChild.appendChild(spareNode);
926 cascadeFn = jasmine.createSpy();
927 });
928
929 it("should call cascadeFn 4 times", function() {
930 rootNode.cascadeBy(cascadeFn);
931
932 expect(cascadeFn.callCount).toEqual(4);
933 });
934
935 it("should call cascadeFn with node root, leftChild, spareNode, rightChild", function() {
936 rootNode.cascadeBy(cascadeFn);
937
938 expect(cascadeFn.calls[0].args).toEqual([rootNode]);
939 expect(cascadeFn.calls[1].args).toEqual([leftChild]);
940 expect(cascadeFn.calls[2].args).toEqual([spareNode]);
941 expect(cascadeFn.calls[3].args).toEqual([rightChild]);
942 });
943
944 it("should call cascadeFn with a defined scope", function() {
945 rootNode.cascadeBy(cascadeFn, fakeScope);
946
947 expect(cascadeFn.calls[0].object).toBe(fakeScope);
948 expect(cascadeFn.calls[1].object).toBe(fakeScope);
949 expect(cascadeFn.calls[2].object).toBe(fakeScope);
950 expect(cascadeFn.calls[3].object).toBe(fakeScope);
951 });
952
953 it("should call cascadeFn with customs arguments", function() {
954 var customArgs = ['some', 'args'];
955
956 rootNode.cascadeBy(cascadeFn, rootNode, customArgs);
957
958 expect(cascadeFn.calls[0].args).toEqual(customArgs);
959 expect(cascadeFn.calls[1].args).toEqual(customArgs);
960 expect(cascadeFn.calls[2].args).toEqual(customArgs);
961 expect(cascadeFn.calls[3].args).toEqual(customArgs);
962 });
963
964 it("should stop at end of branch when cascadeFn return false", function() {
965 cascadeFn.andCallFake(function(node) {
966 if (node.getId() == 'left') {
967 return false;
968 }
969 });
970
971 rootNode.cascadeBy(cascadeFn);
972
973 expect(cascadeFn.callCount).toEqual(3);
974 });
975 });
976
977 describe("each child", function() {
978 var eachFn;
979
980 beforeEach(function (){
981 insertDefaultChildren.call(this);
982 eachFn = jasmine.createSpy();
983 });
984
985 it("should be called 2 times", function() {
986
987 rootNode.eachChild(eachFn);
988
989 expect(eachFn.callCount).toEqual(2);
990 });
991
992 it("should call eachFn with node root, leftChild, rightChild", function() {
993 rootNode.eachChild(eachFn);
994
995 expect(eachFn.calls[0].args).toEqual([leftChild]);
996 expect(eachFn.calls[1].args).toEqual([rightChild]);
997 });
998
999 it("should call eachFn with a defined scope", function() {
1000 rootNode.eachChild(eachFn, fakeScope);
1001
1002 expect(eachFn.calls[0].object).toBe(fakeScope);
1003 expect(eachFn.calls[1].object).toBe(fakeScope);
1004 });
1005
1006 it("should call eachFn with customs arguments", function() {
1007 var customArgs = ['some', 'args'];
1008
1009 rootNode.eachChild(eachFn, rootNode, customArgs);
1010
1011 expect(eachFn.calls[0].args).toEqual(customArgs);
1012 expect(eachFn.calls[1].args).toEqual(customArgs);
1013 });
1014
1015 it("should stop when eachFn return false", function() {
1016 eachFn.andCallFake(function(node) {
1017 if (node.getId() == 'left') {
1018 return false;
1019 }
1020 });
1021
1022 rootNode.eachChild(eachFn);
1023
1024 expect(eachFn.callCount).toEqual(1);
1025 });
1026 });
1027
1028 describe("ancestors", function() {
1029 beforeEach(function (){
1030 insertDefaultChildren.call(this);
1031 leftChild.appendChild(spareNode);
1032 });
1033
1034 it("should have parent as ancestor", function() {
1035 expect(spareNode.isAncestor(leftChild)).toBe(true);
1036 });
1037
1038 it("should have root as ancestor", function() {
1039 expect(spareNode.isAncestor(rootNode)).toBe(true);
1040 });
1041
1042 it("should not have uncle as ancestor", function() {
1043 expect(spareNode.isAncestor(rightChild)).toBe(false);
1044 });
1045 });
1046
1047 describe("contains", function() {
1048 beforeEach(function (){
1049 insertDefaultChildren.call(this);
1050 leftChild.appendChild(spareNode);
1051 });
1052
1053 it("should contain child", function() {
1054 expect(rootNode.contains(leftChild)).toBe(true);
1055 });
1056
1057 it("should contain grand child", function() {
1058 expect(rootNode.contains(spareNode)).toBe(true);
1059 });
1060
1061 it("should not contain parent", function() {
1062 expect(spareNode.contains(leftChild)).toBe(false);
1063 });
1064 });
1065
1066 describe("finding children", function() {
1067 beforeEach(function (){
1068 insertDefaultChildren.call(this);
1069 leftChild.appendChild(spareNode);
1070 });
1071
1072 describe("findChild", function() {
1073 it("should find shallow children", function() {
1074 expect(rootNode.findChild('id', 'left')).toEqual(leftChild);
1075 });
1076
1077 it("should not find deep children if deep is not specified", function() {
1078 expect(rootNode.findChild('id', 'spare')).toBeNull();
1079 });
1080
1081 it("should not find deep children if deep is false", function() {
1082 expect(rootNode.findChild('id', 'spare', false)).toBeNull();
1083 });
1084
1085 it("should find deep children if deep is true", function() {
1086 expect(rootNode.findChild('id', 'spare', true)).toEqual(spareNode);
1087 });
1088 });
1089
1090 describe("findChildBy", function() {
1091 var child;
1092
1093 it("should find shallow children", function(){
1094 child = rootNode.findChildBy(function(node) {
1095 return node.getId() == 'right';
1096 });
1097
1098 expect(child).toEqual(rightChild);
1099 });
1100
1101 it("should not find deep children if deep is not specified", function(){
1102 child = rootNode.findChildBy(function(node) {
1103 return node.getId() == 'spare';
1104 });
1105
1106 expect(child).toBeNull();
1107 });
1108
1109 it("should not find deep children if deep is false", function(){
1110 child = rootNode.findChildBy(function(node) {
1111 return node.getId() == 'spare';
1112 }, this, false);
1113
1114 expect(child).toBeNull();
1115 });
1116
1117 it("should find deep children if deep is true", function(){
1118 child = rootNode.findChildBy(function(node) {
1119 return node.getId() == 'spare';
1120 }, this, true);
1121
1122 expect(child).toEqual(spareNode);
1123 });
1124
1125 it("should call function with good scope", function(){
1126 var findChildFn = jasmine.createSpy().andReturn(false);
1127
1128 child = rootNode.findChildBy(findChildFn, fakeScope, true);
1129
1130 expect(findChildFn.calls[0].object).toBe(fakeScope);
1131 expect(findChildFn.calls[1].object).toBe(fakeScope);
1132 expect(findChildFn.calls[2].object).toBe(fakeScope);
1133 });
1134 });
1135 });
1136
1137 describe("sort", function() {
1138 var node1,
1139 node2,
1140 node3,
1141 node4,
1142 sortFn;
1143
1144 beforeEach(function() {
1145 Ext.define('spec.EmployeeTreeNode', {
1146 extend: 'Ext.data.Model',
1147 fields: [
1148 { name: 'lastname', type: 'string' },
1149 { name: 'firstname', type: 'string' }
1150 ]
1151 });
1152 Ext.data.NodeInterface.decorate(spec.EmployeeTreeNode);
1153 node1 = new spec.EmployeeTreeNode({lastname: "Avins", firstname: "Jamie"});
1154 node2 = new spec.EmployeeTreeNode({lastname: "Dougan", firstname: "Robert"});
1155 node3 = new spec.EmployeeTreeNode({lastname: "Ferrero", firstname: "Nicolas"});
1156 node4 = new spec.EmployeeTreeNode({lastname: "Spencer", firstname: "Edward"});
1157
1158 rootNode.appendChild([node4, node2, node3, node1]);
1159
1160 sortFn = jasmine.createSpy();
1161 sortFn.andCallFake(function(a, b){
1162 if (a.get('lastname') === b.get('lastname')) {
1163 return 0;
1164 }
1165 return (a.get('lastname') < b.get('lastname')) ? -1 : 1;
1166 });
1167
1168 rootNode.sort(sortFn);
1169 });
1170 afterEach(function() {
1171 Ext.undefine('spec.EmployeeTreeNode');
1172 });
1173
1174 it("should sort the child by lastname with the correct function", function() {
1175 expect(rootNode.childNodes[0]).toEqual(node1);
1176 expect(rootNode.childNodes[1]).toEqual(node2);
1177 expect(rootNode.childNodes[2]).toEqual(node3);
1178 expect(rootNode.childNodes[3]).toEqual(node4);
1179 });
1180
1181 });
1182
1183 describe("copy", function(){
1184 it("should not copy childNodes by default", function(){
1185 var node = new spec.TreeNode({
1186 text: 'Text',
1187 id: 1
1188 });
1189
1190 var newNode = node.copy();
1191
1192 expect(newNode.getData()).toEqual({
1193 allowDrag: true,
1194 allowDrop: true,
1195 checked: null,
1196 children: null,
1197 cls: '',
1198 depth: 0,
1199 expandable: true,
1200 expanded: false,
1201 href: '',
1202 hrefTarget: '',
1203 icon: '',
1204 iconCls: '',
1205 id: 1,
1206 index: -1,
1207 isFirst: false,
1208 isLast: false,
1209 leaf: false,
1210 loaded: false,
1211 loading: false,
1212 parentId: null,
1213 qtip: '',
1214 qtitle: '',
1215 qshowDelay: 0,
1216 root: false,
1217 text: 'Text',
1218 visible: true
1219 });
1220 });
1221
1222 it("should accept a new id", function(){
1223 var node = new spec.TreeNode({
1224 text: 'Text',
1225 id: 1
1226 });
1227
1228 var newNode = node.copy(2);
1229
1230 expect(newNode.getData()).toEqual({
1231 allowDrag: true,
1232 allowDrop: true,
1233 checked: null,
1234 children: null,
1235 cls: '',
1236 depth: 0,
1237 expandable: true,
1238 expanded: false,
1239 href: '',
1240 hrefTarget: '',
1241 icon: '',
1242 iconCls: '',
1243 id: 2,
1244 index: -1,
1245 isFirst: false,
1246 isLast: false,
1247 leaf: false,
1248 loaded: false,
1249 loading: false,
1250 parentId: null,
1251 qtip: '',
1252 qtitle: '',
1253 qshowDelay: 0,
1254 root: false,
1255 text: 'Text',
1256 visible: true
1257 });
1258 });
1259
1260 it("should clone children if deep: true is specified", function(){
1261 var root = new spec.TreeNode({
1262 id: 1,
1263 text: 'Root'
1264 });
1265 var child1 = root.appendChild(new spec.TreeNode({
1266 id: 2,
1267 text: 'Child1'
1268 }));
1269 var child2 = child1.appendChild(new spec.TreeNode({
1270 id: 3,
1271 text: 'Child2'
1272 }));
1273 child2.appendChild(new spec.TreeNode({
1274 id: 4,
1275 text: 'Child3'
1276 }));
1277
1278 var newNode = root.copy(undefined, true);
1279 expect(newNode.childNodes[0].getId()).toBe(2);
1280 expect(newNode.childNodes[0].get('text')).toBe('Child1');
1281
1282 newNode = newNode.childNodes[0];
1283 expect(newNode.childNodes[0].getId()).toBe(3);
1284 expect(newNode.childNodes[0].get('text')).toBe('Child2');
1285
1286 newNode = newNode.childNodes[0];
1287 expect(newNode.childNodes[0].getId()).toBe(4);
1288 expect(newNode.childNodes[0].get('text')).toBe('Child3');
1289 });
1290 });
1291
1292 });
1293
1294 describe("serialize", function(){
1295 it('should create an object representation of the node', function() {
1296 var node = new spec.TreeNode({
1297 text: 'Root',
1298 id: 1
1299 }),
1300 c1 = node.appendChild(new spec.TreeNode({
1301 text: 'C1',
1302 id: 2
1303 })),
1304 c2 = node.appendChild(new spec.TreeNode({
1305 text: 'C1',
1306 id: 3
1307 }));
1308 c1.appendChild( new spec.TreeNode({
1309 text: 'c1.1',
1310 id: 4
1311 }));
1312 c2.appendChild( new spec.TreeNode({
1313 text: 'c2.1',
1314 id: 5
1315 }));
1316
1317 expect(node.serialize()).toEqual({
1318 "text": "Root",
1319 "id": 1,
1320 "parentId": null,
1321 "leaf": false,
1322 "children": [
1323 {
1324 "text": "C1",
1325 "id": 2,
1326 "parentId": 1,
1327 "leaf": false,
1328 "children": [
1329 {
1330 "text": "c1.1",
1331 "id": 4,
1332 "parentId": 2,
1333 "leaf": false
1334 }
1335 ]
1336 },
1337 {
1338 "text": "C1",
1339 "id": 3,
1340 "parentId": 1,
1341 "leaf": false,
1342 "children": [
1343 {
1344 "text": "c2.1",
1345 "id": 5,
1346 "parentId": 3,
1347 "leaf": false
1348 }
1349 ]
1350 }
1351 ]
1352 });
1353 });
1354
1355 it("should not include children if there are none", function(){
1356 var o = new spec.TreeNode({
1357 text: 'foo'
1358 }), s = o.serialize();
1359
1360 expect(s.text).toBe('foo');
1361 expect(s.children).toBeUndefined();
1362 });
1363
1364 it("should include children if they exist", function(){
1365 var o = new spec.TreeNode({
1366 text: 'foo'
1367 }), s;
1368
1369 o.appendChild(new spec.TreeNode({
1370 text: 'bar'
1371 }));
1372
1373 s = o.serialize();
1374 expect(s.text).toBe('foo');
1375 expect(s.children[0].text).toBe('bar');
1376 });
1377 });
1378
1379 describe("collapse", function() {
1380 it("should fire the collapse callback when there are no child nodes", function() {
1381 var root = new spec.TreeNode(),
1382 called;
1383
1384 root.collapseChildren(false, function() {
1385 called = true;
1386 });
1387 expect(called).toBe(true);
1388 root = null;
1389 });
1390 });
1391
1392 describe("modified property tracking", function() {
1393 // Tests for https://sencha.jira.com/browse/EXTJSIV-9223 and https://sencha.jira.com/browse/EXTJSIV-9165
1394 it("should track modifications of fields set as a result of node movement", function() {
1395
1396 // Create a TreeNode subclass in which the index property is persistent.
1397 Ext.define('spec.PersistentIndexTreeNode', {
1398 extend: 'Ext.data.TreeModel',
1399 fields: [
1400 {name: 'text', type: 'string'},
1401 {name: 'index', type: 'int', persist: true, defaultValue: -1}
1402 ],
1403 proxy: {
1404 type: 'memory'
1405 }
1406 });
1407
1408 var root = new spec.PersistentIndexTreeNode({
1409 id: 'TestRoot'
1410 }),
1411 root1 = new spec.PersistentIndexTreeNode({
1412 id: 'OtherTestRoot'
1413 }),
1414 node = new spec.PersistentIndexTreeNode({
1415 id: 'node'
1416 }),
1417 node1;
1418 root.appendChild(node);
1419
1420 // The modified shows that parentId was changed *from* null.
1421 // And the index was changed from -1 (not attached) to 0
1422 expect(node.modified).toEqual({
1423 index: -1,
1424 parentId: null
1425 });
1426
1427 // Clears modified
1428 node.commit();
1429 expect(node.modified).toBeNull();
1430
1431 // Move from parent "TestRoot", index 0 to parent "OtherTestRoot", index 0
1432 // Index should appear in modified object because it has moved parent
1433 root1.appendChild(node);
1434 expect(node.modified).toEqual({
1435 parentId: 'TestRoot',
1436 index: 0
1437 });
1438
1439 // Clears modified
1440 node.commit();
1441
1442 // Should clear the parentId, so the modified now indicates that it was removed from index 0 of "OtherTestRoot"
1443 // removeChild does NOT clear context data properties.
1444 // Mainly because TreeStore.clearRemovedOnLoad uses these values to determine whether
1445 // nodes in the removed list are descendants of a loading node so that tey can be evicted from the removed list.
1446 root1.removeChild(node);
1447 expect(node.modified).toEqual({
1448 lastParentId: undefined,
1449 parentId: 'OtherTestRoot'
1450 });
1451
1452 node = new spec.PersistentIndexTreeNode({
1453 id: 'node'
1454 });
1455 node1 = new spec.PersistentIndexTreeNode({
1456 id: 'node1'
1457 });
1458 root.clear();
1459 root.appendChild([node, node1]);
1460
1461 expect([node.get('index'), node1.get('index')]).toEqual([0, 1]);
1462 node.commit();
1463 node1.commit();
1464
1465 // Move node1 to index:0
1466 root.insertBefore(node1, node);
1467
1468 // Indexes data values are switched
1469 expect([node1.get('index'), node.get('index')]).toEqual([0, 1]);
1470
1471 // node1 must report that its index was modified from initial value 1
1472 expect(node1.modified).toEqual({
1473 index: 1
1474 });
1475
1476 // node must report that its index was modified from initial value 0
1477 expect(node.modified).toEqual({
1478 index: 0
1479 });
1480
1481 root1.appendChild(node1);
1482
1483 Ext.undefine('spec.PersistentIndexTreeNode');
1484 });
1485 });
1486 });