]> git.proxmox.com Git - extjs.git/blame - extjs/packages/core/test/specs/data/Session.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / test / specs / data / Session.js
CommitLineData
6527f429
DM
1describe("Ext.data.Session", function() {\r
2 function completeRequest(data, requestId) {\r
3 Ext.Ajax.mockComplete({\r
4 status: 200,\r
5 responseText: Ext.encode(data)\r
6 }, requestId);\r
7 }\r
8\r
9 var session;\r
10\r
11 function idSort(a, b) {\r
12 if (Ext.isObject(a)) {\r
13 a = a.id;\r
14 b = b.id;\r
15 }\r
16 return a - b;\r
17 }\r
18\r
19 var adminGroup, peonGroup;\r
20 var rufusGroups, billGroups, tedGroups;\r
21 var adminUsers, peonUsers;\r
22 var userRufus, userBill, userTed;\r
23\r
24 function getAndComplete(type, id, theSession, data) {\r
25 theSession = theSession || session;\r
26 data = data || {};\r
27 var rec = theSession.getRecord(type, id);\r
28 data.id = data.id || id;\r
29 completeRequest(data);\r
30 return rec;\r
31 }\r
32\r
33 beforeEach(function() {\r
34 MockAjaxManager.addMethods();\r
35\r
36 adminGroup = {\r
37 id: 42,\r
38 name: 'Admins'\r
39 };\r
40 peonGroup = {\r
41 id: 427,\r
42 name: 'Peons'\r
43 };\r
44 userRufus = {\r
45 id: 10,\r
46 name: 'Rufus'\r
47 };\r
48 userBill = {\r
49 id: 20,\r
50 name: 'Bill'\r
51 };\r
52 userTed = {\r
53 id: 30,\r
54 name: 'Ted'\r
55 };\r
56\r
57 rufusGroups = [ adminGroup, peonGroup ];\r
58 billGroups = [ peonGroup ];\r
59 tedGroups = [ peonGroup ];\r
60\r
61 adminUsers = [ userRufus ];\r
62 peonUsers = [ userBill, userTed, userRufus ];\r
63 });\r
64\r
65 afterEach(function() {\r
66 MockAjaxManager.removeMethods();\r
67 Ext.destroy(session);\r
68 session = null;\r
69 });\r
70\r
71 describe("record access", function() {\r
72 var User, parent, rec;\r
73 beforeEach(function() {\r
74 Ext.data.Model.schema.setNamespace('spec');\r
75 User = Ext.define('spec.User', {\r
76 extend: 'Ext.data.Model',\r
77 fields: ['name', {\r
78 name: 'addressId',\r
79 reference: 'Address',\r
80 unique: true\r
81 }],\r
82 manyToMany: 'Group'\r
83 });\r
84 parent = new Ext.data.Session();\r
85 session = new Ext.data.Session();\r
86 });\r
87\r
88 afterEach(function() {\r
89 parent.destroy();\r
90 User = rec = parent = null;\r
91 Ext.undefine('spec.User');\r
92 Ext.data.Model.schema.clear(true);\r
93 });\r
94\r
95 describe("adopt", function() {\r
96 it("should cache the record into the session", function() {\r
97 rec = new User({\r
98 id: 1\r
99 });\r
100 session.adopt(rec);\r
101 expect(session.peekRecord('User', 1)).toBe(rec);\r
102 });\r
103\r
104 it("should put the session on the record", function() {\r
105 rec = new User({\r
106 id: 1\r
107 });\r
108 session.adopt(rec);\r
109 expect(rec.session).toBe(session);\r
110 });\r
111\r
112 it("should not throw an error if a record already in the session is adopted", function() {\r
113 rec = getAndComplete('User', 1);\r
114 expect(function() {\r
115 session.adopt(rec);\r
116 }).not.toThrow();\r
117 });\r
118\r
119 describe("invalid conditions", function() {\r
120 it("should not allow a record attached to another session", function() {\r
121 var other = new Ext.data.Session(),\r
122 rec = getAndComplete('User', 1, other);\r
123\r
124 expect(function() {\r
125 session.adopt(rec);\r
126 }).toThrow();\r
127 other.destroy();\r
128 });\r
129\r
130 it("should raise an error if add a model the schema does not know about", function() {\r
131 var customSchema = new Ext.data.schema.Schema();\r
132 Ext.define('spec.CustomModel', {\r
133 extend: 'Ext.data.Model',\r
134 schema: customSchema\r
135 });\r
136\r
137 rec = new spec.CustomModel({\r
138 id: 1\r
139 });\r
140 expect(function() {\r
141 session.adopt(rec);\r
142 }).toThrow();\r
143 });\r
144\r
145 it("should raise an error if adding an existing record", function() {\r
146 rec = getAndComplete('User', 1);\r
147 expect(function() {\r
148 session.adopt(new User({\r
149 id: 1\r
150 }));\r
151 }).toThrow();\r
152 });\r
153 });\r
154\r
155 describe("associations", function() {\r
156 describe("many to one", function() {\r
157 var user;\r
158\r
159 beforeEach(function() {\r
160 Ext.define('spec.Post', {\r
161 extend: 'Ext.data.Model',\r
162 fields: ['content', {\r
163 name: 'userId',\r
164 reference: 'User'\r
165 }]\r
166 });\r
167 });\r
168\r
169 afterEach(function() {\r
170 Ext.undefine('spec.Post');\r
171 user = null;\r
172 });\r
173\r
174 function makeUser(id) {\r
175 user = new User({\r
176 id: id\r
177 });\r
178 }\r
179\r
180 describe("the many", function() {\r
181 it("should not attempt to load the owner", function() {\r
182 var post = new spec.Post({\r
183 id: 101,\r
184 userId: 1\r
185 });\r
186\r
187 var spy = spyOn(User.getProxy(), 'read');\r
188 session.adopt(post);\r
189 expect(spy).not.toHaveBeenCalled();\r
190 });\r
191\r
192 it("should also adopt the owner", function() {\r
193 var post = new spec.Post({\r
194 id: 101,\r
195 userId: 1\r
196 });\r
197\r
198 makeUser(1);\r
199 post.setUser(user);\r
200 session.adopt(post);\r
201 expect(user.session).toBe(session);\r
202 });\r
203 });\r
204\r
205 describe("the one", function() {\r
206 it("should not load the store", function() {\r
207 makeUser(1);\r
208 user.posts();\r
209 var spy = spyOn(User.getProxy(), 'read');\r
210 session.adopt(user);\r
211 expect(spy).not.toHaveBeenCalled();\r
212 });\r
213\r
214 it("should set the session onto the store", function() {\r
215 makeUser(1);\r
216 var posts = user.posts();\r
217 session.adopt(user);\r
218 expect(posts.getSession()).toBe(session);\r
219 });\r
220\r
221 it("should adopt existing children", function() {\r
222 makeUser(1);\r
223 var posts = user.posts();\r
224 posts.add({id: 101}, {id: 102}, {id: 103});\r
225 session.adopt(user);\r
226 expect(posts.getAt(0).session).toBe(session);\r
227 expect(posts.getAt(1).session).toBe(session);\r
228 expect(posts.getAt(2).session).toBe(session);\r
229 });\r
230\r
231 it("should adopt any newly loaded items after adopting", function() {\r
232 makeUser(1);\r
233 var posts = user.posts();\r
234 session.adopt(user);\r
235 posts.load();\r
236 completeRequest([{id: 101, userId: 1}, {id: 102, userId: 1}, {id: 103, userId: 1}]);\r
237 expect(posts.getAt(0).session).toBe(session);\r
238 expect(posts.getAt(1).session).toBe(session);\r
239 expect(posts.getAt(2).session).toBe(session);\r
240 });\r
241 });\r
242 });\r
243\r
244 describe("one to one", function() {\r
245 beforeEach(function() {\r
246 Ext.define('spec.Address', {\r
247 extend: 'Ext.data.Model',\r
248 fields: ['city']\r
249 });\r
250 });\r
251\r
252 afterEach(function() {\r
253 Ext.undefine('spec.Address');\r
254 });\r
255\r
256 describe("the key holder", function() {\r
257 it("should not attempt to load the non key holder", function() {\r
258 var user = new User({\r
259 id: 1,\r
260 addressId: 101\r
261 });\r
262 var spy = spyOn(spec.Address.getProxy(), 'read');\r
263 session.adopt(user);\r
264 expect(spy).not.toHaveBeenCalled();\r
265 });\r
266\r
267 it("should adopt the non key holder", function() {\r
268 var address = new spec.Address({\r
269 id: 101\r
270 });\r
271\r
272 var user = new User({\r
273 id: 1\r
274 });\r
275\r
276 user.setAddress(address);\r
277 session.adopt(user);\r
278 expect(address.session).toBe(session);\r
279 });\r
280 });\r
281\r
282 describe("the non key holder", function() {\r
283 it("should not attempt to load the key holder", function() {\r
284 var address = new spec.Address({\r
285 id: 101\r
286 });\r
287 var spy = spyOn(User.getProxy(), 'read');\r
288 session.adopt(address);\r
289 expect(spy).not.toHaveBeenCalled();\r
290 });\r
291\r
292 it("should also adopt the key holder", function() {\r
293 var address = new spec.Address({\r
294 id: 101\r
295 });\r
296\r
297 var user = new User({\r
298 id: 1\r
299 });\r
300 address.setUser(user);\r
301\r
302 session.adopt(address);\r
303 expect(user.session).toBe(session);\r
304 });\r
305 });\r
306 });\r
307\r
308 describe("many to many", function() {\r
309 var Group;\r
310\r
311 beforeEach(function() {\r
312 Group = Ext.define('spec.Group', {\r
313 extend: 'Ext.data.Model',\r
314 fields: ['name']\r
315 });\r
316 });\r
317\r
318 afterEach(function() {\r
319 Ext.undefine('spec.Group');\r
320 Group = null;\r
321 });\r
322\r
323 describe("the left", function() {\r
324 it("should not load the store", function() {\r
325 var group = new Group({id: 1});\r
326 group.users();\r
327 var spy = spyOn(Group.getProxy(), 'read');\r
328 session.adopt(group);\r
329 expect(spy).not.toHaveBeenCalled();\r
330 });\r
331\r
332 it("should set the session onto the store", function() {\r
333 var group = new Group({id: 1}),\r
334 users = group.users();\r
335\r
336 session.adopt(group);\r
337 expect(users.getSession()).toBe(session);\r
338 });\r
339\r
340 it("should adopt existing children", function() {\r
341 var group = new Group({id: 1}),\r
342 users = group.users();\r
343\r
344 users.add({id: 101}, {id: 102}, {id: 103});\r
345 session.adopt(group);\r
346 expect(users.getAt(0).session).toBe(session);\r
347 expect(users.getAt(1).session).toBe(session);\r
348 expect(users.getAt(2).session).toBe(session);\r
349 });\r
350\r
351 it("should adopt any newly loaded items after adopting", function() {\r
352 var group = new Group({id: 1}),\r
353 users = group.users();\r
354\r
355 session.adopt(group);\r
356 users.load();\r
357 completeRequest([{id: 101}, {id: 102}, {id: 103}]);\r
358 expect(users.getAt(0).session).toBe(session);\r
359 expect(users.getAt(1).session).toBe(session);\r
360 expect(users.getAt(2).session).toBe(session);\r
361 });\r
362 });\r
363\r
364 describe("the right", function() {\r
365 it("should not load the store", function() {\r
366 var user = new User({id: 1});\r
367 user.groups();\r
368 var spy = spyOn(Group.getProxy(), 'read');\r
369 session.adopt(user);\r
370 expect(spy).not.toHaveBeenCalled();\r
371 });\r
372\r
373 it("should set the session onto the store", function() {\r
374 var user = new User({id: 1}),\r
375 groups = user.groups();\r
376\r
377 session.adopt(user);\r
378 expect(groups.getSession()).toBe(session);\r
379 });\r
380\r
381 it("should adopt existing children", function() {\r
382 var user = new User({id: 1}),\r
383 groups = user.groups();\r
384\r
385 groups.add({id: 101}, {id: 102}, {id: 103});\r
386 session.adopt(user);\r
387 expect(groups.getAt(0).session).toBe(session);\r
388 expect(groups.getAt(1).session).toBe(session);\r
389 expect(groups.getAt(2).session).toBe(session);\r
390 });\r
391\r
392 it("should adopt any newly loaded items after adopting", function() {\r
393 var user = new User({id: 1}),\r
394 groups = user.groups();\r
395\r
396 session.adopt(user);\r
397 groups.load();\r
398 completeRequest([{id: 101}, {id: 102}, {id: 103}]);\r
399 expect(groups.getAt(0).session).toBe(session);\r
400 expect(groups.getAt(1).session).toBe(session);\r
401 expect(groups.getAt(2).session).toBe(session);\r
402 });\r
403 });\r
404 });\r
405\r
406 describe("nested", function() {\r
407 beforeEach(function() {\r
408 Ext.define('spec.Order', {\r
409 extend: 'Ext.data.Model',\r
410 fields: [{\r
411 name: 'userId',\r
412 reference: 'User'\r
413 }, {\r
414 name: 'addressId',\r
415 reference: 'Address',\r
416 unique: true\r
417 }]\r
418 });\r
419\r
420 Ext.define('spec.OrderItem', {\r
421 extend: 'Ext.data.Model',\r
422 fields: [{\r
423 name: 'orderId',\r
424 reference: 'Order'\r
425 }]\r
426 });\r
427\r
428 Ext.define('spec.Address', {\r
429 extend: 'Ext.data.Model',\r
430 fields: ['city']\r
431 });\r
432 });\r
433\r
434 afterEach(function() {\r
435 Ext.undefine('spec.Address');\r
436 Ext.undefine('spec.OrderItem');\r
437 Ext.undefine('spec.Order');\r
438 });\r
439\r
440 it("should adopt data deeply", function() {\r
441 var user = User.load(1);\r
442 completeRequest({\r
443 id: 1,\r
444 orders: [{\r
445 id: 101,\r
446 userId: 1,\r
447 address: {\r
448 id: 201\r
449 },\r
450 orderItems: [{\r
451 id: 301,\r
452 orderId: 101\r
453 }, {\r
454 id: 302,\r
455 orderId: 101\r
456 }]\r
457 }, {\r
458 id: 102,\r
459 userId: 1,\r
460 address: {\r
461 id: 202\r
462 },\r
463 orderItems: [{\r
464 id: 303,\r
465 orderId: 102\r
466 }, {\r
467 id: 304,\r
468 orderId: 102\r
469 }]\r
470 }]\r
471 });\r
472 session.adopt(user);\r
473\r
474 var order = user.orders().getAt(0);\r
475 expect(order.session).toBe(session);\r
476 expect(order.getAddress().session).toBe(session);\r
477 expect(order.orderItems().getAt(0).session).toBe(session);\r
478 expect(order.orderItems().getAt(1).session).toBe(session);\r
479\r
480 order = user.orders().getAt(1);\r
481 expect(order.session).toBe(session);\r
482 expect(order.getAddress().session).toBe(session);\r
483 expect(order.orderItems().getAt(0).session).toBe(session);\r
484 expect(order.orderItems().getAt(1).session).toBe(session);\r
485 });\r
486 });\r
487 });\r
488 });\r
489\r
490 describe("createRecord", function() {\r
491 it("should accept the entity name", function() {\r
492 rec = session.createRecord('User', {\r
493 name: 'Foo'\r
494 });\r
495 expect(rec.$className).toBe('spec.User');\r
496 expect(rec.get('name')).toBe('Foo');\r
497 });\r
498\r
499 it("should accept the entity class", function() {\r
500 rec = session.createRecord(spec.User, {\r
501 name: 'Foo'\r
502 });\r
503 expect(rec.$className).toBe('spec.User');\r
504 expect(rec.get('name')).toBe('Foo');\r
505 });\r
506\r
507 it("should throw an exception with an unrecognized model name", function() {\r
508 expect(function() {\r
509 session.createRecord('Luser', {});\r
510 }).toThrow();\r
511 });\r
512\r
513 it("should throw an exception creating an anonymous model", function() {\r
514 var Model = Ext.define(null, {\r
515 extend: 'Ext.data.Model',\r
516 fields: ['name']\r
517 });\r
518\r
519 expect(function() {\r
520 session.createRecord(Model, {});\r
521 }).toThrow();\r
522 });\r
523\r
524 it("should cache the record in the session", function() {\r
525 rec = session.createRecord('User', {\r
526 });\r
527 expect(session.getRecord('User', rec.getId())).toBe(rec);\r
528 });\r
529\r
530 it("should set the session on the instance", function() {\r
531 rec = session.createRecord('User', {\r
532 });\r
533 expect(rec.session).toBe(session);\r
534 });\r
535\r
536 it("should throw an exception if the record exists in the session", function() {\r
537 getAndComplete('User', 1);\r
538 expect(function() {\r
539 rec = session.createRecord('User', {\r
540 id: 1\r
541 }).toThrow();\r
542 });\r
543 });\r
544\r
545 it("should be able to create a phantom with no data", function() {\r
546 var rec;\r
547 expect(function() {\r
548 rec = session.createRecord('User');\r
549 }).not.toThrow();\r
550 expect(rec.phantom).toBe(true);\r
551 });\r
552\r
553 describe("with a parent", function() {\r
554 beforeEach(function() {\r
555 session.setParent(parent);\r
556 });\r
557\r
558 it("should not create the record in the parent session", function() {\r
559 getAndComplete('User', 1);\r
560 expect(parent.peekRecord('User', 1)).toBeNull();\r
561 });\r
562\r
563 it("should raise an exception if the record exists in the parent", function() {\r
564 getAndComplete('User', 1, parent);\r
565 expect(function() {\r
566 session.createRecord('User', {\r
567 id: 1\r
568 });\r
569 }).toThrow();\r
570 });\r
571\r
572 it("should be able to create a phantom with no data", function() {\r
573 var rec;\r
574 expect(function() {\r
575 rec = session.createRecord('User');\r
576 }).not.toThrow();\r
577 expect(rec.phantom).toBe(true);\r
578 });\r
579 });\r
580 });\r
581\r
582 describe("getRecord", function() {\r
583 it("should throw an exception with an unrecognized model name", function() {\r
584 expect(function() {\r
585 session.getRecord('Luser', 1);\r
586 }).toThrow();\r
587 });\r
588\r
589 it("should throw an exception creating an anonymous model", function() {\r
590 var Model = Ext.define(null, {\r
591 extend: 'Ext.data.Model',\r
592 fields: ['name']\r
593 });\r
594\r
595 expect(function() {\r
596 session.getRecord(Model, 1);\r
597 }).toThrow();\r
598 });\r
599\r
600 describe("with no record", function() {\r
601 it("should accept the entity name", function() {\r
602 rec = session.getRecord('User', 1);\r
603 expect(rec.getId()).toBe(1);\r
604 expect(rec.$className).toBe('spec.User');\r
605 });\r
606\r
607 it("should accept the entity class", function() {\r
608 rec = session.getRecord(spec.User, 1);\r
609 expect(rec.getId()).toBe(1);\r
610 expect(rec.$className).toBe('spec.User');\r
611 });\r
612\r
613 it("should accept an existing record and adopt it", function() {\r
614 rec = new spec.User({\r
615 id: 1\r
616 });\r
617 expect(session.getRecord(rec)).toBe(rec);\r
618 expect(rec.session).toBe(session);\r
619 });\r
620\r
621 it("should create a new record and track it in the session", function() {\r
622 rec = session.getRecord('User', 1);\r
623 expect(session.peekRecord('User', 1)).toBe(rec);\r
624 });\r
625\r
626 it("should set the session on the record", function() {\r
627 rec = session.getRecord('User', 1);\r
628 expect(rec.session).toBe(session);\r
629 });\r
630\r
631 describe("autoLoad", function() {\r
632 it("should autoLoad by default", function() {\r
633 var spy = spyOn(spec.User.getProxy(), 'read');\r
634 session.getRecord('User', 1);\r
635 expect(spy).toHaveBeenCalled();\r
636 });\r
637\r
638 it("should not autoLoad when passed: false", function() {\r
639 var spy = spyOn(spec.User.getProxy(), 'read');\r
640 session.getRecord('User', 1, false);\r
641 expect(spy).not.toHaveBeenCalled();\r
642 });\r
643\r
644 it("should pass parameters to load", function() {\r
645 var spy = spyOn(spec.User.getProxy(), 'read');\r
646 session.getRecord('User', 1, {\r
647 params: {\r
648 someId: 1\r
649 }\r
650 });\r
651 expect(spy.mostRecentCall.args[0].getParams()).toEqual({\r
652 someId: 1\r
653 });\r
654 });\r
655 });\r
656\r
657 describe("with parent session", function() {\r
658 beforeEach(function() {\r
659 session.setParent(parent);\r
660 });\r
661\r
662 describe("record exists in the parent", function() {\r
663 beforeEach(function() {\r
664 rec = getAndComplete('User', 1, parent, {name: 'Foo'});\r
665 });\r
666\r
667 describe("with type/id", function() {\r
668 it("should return an existing record from the parent", function() {\r
669 var child = session.getRecord('User', 1);\r
670 expect(child.get('name')).toBe('Foo');\r
671 expect(child.$className).toBe('spec.User');\r
672 });\r
673\r
674 it("should return a copy, not the same instance", function() {\r
675 var child = session.getRecord('User', 1);\r
676 expect(child).not.toBe(rec);\r
677 });\r
678\r
679 it("should set the session to be the child session", function() {\r
680 var child = session.getRecord('User', 1);\r
681 expect(child.session).toBe(session);\r
682 });\r
683\r
684 it("should not trigger a load", function() {\r
685 var spy = spyOn(spec.User.getProxy(), 'read');\r
686\r
687 session.getRecord('User', 1);\r
688 expect(spy).not.toHaveBeenCalled();\r
689 });\r
690\r
691 it("should not update the parent record when the child changes", function() {\r
692 var child = session.getRecord('User', 1);\r
693 child.set('name', 'Bar');\r
694 expect(rec.get('name')).toBe('Foo');\r
695 });\r
696\r
697 it("should not update the child record when the parent changes", function() {\r
698 var child = session.getRecord('User', 1);\r
699 rec.set('name', 'Bar');\r
700 expect(child.get('name')).toBe('Foo');\r
701 });\r
702\r
703 it("should not copy the record if the parent is loading", function() {\r
704 rec = parent.getRecord('User', 2);\r
705 var child = session.getRecord('User', 2);\r
706\r
707 completeRequest({name: 'Foo'});\r
708 expect(rec.get('name')).toBe('Foo');\r
709 expect(child.isLoading()).toBe(true);\r
710 expect(child.get('name')).toBeUndefined();\r
711\r
712 });\r
713 });\r
714\r
715 describe("with parent instance", function() {\r
716 it("should return an existing record from the parent", function() {\r
717 var child = session.getRecord(rec);\r
718 expect(child.get('name')).toBe('Foo');\r
719 expect(child.$className).toBe('spec.User');\r
720 });\r
721\r
722 it("should return a copy, not the same instance", function() {\r
723 var child = session.getRecord(rec);\r
724 expect(child).not.toBe(rec);\r
725 });\r
726\r
727 it("should set the session to be the child session", function() {\r
728 var child = session.getRecord(rec);\r
729 expect(child.session).toBe(session);\r
730 });\r
731\r
732 it("should not trigger a load", function() {\r
733 var spy = spyOn(spec.User.getProxy(), 'read');\r
734\r
735 session.getRecord(rec);\r
736 expect(spy).not.toHaveBeenCalled();\r
737 });\r
738\r
739 it("should not update the parent record when the child changes", function() {\r
740 var child = session.getRecord(rec);\r
741 child.set('name', 'Bar');\r
742 expect(rec.get('name')).toBe('Foo');\r
743 });\r
744\r
745 it("should not update the child record when the parent changes", function() {\r
746 var child = session.getRecord(rec);\r
747 rec.set('name', 'Bar');\r
748 expect(child.get('name')).toBe('Foo');\r
749 });\r
750\r
751 it("should not copy the record if the parent is loading", function() {\r
752 rec = parent.getRecord('User', 2);\r
753 var child = session.getRecord(rec);\r
754\r
755 completeRequest({name: 'Foo'});\r
756 expect(rec.get('name')).toBe('Foo');\r
757 expect(child.isLoading()).toBe(true);\r
758 expect(child.get('name')).toBeUndefined();\r
759\r
760 });\r
761 });\r
762 });\r
763\r
764 describe("record does not exist in the parent", function() {\r
765 describe("with type/id", function() {\r
766 it("should create an instance", function() {\r
767 rec = session.getRecord('User', 1);\r
768 expect(rec.getId()).toBe(1);\r
769 expect(rec.$className).toBe('spec.User');\r
770 });\r
771\r
772 it("not push the instance into the parent", function() {\r
773 session.getRecord('User', 1);\r
774 expect(parent.peekRecord('User', 1)).toBeNull();\r
775 });\r
776 });\r
777\r
778 describe("with an instance", function() {\r
779 it("should use the passed instance", function() {\r
780 rec = new spec.User({id: 1});\r
781 expect(session.getRecord(rec)).toBe(rec);\r
782 expect(rec.session).toBe(session);\r
783 });\r
784\r
785 it("not push the instance into the parent", function() {\r
786 rec = new spec.User({id: 1});\r
787 session.getRecord(rec);\r
788 expect(parent.peekRecord('User', 1)).toBeNull();\r
789 });\r
790 });\r
791 });\r
792 });\r
793 });\r
794\r
795 describe("with an existing record", function() {\r
796 beforeEach(function() {\r
797 rec = getAndComplete('User', 1);\r
798 });\r
799\r
800 it("should accept the entity name", function() {\r
801 expect(session.getRecord('User', 1)).toBe(rec);\r
802 });\r
803\r
804 it("should accept the entity class", function() {\r
805 expect(session.getRecord(spec.User, 1)).toBe(rec);\r
806 });\r
807\r
808 it("should return the same instance", function() {\r
809 expect(session.getRecord(rec)).toBe(rec);\r
810 });\r
811\r
812 it("should return the existing record", function() {\r
813 expect(session.getRecord('User', 1)).toBe(rec);\r
814 });\r
815\r
816 it("should not attempt to load the record", function() {\r
817 var spy = spyOn(spec.User.getProxy(), 'read');\r
818 session.getRecord('User', 1);\r
819 expect(spy).not.toHaveBeenCalled();\r
820 });\r
821 });\r
822 });\r
823\r
824 describe("peekRecord", function() {\r
825 beforeEach(function() {\r
826 rec = getAndComplete('User', 1);\r
827 });\r
828\r
829 it("should accept the entity name", function() {\r
830 expect(session.peekRecord('User', 1)).toBe(rec);\r
831 });\r
832\r
833 it("should accept the entity class", function() {\r
834 expect(session.peekRecord(spec.User, 1)).toBe(rec);\r
835 });\r
836\r
837 it("should throw an exception with an unrecognized model name", function() {\r
838 expect(function() {\r
839 session.peekRecord('Luser', 1);\r
840 }).toThrow();\r
841 });\r
842\r
843 it("should throw an exception creating an anonymous model", function() {\r
844 var Model = Ext.define(null, {\r
845 extend: 'Ext.data.Model',\r
846 fields: ['name']\r
847 });\r
848\r
849 expect(function() {\r
850 session.peekRecord(Model, 1);\r
851 }).toThrow();\r
852 });\r
853\r
854 it("should return the model instance", function() {\r
855 var result = session.peekRecord('User', 1);\r
856 expect(result.isModel).toBe(true);\r
857 expect(result).toBe(rec);\r
858 });\r
859\r
860 it("should return null if the record does not exist", function() {\r
861 expect(session.peekRecord('User', 12)).toBeNull();\r
862 });\r
863\r
864 describe("parent", function() {\r
865 it("should not return a record from a parent without deep=true", function() {\r
866 session.setParent(parent);\r
867 getAndComplete('User', 2, parent);\r
868 expect(session.peekRecord('User', 2)).toBeNull();\r
869 });\r
870\r
871 it("should find a record in the parent session if we pass deep=true", function() {\r
872 session.setParent(parent);\r
873 rec = getAndComplete('User', 2, parent);\r
874 expect(session.peekRecord('User', 2, true)).toBe(rec);\r
875 });\r
876\r
877 it("should favour a record in the child", function() {\r
878 session.setParent(parent);\r
879 getAndComplete('User', 2, parent);\r
880 rec = session.getRecord('User', 2);\r
881 expect(session.peekRecord('User', 2, true)).toBe(rec);\r
882 });\r
883\r
884 it("should not consider a parent if we pass deep=true and there is no parent", function() {\r
885 expect(session.peekRecord('User', 1000, true)).toBeNull();\r
886 });\r
887 });\r
888 });\r
889\r
890 describe("associations", function() {\r
891 describe("many to one", function() {\r
892 beforeEach(function() {\r
893 Ext.define('spec.Post', {\r
894 extend: 'Ext.data.Model',\r
895 fields: ['content', {\r
896 name: 'userId',\r
897 reference: 'User'\r
898 }]\r
899 });\r
900 });\r
901\r
902 function makePost(id, userId, data) {\r
903 data = data || {};\r
904 data.id = id;\r
905 data.userId = userId;\r
906 return getAndComplete('Post', id, session, data);\r
907 }\r
908\r
909 function makeUser(id, data) {\r
910 data = data || {};\r
911 data.id = id;\r
912 return getAndComplete('User', id, session, data);\r
913 }\r
914\r
915 afterEach(function() {\r
916 Ext.undefine('spec.Post');\r
917 });\r
918\r
919 describe("the one", function() {\r
920 it("should use an existing record instance from the session", function() {\r
921 var user = makeUser(1),\r
922 post = makePost(17, 1);\r
923\r
924 expect(post.getUser()).toBe(user);\r
925 });\r
926\r
927 it("should not trigger a load when the record instance exists", function() {\r
928 var user = makeUser(1),\r
929 post = makePost(17, 1),\r
930 spy = spyOn(spec.User.getProxy(), 'read');\r
931\r
932 post.getUser();\r
933 expect(spy).not.toHaveBeenCalled();\r
934 });\r
935\r
936 it("should request an instance it needs and put it in the session", function() {\r
937 var post = makePost(17, 1),\r
938 user = post.getUser();\r
939\r
940 completeRequest({\r
941 id: 1\r
942 });\r
943 expect(session.getRecord('User', 1)).toBe(user);\r
944 });\r
945 });\r
946\r
947 describe("the many", function() {\r
948 var user;\r
949 beforeEach(function() {\r
950 user = makeUser(1);\r
951 });\r
952\r
953 afterEach(function() {\r
954 user = null;\r
955 });\r
956\r
957 it("should be empty by default and not complete", function() {\r
958 var posts = user.posts();\r
959 expect(posts.getCount()).toBe(0);\r
960 expect(posts.complete).toBe(false);\r
961 });\r
962\r
963 it("should not trigger a load", function() {\r
964 var spy = spyOn(spec.Post.getProxy(), 'read');\r
965 user.posts();\r
966 expect(spy).not.toHaveBeenCalled();\r
967 });\r
968\r
969 it("should create records on loading", function() {\r
970 var posts = user.posts();\r
971 posts.load();\r
972 completeRequest([{id: 101, userId: 1}, {id: 102, userId: 1}, {id: 103, userId: 1}]);\r
973 expect(posts.getAt(0)).toBe(session.peekRecord('Post', 101));\r
974 expect(posts.getAt(1)).toBe(session.peekRecord('Post', 102));\r
975 expect(posts.getAt(2)).toBe(session.peekRecord('Post', 103));\r
976 });\r
977\r
978 it("should set the complete flag on loading", function() {\r
979 var posts = user.posts();\r
980 posts.load();\r
981 completeRequest([]);\r
982 expect(posts.complete).toBe(true);\r
983 });\r
984\r
985 describe("local modifications", function() {\r
986 it("should add items with a matching foreign key on creation, but not be complete", function() {\r
987 // post1 & post3 exist in the session with a matching FK, as soon as the\r
988 // store is spun up they should exist in the store\r
989 var post1 = makePost(101, 1),\r
990 post3 = makePost(103, 1),\r
991 posts = user.posts();\r
992\r
993 expect(posts.getCount()).toBe(2);\r
994 expect(posts.getAt(0)).toBe(post1);\r
995 expect(posts.getAt(1)).toBe(post3);\r
996 expect(posts.complete).toBe(false);\r
997 });\r
998\r
999 it("should respect the server order when loading with existing items", function() {\r
1000 // Although we have the records, maintain the order the server sent.\r
1001 var post1 = makePost(101, 1),\r
1002 post2 = makePost(102, 1),\r
1003 post3 = makePost(103, 1),\r
1004 posts = user.posts();\r
1005\r
1006 posts.load();\r
1007 completeRequest([{id: 103, userId: 1}, {id: 102, userId: 1}, {id: 101, userId: 1}]);\r
1008\r
1009 expect(posts.getAt(0)).toBe(post3);\r
1010 expect(posts.getAt(1)).toBe(post2);\r
1011 expect(posts.getAt(2)).toBe(post1);\r
1012 });\r
1013\r
1014 it("should add matching FK items to the end when loading", function() {\r
1015 // We have a record with a matching key before the store is created.\r
1016 // Once we load the store we should include it in the active set\r
1017 var post2 = makePost(102, 1),\r
1018 posts = user.posts();\r
1019\r
1020 posts.load();\r
1021 completeRequest([{id: 103, userId: 1}, {id: 101, userId: 1}]);\r
1022 expect(posts.getAt(0)).toBe(session.peekRecord('Post', 103));\r
1023 expect(posts.getAt(1)).toBe(session.peekRecord('Post', 101));\r
1024 expect(posts.getAt(2)).toBe(session.peekRecord('Post', 102));\r
1025 });\r
1026\r
1027 it("should remove no-longer matching FK items when loading", function() {\r
1028 // We had a record with a matching key which was subsequently changed.\r
1029 // Reject it from the server load since the local copy is correct.\r
1030 var post2 = makePost(102, 1);\r
1031 post2.set('userId', null);\r
1032\r
1033 var posts = user.posts();\r
1034 posts.load();\r
1035 completeRequest([{id: 101, userId: 1}, {id: 102, userId: 1}, {id: 103, userId: 1}]);\r
1036 expect(posts.getCount()).toBe(2);\r
1037 expect(posts.getAt(0)).toBe(session.peekRecord('Post', 101));\r
1038 expect(posts.getAt(1)).toBe(session.peekRecord('Post', 103));\r
1039 });\r
1040 });\r
1041 });\r
1042 });\r
1043\r
1044 describe("one to one", function() {\r
1045 beforeEach(function() {\r
1046 Ext.define('spec.Address', {\r
1047 extend: 'Ext.data.Model',\r
1048 fields: ['city']\r
1049 });\r
1050 });\r
1051\r
1052 function makeAddress(id, data) {\r
1053 data = data || {};\r
1054 data.id = id;\r
1055 return getAndComplete('Address', id, session, data);\r
1056 }\r
1057\r
1058 function makeUser(id, addressId, data) {\r
1059 data = data || {};\r
1060 data.id = id;\r
1061 data.addressId = addressId;\r
1062 return getAndComplete('User', id, session, data);\r
1063 }\r
1064\r
1065 afterEach(function() {\r
1066 Ext.undefine('spec.Address');\r
1067 });\r
1068\r
1069 describe("the key holder", function() {\r
1070 it("should use an existing record instance from the session", function() {\r
1071 var address = makeAddress(1),\r
1072 user = makeUser(17, 1);\r
1073\r
1074 expect(user.getAddress()).toBe(address);\r
1075 });\r
1076\r
1077 it("should not trigger a load when the record instance exists", function() {\r
1078 var address = makeAddress(1),\r
1079 user = makeUser(17, 1),\r
1080 spy = spyOn(spec.User.getProxy(), 'read');\r
1081\r
1082 user.getAddress();\r
1083 expect(spy).not.toHaveBeenCalled();\r
1084 });\r
1085\r
1086 it("should request an instance it needs and put it in the session", function() {\r
1087 var user = makeUser(17, 1),\r
1088 address = user.getAddress();\r
1089\r
1090 completeRequest({\r
1091 id: 1\r
1092 });\r
1093 expect(session.getRecord('Address', 1)).toBe(address);\r
1094 });\r
1095 });\r
1096 });\r
1097\r
1098 describe("many to many", function() {\r
1099 beforeEach(function() {\r
1100 Ext.define('spec.Group', {\r
1101 extend: 'Ext.data.Model',\r
1102 fields: ['name']\r
1103 });\r
1104 });\r
1105\r
1106 function makeUser(id) {\r
1107 return getAndComplete('User', id, session);\r
1108 }\r
1109\r
1110 function makeGroup(id) {\r
1111 return getAndComplete('Group', id, session);\r
1112 }\r
1113\r
1114 afterEach(function() {\r
1115 Ext.undefine('spec.Group');\r
1116 });\r
1117\r
1118 describe("the left", function() {\r
1119 var user;\r
1120 beforeEach(function() {\r
1121 user = makeUser(1);\r
1122 });\r
1123\r
1124 afterEach(function() {\r
1125 user = null;\r
1126 });\r
1127\r
1128 it("should be empty by default and not complete", function() {\r
1129 var groups = user.groups();\r
1130 expect(groups.getCount()).toBe(0);\r
1131 expect(groups.complete).toBe(false);\r
1132 });\r
1133\r
1134 it("should not trigger a load", function() {\r
1135 var spy = spyOn(spec.Group.getProxy(), 'read');\r
1136 user.groups();\r
1137 expect(spy).not.toHaveBeenCalled();\r
1138 });\r
1139\r
1140 it("should create records on loading", function() {\r
1141 var groups = user.groups();\r
1142 groups.load();\r
1143 completeRequest([{id: 101}, {id: 102}, {id: 103}]);\r
1144 expect(groups.getAt(0)).toBe(session.peekRecord('Group', 101));\r
1145 expect(groups.getAt(1)).toBe(session.peekRecord('Group', 102));\r
1146 expect(groups.getAt(2)).toBe(session.peekRecord('Group', 103));\r
1147 });\r
1148\r
1149 it("should set the complete flag on loading", function() {\r
1150 var groups = user.groups();\r
1151 groups.load();\r
1152 completeRequest([]);\r
1153 expect(groups.complete).toBe(true);\r
1154 });\r
1155\r
1156 describe("local modifications", function() {\r
1157 it("should respect the load order when loading with existing items", function() {\r
1158 var group2 = makeGroup(102),\r
1159 groups = user.groups();\r
1160\r
1161 groups.add(group2);\r
1162 groups.load();\r
1163 completeRequest([{id: 103}, {id: 102}, {id: 101}]);\r
1164\r
1165 expect(groups.getAt(0)).toBe(session.peekRecord('Group', 103));\r
1166 expect(groups.getAt(1)).toBe(group2);\r
1167 expect(groups.getAt(2)).toBe(session.peekRecord('Group', 101));\r
1168 });\r
1169\r
1170 it("should add matching FK items to the end when loading", function() {\r
1171 var group2 = makeGroup(102),\r
1172 groups = user.groups();\r
1173\r
1174 groups.add(group2);\r
1175 groups.load();\r
1176 completeRequest([{id: 103}, {id: 101}]);\r
1177 expect(groups.getAt(0)).toBe(session.peekRecord('Group', 103));\r
1178 expect(groups.getAt(1)).toBe(session.peekRecord('Group', 101));\r
1179 expect(groups.getAt(2)).toBe(group2);\r
1180 });\r
1181 });\r
1182 });\r
1183\r
1184 describe("the right", function() {\r
1185 var group;\r
1186 beforeEach(function() {\r
1187 group = makeGroup(1);\r
1188 });\r
1189\r
1190 afterEach(function() {\r
1191 group = null;\r
1192 });\r
1193\r
1194 it("should be empty by default and not complete", function() {\r
1195 var users = group.users();\r
1196 expect(users.getCount()).toBe(0);\r
1197 expect(users.complete).toBe(false);\r
1198 });\r
1199\r
1200 it("should not trigger a load", function() {\r
1201 var spy = spyOn(spec.User.getProxy(), 'read');\r
1202 group.users();\r
1203 expect(spy).not.toHaveBeenCalled();\r
1204 });\r
1205\r
1206 it("should create records on loading", function() {\r
1207 var users = group.users();\r
1208 users.load();\r
1209 completeRequest([{id: 101}, {id: 102}, {id: 103}]);\r
1210 expect(users.getAt(0)).toBe(session.peekRecord('User', 101));\r
1211 expect(users.getAt(1)).toBe(session.peekRecord('User', 102));\r
1212 expect(users.getAt(2)).toBe(session.peekRecord('User', 103));\r
1213 });\r
1214\r
1215 it("should set the complete flag on loading", function() {\r
1216 var users = group.users();\r
1217 users.load();\r
1218 completeRequest([]);\r
1219 expect(users.complete).toBe(true);\r
1220 });\r
1221\r
1222 describe("local modifications", function() {\r
1223 it("should respect the load order when loading with existing items", function() {\r
1224 var user2 = makeUser(102),\r
1225 users = group.users();\r
1226\r
1227 users.add(user2);\r
1228 users.load();\r
1229 completeRequest([{id: 103}, {id: 102}, {id: 101}]);\r
1230\r
1231 expect(users.getAt(0)).toBe(session.peekRecord('User', 103));\r
1232 expect(users.getAt(1)).toBe(user2);\r
1233 expect(users.getAt(2)).toBe(session.peekRecord('User', 101));\r
1234 });\r
1235\r
1236 it("should add matching FK items to the end when loading", function() {\r
1237 var user2 = makeUser(102),\r
1238 users = group.users();\r
1239\r
1240 users.add(user2);\r
1241 users.load();\r
1242 completeRequest([{id: 103}, {id: 101}]);\r
1243 expect(users.getAt(0)).toBe(session.peekRecord('User', 103));\r
1244 expect(users.getAt(1)).toBe(session.peekRecord('User', 101));\r
1245 expect(users.getAt(2)).toBe(user2);\r
1246 });\r
1247 });\r
1248 });\r
1249 });\r
1250 });\r
1251 });\r
1252\r
1253 describe("getChanges", function() {\r
1254 var User;\r
1255\r
1256 beforeEach(function() {\r
1257 Ext.data.Model.schema.setNamespace('spec');\r
1258 session = new Ext.data.Session();\r
1259\r
1260 User = Ext.define('spec.User', {\r
1261 extend: 'Ext.data.Model',\r
1262 fields: ['id', 'name', 'age', {\r
1263 name: 'addressId',\r
1264 reference: 'Address',\r
1265 unique: true\r
1266 }, {\r
1267 name: 'serializeField',\r
1268 serialize: function(v) {\r
1269 return v.toString();\r
1270 }\r
1271 }],\r
1272 manyToMany: '#Group'\r
1273 });\r
1274\r
1275 Ext.define('spec.Group', {\r
1276 extend: 'Ext.data.Model',\r
1277 fields: ['name']\r
1278 });\r
1279\r
1280 Ext.define('spec.Address', {\r
1281 extend: 'Ext.data.Model',\r
1282 fields: ['city']\r
1283 });\r
1284\r
1285 Ext.define('spec.Post', {\r
1286 extend: 'Ext.data.Model',\r
1287 fields: ['content', {\r
1288 name: 'userId',\r
1289 reference: 'User'\r
1290 }]\r
1291 })\r
1292 });\r
1293\r
1294 afterEach(function() {\r
1295 Ext.undefine('spec.User');\r
1296 Ext.undefine('spec.Address');\r
1297 Ext.undefine('spec.Post');\r
1298 Ext.undefine('spec.Group');\r
1299 Ext.data.Model.schema.clear(true);\r
1300 User = null;\r
1301 });\r
1302\r
1303 describe("serialization options", function() {\r
1304 it("should serialize field data", function() {\r
1305 var user = session.createRecord('User', {\r
1306 serializeField: 1000\r
1307 });\r
1308 expect(session.getChanges()).toEqual({\r
1309 User: {\r
1310 C: [{\r
1311 id: user.getId(),\r
1312 serializeField: '1000'\r
1313 }]\r
1314 }\r
1315 });\r
1316 });\r
1317 });\r
1318\r
1319 describe("basic operations", function() {\r
1320 describe("create", function() {\r
1321 it("should include a phantom record", function() {\r
1322 var user = session.createRecord('User', {\r
1323 name: 'Foo',\r
1324 age: 34\r
1325 });\r
1326 expect(session.getChanges()).toEqual({\r
1327 User: {\r
1328 C: [{\r
1329 id: user.getId(),\r
1330 name: 'Foo',\r
1331 age: 34\r
1332 }]\r
1333 }\r
1334 });\r
1335 });\r
1336\r
1337 it("should include the updated record state", function() {\r
1338 var user = session.createRecord('User', {\r
1339 name: 'Foo',\r
1340 age: 34\r
1341 });\r
1342 user.set('name', 'Bar');\r
1343 user.set('age', 5000);\r
1344 expect(session.getChanges()).toEqual({\r
1345 User: {\r
1346 C: [{\r
1347 id: user.getId(),\r
1348 name: 'Bar',\r
1349 age: 5000\r
1350 }]\r
1351 }\r
1352 });\r
1353 });\r
1354\r
1355 it("should be able to create multiple records", function() {\r
1356 var user1 = session.createRecord('User'),\r
1357 user2 = session.createRecord('User'),\r
1358 user3 = session.createRecord('User');\r
1359\r
1360 expect(session.getChanges()).toEqual({\r
1361 User: {\r
1362 C: [{id: user1.getId()}, {id: user2.getId()}, {id: user3.getId()}]\r
1363 }\r
1364 });\r
1365 });\r
1366\r
1367 it("should not include non-phantoms", function() {\r
1368 var user = getAndComplete('User', 1);\r
1369 expect(session.getChanges()).toBeNull();\r
1370 });\r
1371\r
1372 it("should not include phantom records that are dropped", function() {\r
1373 var user = session.createRecord('User');\r
1374 user.drop();\r
1375 expect(session.getChanges()).toBeNull();\r
1376 });\r
1377 });\r
1378\r
1379 describe("update", function() {\r
1380 it("should include the updated record", function() {\r
1381 var user = getAndComplete('User', 1);\r
1382 user.set('name', 'Foo');\r
1383 expect(session.getChanges()).toEqual({\r
1384 User: {\r
1385 U: [{\r
1386 id: 1,\r
1387 name: 'Foo'\r
1388 }]\r
1389 }\r
1390 });\r
1391 });\r
1392\r
1393 it("should include the most recently updated state", function() {\r
1394 var user = getAndComplete('User', 1);\r
1395 user.set('name', 'Foo');\r
1396 user.set('name', 'Bar');\r
1397 user.set('name', 'Baz');\r
1398 expect(session.getChanges()).toEqual({\r
1399 User: {\r
1400 U: [{\r
1401 id: 1,\r
1402 name: 'Baz'\r
1403 }]\r
1404 }\r
1405 });\r
1406 });\r
1407\r
1408 it("should be able to update many records", function() {\r
1409 var user1 = getAndComplete('User', 1),\r
1410 user2 = getAndComplete('User', 2),\r
1411 user3 = getAndComplete('User', 3);\r
1412\r
1413 user1.set('name', 'Foo');\r
1414 user2.set('name', 'Bar');\r
1415 user3.set('name', 'Baz');\r
1416\r
1417 expect(session.getChanges()).toEqual({\r
1418 User: {\r
1419 U: [{\r
1420 id: 1,\r
1421 name: 'Foo'\r
1422 }, {\r
1423 id: 2,\r
1424 name: 'Bar'\r
1425 }, {\r
1426 id: 3,\r
1427 name: 'Baz'\r
1428 }]\r
1429 }\r
1430 });\r
1431 });\r
1432\r
1433 it("should not include a non-dirty record", function() {\r
1434 var user = getAndComplete('User', 1);\r
1435 user.set('name', 'Foo');\r
1436 user.commit();\r
1437 expect(session.getChanges()).toBeNull();\r
1438 });\r
1439\r
1440 it("should not include a dropped record", function() {\r
1441 var user = getAndComplete('User', 1);\r
1442 user.set('name', 'Foo');\r
1443 user.drop();\r
1444 expect(session.getChanges().User.U).toBeUndefined();\r
1445 });\r
1446\r
1447 it("should not include changes to phantoms", function() {\r
1448 var user = session.createRecord('User');\r
1449 user.set('name', 'Foo');\r
1450 expect(session.getChanges().User.U).toBeUndefined();\r
1451 });\r
1452 });\r
1453\r
1454 describe("drop", function() {\r
1455 it("should include dropped records", function() {\r
1456 var user = getAndComplete('User', 1);\r
1457 user.drop();\r
1458 expect(session.getChanges()).toEqual({\r
1459 User: {\r
1460 D: [1]\r
1461 }\r
1462 });\r
1463 });\r
1464\r
1465 it("should drop multiple records", function() {\r
1466 var user1 = getAndComplete('User', 1),\r
1467 user2 = getAndComplete('User', 2),\r
1468 user3 = getAndComplete('User', 3);\r
1469\r
1470 user1.drop();\r
1471 user2.drop();\r
1472 user3.drop();\r
1473 expect(session.getChanges()).toEqual({\r
1474 User: {\r
1475 D: [1, 2, 3]\r
1476 }\r
1477 });\r
1478 });\r
1479\r
1480 it("should not include phantom records", function() {\r
1481 var user = session.createRecord('User');\r
1482 user.drop();\r
1483 expect(session.getChanges()).toBeNull();\r
1484 });\r
1485 });\r
1486 });\r
1487\r
1488 describe("associations", function() {\r
1489 describe("pending drops", function() {\r
1490 beforeEach(function() {\r
1491 Ext.define('spec.Order', {\r
1492 extend: 'Ext.data.Model',\r
1493 fields: ['id', 'date', {\r
1494 name: 'addressId',\r
1495 unique: true,\r
1496 reference: {\r
1497 child: 'Address'\r
1498 }\r
1499 }]\r
1500 });\r
1501 Ext.define('spec.OrderItem', {\r
1502 extend: 'Ext.data.Model',\r
1503 fields: ['id', 'price', 'qty', {\r
1504 name: 'orderId',\r
1505 reference: {\r
1506 parent: 'Order'\r
1507 }\r
1508 }]\r
1509 });\r
1510 });\r
1511\r
1512 afterEach(function() {\r
1513 Ext.undefine('spec.Order');\r
1514 Ext.undefine('spec.OrderItem');\r
1515 });\r
1516\r
1517 it("should resolve any pending drops", function() {\r
1518 var order = getAndComplete('Order', 1, session, {\r
1519 addressId: 101\r
1520 });\r
1521\r
1522 var address = order.getAddress();\r
1523 getAndComplete('Address', 101);\r
1524\r
1525 var orderItems = order.orderItems();\r
1526\r
1527 orderItems.load();\r
1528 completeRequest([{\r
1529 id: 201,\r
1530 orderId: 1\r
1531 }, {\r
1532 id: 202,\r
1533 orderId: 1\r
1534 }]);\r
1535 \r
1536 var orderItem = orderItems.getAt(0);\r
1537\r
1538 order.setAddress(null);\r
1539 orderItems.removeAt(0);\r
1540\r
1541 expect(address.dropped).toBe(false);\r
1542 expect(orderItem.dropped).toBe(false);\r
1543 expect(session.getChanges()).toEqual({\r
1544 Address: {\r
1545 D: [101]\r
1546 },\r
1547 Order: {\r
1548 U: [{\r
1549 id: 1,\r
1550 addressId: null\r
1551 }]\r
1552 },\r
1553 OrderItem: {\r
1554 D: [201]\r
1555 }\r
1556 });\r
1557 });\r
1558 });\r
1559 describe("one to one", function() {\r
1560 var user, address;\r
1561 \r
1562 afterEach(function() {\r
1563 user = address = null;\r
1564 });\r
1565\r
1566 describe("basic operations", function() {\r
1567 beforeEach(function() {\r
1568 user = getAndComplete('User', 1, session, {\r
1569 addressId: 17\r
1570 });\r
1571 address = user.getAddress();\r
1572 getAndComplete('Address', 17);\r
1573 });\r
1574\r
1575 it("should not include any changes when loading an association", function() {\r
1576 expect(session.getChanges()).toBeNull();\r
1577 });\r
1578\r
1579 it("should not include extraneous records when changing the key", function() {\r
1580 user.setAddress(3);\r
1581 expect(session.getChanges()).toEqual({\r
1582 User: {\r
1583 U: [{\r
1584 id: 1,\r
1585 addressId: 3\r
1586 }]\r
1587 }\r
1588 })\r
1589 });\r
1590\r
1591 it("should read the non key holder when nulling out a reference", function() {\r
1592 user.setAddress(null);\r
1593 expect(session.getChanges()).toEqual({\r
1594 User: {\r
1595 U: [{\r
1596 id: 1,\r
1597 addressId: null\r
1598 }]\r
1599 }\r
1600 })\r
1601 });\r
1602 });\r
1603\r
1604 describe("changing id", function() {\r
1605 it("should update when changing the id of the non key holder", function() {\r
1606 address = session.createRecord('spec.Address');\r
1607 user = session.createRecord('spec.User');\r
1608 user.setAddress(address);\r
1609 address.setId(1000);\r
1610 expect(session.getChanges()).toEqual({\r
1611 Address: {\r
1612 C: [{\r
1613 id: 1000\r
1614 }]\r
1615 },\r
1616 User: {\r
1617 C: [{\r
1618 id: user.getId(),\r
1619 addressId: 1000\r
1620 }]\r
1621 }\r
1622 });\r
1623 });\r
1624 });\r
1625 });\r
1626\r
1627 describe("many to one", function() {\r
1628 var user, posts;\r
1629\r
1630 afterEach(function() {\r
1631 user = posts = null;\r
1632 });\r
1633\r
1634 describe("basic operations", function() {\r
1635 beforeEach(function() {\r
1636 user = getAndComplete('User', 1);\r
1637 posts = user.posts();\r
1638 posts.load();\r
1639 completeRequest([{id: 101, userId: 1}, {id: 102, userId: 1}, {id: 103, userId: 1}]);\r
1640 });\r
1641\r
1642 it("should not include any read records", function() {\r
1643 expect(session.getChanges()).toBeNull();\r
1644 });\r
1645\r
1646 it("should push store removals as updates", function() {\r
1647 posts.removeAt(0);\r
1648 expect(session.getChanges()).toEqual({\r
1649 Post: {\r
1650 U: [{\r
1651 id: 101,\r
1652 userId: null\r
1653 }]\r
1654 }\r
1655 });\r
1656 });\r
1657\r
1658 it("should push store adds as updates", function() {\r
1659 var post = getAndComplete('Post', 104);\r
1660 posts.add(post);\r
1661 expect(session.getChanges()).toEqual({\r
1662 Post: {\r
1663 U: [{\r
1664 id: 104,\r
1665 userId: 1\r
1666 }]\r
1667 }\r
1668 });\r
1669 });\r
1670\r
1671 it("should push store adds of phantoms as creates", function() {\r
1672 var post = session.createRecord('Post');\r
1673 posts.add(post);\r
1674 expect(session.getChanges()).toEqual({\r
1675 Post: {\r
1676 C: [{\r
1677 id: post.getId(),\r
1678 userId: 1\r
1679 }]\r
1680 }\r
1681 })\r
1682 });\r
1683 });\r
1684\r
1685 describe("changing id", function() {\r
1686 it("should update when changing the id of the non key holder", function() {\r
1687 user = session.createRecord('User');\r
1688 var post = session.createRecord('Post');\r
1689 user.posts().add(post);\r
1690\r
1691 user.setId(1000);\r
1692 expect(session.getChanges()).toEqual({\r
1693 User: {\r
1694 C: [{\r
1695 id: 1000\r
1696 }]\r
1697 },\r
1698 Post: {\r
1699 C: [{\r
1700 id: post.getId(),\r
1701 userId: 1000\r
1702 }]\r
1703 }\r
1704 });\r
1705 });\r
1706 });\r
1707 });\r
1708\r
1709 describe("many to many", function() {\r
1710 describe("via store modifications", function() {\r
1711 var user, groups;\r
1712\r
1713 beforeEach(function() {\r
1714 user = getAndComplete('User', 1);\r
1715 groups = user.groups();\r
1716 });\r
1717\r
1718 afterEach(function() {\r
1719 user = groups = null;\r
1720 });\r
1721\r
1722 describe("store not loaded", function() {\r
1723 it("should include local adds", function() {\r
1724 var group1 = getAndComplete('Group', 101);\r
1725 groups.add(group1);\r
1726 expect(session.getChanges()).toEqual({\r
1727 User: {\r
1728 groups: {\r
1729 C: {\r
1730 1: [101]\r
1731 }\r
1732 }\r
1733 }\r
1734 });\r
1735 });\r
1736\r
1737 it("should include local phantom adds and a create", function() {\r
1738 var group = session.createRecord('Group'),\r
1739 id = group.getId();\r
1740\r
1741 groups.add(group);\r
1742 expect(session.getChanges()).toEqual({\r
1743 User: {\r
1744 groups: {\r
1745 C: {\r
1746 1: [id]\r
1747 }\r
1748 }\r
1749 },\r
1750 Group: {\r
1751 C: [{\r
1752 id: group.getId()\r
1753 }]\r
1754 }\r
1755 });\r
1756 });\r
1757\r
1758 it("should ignore adds & cancelling removes", function() {\r
1759 var group1 = getAndComplete('Group', 101);\r
1760 groups.add(group1);\r
1761 groups.removeAt(0);\r
1762 expect(session.getChanges()).toBeNull();\r
1763 });\r
1764 });\r
1765\r
1766 describe("store loaded", function() {\r
1767 beforeEach(function() {\r
1768 groups.load();\r
1769 completeRequest([{id: 101}, {id: 102}, {id: 103}]);\r
1770 });\r
1771\r
1772 it("should not include any records loaded into the store", function() {\r
1773 expect(session.getChanges()).toBeNull();\r
1774 });\r
1775\r
1776 it("should include a local add to existing records", function() {\r
1777 var group4 = getAndComplete('Group', 104);\r
1778 groups.add(group4);\r
1779 expect(session.getChanges()).toEqual({\r
1780 User: {\r
1781 groups: {\r
1782 C: {\r
1783 1: [104]\r
1784 }\r
1785 }\r
1786 }\r
1787 });\r
1788 });\r
1789\r
1790 it("should include local phantom adds and a create", function() {\r
1791 var group = session.createRecord('Group'),\r
1792 id = group.getId();\r
1793\r
1794 groups.add(group);\r
1795 expect(session.getChanges()).toEqual({\r
1796 User: {\r
1797 groups: {\r
1798 C: {\r
1799 1: [id]\r
1800 }\r
1801 }\r
1802 },\r
1803 Group: {\r
1804 C: [{\r
1805 id: group.getId()\r
1806 }]\r
1807 }\r
1808 });\r
1809 });\r
1810\r
1811 it("should ignore adds & cancelling removes", function() {\r
1812 var group4 = getAndComplete('Group', 104);\r
1813 groups.add(group4);\r
1814 groups.remove(group4);\r
1815 expect(session.getChanges()).toBeNull();\r
1816 });\r
1817\r
1818 it("should drop records removed from the store", function() {\r
1819 groups.removeAt(0);\r
1820 expect(session.getChanges()).toEqual({\r
1821 User: {\r
1822 groups: {\r
1823 D: {\r
1824 1: [101]\r
1825 }\r
1826 }\r
1827 }\r
1828 });\r
1829 });\r
1830 });\r
1831 });\r
1832\r
1833 describe("via updates", function() {\r
1834 it("should process creates without records", function() {\r
1835 session.update({\r
1836 User: {\r
1837 groups: {\r
1838 C: {\r
1839 1: [101, 102]\r
1840 }\r
1841 }\r
1842 }\r
1843 });\r
1844\r
1845 expect(session.getChanges()).toEqual({\r
1846 User: {\r
1847 groups: {\r
1848 C: {\r
1849 1: [101, 102]\r
1850 }\r
1851 }\r
1852 }\r
1853 });\r
1854 });\r
1855\r
1856 it("should process drops without records", function() {\r
1857 session.update({\r
1858 User: {\r
1859 groups: {\r
1860 D: {\r
1861 1: [103, 104]\r
1862 }\r
1863 }\r
1864 }\r
1865 });\r
1866\r
1867 expect(session.getChanges()).toEqual({\r
1868 User: {\r
1869 groups: {\r
1870 D: {\r
1871 1: [103, 104]\r
1872 }\r
1873 }\r
1874 }\r
1875 });\r
1876 });\r
1877 });\r
1878\r
1879 describe("changing id", function() {\r
1880 it("should update when changing the id of the left", function() {\r
1881 var group = session.createRecord('Group'),\r
1882 user = session.createRecord('User');\r
1883\r
1884 user.groups().add(group);\r
1885 user.setId(1000);\r
1886\r
1887 expect(session.getChanges()).toEqual({\r
1888 User: {\r
1889 C: [{\r
1890 id: 1000\r
1891 }],\r
1892 groups: {\r
1893 C: {\r
1894 1000: [group.getId()]\r
1895 }\r
1896 }\r
1897 },\r
1898 Group: {\r
1899 C: [{\r
1900 id: group.getId()\r
1901 }]\r
1902 }\r
1903 });\r
1904 });\r
1905\r
1906 it("should update when changing the id of the right", function() {\r
1907 var group = session.createRecord('Group'),\r
1908 user = session.createRecord('User');\r
1909\r
1910 group.users().add(user);\r
1911 group.setId(1000);\r
1912\r
1913 var o = {};\r
1914 o[user.getId()] = [1000];\r
1915\r
1916 expect(session.getChanges()).toEqual({\r
1917 User: {\r
1918 C: [{\r
1919 id: user.getId()\r
1920 }],\r
1921 groups: {\r
1922 C: o\r
1923 }\r
1924 },\r
1925 Group: {\r
1926 C: [{\r
1927 id: 1000\r
1928 }]\r
1929 }\r
1930 });\r
1931 });\r
1932 });\r
1933 });\r
1934 });\r
1935 });\r
1936\r
1937 describe("update", function() {\r
1938 beforeEach(function() {\r
1939 Ext.data.Model.schema.setNamespace('spec');\r
1940 session = new Ext.data.Session();\r
1941 });\r
1942\r
1943 afterEach(function() {\r
1944 Ext.data.Model.schema.clear(true);\r
1945 }); \r
1946\r
1947 describe("basic operations", function() {\r
1948 var User;\r
1949\r
1950 function wrapBlock(action, data) {\r
1951 var o = {\r
1952 User: {}\r
1953 };\r
1954 o.User[action] = data;\r
1955 return o;\r
1956 }\r
1957\r
1958 beforeEach(function() {\r
1959 User = Ext.define('spec.User', {\r
1960 extend: 'Ext.data.Model',\r
1961 fields: ['id', 'name', 'age']\r
1962 });\r
1963 });\r
1964\r
1965 afterEach(function() {\r
1966 Ext.undefine('spec.User');\r
1967 User = null;\r
1968 });\r
1969\r
1970 it("should throw an exception for an unrecognized entity", function() {\r
1971 expect(function() {\r
1972 session.update({\r
1973 Luser: {}\r
1974 });\r
1975 }).toThrow();\r
1976 });\r
1977\r
1978 describe("read", function() {\r
1979 it("should add the record to the session", function() {\r
1980 session.update(wrapBlock('R', [{\r
1981 id: 17\r
1982 }]));\r
1983 expect(session.peekRecord('User', 17)).not.toBeNull();\r
1984 });\r
1985\r
1986 it("should have the data on the record", function() {\r
1987 session.update(wrapBlock('R', [{\r
1988 id: 17,\r
1989 name: 'Foo',\r
1990 age: 32\r
1991 }]));\r
1992 var user = session.getRecord('User', 17);\r
1993 expect(user.get('name')).toBe('Foo');\r
1994 expect(user.get('age')).toBe(32);\r
1995 });\r
1996\r
1997 it("should not be dirty", function() {\r
1998 session.update(wrapBlock('R', [{\r
1999 id: 17,\r
2000 name: 'Foo',\r
2001 age: 32\r
2002 }]));\r
2003 var user = session.getRecord('User', 17);\r
2004 expect(user.dirty).toBe(false);\r
2005 });\r
2006\r
2007 it("should not be phantom even if we don't have an id", function() {\r
2008 session.update(wrapBlock('R', [{\r
2009 name: 'Foo',\r
2010 age: 32\r
2011 }]));\r
2012 var user = session.getRecord('User', 'User-1');\r
2013 expect(user.phantom).toBe(false);\r
2014 });\r
2015\r
2016 it("should be able to read multiple records", function() {\r
2017 session.update(wrapBlock('R', [{\r
2018 id: 1,\r
2019 name: 'Foo'\r
2020 }, {\r
2021 id: 2,\r
2022 name: 'Bar'\r
2023 }, {\r
2024 id: 3,\r
2025 name: 'Baz'\r
2026 }]));\r
2027 expect(session.peekRecord('User', 1)).not.toBeNull();\r
2028 expect(session.peekRecord('User', 2)).not.toBeNull();\r
2029 expect(session.peekRecord('User', 3)).not.toBeNull();\r
2030 });\r
2031\r
2032 it("should throw an exception if the record is in the session", function() {\r
2033 getAndComplete('User', 1);\r
2034 expect(function() {\r
2035 session.update(wrapBlock('R', [{\r
2036 id: 1\r
2037 }]));\r
2038 }).toThrow();\r
2039 });\r
2040 });\r
2041\r
2042 describe("create", function() {\r
2043 it("should add the record to the session", function() {\r
2044 session.update(wrapBlock('C', [{\r
2045 id: 17\r
2046 }]));\r
2047 expect(session.peekRecord('User', 17)).not.toBeNull();\r
2048 });\r
2049\r
2050 it("should have the data on the record", function() {\r
2051 session.update(wrapBlock('C', [{\r
2052 id: 17,\r
2053 name: 'Foo',\r
2054 age: 32\r
2055 }]));\r
2056 var user = session.getRecord('User', 17);\r
2057 expect(user.get('name')).toBe('Foo');\r
2058 expect(user.get('age')).toBe(32);\r
2059 });\r
2060\r
2061 it("should be a phantom", function() {\r
2062 session.update(wrapBlock('C', [{\r
2063 name: 'Foo',\r
2064 age: 32\r
2065 }]));\r
2066 var user = session.getRecord('User', 'User-1');\r
2067 expect(user.phantom).toBe(true);\r
2068 });\r
2069\r
2070 it("should be able to create multiple records", function() {\r
2071 session.update(wrapBlock('C', [{\r
2072 name: 'Foo'\r
2073 }, {\r
2074 name: 'Bar'\r
2075 }, {\r
2076 name: 'Baz'\r
2077 }]));\r
2078 expect(session.peekRecord('User', 'User-1')).not.toBeNull();\r
2079 expect(session.peekRecord('User', 'User-2')).not.toBeNull();\r
2080 expect(session.peekRecord('User', 'User-3')).not.toBeNull();\r
2081 });\r
2082\r
2083 it("should throw an exception if the record is in the session", function() {\r
2084 getAndComplete('User', 17);\r
2085 expect(function() {\r
2086 session.update(wrapBlock('C', [{\r
2087 id: 17\r
2088 }]));\r
2089 }).toThrow();\r
2090 });\r
2091 });\r
2092\r
2093 describe("drop", function() {\r
2094 it("should drop the record", function() {\r
2095 var user = getAndComplete('User', 100);\r
2096 session.update(wrapBlock('D', [100]));\r
2097 expect(user.dropped).toBe(true);\r
2098 });\r
2099 it("should be able to drop multiple records", function() {\r
2100 var user100 = getAndComplete('User', 100),\r
2101 user200 = getAndComplete('User', 200),\r
2102 user300 = getAndComplete('User', 300);\r
2103\r
2104 session.update(wrapBlock('D', [100, 200, 300]));\r
2105 expect(user100.dropped).toBe(true);\r
2106 expect(user200.dropped).toBe(true);\r
2107 expect(user300.dropped).toBe(true);\r
2108 });\r
2109\r
2110 it("should throw an exception if the record does not exist", function() {\r
2111 expect(function() {\r
2112 session.update(wrapBlock('D', [100]));\r
2113 }).toThrow();\r
2114 });\r
2115\r
2116 it("should handle a writer with writeAllFields: true", function() {\r
2117 User.getProxy().getWriter().setWriteAllFields(true);\r
2118 var user = getAndComplete('User', 100, session, {\r
2119 name: 'Foo',\r
2120 age: 100\r
2121 });\r
2122 session.update(wrapBlock('D', [{\r
2123 id: 100,\r
2124 name: 'Foo',\r
2125 age: 100\r
2126 }]));\r
2127 expect(user.dropped).toBe(true);\r
2128 });\r
2129 });\r
2130\r
2131 describe("update", function() {\r
2132 it("should update the record data", function() {\r
2133 var user = session.createRecord('User', {\r
2134 id: 100,\r
2135 name: 'Foo'\r
2136 });\r
2137 session.update(wrapBlock('U', [{\r
2138 id: 100,\r
2139 name: 'Bar'\r
2140 }]));\r
2141 expect(user.get('name')).toBe('Bar');\r
2142 });\r
2143\r
2144 it("should not commit the record", function() {\r
2145 var user = session.createRecord('User', {\r
2146 id: 100,\r
2147 name: 'Foo',\r
2148 age: 10\r
2149 });\r
2150 session.update(wrapBlock('U', [{\r
2151 id: 100,\r
2152 name: 'Bar',\r
2153 age: 11\r
2154 }]));\r
2155 expect(user.dirty).toBe(true);\r
2156 expect(user.isModified('name')).toBe(true);\r
2157 expect(user.isModified('age')).toBe(true);\r
2158 });\r
2159\r
2160 it("should not be dirty if the data does not change", function() {\r
2161 var user = session.createRecord('User', {\r
2162 id: 100,\r
2163 name: 'Foo',\r
2164 age: 23\r
2165 });\r
2166 session.update(wrapBlock('U', [{\r
2167 id: 100,\r
2168 name: 'Foo',\r
2169 age: 23\r
2170 }]));\r
2171 expect(user.dirty).toBe(false);\r
2172 });\r
2173\r
2174 it("should handle multiple updates", function() {\r
2175 var user101 = session.createRecord('User', {\r
2176 id: 101,\r
2177 name: 'Foo'\r
2178 });\r
2179 var user102 = session.createRecord('User', {\r
2180 id: 102,\r
2181 name: 'Bar'\r
2182 });\r
2183 var user103 = session.createRecord('User', {\r
2184 id: 103,\r
2185 name: 'Baz'\r
2186 });\r
2187\r
2188 session.update(wrapBlock('U', [{\r
2189 id: 101,\r
2190 name: 'A'\r
2191 }, {\r
2192 id: 102,\r
2193 name: 'B'\r
2194 }, {\r
2195 id: 103,\r
2196 name: 'C'\r
2197 }]));\r
2198 expect(user101.get('name')).toBe('A');\r
2199 expect(user102.get('name')).toBe('B');\r
2200 expect(user103.get('name')).toBe('C');\r
2201\r
2202 });\r
2203\r
2204 it("should handle object syntax", function() {\r
2205 var user101 = session.createRecord('User', {\r
2206 id: 101,\r
2207 name: 'Foo'\r
2208 });\r
2209 var user102 = session.createRecord('User', {\r
2210 id: 102,\r
2211 name: 'Bar'\r
2212 });\r
2213 var user103 = session.createRecord('User', {\r
2214 id: 103,\r
2215 name: 'Baz'\r
2216 });\r
2217\r
2218 session.update(wrapBlock('U', {\r
2219 101: {\r
2220 name: 'A'\r
2221 },\r
2222 102: {\r
2223 name: 'B'\r
2224 },\r
2225 103: {\r
2226 name: 'C'\r
2227 }\r
2228 }));\r
2229 expect(user101.get('name')).toBe('A');\r
2230 expect(user102.get('name')).toBe('B');\r
2231 expect(user103.get('name')).toBe('C');\r
2232 });\r
2233\r
2234 it("should throw an exception if the record does not exist in the store", function() {\r
2235 expect(function() {\r
2236 session.update(wrapBlock('U', [{\r
2237 id: 100,\r
2238 name: 'Bar'\r
2239 }]));\r
2240 }).toThrow();\r
2241 });\r
2242\r
2243 it("should throw an exception if the record is dropped", function() {\r
2244 var user = getAndComplete('User', 1);\r
2245 user.drop();\r
2246 expect(function() {\r
2247 session.update(wrapBlock('U', [{\r
2248 id: 100,\r
2249 name: 'Bar'\r
2250 }]));\r
2251 }).toThrow();\r
2252 });\r
2253 });\r
2254\r
2255 describe("associations", function() {\r
2256 var Post, Group;\r
2257\r
2258 beforeEach(function() {\r
2259 Post = Ext.define('spec.Post', {\r
2260 extend: 'Ext.data.Model',\r
2261 fields: ['id', 'content', {\r
2262 name: 'userId',\r
2263 reference: 'User'\r
2264 }]\r
2265 });\r
2266\r
2267 Group = Ext.define('spec.Group', {\r
2268 extend: 'Ext.data.Model',\r
2269 fields: ['id', 'name'],\r
2270 manyToMany: 'User'\r
2271 });\r
2272 });\r
2273\r
2274 afterEach(function() {\r
2275 Post = Group = null;\r
2276 Ext.undefine('spec.Post');\r
2277 Ext.undefine('spec.Group');\r
2278 });\r
2279\r
2280 describe("pending drops", function() {\r
2281 beforeEach(function() {\r
2282 Ext.define('spec.Order', {\r
2283 extend: 'Ext.data.Model',\r
2284 fields: ['id', 'date', {\r
2285 name: 'addressId',\r
2286 unique: true,\r
2287 reference: {\r
2288 child: 'Address'\r
2289 }\r
2290 }]\r
2291 });\r
2292\r
2293 Ext.define('spec.Address', {\r
2294 extend: 'Ext.data.Model',\r
2295 fields: ['id', 'city']\r
2296 });\r
2297\r
2298 Ext.define('spec.OrderItem', {\r
2299 extend: 'Ext.data.Model',\r
2300 fields: ['id', 'price', 'qty', {\r
2301 name: 'orderId',\r
2302 reference: {\r
2303 parent: 'Order'\r
2304 }\r
2305 }]\r
2306 });\r
2307 });\r
2308\r
2309 afterEach(function() {\r
2310 Ext.undefine('spec.Order');\r
2311 Ext.undefine('spec.Address');\r
2312 Ext.undefine('spec.OrderItem');\r
2313 });\r
2314\r
2315 it("should resolve any pending", function() {\r
2316 var order = getAndComplete('Order', 1, session, {\r
2317 addressId: 101\r
2318 });\r
2319\r
2320 var address = order.getAddress();\r
2321 getAndComplete('Address', 101);\r
2322\r
2323 var orderItems = order.orderItems();\r
2324\r
2325 orderItems.load();\r
2326 completeRequest([{\r
2327 id: 201,\r
2328 orderId: 1\r
2329 }, {\r
2330 id: 202,\r
2331 orderId: 1\r
2332 }]);\r
2333 \r
2334 var orderItem = orderItems.getAt(0);\r
2335\r
2336 order.setAddress(null);\r
2337 orderItems.removeAt(0);\r
2338\r
2339 expect(address.dropped).toBe(false);\r
2340 expect(orderItem.dropped).toBe(false);\r
2341 session.update({\r
2342 Order: {\r
2343 U: [{\r
2344 id: 1,\r
2345 date: new Date()\r
2346 }]\r
2347 }\r
2348 });\r
2349 expect(address.dropped).toBe(true);\r
2350 expect(orderItem.dropped).toBe(true);\r
2351 });\r
2352 });\r
2353\r
2354 describe("many to one", function() {\r
2355 it("should process any CRUD operations before associations", function() {\r
2356 session.update({\r
2357 User: {\r
2358 R: [{id: 1}],\r
2359 posts: {\r
2360 R: {\r
2361 1: [101]\r
2362 }\r
2363 }\r
2364 },\r
2365 Post: {\r
2366 R: [{\r
2367 id: 101,\r
2368 userId: 1\r
2369 }]\r
2370 }\r
2371 });\r
2372\r
2373 var user = session.getRecord('User', 1);\r
2374 expect(user.posts().indexOfId(101)).toBe(0);\r
2375 });\r
2376\r
2377 describe("without an entity CRUD block", function() {\r
2378 it("should throw an exception if the owner model does not exist", function() {\r
2379 getAndComplete('Post', 101);\r
2380 expect(function() {\r
2381 session.update({\r
2382 User: {\r
2383 posts: {\r
2384 R: {\r
2385 1: [101]\r
2386 }\r
2387 }\r
2388 }\r
2389 });\r
2390 }).toThrow();\r
2391 });\r
2392\r
2393 it("should throw an exception if the child model does not exist", function() {\r
2394 getAndComplete('User', 1);\r
2395 expect(function() {\r
2396 session.update({\r
2397 User: {\r
2398 posts: {\r
2399 R: {\r
2400 1: [101]\r
2401 }\r
2402 }\r
2403 }\r
2404 });\r
2405 }).toThrow();\r
2406 });\r
2407 });\r
2408\r
2409 describe("with an entity CRUD block", function() {\r
2410 it("should throw an exception if the owner model does not exist and wasn't read", function() {\r
2411 expect(function() {\r
2412 session.update({\r
2413 User: {\r
2414 posts: {\r
2415 R: {\r
2416 1: [101]\r
2417 }\r
2418 }\r
2419 },\r
2420 Post: {\r
2421 R: [{\r
2422 id: 101,\r
2423 userId: 1\r
2424 }]\r
2425 }\r
2426 });\r
2427 }).toThrow();\r
2428 });\r
2429\r
2430 it("should throw an exception if the child model does not exist and wasn't read", function() {\r
2431 expect(function() {\r
2432 session.update({\r
2433 User: {\r
2434 R: [{id: 1}],\r
2435 posts: {\r
2436 R: {\r
2437 1: [101]\r
2438 }\r
2439 }\r
2440 }\r
2441 });\r
2442 }).toThrow();\r
2443 });\r
2444 });\r
2445\r
2446 describe("with the store not created", function() {\r
2447 it("should create the store with data", function() {\r
2448 var user = getAndComplete('User', 1);\r
2449\r
2450 session.update({\r
2451 User: {\r
2452 posts: {\r
2453 R: {\r
2454 1: [101, 102, 103]\r
2455 }\r
2456 }\r
2457 },\r
2458 Post: {\r
2459 R: [{\r
2460 id: 101,\r
2461 userId: 1\r
2462 }, {\r
2463 id: 102,\r
2464 userId: 1\r
2465 }, {\r
2466 id: 103,\r
2467 userId: 1\r
2468 }]\r
2469 }\r
2470 });\r
2471\r
2472 var posts = user.posts();\r
2473 expect(posts.getCount()).toBe(3);\r
2474 expect(posts.getAt(0)).toBe(session.peekRecord('Post', 101));\r
2475 expect(posts.getAt(1)).toBe(session.peekRecord('Post', 102));\r
2476 expect(posts.getAt(2)).toBe(session.peekRecord('Post', 103));\r
2477 });\r
2478\r
2479 it("should create the store and not trigger a load, should set loadCount & complete", function() {\r
2480 var user = getAndComplete('User', 1),\r
2481 spy = spyOn(Post.getProxy(), 'read');\r
2482\r
2483 session.update({\r
2484 User: {\r
2485 posts: {\r
2486 R: {\r
2487 1: [101]\r
2488 }\r
2489 }\r
2490 },\r
2491 Post: {\r
2492 R: [{\r
2493 id: 101,\r
2494 userId: 1\r
2495 }]\r
2496 }\r
2497 });\r
2498 var posts = user.posts();\r
2499 expect(posts.getCount()).toBe(1);\r
2500 expect(posts.loadCount).toBe(1);\r
2501 expect(posts.complete).toBe(true);\r
2502 expect(spy).not.toHaveBeenCalled();\r
2503 });\r
2504\r
2505 it("should include local records with matching FK", function() {\r
2506 var user = getAndComplete('User', 1),\r
2507 post1 = getAndComplete('Post', 101);\r
2508\r
2509 post1.set('userId', 1);\r
2510\r
2511 session.update({\r
2512 User: {\r
2513 posts: {\r
2514 R: {\r
2515 1: [102]\r
2516 }\r
2517 }\r
2518 },\r
2519 Post: {\r
2520 R: [{\r
2521 id: 102,\r
2522 userId: 1\r
2523 }]\r
2524 }\r
2525 });\r
2526\r
2527 var posts = user.posts();\r
2528 expect(posts.getCount()).toBe(2);\r
2529 });\r
2530\r
2531 it("should infer the key when one is not specified", function() {\r
2532 var user = getAndComplete('User', 1),\r
2533 post1 = getAndComplete('Post', 101);\r
2534\r
2535 // Post exists, but doesn't have a FK to the user\r
2536 session.update({\r
2537 User: {\r
2538 posts: {\r
2539 R: {\r
2540 1: [101]\r
2541 }\r
2542 }\r
2543 }\r
2544 });\r
2545\r
2546 var posts = user.posts();\r
2547 expect(posts.getCount()).toBe(1);\r
2548 expect(posts.getAt(0)).toBe(post1);\r
2549 });\r
2550\r
2551 it("should exclude local records where the FK does not match", function() {\r
2552 var user = getAndComplete('User', 1),\r
2553 post1 = getAndComplete('Post', 101, null, {\r
2554 userId: 2\r
2555 });\r
2556\r
2557 // Post exists, but doesn't have a FK to the user\r
2558 session.update({\r
2559 User: {\r
2560 posts: {\r
2561 R: {\r
2562 1: [101]\r
2563 }\r
2564 }\r
2565 }\r
2566 });\r
2567\r
2568 var posts = user.posts();\r
2569 expect(posts.getCount()).toBe(0);\r
2570 });\r
2571 });\r
2572\r
2573 describe("with the store created", function() {\r
2574 it("should fill an empty store", function() {\r
2575 var user = getAndComplete('User', 1),\r
2576 posts = user.posts();\r
2577\r
2578 session.update({\r
2579 User: {\r
2580 posts: {\r
2581 R: {\r
2582 1: [101, 102, 103]\r
2583 }\r
2584 }\r
2585 },\r
2586 Post: {\r
2587 R: [{\r
2588 id: 101,\r
2589 userId: 1\r
2590 }, {\r
2591 id: 102,\r
2592 userId: 1\r
2593 }, {\r
2594 id: 103,\r
2595 userId: 1\r
2596 }]\r
2597 }\r
2598 });\r
2599\r
2600 expect(posts.getCount()).toBe(3);\r
2601 expect(posts.getAt(0)).toBe(session.peekRecord('Post', 101));\r
2602 expect(posts.getAt(1)).toBe(session.peekRecord('Post', 102));\r
2603 expect(posts.getAt(2)).toBe(session.peekRecord('Post', 103));\r
2604 expect(posts.complete).toBe(true);\r
2605 });\r
2606\r
2607 describe("store not loaded", function() {\r
2608 it("should include locally added records", function() {\r
2609 var user = getAndComplete('User', 1),\r
2610 post1 = getAndComplete('Post', 101),\r
2611 posts = user.posts();\r
2612\r
2613 posts.add(post1);\r
2614 session.update({\r
2615 User: {\r
2616 posts: {\r
2617 R: {\r
2618 1: [102]\r
2619 }\r
2620 }\r
2621 },\r
2622 Post: {\r
2623 R: [{\r
2624 id: 102,\r
2625 userId: 1\r
2626 }]\r
2627 }\r
2628 });\r
2629 expect(posts.getCount()).toBe(2);\r
2630 expect(posts.indexOf(post1)).toBe(1);\r
2631 });\r
2632\r
2633 it("should include records with local foreign key modifications", function() {\r
2634 var user = getAndComplete('User', 1),\r
2635 post1 = getAndComplete('Post', 101),\r
2636 posts = user.posts();\r
2637\r
2638 post1.set('userId', 1);\r
2639 session.update({\r
2640 User: {\r
2641 posts: {\r
2642 R: {\r
2643 1: [102]\r
2644 }\r
2645 }\r
2646 },\r
2647 Post: {\r
2648 R: [{\r
2649 id: 102,\r
2650 userId: 1\r
2651 }]\r
2652 }\r
2653 });\r
2654 expect(posts.getCount()).toBe(2);\r
2655 expect(posts.indexOf(session.peekRecord('Post', 102))).toBe(0);\r
2656 expect(posts.indexOf(post1)).toBe(1);\r
2657 });\r
2658\r
2659 it("should exclude locally removed records", function() {\r
2660 var user = getAndComplete('User', 1),\r
2661 post1 = getAndComplete('Post', 101, session, {userId: 1}),\r
2662 post2 = getAndComplete('Post', 102, session, {userId: 1}),\r
2663 posts = user.posts();\r
2664\r
2665 posts.removeAt(0);\r
2666\r
2667 session.update({\r
2668 User: {\r
2669 posts: {\r
2670 R: {\r
2671 1: [101, 102]\r
2672 }\r
2673 }\r
2674 }\r
2675 });\r
2676\r
2677 expect(posts.getCount()).toBe(1);\r
2678 expect(posts.indexOf(post2)).toBe(0);\r
2679 expect(posts.indexOf(post1)).toBe(-1);\r
2680 });\r
2681\r
2682 it("should exclude records with local foreign key modifications", function() {\r
2683 var user = getAndComplete('User', 1),\r
2684 post1 = getAndComplete('Post', 101, session, {userId: 1}),\r
2685 post2 = getAndComplete('Post', 102, session, {userId: 1}),\r
2686 posts = user.posts();\r
2687\r
2688 post1.set('userId', null);\r
2689\r
2690 session.update({\r
2691 User: {\r
2692 posts: {\r
2693 R: {\r
2694 1: [101, 102]\r
2695 }\r
2696 }\r
2697 }\r
2698 });\r
2699\r
2700 expect(posts.getCount()).toBe(1);\r
2701 expect(posts.indexOf(post2)).toBe(0);\r
2702 expect(posts.indexOf(post1)).toBe(-1);\r
2703 });\r
2704 });\r
2705\r
2706 describe("store already loaded", function() {\r
2707 it("should include added items", function() {\r
2708 var user = getAndComplete('User', 1),\r
2709 post1 = getAndComplete('Post', 101),\r
2710 posts = user.posts();\r
2711\r
2712 posts.load();\r
2713 completeRequest([]);\r
2714 posts.add(post1);\r
2715 session.update({\r
2716 User: {\r
2717 posts: {\r
2718 R: {\r
2719 1: [102]\r
2720 }\r
2721 }\r
2722 },\r
2723 Post: {\r
2724 R: [{\r
2725 id: 102,\r
2726 userId: 1\r
2727 }]\r
2728 }\r
2729 });\r
2730 expect(posts.getCount()).toBe(2);\r
2731 expect(posts.getAt(0)).toBe(session.peekRecord('Post', 102));\r
2732 expect(posts.getAt(1)).toBe(post1);\r
2733 });\r
2734\r
2735 it("should exclude removed items", function() {\r
2736 var user = getAndComplete('User', 1),\r
2737 posts = user.posts();\r
2738\r
2739 posts.load();\r
2740 completeRequest([{id: 101, userId: 1}, {id: 102, userId: 1}, {id: 103, userId: 1}]);\r
2741 posts.removeAt(0);\r
2742 session.update({\r
2743 User: {\r
2744 posts: {\r
2745 R: {\r
2746 1: [101, 102, 103]\r
2747 }\r
2748 }\r
2749 }\r
2750 });\r
2751 expect(posts.getCount()).toBe(2);\r
2752 expect(posts.getAt(0)).toBe(session.peekRecord('Post', 102));\r
2753 expect(posts.getAt(1)).toBe(session.peekRecord('Post', 103));\r
2754 });\r
2755 })\r
2756 }); \r
2757 \r
2758 });\r
2759\r
2760 describe("many to many", function() {\r
2761 describe("read", function() {\r
2762 it("should read CRUD records before processing", function() {\r
2763 session.update({\r
2764 User: {\r
2765 R: [{id: 1}],\r
2766 groups: {\r
2767 R: {\r
2768 1: [101, 102]\r
2769 }\r
2770 }\r
2771 },\r
2772 Group: {\r
2773 R: [{id: 101}, {id: 102}]\r
2774 }\r
2775 });\r
2776 var user = session.getRecord('User', 1),\r
2777 groups = user.groups();\r
2778\r
2779 expect(groups.getCount()).toBe(2);\r
2780 expect(groups.getAt(0)).toBe(session.peekRecord('Group', 101));\r
2781 expect(groups.getAt(1)).toBe(session.peekRecord('Group', 102));\r
2782 });\r
2783\r
2784 it("should require the child records being read to be present", function() {\r
2785 // Group 101/102 don't exist and weren't read\r
2786 expect(function() {\r
2787 session.update({\r
2788 User: {\r
2789 R: [{id: 1}],\r
2790 groups: {\r
2791 R: {\r
2792 1: [101, 102]\r
2793 }\r
2794 }\r
2795 }\r
2796 });\r
2797 }).toThrow();\r
2798 });\r
2799\r
2800 it("should require the parent record being read to be present", function() {\r
2801 // User 1 doesn't exist and wasn't read\r
2802 expect(function() {\r
2803 session.update({\r
2804 User: {\r
2805 groups: {\r
2806 R: {\r
2807 1: [101]\r
2808 }\r
2809 }\r
2810 },\r
2811 Post: {\r
2812 R: [{\r
2813 id: 101\r
2814 }]\r
2815 }\r
2816 });\r
2817 }).toThrow();\r
2818 });\r
2819\r
2820 describe("with the store not created", function() {\r
2821 it("should create a store with the items", function() {\r
2822 var user = getAndComplete('User', 1);\r
2823\r
2824 session.update({\r
2825 User: {\r
2826 groups: {\r
2827 R: {\r
2828 1: [101]\r
2829 }\r
2830 }\r
2831 },\r
2832 Group: {\r
2833 R: [{\r
2834 id: 101\r
2835 }]\r
2836 }\r
2837 });\r
2838 var groups = user.groups();\r
2839 expect(groups.getCount()).toBe(1);\r
2840 expect(groups.getAt(0)).toBe(session.peekRecord('Group', 101));\r
2841 });\r
2842\r
2843 it("should not trigger a load", function() {\r
2844 var user = getAndComplete('User', 1),\r
2845 spy = spyOn(Group.getProxy(), 'read');\r
2846\r
2847 session.update({\r
2848 User: {\r
2849 groups: {\r
2850 R: {\r
2851 1: [101]\r
2852 }\r
2853 }\r
2854 },\r
2855 Group: {\r
2856 R: [{\r
2857 id: 101\r
2858 }]\r
2859 }\r
2860 });\r
2861 expect(spy).not.toHaveBeenCalled();\r
2862 });\r
2863\r
2864 it("should set the complete flag", function() {\r
2865 var user = getAndComplete('User', 1);\r
2866\r
2867 session.update({\r
2868 User: {\r
2869 groups: {\r
2870 R: {\r
2871 1: [101]\r
2872 }\r
2873 }\r
2874 },\r
2875 Group: {\r
2876 R: [{\r
2877 id: 101\r
2878 }]\r
2879 }\r
2880 });\r
2881 expect(user.groups().complete).toBe(true);\r
2882 });\r
2883\r
2884 it("should include locally created records", function() {\r
2885 var user = getAndComplete('User', 1);\r
2886\r
2887 session.update({\r
2888 User: {\r
2889 groups: {\r
2890 C: {\r
2891 1: [101]\r
2892 }\r
2893 }\r
2894 }\r
2895 });\r
2896\r
2897 session.update({\r
2898 User: {\r
2899 groups: {\r
2900 R: {\r
2901 1: [102]\r
2902 }\r
2903 }\r
2904 },\r
2905 Group: {\r
2906 R: [{\r
2907 id: 102\r
2908 }]\r
2909 }\r
2910 });\r
2911\r
2912 getAndComplete('Group', 101);\r
2913 var groups = user.groups();\r
2914 expect(groups.getCount()).toBe(2);\r
2915 expect(groups.indexOfId(102)).toBe(0);\r
2916 expect(groups.indexOfId(101)).toBe(1);\r
2917 });\r
2918\r
2919 it("should exclude locally dropped records", function() {\r
2920 var user = getAndComplete('User', 1);\r
2921\r
2922 session.update({\r
2923 User: {\r
2924 groups: {\r
2925 D: {\r
2926 1: [101]\r
2927 }\r
2928 }\r
2929 }\r
2930 });\r
2931\r
2932 session.update({\r
2933 User: {\r
2934 groups: {\r
2935 R: {\r
2936 1: [101, 102]\r
2937 }\r
2938 }\r
2939 },\r
2940 Group: {\r
2941 R: [{\r
2942 id: 101\r
2943 }, {\r
2944 id: 102\r
2945 }]\r
2946 }\r
2947 });\r
2948\r
2949 var groups = user.groups();\r
2950 expect(groups.getCount()).toBe(1);\r
2951 expect(groups.indexOfId(102)).toBe(0);\r
2952 });\r
2953 });\r
2954\r
2955 describe("with the store created", function() {\r
2956 describe("store not loaded", function() {\r
2957 it("should fill an empty store", function() {\r
2958 var user = getAndComplete('User', 1),\r
2959 groups = user.groups();\r
2960\r
2961 session.update({\r
2962 User: {\r
2963 groups: {\r
2964 R: {\r
2965 1: [101]\r
2966 }\r
2967 }\r
2968 },\r
2969 Group: {\r
2970 R: [{\r
2971 id: 101\r
2972 }]\r
2973 }\r
2974 });\r
2975 expect(groups.getCount()).toBe(1);\r
2976 expect(groups.getAt(0)).toBe(session.peekRecord('Group', 101));\r
2977 });\r
2978\r
2979 it("should include local store adds", function() {\r
2980 var user = getAndComplete('User', 1),\r
2981 group1 = getAndComplete('Group', 101),\r
2982 groups = user.groups();\r
2983\r
2984 groups.add(group1);\r
2985\r
2986 session.update({\r
2987 User: {\r
2988 groups: {\r
2989 R: {\r
2990 1: [102]\r
2991 }\r
2992 }\r
2993 },\r
2994 Group: {\r
2995 R: [{\r
2996 id: 102\r
2997 }]\r
2998 }\r
2999 });\r
3000\r
3001 expect(groups.getCount()).toBe(2);\r
3002 expect(groups.getAt(0)).toBe(session.peekRecord('Group', 102));\r
3003 expect(groups.getAt(1)).toBe(group1);\r
3004 });\r
3005\r
3006 it("should exclude local drops", function() {\r
3007 var user = getAndComplete('User', 1),\r
3008 groups = user.groups();\r
3009\r
3010 session.update({\r
3011 User: {\r
3012 groups: {\r
3013 D: {\r
3014 1: [101]\r
3015 }\r
3016 }\r
3017 }\r
3018 });\r
3019\r
3020 session.update({\r
3021 User: {\r
3022 groups: {\r
3023 R: {\r
3024 1: [101, 102]\r
3025 }\r
3026 }\r
3027 },\r
3028 Group: {\r
3029 R: [{\r
3030 id: 101\r
3031 }, {\r
3032 id: 102\r
3033 }]\r
3034 }\r
3035 });\r
3036 expect(groups.getCount()).toBe(1);\r
3037 expect(groups.getAt(0)).toBe(session.peekRecord('Group', 102));\r
3038 });\r
3039 });\r
3040\r
3041 describe("store already loaded", function() {\r
3042 it("should include local store adds", function() {\r
3043 var user = getAndComplete('User', 1),\r
3044 groups = user.groups(),\r
3045 group4 = getAndComplete('Group', 104);\r
3046\r
3047 groups.load();\r
3048 completeRequest([{id: 101}, {id: 102}, {id: 103}]);\r
3049 groups.add(group4);\r
3050\r
3051 session.update({\r
3052 User: {\r
3053 groups: {\r
3054 R: {\r
3055 1: [101, 102, 103]\r
3056 }\r
3057 }\r
3058 }\r
3059 });\r
3060 expect(groups.getCount()).toBe(4);\r
3061 expect(groups.indexOf(group4)).toBe(3);\r
3062 });\r
3063\r
3064 it("should exclude local store removes", function() {\r
3065 var user = getAndComplete('User', 1),\r
3066 groups = user.groups();\r
3067\r
3068 groups.load();\r
3069 completeRequest([{id: 101}, {id: 102}]);\r
3070 groups.removeAt(0);\r
3071\r
3072 session.update({\r
3073 User: {\r
3074 groups: {\r
3075 R: {\r
3076 1: [101, 102]\r
3077 }\r
3078 }\r
3079 }\r
3080 });\r
3081 expect(groups.getCount()).toBe(1);\r
3082 });\r
3083 });\r
3084 });\r
3085 });\r
3086\r
3087 describe("create", function() {\r
3088 describe("with the store not created", function() {\r
3089 describe("with the record created", function() {\r
3090 it("should have the record present in the store when the store is created", function() {\r
3091 var user = getAndComplete('User', 1),\r
3092 group1 = getAndComplete('Group', 101);\r
3093\r
3094 session.update({\r
3095 User: {\r
3096 groups: {\r
3097 C: {\r
3098 1: [101]\r
3099 }\r
3100 }\r
3101 }\r
3102 });\r
3103\r
3104 var groups = user.groups();\r
3105 expect(groups.getCount()).toBe(1);\r
3106 expect(groups.getAt(0)).toBe(group1);\r
3107 });\r
3108 });\r
3109\r
3110 describe("with the record not created", function() {\r
3111 it("should have the record in the store when the record is created", function() {\r
3112 var user = getAndComplete('User', 1);\r
3113\r
3114 session.update({\r
3115 User: {\r
3116 groups: {\r
3117 C: {\r
3118 1: [101]\r
3119 }\r
3120 }\r
3121 }\r
3122 });\r
3123\r
3124 var groups = user.groups();\r
3125 expect(groups.getCount()).toBe(0);\r
3126 var group1 = getAndComplete('Group', 101);\r
3127 expect(groups.getCount()).toBe(1);\r
3128 expect(groups.getAt(0)).toBe(group1);\r
3129 });\r
3130 });\r
3131 });\r
3132\r
3133 describe("with the store created", function() {\r
3134 describe("store not loaded", function() {\r
3135 describe("with the record created", function() {\r
3136 it("should add the record to the store", function() {\r
3137 var user = getAndComplete('User', 1),\r
3138 group1 = getAndComplete('Group', 101),\r
3139 groups = user.groups();\r
3140\r
3141 session.update({\r
3142 User: {\r
3143 groups: {\r
3144 C: {\r
3145 1: [101]\r
3146 }\r
3147 }\r
3148 }\r
3149 });\r
3150\r
3151 expect(groups.getCount()).toBe(1);\r
3152 expect(groups.getAt(0)).toBe(group1);\r
3153 });\r
3154 });\r
3155\r
3156 describe("with the record not created", function() {\r
3157 it("should have the record in the store when the record is created", function() {\r
3158 var user = getAndComplete('User', 1),\r
3159 groups = user.groups();\r
3160\r
3161 session.update({\r
3162 User: {\r
3163 groups: {\r
3164 C: {\r
3165 1: [101]\r
3166 }\r
3167 }\r
3168 }\r
3169 });\r
3170 expect(groups.getCount()).toBe(0);\r
3171 var group1 = getAndComplete('Group', 101);\r
3172 expect(groups.getAt(0)).toBe(group1);\r
3173 });\r
3174 });\r
3175 });\r
3176\r
3177 describe("store already loaded", function() {\r
3178 describe("with the record created", function() {\r
3179 it("should add the record to the store", function() {\r
3180 var user = getAndComplete('User', 1),\r
3181 groups = user.groups(),\r
3182 group3 = getAndComplete('Group', 103);\r
3183\r
3184 groups.load();\r
3185 completeRequest([{id: 101}, {id: 102}]);\r
3186\r
3187 expect(groups.getCount()).toBe(2);\r
3188\r
3189 session.update({\r
3190 User: {\r
3191 groups: {\r
3192 C: {\r
3193 1: [103]\r
3194 }\r
3195 }\r
3196 }\r
3197 });\r
3198 expect(groups.getCount()).toBe(3);\r
3199 expect(groups.getAt(2)).toBe(group3);\r
3200 });\r
3201 });\r
3202\r
3203 describe("with the record not created", function() {\r
3204 it("should have the record in the store when the record is created", function() {\r
3205 var user = getAndComplete('User', 1),\r
3206 groups = user.groups();\r
3207\r
3208 groups.load();\r
3209 completeRequest([{id: 101}, {id: 102}]);\r
3210\r
3211 session.update({\r
3212 User: {\r
3213 groups: {\r
3214 C: {\r
3215 1: [103]\r
3216 }\r
3217 }\r
3218 }\r
3219 });\r
3220 expect(groups.getCount()).toBe(2);\r
3221\r
3222 var group3 = getAndComplete('Group', 103);\r
3223\r
3224 expect(groups.getCount()).toBe(3);\r
3225 expect(groups.getAt(2)).toBe(group3);\r
3226 });\r
3227 });\r
3228 });\r
3229 });\r
3230 });\r
3231\r
3232 describe("drop", function() {\r
3233 describe("with the store not created", function() {\r
3234 it("should exclude the record when it is loaded", function() {\r
3235 var user = getAndComplete('User', 1);\r
3236\r
3237 session.update({\r
3238 User: {\r
3239 groups: {\r
3240 D: {\r
3241 1: [101]\r
3242 }\r
3243 }\r
3244 }\r
3245 });\r
3246\r
3247 var groups = user.groups();\r
3248 groups.load();\r
3249 completeRequest([{id: 101}, {id: 102}]);\r
3250 expect(groups.getCount()).toBe(1);\r
3251 expect(groups.getAt(0)).toBe(session.peekRecord('Group', 102));\r
3252 });\r
3253 });\r
3254\r
3255 describe("with the store created", function() {\r
3256 describe("store not loaded", function() {\r
3257 it("should exclude the record when it is loaded", function() {\r
3258 var user = getAndComplete('User', 1),\r
3259 groups = user.groups();\r
3260\r
3261 session.update({\r
3262 User: {\r
3263 groups: {\r
3264 D: {\r
3265 1: [101]\r
3266 }\r
3267 }\r
3268 }\r
3269 });\r
3270\r
3271 groups.load();\r
3272 completeRequest([{id: 101}, {id: 102}]);\r
3273 expect(groups.getCount()).toBe(1);\r
3274 expect(groups.getAt(0)).toBe(session.peekRecord('Group', 102));\r
3275 });\r
3276 });\r
3277\r
3278 describe("store already loaded", function() {\r
3279 it("should exclude the record from the store", function() {\r
3280 var user = getAndComplete('User', 1),\r
3281 groups = user.groups();\r
3282\r
3283 groups.load();\r
3284 completeRequest([{id: 101}, {id: 102}]);\r
3285\r
3286 session.update({\r
3287 User: {\r
3288 groups: {\r
3289 D: {\r
3290 1: [101]\r
3291 }\r
3292 }\r
3293 }\r
3294 });\r
3295 expect(groups.getCount()).toBe(1);\r
3296 expect(groups.getAt(0)).toBe(session.peekRecord('Group', 102));\r
3297 });\r
3298 });\r
3299 });\r
3300 });\r
3301 });\r
3302 });\r
3303 });\r
3304 });\r
3305\r
3306 describe("drop/erase records", function() {\r
3307 beforeEach(function() {\r
3308 Ext.data.Model.schema.setNamespace('spec');\r
3309 Ext.define('spec.User', {\r
3310 extend: 'Ext.data.Model',\r
3311 fields: ['name']\r
3312 });\r
3313 session = new Ext.data.Session();\r
3314 });\r
3315\r
3316 afterEach(function() {\r
3317 Ext.undefine('spec.User');\r
3318 Ext.data.Model.schema.clear(true);\r
3319 });\r
3320\r
3321 it("should evict a phantom when it is dropped", function() {\r
3322 var user = new spec.User({}, session),\r
3323 id = user.id;\r
3324\r
3325 user.drop();\r
3326 expect(session.peekRecord('User', id)).toBeNull();\r
3327 });\r
3328\r
3329 it("should not evict a non-phantom when it is dropped", function() {\r
3330 var user = new spec.User({id: 1}, session);\r
3331 user.drop();\r
3332 expect(session.peekRecord('User', 1)).toBe(user);\r
3333 });\r
3334\r
3335 it("should evict a non-phantom when it is erased", function() {\r
3336 var user = new spec.User({id: 1}, session);\r
3337 user.erase();\r
3338 expect(session.peekRecord('User', 1)).toBe(user);\r
3339 completeRequest({});\r
3340 expect(session.peekRecord('User', 1)).toBeNull();\r
3341 });\r
3342 });\r
3343\r
3344 describe("commit", function() {\r
3345 var User;\r
3346\r
3347 beforeEach(function() {\r
3348 Ext.data.Model.schema.setNamespace('spec');\r
3349 User =Ext.define('spec.User', {\r
3350 extend: 'Ext.data.Model',\r
3351 fields: ['name']\r
3352 });\r
3353 session = new Ext.data.Session();\r
3354 });\r
3355\r
3356 afterEach(function() {\r
3357 User = null;\r
3358 Ext.undefine('spec.User');\r
3359 Ext.data.Model.schema.clear(true);\r
3360 });\r
3361\r
3362 it("should call commit on created records", function() {\r
3363 var user = new spec.User({}, session);\r
3364 expect(session.getChanges()).toEqual({\r
3365 User: {\r
3366 C: [{\r
3367 id: user.getId()\r
3368 }]\r
3369 }\r
3370 });\r
3371 session.commit();\r
3372 expect(session.getChanges()).toBeNull();\r
3373 });\r
3374\r
3375 it("should call commit on updated records", function() {\r
3376 var user = new spec.User({id: 1, name: 'Foo'}, session);\r
3377 user.set('name', 'Bar');\r
3378 expect(session.getChanges()).toEqual({\r
3379 User: {\r
3380 U: [{\r
3381 id: 1,\r
3382 name: 'Bar'\r
3383 }]\r
3384 }\r
3385 });\r
3386 session.commit();\r
3387 expect(session.getChanges()).toBeNull();\r
3388 });\r
3389\r
3390 it("should call commit on dropped records", function() {\r
3391 var user = new spec.User({id: 1}, session);\r
3392 user.drop();\r
3393 expect(session.getChanges()).toEqual({\r
3394 User: {\r
3395 D: [1]\r
3396 }\r
3397 });\r
3398 session.commit();\r
3399 expect(session.getChanges()).toBeNull();\r
3400 });\r
3401\r
3402 describe("associations", function() {\r
3403 describe("many to many", function() {\r
3404 var Group;\r
3405\r
3406 beforeEach(function() {\r
3407 Group = Ext.define('spec.Group', {\r
3408 extend: 'Ext.data.Model',\r
3409 fields: ['name'],\r
3410 manyToMany: 'User'\r
3411 });\r
3412 });\r
3413\r
3414 afterEach(function() {\r
3415 Group = null;\r
3416 Ext.undefine('spec.Group');\r
3417 });\r
3418\r
3419 it("should commit adds", function() {\r
3420 var user = getAndComplete(User, 1),\r
3421 group = getAndComplete(Group, 100);\r
3422\r
3423 user.groups().add(group);\r
3424 expect(session.getChanges()).toEqual({\r
3425 Group: {\r
3426 users: {\r
3427 C: {\r
3428 100: [1]\r
3429 }\r
3430 }\r
3431 }\r
3432 });\r
3433 session.commit();\r
3434 expect(session.getChanges()).toBeNull();\r
3435 });\r
3436\r
3437 it("should commit deletes", function() {\r
3438 var user = getAndComplete(User, 1),\r
3439 groups = user.groups();\r
3440\r
3441 groups.load();\r
3442 completeRequest([{\r
3443 id: 101\r
3444 }, {\r
3445 id: 102\r
3446 }, {\r
3447 id: 103\r
3448 }]);\r
3449 expect(session.getChanges()).toBeNull();\r
3450 groups.removeAt(0);\r
3451 expect(session.getChanges()).toEqual({\r
3452 Group: {\r
3453 users: {\r
3454 D: {\r
3455 101: [1]\r
3456 }\r
3457 }\r
3458 }\r
3459 });\r
3460 session.commit();\r
3461 expect(session.getChanges()).toBeNull();\r
3462 });\r
3463 });\r
3464 });\r
3465 });\r
3466\r
3467 describe("spawn", function() {\r
3468 var parent;\r
3469\r
3470 beforeEach(function() {\r
3471 Ext.data.Model.schema.setNamespace('spec');\r
3472 Ext.define('spec.User', {\r
3473 extend: 'Ext.data.Model',\r
3474 fields: ['name']\r
3475 });\r
3476 });\r
3477\r
3478 afterEach(function() {\r
3479 Ext.destroy(parent);\r
3480 parent = null;\r
3481 Ext.undefine('spec.User');\r
3482 Ext.data.Model.schema.clear(true);\r
3483 });\r
3484\r
3485 it("should set the schema from the parent", function() {\r
3486 var schema = new Ext.data.schema.Schema();\r
3487\r
3488 parent = new Ext.data.Session({\r
3489 schema: schema\r
3490 });\r
3491\r
3492 session = parent.spawn();\r
3493 expect(session.getSchema()).toBe(schema);\r
3494 });\r
3495\r
3496 it("should set the parent reference", function() {\r
3497 parent = new Ext.data.Session();\r
3498 session = parent.spawn();\r
3499 expect(session.getParent()).toBe(parent);\r
3500 });\r
3501\r
3502 describe("stores", function() {\r
3503 it("should use the data from parent records if they exist", function() {\r
3504 parent = new Ext.data.Session();\r
3505\r
3506 var user = getAndComplete('User', 1, parent);\r
3507 // Local change\r
3508 user.set('name', 'Foo');\r
3509\r
3510 session = parent.spawn();\r
3511 var store = new Ext.data.Store({\r
3512 asynchronousLoad: false,\r
3513 model: spec.User,\r
3514 session: session,\r
3515 proxy: {\r
3516 type: 'ajax',\r
3517 url: 'fakeUrl'\r
3518 }\r
3519 });\r
3520 store.load();\r
3521 completeRequest([{id: 1, name: 'Bar'}]);\r
3522 expect(session.peekRecord('User', 1).get('name')).toBe('Foo');\r
3523\r
3524 });\r
3525 });\r
3526\r
3527 describe("id generation", function() {\r
3528 it("should generate ids in sequence from parent to child", function() {\r
3529 parent = new Ext.data.Session();\r
3530 expect(parent.createRecord('User', {}).id).toBe('User-1');\r
3531 expect(parent.createRecord('User', {}).id).toBe('User-2');\r
3532\r
3533 session = parent.spawn();\r
3534\r
3535 expect(session.createRecord('User', {}).id).toBe('User-3');\r
3536 expect(session.createRecord('User', {}).id).toBe('User-4');\r
3537\r
3538 session.save();\r
3539\r
3540 expect(parent.createRecord('User', {}).id).toBe('User-5');\r
3541 expect(parent.createRecord('User', {}).id).toBe('User-6');\r
3542 });\r
3543\r
3544 it("should generate in sequence when sharing multiple children", function() {\r
3545 parent = new Ext.data.Session();\r
3546\r
3547 var child1 = parent.spawn(),\r
3548 child2 = parent.spawn(),\r
3549 child3 = parent.spawn();\r
3550\r
3551 expect(child1.createRecord('User', {}).id).toBe('User-1');\r
3552 expect(child2.createRecord('User', {}).id).toBe('User-2');\r
3553 expect(child3.createRecord('User', {}).id).toBe('User-3');\r
3554\r
3555 expect(child1.createRecord('User', {}).id).toBe('User-4');\r
3556 expect(child2.createRecord('User', {}).id).toBe('User-5');\r
3557 expect(child3.createRecord('User', {}).id).toBe('User-6');\r
3558\r
3559 expect(child1.createRecord('User', {}).id).toBe('User-7');\r
3560\r
3561 expect(child1.createRecord('User', {}).id).toBe('User-8');\r
3562 expect(child2.createRecord('User', {}).id).toBe('User-9');\r
3563 expect(child3.createRecord('User', {}).id).toBe('User-10');\r
3564\r
3565 Ext.destroy(child1, child2, child3);\r
3566 });\r
3567 });\r
3568\r
3569 describe("associations", function() {\r
3570 beforeEach(function() {\r
3571 Ext.define('spec.Post', {\r
3572 extend: 'Ext.data.Model',\r
3573 fields: [{\r
3574 name: 'userId',\r
3575 reference: 'User'\r
3576 }]\r
3577 });\r
3578 });\r
3579\r
3580 afterEach(function() {\r
3581 Ext.undefine('spec.Post');\r
3582 });\r
3583\r
3584 describe("record copying", function() {\r
3585 it("should copy any phantom records used in associations, when needed", function() {\r
3586 parent = new Ext.data.Session();\r
3587 getAndComplete('User', 1, parent);\r
3588 var post = parent.createRecord('Post', {\r
3589 userId: 1\r
3590 });\r
3591\r
3592 session = parent.spawn();\r
3593\r
3594 var posts = session.getRecord('User', 1).posts();\r
3595 expect(posts.getAt(0).id).toBe(post.id);\r
3596 expect(posts.getAt(0)).not.toBe(post);\r
3597 });\r
3598\r
3599 it("should copy any records added to the association, when needed", function() {\r
3600 parent = new Ext.data.Session();\r
3601 getAndComplete('User', 1, parent);\r
3602 var post = getAndComplete('Post', 101, parent, {\r
3603 userId: 1\r
3604 });\r
3605\r
3606 session = parent.spawn();\r
3607\r
3608 var posts = session.getRecord('User', 1).posts();\r
3609 expect(posts.getAt(0).id).toBe(101);\r
3610 expect(posts.getAt(0)).not.toBe(post);\r
3611 });\r
3612 });\r
3613\r
3614 describe("stores", function() {\r
3615 it("should mark loaded stores as complete", function() {\r
3616 parent = new Ext.data.Session();\r
3617 getAndComplete('User', 1, parent, {\r
3618 id: 1,\r
3619 posts: [{\r
3620 id: 101,\r
3621 userId: 1\r
3622 }, {\r
3623 id: 102,\r
3624 userId: 1\r
3625 }]\r
3626 });\r
3627\r
3628 session = parent.spawn();\r
3629\r
3630 var parentPosts = parent.getRecord('User', 1).posts(),\r
3631 posts = session.getRecord('User', 1).posts();\r
3632\r
3633 expect(posts.complete).toBe(true);\r
3634 expect(posts.getAt(0)).not.toBe(parentPosts.getAt(0));\r
3635 expect(posts.getAt(0).getId()).toBe(parentPosts.getAt(0).getId());\r
3636 expect(posts.getAt(1)).not.toBe(parentPosts.getAt(1));\r
3637 expect(posts.getAt(1).getId()).toBe(parentPosts.getAt(1).getId());\r
3638 });\r
3639\r
3640 it("should not mark stores as complete if not loaded", function() {\r
3641 parent = new Ext.data.Session();\r
3642 getAndComplete('User', 1, parent);\r
3643 getAndComplete('Post', 101, parent, {\r
3644 userId: 1\r
3645 })\r
3646\r
3647 session = parent.spawn();\r
3648\r
3649 var parentPosts = parent.getRecord('User', 1).posts(),\r
3650 posts = session.getRecord('User', 1).posts();\r
3651\r
3652 expect(posts.complete).toBe(false);\r
3653 expect(posts.getAt(0)).not.toBe(parentPosts.getAt(0));\r
3654 expect(posts.getAt(0).getId()).toBe(parentPosts.getAt(0).getId());\r
3655 });\r
3656 });\r
3657 });\r
3658 });\r
3659\r
3660 describe("updating from child to parent sessions", function() {\r
3661 var child, rec;\r
3662 beforeEach(function() {\r
3663 Ext.data.Model.schema.setNamespace('spec');\r
3664 Ext.define('spec.User', {\r
3665 extend: 'Ext.data.Model',\r
3666 fields: ['id', 'name', 'age', {\r
3667 name: 'serializeField',\r
3668 serialize: function(v) {\r
3669 return v.toString();\r
3670 }\r
3671 }]\r
3672 });\r
3673\r
3674 session = new Ext.data.Session();\r
3675 });\r
3676\r
3677 afterEach(function() {\r
3678 Ext.undefine('spec.User');\r
3679 Ext.data.Model.schema.clear(true);\r
3680 Ext.destroy(child);\r
3681 child = null;\r
3682 });\r
3683\r
3684 it("should handle when there are no changes", function() {\r
3685 child = session.spawn();\r
3686 expect(child.getChangesForParent()).toBeNull();\r
3687 child.save();\r
3688 expect(session.getChanges()).toBeNull();\r
3689 });\r
3690\r
3691 it("should not attempt to serialize values when pushing up to a parent", function() {\r
3692 child = session.spawn();\r
3693 rec = child.createRecord('User', {\r
3694 serializeField: 1000\r
3695 });\r
3696 var spy = spyOn(rec.getField('serializeField'), 'serialize');\r
3697 child.save();\r
3698 expect(spy).not.toHaveBeenCalled();\r
3699 expect(session.getRecord('User', rec.id).get('serializeField')).toBe(1000);\r
3700 });\r
3701\r
3702 describe("create", function() {\r
3703 it("should push up creates to the parent", function() {\r
3704 child = session.spawn();\r
3705 rec = child.createRecord('User', {\r
3706 name: 'Foo'\r
3707 });\r
3708 child.save();\r
3709 expect(session.getChanges()).toEqual({\r
3710 User: {\r
3711 C: [{\r
3712 id: rec.getId(),\r
3713 name: 'Foo'\r
3714 }]\r
3715 }\r
3716 });\r
3717 });\r
3718 });\r
3719\r
3720 describe("update", function() {\r
3721 it("should reflect update changes in the parent", function() {\r
3722 getAndComplete('User', 1);\r
3723 child = session.spawn();\r
3724 child.getRecord('User', 1).set('name', 'Foo');\r
3725 child.save();\r
3726 expect(session.getChanges()).toEqual({\r
3727 User: {\r
3728 U: [{\r
3729 id: 1,\r
3730 name: 'Foo'\r
3731 }]\r
3732 }\r
3733 });\r
3734 });\r
3735\r
3736 it("should use a record that is a phantom in the parent as an update from the child", function() {\r
3737 var rec = session.createRecord('User', {\r
3738 name: 'Foo'\r
3739 }), id = rec.getId();\r
3740\r
3741 child = session.spawn();\r
3742 child.getRecord('User', id).set('name', 'Bar');\r
3743 child.save();\r
3744 expect(session.getChanges()).toEqual({\r
3745 User: {\r
3746 C: [{\r
3747 id: id,\r
3748 name: 'Bar'\r
3749 }]\r
3750 }\r
3751 });\r
3752 });\r
3753 });\r
3754\r
3755 describe("drop", function() {\r
3756 it("should propagate a drop to the parent", function() {\r
3757 getAndComplete('User', 1);\r
3758 child = session.spawn();\r
3759 child.getRecord('User', 1).drop();\r
3760 child.save();\r
3761 expect(session.getChanges()).toEqual({\r
3762 User: {\r
3763 D: [1]\r
3764 }\r
3765 });\r
3766 });\r
3767\r
3768 it("should propagate a drop of a parent phantom, meaning we have no changes", function() {\r
3769 var rec = session.createRecord('User'),\r
3770 id = rec.getId();\r
3771\r
3772 child = session.spawn();\r
3773 child.getRecord('User', id).drop();\r
3774 child.save();\r
3775 expect(session.getChanges()).toBeNull();\r
3776 });\r
3777 });\r
3778\r
3779 describe("associations", function() {\r
3780 describe("pending drops", function() {\r
3781 beforeEach(function() {\r
3782 Ext.define('spec.Order', {\r
3783 extend: 'Ext.data.Model',\r
3784 fields: ['id', 'date', {\r
3785 name: 'addressId',\r
3786 unique: true,\r
3787 reference: {\r
3788 child: 'Address'\r
3789 }\r
3790 }]\r
3791 });\r
3792\r
3793 Ext.define('spec.Address', {\r
3794 extend: 'Ext.data.Model',\r
3795 fields: ['id', 'city']\r
3796 });\r
3797\r
3798 Ext.define('spec.OrderItem', {\r
3799 extend: 'Ext.data.Model',\r
3800 fields: ['id', 'price', 'qty', {\r
3801 name: 'orderId',\r
3802 reference: {\r
3803 parent: 'Order'\r
3804 }\r
3805 }]\r
3806 });\r
3807 });\r
3808\r
3809 afterEach(function() {\r
3810 Ext.undefine('spec.Order');\r
3811 Ext.undefine('spec.Address');\r
3812 Ext.undefine('spec.OrderItem');\r
3813 });\r
3814\r
3815 it("should resolve any pending drops", function() {\r
3816 var child = session.spawn();\r
3817\r
3818 var order = getAndComplete('Order', 1, child, {\r
3819 addressId: 101\r
3820 });\r
3821\r
3822 var address = order.getAddress();\r
3823 getAndComplete('Address', 101, child);\r
3824\r
3825 var orderItems = order.orderItems();\r
3826\r
3827 orderItems.load();\r
3828 completeRequest([{\r
3829 id: 201,\r
3830 orderId: 1\r
3831 }, {\r
3832 id: 202,\r
3833 orderId: 1\r
3834 }]);\r
3835 \r
3836 var orderItem = orderItems.getAt(0);\r
3837\r
3838 order.setAddress(null);\r
3839 orderItems.removeAt(0);\r
3840\r
3841 expect(address.dropped).toBe(false);\r
3842 expect(orderItem.dropped).toBe(false);\r
3843 child.save();\r
3844 expect(session.getChanges()).toEqual({\r
3845 Address: {\r
3846 D: [101]\r
3847 },\r
3848 Order: {\r
3849 U: [{\r
3850 id: 1,\r
3851 addressId: null\r
3852 }]\r
3853 },\r
3854 OrderItem: {\r
3855 D: [201]\r
3856 }\r
3857 });\r
3858 });\r
3859 });\r
3860\r
3861 describe("many to one", function() {\r
3862 beforeEach(function() {\r
3863 Ext.define('spec.Post', {\r
3864 extend: 'Ext.data.Model',\r
3865 fields: ['content', {\r
3866 name: 'userId',\r
3867 reference: 'User'\r
3868 }]\r
3869 });\r
3870 });\r
3871\r
3872 afterEach(function() {\r
3873 Ext.undefine('spec.Post');\r
3874 });\r
3875\r
3876 describe("store loaded in the parent", function() {\r
3877 var user, posts;\r
3878\r
3879 beforeEach(function() {\r
3880 user = getAndComplete('User', 1);\r
3881 posts = user.posts();\r
3882\r
3883 posts.load();\r
3884 completeRequest([{id: 101, userId: 1}, {id: 102, userId: 1}]);\r
3885 child = session.spawn();\r
3886\r
3887 posts = child.getRecord('User', 1).posts();\r
3888 });\r
3889\r
3890\r
3891 afterEach(function() {\r
3892 user = posts = null;\r
3893 });\r
3894\r
3895 it("should push up a store removal as an update to the FK", function() {\r
3896 posts.removeAt(0);\r
3897 child.save();\r
3898\r
3899 expect(session.getChanges()).toEqual({\r
3900 Post: {\r
3901 U: [{\r
3902 userId: null,\r
3903 id: 101\r
3904 }]\r
3905 }\r
3906 });\r
3907 expect(session.peekRecord('Post', 101).dirty).toBe(true);\r
3908 });\r
3909\r
3910 it("should push up a drop", function() {\r
3911 posts.getAt(0).drop();\r
3912 child.save();\r
3913\r
3914 expect(session.getChanges()).toEqual({\r
3915 Post: {\r
3916 D: [101]\r
3917 }\r
3918 });\r
3919 expect(session.peekRecord('Post', 101).dropped).toBe(true);\r
3920 });\r
3921\r
3922 it("should push a new phantom record as a creation", function() {\r
3923 var id = posts.add({})[0].getId();\r
3924 child.save();\r
3925\r
3926 expect(session.getChanges()).toEqual({\r
3927 Post: {\r
3928 C: [{\r
3929 id: id,\r
3930 userId: 1\r
3931 }]\r
3932 }\r
3933 });\r
3934 expect(session.peekRecord('Post', id).phantom).toBe(true);\r
3935 });\r
3936\r
3937 it("should push an added record as an update to the FK", function() {\r
3938 var post = getAndComplete('Post', 105, child);\r
3939 posts.add(post);\r
3940 child.save();\r
3941\r
3942 expect(session.getChanges()).toEqual({\r
3943 Post: {\r
3944 U: [{\r
3945 id: 105,\r
3946 userId: 1\r
3947 }]\r
3948 }\r
3949 });\r
3950 expect(session.peekRecord('Post', 105).dirty).toBe(true);\r
3951 });\r
3952 });\r
3953\r
3954 describe("store not loaded in the parent", function() {\r
3955 var user, posts, childUser;\r
3956\r
3957 beforeEach(function() {\r
3958 user = getAndComplete('User', 1);\r
3959 child = session.spawn();\r
3960\r
3961 childUser = child.getRecord('User', 1);\r
3962 posts = childUser.posts();\r
3963 });\r
3964\r
3965 afterEach(function() {\r
3966 user = posts = childUser = null;\r
3967 });\r
3968\r
3969 it("should read & update for a foreign key change", function() {\r
3970 posts.load();\r
3971 completeRequest([{id: 101, userId: 1}, {id: 102, userId: 1}, {id: 103, userId: 1}]);\r
3972 posts.removeAt(1);\r
3973 child.save();\r
3974\r
3975 expect(session.getChanges()).toEqual({\r
3976 Post: {\r
3977 U: [{\r
3978 id: 102,\r
3979 userId: null\r
3980 }]\r
3981 }\r
3982 });\r
3983 expect(session.peekRecord('Post', 102).dirty).toBe(true);\r
3984 });\r
3985\r
3986 it("should read and update for a drop", function() {\r
3987 posts.load();\r
3988 completeRequest([{id: 101, userId: 1}, {id: 102, userId: 1}, {id: 103, userId: 1}]);\r
3989 posts.getAt(1).drop();\r
3990 child.save();\r
3991\r
3992 expect(session.getChanges()).toEqual({\r
3993 Post: {\r
3994 D: [102]\r
3995 }\r
3996 });\r
3997 expect(session.peekRecord('Post', 102).dropped).toBe(true);\r
3998 });\r
3999\r
4000 it("should push up phantom records as creates", function() {\r
4001 var id = posts.add({\r
4002 content: 'Foo'\r
4003 })[0].getId();\r
4004 child.save();\r
4005\r
4006 expect(session.getChanges()).toEqual({\r
4007 Post: {\r
4008 C: [{\r
4009 id: id,\r
4010 userId: 1,\r
4011 content: 'Foo'\r
4012 }]\r
4013 }\r
4014 });\r
4015 expect(session.peekRecord('Post', id).phantom).toBe(true);\r
4016 });\r
4017\r
4018 it("should have no changes if the store is loaded", function() {\r
4019 posts.load();\r
4020 completeRequest([{id: 101, userId: 1}, {id: 102, userId: 1}, {id: 103, userId: 1}]);\r
4021 child.save();\r
4022 expect(session.getChanges()).toBeNull();\r
4023 });\r
4024 });\r
4025 });\r
4026\r
4027 describe("many to many", function() {\r
4028 var group, users, childGroup, childUsers;\r
4029\r
4030 beforeEach(function() {\r
4031 Ext.define('spec.Group', {\r
4032 extend: 'Ext.data.Model',\r
4033 fields: ['name'],\r
4034 manyToMany: 'User'\r
4035 });\r
4036 });\r
4037\r
4038 afterEach(function() {\r
4039 Ext.undefine('spec.Group');\r
4040 group = users = childGroup = childUsers;\r
4041 });\r
4042\r
4043 describe("store loaded in the parent", function() {\r
4044 beforeEach(function() {\r
4045 group = getAndComplete('Group', 1);\r
4046 users = group.users();\r
4047 users.load();\r
4048 completeRequest([{id: 101}, {id: 102}, {id: 103}]);\r
4049 child = session.spawn();\r
4050 childGroup = child.getRecord('Group', 1);\r
4051 childUsers = childGroup.users();\r
4052 });\r
4053\r
4054 it("should copy the store from the parent", function() {\r
4055 expect(childUsers.getCount()).toBe(3);\r
4056 });\r
4057\r
4058 it("should add an existing record to the parent collection", function() {\r
4059 // Gets it in the parent, we get a copy in the child below\r
4060 var user = getAndComplete('User', 104);\r
4061 childUsers.add(child.getRecord('User', 104));\r
4062 child.save();\r
4063 expect(session.getChanges()).toEqual({\r
4064 Group: {\r
4065 users: {\r
4066 C: {\r
4067 1: [104]\r
4068 }\r
4069 }\r
4070 }\r
4071 });\r
4072 expect(users.getCount()).toBe(4);\r
4073 expect(users.getAt(3)).toBe(user);\r
4074 });\r
4075\r
4076 it("should have a pending add in the parent, not read the record up", function() {\r
4077 var user = getAndComplete('User', 104, child);\r
4078 childUsers.add(user);\r
4079 child.save();\r
4080 expect(session.getChanges()).toEqual({\r
4081 Group: {\r
4082 users: {\r
4083 C: {\r
4084 1: [104]\r
4085 }\r
4086 }\r
4087 }\r
4088 });\r
4089 expect(session.peekRecord('User', 104)).toBeNull();\r
4090 // Doesn't exist yet\r
4091 expect(users.getCount()).toBe(3);\r
4092 // Get it in the parent\r
4093 user = getAndComplete('User', 104);\r
4094 expect(users.getCount()).toBe(4);\r
4095 expect(users.indexOf(user)).toBe(3);\r
4096 });\r
4097\r
4098 it("should push up a removal", function() {\r
4099 childUsers.removeAt(0);\r
4100 child.save();\r
4101 expect(session.getChanges()).toEqual({\r
4102 Group: {\r
4103 users: {\r
4104 D: {\r
4105 1: [101]\r
4106 }\r
4107 }\r
4108 }\r
4109 });\r
4110 expect(users.getCount()).toBe(2);\r
4111 });\r
4112 });\r
4113\r
4114 describe("store not loaded, created in the parent", function() {\r
4115 var user1, user2;\r
4116 beforeEach(function() {\r
4117 group = getAndComplete('Group', 1);\r
4118 users = group.users();\r
4119 user1 = getAndComplete('User', 101);\r
4120 user2 = getAndComplete('User', 102);\r
4121 users.add(user1, user2);\r
4122\r
4123 child = session.spawn();\r
4124 childGroup = child.getRecord('Group', 1);\r
4125 childUsers = childGroup.users();\r
4126 });\r
4127\r
4128 afterEach(function() {\r
4129 user1 = user2 = null;\r
4130 });\r
4131\r
4132 it("should copy the store from the parent", function() {\r
4133 expect(childUsers.getCount()).toBe(2);\r
4134 });\r
4135\r
4136 it("should add an existing record to the parent collection", function() {\r
4137 // Gets it in the parent, we get a copy in the child below\r
4138 var user = getAndComplete('User', 104);\r
4139 childUsers.add(child.getRecord('User', 104));\r
4140 child.save();\r
4141 expect(session.getChanges()).toEqual({\r
4142 Group: {\r
4143 users: {\r
4144 C: {\r
4145 1: [101, 102, 104]\r
4146 }\r
4147 }\r
4148 }\r
4149 });\r
4150 expect(users.getCount()).toBe(3);\r
4151 expect(users.getAt(2)).toBe(user);\r
4152 });\r
4153\r
4154 it("should have a pending add in the parent, not read the record up", function() {\r
4155 var user = getAndComplete('User', 104, child);\r
4156 childUsers.add(user);\r
4157 child.save();\r
4158 expect(session.getChanges()).toEqual({\r
4159 Group: {\r
4160 users: {\r
4161 C: {\r
4162 1: [101, 102, 104]\r
4163 }\r
4164 }\r
4165 }\r
4166 });\r
4167 // Doesn't exist yet\r
4168 expect(users.getCount()).toBe(2);\r
4169 // Get it in the parent\r
4170 user = getAndComplete('User', 104);\r
4171 expect(users.getCount()).toBe(3);\r
4172 expect(users.indexOf(user)).toBe(2);\r
4173 });\r
4174\r
4175 it("should push up a removal", function() {\r
4176 childUsers.removeAt(0);\r
4177 child.save();\r
4178 expect(session.getChanges()).toEqual({\r
4179 Group: {\r
4180 users: {\r
4181 C: {\r
4182 1: [102]\r
4183 }\r
4184 }\r
4185 }\r
4186 });\r
4187 expect(users.getCount()).toBe(1);\r
4188 });\r
4189 });\r
4190\r
4191 describe("store loaded in the child", function() {\r
4192 beforeEach(function() {\r
4193 group = getAndComplete('Group', 1);\r
4194 child = session.spawn();\r
4195 childGroup = child.getRecord('Group', 1);\r
4196 childUsers = childGroup.users();\r
4197 childUsers.load();\r
4198 completeRequest([{id: 101}, {id: 102}, {id: 103}]);\r
4199 });\r
4200\r
4201 it("should add an existing record to the parent collection", function() {\r
4202 // Gets it in the parent, we get a copy in the child below\r
4203 var user = getAndComplete('User', 104);\r
4204 childUsers.add(child.getRecord('User', 104));\r
4205 child.save();\r
4206 expect(session.getChanges()).toEqual({\r
4207 Group: {\r
4208 users: {\r
4209 C: {\r
4210 1: [104]\r
4211 }\r
4212 }\r
4213 }\r
4214 });\r
4215 users = group.users();\r
4216 expect(users.getCount()).toBe(1);\r
4217 expect(users.getAt(0)).toBe(user);\r
4218 });\r
4219\r
4220 it("should have a pending add in the parent, not read the record up", function() {\r
4221 var user = getAndComplete('User', 104, child);\r
4222 childUsers.add(user);\r
4223 child.save();\r
4224 expect(session.getChanges()).toEqual({\r
4225 Group: {\r
4226 users: {\r
4227 C: {\r
4228 1: [104]\r
4229 }\r
4230 }\r
4231 }\r
4232 });\r
4233 // Doesn't exist yet\r
4234 users = group.users();\r
4235 expect(users.getCount()).toBe(0);\r
4236 // Get it in the parent\r
4237 user = getAndComplete('User', 104);\r
4238 expect(users.getCount()).toBe(1);\r
4239 expect(users.indexOf(user)).toBe(0);\r
4240 });\r
4241\r
4242 it("should have a pending removal", function() {\r
4243 childUsers.removeAt(0);\r
4244 child.save();\r
4245 expect(session.getChanges()).toEqual({\r
4246 Group: {\r
4247 users: {\r
4248 D: {\r
4249 1: [101]\r
4250 }\r
4251 }\r
4252 }\r
4253 });\r
4254 users = group.users();\r
4255 // We don't have any users in the session\r
4256 expect(users.getCount()).toBe(0);\r
4257 users.load();\r
4258 completeRequest([{id: 101}, {id: 102}, {id: 103}]);\r
4259 // Now that we have loaded, we exclude 101\r
4260 expect(users.getCount()).toBe(2);\r
4261 });\r
4262 });\r
4263\r
4264 describe("store not loaded, created in the child", function() {\r
4265 var user1, user2;\r
4266\r
4267 beforeEach(function() {\r
4268 group = getAndComplete('Group', 1);\r
4269 child = session.spawn();\r
4270 childGroup = child.getRecord('Group', 1);\r
4271 childUsers = childGroup.users();\r
4272 user1 = getAndComplete('User', 101, child);\r
4273 user2 = getAndComplete('User', 102, child);\r
4274 childUsers.add(user1, user2);\r
4275 });\r
4276\r
4277 afterEach(function() {\r
4278 user1 = user2 = null;\r
4279 });\r
4280\r
4281 it("should add an existing record to the parent collection", function() {\r
4282 // Gets it in the parent, we get a copy in the child below\r
4283 var user = getAndComplete('User', 104);\r
4284 childUsers.add(child.getRecord('User', 104));\r
4285 child.save();\r
4286 expect(session.getChanges()).toEqual({\r
4287 Group: {\r
4288 users: {\r
4289 C: {\r
4290 1: [101, 102, 104]\r
4291 }\r
4292 }\r
4293 }\r
4294 });\r
4295 // 101 & 102 don't exist in the parent, don't read them up\r
4296 users = group.users();\r
4297 expect(users.getCount()).toBe(1);\r
4298 expect(users.getAt(0)).toBe(user);\r
4299 });\r
4300\r
4301 it("should have a pending add in the parent, not read the record up", function() {\r
4302 child.save();\r
4303 expect(session.getChanges()).toEqual({\r
4304 Group: {\r
4305 users: {\r
4306 C: {\r
4307 1: [101, 102]\r
4308 }\r
4309 }\r
4310 }\r
4311 });\r
4312 // Doesn't exist yet\r
4313 users = group.users();\r
4314 expect(users.getCount()).toBe(0);\r
4315 // Get it in the parent\r
4316 user1 = getAndComplete('User', 101);\r
4317 user2 = getAndComplete('User', 102);\r
4318 expect(users.getCount()).toBe(2);\r
4319 expect(users.indexOf(user1)).toBe(0);\r
4320 expect(users.indexOf(user2)).toBe(1);\r
4321 });\r
4322 });\r
4323\r
4324 describe("empty parent", function() {\r
4325 it("should not push up the owning record if it was loaded in the child", function() {\r
4326 child = session.spawn();\r
4327 var group = getAndComplete('Group', 1, child),\r
4328 user = child.createRecord('User');\r
4329\r
4330 group.users().add(user);\r
4331 child.save();\r
4332 expect(session.peekRecord('Group', 1)).toBeNull();\r
4333 });\r
4334\r
4335 it("should allow a create", function() {\r
4336 child = session.spawn();\r
4337 var group = getAndComplete('Group', 1, child),\r
4338 user = getAndComplete('User', 101, child);\r
4339\r
4340 group.users().add(user);\r
4341 child.save();\r
4342 expect(session.getChanges()).toEqual({\r
4343 Group: {\r
4344 users: {\r
4345 C: {\r
4346 1: [101]\r
4347 }\r
4348 }\r
4349 }\r
4350 });\r
4351 });\r
4352\r
4353 it("should establish a relationship when both parties load", function() {\r
4354 child = session.spawn();\r
4355 var group = getAndComplete('Group', 1, child),\r
4356 user = getAndComplete('User', 101, child);\r
4357\r
4358 group.users().add(user);\r
4359 child.save();\r
4360\r
4361 // Load into the parent now\r
4362 group = getAndComplete('Group', 1);\r
4363 user = getAndComplete('User', 101);\r
4364 expect(user.groups().indexOf(group)).toBe(0);\r
4365\r
4366 });\r
4367 });\r
4368\r
4369 });\r
4370 });\r
4371 });\r
4372\r
4373 describe('Provisional identifiers', function () {\r
4374 function makeSuite (title, schema, expectations) {\r
4375 describe('Schema with ' + title, function () {\r
4376 var Base, Derived;\r
4377\r
4378 beforeEach(function() {\r
4379 schema.setNamespace('spec');\r
4380 Base = Ext.define('spec.Base', {\r
4381 extend: Ext.data.Model,\r
4382\r
4383 schema: schema,\r
4384\r
4385 fields: ['id', 'name', 'key']\r
4386 });\r
4387\r
4388 Derived = Ext.define('spec.Derived', {\r
4389 extend: Base\r
4390 });\r
4391\r
4392 session = new Ext.data.Session({\r
4393 schema: Base.schema\r
4394 });\r
4395 });\r
4396\r
4397 afterEach(function() {\r
4398 Ext.undefine('spec.Base');\r
4399 Ext.undefine('spec.Derived');\r
4400 Base.schema.clear(true);\r
4401\r
4402 Base = Derived = null;\r
4403 });\r
4404\r
4405 describe("record creation", function() {\r
4406 it('should isolate id generation to the session', function () {\r
4407 var standaloneRecord = new Base();\r
4408\r
4409 var sessionRecord = session.createRecord('Base', {\r
4410 name: 'Don'\r
4411 });\r
4412\r
4413 expect(standaloneRecord).not.toBe(sessionRecord);\r
4414 expect(sessionRecord.id).toBe(standaloneRecord.id);\r
4415 });\r
4416\r
4417 it('should track all created records', function () {\r
4418 var a = session.createRecord('Base', {\r
4419 name: 'Don'\r
4420 });\r
4421 expect(a.id).toBe(expectations['B-1']);\r
4422\r
4423 var b = session.createRecord('Derived', {\r
4424 name: 'Evan'\r
4425 });\r
4426 expect(b.id).toBe(expectations['D-1']);\r
4427\r
4428 var changes = session.getChanges();\r
4429\r
4430 expect(changes).toEqual({\r
4431 Base: {\r
4432 C: [{\r
4433 id: a.id,\r
4434 name: 'Don'\r
4435 }]\r
4436 },\r
4437 Derived: {\r
4438 C: [{\r
4439 id: b.id,\r
4440 name: 'Evan'\r
4441 }]\r
4442 }\r
4443 });\r
4444 });\r
4445 }); // record creation\r
4446 });\r
4447 } // makeSuite\r
4448\r
4449 makeSuite('default identities', Ext.data.Model.schema, {\r
4450 'B-1': 'Base-1',\r
4451 'D-1': 'Derived-1'\r
4452 });\r
4453\r
4454 makeSuite('negative identities', new Ext.data.schema.Schema({\r
4455 defaultIdentifier: 'negative'\r
4456 }), {\r
4457 'B-1': -1,\r
4458 'D-1': -1\r
4459 });\r
4460\r
4461 makeSuite('sequential identities', new Ext.data.schema.Schema({\r
4462 defaultIdentifier: 'sequential'\r
4463 }), {\r
4464 'B-1': 1,\r
4465 'D-1': 1\r
4466 });\r
4467 });\r
4468\r
4469 describe("Random UUID's", function () {\r
4470 var Base, Derived;\r
4471 var schema;\r
4472\r
4473 beforeEach(function() {\r
4474 if (!schema) {\r
4475 schema = new Ext.data.schema.Schema({\r
4476 defaultIdentifier: 'uuid'\r
4477 });\r
4478 }\r
4479 schema.setNamespace('spec');\r
4480\r
4481 Base = Ext.define('spec.Base', {\r
4482 extend: Ext.data.Model,\r
4483\r
4484 schema: schema,\r
4485\r
4486 fields: ['id', 'name', 'key']\r
4487 });\r
4488\r
4489 session = new Ext.data.Session({\r
4490 schema: Base.schema\r
4491 });\r
4492 });\r
4493\r
4494 afterEach(function() {\r
4495\r
4496 Ext.undefine('spec.Base');\r
4497 Ext.undefine('spec.Derived');\r
4498 Base.schema.clear(true);\r
4499\r
4500 Base = Derived = null;\r
4501 });\r
4502\r
4503 describe("record creation", function() {\r
4504 it('should copy identifier reference into the session', function () {\r
4505 var standaloneRecord = new Base();\r
4506\r
4507 var sessionRecord = session.createRecord('Base', {\r
4508 name: 'Don'\r
4509 });\r
4510\r
4511 expect(standaloneRecord).not.toBe(sessionRecord);\r
4512 expect(sessionRecord.id).not.toBe(standaloneRecord.id); // uuid !\r
4513\r
4514 var defaultIdentifier = session.getSchema().getDefaultIdentifier();\r
4515 var identA = session.getIdentifier(Base);\r
4516\r
4517 expect(identA).toBe(Base.identifier); // not cloned\r
4518 expect(identA).toBe(defaultIdentifier);\r
4519 expect(identA).toBe(Ext.data.identifier.Uuid.Global);\r
4520 });\r
4521 }); // record creation\r
4522 }); // Random UUID's\r
4523\r
4524 describe("Sequential UUID's", function () {\r
4525 var Base, Derived;\r
4526 var schema;\r
4527\r
4528 beforeEach(function() {\r
4529 if (!schema) {\r
4530 schema = new Ext.data.schema.Schema({\r
4531 defaultIdentifier: {\r
4532 type: 'uuid',\r
4533 version: 1,\r
4534 timestamp: 0xDEFACED,\r
4535 salt: 0xBEEFF00D,\r
4536 clockSeq: 0xBAD\r
4537 }\r
4538 });\r
4539 }\r
4540 schema.setNamespace('spec');\r
4541\r
4542 Base = Ext.define('spec.Base', {\r
4543 extend: Ext.data.Model,\r
4544\r
4545 schema: schema,\r
4546\r
4547 fields: ['id', 'name', 'key']\r
4548 });\r
4549\r
4550 session = new Ext.data.Session({\r
4551 schema: Base.schema\r
4552 });\r
4553 });\r
4554\r
4555 afterEach(function() {\r
4556 Ext.undefine('spec.Base');\r
4557 Ext.undefine('spec.Derived');\r
4558 Base.schema.clear(true);\r
4559\r
4560 Base = Derived = null;\r
4561 });\r
4562\r
4563 describe("record creation", function() {\r
4564 it('should copy identifier reference into the session', function () {\r
4565 var standaloneRec = new Base();\r
4566\r
4567 var sessionRecord = session.createRecord('Base', {\r
4568 name: 'Don'\r
4569 });\r
4570\r
4571 expect(standaloneRec.id).toBe('0defaced-0000-1000-8bad-0100beeff00d');\r
4572 expect(sessionRecord.id).toBe('0defacee-0000-1000-8bad-0100beeff00d');\r
4573 // changes right here ^\r
4574\r
4575 var defaultIdentifier = session.getSchema().getDefaultIdentifier();\r
4576 var identA = session.getIdentifier(Base);\r
4577\r
4578 expect(identA).toBe(Base.identifier); // not cloned\r
4579 expect(identA).toBe(defaultIdentifier);\r
4580\r
4581 expect(identA).not.toBe(Ext.data.identifier.Uuid.Global);\r
4582 });\r
4583 }); // record creation\r
4584 }); // Sequential UUID's\r
4585\r
4586 describe('Many-to-many associations', function () {\r
4587 var User, Group;\r
4588\r
4589 beforeEach(function() {\r
4590 Ext.data.Model.schema.setNamespace('spec');\r
4591\r
4592 User = Ext.define('spec.User', {\r
4593 extend: Ext.data.Model,\r
4594\r
4595 fields: [ 'name', 'key' ],\r
4596\r
4597 manyToMany: '#Group'\r
4598 });\r
4599\r
4600 Group = Ext.define('spec.Group', {\r
4601 extend: Ext.data.Model,\r
4602\r
4603 fields: [ 'name', 'key' ]\r
4604\r
4605 // should not need to specify manyToMany here\r
4606 });\r
4607\r
4608 session = new Ext.data.Session({\r
4609 schema: User.schema\r
4610 });\r
4611 });\r
4612\r
4613 afterEach(function() {\r
4614 Ext.undefine('spec.User');\r
4615 Ext.undefine('spec.Group');\r
4616 User.schema.clear(true);\r
4617\r
4618 User = Group = null;\r
4619 });\r
4620\r
4621 describe("loading a many-to-many", function() {\r
4622 it('should load groups for a user', function () {\r
4623 var groups = session.getRecord('User', userRufus.id, false).groups();\r
4624 groups.load();\r
4625 completeRequest(rufusGroups);\r
4626\r
4627 expect(groups.isStore).toBe(true);\r
4628 expect(groups.getCount()).toBe(2);\r
4629 expect(groups.getById(adminGroup.id)).toBeTruthy();\r
4630 expect(groups.getById(peonGroup.id)).toBeTruthy();\r
4631\r
4632 // Some whitebox testing here. We peek into the sessions matrix pool and\r
4633 // verify that ids are on the proper "sides".\r
4634 var matrix = session.matrices.UserGroups;\r
4635\r
4636 expect(matrix.left.slices[10].members[42]).toEqual([10, 42, 0]);\r
4637 expect(matrix.left.slices[10].members[427]).toEqual([10, 427, 0]);\r
4638\r
4639 expect(matrix.right.slices[42].members[10]).toEqual([10, 42, 0]);\r
4640 expect(matrix.right.slices[427].members[10]).toEqual([10, 427, 0]);\r
4641 });\r
4642\r
4643 it('should load both sides of a matrix', function () {\r
4644 var rufusGroupsStore = session.getRecord('User', userRufus.id, false).groups(),\r
4645 adminUsersStore = session.getRecord('Group', adminGroup.id, false).users(),\r
4646 peonUsersStore = session.getRecord('Group', peonGroup.id, false).users();\r
4647\r
4648 rufusGroupsStore.load();\r
4649 peonUsersStore.load();\r
4650 adminUsersStore.load();\r
4651\r
4652 completeRequest(rufusGroups, 1);\r
4653 completeRequest(peonUsers, 2);\r
4654 completeRequest(adminUsers, 3);\r
4655\r
4656 expect(rufusGroupsStore.isStore).toBe(true);\r
4657 expect(rufusGroupsStore.getCount()).toBe(2);\r
4658 expect(rufusGroupsStore.getById(adminGroup.id)).toBeTruthy();\r
4659 expect(rufusGroupsStore.getById(peonGroup.id)).toBeTruthy();\r
4660\r
4661 var rufusRec1, rufusRec2;\r
4662\r
4663 expect(adminUsersStore.isStore).toBe(true);\r
4664 expect(adminUsersStore.getCount()).toBe(1);\r
4665 expect(rufusRec1 = adminUsersStore.getById(userRufus.id)).toBeTruthy();\r
4666\r
4667 expect(peonUsersStore.isStore).toBe(true);\r
4668 expect(peonUsersStore.getCount()).toBe(3);\r
4669 expect(peonUsersStore.getById(userBill.id)).toBeTruthy();\r
4670 expect(peonUsersStore.getById(userTed.id)).toBeTruthy();\r
4671 expect(rufusRec2 = peonUsersStore.getById(userRufus.id)).toBeTruthy();\r
4672\r
4673 expect(rufusRec1).toBe(rufusRec2);\r
4674 });\r
4675\r
4676 it('should allow editing on both sides of a matrix', function () {\r
4677 var billGroupsStore = session.getRecord('User', userBill.id, false).groups(),\r
4678 rufusGroupsStore = session.getRecord('User', userRufus.id, false).groups(),\r
4679 adminUsersStore = session.getRecord('Group', adminGroup.id, false).users(),\r
4680 peonUsersStore = session.getRecord('Group', peonGroup.id, false).users();\r
4681\r
4682 rufusGroupsStore.load();\r
4683 billGroupsStore.load();\r
4684 peonUsersStore.load();\r
4685 adminUsersStore.load();\r
4686\r
4687 completeRequest(rufusGroups, 1);\r
4688 completeRequest(billGroups, 2);\r
4689 completeRequest(peonUsers, 3);\r
4690 completeRequest(adminUsers, 4);\r
4691\r
4692 // Removing Rufus from the adminUsersStore should reflexively remove\r
4693 // the adminGroup from rufusGroupsStore.\r
4694 expect(rufusGroupsStore.getCount()).toBe(2);\r
4695 expect(rufusGroupsStore.getById(adminGroup.id)).toBeTruthy();\r
4696\r
4697 var rufusRec = adminUsersStore.getById(userRufus.id);\r
4698 adminUsersStore.remove(rufusRec);\r
4699\r
4700 expect(rufusGroupsStore.getCount()).toBe(1);\r
4701 expect(rufusGroupsStore.getById(adminGroup.id)).toBe(null);\r
4702\r
4703 // Adding Bill to the adminUsersStore should reflexively add adminGroup\r
4704 // to billGroupsStore\r
4705 expect(billGroupsStore.getCount()).toBe(1);\r
4706 expect(billGroupsStore.getById(adminGroup.id)).toBe(null);\r
4707\r
4708 var billRec = peonUsersStore.getById(userBill.id);\r
4709 adminUsersStore.add(billRec);\r
4710\r
4711 expect(billGroupsStore.getCount()).toBe(2);\r
4712 expect(billGroupsStore.getById(adminGroup.id)).toBeTruthy();\r
4713\r
4714 var changes = session.getChanges();\r
4715 expect(changes).toEqual({\r
4716 User: {\r
4717 groups: {\r
4718 C: {\r
4719 20: [42]\r
4720 },\r
4721 D: {\r
4722 10: [42]\r
4723 }\r
4724 }\r
4725 }\r
4726 });\r
4727 }); // should allow editing on both sides of a matrix\r
4728 }); // loading a many-to-many\r
4729 }); // Many-to-many associations\r
4730\r
4731 describe('transactions', function () {\r
4732 var Base, Parent, Child, GrandChild, Group, User;\r
4733 var parentData, childData, grandChildData;\r
4734\r
4735 beforeEach(function() {\r
4736 Ext.data.Model.schema.setNamespace('spec');\r
4737\r
4738 parentData = [ { id: 1, name: 'parent1', code: 'abc', foo: 42 },\r
4739 { id: 2, name: 'parent2', code: 'def', foo: 427 } ];\r
4740\r
4741 childData = [ { id: 10, name: 'child1', parentId: 1 },\r
4742 { id: 20, name: 'child2', parentId: 2 } ];\r
4743\r
4744 grandChildData = [ { id: 100, name: 'grand1', childId: 10 },\r
4745 { id: 200, name: 'grand2', childId: 20 } ];\r
4746\r
4747 Base = Ext.define('spec.Base', {\r
4748 extend: Ext.data.Model\r
4749 });\r
4750\r
4751 User = Ext.define('spec.User', {\r
4752 extend: Ext.data.Model,\r
4753\r
4754 fields: [ 'name', 'key' ],\r
4755\r
4756 manyToMany: '#Group'\r
4757 });\r
4758\r
4759 Group = Ext.define('spec.Group', {\r
4760 extend: Ext.data.Model,\r
4761\r
4762 fields: [ 'name', 'key' ]\r
4763\r
4764 // should not need to specify manyToMany here\r
4765 });\r
4766\r
4767 Parent = Ext.define('spec.Parent', {\r
4768 extend: Base,\r
4769\r
4770 identifier: {\r
4771 type: 'negative'\r
4772 },\r
4773 fields: [\r
4774 'name',\r
4775 'code',\r
4776 { name: 'foo', critical: true }\r
4777 ]\r
4778 });\r
4779\r
4780 Child = Ext.define('spec.Child', {\r
4781 extend: Base,\r
4782\r
4783 identifier: {\r
4784 type: 'negative',\r
4785 seed: -10\r
4786 },\r
4787 fields: [\r
4788 'name',\r
4789 { name: 'parentId', reference: 'Parent' }\r
4790 ]\r
4791 });\r
4792\r
4793 GrandChild = Ext.define('spec.GrandChild', {\r
4794 extend: Base,\r
4795\r
4796 identifier: {\r
4797 type: 'negative',\r
4798 seed: -100\r
4799 },\r
4800\r
4801 clientIdProperty: 'cid',\r
4802\r
4803 fields: [\r
4804 'name',\r
4805 { name: 'childId', reference: 'Child' }\r
4806 ]\r
4807 });\r
4808\r
4809 session = new Ext.data.Session({\r
4810 schema: Base.schema\r
4811 });\r
4812 });\r
4813\r
4814 afterEach(function() {\r
4815 Ext.undefine('spec.Base');\r
4816 Ext.undefine('spec.Parent');\r
4817 Ext.undefine('spec.Child');\r
4818 Ext.undefine('spec.GrandChild');\r
4819 Ext.undefine('spec.Group');\r
4820 Ext.undefine('spec.User');\r
4821\r
4822 Ext.data.Model.schema.clear(true);\r
4823\r
4824 session = null;\r
4825 Base = Parent = Child = GrandChild = Group = User = null;\r
4826 });\r
4827\r
4828 describe('complex transaction', function () {\r
4829 var state;\r
4830\r
4831 beforeEach(function () {\r
4832 state = {\r
4833 parentRecs: [],\r
4834 childRecs: [],\r
4835 grandChildRecs: []\r
4836 };\r
4837\r
4838 Ext.each([0, 1], function (n) {\r
4839 state.grandChildRecs.push(session.createRecord('GrandChild', grandChildData[n]));\r
4840 state.childRecs.push(session.createRecord('Child', childData[n]));\r
4841 state.parentRecs.push(session.createRecord('Parent', parentData[n]));\r
4842 });\r
4843\r
4844 // Make some changes - creates, updates and deletes of all types\r
4845 state.parentRecs[0].set('code', 'xyz');\r
4846 state.childRecs[0].set('name', 'child1a');\r
4847 state.grandChildRecs[0].set('name', 'grand1a');\r
4848\r
4849 state.parentRecs[1].drop();\r
4850 state.childRecs[1].drop();\r
4851 state.grandChildRecs[1].drop();\r
4852\r
4853 state.newParent = session.createRecord('Parent', { name: 'newParent', foo: -42 });\r
4854 state.newChild = session.createRecord('Child', { name: 'newChild' });\r
4855 state.newGrandChild = session.createRecord('GrandChild');\r
4856\r
4857 state.newChild.setParent(state.newParent);\r
4858 state.newGrandChild.setChild(state.newChild);\r
4859 });\r
4860 afterEach(function () {\r
4861 state = null;\r
4862 });\r
4863\r
4864 it('should describe the transaction via getChanges', function () {\r
4865 // Quick sanity check on pending changes\r
4866 var changes = session.getChanges();\r
4867\r
4868 expect(changes).toEqual({\r
4869 Parent: {\r
4870 C: [ { id: -1, name: 'newParent', foo: -42 } ],\r
4871 U: [ { id: 1, code: 'xyz', foo: 42 } ], // foo is a "critical" field\r
4872 D: [ 2 ]\r
4873 },\r
4874 Child: {\r
4875 C: [ { id: -10, name: 'newChild', parentId: -1 } ],\r
4876 U: [ { id: 10, name: 'child1a' } ],\r
4877 D: [ 20 ]\r
4878 },\r
4879 GrandChild: {\r
4880 C: [ { id: -100, childId: -10 } ],\r
4881 U: [ { id: 100, name: 'grand1a' } ],\r
4882 D: [ 200 ]\r
4883 }\r
4884 });\r
4885 });\r
4886\r
4887 it('should produce a Batch via getSaveBatch', function () {\r
4888 var batch = session.getSaveBatch();\r
4889\r
4890 expect(batch.operations.length).toBe(9);\r
4891\r
4892 Ext.each([\r
4893 [ 'create', 'Parent', [ state.newParent ] ],\r
4894 [ 'create', 'Child', [ state.newChild ] ],\r
4895 [ 'create', 'GrandChild', [ state.newGrandChild ] ],\r
4896\r
4897 [ 'update', 'Parent', [ state.parentRecs[0] ] ],\r
4898 [ 'update', 'Child', [ state.childRecs[0] ] ],\r
4899 [ 'update', 'GrandChild', [ state.grandChildRecs[0] ] ],\r
4900\r
4901 [ 'destroy', 'GrandChild', [ state.grandChildRecs[1] ] ],\r
4902 [ 'destroy', 'Child', [ state.childRecs[1] ] ],\r
4903 [ 'destroy', 'Parent', [ state.parentRecs[1] ] ]\r
4904 ], function (expectedData, index) {\r
4905 var operation = batch.operations[index],\r
4906 str;\r
4907\r
4908 str = 'operation[' + index + '].action=';\r
4909 expect(str + operation.action).toBe(str + expectedData[0]);\r
4910\r
4911 str = 'operation[' + index + '].type=';\r
4912 expect(str + operation.entityType.entityName).toBe(str + expectedData[1]);\r
4913\r
4914 str = 'operation[' + index + '].records=';\r
4915 var actual = Ext.Array.pluck(operation.getRecords(), 'id');\r
4916 actual = Ext.encode(actual);\r
4917 var expected = Ext.Array.pluck(expectedData[2], 'id');\r
4918 expected = Ext.encode(expected);\r
4919 expect(str + actual).toBe(str + expected);\r
4920 });\r
4921 });\r
4922\r
4923 it('should progress save batch to completion', function () {\r
4924 var newGrandChild2 = session.createRecord('GrandChild');\r
4925\r
4926 newGrandChild2.setChild(state.newChild);\r
4927 expect(newGrandChild2.id).toBe(-101);\r
4928 expect(newGrandChild2.data.childId).toBe(-10);\r
4929\r
4930 var batch = session.getSaveBatch();\r
4931\r
4932 expect(batch.operations.length).toBe(9);\r
4933\r
4934 // These should be in this order so that clientIdProperty can be tested\r
4935 // properly - we send the response records in the reverse order to ensure\r
4936 // we are not just matching by indexes.\r
4937 var createGrandChildRecs = batch.operations[2].getRecords();\r
4938 expect(createGrandChildRecs[0]).toBe(state.newGrandChild);\r
4939 expect(createGrandChildRecs[1]).toBe(newGrandChild2);\r
4940\r
4941 batch.start();\r
4942\r
4943 // Create Parent\r
4944 completeRequest({\r
4945 id: 1000\r
4946 });\r
4947 expect(state.newParent.id).toBe(1000);\r
4948 expect(state.newChild.data.parentId).toBe(1000);\r
4949\r
4950 // Create Child\r
4951 completeRequest({\r
4952 id: 2000\r
4953 });\r
4954 expect(state.newChild.id).toBe(2000);\r
4955 expect(state.newGrandChild.data.childId).toBe(2000);\r
4956 expect(newGrandChild2.data.childId).toBe(2000);\r
4957\r
4958 // We still have some requests pending, so just pause here so we don't\r
4959 // fire off any more stuff\r
4960 batch.pause();\r
4961\r
4962 // Create GrandChild (respond in reverse order & custom clientIdProperty)\r
4963 completeRequest([{\r
4964 cid: newGrandChild2.id,\r
4965 id: 3001\r
4966 },{\r
4967 cid: state.newGrandChild.id,\r
4968 id: 3000\r
4969 }]);\r
4970 expect(state.newGrandChild.id).toBe(3000);\r
4971 expect(newGrandChild2.id).toBe(3001);\r
4972\r
4973 });\r
4974 }); // complex transaction\r
4975\r
4976 describe('matrix updates', function () {\r
4977 it('should be able to create matrix for new record', function () {\r
4978 var rufusGroupsStore = session.getRecord('User', userRufus.id, false).groups();\r
4979 rufusGroupsStore.load();\r
4980 completeRequest(rufusGroups);\r
4981\r
4982 var user = session.createRecord('User');\r
4983 var groups = user.groups();\r
4984 groups.add(rufusGroupsStore.getAt(0));\r
4985\r
4986 var batch = session.getSaveBatch();\r
4987 var changes = session.getChanges();\r
4988\r
4989 expect(batch.operations.length).toBe(1); // Create for new User\r
4990 expect(changes).toEqual({\r
4991 User: {\r
4992 C: [{\r
4993 id: 'User-1'\r
4994 }],\r
4995\r
4996 groups: {\r
4997 C: {\r
4998 'User-1': [ 42 ] // this is the generated id\r
4999 }\r
5000 }\r
5001 }\r
5002 });\r
5003\r
5004 batch.start();\r
5005 completeRequest({\r
5006 id: 500\r
5007 });\r
5008\r
5009 var remainingChanges = session.getChanges();\r
5010 expect(remainingChanges).toEqual({\r
5011 User: {\r
5012 groups: {\r
5013 C: {\r
5014 500: [ 42 ] // make sure the matrix has the newId\r
5015 }\r
5016 }\r
5017 }\r
5018 });\r
5019 });\r
5020 });\r
5021 });\r
5022});