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