]> git.proxmox.com Git - extjs.git/blame - extjs/packages/core/test/specs/data/Store.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / test / specs / data / Store.js
CommitLineData
6527f429
DM
1describe("Ext.data.Store", function() {\r
2 var fakeScope = {},\r
3 abeRaw, aaronRaw, edRaw, tommyRaw,\r
4 abeRec, aaronRec, edRec, tommyRec,\r
5 store, User, data, spy;\r
6\r
7 function customSort(v) {\r
8 return v * -1;\r
9 }\r
10\r
11 function spyOnEvent(object, eventName, fn) {\r
12 var obj = {\r
13 fn: fn || Ext.emptyFn\r
14 },\r
15 spy = spyOn(obj, "fn");\r
16 object.addListener(eventName, obj.fn);\r
17 return spy;\r
18 }\r
19\r
20 function addStoreData() {\r
21 store.add(edRaw, abeRaw, aaronRaw, tommyRaw);\r
22 assignRecs();\r
23 }\r
24\r
25 function assignRecs() {\r
26 edRec = store.getAt(0);\r
27 abeRec = store.getAt(1);\r
28 aaronRec = store.getAt(2);\r
29 tommyRec = store.getAt(3);\r
30 }\r
31\r
32 function makeUser(email, data) {\r
33 if (Ext.isObject(email)) {\r
34 data = email;\r
35 } else {\r
36 data = data || {};\r
37 if (!data.email) {\r
38 data.email = email;\r
39 }\r
40 }\r
41 return new User(data);\r
42 }\r
43\r
44 function createStore(cfg, withData) {\r
45 cfg = cfg || {};\r
46 store = new Ext.data.Store(Ext.applyIf(cfg, {\r
47 asynchronousLoad: false,\r
48 model: User,\r
49 data: withData ? [edRaw, abeRaw, aaronRaw, tommyRaw] : null\r
50 }));\r
51 if (withData) {\r
52 assignRecs();\r
53 }\r
54 data = store.data;\r
55 }\r
56\r
57 function completeWithData(data) {\r
58 Ext.Ajax.mockComplete({\r
59 status: 200,\r
60 responseText: Ext.JSON.encode(data)\r
61 });\r
62 }\r
63\r
64 function complete(status, text) {\r
65 Ext.Ajax.mockComplete({\r
66 status: status,\r
67 responseText: ''\r
68 });\r
69 }\r
70\r
71 beforeEach(function() {\r
72 MockAjaxManager.addMethods();\r
73 User = Ext.define('spec.User', {\r
74 extend: 'Ext.data.Model',\r
75 idProperty: 'email',\r
76\r
77 fields: [\r
78 {name: 'name', type: 'string'},\r
79 {name: 'email', type: 'string'},\r
80 {name: 'evilness', type: 'int'},\r
81 {name: 'group', type: 'string'},\r
82 {name: 'old', type: 'boolean'},\r
83 {name: 'valid', type: 'string'},\r
84 {\r
85 name: 'age',\r
86 type: 'int',\r
87 sortType: customSort\r
88 }, {\r
89 name: 'validField',\r
90 validators: 'presence'\r
91 }\r
92 ]\r
93 });\r
94\r
95 edRaw = {name: 'Ed Spencer', email: 'ed@sencha.com', evilness: 100, group: 'code', old: false, age: 25, valid: 'yes'};\r
96 abeRaw = {name: 'Abe Elias', email: 'abe@sencha.com', evilness: 70, group: 'admin', old: false, age: 20, valid: 'yes'};\r
97 aaronRaw = {name: 'Aaron Conran', email: 'aaron@sencha.com', evilness: 5, group: 'admin', old: true, age: 26, valid: 'yes'};\r
98 tommyRaw = {name: 'Tommy Maintz', email: 'tommy@sencha.com', evilness: -15, group: 'code', old: true, age: 70, valid: 'yes'};\r
99 });\r
100\r
101 afterEach(function() {\r
102 MockAjaxManager.removeMethods();\r
103 Ext.undefine('spec.User');\r
104 Ext.data.Model.schema.clear();\r
105 store = spy = User = data = Ext.destroy(store);\r
106 edRaw = edRec = abeRaw = abeRec = aaronRaw = aaronRec = tommyRaw = tommyRec = null;\r
107 });\r
108\r
109 describe("initializing", function() {\r
110 describe("store manager", function() {\r
111 it("should register if a storeId is passed", function() {\r
112 createStore({\r
113 storeId: 'foo'\r
114 });\r
115 expect(Ext.data.StoreManager.get('foo')).toBe(store);\r
116 });\r
117 });\r
118\r
119 describe("proxy", function() {\r
120 describe("configured on the store", function() {\r
121 it("should create from a string", function() {\r
122 createStore({\r
123 proxy: 'jsonp'\r
124 });\r
125 expect(store.getProxy() instanceof Ext.data.proxy.JsonP).toBe(true);\r
126 });\r
127 \r
128 it("should create from a config", function() {\r
129 createStore({\r
130 proxy: {\r
131 type: 'ajax',\r
132 url: 'foo'\r
133 }\r
134 });\r
135 var proxy = store.getProxy();\r
136 expect(proxy instanceof Ext.data.proxy.Ajax);\r
137 expect(proxy.getUrl()).toBe('foo');\r
138 });\r
139 \r
140 it("should accept an instance", function() {\r
141 var proxy = new Ext.data.proxy.Memory();\r
142 createStore({\r
143 proxy: proxy\r
144 });\r
145 expect(store.getProxy()).toBe(proxy);\r
146 });\r
147 });\r
148 \r
149 describe("configured on the model", function() {\r
150 it("should use the proxy from the model", function() {\r
151 Ext.define('spec.ProxyWithModel', {\r
152 extend: 'Ext.data.Model',\r
153 fields: [],\r
154 \r
155 proxy: {\r
156 type: 'ajax'\r
157 }\r
158 });\r
159 createStore({\r
160 model: 'spec.ProxyWithModel'\r
161 });\r
162 expect(store.getProxy()).toBe(spec.ProxyWithModel.getProxy());\r
163 Ext.undefine('spec.ProxyWithModel');\r
164 });\r
165 });\r
166 \r
167 describe("configured on both", function() {\r
168 it("should favour the store proxy", function() {\r
169 Ext.define('spec.ProxyWithModel', {\r
170 extend: 'Ext.data.Model',\r
171 fields: [],\r
172 \r
173 proxy: {\r
174 type: 'ajax'\r
175 }\r
176 });\r
177 var proxy = new Ext.data.proxy.Ajax();\r
178 createStore({\r
179 model: 'spec.ProxyWithModel',\r
180 proxy: proxy\r
181 });\r
182 expect(store.getProxy()).toBe(proxy);\r
183 Ext.undefine('spec.ProxyWithModel');\r
184 });\r
185 });\r
186 \r
187 describe("memory with data", function() {\r
188 it("should load the data instantly", function() {\r
189 createStore({\r
190 proxy: {\r
191 type: 'memory'\r
192 },\r
193 data: [edRaw, abeRaw]\r
194 });\r
195 expect(store.getCount()).toBe(2);\r
196 });\r
197 });\r
198\r
199 describe("using an implicit model", function() {\r
200 it("should use the model's memory proxy when no proxy is defined on the store", function() {\r
201 store = new Ext.data.Store({\r
202 fields: ['id', 'height', 'width']\r
203 });\r
204 expect(store.getProxy().isMemoryProxy).toBe(true);\r
205 expect(store.getProxy()).toBe(store.getModel().getProxy());\r
206 });\r
207\r
208 it("should set the store's proxy on the model", function() {\r
209 store = new Ext.data.Store({\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().isAjaxProxy).toBe(true);\r
217 expect(store.getProxy().url).toBe('foo');\r
218 expect(store.getProxy()).toBe(store.getModel().getProxy());\r
219 });\r
220\r
221 it("should have the model set on the proxy & the reader", function() {\r
222 store = new Ext.data.Store({\r
223 fields: ['id', 'height', 'width'],\r
224 proxy: {\r
225 type: 'ajax',\r
226 url: 'foo'\r
227 }\r
228 });\r
229 expect(store.getProxy().getModel()).toBe(store.getModel());\r
230 expect(store.getProxy().getReader().getModel()).toBe(store.getModel());\r
231 });\r
232\r
233 it("should extend Ext.data.Model", function() {\r
234 store = new Ext.data.Store({\r
235 fields: ['id', 'height', 'width']\r
236 });\r
237 expect(store.getModel().superclass.self).toBe(Ext.data.Model);\r
238 });\r
239 });\r
240 });\r
241 \r
242 describe("autoLoad", function() {\r
243 it("should not auto load by default", function() {\r
244 spyOn(Ext.data.Store.prototype, 'flushLoad').andReturn();\r
245 createStore({\r
246 asynchronousLoad: true\r
247 });\r
248 waits(50);\r
249 runs(function() {\r
250 expect(store.flushLoad).not.toHaveBeenCalled();\r
251 });\r
252 });\r
253 \r
254 describe("autoLoad: true", function() {\r
255 it("should load the store", function() {\r
256 spyOn(Ext.data.Store.prototype, 'flushLoad').andCallThrough();\r
257 createStore({\r
258 asynchronousLoad: true,\r
259 autoLoad: true\r
260 });\r
261 waitsFor(function() {\r
262 return store.flushLoad.callCount > 0;\r
263 }, 'Load never called');\r
264 runs(function() {\r
265 expect(store.flushLoad.callCount).toBe(1);\r
266 });\r
267 });\r
268 \r
269 it("should pass the options if autoLoad is an object", function() {\r
270 var p = {\r
271 foo: 'bar'\r
272 },\r
273 o = {\r
274 params: p\r
275 },\r
276 loadParams;\r
277\r
278 createStore({\r
279 autoLoad: o,\r
280 asynchronousLoad: false,\r
281 listeners: {\r
282 beforeload: function(s, operation) {\r
283 loadParams = operation.getParams();\r
284 }\r
285 }\r
286 });\r
287\r
288 // Capture parameter from load options to verify\r
289 waitsFor(function() {\r
290 return loadParams.foo === 'bar';\r
291 });\r
292 });\r
293 });\r
294 });\r
295 \r
296 describe("fields", function() {\r
297 it("should create a model with the configured fields", function() {\r
298 createStore({\r
299 fields: ['id', 'height', 'width']\r
300 });\r
301 var Model = store.getModel(),\r
302 fields = Model.getFields();\r
303 \r
304 expect(Model.prototype.isModel).toBe(true);\r
305 expect(fields[0].getName()).toBe('id');\r
306 expect(fields[1].getName()).toBe('height');\r
307 expect(fields[2].getName()).toBe('width');\r
308 });\r
309 \r
310 it("should not be created with a class name", function() {\r
311 createStore({\r
312 fields: ['id', 'height', 'width']\r
313 });\r
314 var Model = store.getModel();\r
315 expect(Model.$className).toBe(null);\r
316 });\r
317 });\r
318\r
319 describe("data", function() {\r
320 describe("with no proxy", function() {\r
321 it("should add any inline data", function() {\r
322 createStore({\r
323 data: [edRaw, abeRaw]\r
324 });\r
325 expect(store.first().id).toBe('ed@sencha.com');\r
326 expect(store.last().id).toBe('abe@sencha.com');\r
327 });\r
328\r
329 it("should not fire any events", function() {\r
330 var spy = jasmine.createSpy();\r
331 createStore({\r
332 listeners: {\r
333 clear: spy,\r
334 add: spy,\r
335 load: spy,\r
336 datachanged: spy,\r
337 refresh: spy\r
338 },\r
339 data: [edRaw, abeRaw, tommyRaw]\r
340 });\r
341 expect(spy).not.toHaveBeenCalled();\r
342 });\r
343 });\r
344\r
345 describe("with a proxy", function() {\r
346 describe("with a memory proxy", function() {\r
347 it("should load the data and call proxy.read", function() {\r
348 var proxy = new Ext.data.proxy.Memory();\r
349 spyOn(proxy, 'read').andCallThrough();\r
350 createStore({\r
351 proxy: proxy,\r
352 data: [abeRaw, tommyRaw]\r
353 });\r
354 expect(store.first().id).toBe('abe@sencha.com');\r
355 expect(store.last().id).toBe('tommy@sencha.com');\r
356 expect(proxy.read.callCount).toBe(1);\r
357 });\r
358\r
359 it("should not fire any events", function() {\r
360 var spy = jasmine.createSpy();\r
361 var proxy = new Ext.data.proxy.Memory();\r
362 createStore({\r
363 proxy: proxy,\r
364 data: [abeRaw, tommyRaw],\r
365 listeners: {\r
366 clear: spy,\r
367 add: spy,\r
368 load: spy,\r
369 datachanged: spy,\r
370 refresh: spy\r
371 }\r
372 });\r
373 expect(spy).not.toHaveBeenCalled();\r
374 });\r
375 });\r
376\r
377 describe("with a server proxy", function() {\r
378 it("should load the data and not call proxy.read", function() {\r
379 var proxy = new Ext.data.proxy.Ajax();\r
380 spyOn(proxy, 'read').andCallThrough();\r
381 createStore({\r
382 proxy: proxy,\r
383 data: [aaronRaw, edRaw]\r
384 });\r
385 expect(store.first().id).toBe('aaron@sencha.com');\r
386 expect(store.last().id).toBe('ed@sencha.com');\r
387 expect(proxy.read).not.toHaveBeenCalled();\r
388 });\r
389\r
390 it("should not fire any events", function() {\r
391 var spy = jasmine.createSpy();\r
392 var proxy = new Ext.data.proxy.Ajax();\r
393 createStore({\r
394 proxy: proxy,\r
395 data: [aaronRaw, edRaw],\r
396 listeners: {\r
397 clear: spy,\r
398 add: spy,\r
399 load: spy,\r
400 datachanged: spy,\r
401 refresh: spy\r
402 }\r
403 });\r
404 expect(spy).not.toHaveBeenCalled();\r
405 });\r
406 });\r
407 });\r
408 });\r
409\r
410 describe('buffered stores', function () {\r
411 it('should create a BufferedStore if given buffered:true', function () {\r
412 createStore({\r
413 buffered: true\r
414 });\r
415\r
416 expect(store.isBufferedStore).toBe(true);\r
417 expect(store instanceof Ext.data.BufferedStore).toBe(true);\r
418 });\r
419\r
420 it('should create a BufferedStore if given type:buffered', function () {\r
421 // Silence console warning about store created with no model\r
422 spyOn(Ext.log, 'warn');\r
423 \r
424 store = Ext.Factory.store({\r
425 type: 'buffered'\r
426 });\r
427\r
428 expect(store.isBufferedStore).toBe(true);\r
429 expect(store instanceof Ext.data.BufferedStore).toBe(true);\r
430 });\r
431 });\r
432 });\r
433 \r
434 describe("getting records", function() {\r
435 beforeEach(function() {\r
436 createStore();\r
437 addStoreData();\r
438 });\r
439 \r
440 describe("first", function() {\r
441 it("should return the first record", function() {\r
442 expect(store.first()).toBe(edRec);\r
443 });\r
444\r
445 it("should return the record if there is only 1", function() {\r
446 store.remove([edRec, abeRec, tommyRec]);\r
447 expect(store.first()).toBe(aaronRec);\r
448 });\r
449 \r
450 it("should return null with an empty store", function() {\r
451 store.removeAll();\r
452 expect(store.first()).toBeNull();\r
453 });\r
454\r
455 it("should be affected by filters", function() {\r
456 store.getFilters().add({\r
457 property: 'group',\r
458 value: 'admin'\r
459 });\r
460 expect(store.first()).toBe(abeRec);\r
461 });\r
462 });\r
463 \r
464 describe("last", function() {\r
465 it("should return the last record", function() {\r
466 expect(store.last()).toBe(tommyRec);\r
467 });\r
468\r
469 it("should return the record if there is only 1", function() {\r
470 store.remove([edRec, abeRec, tommyRec]);\r
471 expect(store.last()).toBe(aaronRec);\r
472 });\r
473 \r
474 it("should return null with an empty store", function() {\r
475 store.removeAll();\r
476 expect(store.last()).toBeNull();\r
477 });\r
478\r
479 it("should be affected by filters", function() {\r
480 store.getFilters().add({\r
481 property: 'group',\r
482 value: 'admin'\r
483 });\r
484 expect(store.last()).toBe(aaronRec);\r
485 });\r
486 });\r
487 \r
488 describe("getAt", function() {\r
489 it("should return the record at the specified index", function() {\r
490 expect(store.getAt(1)).toBe(abeRec);\r
491 });\r
492 \r
493 it("should return null when the index is outside the store bounds", function() {\r
494 expect(store.getAt(100)).toBe(null);\r
495 });\r
496 \r
497 it("should return null when the store is empty", function() {\r
498 store.removeAll();\r
499 expect(store.getAt(0)).toBe(null);\r
500 });\r
501 });\r
502 \r
503 describe("getById", function() {\r
504 it("should return the record with the matching id", function() {\r
505 expect(store.getById('tommy@sencha.com')).toBe(tommyRec);\r
506 });\r
507 \r
508 it("should return null if a matching id is not found", function() {\r
509 expect(store.getById('foo@sencha.com')).toBe(null);\r
510 });\r
511 \r
512 it("should return null when the store is empty", function() {\r
513 store.removeAll();\r
514 expect(store.getById('ed@sencha.com')).toBe(null);\r
515 });\r
516 \r
517 it("should ignore filters", function() {\r
518 store.filter('email', 'ed@sencha.com');\r
519 expect(store.getById('aaron@sencha.com')).toBe(aaronRec);\r
520 });\r
521 });\r
522 \r
523 describe("getByInternalId", function() {\r
524 it("should return the record with the matching id", function() {\r
525 expect(store.getByInternalId(tommyRec.internalId)).toBe(tommyRec);\r
526 });\r
527 \r
528 it("should return null if a matching id is not found", function() {\r
529 expect(store.getByInternalId('foo@sencha.com')).toBe(null);\r
530 });\r
531 \r
532 it("should return null when the store is empty", function() {\r
533 store.removeAll();\r
534 expect(store.getByInternalId('ed@sencha.com')).toBe(null);\r
535 });\r
536 \r
537 it("should ignore filters", function() {\r
538 store.filter('email', 'ed@sencha.com');\r
539 expect(store.getByInternalId(aaronRec.internalId)).toBe(aaronRec);\r
540 });\r
541\r
542 it("should work correctly if not called before filtering", function() {\r
543 store.filter('email', 'ed@sencha.com');\r
544 expect(store.getByInternalId(aaronRec.internalId)).toBe(aaronRec);\r
545 });\r
546\r
547 it("should work correctly if called before & after filtering", function() {\r
548 expect(store.getByInternalId(aaronRec.internalId)).toBe(aaronRec);\r
549 store.filter('email', 'ed@sencha.com');\r
550 expect(store.getByInternalId(aaronRec.internalId)).toBe(aaronRec);\r
551 });\r
552 });\r
553 \r
554 describe("getRange", function() {\r
555 it("should default to the full store range", function() {\r
556 expect(store.getRange()).toEqual([edRec, abeRec, aaronRec, tommyRec]);\r
557 });\r
558 \r
559 it("should return from the start index", function() {\r
560 expect(store.getRange(2)).toEqual([aaronRec, tommyRec]);\r
561 });\r
562 \r
563 it("should use the end index, and include it", function() {\r
564 expect(store.getRange(0, 2)).toEqual([edRec, abeRec, aaronRec]);\r
565 });\r
566 \r
567 it("should ignore an end index greater than the store range", function() {\r
568 expect(store.getRange(1, 100)).toEqual([abeRec, aaronRec, tommyRec]);\r
569 });\r
570 });\r
571\r
572 describe("query", function() {\r
573 var coders,\r
574 slackers;\r
575\r
576 it("should return records with group: 'coder'", function() {\r
577 coders = store.query('group', 'code');\r
578 expect(coders.length).toBe(2);\r
579 expect(coders.contains(edRec)).toBe(true);\r
580 expect(coders.contains(tommyRec)).toBe(true);\r
581 expect(coders.contains(aaronRec)).toBe(false);\r
582 expect(coders.contains(abeRec)).toBe(false);\r
583 });\r
584 \r
585 it("should return null if a matching id is not found", function() {\r
586 slackers = store.query('group', 'slackers');\r
587 expect(slackers.length).toBe(0);\r
588 });\r
589 \r
590 it("should return null when the store is empty", function() {\r
591 store.removeAll();\r
592 coders = store.query('group', 'code');\r
593 expect(coders.length).toBe(0);\r
594 });\r
595 \r
596 it("should ignore filters", function() {\r
597 store.filter('email', 'ed@sencha.com');\r
598 expect(store.getCount()).toBe(1);\r
599 coders = store.query('group', 'code');\r
600 expect(coders.length).toBe(2);\r
601 expect(coders.contains(edRec)).toBe(true);\r
602 expect(coders.contains(tommyRec)).toBe(true);\r
603 expect(coders.contains(aaronRec)).toBe(false);\r
604 expect(coders.contains(abeRec)).toBe(false);\r
605 });\r
606 }); \r
607 });\r
608 \r
609 describe("finding", function() {\r
610 beforeEach(function() {\r
611 createStore();\r
612 addStoreData();\r
613 });\r
614 \r
615 describe("find", function() {\r
616 // Only minimal tests here, since it just calls through to the collection\r
617 it("should find by the field", function() {\r
618 expect(store.find('email', 'tommy@sencha.com')).toBe(3);\r
619 });\r
620 \r
621 it("should find the first matching index", function() {\r
622 expect(store.find('group', 'admin')).toBe(1);\r
623 });\r
624\r
625 it("should return -1 if value is empty", function() {\r
626 expect(store.find('id', null)).toBe(-1);\r
627 expect(store.find('id', '')).toBe(-1);\r
628 expect(store.find('id', undefined)).toBe(-1);\r
629 expect(store.find('id', [])).toBe(-1);\r
630 expect(store.find('id', 'foo')).toBe(-1);\r
631 });\r
632\r
633 it("should match the start of strings as a default", function() {\r
634 expect(store.find('email', 'to')).toBe(3);\r
635 });\r
636 });\r
637 \r
638 // Only minimal tests here, since it just calls through to the collection\r
639 describe("findRecord", function() {\r
640 it("should return the record instance", function() {\r
641 expect(store.findRecord('name', 'Ed Spencer')).toBe(edRec);\r
642 });\r
643 \r
644 it("should find the first matching record", function() {\r
645 expect(store.findRecord('group', 'code')).toBe(edRec);\r
646 });\r
647\r
648 it("should return null when not found", function() {\r
649 expect(store.findRecord('name', 'Derp')).toBeNull();\r
650 });\r
651 });\r
652\r
653 // Only minimal tests here, since it just calls through to the collection\r
654 describe("finding exact", function() {\r
655 it("should find the first exact matching record", function() {\r
656 expect(store.findExact('name', 'Aaron Conran')).toBe(2);\r
657 });\r
658 \r
659 it("should return -1 if there is no match", function() {\r
660 expect(store.findExact('name', 'Bed Spencer')).toBe(-1);\r
661 });\r
662\r
663 it("should honor the start index", function() {\r
664 expect(store.findExact('group', 'admin', 1)).toBe(1);\r
665 });\r
666 \r
667 it("should not do any type coercion", function() {\r
668 expect(store.findExact('evilness', '70')).toBe(-1);\r
669 });\r
670 });\r
671\r
672 // Only minimal tests here, since it just calls through to the collection\r
673 describe("findBy", function() {\r
674 it("should find by the matching FN", function() {\r
675 var index = store.findBy(function(rec) {\r
676 return rec.get('email') === 'abe@sencha.com';\r
677 });\r
678 expect(index).toBe(1);\r
679 });\r
680 });\r
681 \r
682 // Only minimal tests here, since it just calls through to the collection\r
683 describe("collect", function() {\r
684 it("should collect values in order", function() {\r
685 expect(store.collect('age')).toEqual([25, 20, 26, 70]);\r
686 });\r
687 \r
688 it("should ignore filtered out values", function() {\r
689 store.filter('group', 'code');\r
690 expect(store.collect('age')).toEqual([25, 70]);\r
691 });\r
692 \r
693 it("should bypass the filter if we pass the bypass param", function() {\r
694 store.filter('group', 'code');\r
695 expect(store.collect('age', true, true)).toEqual([25, 20, 26, 70]);\r
696 });\r
697 });\r
698 });\r
699 \r
700 describe("iterating", function() {\r
701 var spy;\r
702 beforeEach(function() {\r
703 createStore();\r
704 addStoreData();\r
705 spy = jasmine.createSpy();\r
706 });\r
707 \r
708 describe("each", function() {\r
709 it("should iterate over each record", function() {\r
710 store.each(spy);\r
711 expect(spy.callCount).toBe(4);\r
712 });\r
713 \r
714 it("should pass the record, index & total length", function() {\r
715 store.each(spy);\r
716 \r
717 var args = spy.calls[0].args;\r
718 expect(args[0]).toBe(edRec);\r
719 expect(args[1]).toBe(0);\r
720 expect(args[2]).toBe(4);\r
721 \r
722 args = spy.calls[1].args;\r
723 expect(args[0]).toBe(abeRec);\r
724 expect(args[1]).toBe(1);\r
725 expect(args[2]).toBe(4);\r
726 \r
727 args = spy.calls[2].args;\r
728 expect(args[0]).toBe(aaronRec);\r
729 expect(args[1]).toBe(2);\r
730 expect(args[2]).toBe(4);\r
731 \r
732 args = spy.calls[3].args;\r
733 expect(args[0]).toBe(tommyRec);\r
734 expect(args[1]).toBe(3);\r
735 expect(args[2]).toBe(4);\r
736 });\r
737 \r
738 it("should stop iterating if false is returned", function() {\r
739 var count = 0;\r
740 store.each(function(rec, idx) {\r
741 if (idx > 1) {\r
742 return false;\r
743 }\r
744 ++count;\r
745 });\r
746 expect(count).toBe(2);\r
747 });\r
748 \r
749 it("should default the scope to the record", function() {\r
750 store.each(spy);\r
751 expect(spy.mostRecentCall.object).toBe(store.last());\r
752 });\r
753 \r
754 it("should use the passed scope", function() {\r
755 store.each(spy, fakeScope);\r
756 expect(spy.mostRecentCall.object).toBe(fakeScope);\r
757 });\r
758 });\r
759 });\r
760 \r
761 describe("index", function() {\r
762 \r
763 beforeEach(function() {\r
764 createStore();\r
765 addStoreData();\r
766 });\r
767 \r
768 describe("indexOf", function() {\r
769 it("should return the index of a record that exists in the store", function() {\r
770 expect(store.indexOf(abeRec)).toBe(1);\r
771 }); \r
772 \r
773 it("should return -1 when the record does not exist in the store", function() {\r
774 expect(store.indexOf(makeUser('foo@sencha.com'))).toBe(-1);\r
775 });\r
776 \r
777 it("should return -1 when the store is empty", function() {\r
778 store.removeAll();\r
779 expect(store.indexOf(edRec)).toBe(-1);\r
780 });\r
781 \r
782 it("should return -1 when the passed record is null", function() {\r
783 expect(store.indexOf(null)).toBe(-1);\r
784 });\r
785 });\r
786 \r
787 describe("indexOfId", function() {\r
788 it("should return the record with matching index", function() {\r
789 expect(store.indexOfId('aaron@sencha.com')).toBe(2);\r
790 });\r
791 \r
792 it("should return -1 when the id does not exist in the store", function() {\r
793 expect(store.indexOfId('foo@sencha.com')).toBe(-1);\r
794 });\r
795 \r
796 it("should return -1 when the store is empty", function() {\r
797 store.removeAll();\r
798 expect(store.indexOfId('ed@sencha.com')).toBe(-1);\r
799 });\r
800 });\r
801 });\r
802 \r
803 describe("counting", function() {\r
804 describe("getCount", function() {\r
805 beforeEach(function() {\r
806 createStore();\r
807 });\r
808 \r
809 it("should return 0 when the store is empty", function() {\r
810 expect(store.getCount()).toBe(0);\r
811 });\r
812 \r
813 it("should return the number of records currently in the store", function() {\r
814 addStoreData();\r
815 expect(store.getCount()).toBe(4);\r
816 });\r
817 });\r
818 \r
819 describe("getTotalCount", function() {\r
820 it("should default to 0", function() {\r
821 createStore();\r
822 expect(store.getTotalCount()).toBe(0);\r
823 });\r
824 \r
825 it("should set a value returned from a proxy read", function() {\r
826 createStore({\r
827 proxy: {\r
828 type: 'memory',\r
829 data: {\r
830 total: 1234\r
831 },\r
832 reader: 'json'\r
833 }\r
834 });\r
835 store.load();\r
836 expect(store.getTotalCount()).toBe(1234);\r
837 });\r
838 });\r
839 });\r
840 \r
841 describe("adding records", function() {\r
842 describe("add", function() {\r
843 beforeEach(function() {\r
844 createStore();\r
845 });\r
846 \r
847 describe("position", function() { \r
848 it("should add to the end of the store", function() {\r
849 addStoreData();\r
850 var rec = makeUser('foo@sencha.com');\r
851 store.add(rec);\r
852 expect(store.getAt(4)).toBe(rec);\r
853 });\r
854 });\r
855 \r
856 describe("arg values", function() {\r
857 it("should add a model instance", function() {\r
858 var rec = makeUser('foo@sencha.com');\r
859 store.add(rec);\r
860 expect(store.first()).toBe(rec);\r
861 });\r
862 \r
863 it("should create a model from an object config", function() {\r
864 store.add({\r
865 email: 'foo@sencha.com',\r
866 name: 'Foo'\r
867 });\r
868 var rec = store.first();\r
869 expect(rec.isModel).toBe(true);\r
870 expect(rec.get('name')).toBe('Foo');\r
871 });\r
872 });\r
873 \r
874 describe("adding multiple", function() {\r
875 it("should add an array of records", function() {\r
876 store.add([{\r
877 email: 'personA@sencha.com',\r
878 name: 'Person A'\r
879 }, {\r
880 email: 'personB@sencha.com',\r
881 name: 'Person B'\r
882 }]);\r
883 expect(store.first().get('name')).toBe('Person A');\r
884 expect(store.last().get('name')).toBe('Person B');\r
885 });\r
886\r
887 it("should add multiple arguments", function() {\r
888 store.add({\r
889 email: 'personA@sencha.com',\r
890 name: 'Person A'\r
891 }, {\r
892 email: 'personB@sencha.com',\r
893 name: 'Person B'\r
894 });\r
895 expect(store.first().get('name')).toBe('Person A');\r
896 expect(store.last().get('name')).toBe('Person B');\r
897 });\r
898 });\r
899 \r
900 describe("return value", function() {\r
901 it("should return an array when adding a single item", function() {\r
902 var rec = makeUser('foo@sencha.com');\r
903 expect(store.add(rec)).toEqual([rec]);\r
904 });\r
905 \r
906 it("should return an array when adding an array, should not mutate the array", function() {\r
907 var rec = makeUser('foo@sencha.com'), \r
908 arr = [rec], \r
909 result = store.add(arr);\r
910 \r
911 expect(result).toEqual([rec]);\r
912 expect(result).not.toBe(arr);\r
913 });\r
914 \r
915 it("should return an array when adding multiple args", function() {\r
916 var rec1 = makeUser('user1@sencha.com'),\r
917 rec2 = makeUser('user2@sencha.com');\r
918 \r
919 expect(store.add(rec1, rec2)).toEqual([rec1, rec2]);\r
920 });\r
921 \r
922 it("should return an empty array if nothing was passed", function() {\r
923 expect(store.add()).toEqual([]);\r
924 });\r
925 \r
926 it("should return an empty array if an empty array was passed", function() {\r
927 expect(store.add([])).toEqual([]);\r
928 });\r
929 });\r
930 \r
931 describe("events", function() {\r
932 var spy, rec1, rec2;\r
933 \r
934 beforeEach(function() {\r
935 addStoreData();\r
936 spy = jasmine.createSpy();\r
937 rec1 = makeUser('user1@sencha.com');\r
938 rec2 = makeUser('user2@sencha.com');\r
939 });\r
940 \r
941 describe("a single item", function() {\r
942 it("should fire the add event, passing the store, the records & the added index", function() {\r
943 store.on('add', spy);\r
944 store.add(rec1);\r
945 var args = spy.mostRecentCall.args;\r
946 expect(spy.callCount).toBe(1);\r
947 expect(args[0]).toBe(store);\r
948 expect(args[1]).toEqual([rec1]);\r
949 expect(args[2]).toBe(4);\r
950 });\r
951 \r
952 \r
953 it("should fire the datachanged event", function() {\r
954 store.on('datachanged', spy);\r
955 store.add(rec1, rec2);\r
956 var args = spy.mostRecentCall.args;\r
957 expect(spy.callCount).toBe(1);\r
958 expect(args[0]).toBe(store);\r
959 });\r
960 });\r
961 \r
962 describe("multiple items", function() {\r
963 describe("contiguous range", function() {\r
964 it("should fire the add event, passing the store, the records & the added index", function() {\r
965 store.on('add', spy);\r
966 store.add(rec1, rec2);\r
967 var args = spy.mostRecentCall.args;\r
968 expect(spy.callCount).toBe(1);\r
969 expect(args[0]).toBe(store);\r
970 expect(args[1]).toEqual([rec1, rec2]);\r
971 expect(args[2]).toBe(4);\r
972 });\r
973\r
974\r
975 it("should fire the datachanged event", function() {\r
976 store.on('datachanged', spy);\r
977 store.add(rec1, rec2);\r
978 var args = spy.mostRecentCall.args;\r
979 expect(spy.callCount).toBe(1);\r
980 expect(args[0]).toBe(store);\r
981 });\r
982 });\r
983 \r
984 // This is only possible if the store is sorted, since adding multiple items may\r
985 // require them to be split into different "add" groups\r
986 describe("discontiguous range", function() {\r
987 var recs;\r
988 beforeEach(function() {\r
989 store.removeAll();\r
990 store.sort('email');\r
991 store.add(\r
992 makeUser('e@sencha.com'),\r
993 makeUser('j@sencha.com'),\r
994 makeUser('o@sencha.com'),\r
995 makeUser('t@sencha.com')\r
996 );\r
997 \r
998 recs = [\r
999 makeUser('a@sencha.com'),\r
1000 makeUser('b@sencha.com'),\r
1001 makeUser('f@sencha.com'),\r
1002 makeUser('g@sencha.com'),\r
1003 makeUser('h@sencha.com'),\r
1004 makeUser('l@sencha.com'),\r
1005 makeUser('p@sencha.com'),\r
1006 makeUser('q@sencha.com'),\r
1007 makeUser('r@sencha.com'),\r
1008 makeUser('s@sencha.com')\r
1009 ];\r
1010 });\r
1011 \r
1012 it("should fire the add event, passing the store, the records & the added index for each chunk", function() {\r
1013 store.on('add', spy);\r
1014 store.add(\r
1015 recs[6],\r
1016 recs[1],\r
1017 recs[9],\r
1018 recs[4],\r
1019 recs[3],\r
1020 recs[7],\r
1021 recs[5],\r
1022 recs[2],\r
1023 recs[8],\r
1024 recs[0]\r
1025 );\r
1026 \r
1027 expect(spy.callCount).toBe(4);\r
1028 \r
1029 var args = spy.calls[0].args;\r
1030 expect(args[0]).toBe(store);\r
1031 expect(args[1]).toEqual([recs[0], recs[1]]);\r
1032 expect(args[2]).toBe(0);\r
1033 \r
1034 args = spy.calls[1].args;\r
1035 expect(args[0]).toBe(store);\r
1036 expect(args[1]).toEqual([recs[2], recs[3], recs[4]]);\r
1037 expect(args[2]).toBe(3);\r
1038 \r
1039 args = spy.calls[2].args;\r
1040 expect(args[0]).toBe(store);\r
1041 expect(args[1]).toEqual([recs[5]]);\r
1042 expect(args[2]).toBe(7);\r
1043 \r
1044 args = spy.calls[3].args;\r
1045 expect(args[0]).toBe(store);\r
1046 expect(args[1]).toEqual([recs[6], recs[7], recs[8], recs[9]]);\r
1047 expect(args[2]).toBe(9);\r
1048 });\r
1049\r
1050\r
1051 it("should fire the datachanged event", function() {\r
1052 store.on('datachanged', spy);\r
1053 store.add(\r
1054 recs[6],\r
1055 recs[1],\r
1056 recs[9],\r
1057 recs[4],\r
1058 recs[3],\r
1059 recs[7],\r
1060 recs[5],\r
1061 recs[2],\r
1062 recs[8],\r
1063 recs[0]\r
1064 );\r
1065 var args = spy.mostRecentCall.args;\r
1066 expect(spy.callCount).toBe(1);\r
1067 expect(args[0]).toBe(store);\r
1068 });\r
1069 });\r
1070 \r
1071 });\r
1072 \r
1073 describe("invalid cases", function() {\r
1074 it("should not call the event when the record is null", function() {\r
1075 store.on({\r
1076 add: spy,\r
1077 datachanged: spy\r
1078 });\r
1079 store.add(null);\r
1080 expect(spy).not.toHaveBeenCalled();\r
1081 });\r
1082 \r
1083 it("should not call the event when the array is empty", function() {\r
1084 store.on({\r
1085 add: spy,\r
1086 datachanged: spy\r
1087 });\r
1088 store.add([]);\r
1089 expect(spy).not.toHaveBeenCalled();\r
1090 });\r
1091 });\r
1092 });\r
1093 });\r
1094 \r
1095 describe("insert", function() {\r
1096 beforeEach(function() {\r
1097 createStore();\r
1098 });\r
1099 \r
1100 describe("position", function() { \r
1101 it("should add at the specified position", function() {\r
1102 addStoreData();\r
1103 var rec = makeUser('foo@sencha.com');\r
1104 store.insert(2, rec);\r
1105 expect(store.getAt(2)).toBe(rec);\r
1106 });\r
1107 \r
1108 it("should add to the end if the index is larger than the bounds", function() {\r
1109 var rec = makeUser('foo@sencha.com');\r
1110 store.insert(100, rec);\r
1111 expect(store.first()).toBe(rec);\r
1112 });\r
1113 });\r
1114 \r
1115 describe("arg values", function() {\r
1116 it("should add a model instance", function() {\r
1117 var rec = makeUser('foo@sencha.com');\r
1118 store.insert(0, rec);\r
1119 expect(store.first()).toBe(rec);\r
1120 });\r
1121 \r
1122 it("should create a model from an object config", function() {\r
1123 store.insert(0, {\r
1124 email: 'foo@sencha.com',\r
1125 name: 'Foo'\r
1126 });\r
1127 var rec = store.first();\r
1128 expect(rec.isModel).toBe(true);\r
1129 expect(rec.get('name')).toBe('Foo');\r
1130 });\r
1131 });\r
1132 \r
1133 describe("adding multiple", function() {\r
1134 it("should add an array of records", function() {\r
1135 store.insert(0, [{\r
1136 email: 'personA@sencha.com',\r
1137 name: 'Person A'\r
1138 }, {\r
1139 email: 'personB@sencha.com',\r
1140 name: 'Person B'\r
1141 }]);\r
1142 expect(store.first().get('name')).toBe('Person A');\r
1143 expect(store.last().get('name')).toBe('Person B');\r
1144 });\r
1145 });\r
1146 \r
1147 describe("return value", function() {\r
1148 it("should return an array when adding a single item", function() {\r
1149 var rec = makeUser('foo@sencha.com');\r
1150 expect(store.insert(0, rec)).toEqual([rec]);\r
1151 });\r
1152 \r
1153 it("should return an array when adding an array, should not mutate the array", function() {\r
1154 var rec = makeUser('foo@sencha.com'), \r
1155 arr = [rec], \r
1156 result = store.insert(0, arr);\r
1157 \r
1158 expect(result).toEqual([rec]);\r
1159 expect(result).not.toBe(arr);\r
1160 });\r
1161 \r
1162 it("should return an empty array if nothing was passed", function() {\r
1163 expect(store.insert(0)).toEqual([]);\r
1164 });\r
1165 \r
1166 it("should return an empty array if an empty array was passed", function() {\r
1167 expect(store.insert(0, [])).toEqual([]);\r
1168 });\r
1169 });\r
1170\r
1171 describe("when filtered", function() {\r
1172 beforeEach(function() {\r
1173 addStoreData();\r
1174 });\r
1175\r
1176 it("should insert at the correct position", function() {\r
1177 store.filter('group', 'code');\r
1178 var rec = store.insert(0, {\r
1179 email: 'foo@sencha.com',\r
1180 group: 'code'\r
1181 })[0];\r
1182 expect(store.getAt(0)).toBe(rec);\r
1183 });\r
1184\r
1185 it("should retain the relative position when filters are cleared", function() {\r
1186 store.filter('group', 'code');\r
1187 var rec = store.insert(1, {\r
1188 email: 'foo@sencha.com',\r
1189 group: 'code'\r
1190 })[0];\r
1191 store.getFilters().removeAll();\r
1192 expect(store.indexOf(rec)).toBe(3);\r
1193 });\r
1194\r
1195 it("should have the correct position when inserted but filtered out", function() {\r
1196 store.filter('group', 'code');\r
1197 var rec = store.insert(0, {\r
1198 email: 'foo@sencha.com',\r
1199 group: 'admin'\r
1200 })[0];\r
1201 store.getFilters().removeAll();\r
1202 expect(store.indexOf(rec)).toBe(0);\r
1203 });\r
1204 });\r
1205 \r
1206 describe("events", function() {\r
1207 var spy, rec1, rec2;\r
1208 \r
1209 beforeEach(function() {\r
1210 addStoreData();\r
1211 spy = jasmine.createSpy();\r
1212 rec1 = makeUser('user1@sencha.com');\r
1213 rec2 = makeUser('user2@sencha.com');\r
1214 });\r
1215 \r
1216 it("should fire the add event, passing the store, the records & the added index", function() {\r
1217 store.on('add', spy);\r
1218 store.insert(0, [rec1, rec2]);\r
1219 var args = spy.mostRecentCall.args;\r
1220 expect(spy.callCount).toBe(1);\r
1221 expect(args[0]).toBe(store);\r
1222 expect(args[1]).toEqual([rec1, rec2]);\r
1223 expect(args[2]).toBe(0);\r
1224 });\r
1225 \r
1226 it("should correct the added index when it exceeds the bounds of the store", function() {\r
1227 store.on('add', spy);\r
1228 store.insert(50, [rec1, rec2]);\r
1229 var args = spy.mostRecentCall.args;\r
1230 expect(args[2]).toBe(4);\r
1231 });\r
1232 \r
1233 it("should fire the datachanged event", function() {\r
1234 store.on('datachanged', spy);\r
1235 store.insert(0, [rec1, rec2]);\r
1236 var args = spy.mostRecentCall.args;\r
1237 expect(spy.callCount).toBe(1);\r
1238 expect(args[0]).toBe(store);\r
1239 });\r
1240 \r
1241 describe("invalid cases", function() {\r
1242 it("should not call the event when the record is null", function() {\r
1243 store.on({\r
1244 add: spy,\r
1245 datachanged: spy\r
1246 });\r
1247 store.insert(0, null);\r
1248 expect(spy).not.toHaveBeenCalled();\r
1249 });\r
1250 \r
1251 it("should not call the event when the array is empty", function() {\r
1252 store.on({\r
1253 add: spy,\r
1254 datachanged: spy\r
1255 });\r
1256 store.insert(0, []);\r
1257 expect(spy).not.toHaveBeenCalled();\r
1258 });\r
1259 });\r
1260 });\r
1261 });\r
1262\r
1263 describe("moving records", function() {\r
1264 var spy, refreshMap;\r
1265\r
1266 beforeEach(function() {\r
1267 createStore();\r
1268 addStoreData();\r
1269 spy = jasmine.createSpy();\r
1270 });\r
1271\r
1272 afterEach(function() {\r
1273 refreshMap = spy = null;\r
1274 });\r
1275\r
1276 function mapify(map, records) {\r
1277 Ext.Array.forEach(records, function(record) {\r
1278 if (store.isMoving(record)) {\r
1279 map[record.id] = true;\r
1280 }\r
1281 });\r
1282 }\r
1283\r
1284 function setupRefreshListener() {\r
1285 refreshMap = {};\r
1286\r
1287 store.on('refresh', function(store) {\r
1288 refreshMap = Ext.clone(store.moveMap);\r
1289 });\r
1290 }\r
1291\r
1292 describe("via add", function() {\r
1293 describe("a single record", function() {\r
1294 it("should move the record to the end of the collection", function() {\r
1295 store.add(edRec);\r
1296 expect(store.indexOf(edRec)).toBe(3);\r
1297 });\r
1298\r
1299 it("should be moving during the add & remove event", function() {\r
1300 setupRefreshListener();\r
1301\r
1302 store.add(edRec);\r
1303 var expected = {\r
1304 'ed@sencha.com': 1\r
1305 };\r
1306 expect(refreshMap).toEqual(expected);\r
1307\r
1308 expect(store.isMoving([edRec])).toBe(0);\r
1309 expect(store.isMoving()).toBe(0);\r
1310 });\r
1311 });\r
1312\r
1313 describe("multiple records", function() {\r
1314 describe("existing records only", function() {\r
1315 describe("in a contiguous range", function() {\r
1316 it("should move the records to the end of the collection", function() {\r
1317 store.add([edRec, abeRec]);\r
1318 expect(store.indexOf(edRec)).toBe(2);\r
1319 expect(store.indexOf(abeRec)).toBe(3);\r
1320 });\r
1321\r
1322 it("should be moving during the add & remove event", function() {\r
1323 setupRefreshListener();\r
1324\r
1325 store.add([edRec, abeRec]);\r
1326 var expected = {\r
1327 'ed@sencha.com': 1,\r
1328 'abe@sencha.com': 1\r
1329 };\r
1330\r
1331 expect(refreshMap).toEqual(expected);\r
1332\r
1333 expect(store.isMoving([edRec, abeRec])).toBe(0);\r
1334 expect(store.isMoving()).toBe(0);\r
1335 });\r
1336 });\r
1337\r
1338 describe("in a discontiguous range", function() {\r
1339 var fooRec, barRec, bazRec;\r
1340\r
1341 beforeEach(function() {\r
1342 fooRec = makeUser('foo@sencha.com');\r
1343 barRec = makeUser('bar@sencha.com');\r
1344 bazRec = makeUser('baz@sencha.com');\r
1345 store.add([fooRec, barRec, bazRec]);\r
1346 });\r
1347\r
1348 afterEach(function() {\r
1349 fooRec = barRec = bazRec = null;\r
1350 });\r
1351\r
1352 it("should move the records to the end of the collection", function() {\r
1353 store.add([abeRec, tommyRec, barRec]);\r
1354 expect(store.indexOf(abeRec)).toBe(4);\r
1355 expect(store.indexOf(tommyRec)).toBe(5);\r
1356 expect(store.indexOf(barRec)).toBe(6);\r
1357 });\r
1358\r
1359 it("should be moving during the add & remove event", function() {\r
1360 setupRefreshListener();\r
1361\r
1362 store.add([abeRec, tommyRec, barRec]);\r
1363 var expected = {\r
1364 'abe@sencha.com': 1,\r
1365 'tommy@sencha.com': 1,\r
1366 'bar@sencha.com': 1\r
1367 };\r
1368\r
1369 expect(refreshMap).toEqual(expected);\r
1370\r
1371 expect(store.isMoving([abeRec, tommyRec, barRec])).toBe(0);\r
1372 expect(store.isMoving()).toBe(0);\r
1373 });\r
1374 });\r
1375 });\r
1376\r
1377 describe("mixture of new/existing records", function() {\r
1378 var fooRec, barRec, bazRec;\r
1379\r
1380 beforeEach(function() {\r
1381 fooRec = makeUser('foo@sencha.com');\r
1382 barRec = makeUser('bar@sencha.com');\r
1383 bazRec = makeUser('baz@sencha.com');\r
1384 });\r
1385\r
1386 afterEach(function() {\r
1387 fooRec = barRec = bazRec = null;\r
1388 });\r
1389\r
1390 it("should add the records to the end", function() {\r
1391 store.add([fooRec, edRec]);\r
1392 expect(store.indexOf(fooRec)).toBe(3);\r
1393 expect(store.indexOf(edRec)).toBe(4);\r
1394 });\r
1395\r
1396 it("should only mark existing records as moving", function() {\r
1397 setupRefreshListener();\r
1398\r
1399 store.add([fooRec, edRec]);\r
1400 var expected = {\r
1401 'ed@sencha.com': 1\r
1402 };\r
1403 expect(refreshMap).toEqual(expected);\r
1404\r
1405 expect(store.isMoving([fooRec, edRec])).toBe(0);\r
1406 expect(store.isMoving()).toBe(0);\r
1407 });\r
1408 });\r
1409 });\r
1410 });\r
1411\r
1412 describe("via insert", function() {\r
1413 describe("a single record", function() {\r
1414 it("should move the record to the specified position", function() {\r
1415 store.insert(0, tommyRec);\r
1416 expect(store.indexOf(tommyRec)).toBe(0);\r
1417 });\r
1418\r
1419 it("should be moving during the add & remove event", function() {\r
1420 setupRefreshListener();\r
1421\r
1422 store.insert(0, tommyRec);\r
1423 var expected = {\r
1424 'tommy@sencha.com': 1\r
1425 };\r
1426 expect(refreshMap).toEqual(expected);\r
1427\r
1428 expect(store.isMoving(tommyRec)).toBe(0);\r
1429 expect(store.isMoving()).toBe(0);\r
1430 });\r
1431 });\r
1432\r
1433 describe("multiple records", function() {\r
1434 describe("existing records only", function() {\r
1435 describe("in a contiguous range", function() {\r
1436 it("should move the records to the specified position", function() {\r
1437 store.insert(0, [aaronRec, tommyRec]);\r
1438 expect(store.indexOf(aaronRec)).toBe(0);\r
1439 expect(store.indexOf(tommyRec)).toBe(1);\r
1440 });\r
1441\r
1442 it("should be moving during the add & remove event", function() {\r
1443 setupRefreshListener();\r
1444\r
1445 store.insert(0, [aaronRec, tommyRec]);\r
1446 var expected = {\r
1447 'aaron@sencha.com': 1,\r
1448 'tommy@sencha.com': 1\r
1449 };\r
1450\r
1451 expect(refreshMap).toEqual(expected);\r
1452\r
1453 expect(store.isMoving([aaronRec, tommyRec])).toBe(0);\r
1454 expect(store.isMoving()).toBe(0);\r
1455 });\r
1456 });\r
1457\r
1458 describe("in a discontiguous range", function() {\r
1459 var fooRec, barRec, bazRec;\r
1460\r
1461 beforeEach(function() {\r
1462 fooRec = makeUser('foo@sencha.com');\r
1463 barRec = makeUser('bar@sencha.com');\r
1464 bazRec = makeUser('baz@sencha.com');\r
1465 store.add([fooRec, barRec, bazRec]);\r
1466 });\r
1467\r
1468 afterEach(function() {\r
1469 fooRec = barRec = bazRec = null;\r
1470 });\r
1471\r
1472 it("should move the records to the specified position", function() {\r
1473 store.insert(0, [abeRec, tommyRec, barRec]);\r
1474 expect(store.indexOf(abeRec)).toBe(0);\r
1475 expect(store.indexOf(tommyRec)).toBe(1);\r
1476 expect(store.indexOf(barRec)).toBe(2);\r
1477 });\r
1478\r
1479 it("should be moving during the add & remove event", function() {\r
1480 setupRefreshListener();\r
1481\r
1482 store.insert(0, [abeRec, tommyRec, barRec]);\r
1483 var expected = {\r
1484 'abe@sencha.com': 1,\r
1485 'tommy@sencha.com': 1,\r
1486 'bar@sencha.com': 1\r
1487 };\r
1488\r
1489 expect(refreshMap).toEqual(expected);\r
1490\r
1491 expect(store.isMoving([abeRec, tommyRec, barRec])).toBe(0);\r
1492 expect(store.isMoving()).toBe(0);\r
1493 });\r
1494 });\r
1495 });\r
1496\r
1497 describe("mixture of new/existing records", function() {\r
1498 var fooRec, barRec, bazRec;\r
1499\r
1500 beforeEach(function() {\r
1501 fooRec = makeUser('foo@sencha.com');\r
1502 barRec = makeUser('bar@sencha.com');\r
1503 bazRec = makeUser('baz@sencha.com');\r
1504 });\r
1505\r
1506 afterEach(function() {\r
1507 fooRec = barRec = bazRec = null;\r
1508 });\r
1509\r
1510 it("should move the records to the specified position", function() {\r
1511 store.insert(2, [fooRec, edRec]);\r
1512 expect(store.indexOf(fooRec)).toBe(1);\r
1513 expect(store.indexOf(edRec)).toBe(2);\r
1514 });\r
1515\r
1516 it("should only mark existing records as moving", function() {\r
1517 setupRefreshListener();\r
1518\r
1519 store.insert(2, [fooRec, edRec]);\r
1520 var expected = {\r
1521 'ed@sencha.com': 1\r
1522 };\r
1523 expect(refreshMap).toEqual(expected);\r
1524\r
1525 expect(store.isMoving([fooRec, edRec])).toBe(0);\r
1526 expect(store.isMoving()).toBe(0);\r
1527 });\r
1528 });\r
1529 });\r
1530 });\r
1531\r
1532 describe("via edits that cause position changes", function() {\r
1533 beforeEach(function() {\r
1534 store.getSorters().add({\r
1535 property: 'name',\r
1536 direction: 'ASC'\r
1537 })\r
1538 });\r
1539\r
1540 it("should move the record to the correct position", function() {\r
1541 tommyRec.set('name', 'cccc');\r
1542 expect(store.indexOf(tommyRec)).toBe(2);\r
1543 });\r
1544\r
1545 it("should be moving during the add & remove event", function() {\r
1546 setupRefreshListener();\r
1547 tommyRec.set('name', 'cccc');\r
1548\r
1549 var expected = {\r
1550 'tommy@sencha.com': 1\r
1551 };\r
1552 expect(refreshMap).toEqual(expected);\r
1553\r
1554 expect(store.isMoving(tommyRec)).toBe(0);\r
1555 expect(store.isMoving()).toBe(0);\r
1556 });\r
1557 });\r
1558\r
1559 describe("isMoving", function() {\r
1560 describe("no records", function() {\r
1561 it("should return 0 when no records are moving", function() {\r
1562 expect(store.isMoving()).toBe(0);\r
1563 });\r
1564\r
1565 it("should return 1 when a single record is moving", function() {\r
1566 var moving;\r
1567\r
1568 // Moving a record does not fire an add. It's a refresh operation\r
1569 store.on('refresh', function() {\r
1570 moving = store.isMoving();\r
1571 });\r
1572 store.add(edRec);\r
1573 expect(moving).toBe(1);\r
1574 });\r
1575\r
1576 it("should return the amount of moving records", function() {\r
1577 var moving;\r
1578\r
1579 // Moving a record does not fire an add. It's a refresh operation\r
1580 store.on('refresh', function() {\r
1581 moving = store.isMoving();\r
1582 });\r
1583 store.add([edRec, abeRec]);\r
1584 expect(moving).toBe(2);\r
1585 });\r
1586 });\r
1587\r
1588 describe("single record", function() {\r
1589 it("should return 0 when the record is not moving", function() {\r
1590 expect(store.isMoving(edRec)).toBe(0);\r
1591 });\r
1592\r
1593 it("should return 1 when the record is moving", function() {\r
1594 var moving;\r
1595\r
1596 // Moving a record does not fire an add. It's a refresh operation\r
1597 store.on('refresh', function() {\r
1598 moving = store.isMoving(edRec);\r
1599 });\r
1600 store.add(edRec);\r
1601 expect(moving).toBe(1);\r
1602 });\r
1603 });\r
1604\r
1605 describe("multiple records", function() {\r
1606 it("should return 0 for an empty array", function() {\r
1607 expect(store.isMoving([])).toBe(0);\r
1608 });\r
1609\r
1610 it("should return the number of moving records", function() {\r
1611 var moving;\r
1612\r
1613 // Moving a record does not fire an add. It's a refresh operation\r
1614 store.on('refresh', function() {\r
1615 moving = store.isMoving([edRec, abeRec, aaronRec, tommyRec]);\r
1616 });\r
1617 store.add([edRec, abeRec]);\r
1618 expect(moving).toBe(2);\r
1619 });\r
1620 });\r
1621 });\r
1622 });\r
1623 });\r
1624 \r
1625 describe("removing", function() {\r
1626 beforeEach(function() {\r
1627 createStore();\r
1628 addStoreData();\r
1629 });\r
1630 \r
1631 describe("remove", function() {\r
1632 describe("single record", function() {\r
1633 it("should remove a single record", function() {\r
1634 store.remove(abeRec);\r
1635 expect(store.getCount()).toBe(3);\r
1636 expect(store.indexOf(abeRec)).toBe(-1);\r
1637 });\r
1638 \r
1639 it("should return an array", function() {\r
1640 expect(store.remove(aaronRec)).toEqual([aaronRec]);\r
1641 });\r
1642 \r
1643 it("should return an empty array if the item is not in the store", function() {\r
1644 var rec = makeUser('foo@sencha.com');\r
1645 expect(store.remove(rec)).toEqual([]);\r
1646 });\r
1647 });\r
1648 \r
1649 describe("array of records", function() {\r
1650 it("should remove an array", function() {\r
1651 store.remove([abeRec, tommyRec]);\r
1652 expect(store.getCount()).toBe(2);\r
1653 expect(store.indexOf(abeRec)).toBe(-1);\r
1654 expect(store.indexOf(tommyRec)).toBe(-1);\r
1655 });\r
1656 \r
1657 it("should return an array, not mutated", function() {\r
1658 var records = [edRec, aaronRec],\r
1659 result = store.remove(records);\r
1660 \r
1661 expect(result).toEqual([edRec, aaronRec]);\r
1662 expect(result).not.toBe(records);\r
1663 });\r
1664 \r
1665 it("should return an empty array if the array is empty", function() {\r
1666 expect(store.remove([])).toEqual([]);\r
1667 });\r
1668 \r
1669 it("should only return records that could be removed", function() {\r
1670 var rec = makeUser('foo@sencha.com');\r
1671 expect(store.remove([edRec, rec, tommyRec])).toEqual([edRec, tommyRec]);\r
1672 });\r
1673 });\r
1674 \r
1675 it("should shift other items into the correct position", function() {\r
1676 store.remove([edRec, tommyRec]);\r
1677 expect(store.indexOf(abeRec)).toBe(0);\r
1678 expect(store.indexOf(aaronRec)).toBe(1);\r
1679 });\r
1680 \r
1681 describe("events", function() {\r
1682 var spy;\r
1683 beforeEach(function() {\r
1684 spy = jasmine.createSpy();\r
1685 });\r
1686 \r
1687 describe("a single record", function() {\r
1688 it("should fire the remove event, passing the store, array of records & index", function() {\r
1689 store.on('remove', spy);\r
1690 store.remove(aaronRec);\r
1691 expect(spy.callCount).toBe(1);\r
1692 var args = spy.mostRecentCall.args;\r
1693 expect(args[0]).toBe(store);\r
1694 expect(args[1]).toEqual([aaronRec]);\r
1695 expect(args[2]).toBe(2);\r
1696 });\r
1697 \r
1698 it("should fire the datachanged event", function() {\r
1699 store.on('datachanged', spy);\r
1700 store.remove(edRec);\r
1701 expect(spy.callCount).toBe(1);\r
1702 var args = spy.mostRecentCall.args;\r
1703 expect(args[0]).toBe(store);\r
1704 });\r
1705 \r
1706 describe("invalid cases", function() {\r
1707 it("should not fire an event if the record is null", function() {\r
1708 store.on('remove', spy);\r
1709 store.remove(null);\r
1710 expect(spy).not.toHaveBeenCalled();\r
1711 });\r
1712 \r
1713 it("should not fire an event if the record is not in the store", function() {\r
1714 store.on('remove', spy);\r
1715 store.remove(makeUser('foo@sencha.com'));\r
1716 expect(spy).not.toHaveBeenCalled();\r
1717 });\r
1718 });\r
1719 });\r
1720 \r
1721 describe("multiple records", function() {\r
1722 describe("contiguous range", function() {\r
1723 it("should fire the remove event, passing the store, array of records & index", function() {\r
1724 store.on('remove', spy);\r
1725 store.remove([abeRec, aaronRec]);\r
1726 expect(spy.callCount).toBe(1);\r
1727 var args = spy.mostRecentCall.args;\r
1728 expect(args[0]).toBe(store);\r
1729 expect(args[1]).toEqual([abeRec, aaronRec]);\r
1730 expect(args[2]).toBe(1);\r
1731 });\r
1732 \r
1733 it("should fire the datachanged event", function() {\r
1734 store.on('datachanged', spy);\r
1735 store.remove([abeRec, aaronRec]);\r
1736 expect(spy.callCount).toBe(1);\r
1737 var args = spy.mostRecentCall.args;\r
1738 expect(args[0]).toBe(store);\r
1739 });\r
1740 });\r
1741 \r
1742 describe("discontiguous range", function() {\r
1743 var all;\r
1744 \r
1745 beforeEach(function() {\r
1746 store.removeAll();\r
1747 all = [];\r
1748 \r
1749 for (var i = 0; i < 10; ++i) {\r
1750 all.push(makeUser('user' + i + '@sencha.com'));\r
1751 }\r
1752 store.add(all);\r
1753 });\r
1754 \r
1755 it("should fire a remove event for each contiguous chunk, with highest indexes first", function() {\r
1756 store.on('remove', spy);\r
1757 store.remove([all[1], all[4], all[2], all[6], all[7], all[0]]);\r
1758 \r
1759 expect(spy.callCount).toBe(3);\r
1760 \r
1761 var args = spy.calls[0].args;\r
1762 expect(args[0]).toBe(store);\r
1763 expect(args[1]).toEqual([all[6], all[7]]);\r
1764 expect(args[2]).toBe(6);\r
1765 \r
1766 args = spy.calls[1].args;\r
1767 expect(args[0]).toBe(store);\r
1768 expect(args[1]).toEqual([all[4]]);\r
1769 expect(args[2]).toBe(4);\r
1770 \r
1771 args = spy.calls[2].args;\r
1772 expect(args[0]).toBe(store);\r
1773 expect(args[1]).toEqual([all[0], all[1], all[2]]);\r
1774 expect(args[2]).toBe(0);\r
1775 });\r
1776 \r
1777 it("should fire a single datachanged event", function() {\r
1778 store.on('datachanged', spy);\r
1779 store.remove([all[1], all[4], all[2], all[6], all[7], all[0]]);\r
1780 expect(spy.callCount).toBe(1);\r
1781 });\r
1782 });\r
1783 \r
1784 describe("invalid cases", function() {\r
1785 it("should not fire when the array is empty", function() {\r
1786 store.on({\r
1787 remove: spy,\r
1788 datachanged: spy\r
1789 });\r
1790 store.on('remove', spy);\r
1791 store.remove([]);\r
1792 expect(spy).not.toHaveBeenCalled();\r
1793 });\r
1794 \r
1795 it("should not fire when the array is stripped of items not in the store", function() {\r
1796 store.on({\r
1797 remove: spy,\r
1798 datachanged: spy\r
1799 });\r
1800 store.remove([makeUser('foo@sencha.com')]);\r
1801 expect(spy).not.toHaveBeenCalled();\r
1802 });\r
1803 });\r
1804 });\r
1805 });\r
1806 });\r
1807 \r
1808 describe("removeAt", function() {\r
1809 it("should remove the record at the specified index", function() {\r
1810 store.removeAt(1);\r
1811 expect(store.indexOf(abeRec)).toBe(-1);\r
1812 });\r
1813 \r
1814 it("should do nothing if the index is larger than the store size", function() {\r
1815 store.removeAt(100);\r
1816 expect(store.getCount()).toBe(4);\r
1817 });\r
1818 \r
1819 describe("with count", function() {\r
1820 it("should remove a single item with count = 1", function() {\r
1821 store.removeAt(2, 1);\r
1822 expect(store.getCount()).toBe(3);\r
1823 expect(store.indexOf(aaronRec)).toBe(-1);\r
1824 });\r
1825 \r
1826 it("should remove the counted amount of items", function() {\r
1827 store.removeAt(0, 3);\r
1828 expect(store.getCount()).toBe(1);\r
1829 expect(store.indexOf(edRec)).toBe(-1);\r
1830 expect(store.indexOf(abeRec)).toBe(-1);\r
1831 expect(store.indexOf(aaronRec)).toBe(-1);\r
1832 });\r
1833 \r
1834 it("should clip the count to the collection size", function() {\r
1835 store.removeAt(2, 50);\r
1836 expect(store.getCount()).toBe(2);\r
1837 expect(store.indexOf(aaronRec)).toBe(-1);\r
1838 expect(store.indexOf(tommyRec)).toBe(-1);\r
1839 });\r
1840 });\r
1841 \r
1842 describe("events", function() {\r
1843 var spy;\r
1844 \r
1845 beforeEach(function() {\r
1846 spy = jasmine.createSpy();\r
1847 });\r
1848 \r
1849 it("should fire a remove event with the removed records", function() {\r
1850 store.on('remove', spy);\r
1851 store.removeAt(1, 2);\r
1852 expect(spy.callCount).toBe(1);\r
1853 var args = spy.mostRecentCall.args;\r
1854 expect(args[0]).toBe(store);\r
1855 expect(args[1]).toEqual([abeRec, aaronRec]);\r
1856 expect(args[2]).toBe(1);\r
1857 });\r
1858 \r
1859 it("should fire a datachanged event", function() {\r
1860 store.on('datachanged', spy);\r
1861 store.removeAt(1, 2);\r
1862 expect(spy.callCount).toBe(1);\r
1863 var args = spy.mostRecentCall.args;\r
1864 expect(args[0]).toBe(store);\r
1865 });\r
1866 \r
1867 describe("invalid cases", function() {\r
1868 it("should not fire when the index is greater than the store bounds", function() {\r
1869 store.on({\r
1870 remove: spy,\r
1871 datachanged: spy\r
1872 });\r
1873 store.removeAt(100);\r
1874 expect(spy).not.toHaveBeenCalled();\r
1875 });\r
1876 });\r
1877 });\r
1878 });\r
1879 \r
1880 describe("removeAll", function() {\r
1881 it("should remove all records from the store", function() {\r
1882 store.removeAll();\r
1883 expect(store.getCount()).toBe(0);\r
1884 });\r
1885 \r
1886 it("should do nothing if the store is empty", function() {\r
1887 store.destroy();\r
1888 createStore();\r
1889 store.removeAll();\r
1890 expect(store.getCount()).toBe(0);\r
1891 });\r
1892\r
1893 describe("when filtered", function() {\r
1894 it("should only remove items in the active collection", function() {\r
1895 store.filter('group', 'code');\r
1896 store.removeAll();\r
1897 expect(store.getCount()).toBe(0);\r
1898 store.getFilters().removeAll();\r
1899 expect(store.getCount()).toBe(2);\r
1900 expect(store.getAt(0)).toBe(abeRec);\r
1901 expect(store.getAt(1)).toBe(aaronRec);\r
1902 });\r
1903 });\r
1904\r
1905 it("should return the removed records", function() {\r
1906 createStore();\r
1907 var records = store.getRange();\r
1908 expect(store.removeAll()).toEqual(records);\r
1909 });\r
1910\r
1911 it("should return an empty array if the store is empty", function() {\r
1912 createStore();\r
1913 store.removeAll();\r
1914 expect(store.removeAll()).toEqual([]);\r
1915 });\r
1916 \r
1917 describe("events", function() {\r
1918 var spy;\r
1919 beforeEach(function() {\r
1920 spy = jasmine.createSpy();\r
1921 });\r
1922 \r
1923 it("should not fire any remove events", function() {\r
1924 store.on('remove', spy);\r
1925 store.removeAll();\r
1926 expect(spy).not.toHaveBeenCalled();\r
1927 });\r
1928 \r
1929 it("should fire the clear event and pass the current records", function() {\r
1930 var range = store.getRange();\r
1931 store.on('clear', spy);\r
1932 store.removeAll();\r
1933 expect(spy.callCount).toBe(1);\r
1934 expect(spy.mostRecentCall.args[0]).toBe(store);\r
1935 expect(spy.mostRecentCall.args[1]).toEqual(range);\r
1936 });\r
1937 \r
1938 it("should fire the datachanged event", function() {\r
1939 store.on('datachanged', spy);\r
1940 store.removeAll();\r
1941 expect(spy.callCount).toBe(1);\r
1942 });\r
1943 \r
1944 describe("with silent: true", function() {\r
1945 it("should not fire the clear event", function() {\r
1946 store.on('clear', spy);\r
1947 store.removeAll(true);\r
1948 expect(spy).not.toHaveBeenCalled();\r
1949 });\r
1950 \r
1951 it("should not fire the datachanged event", function() {\r
1952 store.on('datachanged', spy);\r
1953 store.removeAll(true);\r
1954 expect(spy).not.toHaveBeenCalled();\r
1955 });\r
1956 });\r
1957\r
1958 it("should be able to fire remove events after calling removeAll", function() {\r
1959 store.removeAll();\r
1960 store.add({}, {}, {});\r
1961 store.on('remove', spy);\r
1962 store.removeAt(0);\r
1963 expect(spy.callCount).toBe(1);\r
1964 });\r
1965 });\r
1966 });\r
1967\r
1968 describe("getRemovedRecords", function() {\r
1969 it("should be empty by default", function() {\r
1970 expect(store.getRemovedRecords()).toEqual([]);\r
1971 });\r
1972\r
1973 it("should return removed records", function() {\r
1974 store.remove(edRec);\r
1975 store.remove(aaronRec);\r
1976 expect(store.getRemovedRecords()).toEqual([edRec, aaronRec]);\r
1977 });\r
1978\r
1979 it("should return a copy of the records, modifying the value should not mutate the records", function() {\r
1980 store.remove(edRec);\r
1981 store.remove(aaronRec);\r
1982 var records = store.getRemovedRecords();\r
1983 records.splice(0, 2);\r
1984 expect(records).toEqual([]);\r
1985 expect(store.getRemovedRecords()).toEqual([edRec, aaronRec]);\r
1986 });\r
1987\r
1988 it("should exclude phantom records", function() {\r
1989 var rec = new spec.User();\r
1990 store.add(rec);\r
1991 store.remove(rec);\r
1992 expect(store.getRemovedRecords()).toEqual([]);\r
1993 });\r
1994\r
1995 it("should exclude re-added records", function() {\r
1996 store.remove(edRec);\r
1997 store.add(edRec);\r
1998 expect(store.getRemovedRecords()).toEqual([]);\r
1999 });\r
2000\r
2001 it("should be cleared after calling rejectChanges", function() {\r
2002 store.remove(edRec);\r
2003 store.rejectChanges();\r
2004 expect(store.getRemovedRecords()).toEqual([]);\r
2005 });\r
2006\r
2007 it("should be cleared after calling commitChanges", function() {\r
2008 store.remove(edRec);\r
2009 store.commitChanges();\r
2010 expect(store.getRemovedRecords()).toEqual([]);\r
2011 });\r
2012\r
2013 describe("clearRemovedOnLoad", function() {\r
2014 describe("clearRemovedOnLoad: true", function() {\r
2015 it("should clear removed records", function() {\r
2016 store.setClearRemovedOnLoad(true);\r
2017 store.remove(edRec);\r
2018 store.loadData([]);\r
2019 expect(store.getRemovedRecords()).toEqual([]);\r
2020 });\r
2021 });\r
2022\r
2023 describe("clearRemovedOnLoad: false", function() {\r
2024 it("should not clear removed records", function() {\r
2025 store.setClearRemovedOnLoad(false);\r
2026 store.remove(edRec);\r
2027 store.loadData([]);\r
2028 expect(store.getRemovedRecords()).toEqual([edRec]);\r
2029 });\r
2030 });\r
2031 });\r
2032 });\r
2033 });\r
2034\r
2035 describe("loading", function() {\r
2036 describe('an empty store', function () {\r
2037 // Note that we need to test the actual empty store that is registered with the StoreMgr\r
2038 // because it neuters several store operations, and this is part of what is being tested.\r
2039 // See the StoreManager class callback function.\r
2040 it('should allow an empty store to load', function () {\r
2041 expect(function () {\r
2042 Ext.StoreMgr.get('ext-empty-store').load();\r
2043 }).not.toThrow();\r
2044 });\r
2045\r
2046 it('should return an empty result set', function () {\r
2047 expect(Ext.StoreMgr.get('ext-empty-store').load().getCount()).toBe(0);\r
2048 });\r
2049 });\r
2050\r
2051 describe("loadCount", function() {\r
2052 it("should default to 0", function() {\r
2053 createStore();\r
2054 expect(store.loadCount).toBe(0);\r
2055 });\r
2056\r
2057 describe("construction", function() {\r
2058 it("should increment the loadCount when passing data with no proxy", function() {\r
2059 createStore({\r
2060 data: [abeRaw]\r
2061 });\r
2062 expect(store.loadCount).toBe(1);\r
2063 });\r
2064\r
2065 it("should increment the loadCount when passing data with a memory proxy", function() {\r
2066 createStore({\r
2067 proxy: {\r
2068 type: 'memory'\r
2069 },\r
2070 data: [abeRaw]\r
2071 });\r
2072 expect(store.loadCount).toBe(1);\r
2073 });\r
2074 });\r
2075\r
2076 describe("with no proxy", function() {\r
2077 beforeEach(function() {\r
2078 createStore();\r
2079 });\r
2080\r
2081 it("should increment when using loadRecords", function() {\r
2082 store.loadRecords([makeUser('foo@sencha.com')]);\r
2083 expect(store.loadCount).toBe(1);\r
2084 });\r
2085\r
2086 it("should increment when using loadData", function() {\r
2087 store.loadData([tommyRaw]);\r
2088 expect(store.loadCount).toBe(1);\r
2089 });\r
2090 });\r
2091\r
2092 describe("with a proxy", function() {\r
2093 beforeEach(function() {\r
2094 createStore({\r
2095 proxy: {\r
2096 type: 'ajax',\r
2097 url: '/foo'\r
2098 }\r
2099 });\r
2100 });\r
2101\r
2102 it("should increment on a successful load with no records", function() {\r
2103 store.load();\r
2104 completeWithData([]);\r
2105 expect(store.loadCount).toBe(1);\r
2106 });\r
2107\r
2108 it("should increment on a successful load with records", function() {\r
2109 store.load();\r
2110 completeWithData([abeRaw, aaronRaw]);\r
2111 expect(store.loadCount).toBe(1);\r
2112 });\r
2113\r
2114 it("should not increment on an unsuccessful load", function() {\r
2115 store.load();\r
2116 complete(500);\r
2117 expect(store.loadCount).toBe(0);\r
2118 });\r
2119 });\r
2120\r
2121 it("should increment for each load", function() {\r
2122 createStore({\r
2123 proxy: {\r
2124 type: 'ajax',\r
2125 url: '/foo'\r
2126 }\r
2127 });\r
2128 for (var i = 0; i < 5; ++i) {\r
2129 store.load();\r
2130 completeWithData([]);\r
2131 }\r
2132 expect(store.loadCount).toBe(5);\r
2133 });\r
2134 });\r
2135\r
2136 describe("local data", function() {\r
2137 describe("loadData", function() {\r
2138 beforeEach(function() {\r
2139 createStore();\r
2140 });\r
2141 \r
2142 it("should create model instances", function() {\r
2143 store.loadData([edRaw, abeRaw]);\r
2144 expect(store.first().get('name')).toBe('Ed Spencer');\r
2145 expect(store.last().get('name')).toBe('Abe Elias');\r
2146 expect(store.getCount()).toBe(2);\r
2147 });\r
2148 \r
2149 it("should accept model instances", function() {\r
2150 edRec = makeUser(edRaw);\r
2151 abeRec = makeUser(abeRaw);\r
2152 aaronRec = makeUser(aaronRaw);\r
2153 \r
2154 store.loadData([edRec, abeRec, aaronRec]);\r
2155 expect(store.first()).toBe(edRec);\r
2156 expect(store.getAt(1)).toBe(abeRec);\r
2157 expect(store.last()).toBe(aaronRec);\r
2158 expect(store.getCount()).toBe(3);\r
2159 });\r
2160 \r
2161 it("should clear existing records by default", function() {\r
2162 addStoreData();\r
2163 store.loadData([{\r
2164 email: 'foo@sencha.com'\r
2165 }]);\r
2166 expect(store.first().get('email')).toBe('foo@sencha.com');\r
2167 expect(store.getCount()).toBe(1);\r
2168 });\r
2169 \r
2170 it("should append records to the end when using append: true", function() {\r
2171 addStoreData();\r
2172 store.loadData([{\r
2173 email: 'foo@sencha.com'\r
2174 }], true);\r
2175 expect(store.last().get('email')).toBe('foo@sencha.com');\r
2176 expect(store.getCount()).toBe(5);\r
2177 });\r
2178\r
2179 describe("when filtered", function() {\r
2180 it("should clear filtered out records", function() {\r
2181 addStoreData();\r
2182 store.getFilters().add({\r
2183 property: 'email',\r
2184 value: 'ed@sencha.com'\r
2185 });\r
2186\r
2187 expect(store.getCount()).toBe(1);\r
2188\r
2189 store.loadData([{\r
2190 email: 'a@sencha.com'\r
2191 }, {\r
2192 email: 'b@sencha.com'\r
2193 }]);\r
2194\r
2195 expect(store.getCount()).toBe(0);\r
2196\r
2197 store.getFilters().removeAll();\r
2198\r
2199 expect(store.getCount()).toBe(2);\r
2200 expect(store.getAt(0).getId()).toBe('a@sencha.com');\r
2201 expect(store.getAt(1).getId()).toBe('b@sencha.com');\r
2202\r
2203 expect(store.indexOf(edRec)).toBe(-1);\r
2204 });\r
2205\r
2206 it("should include only matching records", function() {\r
2207 addStoreData();\r
2208 store.getFilters().add({\r
2209 property: 'group',\r
2210 value: 'code'\r
2211 });\r
2212\r
2213 expect(store.getCount()).toBe(2);\r
2214\r
2215 store.loadData([{\r
2216 email: 'a@sencha.com',\r
2217 group: 'admin'\r
2218 }, {\r
2219 email: 'b@sencha.com',\r
2220 group: 'code'\r
2221 }]);\r
2222\r
2223 expect(store.getCount()).toBe(1);\r
2224 expect(store.getAt(0).getId()).toBe('b@sencha.com');\r
2225\r
2226 store.getFilters().removeAll();\r
2227\r
2228 expect(store.getCount()).toBe(2);\r
2229 expect(store.getAt(0).getId()).toBe('a@sencha.com');\r
2230 expect(store.getAt(1).getId()).toBe('b@sencha.com');\r
2231\r
2232 expect(store.indexOf(edRec)).toBe(-1);\r
2233 expect(store.indexOf(tommyRec)).toBe(-1);\r
2234 });\r
2235 });\r
2236 \r
2237 describe("events", function() {\r
2238 var spy;\r
2239 beforeEach(function() {\r
2240 spy = jasmine.createSpy();\r
2241 });\r
2242 \r
2243 it("should fire a datachanged event", function() {\r
2244 store.on('datachanged', spy);\r
2245 store.loadData([edRaw]);\r
2246 expect(spy.callCount).toBe(1);\r
2247 expect(spy.mostRecentCall.args[0]).toBe(store);\r
2248 });\r
2249 \r
2250 it("should fire a refresh event", function() {\r
2251 store.on('refresh', spy);\r
2252 store.loadData([edRaw]);\r
2253 expect(spy.callCount).toBe(1);\r
2254 expect(spy.mostRecentCall.args[0]).toBe(store);\r
2255 });\r
2256 \r
2257 it("should not fire any add events", function() {\r
2258 store.on('add', spy);\r
2259 store.loadData([tommyRaw]);\r
2260 expect(spy).not.toHaveBeenCalled();\r
2261 });\r
2262 \r
2263 it("should not fire any remove event when records are being cleared", function() {\r
2264 store.on('remove', spy);\r
2265 store.loadData([tommyRaw]);\r
2266 expect(spy).not.toHaveBeenCalled();\r
2267 });\r
2268 });\r
2269 });\r
2270 \r
2271 describe("loadRecords", function() {\r
2272 beforeEach(function() {\r
2273 createStore();\r
2274 });\r
2275 \r
2276 it("should accept model instances", function() {\r
2277 edRec = makeUser(edRaw);\r
2278 abeRec = makeUser(abeRaw);\r
2279 aaronRec = makeUser(aaronRaw);\r
2280 \r
2281 store.loadRecords([edRec, abeRec, aaronRec]);\r
2282 expect(store.first()).toBe(edRec);\r
2283 expect(store.getAt(1)).toBe(abeRec);\r
2284 expect(store.last()).toBe(aaronRec);\r
2285 expect(store.getCount()).toBe(3);\r
2286 });\r
2287 \r
2288 it("should clear existing records by default", function() {\r
2289 addStoreData();\r
2290 var rec = makeUser('foo@sencha.com');\r
2291 store.loadRecords([rec]);\r
2292 expect(store.first().get('email')).toBe('foo@sencha.com');\r
2293 expect(store.getCount()).toBe(1);\r
2294 });\r
2295 \r
2296 it("should append records to the end when using append: true", function() {\r
2297 addStoreData();\r
2298 var rec = makeUser('foo@sencha.com');\r
2299 store.loadRecords([rec], {\r
2300 addRecords: true\r
2301 });\r
2302 expect(store.last()).toBe(rec);\r
2303 expect(store.getCount()).toBe(5);\r
2304 });\r
2305\r
2306 describe("when filtered", function() {\r
2307 it("should clear filtered out records", function() {\r
2308 addStoreData();\r
2309 store.getFilters().add({\r
2310 property: 'email',\r
2311 value: 'ed@sencha.com'\r
2312 });\r
2313\r
2314 expect(store.getCount()).toBe(1);\r
2315\r
2316 store.loadRecords([makeUser('a@sencha.com'), makeUser('b@sencha.com')]);\r
2317\r
2318 expect(store.getCount()).toBe(0);\r
2319\r
2320 store.getFilters().removeAll();\r
2321\r
2322 expect(store.getCount()).toBe(2);\r
2323 expect(store.getAt(0).getId()).toBe('a@sencha.com');\r
2324 expect(store.getAt(1).getId()).toBe('b@sencha.com');\r
2325\r
2326 expect(store.indexOf(edRec)).toBe(-1);\r
2327 });\r
2328\r
2329 it("should include only matching records", function() {\r
2330 addStoreData();\r
2331 store.getFilters().add({\r
2332 property: 'group',\r
2333 value: 'code'\r
2334 });\r
2335\r
2336 expect(store.getCount()).toBe(2);\r
2337 \r
2338 store.loadRecords([makeUser('a@sencha.com', {\r
2339 group: 'admin'\r
2340 }), makeUser('b@sencha.com', {\r
2341 group: 'code'\r
2342 })]);\r
2343\r
2344 expect(store.getCount()).toBe(1);\r
2345 expect(store.getAt(0).getId()).toBe('b@sencha.com');\r
2346\r
2347 store.getFilters().removeAll();\r
2348\r
2349 expect(store.getCount()).toBe(2);\r
2350 expect(store.getAt(0).getId()).toBe('a@sencha.com');\r
2351 expect(store.getAt(1).getId()).toBe('b@sencha.com');\r
2352\r
2353 expect(store.indexOf(edRec)).toBe(-1);\r
2354 expect(store.indexOf(tommyRec)).toBe(-1);\r
2355 });\r
2356 });\r
2357 \r
2358 describe("events", function() {\r
2359 var spy;\r
2360 beforeEach(function() {\r
2361 spy = jasmine.createSpy();\r
2362 });\r
2363 \r
2364 it("should fire a datachanged event", function() {\r
2365 store.on('datachanged', spy);\r
2366 store.loadRecords([makeUser(edRaw)]);\r
2367 expect(spy.callCount).toBe(1);\r
2368 expect(spy.mostRecentCall.args[0]).toBe(store);\r
2369 });\r
2370 \r
2371 it("should fire a refresh event", function() {\r
2372 store.on('refresh', spy);\r
2373 store.loadRecords([makeUser(edRaw)]);\r
2374 expect(spy.callCount).toBe(1);\r
2375 expect(spy.mostRecentCall.args[0]).toBe(store);\r
2376 });\r
2377 \r
2378 it("should not fire any add events", function() {\r
2379 store.on('add', spy);\r
2380 store.loadRecords([makeUser(tommyRaw)]);\r
2381 expect(spy).not.toHaveBeenCalled();\r
2382 });\r
2383 \r
2384 it("should not fire any remove event when records are being cleared", function() {\r
2385 store.on('remove', spy);\r
2386 store.loadRecords([makeUser(tommyRaw)]);\r
2387 expect(spy).not.toHaveBeenCalled();\r
2388 });\r
2389 });\r
2390 });\r
2391 \r
2392 describe("loadRawData", function() {\r
2393 beforeEach(function() {\r
2394 Ext.define('spec.UserWithReader', {\r
2395 extend: 'Ext.data.Model',\r
2396 idProperty: 'email',\r
2397 fields: ['email', {\r
2398 name: 'name',\r
2399 mapping: 'person'\r
2400 }],\r
2401 proxy: {\r
2402 type: 'memory',\r
2403 reader: {\r
2404 type: 'json',\r
2405 rootProperty: 'data',\r
2406 successProperty: 'success',\r
2407 totalProperty: 'myTotal'\r
2408 }\r
2409 }\r
2410 });\r
2411 createStore({\r
2412 model: 'spec.UserWithReader'\r
2413 });\r
2414 });\r
2415 \r
2416 afterEach(function() {\r
2417 Ext.undefine('spec.UserWithReader');\r
2418 });\r
2419 \r
2420 describe("reader processing", function() {\r
2421 it("should pass the data through the proxy reader", function() {\r
2422 store.loadRawData({\r
2423 success: true,\r
2424 data: [{\r
2425 email: 'foo@sencha.com',\r
2426 person: 'The name'\r
2427 }]\r
2428 });\r
2429 var rec = store.first();\r
2430 expect(rec.$className).toBe('spec.UserWithReader');\r
2431 expect(rec.get('name')).toBe('The name');\r
2432 });\r
2433 \r
2434 \r
2435 it("should read the totalCount", function() {\r
2436 store.loadRawData({\r
2437 success: true,\r
2438 myTotal: 9876,\r
2439 data: []\r
2440 });\r
2441 expect(store.getTotalCount()).toBe(9876);\r
2442 });\r
2443 \r
2444 it("should return true when the records are read", function() {\r
2445 var result = store.loadRawData({\r
2446 success: true,\r
2447 data: [{\r
2448 email: 'foo@sencha.com',\r
2449 person: 'The name'\r
2450 }]\r
2451 });\r
2452 expect(result).toBe(true);\r
2453 });\r
2454 \r
2455 it("should return false if the reader can't read the data and load no records", function() {\r
2456 var result = store.loadRawData({\r
2457 success: false,\r
2458 data: [{\r
2459 email: 'foo@sencha.com',\r
2460 person: 'Name1'\r
2461 }]\r
2462 });\r
2463 expect(result).toBe(false);\r
2464 expect(store.getCount()).toBe(0);\r
2465 });\r
2466 });\r
2467 \r
2468 it("should clear existing records by default", function() {\r
2469 addStoreData();\r
2470 store.loadRawData({\r
2471 success: true,\r
2472 data: [{\r
2473 email: 'foo@sencha.com'\r
2474 }]\r
2475 });\r
2476 expect(store.first().get('email')).toBe('foo@sencha.com');\r
2477 expect(store.getCount()).toBe(1);\r
2478 });\r
2479 \r
2480 it("should append records to the end when using append: true", function() {\r
2481 addStoreData();\r
2482 store.loadRawData({\r
2483 success: true,\r
2484 data: [{\r
2485 email: 'foo@sencha.com'\r
2486 }]\r
2487 }, {\r
2488 addRecords: true\r
2489 });\r
2490 expect(store.last().get('email')).toBe('foo@sencha.com');\r
2491 expect(store.getCount()).toBe(5);\r
2492 });\r
2493\r
2494 describe("when filtered", function() {\r
2495 it("should clear filtered out records", function() {\r
2496 addStoreData();\r
2497 store.getFilters().add({\r
2498 property: 'email',\r
2499 value: 'ed@sencha.com'\r
2500 });\r
2501\r
2502 expect(store.getCount()).toBe(1);\r
2503\r
2504 store.loadRawData([{\r
2505 email: 'a@sencha.com'\r
2506 }, {\r
2507 email: 'b@sencha.com'\r
2508 }]);\r
2509\r
2510 expect(store.getCount()).toBe(0);\r
2511\r
2512 store.getFilters().removeAll();\r
2513\r
2514 expect(store.getCount()).toBe(2);\r
2515 expect(store.getAt(0).getId()).toBe('a@sencha.com');\r
2516 expect(store.getAt(1).getId()).toBe('b@sencha.com');\r
2517\r
2518 expect(store.indexOf(edRec)).toBe(-1);\r
2519 });\r
2520\r
2521 it("should include only matching records", function() {\r
2522 addStoreData();\r
2523 store.getFilters().add({\r
2524 property: 'group',\r
2525 value: 'code'\r
2526 });\r
2527\r
2528 expect(store.getCount()).toBe(2);\r
2529\r
2530 store.loadRawData([{\r
2531 email: 'a@sencha.com',\r
2532 group: 'admin'\r
2533 }, {\r
2534 email: 'b@sencha.com',\r
2535 group: 'code'\r
2536 }]);\r
2537\r
2538 expect(store.getCount()).toBe(1);\r
2539 expect(store.getAt(0).getId()).toBe('b@sencha.com');\r
2540\r
2541 store.getFilters().removeAll();\r
2542\r
2543 expect(store.getCount()).toBe(2);\r
2544 expect(store.getAt(0).getId()).toBe('a@sencha.com');\r
2545 expect(store.getAt(1).getId()).toBe('b@sencha.com');\r
2546\r
2547 expect(store.indexOf(edRec)).toBe(-1);\r
2548 expect(store.indexOf(tommyRec)).toBe(-1);\r
2549 });\r
2550 });\r
2551 \r
2552 describe("events", function() {\r
2553 var spy, data;\r
2554 beforeEach(function() {\r
2555 spy = jasmine.createSpy();\r
2556 data = {\r
2557 success: true,\r
2558 data: [{\r
2559 email: 'foo@sencha.com'\r
2560 }]\r
2561 };\r
2562 });\r
2563 \r
2564 it("should fire a datachanged event", function() {\r
2565 store.on('datachanged', spy);\r
2566 store.loadRawData(data);\r
2567 expect(spy.callCount).toBe(1);\r
2568 expect(spy.mostRecentCall.args[0]).toBe(store);\r
2569 });\r
2570 \r
2571 it("should fire a refresh event", function() {\r
2572 store.on('refresh', spy);\r
2573 store.loadRawData(data);\r
2574 expect(spy.callCount).toBe(1);\r
2575 expect(spy.mostRecentCall.args[0]).toBe(store);\r
2576 });\r
2577 \r
2578 it("should not fire any add events", function() {\r
2579 store.on('add', spy);\r
2580 store.loadRawData(data);\r
2581 expect(spy).not.toHaveBeenCalled();\r
2582 });\r
2583 \r
2584 it("should not fire any remove event when records are being cleared", function() {\r
2585 store.on('remove', spy);\r
2586 store.loadRawData(data);\r
2587 expect(spy).not.toHaveBeenCalled();\r
2588 });\r
2589 });\r
2590 });\r
2591 });\r
2592 \r
2593 describe("loading remote data", function() {\r
2594 var successData;\r
2595 \r
2596 beforeEach(function() {\r
2597 successData = {\r
2598 success: true,\r
2599 data: [{\r
2600 email: 'foo@sencha.com'\r
2601 }]\r
2602 };\r
2603 createStore({\r
2604 proxy: {\r
2605 type: 'ajax',\r
2606 url: 'foo',\r
2607 reader: {\r
2608 type: 'json',\r
2609 successProperty: 'success',\r
2610 rootProperty: 'data'\r
2611 }\r
2612 }\r
2613 });\r
2614 });\r
2615 \r
2616 describe("setting options on the operation", function() {\r
2617 var proxySpy,\r
2618 collectionSortSpy;\r
2619 \r
2620 function getOperation() {\r
2621 return proxySpy.mostRecentCall.args[0];\r
2622 }\r
2623 \r
2624 beforeEach(function() {\r
2625 proxySpy = spyOn(store.getProxy(), 'read').andReturn();\r
2626 collectionSortSpy = spyOn(store.getData(), 'sortItems').andCallThrough();\r
2627 });\r
2628 \r
2629 describe("sorters", function() {\r
2630 function makeSorter(prop, dir) {\r
2631 return {\r
2632 property: prop,\r
2633 direction: dir\r
2634 };\r
2635 }\r
2636 \r
2637 it("should pass along sorters if remoteSort: true", function() {\r
2638 var beforeSortSpy = spyOnEvent(store, 'beforesort'),\r
2639 sorters;\r
2640\r
2641 store.setRemoteSort(true);\r
2642 store.getSorters().add(makeSorter('email', 'ASC'));\r
2643\r
2644 // Mutating the sorters of a remoteSort ProxyStore triggers a load of sorted data\r
2645 expect(beforeSortSpy.callCount).toBe(1);\r
2646\r
2647 // beforesort event passes Sorters as an array\r
2648 expect(beforeSortSpy.calls[0].args[1]).toEqual(store.getSorters().getRange());\r
2649\r
2650 store.getSorters().add(makeSorter('evilness', 'DESC'));\r
2651\r
2652 // Mutating the sorters of a remoteSort ProxyStore triggers a load of sorted data\r
2653 expect(beforeSortSpy.callCount).toBe(2);\r
2654\r
2655 // beforesort event passes Sorters as an array\r
2656 expect(beforeSortSpy.calls[1].args[1]).toEqual(store.getSorters().getRange());\r
2657\r
2658 store.load();\r
2659\r
2660 // The beforesort event should be fired on load.\r
2661 expect(beforeSortSpy.callCount).toBe(3);\r
2662\r
2663 // beforesort event passes Sorters as an array\r
2664 expect(beforeSortSpy.calls[2].args[1]).toEqual(store.getSorters().getRange());\r
2665\r
2666 sorters = getOperation().getSorters();\r
2667 expect(sorters[0].getProperty()).toBe('email');\r
2668 expect(sorters[0].getDirection()).toBe('ASC');\r
2669 \r
2670 expect(sorters[1].getProperty()).toBe('evilness');\r
2671 expect(sorters[1].getDirection()).toBe('DESC');\r
2672\r
2673 // The Collection should not have sorted itself\r
2674 expect(collectionSortSpy).not.toHaveBeenCalled();\r
2675 });\r
2676 \r
2677 it("should not pass sorters if there are none", function() {\r
2678 store.setRemoteSort(true);\r
2679 store.load();\r
2680 expect(getOperation().getSorters()).toBeUndefined();\r
2681\r
2682 // The Collection should not have sorted itself\r
2683 expect(collectionSortSpy).not.toHaveBeenCalled();\r
2684 });\r
2685 \r
2686 it("should not pass sorters if remoteSort: false", function() {\r
2687 var beforeSortSpy = spyOnEvent(store, 'beforesort');\r
2688\r
2689 store.setRemoteSort(false);\r
2690 store.getSorters().add(makeSorter('email', 'ASC'));\r
2691\r
2692 // Mutating the Sorters Collection causes a sort if remoteSort is false\r
2693 expect(beforeSortSpy.callCount).toBe(1);\r
2694\r
2695 // beforesort event passes Sorters as an array\r
2696 expect(beforeSortSpy.calls[0].args[1]).toEqual(store.getSorters().getRange());\r
2697\r
2698 store.getSorters().add(makeSorter('evilness', 'DESC'));\r
2699\r
2700 // Mutating the Sorters Collection causes a sort if remoteSort is false\r
2701 expect(beforeSortSpy.callCount).toBe(2);\r
2702\r
2703 // beforesort event passes Sorters as an array\r
2704 expect(beforeSortSpy.calls[1].args[1]).toEqual(store.getSorters().getRange());\r
2705\r
2706 store.load();\r
2707 expect(getOperation().getSorters()).toBeUndefined();\r
2708\r
2709 // The Collection should have sorted itself\r
2710 expect(collectionSortSpy.callCount).toBe(2);\r
2711 });\r
2712 });\r
2713 \r
2714 describe("grouper", function() {\r
2715 function makeGrouper(prop, dir) {\r
2716 return {\r
2717 property: prop,\r
2718 direction: dir\r
2719 };\r
2720 }\r
2721 \r
2722 it("should pass along the grouper if remoteSort: true", function() {\r
2723 store.setRemoteSort(true);\r
2724 store.setGrouper(makeGrouper('group', 'ASC'));\r
2725 store.load();\r
2726 var grouper = getOperation().getGrouper();\r
2727 expect(grouper.getProperty()).toBe('group');\r
2728 expect(grouper.getDirection()).toBe('ASC');\r
2729 });\r
2730 \r
2731 it("should pass the grouper if there isn't one", function() {\r
2732 store.setRemoteSort(true);\r
2733 store.load();\r
2734 expect(getOperation().getGrouper()).toBeUndefined();\r
2735 });\r
2736 \r
2737 it("should pass the grouper if remoteSort: false", function() {\r
2738 store.setRemoteSort(false);\r
2739 store.setGrouper(makeGrouper('group', 'ASC'));\r
2740 store.load();\r
2741 expect(getOperation().getGrouper()).toBeUndefined();\r
2742 });\r
2743\r
2744 it('should sort a grouped store according to the group field and then sorters', function() {\r
2745 var sorted = 0,\r
2746 Task = Ext.define(null, {\r
2747 extend: 'Ext.data.Model',\r
2748 idProperty: 'taskId',\r
2749 fields: [\r
2750 {name: 'projectId', type: 'int'},\r
2751 {name: 'project', type: 'string'},\r
2752 {name: 'taskId', type: 'int'},\r
2753 {name: 'description', type: 'string'},\r
2754 {name: 'estimate', type: 'float'},\r
2755 {name: 'rate', type: 'float'},\r
2756 {name: 'due', type: 'date', dateFormat:'m/d/Y'}\r
2757 ]\r
2758 });\r
2759\r
2760 var data = [\r
2761 {projectId: 100, project: 'Ext Forms: Field Anchoring', taskId: 112, description: 'Integrate 2.0 Forms with 2.0 Layouts', estimate: 6, rate: 150, due:'06/24/2007'},\r
2762 {projectId: 100, project: 'Ext Forms: Field Anchoring', taskId: 113, description: 'Implement AnchorLayout', estimate: 4, rate: 150, due:'06/25/2007'},\r
2763 {projectId: 100, project: 'Ext Forms: Field Anchoring', taskId: 114, description: 'Add support for multiple<br>types of anchors', estimate: 4, rate: 150, due:'06/27/2007'},\r
2764 {projectId: 100, project: 'Ext Forms: Field Anchoring', taskId: 115, description: 'Testing and debugging', estimate: 8, rate: 0, due:'06/29/2007'},\r
2765\r
2766 {projectId: 101, project: 'Ext Grid: Single-level Grouping', taskId: 101, description: 'Add required rendering "hooks" to GridView', estimate: 6, rate: 100, due:'07/01/2007'},\r
2767 {projectId: 101, project: 'Ext Grid: Single-level Grouping', taskId: 102, description: 'Extend GridView and override rendering functions', estimate: 6, rate: 100, due:'07/03/2007'},\r
2768 {projectId: 101, project: 'Ext Grid: Single-level Grouping', taskId: 103, description: 'Extend Store with grouping functionality', estimate: 4, rate: 100, due:'07/04/2007'},\r
2769 {projectId: 101, project: 'Ext Grid: Single-level Grouping', taskId: 121, description: 'Default CSS Styling', estimate: 2, rate: 100, due:'07/05/2007'},\r
2770 {projectId: 101, project: 'Ext Grid: Single-level Grouping', taskId: 104, description: 'Testing and debugging', estimate: 6, rate: 100, due:'07/06/2007'},\r
2771\r
2772 {projectId: 102, project: 'Ext Grid: Summary Rows', taskId: 105, description: 'Ext Grid plugin integration', estimate: 4, rate: 125, due:'07/01/2007'},\r
2773 {projectId: 102, project: 'Ext Grid: Summary Rows', taskId: 106, description: 'Summary creation during rendering phase', estimate: 4, rate: 125, due:'07/02/2007'},\r
2774 {projectId: 102, project: 'Ext Grid: Summary Rows', taskId: 107, description: 'Dynamic summary updates in editor grids', estimate: 6, rate: 125, due:'07/05/2007'},\r
2775 {projectId: 102, project: 'Ext Grid: Summary Rows', taskId: 108, description: 'Remote summary integration', estimate: 4, rate: 125, due:'07/05/2007'},\r
2776 {projectId: 102, project: 'Ext Grid: Summary Rows', taskId: 109, description: 'Summary renderers and calculators', estimate: 4, rate: 125, due:'07/06/2007'},\r
2777 {projectId: 102, project: 'Ext Grid: Summary Rows', taskId: 110, description: 'Integrate summaries with GroupingView', estimate: 10, rate: 125, due:'07/11/2007'},\r
2778 {projectId: 102, project: 'Ext Grid: Summary Rows', taskId: 111, description: 'Testing and debugging', estimate: 8, rate: 125, due:'07/15/2007'}\r
2779 ];\r
2780\r
2781 store = Ext.create('Ext.data.Store', {\r
2782 model: Task,\r
2783 data: data,\r
2784 sorters: {property: 'due', direction: 'ASC'},\r
2785 groupField: 'project'\r
2786 });\r
2787\r
2788 expect(store.getAt(0).get('taskId')).toBe(112);\r
2789 expect(store.getAt(1).get('taskId')).toBe(113);\r
2790 expect(store.getAt(2).get('taskId')).toBe(114);\r
2791 expect(store.getAt(3).get('taskId')).toBe(115);\r
2792\r
2793 expect(store.getAt(4).get('taskId')).toBe(101);\r
2794 expect(store.getAt(5).get('taskId')).toBe(102);\r
2795 expect(store.getAt(6).get('taskId')).toBe(103);\r
2796 expect(store.getAt(7).get('taskId')).toBe(121);\r
2797 expect(store.getAt(8).get('taskId')).toBe(104);\r
2798 \r
2799 expect(store.getAt(9).get('taskId')).toBe(105);\r
2800 expect(store.getAt(10).get('taskId')).toBe(106);\r
2801 expect(store.getAt(11).get('taskId')).toBe(107);\r
2802 expect(store.getAt(12).get('taskId')).toBe(108);\r
2803 expect(store.getAt(13).get('taskId')).toBe(109);\r
2804 expect(store.getAt(14).get('taskId')).toBe(110);\r
2805 expect(store.getAt(15).get('taskId')).toBe(111);\r
2806\r
2807 store.on({\r
2808 sort: function() {\r
2809 expect(store.getAt(0).get('taskId')).toBe(113);\r
2810 expect(store.getAt(1).get('taskId')).toBe(114);\r
2811 expect(store.getAt(2).get('taskId')).toBe(112);\r
2812 expect(store.getAt(3).get('taskId')).toBe(115);\r
2813\r
2814 // Ext Grid: Single-level Grouping\r
2815 expect(store.getAt(4).get('taskId')).toBe(121);\r
2816 expect(store.getAt(5).get('taskId')).toBe(103);\r
2817 expect(store.getAt(6).get('taskId')).toBe(101);\r
2818 expect(store.getAt(7).get('taskId')).toBe(102);\r
2819 expect(store.getAt(8).get('taskId')).toBe(104);\r
2820\r
2821 expect(store.getAt(9).get('taskId')).toBe(105);\r
2822 expect(store.getAt(10).get('taskId')).toBe(106);\r
2823 expect(store.getAt(11).get('taskId')).toBe(108);\r
2824 expect(store.getAt(12).get('taskId')).toBe(109);\r
2825 expect(store.getAt(13).get('taskId')).toBe(107);\r
2826 expect(store.getAt(14).get('taskId')).toBe(111);\r
2827 expect(store.getAt(15).get('taskId')).toBe(110);\r
2828 sorted++;\r
2829 } \r
2830 });\r
2831 // This should switch the order within the two groups.\r
2832 // This becomes the primary sorter instead of evilness\r
2833 // IMPORTANT: The age field has a sortType* function which flips the value to negative.\r
2834 // So to sort by age,ASC, we specify DESC here.\r
2835 store.sort('estimate', undefined, 'replace');\r
2836 expect(sorted).toBe(1);\r
2837 });\r
2838 });\r
2839 \r
2840 describe("filters", function() {\r
2841 function makeFilter(prop, value) {\r
2842 return {\r
2843 property: prop,\r
2844 value: value\r
2845 };\r
2846 }\r
2847 \r
2848 it("should pass along filters if remoteFilter: true", function() {\r
2849 store.setRemoteFilter(true);\r
2850 store.getFilters().add(makeFilter('email', 'ed@sencha.com'));\r
2851 store.getFilters().add(makeFilter('evilness', 100));\r
2852 store.load();\r
2853 var filters = getOperation().getFilters();\r
2854 expect(filters[0].getProperty()).toBe('email');\r
2855 expect(filters[0].getValue()).toBe('ed@sencha.com');\r
2856 \r
2857 expect(filters[1].getProperty()).toBe('evilness');\r
2858 expect(filters[1].getValue()).toBe(100);\r
2859 });\r
2860 \r
2861 it("should not pass filters if there are none", function() {\r
2862 store.setRemoteFilter(true);\r
2863 store.load();\r
2864 expect(getOperation().getFilters()).toBeUndefined();\r
2865 });\r
2866 \r
2867 it("should not pass filters if remoteFilter: false", function() {\r
2868 store.setRemoteFilter(false);\r
2869 store.getFilters().add(makeFilter('email', 'ed@sencha.com'));\r
2870 store.getFilters().add(makeFilter('evilness', 100));\r
2871 store.load();\r
2872 expect(getOperation().getFilters()).toBeUndefined();\r
2873 });\r
2874 });\r
2875 \r
2876 describe("params", function() {\r
2877 it("should pass along params", function() {\r
2878 store.load({\r
2879 params: {\r
2880 foo: 'bar'\r
2881 }\r
2882 });\r
2883 expect(getOperation().getParams()).toEqual({\r
2884 foo: 'bar'\r
2885 });\r
2886 });\r
2887 });\r
2888 \r
2889 describe("paging", function() {\r
2890 describe("page", function() {\r
2891 it("should use the passed page", function() {\r
2892 store.load({\r
2893 page: 7\r
2894 });\r
2895 expect(getOperation().getPage()).toBe(7);\r
2896 });\r
2897\r
2898 it("should set the store currentPage", function() {\r
2899 store.load({\r
2900 page: 7\r
2901 });\r
2902 expect(store.currentPage).toBe(7);\r
2903 });\r
2904 \r
2905 it("should default to the current page in the store", function() {\r
2906 store.load();\r
2907 expect(getOperation().getPage()).toBe(store.currentPage);\r
2908 });\r
2909 });\r
2910 \r
2911 describe("start", function() {\r
2912 it("should use the passed start", function() {\r
2913 store.load({\r
2914 start: 100\r
2915 });\r
2916 expect(getOperation().getStart()).toBe(100);\r
2917 });\r
2918 \r
2919 it("should calculate the start based on the page & pageSize", function() {\r
2920 store.setPageSize(50);\r
2921 store.currentPage = 13;\r
2922 store.load();\r
2923 expect(getOperation().getStart()).toBe(600);\r
2924 });\r
2925 });\r
2926 \r
2927 describe("limit", function() {\r
2928 it("should used the passed limit", function() {\r
2929 store.load({\r
2930 limit: 12\r
2931 });\r
2932 expect(getOperation().getLimit()).toBe(12);\r
2933 });\r
2934 \r
2935 it("should default to the page size", function() {\r
2936 store.load();\r
2937 expect(getOperation().getLimit()).toBe(store.getPageSize());\r
2938 });\r
2939 });\r
2940 \r
2941 describe("with paging disabled", function() {\r
2942 beforeEach(function() {\r
2943 store.setPageSize(0);\r
2944 });\r
2945 \r
2946 describe("page", function() {\r
2947 it("should use the passed page", function() {\r
2948 store.load({\r
2949 page: 7\r
2950 });\r
2951 expect(getOperation().getPage()).toBe(7);\r
2952 });\r
2953\r
2954 it("should not default a page param", function() {\r
2955 store.load();\r
2956 expect(getOperation().getPage()).toBeUndefined();\r
2957 });\r
2958 });\r
2959\r
2960 describe("start", function() {\r
2961 it("should use the passed start", function() {\r
2962 store.load({\r
2963 start: 100\r
2964 });\r
2965 expect(getOperation().getStart()).toBe(100);\r
2966 });\r
2967\r
2968 it("should not default the start", function() {\r
2969 store.load();\r
2970 expect(getOperation().getStart()).toBeUndefined();\r
2971 });\r
2972 });\r
2973\r
2974 describe("limit", function() {\r
2975 it("should used the passed limit", function() {\r
2976 store.load({\r
2977 limit: 12\r
2978 });\r
2979 expect(getOperation().getLimit()).toBe(12);\r
2980 });\r
2981\r
2982 it("should not default the limit", function() {\r
2983 store.load();\r
2984 expect(getOperation().getLimit()).toBeUndefined();\r
2985 });\r
2986 });\r
2987 });\r
2988 });\r
2989 });\r
2990 \r
2991 describe("isLoading", function() {\r
2992 it("should not be loading by default", function() {\r
2993 expect(store.isLoading()).toBe(false);\r
2994 });\r
2995 \r
2996 it("should be loading once a remote request is triggered", function() {\r
2997 store.load();\r
2998 expect(store.isLoading()).toBe(true);\r
2999 });\r
3000 \r
3001 it("should not be loading when the server returns a successful response", function() {\r
3002 store.load();\r
3003 completeWithData(successData);\r
3004 expect(store.isLoading()).toBe(false);\r
3005 });\r
3006 \r
3007 it("should not be loading when the server returns a failed response", function() {\r
3008 store.load();\r
3009 completeWithData({\r
3010 success: false\r
3011 });\r
3012 expect(store.isLoading()).toBe(false);\r
3013 });\r
3014 });\r
3015 \r
3016 describe("callbacks", function() {\r
3017 var spy;\r
3018 beforeEach(function() {\r
3019 spy = jasmine.createSpy();\r
3020 });\r
3021 \r
3022 describe("function paramter", function() {\r
3023 it("should accept a function", function() {\r
3024 store.load(spy);\r
3025 completeWithData(successData);\r
3026 expect(spy.callCount).toBe(1);\r
3027 });\r
3028 \r
3029 it("should default the scope to the store", function() {\r
3030 store.load(spy);\r
3031 completeWithData(successData);\r
3032 expect(spy.mostRecentCall.object).toBe(store);\r
3033 });\r
3034 });\r
3035 \r
3036 describe("object paramter", function() {\r
3037 it("should take a callback parameter", function() {\r
3038 store.load({\r
3039 callback: spy\r
3040 });\r
3041 completeWithData(successData);\r
3042 expect(spy.callCount).toBe(1);\r
3043 });\r
3044 \r
3045 it("should use a passed scope", function() {\r
3046 store.load({\r
3047 callback: spy,\r
3048 scope: fakeScope\r
3049 });\r
3050 completeWithData(successData);\r
3051 expect(spy.mostRecentCall.object).toBe(fakeScope);\r
3052 });\r
3053 \r
3054 it("should default the scope to the store", function() {\r
3055 store.load({\r
3056 callback: spy\r
3057 });\r
3058 completeWithData(successData);\r
3059 expect(spy.mostRecentCall.object).toBe(store);\r
3060 });\r
3061 });\r
3062 \r
3063 describe("arguments", function() {\r
3064 var proxySpy;\r
3065 \r
3066 beforeEach(function() {\r
3067 proxySpy = spyOn(store.getProxy(), 'read').andCallThrough();\r
3068 });\r
3069 \r
3070 describe("on success", function() {\r
3071 it("should pass the records, the operation and success status", function() {\r
3072 store.load(spy);\r
3073 completeWithData({\r
3074 success: true,\r
3075 data: [{\r
3076 email: 'user1@sencha.com'\r
3077 }, {\r
3078 email: 'user2@sencha.com'\r
3079 }]\r
3080 });\r
3081 var args = spy.mostRecentCall.args;\r
3082 expect(args[0][0].get('email')).toBe('user1@sencha.com');\r
3083 expect(args[0][1].get('email')).toBe('user2@sencha.com');\r
3084 expect(args[1]).toBe(proxySpy.mostRecentCall.args[0]);\r
3085 expect(args[2]).toBe(true);\r
3086 });\r
3087 });\r
3088 \r
3089 describe("on failure", function() {\r
3090 it("should pass empty records, the operation and success status", function() {\r
3091 store.load(spy);\r
3092 completeWithData({\r
3093 success: false\r
3094 });\r
3095 var args = spy.mostRecentCall.args;\r
3096 expect(args[0]).toEqual([]);\r
3097 expect(args[1]).toBe(proxySpy.mostRecentCall.args[0]);\r
3098 expect(args[2]).toBe(false);\r
3099 });\r
3100 });\r
3101 });\r
3102 \r
3103 describe("when the callback is triggered", function() {\r
3104 it("should not have the store loading", function() {\r
3105 var loading;\r
3106 store.load(function() {\r
3107 loading = store.isLoading();\r
3108 });\r
3109 completeWithData(successData);\r
3110 expect(loading).toBe(false);\r
3111 });\r
3112 \r
3113 it("should have populated the store", function() {\r
3114 var count;\r
3115 store.load(function() {\r
3116 count = store.getCount();\r
3117 });\r
3118 completeWithData(successData);\r
3119 expect(count).toBe(1);\r
3120 });\r
3121 \r
3122 it("should fire after the load event", function() {\r
3123 var eventFired,\r
3124 loadSpy = jasmine.createSpy();\r
3125 \r
3126 store.on('load', loadSpy);\r
3127 store.load(function() {\r
3128 eventFired = loadSpy.callCount === 1;\r
3129 });\r
3130 completeWithData(successData);\r
3131 expect(eventFired).toBe(true);\r
3132 });\r
3133 });\r
3134 });\r
3135 \r
3136 describe("events", function() {\r
3137 var spy, proxySpy;\r
3138 \r
3139 beforeEach(function() {\r
3140 spy = jasmine.createSpy();\r
3141 proxySpy = spyOn(store.getProxy(), 'read').andCallThrough();\r
3142 });\r
3143 \r
3144 describe("beforeload", function() {\r
3145 it("should fire the beforeload event", function() {\r
3146 store.on('beforeload', spy);\r
3147 store.load();\r
3148 expect(spy.callCount).toBe(1);\r
3149 });\r
3150 \r
3151 it("should pass the store and the operation", function() {\r
3152 store.on('beforeload', spy);\r
3153 store.load();\r
3154 var args = spy.mostRecentCall.args;\r
3155 expect(args[0]).toBe(store);\r
3156 expect(args[1] instanceof Ext.data.operation.Read).toBe(true);\r
3157 });\r
3158 \r
3159 it("should not be loading when the beforeload event is fired", function() {\r
3160 var loading;\r
3161 store.on('beforeload', function() {\r
3162 loading = store.isLoading();\r
3163 });\r
3164 store.load();\r
3165 expect(loading).toBe(false);\r
3166 });\r
3167 \r
3168 it("should not continue with the load if false is returned", function() {\r
3169 store.on('beforeload', function() {\r
3170 return false;\r
3171 });\r
3172 store.load();\r
3173 expect(store.isLoading()).toBe(false);\r
3174 expect(proxySpy).not.toHaveBeenCalled();\r
3175 });\r
3176 });\r
3177 \r
3178 describe("load", function() {\r
3179 describe("on success", function() {\r
3180 it("should fire with the store, records and the success param", function() {\r
3181 store.on('load', spy);\r
3182 store.load();\r
3183 completeWithData(successData);\r
3184 var args = spy.mostRecentCall.args;\r
3185 expect(args[0]).toBe(store);\r
3186 expect(args[1][0].get('email')).toBe('foo@sencha.com');\r
3187 expect(args[2]).toBe(true);\r
3188 });\r
3189 });\r
3190 \r
3191 describe("on failure", function() {\r
3192 it("should fire with the store, an empty record array and the success param", function() {\r
3193 store.on('load', spy);\r
3194 store.load();\r
3195 completeWithData({\r
3196 success: false\r
3197 });\r
3198 var args = spy.mostRecentCall.args;\r
3199 expect(args[0]).toBe(store);\r
3200 expect(args[1]).toEqual([]);\r
3201 expect(args[2]).toBe(false);\r
3202 });\r
3203 });\r
3204 \r
3205 describe("when the event is triggered", function() {\r
3206 it("should not be loading", function() {\r
3207 var loading;\r
3208 store.on('load', function() {\r
3209 loading = store.isLoading();\r
3210 });\r
3211 store.load();\r
3212 completeWithData(successData);\r
3213 expect(loading).toBe(false);\r
3214 });\r
3215 \r
3216 it("should be populated with records", function() {\r
3217 var count;\r
3218 store.on('load', function() {\r
3219 count = store.getCount();\r
3220 });\r
3221 store.load();\r
3222 completeWithData(successData);\r
3223 expect(count).toBe(1);\r
3224 });\r
3225 });\r
3226 });\r
3227 });\r
3228 \r
3229 describe("after the load completes", function() { \r
3230 it("should clear existing records by default", function() {\r
3231 addStoreData();\r
3232 store.load();\r
3233 completeWithData(successData);\r
3234 expect(store.first().get('email')).toBe('foo@sencha.com');\r
3235 expect(store.getCount()).toBe(1);\r
3236 });\r
3237 \r
3238 it("should append records to the end when using append: true", function() {\r
3239 addStoreData();\r
3240 store.load({\r
3241 addRecords: true\r
3242 });\r
3243 completeWithData(successData);\r
3244 expect(store.last().get('email')).toBe('foo@sencha.com');\r
3245 expect(store.getCount()).toBe(5);\r
3246 });\r
3247 \r
3248 describe("events", function() {\r
3249 var spy;\r
3250 beforeEach(function() {\r
3251 spy = jasmine.createSpy();\r
3252 });\r
3253 \r
3254 it("should fire a datachanged event", function() {\r
3255 store.on('datachanged', spy);\r
3256 store.load();\r
3257 completeWithData(successData);\r
3258 expect(spy.callCount).toBe(1);\r
3259 expect(spy.mostRecentCall.args[0]).toBe(store);\r
3260 });\r
3261 \r
3262 it("should fire a refresh event", function() {\r
3263 store.on('refresh', spy);\r
3264 store.load();\r
3265 completeWithData(successData);\r
3266 expect(spy.callCount).toBe(1);\r
3267 expect(spy.mostRecentCall.args[0]).toBe(store);\r
3268 });\r
3269 \r
3270 it("should not fire any add events", function() {\r
3271 store.on('add', spy);\r
3272 store.load();\r
3273 completeWithData(successData);\r
3274 expect(spy).not.toHaveBeenCalled();\r
3275 });\r
3276 \r
3277 it("should not fire any remove event when records are being cleared", function() {\r
3278 store.on('remove', spy);\r
3279 store.load();\r
3280 completeWithData(successData);\r
3281 expect(spy).not.toHaveBeenCalled();\r
3282 });\r
3283 });\r
3284 });\r
3285 \r
3286 describe("reload", function() {\r
3287 it("should be able to be called if load was never called", function() {\r
3288 var spy = jasmine.createSpy();\r
3289 \r
3290 store.reload({\r
3291 callback: spy\r
3292 });\r
3293 completeWithData(successData);\r
3294 expect(spy.callCount).toBe(1);\r
3295 });\r
3296 \r
3297 it("should use the params from the last load", function() {\r
3298 store.load({\r
3299 params: {\r
3300 foo: 'bar'\r
3301 }\r
3302 });\r
3303 completeWithData(successData);\r
3304 var spy = spyOn(store.getProxy(), 'read').andReturn();\r
3305 store.reload();\r
3306 expect(spy.mostRecentCall.args[0].getParams()).toEqual({\r
3307 foo: 'bar'\r
3308 });\r
3309 });\r
3310 });\r
3311 });\r
3312 });\r
3313 \r
3314 describe("paging", function() {\r
3315 var successData, proxySpy;\r
3316 \r
3317 function getOperation() {\r
3318 return proxySpy.mostRecentCall.args[0];\r
3319 }\r
3320\r
3321 beforeEach(function() {\r
3322 successData = {\r
3323 success: true,\r
3324 data: [{\r
3325 email: 'foo@sencha.com'\r
3326 }]\r
3327 };\r
3328 createStore({\r
3329 proxy: {\r
3330 type: 'ajax',\r
3331 url: 'foo',\r
3332 reader: {\r
3333 type: 'json',\r
3334 successProperty: 'success',\r
3335 rootProperty: 'data'\r
3336 }\r
3337 }\r
3338 });\r
3339 proxySpy = spyOn(store.getProxy(), 'read').andCallThrough();\r
3340 });\r
3341 \r
3342 it("should default the current page to 1", function() {\r
3343 expect(store.currentPage).toBe(1);\r
3344 });\r
3345 \r
3346 describe("previousPage", function() {\r
3347 it("should call loadPage with the current page - 1 and pass the options", function() {\r
3348 var o = {};\r
3349 spyOn(store, 'loadPage').andReturn();\r
3350 store.currentPage = 9;\r
3351 store.previousPage(o);\r
3352 expect(store.loadPage).toHaveBeenCalledWith(8, o);\r
3353 expect(store.loadPage.callCount).toBe(1);\r
3354 });\r
3355 });\r
3356 \r
3357 describe("nextPage", function() {\r
3358 it("should call loadPage with the current page + 1 and pass the options", function() {\r
3359 var o = {};\r
3360 spyOn(store, 'loadPage').andReturn();\r
3361 store.currentPage = 3;\r
3362 store.nextPage(o);\r
3363 expect(store.loadPage).toHaveBeenCalledWith(4, o);\r
3364 expect(store.loadPage.callCount).toBe(1);\r
3365 });\r
3366 });\r
3367 \r
3368 describe("params passed to the proxy", function() {\r
3369 describe("page", function() {\r
3370 it("should favour a passed page param", function() {\r
3371 store.loadPage(10, {\r
3372 page: 1234\r
3373 });\r
3374 expect(getOperation().getPage()).toBe(1234);\r
3375 });\r
3376 \r
3377 it("should use the passed the page", function() {\r
3378 store.loadPage(10);\r
3379 expect(getOperation().getPage()).toBe(10);\r
3380 });\r
3381 });\r
3382 \r
3383 describe("start", function() {\r
3384 it("should favour a passed start param", function() {\r
3385 store.loadPage(3, {\r
3386 start: 789\r
3387 });\r
3388 expect(getOperation().getStart()).toBe(789);\r
3389 });\r
3390 \r
3391 it("should calculate the start based off the pageSize", function() {\r
3392 store.loadPage(2);\r
3393 expect(getOperation().getStart()).toBe(25);\r
3394 });\r
3395 });\r
3396 \r
3397 describe("limit", function() {\r
3398 it("should favour a passed limit param", function() {\r
3399 store.loadPage(3, {\r
3400 limit: 456\r
3401 });\r
3402 expect(getOperation().getLimit()).toBe(456);\r
3403 });\r
3404 \r
3405 it("should set the limit to be the pageSize", function() {\r
3406 store.loadPage(2);\r
3407 expect(getOperation().getLimit()).toBe(store.getPageSize());\r
3408 });\r
3409 });\r
3410 \r
3411 describe("other params", function() {\r
3412 it("should pass the params object", function() {\r
3413 store.loadPage(1, {\r
3414 params: {\r
3415 custom: true\r
3416 }\r
3417 });\r
3418 expect(getOperation().getParams()).toEqual({\r
3419 custom: true\r
3420 });\r
3421 });\r
3422 });\r
3423 });\r
3424 \r
3425 describe("after load", function() {\r
3426 it("should set the currentPage", function() {\r
3427 store.loadPage(12);\r
3428 expect(store.currentPage).toBe(12);\r
3429 });\r
3430 \r
3431 it("should clear existing records when using clearOnPageLoad: true", function() {\r
3432 store.setClearOnPageLoad(true);\r
3433 store.setPageSize(1);\r
3434 store.loadPage(1);\r
3435 completeWithData({\r
3436 success: true,\r
3437 data: [{\r
3438 email: 'user1@sencha.com'\r
3439 }]\r
3440 });\r
3441 store.loadPage(2);\r
3442 completeWithData({\r
3443 success: true,\r
3444 data: [{\r
3445 email: 'user2@sencha.com'\r
3446 }]\r
3447 });\r
3448 expect(store.getCount()).toBe(1);\r
3449 expect(store.indexOfId('user1@sencha.com')).toBe(-1);\r
3450 expect(store.indexOfId('user2@sencha.com')).toBe(0);\r
3451 });\r
3452 \r
3453 it("should not clear existing records when using clearOnPageLoad: false", function() {\r
3454 store.setClearOnPageLoad(false);\r
3455 store.setPageSize(1);\r
3456 store.loadPage(1);\r
3457 completeWithData({\r
3458 success: true,\r
3459 data: [{\r
3460 email: 'user1@sencha.com'\r
3461 }]\r
3462 });\r
3463 store.loadPage(2);\r
3464 completeWithData({\r
3465 success: true,\r
3466 data: [{\r
3467 email: 'user2@sencha.com'\r
3468 }]\r
3469 });\r
3470 expect(store.getCount()).toBe(2);\r
3471 expect(store.indexOfId('user1@sencha.com')).toBe(0);\r
3472 expect(store.indexOfId('user2@sencha.com')).toBe(1);\r
3473 });\r
3474 });\r
3475 });\r
3476 \r
3477 describe("sorting", function() {\r
3478 function expectSorter(sorter, property, direction) {\r
3479 expect(sorter.getProperty()).toBe(property);\r
3480 expect(sorter.getDirection()).toBe(direction);\r
3481 }\r
3482\r
3483 describe("the sorter collection", function() {\r
3484 it("should be an instance of Ext.util.SorterCollection", function() {\r
3485 createStore();\r
3486 expect(store.getSorters() instanceof Ext.util.SorterCollection).toBe(true);\r
3487 });\r
3488\r
3489 it("should be empty by default", function() {\r
3490 createStore();\r
3491 expect(store.getSorters().getCount()).toBe(0);\r
3492 });\r
3493\r
3494 it("should add any sorters passed in the constructor", function() {\r
3495 createStore({\r
3496 sorters: [{\r
3497 property: 'foo',\r
3498 direction: 'DESC'\r
3499 }, {\r
3500 property: 'bar',\r
3501 direction: 'ASC'\r
3502 }]\r
3503 });\r
3504 expectSorter(store.getSorters().getAt(0), 'foo', 'DESC');\r
3505 expectSorter(store.getSorters().getAt(1), 'bar', 'ASC');\r
3506 });\r
3507\r
3508 it("should not set the rootProperty as data on the sorter collection", function() {\r
3509 createStore();\r
3510 expect(store.getSorters().getRootProperty()).not.toBe('data');\r
3511 });\r
3512\r
3513 it("should not trigger events when asking for the collection and it has not been created", function () {\r
3514 var spy = jasmine.createSpy();\r
3515 createStore({\r
3516 listeners: {\r
3517 beforesort: spy,\r
3518 sort: spy\r
3519 }\r
3520 });\r
3521 store.getSorters();\r
3522 expect(spy).not.toHaveBeenCalled();\r
3523 });\r
3524 });\r
3525\r
3526 describe("sort method", function() {\r
3527 beforeEach(function() {\r
3528 createStore();\r
3529 });\r
3530 \r
3531 it("should accept a field name, default direction to ASC", function() {\r
3532 store.sort('name');\r
3533 expectSorter(store.getSorters().getAt(0), 'name', 'ASC');\r
3534 });\r
3535 \r
3536 it("should accept a field nameand direction", function() {\r
3537 store.sort('name', 'DESC');\r
3538 expectSorter(store.getSorters().getAt(0), 'name', 'DESC');\r
3539 });\r
3540 \r
3541 it("should toggle the direction if we pass a string name and no direction for an existing sorter", function() {\r
3542 store.sort('name');\r
3543 store.sort('name');\r
3544 expectSorter(store.getSorters().getAt(0), 'name', 'DESC');\r
3545 });\r
3546 \r
3547 it("should clear any existing sorters", function() {\r
3548 store.sort('name');\r
3549 store.sort('evilness');\r
3550 expect(store.getSorters().getCount()).toBe(1);\r
3551 expectSorter(store.getSorters().getAt(0), 'evilness', 'ASC');\r
3552 });\r
3553\r
3554 it("should not throw an error when the store has no model", function() {\r
3555 store.destroy();\r
3556 \r
3557 // Silence console warning\r
3558 spyOn(Ext.log, 'warn');\r
3559 \r
3560 store = new Ext.data.Store();\r
3561 expect(function() {\r
3562 store.sort('something', 'ASC');\r
3563 }).not.toThrow();\r
3564 });\r
3565 });\r
3566 \r
3567 describe("isSorted", function() {\r
3568 beforeEach(function() {\r
3569 createStore();\r
3570 });\r
3571 \r
3572 it("should default to false", function() {\r
3573 expect(store.isSorted()).toBe(false);\r
3574 });\r
3575 \r
3576 it("should return true when sorters are added", function() {\r
3577 store.sort('evilness');\r
3578 expect(store.isSorted()).toBe(true);\r
3579 });\r
3580 \r
3581 it("should return false when all sorters are removed", function() {\r
3582 store.sort('evilness');\r
3583 store.getSorters().remove('evilness');\r
3584 expect(store.isSorted()).toBe(false);\r
3585 });\r
3586 \r
3587 it("should return true if there are groupers and no sorters", function() {\r
3588 store.setGrouper({\r
3589 property: 'group'\r
3590 });\r
3591 expect(store.isSorted()).toBe(true);\r
3592 });\r
3593 });\r
3594\r
3595 describe("setRemoteSort", function() {\r
3596 describe("setting to true", function() {\r
3597 beforeEach(function() {\r
3598 createStore({\r
3599 remoteSort: false\r
3600 });\r
3601 addStoreData();\r
3602 spyOn(store.getProxy(), 'read').andCallThrough();\r
3603 });\r
3604\r
3605 describe("with sorters", function() {\r
3606 it("should not trigger a load and not alter the sort order", function() {\r
3607 store.getSorters().add('name');\r
3608 store.setRemoteSort(true);\r
3609 expect(store.getProxy().read).not.toHaveBeenCalled();\r
3610 expect(store.getAt(0)).toBe(aaronRec);\r
3611 expect(store.getAt(1)).toBe(abeRec);\r
3612 expect(store.getAt(2)).toBe(edRec);\r
3613 expect(store.getAt(3)).toBe(tommyRec);\r
3614 });\r
3615\r
3616 it("should use the order provided by the server", function() {\r
3617 store.getSorters().add('name');\r
3618 store.setRemoteSort(true);\r
3619 store.load();\r
3620 completeWithData([{\r
3621 name: 'Z'\r
3622 }, {\r
3623 name: 'Y'\r
3624 }, {\r
3625 name: 'A'\r
3626 }]);\r
3627 expect(store.getAt(0).get('name')).toBe('Z');\r
3628 expect(store.getAt(1).get('name')).toBe('Y');\r
3629 expect(store.getAt(2).get('name')).toBe('A');\r
3630 });\r
3631 });\r
3632\r
3633 describe("with no sorters", function() {\r
3634 it("should not trigger a load", function() {\r
3635 store.setRemoteSort(true);\r
3636 expect(store.getProxy().read).not.toHaveBeenCalled();\r
3637 });\r
3638 }); \r
3639 });\r
3640\r
3641 describe("setting to false", function() {\r
3642 describe("with sorters", function() {\r
3643 it("should sort the local data", function() {\r
3644 createStore({\r
3645 remoteSort: true,\r
3646 sorters: 'name'\r
3647 });\r
3648 addStoreData();\r
3649\r
3650 store.setRemoteSort(false);\r
3651 expect(store.getAt(0)).toBe(aaronRec);\r
3652 expect(store.getAt(1)).toBe(abeRec);\r
3653 expect(store.getAt(2)).toBe(edRec);\r
3654 expect(store.getAt(3)).toBe(tommyRec);\r
3655 });\r
3656 });\r
3657\r
3658 describe("with no sorters", function() {\r
3659 it("should not sort the data", function() {\r
3660 createStore({\r
3661 remoteSort: true\r
3662 });\r
3663 addStoreData();\r
3664 store.setRemoteSort(false);\r
3665 expect(store.getAt(0)).toBe(edRec);\r
3666 expect(store.getAt(1)).toBe(abeRec);\r
3667 expect(store.getAt(2)).toBe(aaronRec);\r
3668 expect(store.getAt(3)).toBe(tommyRec);\r
3669 });\r
3670 }); \r
3671 });\r
3672 });\r
3673 \r
3674 describe("local", function() {\r
3675 describe("during construction", function() {\r
3676 it("should sort an initial data set", function() {\r
3677 createStore({\r
3678 remoteSort: false,\r
3679 sorters: [{\r
3680 property: 'email'\r
3681 }],\r
3682 data: [edRaw, tommyRaw, aaronRaw, abeRaw]\r
3683 });\r
3684 expect(store.first().get('email')).toBe('aaron@sencha.com');\r
3685 });\r
3686 });\r
3687 \r
3688 describe("dynamic sorters", function() {\r
3689 beforeEach(function() {\r
3690 createStore({\r
3691 remoteSort: false\r
3692 });\r
3693 addStoreData();\r
3694 });\r
3695 it("should sort the dataset when adding sorters", function() {\r
3696 store.sort('email');\r
3697 expect(store.first().get('email')).toBe('aaron@sencha.com');\r
3698 });\r
3699 \r
3700 it("should be able to use multiple sorters", function() {\r
3701 store.getSorters().add({\r
3702 property: 'group'\r
3703 }, {\r
3704 property: 'evilness',\r
3705 direction: 'DESC'\r
3706 });\r
3707 expect(store.first().get('email')).toBe('abe@sencha.com');\r
3708 });\r
3709 \r
3710 it("should trigger a sort when removing a sorter that is not the final sorter", function() {\r
3711 store.getSorters().add({\r
3712 property: 'group'\r
3713 }, {\r
3714 property: 'evilness',\r
3715 direction: 'DESC'\r
3716 });\r
3717 store.getSorters().remove('group');\r
3718 expect(store.first().get('email')).toBe('ed@sencha.com');\r
3719 });\r
3720 \r
3721 it("should ignore invalid fields", function() {\r
3722 //first we'll sort by name to give some reference sorting\r
3723 store.sort('name', 'ASC');\r
3724\r
3725 //this field does not exist\r
3726 store.sort('someUnknownField');\r
3727\r
3728 //make sure the original sorting was preserved\r
3729 expect(store.getAt(0).get('name')).toBe('Aaron Conran');\r
3730 expect(store.getAt(1).get('name')).toBe('Abe Elias');\r
3731 expect(store.getAt(2).get('name')).toBe('Ed Spencer');\r
3732 expect(store.getAt(3).get('name')).toBe('Tommy Maintz');\r
3733 });\r
3734 \r
3735 describe("sortType", function(){\r
3736 it("should not pass the default sortType for the field", function(){\r
3737 store.sort('name', 'ASC');\r
3738 var sorter = store.getSorters().first();\r
3739 expect(sorter.getTransform()).toBe(Ext.data.SortTypes.asUCString);\r
3740 }); \r
3741 \r
3742 it("should pass any custom sort for the field", function(){\r
3743 store.sort('age', 'ASC');\r
3744 var sorter = store.getSorters().first();\r
3745 expect(sorter.getTransform()).toBe(customSort);\r
3746 }); \r
3747 \r
3748 it("should not apply a transform if the field doesn't exist", function(){\r
3749 store.sort('someUnknownField');\r
3750 var sorter = store.getSorters().first();\r
3751 expect(sorter.getTransform()).toBeNull();\r
3752 }); \r
3753 });\r
3754 \r
3755 describe("with loadData", function() {\r
3756 it("should sort data", function() {\r
3757 store.sort('email');\r
3758 store.loadData([tommyRaw, abeRaw, edRaw, aaronRaw]);\r
3759 expect(store.first().get('email')).toBe('aaron@sencha.com');\r
3760 });\r
3761 \r
3762 it("should not fire extra datachanged/refresh events", function() {\r
3763 var spy = jasmine.createSpy();\r
3764 store.sort('email');\r
3765 store.on('refresh', spy);\r
3766 store.on('datachanged', spy);\r
3767 store.loadData([tommyRaw, abeRaw, edRaw, aaronRaw]);\r
3768 // Once for refresh, once for datachanged\r
3769 expect(spy.callCount).toBe(2);\r
3770 });\r
3771 \r
3772 it("should not sort the data with sortOnLoad: false", function() {\r
3773 store.sort('email');\r
3774 store.setSortOnLoad(false);\r
3775 store.loadData([tommyRaw, abeRaw, edRaw, aaronRaw]);\r
3776 expect(store.first().get('email')).toBe('tommy@sencha.com');\r
3777 });\r
3778 });\r
3779 \r
3780 describe("with add", function() {\r
3781 it("should sort data", function() {\r
3782 store.sort('email');\r
3783 store.add({\r
3784 email: 'aaa@sencha.com'\r
3785 });\r
3786 expect(store.first().get('email')).toBe('aaa@sencha.com');\r
3787 });\r
3788 \r
3789 it("should not fire extra datachanged events", function() {\r
3790 var spy = jasmine.createSpy();\r
3791 store.sort('email');\r
3792 store.on('datachanged', spy);\r
3793 store.add({\r
3794 email: 'aaa@sencha.com'\r
3795 });\r
3796 expect(spy.callCount).toBe(1);\r
3797 });\r
3798 });\r
3799 \r
3800 describe("when the field changes", function() {\r
3801 it("should move the record to the correct place when the sorted field is modified", function() {\r
3802 store.sort('evilness', 'DESC');\r
3803 tommyRec.set('evilness', 1234);\r
3804 expect(store.indexOf(tommyRec)).toBe(0);\r
3805 });\r
3806 });\r
3807\r
3808 describe("the sort method", function() {\r
3809 it("should sort the data after toggling an existing sorter", function() {\r
3810 store.getSorters().add('name');\r
3811 var data = store.getRange();\r
3812 data.reverse();\r
3813 store.sort('name');\r
3814 // Toggled now\r
3815 expect(store.getRange()).toEqual(data);\r
3816 expect(store.getSorters().getCount()).toBe(1);\r
3817 });\r
3818\r
3819 it("shouldsort the data when adding a new sorter", function() {\r
3820 store.getSorters().add('group');\r
3821 store.sort('name', 'DESC');\r
3822 expect(store.getSorters().getCount()).toBe(1);\r
3823 expect(store.getAt(0)).toBe(tommyRec);\r
3824 expect(store.getAt(1)).toBe(edRec);\r
3825 expect(store.getAt(2)).toBe(abeRec);\r
3826 expect(store.getAt(3)).toBe(aaronRec);\r
3827 });\r
3828 });\r
3829 });\r
3830 });\r
3831 \r
3832 describe("remote", function() {\r
3833 describe("during construction", function() {\r
3834 it("should not trigger a load when creating with sorters", function() {\r
3835 var spy = spyOn(Ext.data.ProxyStore.prototype, 'load');\r
3836 createStore({\r
3837 remoteSort: true,\r
3838 sorters: [{\r
3839 property: 'evilness'\r
3840 }]\r
3841 });\r
3842 expect(spy).not.toHaveBeenCalled();\r
3843 });\r
3844 });\r
3845 \r
3846 describe("addSorted", function() {\r
3847 it("should insert the record into the correct position", function() {\r
3848 createStore({\r
3849 remoteSort: true\r
3850 });\r
3851 store.sort('email');\r
3852 store.loadData([aaronRaw, abeRaw, edRaw, tommyRaw]);\r
3853 store.addSorted(makeUser('aaz@sencha.com'));\r
3854 expect(store.getAt(1).get('email')).toBe('aaz@sencha.com');\r
3855 });\r
3856 });\r
3857 \r
3858 describe("modifying the sorters", function() {\r
3859 beforeEach(function() {\r
3860 createStore({\r
3861 remoteSort: true\r
3862 });\r
3863 });\r
3864 \r
3865 describe("the sorter collection", function() {\r
3866 it("should trigger a load when adding a sorter", function() {\r
3867 spyOn(store, 'load');\r
3868 store.getSorters().add('name');\r
3869 expect(store.load.callCount).toBe(1);\r
3870 });\r
3871\r
3872 it("should trigger a load when adding to an existing sorter", function() {\r
3873 store.getSorters().add('name');\r
3874 spyOn(store, 'load');\r
3875 store.getSorters().add('evilness');\r
3876 expect(store.load.callCount).toBe(1);\r
3877 });\r
3878\r
3879 it("should not trigger a load when removing the only sorter", function() {\r
3880 store.getSorters().add('name');\r
3881 spyOn(store, 'load');\r
3882 store.getSorters().remove('name');\r
3883 expect(store.load).not.toHaveBeenCalled();\r
3884 });\r
3885\r
3886 it("should trigger a load when removing a sorter and there are others remaining", function() {\r
3887 store.getSorters().add('name');\r
3888 store.getSorters().add('evilness');\r
3889 spyOn(store, 'load');\r
3890 store.getSorters().remove('name');\r
3891 expect(store.load.callCount).toBe(1);\r
3892 });\r
3893 });\r
3894 \r
3895 describe("the sort method", function() {\r
3896 it("should trigger a load without modifying the sorters if called with no params", function() {\r
3897 store.getSorters().add('name');\r
3898 spyOn(store, 'load');\r
3899 store.sort();\r
3900 expect(store.load.callCount).toBe(1);\r
3901 expect(store.getSorters().getCount()).toBe(1);\r
3902 });\r
3903\r
3904 it("should trigger a load when toggling an existing sorter", function() {\r
3905 store.getSorters().add('name');\r
3906 spyOn(store, 'load');\r
3907 store.sort('name');\r
3908 expect(store.load.callCount).toBe(1);\r
3909 expect(store.getSorters().getCount()).toBe(1);\r
3910 });\r
3911\r
3912 it("should trigger a load when adding a new sorter", function() {\r
3913 store.getSorters().add('name');\r
3914 spyOn(store, 'load');\r
3915 store.sort('age', 'DESC');\r
3916 expect(store.load.callCount).toBe(1);\r
3917 expect(store.getSorters().getCount()).toBe(1);\r
3918 });\r
3919 });\r
3920 });\r
3921 \r
3922 describe("store data", function() {\r
3923 it("should ignore sortOnLoad", function() {\r
3924 createStore({\r
3925 remoteSort: true,\r
3926 sorters: ['name'],\r
3927 sortOnLoad: false,\r
3928 proxy: {\r
3929 type: 'ajax',\r
3930 url: 'fakeurl'\r
3931 }\r
3932 }); \r
3933 store.load();\r
3934 completeWithData([{\r
3935 name: 'ZZZ'\r
3936 }, {\r
3937 name: 'AAA'\r
3938 }]);\r
3939 \r
3940 expect(store.first().get('name')).toBe('ZZZ');\r
3941 });\r
3942 \r
3943 it("should not sort the data when the store load has completed", function() {\r
3944 createStore({\r
3945 remoteSort: true,\r
3946 proxy: {\r
3947 type: 'ajax',\r
3948 url: 'fakeurl'\r
3949 }\r
3950 }); \r
3951 store.getSorters().add('name');\r
3952 completeWithData([{\r
3953 name: 'ZZZ'\r
3954 }, {\r
3955 name: 'AAA'\r
3956 }]);\r
3957 \r
3958 expect(store.first().get('name')).toBe('ZZZ');\r
3959 });\r
3960 \r
3961 it("should not sort the data when adding a record", function() {\r
3962 createStore({\r
3963 remoteSort: true,\r
3964 proxy: {\r
3965 type: 'ajax',\r
3966 url: 'fakeurl'\r
3967 }\r
3968 }); \r
3969 store.getSorters().add('name');\r
3970 addStoreData();\r
3971 expect(store.first().get('name')).toBe('Ed Spencer');\r
3972 });\r
3973 });\r
3974 });\r
3975 \r
3976 describe("events", function() {\r
3977 var spy;\r
3978 beforeEach(function() {\r
3979 createStore({\r
3980 proxy: 'memory',\r
3981 remoteSort: true\r
3982 });\r
3983 spy = jasmine.createSpy();\r
3984 });\r
3985\r
3986 it("should fire the sort event when adding a sorter to an empty collection", function() {\r
3987 store.on('sort', spy);\r
3988 store.getSorters().add('name');\r
3989 expect(spy.callCount).toBe(1);\r
3990 var args = spy.mostRecentCall.args;\r
3991 expect(args[0]).toBe(store);\r
3992 expect(args[1].length).toBe(1);\r
3993 expect(args[1][0].getProperty()).toBe('name');\r
3994 expect(args[1][0].getDirection()).toBe('ASC');\r
3995 });\r
3996\r
3997 it("should fire the sort event when adding a sorter to existing sorters", function() {\r
3998 store.getSorters().add('name');\r
3999 store.on('sort', spy);\r
4000 store.getSorters().add('evilness');\r
4001 expect(spy.callCount).toBe(1);\r
4002 var args = spy.mostRecentCall.args;\r
4003 expect(args[0]).toBe(store);\r
4004 expect(args[1].length).toBe(2);\r
4005 expect(args[1][0].getProperty()).toBe('name');\r
4006 expect(args[1][0].getDirection()).toBe('ASC');\r
4007 expect(args[1][1].getProperty()).toBe('evilness');\r
4008 expect(args[1][1].getDirection()).toBe('ASC');\r
4009 });\r
4010\r
4011 it("should fire when removing a sorter from existing sorters", function() {\r
4012 store.getSorters().add('name');\r
4013 store.getSorters().add('evilness');\r
4014 store.on('sort', spy);\r
4015 store.getSorters().remove('name');\r
4016 expect(spy.callCount).toBe(1);\r
4017 var args = spy.mostRecentCall.args;\r
4018 expect(args[0]).toBe(store);\r
4019 expect(args[1].length).toBe(1);\r
4020 expect(args[1][0].getProperty()).toBe('evilness');\r
4021 expect(args[1][0].getDirection()).toBe('ASC');\r
4022 });\r
4023\r
4024 it("should fire when removing the last sorter", function() {\r
4025 store.getSorters().add('name');\r
4026 store.on('sort', spy);\r
4027 store.getSorters().remove('name');\r
4028 expect(spy.callCount).toBe(1);\r
4029 var args = spy.mostRecentCall.args;\r
4030 expect(args[0]).toBe(store);\r
4031 expect(args[1].length).toBe(0);\r
4032 });\r
4033 \r
4034 describe("local", function() {\r
4035 beforeEach(function() {\r
4036 store.setRemoteSort(false);\r
4037 });\r
4038 \r
4039 describe("adding", function() {\r
4040 it("should fire the refresh event", function() {\r
4041 store.on('refresh', spy);\r
4042 store.getSorters().add('name');\r
4043 expect(spy.callCount).toBe(1);\r
4044 expect(spy.mostRecentCall.args[0]).toBe(store);\r
4045 });\r
4046\r
4047 it("should fire the datachanged event", function() {\r
4048 store.on('datachanged', spy);\r
4049 store.getSorters().add('name');\r
4050 expect(spy.callCount).toBe(1);\r
4051 expect(spy.mostRecentCall.args[0]).toBe(store);\r
4052 });\r
4053 });\r
4054 \r
4055 describe("removing", function() {\r
4056 it("should fire the refresh event when we remove the non-last sorter", function() {\r
4057 store.getSorters().add('name');\r
4058 store.getSorters().add('evilness');\r
4059 store.on('refresh', spy);\r
4060 store.getSorters().remove('name');\r
4061 expect(spy.callCount).toBe(1);\r
4062 expect(spy.mostRecentCall.args[0]).toBe(store);\r
4063 });\r
4064 \r
4065 it("should fire the datachanged event when we remove the non-last sorter", function() {\r
4066 store.getSorters().add('name');\r
4067 store.getSorters().add('evilness');\r
4068 store.on('datachanged', spy);\r
4069 store.getSorters().remove('name');\r
4070 expect(spy.callCount).toBe(1);\r
4071 expect(spy.mostRecentCall.args[0]).toBe(store);\r
4072 });\r
4073 \r
4074 \r
4075 it("should not fire either event if we remove the last sorter", function() {\r
4076 store.getSorters().add('name');\r
4077 store.on('refresh', spy);\r
4078 store.on('datachanged', spy);\r
4079 store.getSorters().remove('name');\r
4080 expect(spy).not.toHaveBeenCalled();\r
4081 });\r
4082 });\r
4083 });\r
4084\r
4085 describe("remote", function() {\r
4086\r
4087 // Remote loads synchronously from memory proxy, so refresh and datachanged events fire synchronously\r
4088 describe("adding", function() {\r
4089 it("should fire refresh when adding a sorter", function() {\r
4090 store.on('refresh', spy);\r
4091 store.sort('email');\r
4092 expect(spy.callCount).toBe(1);\r
4093 });\r
4094 \r
4095 it("should fire datachanged when adding a sorter", function() {\r
4096 store.on('datachanged', spy);\r
4097 store.sort('email');\r
4098 expect(spy.callCount).toBe(1);\r
4099 });\r
4100 });\r
4101 \r
4102 describe("removing", function() {\r
4103 it("should fire refresh when removing a the non-last sorter", function() {\r
4104 store.sort('email');\r
4105\r
4106 // MUST use append mode, otherwise store replaces the existing sorter, and the remove will not do anything\r
4107 store.sort('evilness', null, 'append');\r
4108 store.on('refresh', spy);\r
4109 store.getSorters().remove('email');\r
4110 expect(spy.callCount).toBe(1);\r
4111 });\r
4112 \r
4113 it("should fire datachanged when removing the non-last sorter", function() {\r
4114 store.sort('email');\r
4115\r
4116 // MUST use append mode, otherwise store replaces the existing sorter, and the remove will not do anythin\r
4117 store.sort('evilness', null, 'append');\r
4118 store.on('datachanged', spy);\r
4119 store.getSorters().remove('email');\r
4120 expect(spy.callCount).toBe(1);\r
4121 });\r
4122 });\r
4123 });\r
4124\r
4125 describe("remote async", function() {\r
4126 beforeEach(function() {\r
4127 createStore({\r
4128 remoteSort: true\r
4129 });\r
4130 spy = jasmine.createSpy();\r
4131 });\r
4132\r
4133 // Remote loads asynchronously from ajax proxy here, so refresh and datachaged events will not fire synchronously\r
4134 describe("adding", function() {\r
4135 it("should not fire refresh when adding a sorter", function() {\r
4136 store.on('refresh', spy);\r
4137 store.sort('email');\r
4138 expect(spy).not.toHaveBeenCalled();\r
4139 });\r
4140 \r
4141 it("should not fire datachanged when adding a sorter", function() {\r
4142 store.on('datachanged', spy);\r
4143 store.sort('email');\r
4144 expect(spy).not.toHaveBeenCalled();\r
4145 });\r
4146 });\r
4147 \r
4148 describe("removing", function() {\r
4149 it("should not fire refresh when removing a the non-last sorter", function() {\r
4150 store.sort('email');\r
4151 store.sort('evilness');\r
4152 store.on('refresh', spy);\r
4153 store.getSorters().remove('email');\r
4154 expect(spy).not.toHaveBeenCalled();\r
4155 });\r
4156 \r
4157 it("should not fire datachanged when removing the non-last sorter", function() {\r
4158 store.sort('email');\r
4159 store.sort('evilness');\r
4160 store.on('datachanged', spy);\r
4161 store.getSorters().remove('email');\r
4162 expect(spy).not.toHaveBeenCalled();\r
4163 });\r
4164 });\r
4165 });\r
4166 });\r
4167 });\r
4168\r
4169 describe('remote sorting and filtering, and callbacks therein', function() {\r
4170 var callSequence = 0,\r
4171 sortOrdinal,\r
4172 groupOrdinal,\r
4173 allCorrect;\r
4174 \r
4175 beforeEach(function() {\r
4176 // Create a store that kicks off an autoload request\r
4177 createStore({\r
4178 // Override the default in this test suite.\r
4179 // We are testing asynchronousness.\r
4180 asynchronousLoad: true,\r
4181 remoteSort: true,\r
4182 remoteFilter: true,\r
4183 listeners: {\r
4184 sort: function() {\r
4185 sortOrdinal = ++callSequence;\r
4186 },\r
4187 groupchange: function() {\r
4188 groupOrdinal = ++callSequence;\r
4189 }\r
4190 }\r
4191 });\r
4192 spyOn(store, 'load').andCallThrough();\r
4193 spyOn(store, 'flushLoad').andCallThrough();\r
4194 });\r
4195\r
4196 it('should not fire all the callbacks of superceded loads', function() {\r
4197\r
4198 // All these three operations will each kick off a load request\r
4199 store.group('group');\r
4200 store.sort('email');\r
4201 store.filter({\r
4202 property: 'old',\r
4203 value: true\r
4204 });\r
4205\r
4206 // There have been several load calls in this event handler.\r
4207 // Adding a grouper requests a load with a callback scheduled to fire the groupchange event.\r
4208 // Adding sorters requests a load with a callback scheduled to fire the sort event.\r
4209 // Adding filters requests a load\r
4210 expect(store.load.callCount).toBe(3);\r
4211 expect(store.flushLoad.callCount).toBe(0);\r
4212\r
4213 // Now *we* ask for a load to fire another callback.\r
4214 // If should be called last in the sequence:\r
4215 // The groupchange should have fired first, and the sortchange should have fired second\r
4216 // because that is the order they were requested in.\r
4217 store.load(function() {\r
4218 // The callbacks requested by the load requests from the group, and sorter adds should have been lost.\r
4219 allCorrect = !groupOrdinal && !sortOrdinal;\r
4220 });\r
4221\r
4222 // The store will flush its load call in the next tick, so satisfy the Ajax request then.\r
4223 waits(100);\r
4224 \r
4225 runs(function() {\r
4226 completeWithData([aaronRaw, tommyRaw]);\r
4227 });\r
4228\r
4229 // Wait for the load to return correctly with all callbacks called in sequence, and the data in the store\r
4230 waitsFor(function() {\r
4231 return allCorrect && store.getCount() === 2;\r
4232 }, 'the store to load');\r
4233\r
4234 // Whan all this is done, there should have been 5 load calls, but only 1 flushLoad\r
4235 runs(function() {\r
4236 expect(store.load.callCount).toBe(4);\r
4237 expect(store.flushLoad.callCount).toBe(1);\r
4238 });\r
4239 });\r
4240 });\r
4241 \r
4242 describe("grouping", function() {\r
4243 var groups;\r
4244 \r
4245 function groupBy(property, direction) {\r
4246 store.setGrouper({\r
4247 property: property || 'group',\r
4248 direction: direction\r
4249 });\r
4250 }\r
4251 \r
4252 function clearGroup() {\r
4253 store.setGrouper(null);\r
4254 }\r
4255 \r
4256 function sortBy(property, direction) {\r
4257 store.getSorters().add({\r
4258 property: property,\r
4259 direction: direction\r
4260 });\r
4261 }\r
4262 \r
4263 function filterBy(property, value) {\r
4264 store.getFilters().add({\r
4265 property: property,\r
4266 value: value\r
4267 });\r
4268 }\r
4269\r
4270 describe('groupDir and the group() method', function () {\r
4271 it('should default to "ASC"', function () {\r
4272 createStore();\r
4273\r
4274 expect(store.getGroupDir()).toBe('ASC');\r
4275 });\r
4276\r
4277 it('should default to "ASC" when calling group()', function () {\r
4278 createStore();\r
4279\r
4280 store.group('name');\r
4281\r
4282 expect(store.getGrouper().getDirection()).toBe('ASC');\r
4283 });\r
4284\r
4285 it('should use whatever was set in the config when calling group()', function () {\r
4286 createStore({\r
4287 groupDir: 'DESC'\r
4288 });\r
4289\r
4290 store.group('email');\r
4291\r
4292 expect(store.getGrouper().getDirection()).toBe('DESC');\r
4293 });\r
4294 });\r
4295 \r
4296 describe("getGroupField", function() {\r
4297 beforeEach(function() {\r
4298 createStore();\r
4299 });\r
4300 \r
4301 it("should default to ''", function() {\r
4302 expect(store.getGroupField()).toBe('');\r
4303 });\r
4304 \r
4305 it("should return the field name when grouped", function() {\r
4306 store.group('evilness');\r
4307 expect(store.getGroupField()).toBe('evilness');\r
4308 });\r
4309 \r
4310 it("should return '' when grouping is cleared", function() {\r
4311 store.group('evilness');\r
4312 store.clearGrouping();\r
4313 expect(store.getGroupField()).toBe('');\r
4314 });\r
4315 });\r
4316 \r
4317 describe("group method", function() {\r
4318 beforeEach(function() {\r
4319 createStore();\r
4320 });\r
4321 \r
4322 it("should accept a field name & direction", function() {\r
4323 store.group('group', 'DESC');\r
4324 var grouper = store.getGrouper();\r
4325 expect(grouper.getProperty()).toBe('group');\r
4326 expect(grouper.getDirection()).toBe('DESC');\r
4327 });\r
4328 \r
4329 it("should overwrite an existing grouper", function() {\r
4330 store.group('group', 'ASC');\r
4331 store.group('evilness', 'DESC');\r
4332 \r
4333 var grouper = store.getGrouper();\r
4334 expect(grouper.getProperty()).toBe('evilness');\r
4335 expect(grouper.getDirection()).toBe('DESC');\r
4336 });\r
4337 });\r
4338 \r
4339 describe("clearGrouping method", function() {\r
4340 it("should clear existing groupers", function() {\r
4341 createStore();\r
4342 store.group('evilness');\r
4343 store.clearGrouping();\r
4344 expect(store.getGrouper()).toBe(null);\r
4345 });\r
4346 });\r
4347 \r
4348 describe("isGrouped", function() {\r
4349 beforeEach(function() {\r
4350 createStore();\r
4351 });\r
4352 \r
4353 it("should default to false", function() {\r
4354 expect(store.isGrouped()).toBe(false);\r
4355 });\r
4356 \r
4357 it("should return true when the store has a grouper", function() {\r
4358 store.group('evilness');\r
4359 expect(store.isGrouped()).toBe(true);\r
4360 });\r
4361 \r
4362 it("should return false when the grouper is removed", function() {\r
4363 store.group('evilness');\r
4364 store.setGrouper(null);\r
4365 expect(store.isGrouped()).toBe(false);\r
4366 });\r
4367 });\r
4368 \r
4369 describe("local", function() {\r
4370 beforeEach(function() {\r
4371 createStore({\r
4372 remoteSort: false\r
4373 });\r
4374 addStoreData();\r
4375 });\r
4376\r
4377 describe("during construction", function() {\r
4378 it("should create groups", function() {\r
4379 createStore({\r
4380 remoteSort: false,\r
4381 grouper: {\r
4382 property: 'group'\r
4383 }\r
4384 });\r
4385 addStoreData();\r
4386 groups = store.getGroups();\r
4387 expect(groups.getCount()).toBe(2);\r
4388 });\r
4389 });\r
4390\r
4391 it("should group by the specified key", function() {\r
4392 groupBy();\r
4393 groups = store.getGroups();\r
4394 expect(groups.getCount()).toBe(2);\r
4395 });\r
4396\r
4397 it("should have the appropriate item in each group", function() {\r
4398 groupBy();\r
4399 groups = store.getGroups();\r
4400 expect(groups.first().getGroupKey()).toBe('admin');\r
4401 expect(groups.last().getGroupKey()).toBe('code');\r
4402 });\r
4403\r
4404 describe("clearing groups", function() {\r
4405 it("should return no groups by default", function() {\r
4406 expect(store.getGroups()).toBeNull();\r
4407 }); \r
4408\r
4409 it("should return no groups once the grouper has cleared", function() {\r
4410 groupBy();\r
4411 store.getGroups();\r
4412 clearGroup();\r
4413 expect(store.getGroups()).toBeNull();\r
4414 });\r
4415 });\r
4416\r
4417 describe("dynamic manipulation", function() {\r
4418 describe("adding", function() {\r
4419 it("should add to an existing group", function() {\r
4420 groupBy();\r
4421\r
4422 store.add({\r
4423 email: 'new@sencha.com',\r
4424 group: 'admin'\r
4425 });\r
4426\r
4427 var admins = store.getGroups().get('admin');\r
4428 expect(admins.getCount()).toBe(3);\r
4429 expect(admins.indexOfKey('new@sencha.com')).toBe(2);\r
4430 });\r
4431\r
4432 it("should create a new group", function() {\r
4433 groupBy();\r
4434 expect(store.getGroups().get('test')).toBeUndefined(); \r
4435 store.add({\r
4436 email: 'new@sencha.com',\r
4437 group: 'test'\r
4438 });\r
4439 var tests = store.getGroups().get('test');\r
4440 expect(tests.getCount()).toBe(1);\r
4441 expect(tests.indexOfKey('new@sencha.com')).toBe(0);\r
4442 });\r
4443\r
4444 it("should add to an existing group before add event", function() {\r
4445 groupBy();\r
4446\r
4447 var admins = store.getGroups().get('admin');\r
4448\r
4449 store.on({\r
4450 add: function () {\r
4451 expect(admins.getCount()).toBe(3);\r
4452 expect(admins.indexOfKey('new@sencha.com')).toBe(2);\r
4453 }\r
4454 });\r
4455\r
4456 store.add({\r
4457 email: 'new@sencha.com',\r
4458 group: 'admin'\r
4459 });\r
4460 });\r
4461\r
4462 it("should position items correctly when adding multiple items", function() {\r
4463 groupBy();\r
4464 var new1 = makeUser('new1@sencha.com', {\r
4465 group: 'code'\r
4466 }), new2 = makeUser('new2@sencha.com', {\r
4467 group: 'admin'\r
4468 });\r
4469 store.add([new1, new2]);\r
4470 expect(store.indexOf(new1)).toBe(5);\r
4471 expect(store.indexOf(new2)).toBe(2);\r
4472 });\r
4473 });\r
4474\r
4475 describe("removing", function() {\r
4476 it("should remove from an existing group", function() {\r
4477 groupBy();\r
4478 store.remove(abeRec);\r
4479 var admins = store.getGroups().get('admin');\r
4480 expect(admins.getCount()).toBe(1);\r
4481 expect(admins.contains(abeRec)).toBe(false);\r
4482 });\r
4483\r
4484 it("should remove a group", function() {\r
4485 store.remove(abeRec);\r
4486 groupBy();\r
4487 store.remove(aaronRec);\r
4488 expect(store.getGroups().get('admin')).toBeUndefined();\r
4489 });\r
4490\r
4491 it("should remove from an existing group before remove event", function() {\r
4492 groupBy();\r
4493\r
4494 var admins = store.getGroups().get('admin');\r
4495\r
4496 store.on({\r
4497 remove: function () {\r
4498 expect(admins.getCount()).toBe(1);\r
4499 expect(admins.contains(abeRec)).toBe(false);\r
4500 }\r
4501 });\r
4502\r
4503 store.remove(abeRec);\r
4504 });\r
4505\r
4506 describe('using removeAt', function () {\r
4507 it('should remove the record from its group', function () {\r
4508 var admins;\r
4509\r
4510 groupBy();\r
4511 admins = store.getGroups().get('admin');\r
4512\r
4513 expect(admins.contains(abeRec)).toBe(true);\r
4514\r
4515 store.removeAt(0);\r
4516\r
4517 expect(admins.contains(abeRec)).toBe(false);\r
4518 });\r
4519\r
4520 it('should remove a range of records from their groups', function () {\r
4521 var admins, codes;\r
4522\r
4523 groupBy();\r
4524 admins = store.getGroups().get('admin');\r
4525 codes = store.getGroups().get('code');\r
4526\r
4527 expect(codes.contains(edRec)).toBe(true);\r
4528 expect(codes.contains(tommyRec)).toBe(true);\r
4529 expect(admins.contains(aaronRec)).toBe(true);\r
4530\r
4531 store.removeAt(1, 3);\r
4532\r
4533 expect(codes.contains(edRec)).toBe(false);\r
4534 expect(codes.contains(tommyRec)).toBe(false);\r
4535 expect(admins.contains(aaronRec)).toBe(false);\r
4536 // The second group has been removed because all its items were removed.\r
4537 expect(store.getGroups().length).toBe(1);\r
4538 });\r
4539 });\r
4540\r
4541 describe('using removeAll', function () {\r
4542 it('should remove the groups', function () {\r
4543 var admins, groups;\r
4544\r
4545 groupBy();\r
4546 groups = store.getGroups();\r
4547\r
4548 admins = groups.get('admin');\r
4549\r
4550 expect(groups.length).toBe(2);\r
4551\r
4552 store.removeAll();\r
4553\r
4554 expect(groups.length).toBe(0);\r
4555 });\r
4556 });\r
4557\r
4558 describe('phantom group records', function () {\r
4559 it('should remove phantoms from their groups', function () {\r
4560 var admins;\r
4561\r
4562 groupBy();\r
4563 admins = store.getGroups().get('admin');\r
4564\r
4565 var data = [\r
4566 { name: 'Phil' },\r
4567 { name: 'Evan' },\r
4568 { name: 'Nige' },\r
4569 { name: 'Alex' }\r
4570 ],\r
4571 record;\r
4572\r
4573 // Destroy the store created in the beforeEach(). We need a store of phantom records.\r
4574 store.destroy();\r
4575\r
4576 // Use an ajax proxy with local data to so it doesn't go through the reader and marked as non-phantom.\r
4577 store = new Ext.data.Store({\r
4578 fields: ['name'],\r
4579 data: data,\r
4580 groupField: 'name',\r
4581 proxy: {\r
4582 type: 'ajax'\r
4583 }\r
4584 });\r
4585\r
4586 record = store.getAt(0);\r
4587\r
4588 expect(record.phantom).toBe(true);\r
4589\r
4590 store.remove(record);\r
4591\r
4592 expect(store.getGroups().getAt(0).contains(record)).toBe(false);\r
4593 });\r
4594 });\r
4595 });\r
4596\r
4597 describe("updating", function() {\r
4598 it("should move the item if the group changes but the record does not change position", function() {\r
4599 groupBy();\r
4600\r
4601 var index = store.indexOf(aaronRec);\r
4602 aaronRec.set('group', 'code');\r
4603\r
4604 var admins = store.getGroups().get('admin'),\r
4605 coders = store.getGroups().get('code');\r
4606\r
4607 expect(admins.getCount()).toBe(1);\r
4608 expect(admins.contains(aaronRec)).toBe(false);\r
4609 expect(coders.getCount()).toBe(3);\r
4610 expect(coders.contains(aaronRec)).toBe(true);\r
4611 expect(store.indexOf(aaronRec)).toBe(index);\r
4612 });\r
4613\r
4614 it("should move the item if the group changes and the record changes position", function() {\r
4615 groupBy();\r
4616 var index = store.indexOf(abeRec);\r
4617 abeRec.set('group', 'code');\r
4618\r
4619 var admins = store.getGroups().get('admin'),\r
4620 coders = store.getGroups().get('code');\r
4621\r
4622 expect(admins.getCount()).toBe(1);\r
4623 expect(admins.contains(abeRec)).toBe(false);\r
4624 expect(coders.getCount()).toBe(3);\r
4625 expect(coders.contains(abeRec)).toBe(true);\r
4626 expect(store.indexOf(abeRec)).not.toBe(index);\r
4627 });\r
4628 });\r
4629 });\r
4630\r
4631 describe("sorting", function() {\r
4632 function expectOrder(recs, s) {\r
4633 var len = recs.length,\r
4634 i;\r
4635\r
4636 s = s || store;\r
4637 for (i = 0; i < len; ++i) {\r
4638 expect(s.getAt(i)).toBe(recs[i]);\r
4639 }\r
4640 }\r
4641\r
4642 it("should sort the items in the collection by group", function() {\r
4643 store.removeAll();\r
4644 store.add(tommyRec, aaronRec, edRec, abeRec);\r
4645 groupBy('group', 'ASC');\r
4646 expectOrder([aaronRec, abeRec, tommyRec, edRec]);\r
4647 });\r
4648\r
4649 it("should sort the groups according to the group direction", function() {\r
4650 store.removeAll();\r
4651 store.add(abeRec, aaronRec, tommyRec, edRec);\r
4652 groupBy('group', 'DESC');\r
4653 expectOrder([tommyRec, edRec, abeRec, aaronRec]);\r
4654 });\r
4655\r
4656 it("should use the natural order inside the groups", function() {\r
4657 store.removeAll();\r
4658 store.add(tommyRec, aaronRec, edRec, abeRec);\r
4659 groupBy();\r
4660 expectOrder([aaronRec, abeRec, tommyRec, edRec]);\r
4661 var groups = store.getGroups();\r
4662 expectOrder([aaronRec, abeRec], groups.get('admin'));\r
4663 expectOrder([tommyRec, edRec], groups.get('code'));\r
4664 });\r
4665\r
4666 it("should insert the record into the correct collection position", function() {\r
4667 store.removeAll();\r
4668 store.add(aaronRec, tommyRec);\r
4669 groupBy('email');\r
4670 store.add(edRec);\r
4671 expect(store.indexOf(edRec)).toBe(1);\r
4672 });\r
4673\r
4674 it("should sort the groups by the sorterFn", function() {\r
4675 store.setGrouper({\r
4676 property: 'group',\r
4677 sorterFn: function(a, b) {\r
4678 a = a.get('group');\r
4679 b = b.get('group');\r
4680\r
4681 if (a === b) {\r
4682 return 0;\r
4683 }\r
4684 // Inverted on purpose to test sorter\r
4685 return a < b ? 1 : -1;\r
4686 }\r
4687 });\r
4688 var groups = store.getGroups();\r
4689 expect(groups.getAt(0).getGroupKey()).toBe('code');\r
4690 expect(groups.getAt(1).getGroupKey()).toBe('admin');\r
4691 });\r
4692\r
4693 describe("with sorters", function() {\r
4694 it("should sort the collection by grouper first", function() {\r
4695 sortBy('evilness');\r
4696 groupBy();\r
4697 expectOrder([aaronRec, abeRec, tommyRec, edRec]);\r
4698 });\r
4699\r
4700 it("should sort the new groups by the sorter", function() {\r
4701 sortBy('evilness', 'DESC');\r
4702 groupBy();\r
4703 var groups = store.getGroups();\r
4704 expectOrder([abeRec, aaronRec], groups.get('admin'));\r
4705 expectOrder([edRec, tommyRec], groups.get('code'));\r
4706 });\r
4707\r
4708 it("should sort existing groups by the sorter", function() {\r
4709 groupBy();\r
4710 sortBy('evilness', 'DESC');\r
4711 var groups = store.getGroups();\r
4712 expectOrder([abeRec, aaronRec], groups.get('admin'));\r
4713 expectOrder([edRec, tommyRec], groups.get('code'));\r
4714 });\r
4715\r
4716 it("should sort by the sorter after the groups have been cleared", function() {\r
4717 sortBy('evilness');\r
4718 groupBy();\r
4719 clearGroup();\r
4720 expectOrder([tommyRec, aaronRec, abeRec, edRec]);\r
4721 });\r
4722 });\r
4723 });\r
4724\r
4725 describe("filters", function() {\r
4726 it("should respect existing filters while grouping", function() {\r
4727 filterBy('old', true);\r
4728 groupBy();\r
4729\r
4730 var admins = store.getGroups().get('admin'),\r
4731 coders = store.getGroups().get('code');\r
4732\r
4733 expect(admins.getCount()).toBe(1);\r
4734 expect(admins.first()).toBe(aaronRec);\r
4735\r
4736 expect(coders.getCount()).toBe(1);\r
4737 expect(coders.first()).toBe(tommyRec);\r
4738 });\r
4739\r
4740 it("should filter existing groups", function() {\r
4741 groupBy();\r
4742 filterBy('old', true);\r
4743\r
4744 var admins = store.getGroups().get('admin'),\r
4745 coders = store.getGroups().get('code');\r
4746\r
4747 expect(admins.getCount()).toBe(1);\r
4748 expect(admins.first()).toBe(aaronRec);\r
4749\r
4750 expect(coders.getCount()).toBe(1);\r
4751 expect(coders.first()).toBe(tommyRec);\r
4752 });\r
4753\r
4754 it("should update groups when filters are cleared", function() {\r
4755 filterBy('old', true);\r
4756 groupBy();\r
4757\r
4758 store.clearFilter();\r
4759\r
4760 var groups = store.getGroups();\r
4761\r
4762 expect(groups.get('admin').getCount()).toBe(2);\r
4763 expect(groups.get('code').getCount()).toBe(2);\r
4764 });\r
4765\r
4766 it("should remove groups when required", function() {\r
4767 groupBy();\r
4768 store.getFilters().add({\r
4769 filterFn: function(rec) {\r
4770 return rec.get('name') === 'Ed Spencer';\r
4771 }\r
4772 });\r
4773\r
4774 var groups = store.getGroups();\r
4775\r
4776 expect(groups.get('admin')).toBeUndefined();\r
4777 expect(groups.get('code').getCount()).toBe(1);\r
4778 });\r
4779\r
4780 it("should add groups when required", function() {\r
4781 groupBy();\r
4782 var filters = store.getFilters();\r
4783 filters.add({\r
4784 filterFn: function(rec) {\r
4785 return Ext.Array.indexOf(['Ed Spencer', 'Aaron Conran'], rec.get('name')) > -1;\r
4786 }\r
4787 }, {\r
4788 filterFn: function(rec) {\r
4789 return rec.get('name') === 'Aaron Conran';\r
4790 }\r
4791 });\r
4792\r
4793 var groups = store.getGroups();\r
4794\r
4795 expect(groups.get('admin').getCount()).toBe(1);\r
4796 expect(groups.get('code')).toBeUndefined();\r
4797\r
4798 filters.remove(filters.last());\r
4799\r
4800 groups = store.getGroups();\r
4801 expect(groups.get('admin').getCount()).toBe(1);\r
4802 expect(groups.get('code').getCount()).toBe(1);\r
4803 });\r
4804 });\r
4805 });\r
4806 \r
4807 describe("remote", function() {\r
4808 describe("during construction", function() {\r
4809 it("should not trigger a load", function() {\r
4810 var loadSpy = spyOn(Ext.data.ProxyStore.prototype, 'load'),\r
4811 flushLoadSpy = spyOn(Ext.data.Store.prototype, 'flushLoad');\r
4812 createStore({\r
4813 remoteSort: true,\r
4814 asynchronousLoad: true,\r
4815 grouper: {\r
4816 property: 'group'\r
4817 }\r
4818 });\r
4819\r
4820 // group() triggers a load, but being async, it does not get flushed\r
4821 expect(loadSpy).toHaveBeenCalled();\r
4822 expect(flushLoadSpy).not.toHaveBeenCalled();\r
4823 });\r
4824 });\r
4825 \r
4826 describe("dynamic groupers", function() {\r
4827 beforeEach(function() {\r
4828 createStore({\r
4829 remoteSort: true\r
4830 });\r
4831 });\r
4832 \r
4833 describe("via group", function() {\r
4834 it("should trigger a load when adding a grouper", function() {\r
4835 spyOn(store, 'load');\r
4836 store.group('group');\r
4837 expect(store.load.callCount).toBe(1);\r
4838 });\r
4839\r
4840 it("should not trigger a load when removing the grouper", function() {\r
4841 store.group('group');\r
4842 spyOn(store, 'load');\r
4843 store.group(null);\r
4844 expect(store.load).not.toHaveBeenCalled();\r
4845 });\r
4846\r
4847 describe("with sorters", function() {\r
4848 it("should only load once when adding a grouper and there are sorters", function() {\r
4849 store.sort('name');\r
4850 spyOn(store, 'load');\r
4851 store.group('group');\r
4852 expect(store.load.callCount).toBe(1);\r
4853 });\r
4854\r
4855 it("should trigger a load when removing the grouper", function() {\r
4856 store.getSorters().add('name');\r
4857 store.group('group');\r
4858 spyOn(store, 'load');\r
4859 store.group(null);\r
4860 expect(store.load.callCount).toBe(1);\r
4861 });\r
4862 });\r
4863\r
4864 \r
4865 });\r
4866 \r
4867 describe("via setGrouper", function() {\r
4868 it("should trigger a load when adding a grouper", function() {\r
4869 spyOn(store, 'load');\r
4870 store.setGrouper({\r
4871 property: 'group'\r
4872 });\r
4873 expect(store.load.callCount).toBe(1);\r
4874 });\r
4875\r
4876 it("should not trigger a load when removing the grouper", function() {\r
4877 store.setGrouper({\r
4878 property: 'group'\r
4879 });\r
4880 spyOn(store, 'load');\r
4881 store.setGrouper(null);\r
4882 expect(store.load).not.toHaveBeenCalled();\r
4883 });\r
4884\r
4885 describe("with sorters", function() {\r
4886 it("should only load once when adding a grouper and there are sorters", function() {\r
4887 store.getSorters().add('name');\r
4888 spyOn(store, 'load');\r
4889 store.setGrouper({\r
4890 property: 'group'\r
4891 });\r
4892 expect(store.load.callCount).toBe(1);\r
4893 });\r
4894\r
4895 it("should trigger a load when removing the grouper", function() {\r
4896 store.getSorters().add('name');\r
4897 store.setGrouper({\r
4898 property: 'group'\r
4899 });\r
4900 spyOn(store, 'load');\r
4901 store.setGrouper(null);\r
4902 expect(store.load.callCount).toBe(1);\r
4903 });\r
4904 });\r
4905 });\r
4906 });\r
4907\r
4908 describe("with remoteSort", function() {\r
4909 beforeEach(function() {\r
4910 createStore({\r
4911 remoteSort: true,\r
4912 groupField: 'group'\r
4913 });\r
4914 edRaw.group = 'bots';\r
4915 });\r
4916\r
4917 it("should leave the items in group order", function() {\r
4918 store.load();\r
4919 completeWithData([edRaw, tommyRaw, abeRaw, aaronRaw]);\r
4920 expect(store.getAt(0).getId()).toBe('ed@sencha.com');\r
4921 expect(store.getAt(1).getId()).toBe('tommy@sencha.com');\r
4922 expect(store.getAt(2).getId()).toBe('abe@sencha.com');\r
4923 expect(store.getAt(3).getId()).toBe('aaron@sencha.com');\r
4924 });\r
4925\r
4926 it("should leave groups in order", function() {\r
4927 store.load();\r
4928 completeWithData([edRaw, tommyRaw, abeRaw, aaronRaw]);\r
4929\r
4930 var groups = store.getGroups(),\r
4931 group;\r
4932 \r
4933 expect(groups.getCount()).toBe(3);\r
4934\r
4935 group = groups.getAt(0);\r
4936 expect(group.getGroupKey()).toBe('bots');\r
4937 expect(group.getCount()).toBe(1);\r
4938 expect(group.getAt(0).getId()).toBe('ed@sencha.com');\r
4939\r
4940 group = groups.getAt(1);\r
4941 expect(group.getGroupKey()).toBe('code');\r
4942 expect(group.getCount()).toBe(1);\r
4943 expect(group.getAt(0).getId()).toBe('tommy@sencha.com');\r
4944\r
4945 group = groups.getAt(2);\r
4946 expect(group.getGroupKey()).toBe('admin');\r
4947 expect(group.getCount()).toBe(2);\r
4948 expect(group.getAt(0).getId()).toBe('abe@sencha.com');\r
4949 expect(group.getAt(1).getId()).toBe('aaron@sencha.com');\r
4950 });\r
4951 })\r
4952 });\r
4953 \r
4954 describe("events", function() {\r
4955 var spy;\r
4956 \r
4957 beforeEach(function() {\r
4958 spy = jasmine.createSpy();\r
4959 });\r
4960\r
4961 afterEach(function() {\r
4962 spy = null;\r
4963 });\r
4964 \r
4965 describe("local", function() {\r
4966 beforeEach(function() {\r
4967 createStore({\r
4968 remoteSort: false\r
4969 });\r
4970 });\r
4971 \r
4972 describe("adding", function() {\r
4973 describe("with no sorters", function() {\r
4974 it("should trigger the groupchange event and pass the store & grouper", function() {\r
4975 store.on('groupchange', spy);\r
4976 store.group('group');\r
4977 expect(spy.callCount).toBe(1);\r
4978 var args = spy.mostRecentCall.args;\r
4979 expect(args[0]).toBe(store);\r
4980 expect(args[1].getProperty()).toBe('group');\r
4981 expect(args[1].getDirection()).toBe('ASC');\r
4982 });\r
4983\r
4984 it("should trigger the refresh event", function() {\r
4985 store.on('refresh', spy);\r
4986 store.group('group');\r
4987 expect(spy.callCount).toBe(1);\r
4988 expect(spy.mostRecentCall.args[0]).toBe(store);\r
4989 });\r
4990 \r
4991 it("should trigger the datachanged event", function() {\r
4992 store.on('datachanged', spy);\r
4993 store.group('group');\r
4994 expect(spy.callCount).toBe(1);\r
4995 expect(spy.mostRecentCall.args[0]).toBe(store);\r
4996 });\r
4997\r
4998 it("should not trigger the sort event", function() {\r
4999 store.on('sort', spy);\r
5000 store.group('group');\r
5001 expect(spy).not.toHaveBeenCalled();\r
5002 });\r
5003 });\r
5004\r
5005 describe("with sorters", function() {\r
5006 beforeEach(function() {\r
5007 store.getSorters().add('name');\r
5008 });\r
5009\r
5010 it("should trigger the groupchange event and pass the store & grouper", function() {\r
5011 store.on('groupchange', spy);\r
5012 store.group('group');\r
5013 expect(spy.callCount).toBe(1);\r
5014 var args = spy.mostRecentCall.args;\r
5015 expect(args[0]).toBe(store);\r
5016 expect(args[1].getProperty()).toBe('group');\r
5017 expect(args[1].getDirection()).toBe('ASC');\r
5018 });\r
5019\r
5020 it("should trigger the refresh event", function() {\r
5021 store.on('refresh', spy);\r
5022 store.group('group');\r
5023 expect(spy.callCount).toBe(1);\r
5024 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5025 });\r
5026 \r
5027 it("should trigger the datachanged event", function() {\r
5028 store.on('datachanged', spy);\r
5029 store.group('group');\r
5030 expect(spy.callCount).toBe(1);\r
5031 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5032 });\r
5033\r
5034 it("should not trigger the sort event", function() {\r
5035 store.on('sort', spy);\r
5036 store.group('group');\r
5037 expect(spy).not.toHaveBeenCalled();\r
5038 });\r
5039 });\r
5040 });\r
5041 \r
5042 describe("removing", function() {\r
5043 describe("with no sorters", function() {\r
5044 it("should trigger the groupchange event", function() {\r
5045 store.group('group');\r
5046 store.on('groupchange', spy);\r
5047 store.group(null);\r
5048 expect(spy.callCount).toBe(1);\r
5049 var args = spy.mostRecentCall.args;\r
5050 expect(args[0]).toBe(store);\r
5051 expect(args[1]).toBeNull();\r
5052 });\r
5053\r
5054 it("should not trigger the refresh event", function() {\r
5055 store.group('group');\r
5056 store.on('refresh', spy);\r
5057 store.group(null);\r
5058 expect(spy).not.toHaveBeenCalled();\r
5059 });\r
5060 \r
5061 it("should trigger the datachanged event", function() {\r
5062 store.group('group');\r
5063 store.on('datachanged', spy);\r
5064 store.group(null);\r
5065 expect(spy).not.toHaveBeenCalled();\r
5066 });\r
5067\r
5068 it("should not trigger the sort event", function() {\r
5069 store.group('group');\r
5070 store.on('sort', spy);\r
5071 store.group(null);\r
5072 expect(spy).not.toHaveBeenCalled();\r
5073 });\r
5074 });\r
5075\r
5076 describe("with sorters", function() {\r
5077 beforeEach(function() {\r
5078 store.getSorters().add('email');\r
5079 });\r
5080\r
5081 it("should trigger the groupchange event", function() {\r
5082 store.group('group');\r
5083 store.on('groupchange', spy);\r
5084 store.group(null);\r
5085 expect(spy.callCount).toBe(1);\r
5086 var args = spy.mostRecentCall.args;\r
5087 expect(args[0]).toBe(store);\r
5088 expect(args[1]).toBeNull();\r
5089 })\r
5090\r
5091 it("should trigger the refresh event", function() {\r
5092 store.group('group');\r
5093 store.on('refresh', spy);\r
5094 store.group(null);\r
5095 expect(spy.callCount).toBe(1);\r
5096 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5097 });\r
5098 \r
5099 it("should trigger the datachanged event", function() {\r
5100 store.group('group');\r
5101 store.on('datachanged', spy);\r
5102 store.group(null);\r
5103 expect(spy.callCount).toBe(1);\r
5104 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5105 });\r
5106\r
5107 it("should not trigger the sort event", function() {\r
5108 store.group('group');\r
5109 store.on('sort', spy);\r
5110 store.group(null);\r
5111 expect(spy).not.toHaveBeenCalled();\r
5112 });\r
5113 });\r
5114 });\r
5115 });\r
5116 \r
5117 describe("remote", function() {\r
5118 describe("with a synchronous proxy", function() {\r
5119 beforeEach(function() {\r
5120 createStore({\r
5121 remoteSort: true,\r
5122 proxy: {\r
5123 type: 'memory'\r
5124 }\r
5125 });\r
5126 });\r
5127 describe("adding", function() {\r
5128 describe("with no sorters", function() {\r
5129 it("should trigger the groupchange event and pass the store & grouper", function() {\r
5130 store.on('groupchange', spy);\r
5131 store.group('group');\r
5132 expect(spy.callCount).toBe(1);\r
5133 var args = spy.mostRecentCall.args;\r
5134 expect(args[0]).toBe(store);\r
5135 expect(args[1].getProperty()).toBe('group');\r
5136 expect(args[1].getDirection()).toBe('ASC');\r
5137 });\r
5138\r
5139 it("should trigger the refresh event", function() {\r
5140 store.on('refresh', spy);\r
5141 store.group('group');\r
5142 expect(spy.callCount).toBe(1);\r
5143 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5144 });\r
5145 \r
5146 it("should trigger the datachanged event", function() {\r
5147 store.on('datachanged', spy);\r
5148 store.group('group');\r
5149 expect(spy.callCount).toBe(1);\r
5150 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5151 });\r
5152\r
5153 it("should not trigger the sort event", function() {\r
5154 store.on('sort', spy);\r
5155 store.group('group');\r
5156 expect(spy).not.toHaveBeenCalled();\r
5157 });\r
5158 });\r
5159\r
5160 describe("with sorters", function() {\r
5161 beforeEach(function() {\r
5162 store.getSorters().add('name');\r
5163 });\r
5164\r
5165 it("should trigger the groupchange event and pass the store & grouper", function() {\r
5166 store.on('groupchange', spy);\r
5167 store.group('group');\r
5168 expect(spy.callCount).toBe(1);\r
5169 var args = spy.mostRecentCall.args;\r
5170 expect(args[0]).toBe(store);\r
5171 expect(args[1].getProperty()).toBe('group');\r
5172 expect(args[1].getDirection()).toBe('ASC');\r
5173 });\r
5174\r
5175 it("should trigger the refresh event", function() {\r
5176 store.on('refresh', spy);\r
5177 store.group('group');\r
5178 expect(spy.callCount).toBe(1);\r
5179 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5180 });\r
5181 \r
5182 it("should trigger the datachanged event", function() {\r
5183 store.on('datachanged', spy);\r
5184 store.group('group');\r
5185 expect(spy.callCount).toBe(1);\r
5186 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5187 });\r
5188\r
5189 it("should not trigger the sort event", function() {\r
5190 store.on('sort', spy);\r
5191 store.group('group');\r
5192 expect(spy).not.toHaveBeenCalled();\r
5193 });\r
5194 });\r
5195 });\r
5196 \r
5197 describe("removing", function() {\r
5198 describe("with no sorters", function() {\r
5199 it("should trigger the groupchange event", function() {\r
5200 store.group('group');\r
5201 store.on('groupchange', spy);\r
5202 store.group(null);\r
5203 expect(spy.callCount).toBe(1);\r
5204 var args = spy.mostRecentCall.args;\r
5205 expect(args[0]).toBe(store);\r
5206 expect(args[1]).toBeNull();\r
5207 });\r
5208\r
5209 it("should not trigger the refresh event", function() {\r
5210 store.group('group');\r
5211 store.on('refresh', spy);\r
5212 store.group(null);\r
5213 expect(spy).not.toHaveBeenCalled();\r
5214 });\r
5215 \r
5216 it("should trigger the datachanged event", function() {\r
5217 store.group('group');\r
5218 store.on('datachanged', spy);\r
5219 store.group(null);\r
5220 expect(spy).not.toHaveBeenCalled();\r
5221 });\r
5222\r
5223 it("should not trigger the sort event", function() {\r
5224 store.group('group');\r
5225 store.on('sort', spy);\r
5226 store.group(null);\r
5227 expect(spy).not.toHaveBeenCalled();\r
5228 });\r
5229 });\r
5230\r
5231 describe("with sorters", function() {\r
5232 beforeEach(function() {\r
5233 store.getSorters().add('email');\r
5234 });\r
5235\r
5236 it("should trigger the groupchange event", function() {\r
5237 store.group('group');\r
5238 store.on('groupchange', spy);\r
5239 store.group(null);\r
5240 expect(spy.callCount).toBe(1);\r
5241 var args = spy.mostRecentCall.args;\r
5242 expect(args[0]).toBe(store);\r
5243 expect(args[1]).toBeNull();\r
5244 })\r
5245\r
5246 it("should trigger the refresh event", function() {\r
5247 store.group('group');\r
5248 store.on('refresh', spy);\r
5249 store.group(null);\r
5250 expect(spy.callCount).toBe(1);\r
5251 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5252 });\r
5253 \r
5254 it("should trigger the datachanged event", function() {\r
5255 store.group('group');\r
5256 store.on('datachanged', spy);\r
5257 store.group(null);\r
5258 expect(spy.callCount).toBe(1);\r
5259 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5260 });\r
5261\r
5262 it("should not trigger the sort event", function() {\r
5263 store.group('group');\r
5264 store.on('sort', spy);\r
5265 store.group(null);\r
5266 expect(spy).not.toHaveBeenCalled();\r
5267 });\r
5268 });\r
5269 });\r
5270 });\r
5271\r
5272 describe("with an asynchronous proxy", function() {\r
5273 beforeEach(function() {\r
5274 createStore({\r
5275 remoteSort: true,\r
5276 proxy: {\r
5277 type: 'ajax',\r
5278 url: 'foo'\r
5279 }\r
5280 });\r
5281 });\r
5282 describe("adding", function() {\r
5283 describe("with no sorters", function() {\r
5284 it("should trigger the groupchange event and pass the store & grouper after the load completes", function() {\r
5285 store.on('groupchange', spy);\r
5286 store.group('group');\r
5287 expect(spy).not.toHaveBeenCalled();\r
5288 completeWithData([]);\r
5289 expect(spy.callCount).toBe(1);\r
5290 var args = spy.mostRecentCall.args;\r
5291 expect(args[0]).toBe(store);\r
5292 expect(args[1].getProperty()).toBe('group');\r
5293 expect(args[1].getDirection()).toBe('ASC');\r
5294 });\r
5295\r
5296 it("should trigger the refresh event after the load completes", function() {\r
5297 store.on('refresh', spy);\r
5298 store.group('group');\r
5299 expect(spy).not.toHaveBeenCalled();\r
5300 completeWithData([]);\r
5301 expect(spy.callCount).toBe(1);\r
5302 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5303 });\r
5304 \r
5305 it("should trigger the datachanged event after the load completes", function() {\r
5306 store.on('datachanged', spy);\r
5307 store.group('group');\r
5308 expect(spy).not.toHaveBeenCalled();\r
5309 completeWithData([]);\r
5310 expect(spy.callCount).toBe(1);\r
5311 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5312 });\r
5313\r
5314 it("should not trigger the sort event", function() {\r
5315 store.on('sort', spy);\r
5316 store.group('group');\r
5317 expect(spy).not.toHaveBeenCalled();\r
5318 completeWithData([]);\r
5319 expect(spy).not.toHaveBeenCalled();\r
5320 });\r
5321 });\r
5322\r
5323 describe("with sorters", function() {\r
5324 beforeEach(function() {\r
5325 store.getSorters().add('name');\r
5326 completeWithData([]);\r
5327 });\r
5328\r
5329 it("should trigger the groupchange event and pass the store & grouper after the load completes", function() {\r
5330 store.on('groupchange', spy);\r
5331 store.group('group');\r
5332 expect(spy).not.toHaveBeenCalled();\r
5333 completeWithData([]);\r
5334 expect(spy.callCount).toBe(1);\r
5335 var args = spy.mostRecentCall.args;\r
5336 expect(args[0]).toBe(store);\r
5337 expect(args[1].getProperty()).toBe('group');\r
5338 expect(args[1].getDirection()).toBe('ASC');\r
5339 });\r
5340\r
5341 it("should trigger the refresh event", function() {\r
5342 store.on('refresh', spy);\r
5343 store.group('group');\r
5344 expect(spy).not.toHaveBeenCalled();\r
5345 completeWithData([]);\r
5346 expect(spy.callCount).toBe(1);\r
5347 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5348 });\r
5349 \r
5350 it("should trigger the datachanged event", function() {\r
5351 store.on('datachanged', spy);\r
5352 store.group('group');\r
5353 expect(spy).not.toHaveBeenCalled();\r
5354 completeWithData([]);\r
5355 expect(spy.callCount).toBe(1);\r
5356 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5357 });\r
5358\r
5359 it("should not trigger the sort event", function() {\r
5360 store.on('sort', spy);\r
5361 store.group('group');\r
5362 expect(spy).not.toHaveBeenCalled();\r
5363 completeWithData([]);\r
5364 expect(spy).not.toHaveBeenCalled();\r
5365 });\r
5366 });\r
5367 });\r
5368 \r
5369 describe("removing", function() {\r
5370 beforeEach(function() {\r
5371 store.group('group');\r
5372 completeWithData([]);\r
5373 });\r
5374 describe("with no sorters", function() {\r
5375 it("should trigger the groupchange event without loading", function() {\r
5376 store.on('groupchange', spy);\r
5377 store.group(null);\r
5378 expect(spy.callCount).toBe(1);\r
5379 var args = spy.mostRecentCall.args;\r
5380 expect(args[0]).toBe(store);\r
5381 expect(args[1]).toBeNull();\r
5382 });\r
5383\r
5384 it("should not trigger the refresh event without loading", function() {\r
5385 store.on('refresh', spy);\r
5386 store.group(null);\r
5387 expect(spy).not.toHaveBeenCalled();\r
5388 });\r
5389 \r
5390 it("should trigger the datachanged event without loading", function() {\r
5391 store.on('datachanged', spy);\r
5392 store.group(null);\r
5393 expect(spy).not.toHaveBeenCalled();\r
5394 });\r
5395\r
5396 it("should not trigger the sort event", function() {\r
5397 store.on('sort', spy);\r
5398 store.group(null);\r
5399 expect(spy).not.toHaveBeenCalled();\r
5400 });\r
5401 });\r
5402\r
5403 describe("with sorters", function() {\r
5404 beforeEach(function() {\r
5405 store.getSorters().add('email');\r
5406 completeWithData([]);\r
5407 });\r
5408\r
5409 it("should trigger the groupchange event after the load completes", function() {\r
5410 store.on('groupchange', spy);\r
5411 store.group(null);\r
5412 expect(spy).not.toHaveBeenCalled();\r
5413 completeWithData([]);\r
5414 expect(spy.callCount).toBe(1);\r
5415 var args = spy.mostRecentCall.args;\r
5416 expect(args[0]).toBe(store);\r
5417 expect(args[1]).toBeNull();\r
5418 })\r
5419\r
5420 it("should trigger the refresh event after the load completes", function() {\r
5421 store.on('refresh', spy);\r
5422 store.group(null);\r
5423 expect(spy).not.toHaveBeenCalled();\r
5424 completeWithData([]);\r
5425 expect(spy.callCount).toBe(1);\r
5426 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5427 });\r
5428 \r
5429 it("should trigger the datachanged event after the load completes", function() {\r
5430 store.on('datachanged', spy);\r
5431 store.group(null);\r
5432 expect(spy).not.toHaveBeenCalled();\r
5433 completeWithData([]);\r
5434 expect(spy.callCount).toBe(1);\r
5435 expect(spy.mostRecentCall.args[0]).toBe(store);\r
5436 });\r
5437\r
5438 it("should not trigger the sort event", function() {\r
5439 store.on('sort', spy);\r
5440 store.group(null);\r
5441 expect(spy).not.toHaveBeenCalled();\r
5442 completeWithData([]);\r
5443 expect(spy).not.toHaveBeenCalled();\r
5444 });\r
5445 });\r
5446 });\r
5447 });\r
5448 });\r
5449 });\r
5450 });\r
5451 \r
5452 describe("filtering", function() {\r
5453 describe("the filter collection", function() {\r
5454 it("should be an instance of Ext.util.FilterCollection", function() {\r
5455 createStore();\r
5456 expect(store.getFilters() instanceof Ext.util.FilterCollection).toBe(true);\r
5457 });\r
5458\r
5459 it("should be empty by default", function() {\r
5460 createStore();\r
5461 expect(store.getFilters().getCount()).toBe(0);\r
5462 });\r
5463\r
5464 it("should add any filters passed in the constructor", function() {\r
5465 createStore({\r
5466 filters: [{\r
5467 property: 'foo',\r
5468 value: 'a'\r
5469 }, {\r
5470 property: 'bar',\r
5471 value: 'b'\r
5472 }]\r
5473 });\r
5474 var filter = store.getFilters().getAt(0);\r
5475 expect(filter.getProperty()).toBe('foo');\r
5476 expect(filter.getValue()).toBe('a');\r
5477\r
5478 filter = store.getFilters().getAt(1);\r
5479 expect(filter.getProperty()).toBe('bar');\r
5480 expect(filter.getValue()).toBe('b');\r
5481 });\r
5482\r
5483 it("should not set the rootProperty as data on the filter collection", function() {\r
5484 createStore();\r
5485 expect(store.getFilters().getRootProperty()).not.toBe('data');\r
5486 });\r
5487\r
5488 it("should not trigger events when asking for the collection and it has not been created", function () {\r
5489 var spy = jasmine.createSpy();\r
5490 createStore({\r
5491 listeners: {\r
5492 filterchange: spy\r
5493 }\r
5494 });\r
5495 store.getFilters();\r
5496 expect(spy).not.toHaveBeenCalled();\r
5497 });\r
5498 });\r
5499\r
5500 describe("filter method", function() {\r
5501 beforeEach(function() {\r
5502 createStore();\r
5503 });\r
5504 \r
5505 it("should accept a field name & value", function() {\r
5506 store.filter('name', 'Ed Spencer');\r
5507 var filter = store.getFilters().first();\r
5508 expect(filter.getProperty()).toBe('name');\r
5509 expect(filter.getValue()).toBe('Ed Spencer');\r
5510 });\r
5511 \r
5512 it("should add to existing filters", function() {\r
5513 store.filter('group', 'code');\r
5514 store.filter('evilness', 100);\r
5515 var filter = store.getFilters().first();\r
5516 \r
5517 expect(store.getFilters().getCount()).toBe(2); \r
5518 expect(filter.getProperty()).toBe('group');\r
5519 expect(filter.getValue()).toBe('code');\r
5520 \r
5521 filter = store.getFilters().last();\r
5522 expect(filter.getProperty()).toBe('evilness');\r
5523 expect(filter.getValue()).toBe(100);\r
5524 });\r
5525 });\r
5526 \r
5527 describe("filterBy", function() {\r
5528 var spy;\r
5529 beforeEach(function() {\r
5530 createStore();\r
5531 addStoreData();\r
5532 spy = jasmine.createSpy();\r
5533 });\r
5534 \r
5535 it("should add a persistent filter", function() {\r
5536 store.filterBy(spy);\r
5537 expect(store.getFilters().getCount()).toBe(1);\r
5538 });\r
5539 \r
5540 it("should pass along the filter fn", function() {\r
5541 store.filterBy(spy);\r
5542 expect(store.getFilters().first().getFilterFn()).toBe(spy);\r
5543 });\r
5544 \r
5545 it("should execute in the passed scope", function() {\r
5546 store.filterBy(spy, fakeScope);\r
5547 expect(spy.mostRecentCall.object).toBe(fakeScope);\r
5548 });\r
5549 \r
5550 it("should default the scope to the store", function() {\r
5551 store.filterBy(spy);\r
5552 expect(spy.mostRecentCall.object).toBe(store);\r
5553 });\r
5554 });\r
5555 \r
5556 describe("clearFilter method", function() {\r
5557 beforeEach(function() {\r
5558 createStore();\r
5559 });\r
5560 \r
5561 it("should remove all filters", function() {\r
5562 store.filter('evilness', 100);\r
5563 store.clearFilter();\r
5564 expect(store.getFilters().getCount()).toBe(0);\r
5565 });\r
5566 \r
5567 it("should trigger a load when using remoteFilter: true", function() {\r
5568 store.setRemoteFilter(true);\r
5569 store.filter('evilness', 100);\r
5570 spyOn(store, 'load');\r
5571 store.clearFilter();\r
5572 expect(store.load.callCount).toBe(1);\r
5573 });\r
5574 \r
5575 it("should trigger a load when using remoteFilter: true & passing suppressEvent", function() {\r
5576 store.setRemoteFilter(true);\r
5577 store.filter('evilness', 100);\r
5578 spyOn(store, 'load');\r
5579 store.clearFilter(true);\r
5580 expect(store.load).not.toHaveBeenCalled();\r
5581 });\r
5582 \r
5583 describe("events", function() {\r
5584 it("should not fire datachanged and refresh when suppress is passed", function() {\r
5585 var spy = jasmine.createSpy();\r
5586 store.filter('evilness', 100);\r
5587 store.on('refresh', spy);\r
5588 store.on('datachanged', spy);\r
5589 store.clearFilter(true);\r
5590 expect(spy).not.toHaveBeenCalled();\r
5591 });\r
5592 });\r
5593 });\r
5594 \r
5595 describe("isFiltered", function() {\r
5596 beforeEach(function() {\r
5597 createStore();\r
5598 });\r
5599 \r
5600 it("should default to false", function() {\r
5601 expect(store.isFiltered()).toBe(false);\r
5602 });\r
5603 \r
5604 it("should return true when filters are added", function() {\r
5605 store.filter('evilness', 100);\r
5606 expect(store.isFiltered()).toBe(true);\r
5607 });\r
5608 \r
5609 it("should return false when all filters are removed", function() {\r
5610 store.filter('evilness', 100);\r
5611 store.getFilters().remove('evilness');\r
5612 expect(store.isFiltered()).toBe(false);\r
5613 });\r
5614 });\r
5615 \r
5616 describe("local", function() {\r
5617 describe("during construction", function() {\r
5618 it("should filter an initial data set", function() {\r
5619 createStore({\r
5620 remoteFilter: false,\r
5621 filters: [{\r
5622 property: 'group',\r
5623 value: 'code'\r
5624 }],\r
5625 data: [aaronRaw, edRaw, tommyRaw, abeRaw]\r
5626 });\r
5627 expect(store.getCount()).toBe(2);\r
5628 expect(store.first().get('email')).toBe('ed@sencha.com');\r
5629 });\r
5630 });\r
5631 \r
5632 describe("dynamic filters", function() {\r
5633 beforeEach(function() {\r
5634 createStore({\r
5635 remoteFilter: false\r
5636 });\r
5637 addStoreData();\r
5638 });\r
5639 it("should filter the dataset when adding filters", function() {\r
5640 store.filter('evilness', 100);\r
5641 expect(store.first().get('email')).toBe('ed@sencha.com');\r
5642 expect(store.getCount()).toBe(1);\r
5643 });\r
5644 \r
5645 it("should be able to use multiple filter", function() {\r
5646 store.getFilters().add({\r
5647 property: 'group',\r
5648 value: 'code'\r
5649 }, {\r
5650 property: 'evilness',\r
5651 value: 100\r
5652 });\r
5653 expect(store.first().get('email')).toBe('ed@sencha.com');\r
5654 expect(store.getCount()).toBe(1);\r
5655 });\r
5656 \r
5657 it("should unfilter when removing a filter", function() {\r
5658 store.getFilters().add({\r
5659 property: 'group',\r
5660 value: 'code'\r
5661 }, {\r
5662 property: 'evilness',\r
5663 value: 100\r
5664 });\r
5665 store.getFilters().remove('evilness');\r
5666 expect(store.getCount()).toBe(2);\r
5667 });\r
5668 \r
5669 it("should push records that are changed to match the filter to the end with no sort", function() {\r
5670 store.removeAll();\r
5671 store.add(aaronRec, abeRec, edRec, tommyRec);\r
5672 store.filter('group', 'code');\r
5673 aaronRec.set('group', 'code');\r
5674 abeRec.set('group', 'code');\r
5675 expect(store.indexOf(aaronRec)).toBe(2);\r
5676 expect(store.indexOf(abeRec)).toBe(3);\r
5677 });\r
5678 \r
5679 describe("store methods while filtered", function() {\r
5680 describe("getCount", function() {\r
5681 it("should update the count to the filtered count", function() {\r
5682 store.filter('group', 'code');\r
5683 expect(store.getCount()).toBe(2);\r
5684 });\r
5685 });\r
5686 \r
5687 describe("indexOf", function() {\r
5688 it("should report filtered out records as not being in the store", function() {\r
5689 store.filter('group', 'admin');\r
5690 expect(store.indexOf(edRec)).toBe(-1);\r
5691 });\r
5692 });\r
5693 \r
5694 describe("getRange", function() {\r
5695 it("should only return the filtered records", function() {\r
5696 store.filter('group', 'admin');\r
5697 expect(store.getRange().length).toBe(2);\r
5698 });\r
5699 });\r
5700 \r
5701 describe("each", function() {\r
5702 it("should only iterate the filtered items", function() {\r
5703 store.filter('evilness', 100);\r
5704 var spy = jasmine.createSpy();\r
5705 store.each(spy);\r
5706 expect(spy.callCount).toBe(1);\r
5707 });\r
5708 });\r
5709 \r
5710 describe("add", function() {\r
5711 it("should be included in the active set if it matches the filter", function() {\r
5712 store.filter('group', 'code');\r
5713 store.add(makeUser('foo@sencha.com', {\r
5714 group: 'code'\r
5715 }));\r
5716 expect(store.getCount()).toBe(3);\r
5717 expect(store.indexOfId('foo@sencha.com')).toBe(2);\r
5718 });\r
5719 \r
5720 it("should not be included in the active set if it doesn't match the filter or fire the add event", function() {\r
5721 var spy = jasmine.createSpy();\r
5722 store.filter('group', 'code');\r
5723 store.on('add', spy);\r
5724 store.add(makeUser('foo@sencha.com', {\r
5725 group: 'admin'\r
5726 }));\r
5727 expect(store.getCount()).toBe(2);\r
5728 expect(store.indexOfId('foo@sencha.com')).toBe(-1);\r
5729 expect(spy).not.toHaveBeenCalled();\r
5730 });\r
5731 \r
5732 it("should include unmatched added records when removing the filter", function() {\r
5733 store.filter('group', 'code');\r
5734 store.add(makeUser('foo@sencha.com', {\r
5735 group: 'admin'\r
5736 }));\r
5737 store.clearFilter();\r
5738 expect(store.getCount()).toBe(5);\r
5739 expect(store.indexOfId('foo@sencha.com')).toBe(4);\r
5740 });\r
5741 });\r
5742 \r
5743 describe("remove", function() {\r
5744 it("should not include a removed record after a filter is cleared", function() {\r
5745 store.filter('group', 'code');\r
5746 store.remove(edRec);\r
5747 store.clearFilter();\r
5748 expect(store.indexOf(edRec)).toBe(-1);\r
5749 });\r
5750\r
5751 it("should still remove any records filtered out", function() {\r
5752 store.filter('group', 'code');\r
5753 store.remove(abeRec);\r
5754 store.clearFilter();\r
5755 expect(store.indexOf(abeRec)).toBe(-1);\r
5756 });\r
5757 });\r
5758\r
5759 describe("removeAll", function() {\r
5760 it("should only remove the filtered items", function() {\r
5761 store.filter('group', 'code');\r
5762 store.removeAll();\r
5763 store.clearFilter();\r
5764 expect(store.getCount()).toBe(2);\r
5765 });\r
5766 });\r
5767 });\r
5768 \r
5769 describe("sorting", function() {\r
5770 it("should restore any sort order when clearing a filter", function() {\r
5771 store.sort('email', 'DESC');\r
5772 store.getFilters().add({\r
5773 filterFn: function(rec) {\r
5774 // Includes Tommy/Ed\r
5775 return rec.get('group') === 'code';\r
5776 }\r
5777 });\r
5778 store.clearFilter();\r
5779 expect(store.indexOf(tommyRec)).toBe(0);\r
5780 expect(store.indexOf(edRec)).toBe(1);\r
5781 expect(store.indexOf(abeRec)).toBe(2);\r
5782 expect(store.indexOf(aaronRec)).toBe(3);\r
5783 });\r
5784 });\r
5785 });\r
5786\r
5787 describe("removal from collection", function() {\r
5788 beforeEach(function() {\r
5789 createStore();\r
5790 addStoreData();\r
5791 });\r
5792\r
5793 it("should not add records to the removed collection when filtering the store", function() {\r
5794 store.filter('group', 'code');\r
5795 expect(store.getRemovedRecords()).toEqual([]);\r
5796 });\r
5797\r
5798 it("should not add records to the removed collection when changing an item to be filtered", function() {\r
5799 store.filter('group', 'code');\r
5800 edRec.set('group', 'admin');\r
5801 expect(store.getRemovedRecords()).toEqual([]);\r
5802 });\r
5803 });\r
5804\r
5805 describe("joined status", function() {\r
5806 beforeEach(function() {\r
5807 createStore();\r
5808 addStoreData();\r
5809 });\r
5810\r
5811 it("should remain joined when filtering the store", function() {\r
5812 store.filter('group', 'code');\r
5813 expect(abeRec.joined).toEqual([store]);\r
5814 });\r
5815\r
5816 it("should remain joined when changing an item to be filtered", function() {\r
5817 store.filter('group', 'code');\r
5818 edRec.set('group', 'admin');\r
5819 expect(edRec.joined).toEqual([store]);\r
5820 });\r
5821\r
5822 it("should be joined when adding to the store but the item will be filtered", function() {\r
5823 store.filter('group', 'code');\r
5824 var rec = makeUser('new@sencha.com', {\r
5825 group: 'admin'\r
5826 });\r
5827 store.add(rec);\r
5828 expect(rec.joined).toEqual([store]);\r
5829 });\r
5830 });\r
5831 });\r
5832 \r
5833 describe("remote", function() {\r
5834 describe("errors", function() {\r
5835 beforeEach(function() {\r
5836 createStore({\r
5837 remoteFilter: true\r
5838 });\r
5839 addStoreData();\r
5840 });\r
5841\r
5842 it("should raise an exception when calling filterBy", function() {\r
5843 expect(function() {\r
5844 store.filterBy(function() {});\r
5845 }).toThrow();\r
5846 });\r
5847\r
5848 it("should raise an exception when calling addFilter with a filterFn", function() {\r
5849 expect(function() {\r
5850 store.addFilter({\r
5851 filterFn: function() {}\r
5852 });\r
5853 }).toThrow();\r
5854 });\r
5855\r
5856 it("should raise an exception when adding a filter with a filterFn", function() {\r
5857 expect(function() {\r
5858 store.getFilters().add({\r
5859 filterFn: function() {}\r
5860 });\r
5861 }).toThrow();\r
5862 });\r
5863 });\r
5864\r
5865 describe("during construction", function() {\r
5866 it("should not trigger a load when applying initial filters", function() {\r
5867 var spy = spyOn(Ext.data.ProxyStore.prototype, 'load');\r
5868 createStore({\r
5869 remoteFilter: true,\r
5870 filters: [{\r
5871 property: 'group',\r
5872 value: 'code'\r
5873 }]\r
5874 });\r
5875 expect(spy).not.toHaveBeenCalled();\r
5876 });\r
5877 });\r
5878 \r
5879 describe("modifying the filters", function() {\r
5880 beforeEach(function() {\r
5881 createStore({\r
5882 remoteFilter: true\r
5883 });\r
5884 });\r
5885 \r
5886 describe("the filter collection", function() {\r
5887 it("should trigger a load when adding a filter", function() {\r
5888 spyOn(store, 'load');\r
5889 store.getFilters().add({\r
5890 property: 'group',\r
5891 value: 'code'\r
5892 });\r
5893 expect(store.load.callCount).toBe(1);\r
5894 });\r
5895\r
5896 it("should trigger a load when adding to an existing filter", function() {\r
5897 store.getFilters().add({\r
5898 property: 'group',\r
5899 value: 'code'\r
5900 });\r
5901 spyOn(store, 'load');\r
5902 store.getFilters().add({\r
5903 property: 'evilness',\r
5904 value: 100\r
5905 });\r
5906 expect(store.load.callCount).toBe(1);\r
5907 });\r
5908\r
5909 it("should trigger a load when removing the only filter", function() {\r
5910 store.getFilters().add({\r
5911 property: 'group',\r
5912 value: 'code'\r
5913 });\r
5914 spyOn(store, 'load');\r
5915 store.getFilters().remove('group');\r
5916 expect(store.load.callCount).toBe(1);\r
5917 });\r
5918\r
5919 it("should trigger a load when removing a filter and there are others remaining", function() {\r
5920 store.getFilters().add({\r
5921 property: 'group',\r
5922 value: 'code'\r
5923 });\r
5924 store.getFilters().add({\r
5925 property: 'evilness',\r
5926 value: 100\r
5927 });\r
5928 spyOn(store, 'load');\r
5929 store.getFilters().remove('evilness');\r
5930 expect(store.load.callCount).toBe(1);\r
5931 });\r
5932 });\r
5933 });\r
5934 \r
5935 describe("store data", function() {\r
5936 beforeEach(function() {\r
5937 createStore({\r
5938 remoteFilter: true,\r
5939 proxy: {\r
5940 type: 'ajax',\r
5941 url: 'fakeurl'\r
5942 }\r
5943 }); \r
5944 });\r
5945 \r
5946 it("should not filter the data when the store load has completed", function() {\r
5947 store.getFilters().add({\r
5948 property: 'group',\r
5949 value: 'code'\r
5950 });\r
5951 completeWithData([aaronRaw, abeRaw]);\r
5952 expect(store.getCount()).toBe(2);\r
5953 });\r
5954 \r
5955 it("should not filter the data when adding a record", function() {\r
5956 store.getFilters().add({\r
5957 property: 'group',\r
5958 value: 'admin'\r
5959 });\r
5960 store.add(edRaw);\r
5961 expect(store.getCount()).toBe(1);\r
5962 });\r
5963 });\r
5964 });\r
5965 \r
5966 describe("events", function() {\r
5967 beforeEach(function() {\r
5968 createStore({\r
5969 remoteFilter: true\r
5970 });\r
5971 });\r
5972\r
5973 it("should fire when adding a filter to an empty collection", function() {\r
5974 var spy = jasmine.createSpy();\r
5975 store.on('filterchange', spy);\r
5976 store.getFilters().add({\r
5977 property: 'group',\r
5978 value: 'code'\r
5979 });\r
5980 expect(spy.callCount).toBe(1);\r
5981 var args = spy.mostRecentCall.args;\r
5982 expect(args[0]).toBe(store);\r
5983 expect(args[1].length).toBe(1);\r
5984 expect(args[1][0].getProperty()).toBe('group');\r
5985 expect(args[1][0].getValue()).toBe('code');\r
5986 });\r
5987\r
5988 it("should fire when adding a filter to existing filters", function() {\r
5989 var spy = jasmine.createSpy();\r
5990 store.getFilters().add({\r
5991 property: 'group',\r
5992 value: 'code'\r
5993 });\r
5994 store.on('filterchange', spy);\r
5995 store.getFilters().add({\r
5996 property: 'evilness',\r
5997 value: 100\r
5998 });\r
5999 expect(spy.callCount).toBe(1);\r
6000 var args = spy.mostRecentCall.args;\r
6001 expect(args[0]).toBe(store);\r
6002 expect(args[1].length).toBe(2);\r
6003 expect(args[1][0].getProperty()).toBe('group');\r
6004 expect(args[1][0].getValue()).toBe('code');\r
6005 expect(args[1][1].getProperty()).toBe('evilness');\r
6006 expect(args[1][1].getValue()).toBe(100);\r
6007 });\r
6008\r
6009 it("should fire when removing a filter from existing filter", function() {\r
6010 var spy = jasmine.createSpy();\r
6011 store.getFilters().add({\r
6012 property: 'group',\r
6013 value: 'code'\r
6014 });\r
6015 store.getFilters().add({\r
6016 property: 'evilness',\r
6017 value: 100\r
6018 });\r
6019 store.on('filterchange', spy);\r
6020 store.getFilters().remove('group');\r
6021 expect(spy.callCount).toBe(1);\r
6022 var args = spy.mostRecentCall.args;\r
6023 expect(args[0]).toBe(store);\r
6024 expect(args[1].length).toBe(1);\r
6025 expect(args[1][0].getProperty()).toBe('evilness');\r
6026 expect(args[1][0].getValue()).toBe(100);\r
6027 });\r
6028\r
6029 it("should fire when removing the last sorter", function() {\r
6030 var spy = jasmine.createSpy();\r
6031 store.getFilters().add({\r
6032 property: 'group',\r
6033 value: 'admin'\r
6034 });\r
6035 store.on('filterchange', spy);\r
6036 store.getFilters().remove('group');\r
6037 expect(spy.callCount).toBe(1);\r
6038 var args = spy.mostRecentCall.args;\r
6039 expect(args[0]).toBe(store);\r
6040 expect(args[1].length).toBe(0);\r
6041 });\r
6042 \r
6043 describe("local only", function() {\r
6044 beforeEach(function() {\r
6045 store.setRemoteFilter(false);\r
6046 });\r
6047 \r
6048 describe("adding", function() {\r
6049 it("should fire the refresh event", function() {\r
6050 var spy = jasmine.createSpy();\r
6051 store.on('refresh', spy);\r
6052 store.getFilters().add({\r
6053 property: 'group',\r
6054 value: 'code'\r
6055 });\r
6056 expect(spy.callCount).toBe(1);\r
6057 var args = spy.mostRecentCall.args;\r
6058 expect(args[0]).toBe(store);\r
6059 });\r
6060\r
6061 it("should fire the datachanged event", function() {\r
6062 var spy = jasmine.createSpy();\r
6063 store.on('datachanged', spy);\r
6064 store.getFilters().add({\r
6065 property: 'group',\r
6066 value: 'code'\r
6067 });\r
6068 expect(spy.callCount).toBe(1);\r
6069 var args = spy.mostRecentCall.args;\r
6070 expect(args[0]).toBe(store);\r
6071 });\r
6072 });\r
6073 \r
6074 describe("removing", function() {\r
6075 it("should fire the refresh event", function() {\r
6076 var spy = jasmine.createSpy();\r
6077 store.getFilters().add({\r
6078 property: 'group',\r
6079 value: 'code'\r
6080 });\r
6081 store.on('refresh', spy);\r
6082 store.getFilters().remove('group');\r
6083 expect(spy.callCount).toBe(1);\r
6084 var args = spy.mostRecentCall.args;\r
6085 expect(args[0]).toBe(store);\r
6086 });\r
6087\r
6088 it("should fire the datachanged event", function() {\r
6089 var spy = jasmine.createSpy();\r
6090 store.getFilters().add({\r
6091 property: 'group',\r
6092 value: 'code'\r
6093 });\r
6094 store.on('datachanged', spy);\r
6095 store.getFilters().remove('group');\r
6096 expect(spy.callCount).toBe(1);\r
6097 var args = spy.mostRecentCall.args;\r
6098 expect(args[0]).toBe(store);\r
6099 });\r
6100 });\r
6101 });\r
6102 });\r
6103 });\r
6104 \r
6105 describe("aggregation", function(){ \r
6106 \r
6107 beforeEach(function() {\r
6108 createStore({\r
6109 remoteSort: false,\r
6110 grouper: {\r
6111 property: 'group'\r
6112 },\r
6113 sorters: [{\r
6114 property: 'email'\r
6115 }]\r
6116 });\r
6117 addStoreData();\r
6118 aaronRec = store.getAt(0);\r
6119 abeRec = store.getAt(1);\r
6120 edRec = store.getAt(2);\r
6121 tommyRec = store.getAt(3);\r
6122 });\r
6123 \r
6124 describe("first", function(){ \r
6125 it("should ignore the grouped parameter if there's no group field", function(){\r
6126 store.clearGrouping();\r
6127 expect(store.first(true)).toBe(aaronRec);\r
6128 });\r
6129 \r
6130 it("should return in the correct grouped format", function(){\r
6131 expect(store.first(true)).toEqual({\r
6132 admin: aaronRec,\r
6133 code: edRec\r
6134 });\r
6135 });\r
6136 \r
6137 it("should return an empty object if grouped and no items", function(){\r
6138 store.removeAll();\r
6139 expect(store.first(true)).toEqual({});\r
6140 });\r
6141 });\r
6142 \r
6143 describe("last", function(){\r
6144 it("should ignore the grouped parameter if there's no group field", function(){\r
6145 store.clearGrouping();\r
6146 expect(store.last(true)).toBe(tommyRec);\r
6147 });\r
6148 \r
6149 it("should return in the correct grouped format", function(){\r
6150 expect(store.last(true)).toEqual({\r
6151 admin: abeRec,\r
6152 code: tommyRec\r
6153 });\r
6154 });\r
6155 \r
6156 it("should return an empty object if grouped and no items", function(){\r
6157 store.removeAll();\r
6158 expect(store.last(true)).toEqual({});\r
6159 });\r
6160 });\r
6161 \r
6162 describe("sum", function(){\r
6163 it("should return 0 if the store is empty", function(){\r
6164 store.removeAll();\r
6165 expect(store.sum('evilness')).toBe(0);\r
6166 });\r
6167 \r
6168 it("should sum the values specified by the property", function(){\r
6169 expect(store.sum('evilness')).toBe(160);\r
6170 });\r
6171 \r
6172 it("should ignore the grouped parameter if there's no group field", function(){\r
6173 store.clearGrouping();\r
6174 expect(store.sum('evilness', true)).toBe(160);\r
6175 });\r
6176 \r
6177 it("should return in the correct grouped format", function(){\r
6178 expect(store.sum('evilness', true)).toEqual({\r
6179 admin: 75,\r
6180 code: 85\r
6181 });\r
6182 });\r
6183 \r
6184 it("should return an empty object if grouped and no items", function(){\r
6185 store.removeAll();\r
6186 expect(store.sum('evilness', true)).toEqual({});\r
6187 });\r
6188 });\r
6189 \r
6190 describe("count", function(){\r
6191 it("should return 0 if the store is empty", function(){\r
6192 store.removeAll();\r
6193 expect(store.count()).toBe(0);\r
6194 });\r
6195 \r
6196 it("should count the values in the store", function(){\r
6197 expect(store.count()).toBe(4);\r
6198 });\r
6199 \r
6200 it("should ignore the grouped parameter if there's no group field", function(){\r
6201 store.clearGrouping();\r
6202 expect(store.count(true)).toBe(4);\r
6203 });\r
6204 \r
6205 it("should return in the correct grouped format", function(){\r
6206 expect(store.count(true)).toEqual({\r
6207 admin: 2,\r
6208 code: 2\r
6209 });\r
6210 });\r
6211 \r
6212 it("should return an empty object if grouped and no items", function(){\r
6213 store.removeAll();\r
6214 expect(store.count(true)).toEqual({});\r
6215 });\r
6216 });\r
6217 \r
6218 describe("min", function(){\r
6219 it("should return undefined if there are no items", function(){\r
6220 store.removeAll();\r
6221 expect(store.min('age')).toBeUndefined();\r
6222 });\r
6223 \r
6224 it("should return the minimum value", function(){\r
6225 expect(store.min('age')).toBe(20);\r
6226 });\r
6227 \r
6228 it("should ignore the grouped parameter if there's no group field", function(){\r
6229 store.clearGrouping();\r
6230 expect(store.min('age', true)).toBe(20);\r
6231 });\r
6232 \r
6233 it("should return in the correct grouped format", function(){\r
6234 expect(store.min('age', true)).toEqual({\r
6235 admin: 20,\r
6236 code: 25\r
6237 });\r
6238 });\r
6239 \r
6240 it("should return an empty object if grouped and no items", function(){\r
6241 store.removeAll();\r
6242 expect(store.min('age', true)).toEqual({});\r
6243 });\r
6244 });\r
6245 \r
6246 describe("max", function(){\r
6247 it("should return undefined if there are no items", function(){\r
6248 store.removeAll();\r
6249 expect(store.max('age')).toBeUndefined();\r
6250 });\r
6251 \r
6252 it("should return the maximum value", function(){\r
6253 expect(store.max('age')).toBe(70);\r
6254 });\r
6255 \r
6256 it("should ignore the grouped parameter if there's no group field", function(){\r
6257 store.clearGrouping();\r
6258 expect(store.max('age', true)).toBe(70);\r
6259 });\r
6260 \r
6261 it("should return in the correct grouped format", function(){\r
6262 expect(store.max('age', true)).toEqual({\r
6263 admin: 26,\r
6264 code: 70\r
6265 });\r
6266 });\r
6267 \r
6268 it("should return an empty object if grouped and no items", function(){\r
6269 store.removeAll();\r
6270 expect(store.max('age', true)).toEqual({});\r
6271 });\r
6272 });\r
6273 \r
6274 describe("average", function(){\r
6275 it("should return 0 if there are no items", function(){\r
6276 store.removeAll();\r
6277 expect(store.average('evilness')).toBe(0);\r
6278 });\r
6279 \r
6280 it("should return the correct average", function(){\r
6281 expect(store.average('evilness')).toBe(40);\r
6282 });\r
6283 \r
6284 it("should ignore the grouped parameter if there's no groupField", function(){\r
6285 store.clearGrouping();\r
6286 expect(store.average('evilness')).toBe(40);\r
6287 });\r
6288 \r
6289 it("should return in the correct grouped format", function(){\r
6290 expect(store.average('evilness', true)).toEqual({\r
6291 admin: 37.5,\r
6292 code: 42.5\r
6293 });\r
6294 });\r
6295 \r
6296 it("should return an empty object if grouped and no items", function(){\r
6297 store.removeAll();\r
6298 expect(store.average('evilness', true)).toEqual({});\r
6299 });\r
6300 });\r
6301 \r
6302 describe("aggregate", function(){\r
6303 it("should default the scope to the store", function(){\r
6304 var spy = jasmine.createSpy();\r
6305 store.aggregate(spy);\r
6306 expect(spy.mostRecentCall.object).toBe(store);\r
6307 });\r
6308 \r
6309 it("should use any custom scope", function(){\r
6310 var spy = jasmine.createSpy();\r
6311 store.aggregate(spy, fakeScope);\r
6312 expect(spy.mostRecentCall.object).toBe(fakeScope);\r
6313 });\r
6314 \r
6315 it("should call the custom function with an array of values & records", function(){\r
6316 var isArray = true;\r
6317 store.aggregate(function(records, values){\r
6318 isArray = isArray && Ext.isArray(values) && Ext.isArray(records);\r
6319 }, null, false, 'email');\r
6320 expect(isArray).toBe(true);\r
6321 });\r
6322 \r
6323 it("should allow the field parameter to be optional", function(){\r
6324 var value;\r
6325 \r
6326 store.aggregate(function(records, values){\r
6327 value = values[0];\r
6328 });\r
6329 expect(value).toBeUndefined();\r
6330 });\r
6331 \r
6332 it("should pass the field values", function(){\r
6333 var emails;\r
6334 store.aggregate(function(records, values) {\r
6335 emails = values;\r
6336 }, null, true, 'email');\r
6337 expect(emails).toEqual(['ed@sencha.com', 'tommy@sencha.com']);\r
6338 });\r
6339 \r
6340 describe("grouped", function(){\r
6341 it("should return an empty object if there are no groups", function(){\r
6342 store.removeAll();\r
6343 expect(store.aggregate(Ext.emptyFn, null, true)).toEqual({});\r
6344 });\r
6345 \r
6346 it("should return undefined if there is no groupField", function(){\r
6347 store.clearGrouping();\r
6348 expect(store.aggregate(Ext.emptyFn, null, true)).toBeUndefined();\r
6349 });\r
6350\r
6351 it("should return the groups with the aggregated value", function(){\r
6352 var result = store.aggregate(function(records, values){\r
6353 return values.join('');\r
6354 }, null, true, 'email');\r
6355 expect(result).toEqual({\r
6356 admin: 'aaron@sencha.comabe@sencha.com',\r
6357 code: 'ed@sencha.comtommy@sencha.com'\r
6358 });\r
6359 });\r
6360 });\r
6361 });\r
6362 });\r
6363 \r
6364 describe("updating records", function() {\r
6365 var spy;\r
6366 beforeEach(function() {\r
6367 createStore();\r
6368 addStoreData();\r
6369 spy = jasmine.createSpy();\r
6370 });\r
6371 \r
6372 describe("via set", function() {\r
6373 describe("a single value", function() {\r
6374 it("should fire the update event but not the datachanged event", function() {\r
6375 var datachangedSpy = jasmine.createSpy();\r
6376\r
6377 store.on('update', spy);\r
6378 store.on('datachanged', datachangedSpy);\r
6379 edRec.set('name', 'Ned Spencer');\r
6380 expect(spy.callCount).toBe(1);\r
6381\r
6382 // datachanged is only for record additions/removals\r
6383 expect(datachangedSpy).not.toHaveBeenCalled();\r
6384 });\r
6385 \r
6386 it("should pass the store, model, type & modified field", function() {\r
6387 store.on('update', spy);\r
6388 edRec.set('name', 'Ned Spencer');\r
6389 var args = spy.mostRecentCall.args;\r
6390 expect(args[0]).toBe(store);\r
6391 expect(args[1]).toBe(edRec);\r
6392 expect(args[2]).toBe(Ext.data.Model.EDIT);\r
6393 expect(args[3]).toEqual(['name']);\r
6394 });\r
6395\r
6396 it("should not fire an event if the record was removed from the store", function() {\r
6397 store.on('update', spy);\r
6398 store.remove(edRec);\r
6399 edRec.set('name', 'Ned Spencer');\r
6400 expect(spy).not.toHaveBeenCalled();\r
6401 });\r
6402 });\r
6403 \r
6404 describe("multiple values", function() {\r
6405 it("should fire update once but not the datachanged event", function() {\r
6406 var datachangedSpy = jasmine.createSpy();\r
6407\r
6408 store.on('update', spy);\r
6409 store.on('datachanged', datachangedSpy);\r
6410 edRec.set({\r
6411 name: 'Ned Spencer',\r
6412 evilness: 9000\r
6413 });\r
6414 expect(spy.callCount).toBe(1);\r
6415\r
6416 // datachanged is only for record additions/removals\r
6417 expect(datachangedSpy).not.toHaveBeenCalled();\r
6418 });\r
6419 \r
6420 it("should pass the store, model, type & modified fields", function() {\r
6421 store.on('update', spy);\r
6422 edRec.set({\r
6423 name: 'Ned Spencer',\r
6424 evilness: 9000\r
6425 });\r
6426 var args = spy.mostRecentCall.args;\r
6427 expect(args[0]).toBe(store);\r
6428 expect(args[1]).toBe(edRec);\r
6429 expect(args[2]).toBe(Ext.data.Model.EDIT);\r
6430 expect(args[3]).toEqual(['name', 'evilness']);\r
6431 });\r
6432\r
6433 it("should not fire an event if the record was removed from the store", function() {\r
6434 store.on('update', spy);\r
6435 store.remove(edRec);\r
6436 edRec.set({\r
6437 name: 'Ned Spencer',\r
6438 evilness: 9000\r
6439 });\r
6440 expect(spy).not.toHaveBeenCalled();\r
6441 });\r
6442 });\r
6443 \r
6444 describe("changing the id", function() {\r
6445 it("should remap the id value", function() {\r
6446 edRec.setId('ted@sencha.com');\r
6447 expect(store.getById('ted@sencha.com')).toBe(edRec);\r
6448 });\r
6449 \r
6450 it("should remap the id even when filtered out", function() {\r
6451 store.filterBy(function(rec) {\r
6452 return rec !== edRec;\r
6453 });\r
6454 edRec.setId('red@sencha.com');\r
6455 store.clearFilter();\r
6456 expect(store.getById('red@sencha.com')).toBe(edRec);\r
6457 });\r
6458 });\r
6459 \r
6460 describe("when sorted", function() {\r
6461 it("should move the record into the correct position", function() {\r
6462 store.sort('email');\r
6463 tommyRec.set('email', 'aaa@sencha.com');\r
6464 expect(store.indexOf(tommyRec)).toBe(0);\r
6465 });\r
6466\r
6467 it("should not be moving if the position is changed", function() {\r
6468 store.on('add', spy);\r
6469 store.on('remove', spy);\r
6470 store.sort('email');\r
6471 tommyRec.set('name', 'Foo');\r
6472 expect(spy).not.toHaveBeenCalled();\r
6473 });\r
6474\r
6475 it("should be moving in the refresh event if changing position", function() {\r
6476 var remove;\r
6477\r
6478 store.sort('email');\r
6479\r
6480 store.on('refresh', function() {\r
6481 remove = store.isMoving(tommyRec);\r
6482 });\r
6483\r
6484 tommyRec.set('email', 'aaa@sencha.com');\r
6485 expect(remove).toBe(1);\r
6486 });\r
6487 });\r
6488 \r
6489 describe("when filtered", function() {\r
6490 describe("change causes the record to be excluded", function() {\r
6491 it("should remove the record active set", function() {\r
6492 store.filter('group', 'code');\r
6493 edRec.set('group', 'admin');\r
6494 expect(store.indexOf(edRec)).toBe(-1);\r
6495 });\r
6496 \r
6497 it("should still fire the update event", function() {\r
6498 store.on('update', spy);\r
6499 store.filter('group', 'code');\r
6500 edRec.set('group', 'admin');\r
6501\r
6502 // Data events still apply even when nodes are filtered from visibility\r
6503 expect(spy.callCount).toBe(1);\r
6504 });\r
6505 });\r
6506 \r
6507 describe("change causes the record to be included", function() {\r
6508 it("should add the record to the active set if changed to match", function() {\r
6509 store.filter('group', 'code');\r
6510 aaronRec.set('group', 'code');\r
6511 expect(store.indexOf(aaronRec)).toBe(2);\r
6512 });\r
6513 \r
6514 it("should fire the update event", function() {\r
6515 store.on('update', spy);\r
6516 store.filter('group', 'code');\r
6517 aaronRec.set('group', 'code');\r
6518 expect(spy.callCount).toBe(1);\r
6519 });\r
6520 });\r
6521 });\r
6522 });\r
6523 \r
6524 describe("via commit", function() {\r
6525 it("should fire the update event", function() {\r
6526 edRec.set('name', 'Foo');\r
6527 store.on('update', spy);\r
6528 edRec.commit();\r
6529 expect(spy.callCount).toBe(1);\r
6530 });\r
6531 \r
6532 it("should pass the store, model, type & null (modified fields)", function() {\r
6533 edRec.set('name', 'Foo');\r
6534 edRec.set('age', 40);\r
6535 store.on('update', spy);\r
6536 edRec.commit();\r
6537 var args = spy.mostRecentCall.args;\r
6538 expect(args[0]).toBe(store);\r
6539 expect(args[1]).toBe(edRec);\r
6540 expect(args[2]).toBe(Ext.data.Model.COMMIT);\r
6541 // Modified will be null, since we pass nothing to commit\r
6542 expect(args[3]).toBeNull();\r
6543 });\r
6544\r
6545 it("should still fire the update event if the record is filtered out", function() {\r
6546 edRec.set('name', 'Foo');\r
6547 store.on('update', spy);\r
6548 store.filter('name', 'Aaron');\r
6549 edRec.commit();\r
6550 expect(spy.callCount).toBe(1);\r
6551 });\r
6552\r
6553 it("should not fire the update event when removed", function() {\r
6554 edRec.set('name', 'Foo');\r
6555 store.on('update', spy);\r
6556 store.remove(edRec);\r
6557 edRec.commit();\r
6558 expect(spy).not.toHaveBeenCalled();\r
6559 });\r
6560 });\r
6561 \r
6562 describe("via reject", function() {\r
6563 it("should fire the update event", function() {\r
6564 edRec.set('name', 'Foo');\r
6565 store.on('update', spy);\r
6566 edRec.reject();\r
6567 expect(spy.callCount).toBe(1);\r
6568 });\r
6569 \r
6570 it("should pass the store, model, type & null (modified fields)", function() {\r
6571 edRec.set('name', 'Foo');\r
6572 store.on('update', spy);\r
6573 edRec.reject();\r
6574 var args = spy.mostRecentCall.args;\r
6575 expect(args[0]).toBe(store);\r
6576 expect(args[1]).toBe(edRec);\r
6577 expect(args[2]).toBe(Ext.data.Model.REJECT);\r
6578 expect(args[3]).toBeNull();\r
6579 });\r
6580 \r
6581 it("should still fire the update event if the record is filtered out", function() {\r
6582 edRec.set('name', 'Foo');\r
6583 store.on('update', spy);\r
6584 store.filter('name', 'Aaron');\r
6585 edRec.reject();\r
6586 expect(spy.callCount).toBe(1);\r
6587 });\r
6588\r
6589 it("should not fire the update event when removed", function() {\r
6590 edRec.set('name', 'Foo');\r
6591 store.on('update', spy);\r
6592 store.remove(edRec);\r
6593 edRec.reject();\r
6594 expect(spy).not.toHaveBeenCalled();\r
6595 });\r
6596 });\r
6597 \r
6598 describe("via erase", function() {\r
6599 it("should remove the record from the store", function() {\r
6600 edRec.erase();\r
6601 expect(store.indexOf(edRec)).toBe(-1);\r
6602\r
6603 // Because the erase operation communicates the destruction to the server,\r
6604 // it should not trigger the store to need a sync, and should not add to the to remove stack.\r
6605 expect(store.needsSync).toBe(false);\r
6606 expect(store.getRemovedRecords().length).toBe(0);\r
6607 });\r
6608\r
6609 it("should fire the remove event", function() {\r
6610 store.on('remove', spy);\r
6611 edRec.erase();\r
6612 expect(spy.callCount).toBe(1);\r
6613 });\r
6614 });\r
6615\r
6616 describe("via drop", function() {\r
6617 it("should remove the record from the store", function() {\r
6618 edRec.drop();\r
6619 expect(store.indexOf(edRec)).toBe(-1);\r
6620 });\r
6621\r
6622 it("should remove the record from the store", function() {\r
6623 store.on('remove', spy);\r
6624 edRec.drop();\r
6625 expect(spy.callCount).toBe(1);\r
6626 });\r
6627 });\r
6628 });\r
6629\r
6630 describe("commitChanges", function() {\r
6631 beforeEach(function() {\r
6632 createStore();\r
6633 addStoreData();\r
6634 });\r
6635\r
6636 describe("committing records", function() {\r
6637 it("should commit valid phantoms", function() {\r
6638 var phantom = store.add({\r
6639 validField: 'Foo'\r
6640 })[0];\r
6641 spyOn(phantom, 'commit');\r
6642 store.commitChanges();\r
6643 expect(phantom.commit.callCount).toBe(1);\r
6644 });\r
6645\r
6646 it("should not commit invalid phantoms", function() {\r
6647 var phantom = store.add({\r
6648 validField: null\r
6649 })[0];\r
6650 spyOn(phantom, 'commit');\r
6651 store.commitChanges();\r
6652 expect(phantom.commit).not.toHaveBeenCalled();\r
6653 });\r
6654\r
6655 it("should commit valid dirty non-phantoms", function() {\r
6656 edRec.set('validField', 'Foo');\r
6657 spyOn(edRec, 'commit');\r
6658 store.commitChanges();\r
6659 expect(edRec.commit.callCount).toBe(1);\r
6660 });\r
6661\r
6662 it("should not commit invalid dirty non-phantoms", function() {\r
6663 edRec.set('validField', null);\r
6664 spyOn(edRec, 'commit');\r
6665 store.commitChanges();\r
6666 expect(edRec.commit).not.toHaveBeenCalled();\r
6667 });\r
6668\r
6669 it("should not commit non-dirty records", function() {\r
6670 edRec.set('validField', 'foo');\r
6671 edRec.commit();\r
6672 spyOn(edRec, 'commit');\r
6673 store.commitChanges();\r
6674 expect(edRec.commit).not.toHaveBeenCalled();\r
6675\r
6676 });\r
6677 });\r
6678 \r
6679 describe("removed collection", function() {\r
6680 it("should clear the removed collection", function() {\r
6681 store.remove(abeRec);\r
6682 store.remove(aaronRec);\r
6683 expect(store.getCount()).toBe(2);\r
6684 expect(store.removed.length).toBe(2);\r
6685 store.commitChanges();\r
6686 expect(store.removed.length).toBe(0);\r
6687 expect(store.getCount()).toBe(2);\r
6688 });\r
6689 });\r
6690\r
6691 describe("events", function() {\r
6692 it("should fire an update event for each record", function() {\r
6693 var spy = jasmine.createSpy();\r
6694 edRec.set('validField', 'A');\r
6695 aaronRec.set('validField', 'B');\r
6696 tommyRec.set('validField', 'C');\r
6697 store.on('update', spy);\r
6698 store.commitChanges();\r
6699 expect(spy.callCount).toBe(3);\r
6700 expect(spy.calls[0].args[1]).toBe(edRec);\r
6701 expect(spy.calls[1].args[1]).toBe(aaronRec);\r
6702 expect(spy.calls[2].args[1]).toBe(tommyRec);\r
6703 });\r
6704 });\r
6705 });\r
6706\r
6707 describe("rejectChanges", function() {\r
6708 beforeEach(function() {\r
6709 createStore();\r
6710 addStoreData();\r
6711 });\r
6712\r
6713 describe("rejecting records", function() {\r
6714 it("should reject dirty records", function() {\r
6715 edRec.set('name', 'Foo');\r
6716 var spy = spyOn(edRec, 'reject');\r
6717 store.rejectChanges();\r
6718 expect(spy.callCount).toBe(1);\r
6719 });\r
6720\r
6721 it("should reject phantom records", function() {\r
6722 var phantom = store.add({\r
6723 name: 'X'\r
6724 })[0];\r
6725 var spy = spyOn(phantom, 'reject');\r
6726 store.rejectChanges();\r
6727 expect(spy.callCount).toBe(1);\r
6728 });\r
6729\r
6730 it("should reject phantom + dirty records", function() {\r
6731 var phantom = store.add({\r
6732 name: 'X'\r
6733 })[0];\r
6734 phantom.set('name', 'Y');\r
6735 var spy = spyOn(phantom, 'reject');\r
6736 store.rejectChanges();\r
6737 expect(spy.callCount).toBe(1);\r
6738 });\r
6739\r
6740 it("should not reject non-phantom non-dirty records", function() {\r
6741 var edSpy = spyOn(edRec, 'reject'),\r
6742 abeSpy = spyOn(abeRec, 'reject'),\r
6743 aaronSpy = spyOn(aaronRec, 'reject'),\r
6744 tommySpy = spyOn(tommyRec, 'reject');\r
6745\r
6746 store.rejectChanges();\r
6747 expect(edSpy).not.toHaveBeenCalled();\r
6748 expect(abeSpy).not.toHaveBeenCalled();\r
6749 expect(aaronSpy).not.toHaveBeenCalled();\r
6750 expect(tommySpy).not.toHaveBeenCalled();\r
6751 });\r
6752 });\r
6753\r
6754 describe('rejecting non-filtered records', function () {\r
6755 it('should reject dirty records', function() {\r
6756 store.filter('name', 'Ed Spencer');\r
6757 expect(data.getAt(0)).toBe(edRec);\r
6758\r
6759 edRec.set('name', 'Utley Spencer');\r
6760 expect(data.length).toBe(0);\r
6761\r
6762 // Rejecting the change should move the record back into the data collection.\r
6763 store.rejectChanges();\r
6764 expect(data.getAt(0)).toBe(edRec);\r
6765 });\r
6766\r
6767 it('should reject phantom records', function () {\r
6768 var len = store.getDataSource().length,\r
6769 phantom = store.add({\r
6770 name: 'X'\r
6771 })[0];\r
6772\r
6773 store.filter('name', 'Pete');\r
6774 // Rejecting the change should remove the phantom record should from the source.\r
6775 store.rejectChanges();\r
6776\r
6777 expect(store.getDataSource().length).toBe(len);\r
6778 });\r
6779\r
6780 it('should reject phantom + dirty records', function () {\r
6781 var len = store.getDataSource().length,\r
6782 phantom = store.add({\r
6783 name: 'X'\r
6784 })[0];\r
6785\r
6786 store.filter('name', 'Molly');\r
6787 phantom.set('name', 'Y');\r
6788\r
6789 // Rejecting the change should remove the phantom record should from the source.\r
6790 store.rejectChanges();\r
6791 expect(store.getDataSource().length).toBe(len);\r
6792 });\r
6793 });\r
6794\r
6795 describe("removing phantoms", function() {\r
6796 it("should remove phantoms", function() {\r
6797 var phantom1 = store.insert(2, {\r
6798 name: 'X'\r
6799 })[0];\r
6800\r
6801 var phantom2 = store.add({\r
6802 name: 'Y'\r
6803 })[0];\r
6804\r
6805 expect(store.getCount()).toBe(6);\r
6806 store.rejectChanges();\r
6807 expect(store.getCount()).toBe(4);\r
6808 expect(store.indexOf(phantom1)).toBe(-1);\r
6809 expect(store.indexOf(phantom2)).toBe(-1);\r
6810 });\r
6811 });\r
6812\r
6813 describe("re-adding removed records", function() {\r
6814 it("should re-insert removed records", function() {\r
6815 store.remove(abeRec);\r
6816 expect(store.getCount()).toBe(3);\r
6817 store.rejectChanges();\r
6818 expect(store.getAt(1)).toBe(abeRec);\r
6819 });\r
6820\r
6821 it("should clear the removed queue", function() {\r
6822 store.remove(abeRec);\r
6823 expect(store.removed.length).toBe(1);\r
6824 store.rejectChanges();\r
6825 expect(store.removed.length).toBe(0);\r
6826 });\r
6827\r
6828 describe('with and without sorters', function () {\r
6829 function doTests(sorter) {\r
6830 var count;\r
6831\r
6832 beforeEach(function () {\r
6833 if (sorter) {\r
6834 store.sort(sorter);\r
6835 }\r
6836\r
6837 count = store.count;\r
6838 });\r
6839\r
6840 afterEach(function () {\r
6841 count = null;\r
6842 });\r
6843\r
6844 it('should reject dropped records', function () {\r
6845 store.getAt(0).drop();\r
6846 store.rejectChanges();\r
6847 expect(store.count).toBe(count);\r
6848 });\r
6849\r
6850 it('should reject erased records', function () {\r
6851 store.getAt(0).erase();\r
6852 store.rejectChanges();\r
6853 expect(store.count).toBe(count);\r
6854 });\r
6855 }\r
6856\r
6857 doTests(null);\r
6858 doTests({property: 'name', direction: 'DESC'});\r
6859 });\r
6860 });\r
6861\r
6862 describe("events", function() {\r
6863 it("should not fire an update event for phantom records", function() {\r
6864 var phantom1 = store.insert(2, {\r
6865 name: 'X'\r
6866 })[0];\r
6867 var spy = jasmine.createSpy();\r
6868 store.on('update', spy);\r
6869 store.rejectChanges();\r
6870 expect(spy).not.toHaveBeenCalled();\r
6871 });\r
6872\r
6873 it("should fire a remove event for phantom records", function() {\r
6874 var phantom1 = store.insert(0, {\r
6875 name: 'X'\r
6876 })[0];\r
6877\r
6878 var phantom2 = store.add({\r
6879 name: 'Y'\r
6880 })[0];\r
6881 var spy = jasmine.createSpy();\r
6882 store.on('remove', spy);\r
6883 store.rejectChanges();\r
6884 expect(spy.callCount).toBe(2);\r
6885 expect(spy.calls[0].args[1]).toEqual([phantom2]);\r
6886 expect(spy.calls[1].args[1]).toEqual([phantom1]);\r
6887 });\r
6888\r
6889 it("should fire an update event for non-phantom records", function() {\r
6890 edRec.set('name', 'A');\r
6891 tommyRec.set('name', 'B');\r
6892 var spy = jasmine.createSpy();\r
6893 store.on('update', spy);\r
6894 store.rejectChanges();\r
6895 expect(spy.callCount).toBe(2);\r
6896 expect(spy.calls[0].args[1]).toBe(edRec);\r
6897 expect(spy.calls[1].args[1]).toBe(tommyRec);\r
6898 });\r
6899\r
6900 it("should not fire an update event for removed records", function() {\r
6901 var spy = jasmine.createSpy();\r
6902 store.remove(edRec);\r
6903 store.on('update', spy);\r
6904 store.rejectChanges();\r
6905 expect(spy).not.toHaveBeenCalled();\r
6906 });\r
6907\r
6908 it("should not fire an update event for removed records, with sorters", function() {\r
6909 var spy = jasmine.createSpy();\r
6910 store.sort('name', 'DESC');\r
6911 store.remove(edRec);\r
6912 store.on('update', spy);\r
6913 store.rejectChanges();\r
6914 expect(spy).not.toHaveBeenCalled();\r
6915 });\r
6916\r
6917 it("should fire add events for readded records", function() {\r
6918 var spy = jasmine.createSpy();\r
6919 store.remove(edRec);\r
6920 store.on('add', spy);\r
6921 store.rejectChanges();\r
6922 expect(spy.callCount).toBe(1);\r
6923 expect(spy.mostRecentCall.args[1]).toEqual([edRec]);\r
6924 });\r
6925 });\r
6926 });\r
6927\r
6928 describe("metachange event", function () {\r
6929 var wasCalled = false,\r
6930 successData = {\r
6931 success: true,\r
6932 data: [\r
6933 {name: 'alex'},\r
6934 {name: 'ben'},\r
6935 {name: 'don'},\r
6936 {name: 'evan'},\r
6937 {name: 'nige'},\r
6938 {name: 'phil'}\r
6939 ],\r
6940 metaData: {\r
6941 root: 'data'\r
6942 }\r
6943 },\r
6944 args, storeArg, metaArg;\r
6945\r
6946 beforeEach(function () {\r
6947 createStore({\r
6948 proxy: {\r
6949 type: "ajax",\r
6950 url: "foo"\r
6951 },\r
6952 listeners: {\r
6953 metachange: function (store, meta) {\r
6954 wasCalled = true;\r
6955 args = arguments;\r
6956 storeArg = store;\r
6957 metaArg = meta;\r
6958 }\r
6959 }\r
6960 });\r
6961\r
6962 store.load();\r
6963 completeWithData(successData);\r
6964 });\r
6965\r
6966 afterEach(function () {\r
6967 wasCalled = false;\r
6968 args = storeArg = metaArg = null;\r
6969 });\r
6970\r
6971 it("should call the listener", function () {\r
6972 expect(wasCalled).toBe(true);\r
6973 });\r
6974\r
6975 it("should return the store", function () {\r
6976 expect(storeArg).toBe(store);\r
6977 });\r
6978\r
6979 it("should return the meta data", function () {\r
6980 expect(metaArg).toEqual(successData.metaData);\r
6981 });\r
6982\r
6983 it("should return the store as the first arg", function () {\r
6984 expect(args[0]).toBe(store);\r
6985 });\r
6986\r
6987 it("should return the meta data as the second arg", function () {\r
6988 expect(args[1]).toBe(metaArg);\r
6989 });\r
6990\r
6991 describe("disableMetaChangeEvent (for associated models)", function () {\r
6992 var wasCalled = false;\r
6993\r
6994 afterEach(function () {\r
6995 wasCalled = false;\r
6996 });\r
6997\r
6998 it("should not be set by default", function () {\r
6999 createStore({\r
7000 proxy: {\r
7001 type: "ajax",\r
7002 url: "foo"\r
7003 },\r
7004 listeners: {\r
7005 metachange: function (store, meta) {\r
7006 wasCalled = true;\r
7007 }\r
7008 }\r
7009 });\r
7010\r
7011 store.load();\r
7012 completeWithData(successData);\r
7013\r
7014 expect(wasCalled).toBe(true);\r
7015 });\r
7016\r
7017 it("should not fire the event if `true`", function () {\r
7018 createStore({\r
7019 disableMetaChangeEvent: true,\r
7020 proxy: {\r
7021 type: "ajax",\r
7022 url: "foo"\r
7023 },\r
7024 listeners: {\r
7025 metachange: function (store, meta) {\r
7026 wasCalled = true;\r
7027 }\r
7028 }\r
7029 });\r
7030\r
7031 store.load();\r
7032 completeWithData(successData);\r
7033\r
7034 expect(wasCalled).toBe(false);\r
7035 });\r
7036\r
7037 it("should fire the event if `false`", function () {\r
7038 createStore({\r
7039 disableMetaChangeEvent: false,\r
7040 proxy: {\r
7041 type: "ajax",\r
7042 url: "foo"\r
7043 },\r
7044 listeners: {\r
7045 metachange: function (store, meta) {\r
7046 wasCalled = true;\r
7047 }\r
7048 }\r
7049 });\r
7050\r
7051 store.load();\r
7052 completeWithData(successData);\r
7053\r
7054 expect(wasCalled).toBe(true);\r
7055 });\r
7056 });\r
7057 });\r
7058\r
7059 describe("autoSync", function() {\r
7060 var doSync;\r
7061\r
7062 beforeEach(function() {\r
7063 createStore({\r
7064 autoSync: true\r
7065 }, true);\r
7066 spyOn(store, 'sync').andCallFake(function() {\r
7067\r
7068 // If the doSync flag is set, make the call.\r
7069 if (doSync) {\r
7070 store.self.prototype.sync.apply(store, arguments);\r
7071 doSync = false;\r
7072 } else {\r
7073 store.needsSync = false;\r
7074 }\r
7075 });\r
7076 });\r
7077 describe("adding", function() {\r
7078 it("should trigger a sync if the record is a phantom", function() {\r
7079 store.add({});\r
7080 expect(store.sync.callCount).toBe(1);\r
7081 });\r
7082\r
7083 it("should trigger a sync if the record is dirty", function() {\r
7084 var rec = makeUser('foo@sencha.com');\r
7085 rec.set('name', 'foo');\r
7086 store.add(rec);\r
7087 expect(store.sync.callCount).toBe(1);\r
7088 });\r
7089\r
7090 it("should not trigger a sync if the record is not a phantom or dirty", function() {\r
7091 var rec = makeUser('foo@sencha.com');\r
7092 store.add(rec);\r
7093 expect(store.sync).not.toHaveBeenCalled();\r
7094 });\r
7095\r
7096 it("should only trigger a single sync when adding multiple records", function() {\r
7097 store.add([{}, {}, {}, {}]);\r
7098 expect(store.sync.callCount).toBe(1);\r
7099 });\r
7100\r
7101 describe("with sorting", function() {\r
7102 it("should only trigger a single sync when adding multiple records in a discontiguous range", function() {\r
7103 store.sort('email');\r
7104 var recs = [makeUser('aaa@sencha.com'), makeUser('foo@sencha.com'), makeUser('zzz@sencha.com')];\r
7105 Ext.Array.forEach(recs, function(rec, i) {\r
7106 rec.set('name', 'foo' + i);\r
7107 });\r
7108 store.add(recs);\r
7109 expect(store.sync.callCount).toBe(1);\r
7110 });\r
7111 });\r
7112\r
7113 describe("with filtering", function() {\r
7114 it("should trigger a sync if the record is filtered out", function() {\r
7115 store.filter('name', 'Foo');\r
7116 store.add({\r
7117 name: 'Bar'\r
7118 });\r
7119 expect(store.sync.callCount).toBe(1);\r
7120 });\r
7121 });\r
7122 });\r
7123\r
7124 describe("removing", function() {\r
7125 it("should not trigger a sync if the record is a phantom", function() {\r
7126 var rec = store.add({})[0];\r
7127 store.sync.reset();\r
7128 store.remove(rec);\r
7129 expect(store.sync).not.toHaveBeenCalled();\r
7130 });\r
7131\r
7132 it("should trigger when removing a record", function() {\r
7133 store.remove(edRec);\r
7134 expect(store.sync.callCount).toBe(1);\r
7135 });\r
7136\r
7137 it("should only trigger a single sync when removing multiple records", function() {\r
7138 store.remove([edRec, tommyRec]);\r
7139 expect(store.sync.callCount).toBe(1);\r
7140 });\r
7141 });\r
7142\r
7143 describe("updating", function() {\r
7144 it("should not trigger a sync if the record is not dirty", function() {\r
7145 edRec.set('name', 'Foo');\r
7146 store.sync.reset();\r
7147 edRec.set('name', 'Ed Spencer');\r
7148 expect(store.sync).not.toHaveBeenCalled();\r
7149 });\r
7150\r
7151 it("should trigger a sync if the record is dirty", function() {\r
7152 edRec.set('name', 'Foo');\r
7153 expect(store.sync.callCount).toBe(1);\r
7154 });\r
7155 });\r
7156\r
7157 describe("removing with sync response", function() {\r
7158 it("should trigger when removing a record", function() {\r
7159 doSync = true;\r
7160 store.remove(edRec);\r
7161 expect(store.sync.callCount).toBe(1);\r
7162\r
7163 // Before the successful response, the removed queue is still filled\r
7164 expect(store.getRemovedRecords().length).toBe(1);\r
7165\r
7166 completeWithData('"{"success":true,"records":[{"id":"ed@sencha.com"}]}"');\r
7167\r
7168 // Upon success response, the removed queue is drained\r
7169 expect(store.getRemovedRecords().length).toBe(0);\r
7170 });\r
7171\r
7172 it("should only trigger a single sync when removing multiple records", function() {\r
7173 doSync = true;\r
7174 store.remove([edRec, tommyRec]);\r
7175 expect(store.sync.callCount).toBe(1);\r
7176\r
7177 // Before the successful response, the removed queue is still filled\r
7178 expect(store.getRemovedRecords().length).toBe(2);\r
7179\r
7180 completeWithData('"{"success":true,"records":[{"id":"tommy@sencha.com"},{"id":"ed@sencha.com"}]}"');\r
7181\r
7182 // Upon success response, the removed queue is drained\r
7183 expect(store.getRemovedRecords().length).toBe(0);\r
7184 });\r
7185 });\r
7186 });\r
7187\r
7188 describe("joining/unjoining the the store", function() {\r
7189 function expectJoined(rec) {\r
7190 var joined = rec.joined || [];\r
7191 expect(Ext.Array.indexOf(joined, store)).not.toBe(-1);\r
7192 }\r
7193\r
7194 function expectNotJoined(rec) {\r
7195 var joined = rec.joined || [];\r
7196 expect(Ext.Array.indexOf(joined, store)).toBe(-1);\r
7197 }\r
7198\r
7199 describe("loading", function () {\r
7200 it("should join when loaded as part of the constructor", function() {\r
7201 createStore({\r
7202 data: [abeRaw]\r
7203 }); \r
7204 expectJoined(store.getAt(0));\r
7205 });\r
7206\r
7207 it("should join when loading records", function() {\r
7208 createStore();\r
7209 store.loadData([edRaw, tommyRaw]);\r
7210 expectJoined(store.getAt(0));\r
7211 expectJoined(store.getAt(1));\r
7212 });\r
7213\r
7214 it("should unjoin existing records when loading a new data set", function() {\r
7215 createStore();\r
7216 store.loadData([edRaw, tommyRaw]);\r
7217 edRec = store.getAt(0);\r
7218 tommyRec = store.getAt(1);\r
7219 store.loadData([abeRaw, aaronRaw]);\r
7220 expectNotJoined(edRec);\r
7221 expectNotJoined(tommyRec);\r
7222 });\r
7223 });\r
7224\r
7225 describe("adding", function() {\r
7226 beforeEach(function() {\r
7227 createStore();\r
7228 });\r
7229\r
7230 it("should join when adding model data", function() {\r
7231 edRec = store.add(edRaw)[0];\r
7232 expectJoined(edRec);\r
7233 });\r
7234\r
7235 it("should join when adding a model instance", function() {\r
7236 edRec = makeUser(edRaw);\r
7237 store.add(edRec);\r
7238 expectJoined(edRec);\r
7239 });\r
7240\r
7241 it("should join inserting adding model data", function() {\r
7242 edRec = store.insert(0, edRaw)[0];\r
7243 expectJoined(edRec);\r
7244 });\r
7245\r
7246 it("should join when inserting a model instance", function() {\r
7247 edRec = makeUser(edRaw);\r
7248 store.insert(0, edRec);\r
7249 expectJoined(edRec);\r
7250 });\r
7251\r
7252 it("should join even when filtered out", function() {\r
7253 store.filter('group', 'admin');\r
7254 edRec = store.add(edRaw)[0];\r
7255 expectJoined(edRec);\r
7256 });\r
7257 });\r
7258\r
7259 describe("removing", function() {\r
7260 describe("with trackRemoved: true", function() {\r
7261 beforeEach(function() {\r
7262 createStore({\r
7263 trackRemoved: true\r
7264 });\r
7265 addStoreData();\r
7266 });\r
7267\r
7268 it("should not unjoin when removing a non phantom record", function() {\r
7269 store.removeAt(0);\r
7270 // Pushed into the removed collection\r
7271 expectJoined(edRec);\r
7272 });\r
7273\r
7274 it("should unjoin when removing a phantom", function() {\r
7275 var rec = store.add({})[0];\r
7276 store.remove(rec);\r
7277 // Won't be in the removed collection\r
7278 expectNotJoined(rec);\r
7279 });\r
7280\r
7281 it("should only unjoin items not in the removed collection when calling removeAll", function() {\r
7282 var rec = store.add({})[0];\r
7283 store.removeAll();\r
7284 expectJoined(edRec);\r
7285 expectJoined(abeRec);\r
7286 expectJoined(aaronRec);\r
7287 expectJoined(tommyRec);\r
7288 expectNotJoined(rec);\r
7289 });\r
7290 });\r
7291\r
7292 describe("with trackRemoved: false", function() {\r
7293 beforeEach(function() {\r
7294 createStore({\r
7295 trackRemoved: false\r
7296 });\r
7297 addStoreData();\r
7298 });\r
7299\r
7300 it("should unjoin when removing a non phantom record", function() {\r
7301 store.removeAt(0);\r
7302 expectNotJoined(edRec);\r
7303 });\r
7304\r
7305 it("should unjoin when removing a phantom", function() {\r
7306 var rec = store.add({})[0];\r
7307 store.remove(rec);\r
7308 expectNotJoined(rec);\r
7309 });\r
7310\r
7311 it("should unjoin when calling removeAll", function() {\r
7312 var rec = store.add({})[0];\r
7313 store.removeAll();\r
7314 expectNotJoined(edRec);\r
7315 expectNotJoined(abeRec);\r
7316 expectNotJoined(aaronRec);\r
7317 expectNotJoined(tommyRec);\r
7318 expectNotJoined(rec);\r
7319 });\r
7320 });\r
7321 });\r
7322\r
7323 describe("via record changes", function() {\r
7324 describe("with trackRemoved: true", function() {\r
7325 it("should keep the record joined until erased", function() {\r
7326 createStore({\r
7327 trackRemoved: true\r
7328 });\r
7329 addStoreData();\r
7330\r
7331 edRec.drop();\r
7332 expectJoined(edRec);\r
7333 edRec.erase();\r
7334 completeWithData({\r
7335 success: true\r
7336 });\r
7337 expectNotJoined(edRec);\r
7338 });\r
7339 });\r
7340\r
7341 describe("with trackRemoved: false", function() {\r
7342 it("should not keep the record joined when dropped", function() {\r
7343 createStore({\r
7344 trackRemoved: false\r
7345 });\r
7346 addStoreData();\r
7347\r
7348 edRec.drop();\r
7349 expectNotJoined(edRec);\r
7350 });\r
7351 });\r
7352 });\r
7353\r
7354 describe("destroying", function() {\r
7355 beforeEach(function() {\r
7356 createStore();\r
7357 addStoreData();\r
7358 });\r
7359\r
7360 it("should unjoin all records", function() {\r
7361 store.destroy();\r
7362 expectNotJoined(edRec);\r
7363 expectNotJoined(abeRec);\r
7364 expectNotJoined(aaronRec);\r
7365 expectNotJoined(tommyRec);\r
7366 });\r
7367\r
7368 it("should unjoin all records even when filtered", function() {\r
7369 store.filter('group', 'code');\r
7370 store.destroy();\r
7371 expectNotJoined(edRec);\r
7372 expectNotJoined(abeRec);\r
7373 expectNotJoined(aaronRec);\r
7374 expectNotJoined(tommyRec);\r
7375 });\r
7376 });\r
7377 });\r
7378\r
7379 describe("with a session", function() {\r
7380 var session;\r
7381\r
7382 beforeEach(function() {\r
7383 session = new Ext.data.Session();\r
7384 });\r
7385\r
7386 afterEach(function () {\r
7387 session.destroy();\r
7388 session = null;\r
7389 });\r
7390\r
7391 function createSessionStore(cfg) {\r
7392 cfg = Ext.apply({\r
7393 session: session\r
7394 }, cfg);\r
7395 createStore(cfg);\r
7396 }\r
7397\r
7398 describe("loading data", function() {\r
7399 it("should pass the session record creator when using load", function() {\r
7400 createSessionStore({\r
7401 proxy: {\r
7402 type: 'ajax'\r
7403 }\r
7404 });\r
7405 var spy = spyOn(store.getProxy(), 'read').andReturn();\r
7406 store.load();\r
7407 expect(spy.mostRecentCall.args[0].getRecordCreator()).toBe(session.recordCreator);\r
7408 });\r
7409 \r
7410 it("should pass the record creator when using loadRawData", function() {\r
7411 createSessionStore({\r
7412 proxy: {\r
7413 type: 'ajax'\r
7414 }\r
7415 });\r
7416 var spy = spyOn(store.getProxy().getReader(), 'read').andCallThrough();\r
7417 store.loadRawData([]);\r
7418 expect(spy.mostRecentCall.args[1].recordCreator).toBe(session.recordCreator);\r
7419 });\r
7420 });\r
7421\r
7422 describe("for records", function() {\r
7423 function expectSession(rec) {\r
7424 expect(rec.session).toBe(session);\r
7425 }\r
7426\r
7427 describe("loading", function () {\r
7428 it("should set the session when loaded as part of the constructor", function() {\r
7429 createSessionStore({\r
7430 data: [abeRaw]\r
7431 }); \r
7432 expectSession(store.getAt(0));\r
7433 });\r
7434\r
7435 it("should set the session when loading records", function() {\r
7436 createSessionStore();\r
7437 store.loadData([edRaw, tommyRaw]);\r
7438 expectSession(store.getAt(0));\r
7439 expectSession(store.getAt(1));\r
7440 });\r
7441\r
7442 it("should not clear the session when loading a new data set", function() {\r
7443 createSessionStore();\r
7444 store.loadData([edRaw, tommyRaw]);\r
7445 edRec = store.getAt(0);\r
7446 tommyRec = store.getAt(1);\r
7447 store.loadData([abeRaw, aaronRaw]);\r
7448 expectSession(edRec);\r
7449 expectSession(tommyRec);\r
7450 });\r
7451 });\r
7452\r
7453 describe("adding", function() {\r
7454 beforeEach(function() {\r
7455 createSessionStore();\r
7456 });\r
7457\r
7458 it("should set the session when adding model data", function() {\r
7459 edRec = store.add(edRaw)[0];\r
7460 expectSession(edRec);\r
7461 });\r
7462\r
7463 it("should set the session when adding a model instance", function() {\r
7464 edRec = makeUser(edRaw);\r
7465 store.add(edRec);\r
7466 expectSession(edRec);\r
7467 });\r
7468\r
7469 it("should set the session inserting adding model data", function() {\r
7470 edRec = store.insert(0, edRaw)[0];\r
7471 expectSession(edRec);\r
7472 });\r
7473\r
7474 it("should set the session when inserting a model instance", function() {\r
7475 edRec = makeUser(edRaw);\r
7476 store.insert(0, edRec);\r
7477 expectSession(edRec);\r
7478 });\r
7479\r
7480 it("should set the session even when filtered out", function() {\r
7481 store.filter('group', 'admin');\r
7482 edRec = store.add(edRaw)[0];\r
7483 expectSession(edRec);\r
7484 });\r
7485 });\r
7486\r
7487 describe("removing", function() {\r
7488 beforeEach(function() {\r
7489 createSessionStore();\r
7490 addStoreData();\r
7491 });\r
7492\r
7493 it("should not clear the session when removing a record", function() {\r
7494 store.removeAt(0);\r
7495 expectSession(edRec);\r
7496 });\r
7497\r
7498 it("should not clear the session when calling removeAll", function() {\r
7499 store.removeAll();\r
7500 expectSession(edRec);\r
7501 expectSession(abeRec);\r
7502 expectSession(aaronRec);\r
7503 expectSession(tommyRec);\r
7504 });\r
7505 });\r
7506\r
7507 describe("destroying", function() {\r
7508 beforeEach(function() {\r
7509 createSessionStore();\r
7510 addStoreData();\r
7511 });\r
7512\r
7513 it("should not clear the session on any records", function() {\r
7514 store.destroy();\r
7515 expectSession(edRec);\r
7516 expectSession(abeRec);\r
7517 expectSession(aaronRec);\r
7518 expectSession(tommyRec);\r
7519 });\r
7520 });\r
7521 });\r
7522 }); \r
7523\r
7524 describe("destroying", function() {\r
7525 it("should cancel a pending autoLoad", function() {\r
7526 createStore({\r
7527 autoLoad: true\r
7528 });\r
7529 spyOn(store, 'load').andReturn();\r
7530 store.destroy();\r
7531 waits(50);\r
7532 runs(function() {\r
7533 expect(store.load).not.toHaveBeenCalled();\r
7534 });\r
7535 });\r
7536\r
7537 it("should not fire a remove or clear event", function() {\r
7538 createStore();\r
7539 addStoreData();\r
7540\r
7541 var spy = jasmine.createSpy();\r
7542 store.on('remove', spy);\r
7543 store.on('clear', spy);\r
7544 store.destroy();\r
7545 expect(spy).not.toHaveBeenCalled();\r
7546 });\r
7547\r
7548 describe("proxy", function() {\r
7549 function getKeys(proxy) {\r
7550 var items = proxy.hasListeners,\r
7551 o = {},\r
7552 key;\r
7553\r
7554 for (key in items) {\r
7555 if (items.hasOwnProperty(key)) {\r
7556 o[key] = items[key];\r
7557 }\r
7558 }\r
7559 return o;\r
7560 }\r
7561\r
7562 describe("proxy created by the store", function() {\r
7563 it("should destroy a stringified proxy and clear it from the store", function() {\r
7564 createStore({\r
7565 proxy: 'ajax'\r
7566 });\r
7567 var proxy = store.getProxy(),\r
7568 spy = spyOn(proxy, 'destroy').andCallThrough();\r
7569\r
7570 store.destroy();\r
7571 expect(spy).toHaveBeenCalled();\r
7572 expect(store.getProxy()).toBeNull();\r
7573 });\r
7574\r
7575 it("should destroy an object config proxy and clear it from the store", function() {\r
7576 createStore({\r
7577 proxy: {\r
7578 type: 'ajax'\r
7579 }\r
7580 });\r
7581 var proxy = store.getProxy(),\r
7582 spy = spyOn(proxy, 'destroy').andCallThrough();\r
7583\r
7584 store.destroy();\r
7585 expect(spy).toHaveBeenCalled();\r
7586 expect(store.getProxy()).toBeNull();\r
7587 });\r
7588 });\r
7589\r
7590 describe("proxy from model", function() {\r
7591 it("should clear any listeners and detach from the store, but not destroy it", function() {\r
7592 var proxy = User.getProxy(),\r
7593 hasListeners = getKeys(proxy),\r
7594 spy;\r
7595\r
7596 createStore();\r
7597 spy = spyOn(proxy, 'destroy').andCallThrough();\r
7598 store.destroy();\r
7599 expect(spy).not.toHaveBeenCalled();\r
7600 expect(store.getProxy()).toBeNull();\r
7601 expect(getKeys(proxy.hasListeners)).toEqual(hasListeners);\r
7602 });\r
7603 });\r
7604\r
7605 describe("proxy instance", function() {\r
7606 it("should clear any listeners and detach from the store, but not destroy it", function() {\r
7607 var proxy = new Ext.data.proxy.Ajax(),\r
7608 hasListeners = getKeys(proxy),\r
7609 spy;\r
7610\r
7611 createStore({\r
7612 proxy: proxy\r
7613 });\r
7614 spy = spyOn(proxy, 'destroy').andCallThrough();\r
7615 store.destroy();\r
7616 expect(spy).not.toHaveBeenCalled();\r
7617 expect(store.getProxy()).toBeNull();\r
7618 expect(getKeys(proxy.hasListeners)).toEqual(hasListeners);\r
7619 });\r
7620 });\r
7621 });\r
7622 });\r
7623\r
7624 describe("extraKeys", function() {\r
7625 describe("setting after initialization", function() {\r
7626 beforeEach(function() {\r
7627 createStore();\r
7628 addStoreData();\r
7629 store.setExtraKeys({\r
7630 byAge: {\r
7631 property: 'age',\r
7632 rootProperty: 'data'\r
7633 }\r
7634 });\r
7635 });\r
7636\r
7637 it("should have the extraKeys updated when add fires", function() {\r
7638 var rec = makeUser('foo@sencha.com', {\r
7639 age: 100\r
7640 });\r
7641\r
7642 store.on('add', function() {\r
7643 expect(store.byAge.get(100)).toBe(rec);\r
7644 });\r
7645 store.add(rec);\r
7646 });\r
7647\r
7648 it("should have the extraKeys updated when remove fires", function() {\r
7649 store.on('remove', function() {\r
7650 expect(store.byAge.get(25)).toBeNull();\r
7651 });\r
7652 store.remove(edRec);\r
7653 });\r
7654\r
7655 it("should have the extraKeys updated when update fires", function() {\r
7656 store.on('update', function() {\r
7657 expect(store.byAge.get(1)).toBe(edRec);\r
7658 });\r
7659 edRec.set('age', 1);\r
7660 });\r
7661\r
7662 it("should have the extraKeys updated when clear fires", function() {\r
7663 store.on('clear', function() {\r
7664 expect(store.byAge.get(20)).toBeNull();\r
7665 expect(store.byAge.get(25)).toBeNull();\r
7666 expect(store.byAge.get(26)).toBeNull();\r
7667 expect(store.byAge.get(70)).toBeNull();\r
7668 });\r
7669 store.removeAll();\r
7670 });\r
7671 });\r
7672 })\r
7673\r
7674 /*\r
7675 * edRaw = {name: 'Ed Spencer', email: 'ed@sencha.com', evilness: 100, group: 'code', old: false, age: 25, valid: 'yes'};\r
7676 * abeRaw = {name: 'Abe Elias', email: 'abe@sencha.com', evilness: 70, group: 'admin', old: false, age: 20, valid: 'yes'};\r
7677 * aaronRaw = {name: 'Aaron Conran', email: 'aaron@sencha.com', evilness: 5, group: 'admin', old: true, age: 26, valid: 'yes'};\r
7678 * tommyRaw = {name: 'Tommy Maintz', email: 'tommy@sencha.com', evilness: -15, group: 'code', old: true, age: 70, valid: 'yes'};\r
7679 * \r
7680 * NOTE: The age field has a custom sorter which inverts the specified order.\r
7681 */\r
7682 describe('Reactive grouping', function() {\r
7683 var groups,\r
7684 sorters,\r
7685 counter;\r
7686\r
7687 function checkCorrectness(store, sorters, options, controller) {\r
7688 // For checking whether all three events: datachanged, refresh and sort have been fired.\r
7689 counter++;\r
7690\r
7691 // Check descending age order. [Aaron=26, Abe=20, Tommy=70, ed=25]\r
7692 expect(store.getRange()).toEqual([aaronRec, abeRec, tommyRec, edRec]);\r
7693\r
7694 // Cache the Sorters that the store is using\r
7695 sorters = store.getSorters();\r
7696\r
7697 // Cache the GroupCollection\r
7698 groups = store.getGroups();\r
7699\r
7700 // The two groups must have inherited the 'age' sorter used by the store\r
7701 expect(groups.items[0].getSorters().items.length).toBe(1);\r
7702 expect(groups.items[0].getSorters().items[0].getProperty()).toBe('age');\r
7703 expect(groups.items[0].getSorters().items[0] === sorters.items[0]).toBe(true);\r
7704 expect(groups.items[1].getSorters().items[0] === sorters.items[0]).toBe(true);\r
7705\r
7706 // Admin should be [Aaron=26,Abe=20]\r
7707 expect(groups.items[0].items).toEqual([aaronRec, abeRec]);\r
7708\r
7709 // Coded should be [Tommy=70,ed=25]\r
7710 expect(groups.items[1].items).toEqual([tommyRec, edRec]);\r
7711 }\r
7712\r
7713 function checkNewGrouping() {\r
7714 // For checking whether the add event has been fired.\r
7715 counter++;\r
7716\r
7717 // Check descending order. [Aaron=26, ed=25, Abe=20, Tommy=70]\r
7718 expect(store.getRange()).toEqual([aaronRec, edRec, abeRec, tommyRec]);\r
7719\r
7720 // Admin should be [Aaron=26,ed=25,Abe=20]\r
7721 expect(groups.items[0].items).toEqual([aaronRec, edRec, abeRec]);\r
7722\r
7723 // Coded should be [Tommy=70]\r
7724 expect(groups.items[1].items).toEqual([tommyRec]);\r
7725 }\r
7726\r
7727 describe('Test state of groups\' sort at the time the sort event fires', function() {\r
7728 it('should work going from no sorters to some sorters', function() {\r
7729 createStore({\r
7730 groupField: 'group',\r
7731 data: [abeRaw, edRaw, tommyRaw, aaronRaw]\r
7732 });\r
7733 aaronRec = store.getById('aaron@sencha.com');\r
7734 abeRec = store.getById('abe@sencha.com');\r
7735 edRec = store.getById('ed@sencha.com');\r
7736 tommyRec = store.getById('tommy@sencha.com');\r
7737\r
7738 expect(store.getRange()).toEqual([abeRec, aaronRec, edRec, tommyRec]);\r
7739\r
7740 // Check for everything to be sorted and in sync at the time the sort event fires\r
7741 counter = 0;\r
7742 store.on({\r
7743 datachanged: checkCorrectness,\r
7744 refresh: checkCorrectness,\r
7745 sort: checkCorrectness,\r
7746 single: true\r
7747 });\r
7748\r
7749 // This will sort into *DESCENDING* order because of the custom sorter on the age field\r
7750 store.sort('age');\r
7751\r
7752 // All events have fired.\r
7753 expect(counter).toBe(3);\r
7754\r
7755 // Check that new orders are synched in main collection and groups at the time the re-insertion of the record is broadcast\r
7756 counter = 0;\r
7757 store.on({\r
7758 refresh: checkNewGrouping,\r
7759 single: true\r
7760 });\r
7761\r
7762 // Move Ed into the admin group.\r
7763 // Admin group should become [Aaron=26,ed=25,Abe=20]\r
7764 edRec.set('group', 'admin');\r
7765\r
7766 // add event has fired.\r
7767 expect(counter).toBe(1);\r
7768 });\r
7769\r
7770 it('should work changing sorters', function() {\r
7771 createStore({\r
7772 groupField: 'group',\r
7773 data: [abeRaw, edRaw, tommyRaw, aaronRaw],\r
7774 sorters: [{\r
7775 property: 'evilness',\r
7776 direction: 'DESC'\r
7777 }]\r
7778 });\r
7779 aaronRec = store.getById('aaron@sencha.com');\r
7780 abeRec = store.getById('abe@sencha.com');\r
7781 edRec = store.getById('ed@sencha.com');\r
7782 tommyRec = store.getById('tommy@sencha.com');\r
7783\r
7784 // Check descending order. [Abe=70, Aaron=5, ed=100,Tommy=-15]\r
7785 expect(store.getRange()).toEqual([abeRec, aaronRec, edRec, tommyRec]);\r
7786\r
7787 // Cache the Sorters that the store is using\r
7788 sorters = store.getSorters();\r
7789\r
7790 // Cache the GroupCollection\r
7791 groups = store.getGroups();\r
7792\r
7793 // The two groups must have inherited the sorter used by the store from the outset, before any sort has been performed.\r
7794 expect(groups.items[0].getSorters().items.length).toBe(1);\r
7795 expect(groups.items[0].getSorters().items[0].getProperty()).toBe('evilness');\r
7796 expect(groups.items[0].getSorters().items[0] === sorters.items[0]).toBe(true);\r
7797 expect(groups.items[1].getSorters().items[0] === sorters.items[0]).toBe(true);\r
7798\r
7799 // Admin should be [Abe=70,Aaron=5]\r
7800 expect(groups.items[0].items).toEqual([abeRec, aaronRec]);\r
7801\r
7802 // Coded should be [ed=100,Tommy=-15]\r
7803 expect(groups.items[1].items).toEqual([edRec, tommyRec]);\r
7804\r
7805 // Check for everything to be sorted and in sync at the time the sort event fires\r
7806 counter = 0;\r
7807 store.on({\r
7808 datachanged: checkCorrectness,\r
7809 refresh: checkCorrectness,\r
7810 sort: checkCorrectness,\r
7811 single: true\r
7812 });\r
7813\r
7814 // This will sort into *DESCENDING* order because of the custom sorter on the age field\r
7815 // Conditions will now be the same as the spec above\r
7816 store.sort('age');\r
7817\r
7818 // All events have fired.\r
7819 expect(counter).toBe(3);\r
7820\r
7821 // Check that new orders are synched in main collection and groups at the time the re-insertion of the record is broadcast\r
7822 counter = 0;\r
7823 store.on({\r
7824 refresh: checkNewGrouping,\r
7825 single: true\r
7826 });\r
7827\r
7828 // Move Ed into the admin group.\r
7829 // Admin group should become [Aaron=26,ed=25,Abe=20]\r
7830 edRec.set('group', 'admin');\r
7831\r
7832 // add event has fired.\r
7833 expect(counter).toBe(1);\r
7834 });\r
7835 });\r
7836 });\r
7837});\r