]> git.proxmox.com Git - extjs.git/blame - extjs/packages/core/test/specs/data/schema/ManyToOne.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / test / specs / data / schema / ManyToOne.js
CommitLineData
6527f429
DM
1describe("Ext.data.schema.ManyToOne", function() {\r
2 \r
3 var schema, Post, Thread, threadRole, postRole,\r
4 threadCalled = false, \r
5 postCalled = false;\r
6\r
7 function definePost(refCfg) {\r
8 Post = Ext.define('spec.Post', {\r
9 extend: 'Ext.data.Model',\r
10 fields: ['id', 'content', {\r
11 name: 'threadId',\r
12 reference: Ext.apply({\r
13 type: 'Thread'\r
14 }, refCfg)\r
15 }],\r
16\r
17 constructor: function() {\r
18 postCalled = true;\r
19 this.callParent(arguments);\r
20 }\r
21 });\r
22 \r
23 threadRole = Post.associations.thread;\r
24 postRole = Thread.associations.posts;\r
25 }\r
26\r
27 function complete(data, status) {\r
28 Ext.Ajax.mockComplete({\r
29 status: status || 200,\r
30 responseText: Ext.JSON.encode(data)\r
31 });\r
32 }\r
33\r
34 beforeEach(function() {\r
35 threadCalled = postCalled = false;\r
36 MockAjaxManager.addMethods();\r
37 schema = Ext.data.Model.schema;\r
38 schema.setNamespace('spec');\r
39 \r
40 Thread = Ext.define('spec.Thread', {\r
41 extend: 'Ext.data.Model',\r
42 fields: ['id', 'title'],\r
43\r
44 constructor: function() {\r
45 threadCalled = true;\r
46 this.callParent(arguments);\r
47 }\r
48 });\r
49 });\r
50\r
51 afterEach(function() {\r
52 MockAjaxManager.removeMethods();\r
53 Ext.undefine('spec.Post');\r
54 Ext.undefine('spec.Thread');\r
55 \r
56 schema.clear(true);\r
57 Post = postRole = Thread = threadRole = schema = null; \r
58 threadCalled = postCalled = false;\r
59 });\r
60 \r
61 describe("Model.associations", function() {\r
62 it("should have an association role on each model", function() {\r
63 definePost();\r
64 expect(Post.associations.thread).toBeDefined();\r
65 expect(Thread.associations.posts).toBeDefined();\r
66 });\r
67 \r
68 it("should have a reference back to the association for each role", function() {\r
69 definePost();\r
70 expect(Post.associations.thread.association).toBe(Thread.associations.posts.association);\r
71 expect(Thread.associations.posts.association.isManyToOne).toBe(true);\r
72 }); \r
73 });\r
74 \r
75 describe("association default config", function() {\r
76 var assoc;\r
77\r
78 beforeEach(function() {\r
79 definePost();\r
80 assoc = threadRole.association;\r
81 });\r
82 \r
83 it("should have a schema set", function() {\r
84 expect(assoc.schema).toBe(schema); \r
85 });\r
86 \r
87 it("should have the reference field set", function() {\r
88 expect(assoc.field).toBe(Post.getField('threadId'));\r
89 }); \r
90 \r
91 it("should have the left part be set to the key holder", function() {\r
92 expect(assoc.left).toBe(postRole);\r
93 });\r
94 \r
95 it("should set definedBy to the key holder", function() {\r
96 expect(assoc.definedBy).toBe(Post); \r
97 });\r
98 \r
99 it("should have the right part be set to the non key holder", function() {\r
100 expect(assoc.right).toBe(threadRole);\r
101 });\r
102 \r
103 it("should have the owner as null", function() {\r
104 expect(assoc.owner).toBeNull();\r
105 });\r
106 \r
107 it("should set the assoc name to {PluralKeyHolder}By{SingluarOther}", function() {\r
108 expect(assoc.name).toBe('ThreadPosts');\r
109 });\r
110 });\r
111 \r
112 describe("left", function() {\r
113 beforeEach(function() {\r
114 definePost();\r
115 });\r
116 \r
117 it("should set the role to be plural lowercase & the type to be the entity name", function() {\r
118 expect(postRole.role).toBe('posts');\r
119 expect(postRole.type).toBe('Post');\r
120 });\r
121 \r
122 it("should set the inverse role to the right", function() {\r
123 expect(postRole.inverse).toBe(threadRole); \r
124 }); \r
125 \r
126 it("should set the entity", function() {\r
127 expect(postRole.cls).toBe(Post); \r
128 });\r
129 });\r
130 \r
131 describe("right", function() {\r
132 beforeEach(function() {\r
133 definePost();\r
134 });\r
135 \r
136 it("should set the role to be singular lowercase & the type to be the entity name", function() {\r
137 expect(threadRole.role).toBe('thread');\r
138 expect(threadRole.type).toBe('Thread');\r
139 });\r
140 \r
141 it("should set the inverse role to the left", function() {\r
142 expect(threadRole.inverse).toBe(postRole); \r
143 }); \r
144 \r
145 it("should set the entity", function() {\r
146 expect(threadRole.cls).toBe(Thread); \r
147 });\r
148 });\r
149 \r
150 describe("configuring", function() {\r
151 it("should set an association name", function() {\r
152 definePost({\r
153 association: 'CustomName'\r
154 }); \r
155 expect(postRole.association.name).toBe('CustomName');\r
156 });\r
157 \r
158 it("should set the owner based on the child param", function() {\r
159 definePost({\r
160 child: true\r
161 });\r
162 expect(postRole.association.owner).toBe(postRole);\r
163 expect(postRole.owner).toBe(true);\r
164 expect(threadRole.owner).toBe(false);\r
165 });\r
166 \r
167 it("should set the owner based on the parent param", function() {\r
168 definePost({\r
169 parent: true\r
170 });\r
171 expect(postRole.association.owner).toBe(threadRole);\r
172 expect(threadRole.owner).toBe(true);\r
173 expect(postRole.owner).toBe(false);\r
174 });\r
175 \r
176 it("should be able to set a custom role", function() {\r
177 definePost({\r
178 role: 'foo'\r
179 });\r
180 threadRole = Post.associations.foo;\r
181 expect(threadRole.association.name).toBe('ThreadFooPosts');\r
182 expect(threadRole.role).toBe('foo');\r
183 });\r
184 \r
185 describe("inverse", function() {\r
186 it("should set with a string", function() {\r
187 definePost({\r
188 inverse: 'foo'\r
189 });\r
190 postRole = Thread.associations.foo;\r
191 expect(postRole.association.name).toBe('ThreadFoo');\r
192 expect(postRole.role).toBe('foo');\r
193 });\r
194 \r
195 it("should set with an object", function() {\r
196 definePost({\r
197 inverse: {\r
198 role: 'foo'\r
199 }\r
200 });\r
201 postRole = Thread.associations.foo;\r
202 expect(postRole.association.name).toBe('ThreadFoo');\r
203 expect(postRole.role).toBe('foo');\r
204 });\r
205 });\r
206 });\r
207 \r
208 describe("model decoration", function() {\r
209 it("should generate a getter on the key holder", function() {\r
210 definePost();\r
211 expect(typeof Post.prototype.getThread).toBe('function');\r
212 });\r
213 \r
214 it("should generate a setter on the key holder", function() {\r
215 definePost();\r
216 expect(typeof Post.prototype.setThread).toBe('function');\r
217 });\r
218 \r
219 it("should define a getter on the inverse", function() {\r
220 definePost();\r
221 expect(typeof Thread.prototype.posts).toBe('function');\r
222 });\r
223 \r
224 it("should allow a custom getter name on the key holder", function() {\r
225 definePost({\r
226 inverse: {\r
227 getterName: 'getFoo'\r
228 }\r
229 });\r
230 expect(typeof Thread.prototype.getFoo).toBe('function');\r
231 });\r
232 \r
233 it("should allow a custom setter name on the key holder", function() {\r
234 definePost({\r
235 setterName: 'setFoo'\r
236 });\r
237 expect(typeof Post.prototype.setFoo).toBe('function');\r
238 });\r
239 \r
240 it("should allow a custom getter name on the inverse", function() {\r
241 definePost({\r
242 getterName: 'ghosts'\r
243 });\r
244 expect(typeof Post.prototype.ghosts).toBe('function');\r
245 });\r
246\r
247 it("should decorate the model based on the role", function() {\r
248 var OtherPost = Ext.define('spec.OtherPost', {\r
249 extend: 'Ext.data.Model',\r
250 fields: ['id', 'name', {\r
251 name: 'threadAId',\r
252 reference: {\r
253 type: 'Thread',\r
254 role: 'ThreadA'\r
255 }\r
256 }, {\r
257 name: 'threadBId',\r
258 reference: {\r
259 type: 'Thread',\r
260 role: 'ThreadB'\r
261 }\r
262 }]\r
263 });\r
264\r
265 expect(typeof OtherPost.prototype.getThreadA).toBe('function');\r
266 expect(typeof OtherPost.prototype.getThreadB).toBe('function');\r
267\r
268 Ext.undefine('spec.OtherPost');\r
269 });\r
270 });\r
271\r
272 describe("subclassing", function() {\r
273 // Post\r
274 describe("the left", function() {\r
275 var SubPost;\r
276\r
277 beforeEach(function() {\r
278 definePost();\r
279 SubPost = Ext.define('spec.SubPost', {\r
280 extend: 'spec.Post'\r
281 });\r
282 });\r
283\r
284 afterEach(function() {\r
285 Ext.undefine('spec.SubPost');\r
286 SubPost = null;\r
287 });\r
288\r
289 it("should still have the original association", function() {\r
290 var inverse = Post.associations.thread.inverse;\r
291 expect(inverse.role).toBe('posts');\r
292 expect(inverse.cls).toBe(Post);\r
293 });\r
294\r
295 it("should inherit the association from the parent and modify the relevant classes", function() {\r
296 var inverse = SubPost.associations.thread.inverse;\r
297 expect(inverse.role).toBe('subPosts');\r
298 expect(inverse.cls).toBe(SubPost);\r
299 });\r
300 });\r
301\r
302 // Thread\r
303 describe("the right", function() {\r
304 var SubThread;\r
305\r
306 beforeEach(function() {\r
307 definePost();\r
308 SubThread = Ext.define('spec.SubThread', {\r
309 extend: 'spec.Thread'\r
310 });\r
311 });\r
312\r
313 it("should not have any associations", function() {\r
314 expect(SubThread.associations).toEqual({});\r
315 });\r
316 });\r
317 });\r
318\r
319 describe("nested loading", function() {\r
320 it("should infer the key when using remoteFilter: false", function() {\r
321 definePost({\r
322 inverse: {\r
323 storeConfig: {\r
324 remoteFilter: false\r
325 }\r
326 }\r
327 });\r
328 var thread = Thread.load(1);\r
329 complete({\r
330 id: 1,\r
331 posts: [{\r
332 id: 101\r
333 }, {\r
334 id: 102\r
335 }]\r
336 });\r
337 var posts = thread.posts();\r
338 expect(posts.getAt(0).get('threadId')).toBe(1);\r
339 expect(posts.getAt(0).dirty).toBe(false);\r
340 expect(posts.getAt(1).get('threadId')).toBe(1);\r
341 expect(posts.getAt(1).dirty).toBe(false);\r
342 expect(posts.getRemoteFilter()).toBe(false);\r
343 });\r
344\r
345 it("should delete the many from the data collection", function() {\r
346 definePost();\r
347 var thread = Thread.load(1);\r
348 complete({\r
349 id: 1,\r
350 posts: [{\r
351 id: 101\r
352 }, {\r
353 id: 102\r
354 }]\r
355 });\r
356 expect(thread.get('posts')).toBeUndefined();\r
357 expect(thread.posts().getCount()).toBe(2);\r
358 });\r
359\r
360 it("should delete the one from the data collection", function() {\r
361 definePost();\r
362 var post = Post.load(101);\r
363 complete({\r
364 id: 101,\r
365 thread: {\r
366 id: 1\r
367 }\r
368 });\r
369 expect(post.get('thread')).toBeUndefined();\r
370 expect(post.getThread().getId()).toBe(1);\r
371 });\r
372\r
373 it("should not pollute the reader when reading nested data of the same type", function() {\r
374 function getData() {\r
375 return {\r
376 records: [{\r
377 id: 1,\r
378 parentId: null,\r
379 children: [{\r
380 id: 101,\r
381 parentId: 1\r
382 }, {\r
383 id: 102,\r
384 parentId: 1\r
385 }]\r
386 }]\r
387 };\r
388 }\r
389 Ext.define('spec.Node', {\r
390 extend: 'Ext.data.Model',\r
391 fields: [{\r
392 name: 'parentId',\r
393 reference: {\r
394 type: 'Node',\r
395 inverse: 'children'\r
396 }\r
397 }],\r
398 proxy: {\r
399 type: 'ajax',\r
400 reader: {\r
401 type: 'json',\r
402 rootProperty: 'records'\r
403 }\r
404 }\r
405 });\r
406\r
407 var store = new Ext.data.Store({\r
408 // Always want immediate load\r
409 asynchronousLoad: false,\r
410 model: 'Node'\r
411 });\r
412 store.load();\r
413 complete(getData());\r
414 expect(store.first().children().getCount()).toBe(2);\r
415 store.load();\r
416 complete(getData());\r
417 expect(store.first().children().getCount()).toBe(2);\r
418 store.destroy();\r
419 Ext.undefine('spec.Node');\r
420 });\r
421\r
422 describe("key inference", function() {\r
423 describe("without session", function() {\r
424 beforeEach(function() {\r
425 definePost();\r
426 });\r
427\r
428 it("should infer the key from the parent", function() {\r
429 var thread = Thread.load(1);\r
430 complete({\r
431 id: 1,\r
432 posts: [{\r
433 id: 101\r
434 }, {\r
435 id: 102\r
436 }]\r
437 });\r
438 var posts = thread.posts();\r
439 expect(posts.getCount()).toBe(2);\r
440 expect(posts.getAt(0).getId()).toBe(101);\r
441 expect(posts.getAt(0).get('threadId')).toBe(1);\r
442 expect(posts.getAt(0).dirty).toBe(false);\r
443 expect(posts.getAt(1).getId()).toBe(102);\r
444 expect(posts.getAt(1).get('threadId')).toBe(1);\r
445 expect(posts.getAt(1).dirty).toBe(false);\r
446 });\r
447\r
448 it("should infer the key when loading the store, not nested", function() {\r
449 var thread = Thread.load(1);\r
450 complete({\r
451 id: 1\r
452 });\r
453 var posts = thread.posts();\r
454 posts.load();\r
455 complete([{\r
456 id: 101\r
457 }, {\r
458 id: 102\r
459 }]);\r
460 expect(posts.getCount()).toBe(2);\r
461 expect(posts.getAt(0).getId()).toBe(101);\r
462 expect(posts.getAt(0).get('threadId')).toBe(1);\r
463 expect(posts.getAt(0).dirty).toBe(false);\r
464 expect(posts.getAt(1).getId()).toBe(102);\r
465 expect(posts.getAt(1).get('threadId')).toBe(1);\r
466 expect(posts.getAt(1).dirty).toBe(false);\r
467 });\r
468 });\r
469\r
470 describe("with session", function() {\r
471 var session;\r
472\r
473 beforeEach(function() {\r
474 definePost();\r
475 session = new Ext.data.Session();\r
476 });\r
477\r
478 afterEach(function() {\r
479 session.destroy();\r
480 session = null;\r
481 });\r
482\r
483 it("should favour an existing reference", function() {\r
484 var post = session.createRecord('Post', {\r
485 id: 101,\r
486 threadId: 3\r
487 });\r
488\r
489 var thread = Thread.load(1, null, session);\r
490 complete({\r
491 id: 1,\r
492 posts: [{\r
493 id: 101\r
494 }, {\r
495 id: 102\r
496 }]\r
497 });\r
498 var posts = thread.posts();\r
499 expect(posts.getCount()).toBe(1);\r
500 expect(posts.getAt(0).getId()).toBe(102);\r
501 expect(posts.getAt(0).get('threadId')).toBe(1);\r
502 expect(posts.getAt(0).dirty).toBe(false);\r
503 expect(posts.indexOf(post)).toBe(-1);\r
504 });\r
505\r
506 it("should infer the key from the parent if not specified", function() {\r
507 var thread = Thread.load(1, null, session);\r
508 complete({\r
509 id: 1,\r
510 posts: [{\r
511 id: 101\r
512 }, {\r
513 id: 102\r
514 }]\r
515 });\r
516 var posts = thread.posts();\r
517 expect(posts.getCount()).toBe(2);\r
518 expect(posts.getAt(0).getId()).toBe(101);\r
519 expect(posts.getAt(0).get('threadId')).toBe(1);\r
520 expect(posts.getAt(0).dirty).toBe(false);\r
521 expect(posts.getAt(1).getId()).toBe(102);\r
522 expect(posts.getAt(1).get('threadId')).toBe(1);\r
523 expect(posts.getAt(1).dirty).toBe(false);\r
524 });\r
525\r
526 it("should infer the key when loading the store, not nested", function() {\r
527 var thread = Thread.load(1, null, session);\r
528 complete({\r
529 id: 1\r
530 });\r
531 var posts = thread.posts();\r
532 posts.load();\r
533 complete([{\r
534 id: 101\r
535 }, {\r
536 id: 102\r
537 }]);\r
538 expect(posts.getCount()).toBe(2);\r
539 expect(posts.getAt(0).getId()).toBe(101);\r
540 expect(posts.getAt(0).get('threadId')).toBe(1);\r
541 expect(posts.getAt(0).dirty).toBe(false);\r
542 expect(posts.getAt(1).getId()).toBe(102);\r
543 expect(posts.getAt(1).get('threadId')).toBe(1);\r
544 expect(posts.getAt(1).dirty).toBe(false);\r
545 });\r
546\r
547 it("should not infer the key from the parent if a key is specified", function() {\r
548 var thread = Thread.load(1, null, session);\r
549 complete({\r
550 id: 1,\r
551 posts: [{\r
552 id: 101,\r
553 threadId: 100\r
554 }, {\r
555 id: 102\r
556 }]\r
557 });\r
558 var posts = thread.posts();\r
559 expect(posts.getCount()).toBe(1);\r
560 expect(posts.getAt(0).getId()).toBe(102);\r
561 expect(posts.getAt(0).get('threadId')).toBe(1);\r
562 expect(posts.getAt(0).dirty).toBe(false);\r
563\r
564 var rec = session.peekRecord('Post', 101);\r
565 expect(posts.indexOf(rec)).toBe(-1);\r
566 });\r
567 });\r
568 });\r
569 });\r
570 \r
571 describe("getters/setters", function() {\r
572 function createSuite(withSession) {\r
573 describe(withSession ? "with session" : "without session", function() {\r
574 var spy, session, post, thread;\r
575\r
576 beforeEach(function() {\r
577 spy = jasmine.createSpy();\r
578 if (withSession) {\r
579 session = new Ext.data.Session();\r
580 }\r
581 });\r
582 \r
583 afterEach(function() {\r
584 if (withSession) {\r
585 session.destroy();\r
586 }\r
587 session = post = thread = null;\r
588 });\r
589\r
590 describe("the one", function() {\r
591 beforeEach(function() {\r
592 definePost();\r
593 });\r
594\r
595 describe("getter", function() {\r
596 beforeEach(function() {\r
597 post = new Post({\r
598 id: 4\r
599 }, session);\r
600 \r
601 });\r
602 describe("without an instance", function() {\r
603 describe("with no foreign key value", function() {\r
604 it("should return null", function() {\r
605 expect(post.getThread()).toBeNull();\r
606 });\r
607\r
608 it("should not make any request", function() {\r
609 spy = spyOn(Thread.getProxy(), 'read');\r
610 post.getThread();\r
611 expect(spy).not.toHaveBeenCalled();\r
612 });\r
613\r
614 describe("callbacks", function() {\r
615 it("should call the callbacks before the function returns", function() {\r
616 post.getThread(spy);\r
617 expect(spy).toHaveBeenCalled();\r
618 spy.reset();\r
619 post.getThread({\r
620 success: spy\r
621 });\r
622 expect(spy).toHaveBeenCalled();\r
623 spy.reset();\r
624 post.getThread({\r
625 callback: spy\r
626 });\r
627 expect(spy).toHaveBeenCalled();\r
628 });\r
629\r
630 it("should accept a function as the callback and default the scope to the model", function() {\r
631 post.getThread(spy);\r
632 var call = spy.mostRecentCall;\r
633 expect(call.args[0]).toBe(thread);\r
634 expect(call.args[1]).toBeNull();\r
635 expect(call.args[2]).toBe(true);\r
636 expect(call.object).toBe(post);\r
637 });\r
638 \r
639 it("should accept a function with a scope", function() {\r
640 var o = {};\r
641 post.getThread(spy, o);\r
642 expect(spy.mostRecentCall.object).toBe(o); \r
643 });\r
644 \r
645 it("should accept an options object with success and default the scope to the model", function() {\r
646 post.getThread({\r
647 success: spy\r
648 }); \r
649 var call = spy.mostRecentCall; \r
650 expect(call.args[0]).toBe(thread);\r
651 expect(call.args[1]).toBeNull();\r
652 expect(call.object).toBe(post); \r
653 });\r
654\r
655 it("should accept an options object with success and a scope", function() {\r
656 var o = {},\r
657 call;\r
658\r
659 post.getThread({\r
660 scope: o,\r
661 success: spy\r
662 }); \r
663 call = spy.mostRecentCall; \r
664 expect(call.object).toBe(o); \r
665 });\r
666\r
667 it("should accept an options object with callback and default the scope to the model", function() {\r
668 post.getThread({\r
669 callback: spy\r
670 }); \r
671 var call = spy.mostRecentCall; \r
672 expect(call.args[0]).toBe(thread);\r
673 expect(call.args[1]).toBeNull();\r
674 expect(call.args[2]).toBe(true);\r
675 expect(call.object).toBe(post); \r
676 });\r
677 \r
678 it("should accept an options object with callback and a scope", function() {\r
679 var o = {},\r
680 call;\r
681\r
682 post.getThread({\r
683 scope: o,\r
684 callback: spy\r
685 }); \r
686 call = spy.mostRecentCall; \r
687 expect(call.object).toBe(o); \r
688 });\r
689 });\r
690 });\r
691\r
692 describe("with a foreign key value", function() {\r
693 beforeEach(function() {\r
694 post.set('threadId', 17);\r
695 });\r
696\r
697 if (withSession) {\r
698 it("should create an instance in the session", function() {\r
699 expect(post.getThread()).toBe(session.getRecord('Thread', 17, false));\r
700 });\r
701\r
702 it("should use an existing record instance", function() {\r
703 thread = session.getRecord('Thread', 17, false);\r
704 expect(post.getThread()).toBe(thread);\r
705 });\r
706\r
707 it("should not load an existing instance", function() {\r
708 thread = session.getRecord('Thread', {\r
709 id: 17\r
710 }, false);\r
711 post.getThread();\r
712 expect(thread.isLoading()).toBe(false);\r
713 });\r
714 }\r
715\r
716 it("should return an instance with the matching id", function() {\r
717 expect(post.getThread().getId()).toBe(17);\r
718 });\r
719\r
720 it("should be in a loading state", function() {\r
721 expect(post.getThread().isLoading()).toBe(true);\r
722 });\r
723\r
724 it("should trigger a load for the record", function() {\r
725 spy = spyOn(Thread.getProxy(), 'read');\r
726 post.getThread();\r
727 expect(spy.mostRecentCall.args[0].getId()).toBe(17);\r
728 });\r
729\r
730 describe("calling while during a load", function() {\r
731 it("should return the same record", function() {\r
732 var rec = post.getThread();\r
733 expect(post.getThread()).toBe(rec);\r
734 });\r
735\r
736 it("should not trigger a second load", function() {\r
737 post.getThread();\r
738 spy = spyOn(Thread.getProxy(), 'read');\r
739 post.getThread();\r
740 expect(spy).not.toHaveBeenCalled();\r
741 });\r
742\r
743 it("should not trigger any callback until load completes", function() {\r
744 post.getThread();\r
745 post.getThread({\r
746 success: spy,\r
747 callback: spy\r
748 });\r
749 expect(spy).not.toHaveBeenCalled();\r
750 });\r
751\r
752 it("should trigger the callbacks once loaded", function() {\r
753 post.getThread();\r
754 post.getThread({\r
755 success: spy,\r
756 callback: spy\r
757 });\r
758 complete({});\r
759 expect(spy.callCount).toBe(2);\r
760 });\r
761 });\r
762\r
763 describe("callbacks", function() {\r
764 it("should not trigger any callbacks until the load completes", function() {\r
765 post.getThread(spy);\r
766 post.getThread({\r
767 success: spy\r
768 });\r
769 post.getThread({\r
770 failure: spy\r
771 });\r
772 post.getThread({\r
773 callback: spy\r
774 });\r
775 expect(spy).not.toHaveBeenCalled();\r
776\r
777 });\r
778\r
779 describe("when successful", function() {\r
780 it("should accept a function as the callback and default the scope to the model", function() {\r
781 thread = post.getThread(spy);\r
782 complete({});\r
783 var call = spy.mostRecentCall;\r
784 expect(call.args[0]).toBe(thread);\r
785 expect(call.args[1].isOperation).toBe(true);\r
786 expect(call.args[2]).toBe(true);\r
787 expect(call.object).toBe(post);\r
788 });\r
789 \r
790 it("should accept a function with a scope", function() {\r
791 var o = {};\r
792 post.getThread(spy, o);\r
793 complete({});\r
794 expect(spy.mostRecentCall.object).toBe(o); \r
795 });\r
796 \r
797 it("should accept an options object with success and default the scope to the model", function() {\r
798 thread = post.getThread({\r
799 success: spy\r
800 }); \r
801 complete({});\r
802 var call = spy.mostRecentCall; \r
803 expect(call.args[0]).toBe(thread);\r
804 expect(call.args[1].isOperation).toBe(true);\r
805 expect(call.object).toBe(post); \r
806 });\r
807\r
808 it("should accept an options object with success and a scope", function() {\r
809 var o = {},\r
810 call;\r
811\r
812 post.getThread({\r
813 scope: o,\r
814 success: spy\r
815 }); \r
816 complete({});\r
817 call = spy.mostRecentCall; \r
818 expect(call.object).toBe(o); \r
819 });\r
820\r
821 it("should accept an options object with callback and default the scope to the model", function() {\r
822 thread = post.getThread({\r
823 callback: spy\r
824 }); \r
825 complete({});\r
826 var call = spy.mostRecentCall; \r
827 expect(call.args[0]).toBe(thread);\r
828 expect(call.args[1].isOperation).toBe(true);\r
829 expect(call.args[2]).toBe(true);\r
830 expect(call.object).toBe(post); \r
831 });\r
832 \r
833 it("should accept an options object with callback and a scope", function() {\r
834 var o = {},\r
835 call;\r
836\r
837 post.getThread({\r
838 scope: o,\r
839 callback: spy\r
840 }); \r
841 complete({});\r
842 call = spy.mostRecentCall; \r
843 expect(call.object).toBe(o); \r
844 });\r
845 });\r
846\r
847 describe("when failed", function() {\r
848 it("should accept a function as the callback and default the scope to the model", function() {\r
849 thread = post.getThread(spy);\r
850 complete(null, 500);\r
851 var call = spy.mostRecentCall;\r
852 expect(call.args[0]).toBe(thread);\r
853 expect(call.args[1].isOperation).toBe(true);\r
854 expect(call.args[2]).toBe(false);\r
855 expect(call.object).toBe(post);\r
856 });\r
857 \r
858 it("should accept a function with a scope", function() {\r
859 var o = {};\r
860 post.getThread(spy, o);\r
861 complete(null, 500);\r
862 expect(spy.mostRecentCall.object).toBe(o); \r
863 });\r
864 \r
865 it("should accept an options object with failure and default the scope to the model", function() {\r
866 thread = post.getThread({\r
867 failure: spy\r
868 }); \r
869 complete(null, 500);\r
870 var call = spy.mostRecentCall; \r
871 expect(call.args[0]).toBe(thread);\r
872 expect(call.args[1].isOperation).toBe(true);\r
873 expect(call.object).toBe(post); \r
874 });\r
875\r
876 it("should accept an options object with failure and a scope", function() {\r
877 var o = {},\r
878 call;\r
879\r
880 post.getThread({\r
881 scope: o,\r
882 failure: spy\r
883 }); \r
884 complete(null, 500);\r
885 call = spy.mostRecentCall; \r
886 expect(call.object).toBe(o); \r
887 });\r
888\r
889 it("should accept an options object with callback and default the scope to the model", function() {\r
890 thread = post.getThread({\r
891 callback: spy\r
892 }); \r
893 complete(null, 500);\r
894 var call = spy.mostRecentCall; \r
895 expect(call.args[0]).toBe(thread);\r
896 expect(call.args[1].isOperation).toBe(true);\r
897 expect(call.args[2]).toBe(false);\r
898 expect(call.object).toBe(post); \r
899 });\r
900 \r
901 it("should accept an options object with callback and a scope", function() {\r
902 var o = {},\r
903 call;\r
904\r
905 post.getThread({\r
906 scope: o,\r
907 callback: spy\r
908 }); \r
909 complete(null, 500);\r
910 call = spy.mostRecentCall; \r
911 expect(call.object).toBe(o); \r
912 });\r
913 });\r
914 });\r
915 });\r
916 });\r
917\r
918 describe("with an already loaded instance", function() {\r
919 beforeEach(function() {\r
920 thread = new Thread({\r
921 id: 2\r
922 }, session);\r
923 \r
924 \r
925 post.setThread(thread);\r
926 });\r
927\r
928 it("should return the same instance", function() {\r
929 expect(post.getThread()).toBe(thread);\r
930 });\r
931\r
932 it("should not attempt to load", function() {\r
933 spy = spyOn(Thread.getProxy(), 'read');\r
934 post.getThread();\r
935 expect(spy).not.toHaveBeenCalled();\r
936 });\r
937\r
938 it("should attempt to reload if called with options.reload", function() {\r
939 spy = spyOn(Thread.getProxy(), 'read').andReturn();\r
940 post.getThread({\r
941 reload: true\r
942 });\r
943 expect(spy).toHaveBeenCalled();\r
944 });\r
945\r
946 it("should reload the same record when called with reload", function() {\r
947 var result = post.getThread({\r
948 reload: true\r
949 });\r
950 expect(result).toBe(thread);\r
951 });\r
952\r
953 describe("callbacks", function() {\r
954 it("should call the callbacks before the function returns", function() {\r
955 post.getThread(spy);\r
956 expect(spy).toHaveBeenCalled();\r
957 spy.reset();\r
958 post.getThread({\r
959 success: spy\r
960 });\r
961 expect(spy).toHaveBeenCalled();\r
962 spy.reset();\r
963 post.getThread({\r
964 callback: spy\r
965 });\r
966 expect(spy).toHaveBeenCalled();\r
967 });\r
968\r
969 it("should accept a function as the callback and default the scope to the model", function() {\r
970 post.getThread(spy);\r
971 var call = spy.mostRecentCall;\r
972 expect(call.args[0]).toBe(thread);\r
973 expect(call.args[1]).toBeNull();\r
974 expect(call.args[2]).toBe(true);\r
975 expect(call.object).toBe(post);\r
976 });\r
977 \r
978 it("should accept a function with a scope", function() {\r
979 var o = {};\r
980 post.getThread(spy, o);\r
981 expect(spy.mostRecentCall.object).toBe(o); \r
982 });\r
983 \r
984 it("should accept an options object with success and default the scope to the model", function() {\r
985 post.getThread({\r
986 success: spy\r
987 }); \r
988 var call = spy.mostRecentCall; \r
989 expect(call.args[0]).toBe(thread);\r
990 expect(call.args[1]).toBeNull();\r
991 expect(call.object).toBe(post); \r
992 });\r
993\r
994 it("should accept an options object with success and a scope", function() {\r
995 var o = {},\r
996 call;\r
997\r
998 post.getThread({\r
999 scope: o,\r
1000 success: spy\r
1001 }); \r
1002 call = spy.mostRecentCall; \r
1003 expect(call.object).toBe(o); \r
1004 });\r
1005\r
1006 it("should accept an options object with callback and default the scope to the model", function() {\r
1007 post.getThread({\r
1008 callback: spy\r
1009 }); \r
1010 var call = spy.mostRecentCall; \r
1011 expect(call.args[0]).toBe(thread);\r
1012 expect(call.args[1]).toBeNull();\r
1013 expect(call.args[2]).toBe(true);\r
1014 expect(call.object).toBe(post); \r
1015 });\r
1016 \r
1017 it("should accept an options object with callback and a scope", function() {\r
1018 var o = {},\r
1019 call;\r
1020\r
1021 post.getThread({\r
1022 scope: o,\r
1023 callback: spy\r
1024 }); \r
1025 call = spy.mostRecentCall; \r
1026 expect(call.object).toBe(o); \r
1027 });\r
1028 });\r
1029 });\r
1030 });\r
1031 \r
1032 describe("setter", function() {\r
1033 beforeEach(function() {\r
1034 post = new Post({\r
1035 id: 7\r
1036 }, session);\r
1037 });\r
1038\r
1039 describe("instance", function() {\r
1040 var thread;\r
1041\r
1042 beforeEach(function() {\r
1043 thread = new Thread({\r
1044 id: 3\r
1045 }, session);\r
1046 });\r
1047\r
1048 describe("with nothing existing", function() {\r
1049 beforeEach(function() {\r
1050 post.setThread(thread);\r
1051 });\r
1052\r
1053 it("should have the same record reference", function() {\r
1054 expect(post.getThread()).toBe(thread);\r
1055 });\r
1056 \r
1057 it("should set the underlying key value", function() {\r
1058 expect(post.get('threadId')).toBe(3); \r
1059 });\r
1060\r
1061 it("should clear the instance and foreign key when setting to null", function() {\r
1062 post.setThread(null);\r
1063 expect(post.getThread()).toBeNull();\r
1064 expect(post.get('threadId')).toBeNull();\r
1065 });\r
1066 });\r
1067\r
1068 describe("with an existing key, but no instance", function() {\r
1069 beforeEach(function() {\r
1070 post.setThread(1000);\r
1071 post.setThread(thread);\r
1072 });\r
1073\r
1074 it("should have the new record reference", function() {\r
1075 expect(post.getThread()).toBe(thread);\r
1076 });\r
1077\r
1078 it("should set the underlying key value", function() {\r
1079 expect(post.get('threadId')).toBe(3); \r
1080 });\r
1081\r
1082 it("should clear the instance and foreign key when setting to null", function() {\r
1083 post.setThread(null);\r
1084 expect(post.getThread()).toBeNull();\r
1085 expect(post.get('threadId')).toBeNull();\r
1086 });\r
1087 });\r
1088\r
1089 describe("with an existing instance", function() {\r
1090 beforeEach(function() {\r
1091 post.setThread(new Thread({\r
1092 id: 1000\r
1093 }, session));\r
1094 post.setThread(thread);\r
1095 });\r
1096\r
1097 it("should have the new record reference", function() {\r
1098 expect(post.getThread()).toBe(thread);\r
1099 });\r
1100\r
1101 it("should set the underlying key value", function() {\r
1102 expect(post.get('threadId')).toBe(3); \r
1103 });\r
1104\r
1105 it("should clear the instance and foreign key when setting to null", function() {\r
1106 post.setThread(null);\r
1107 expect(post.getThread()).toBeNull();\r
1108 expect(post.get('threadId')).toBeNull();\r
1109 });\r
1110 });\r
1111 });\r
1112 \r
1113 describe("value", function() {\r
1114 describe("with nothing existing", function() {\r
1115 it("should set the underlying key", function() {\r
1116 post.setThread(16);\r
1117 expect(post.get('threadId')).toBe(16); \r
1118 });\r
1119\r
1120 it("should return a new record object that loads", function() {\r
1121 post.setThread(16);\r
1122 spy = spyOn(Thread.getProxy(), 'read');\r
1123 // Reference doesn't exist, so need to grab it again here\r
1124 expect(post.getThread().getId()).toBe(16);\r
1125 expect(spy.mostRecentCall.args[0].getId()).toBe(16);\r
1126 });\r
1127\r
1128 it("should do nothing if the key is null", function() {\r
1129 post.setThread(null);\r
1130 expect(post.getThread()).toBeNull();\r
1131 });\r
1132 });\r
1133\r
1134 describe("with an existing key, but no instance", function() {\r
1135 beforeEach(function() {\r
1136 post.setThread(1000);\r
1137 });\r
1138\r
1139 it("should set the underlying key", function() {\r
1140 post.setThread(16);\r
1141 expect(post.get('threadId')).toBe(16); \r
1142 });\r
1143\r
1144 it("should return a new record object that loads", function() {\r
1145 post.setThread(16);\r
1146 spy = spyOn(Thread.getProxy(), 'read');\r
1147 // Reference doesn't exist, so need to grab it again here\r
1148 expect(post.getThread().getId()).toBe(16);\r
1149 expect(spy.mostRecentCall.args[0].getId()).toBe(16);\r
1150 });\r
1151\r
1152 it("should clear the key", function() {\r
1153 post.setThread(null);\r
1154 expect(post.get('threadId')).toBeNull();\r
1155 expect(post.getThread()).toBeNull();\r
1156 });\r
1157 });\r
1158\r
1159 describe("with an existing instance", function() {\r
1160 beforeEach(function() {\r
1161 post.setThread(new Thread({\r
1162 id: 1000\r
1163 }, session));\r
1164 });\r
1165\r
1166 it("should set the underlying key", function() {\r
1167 post.setThread(16);\r
1168 expect(post.get('threadId')).toBe(16); \r
1169 });\r
1170\r
1171 it("should return a new record object that loads", function() {\r
1172 post.setThread(16);\r
1173 spy = spyOn(Thread.getProxy(), 'read');\r
1174 // Reference doesn't exist, so need to grab it again here\r
1175 expect(post.getThread().getId()).toBe(16);\r
1176 expect(spy.mostRecentCall.args[0].getId()).toBe(16);\r
1177 });\r
1178\r
1179 it("should clear the key", function() {\r
1180 post.setThread(null);\r
1181 expect(post.get('threadId')).toBeNull();\r
1182 expect(post.getThread()).toBeNull();\r
1183 });\r
1184 });\r
1185 });\r
1186\r
1187 describe("timing", function() {\r
1188 var thread, joiner, fn;\r
1189\r
1190 beforeEach(function() {\r
1191 joiner = {\r
1192 afterEdit: function() {\r
1193 fn();\r
1194 }\r
1195 };\r
1196 thread = new Thread({\r
1197 id: 101\r
1198 }, session);\r
1199 });\r
1200\r
1201 afterEach(function() {\r
1202 fn = joiner = null;\r
1203 });\r
1204\r
1205 it("should have the record instances set in afterEdit", function() {\r
1206 var val;\r
1207 fn = function() {\r
1208 val = post.getThread();\r
1209 };\r
1210 post.join(joiner);\r
1211 post.setThread(thread);\r
1212 expect(val).toBe(thread);\r
1213 });\r
1214\r
1215 it("should have the value cleared in afterEdit", function() {\r
1216 var val;\r
1217 post.setThread(thread);\r
1218\r
1219 fn = function() {\r
1220 val = post.getThread();\r
1221 };\r
1222 post.join(joiner);\r
1223 post.setThread(null);\r
1224 expect(val).toBeNull();\r
1225 });\r
1226 });\r
1227 \r
1228 describe("callbacks", function() {\r
1229 it("should accept a function as the second arg, scope should default to the model", function() {\r
1230 post.setThread(16, spy);\r
1231 complete({});\r
1232 var call = spy.mostRecentCall;\r
1233 expect(call.args[0]).toBe(post);\r
1234 expect(call.object).toBe(post);\r
1235 }); \r
1236 \r
1237 it("should accept a function with a scope", function() {\r
1238 var o = {};\r
1239 thread = post.setThread(16, spy, o);\r
1240 complete({});\r
1241 expect(spy.mostRecentCall.object).toBe(o);\r
1242 });\r
1243 \r
1244 describe("options object", function() {\r
1245 var successSpy, failureSpy, callbackSpy;\r
1246\r
1247 beforeEach(function() {\r
1248 successSpy = jasmine.createSpy();\r
1249 failureSpy = jasmine.createSpy();\r
1250 callbackSpy = jasmine.createSpy();\r
1251 });\r
1252\r
1253 afterEach(function() {\r
1254 successSpy = failureSpy = callbackSpy = null;\r
1255 });\r
1256\r
1257 describe("on success", function() {\r
1258 it("should call success/callback and scope should default to the model", function() {\r
1259 post.setThread(16, {\r
1260 success: successSpy,\r
1261 callback: callbackSpy,\r
1262 failure: failureSpy\r
1263 });\r
1264 complete({});\r
1265 expect(failureSpy).not.toHaveBeenCalled();\r
1266 expect(successSpy).toHaveBeenCalled();\r
1267 expect(callbackSpy).toHaveBeenCalled();\r
1268 expect(successSpy.mostRecentCall.object).toBe(post);\r
1269 expect(callbackSpy.mostRecentCall.object).toBe(post);\r
1270 });\r
1271\r
1272 it("should use a passed scope", function() {\r
1273 var scope = {};\r
1274 post.setThread(16, {\r
1275 scope: scope,\r
1276 success: successSpy,\r
1277 callback: callbackSpy\r
1278 });\r
1279 complete({});\r
1280 expect(successSpy.mostRecentCall.object).toBe(scope);\r
1281 expect(callbackSpy.mostRecentCall.object).toBe(scope);\r
1282 });\r
1283 });\r
1284\r
1285 describe("on failure", function() {\r
1286 it("should call failure/callback and scope should default to the model", function() {\r
1287 post.setThread(16, {\r
1288 success: successSpy,\r
1289 callback: callbackSpy,\r
1290 failure: failureSpy\r
1291 });\r
1292 complete(null, 500);\r
1293 expect(successSpy).not.toHaveBeenCalled();\r
1294 expect(failureSpy).toHaveBeenCalled();\r
1295 expect(callbackSpy).toHaveBeenCalled();\r
1296 expect(failureSpy.mostRecentCall.object).toBe(post);\r
1297 expect(callbackSpy.mostRecentCall.object).toBe(post);\r
1298 });\r
1299\r
1300 it("should use a passed scope", function() {\r
1301 var scope = {};\r
1302 post.setThread(16, {\r
1303 scope: scope,\r
1304 failure: failureSpy,\r
1305 callback: callbackSpy\r
1306 });\r
1307 complete(null, 500);\r
1308 expect(failureSpy.mostRecentCall.object).toBe(scope);\r
1309 expect(callbackSpy.mostRecentCall.object).toBe(scope);\r
1310 });\r
1311 });\r
1312 });\r
1313 });\r
1314 });\r
1315\r
1316 describe("modifying the foreign key", function() {\r
1317 var thread, posts;\r
1318\r
1319 beforeEach(function() {\r
1320 thread = new Thread({\r
1321 id: 1\r
1322 }, session);\r
1323 posts = thread.posts();\r
1324 });\r
1325\r
1326 function makePost(id, threadId) {\r
1327 post = new Post({\r
1328 id: id,\r
1329 threadId: threadId || 1\r
1330 }, session);\r
1331 }\r
1332\r
1333 afterEach(function() {\r
1334 posts = thread = null;\r
1335 });\r
1336\r
1337 it("should remove from the store when changing the key to null", function() {\r
1338 makePost(101);\r
1339 posts.add(post);\r
1340 post.set('threadId', null);\r
1341 expect(posts.getCount()).toBe(0);\r
1342 });\r
1343\r
1344 it("should remove from the store when changing the key to some other value", function() {\r
1345 makePost(101);\r
1346 posts.add(post);\r
1347 post.set('threadId', 4);\r
1348 expect(posts.getCount()).toBe(0);\r
1349 });\r
1350\r
1351 it("should null out the one when there is no key", function() {\r
1352 makePost(101);\r
1353 post.setThread(thread);\r
1354 post.set('threadId', null);\r
1355 expect(post.thread).toBeFalsy();\r
1356 expect(post.getThread()).toBeNull();\r
1357 });\r
1358\r
1359 it("should not remove the record from unrelated stores", function() {\r
1360 makePost(101);\r
1361 spyOn(Ext.log, 'warn');\r
1362 var someStore = new Ext.data.Store();\r
1363 someStore.add(post);\r
1364 posts.add(post);\r
1365 post.set('threadId', null);\r
1366 expect(posts.getCount()).toBe(0);\r
1367 expect(someStore.first()).toBe(post);\r
1368 someStore.destroy();\r
1369 });\r
1370\r
1371 if (withSession) {\r
1372 it("should add to an existing store if a matching key is found", function() {\r
1373 var otherThread = new Thread({\r
1374 id: 2\r
1375 }, session);\r
1376 var otherPosts = otherThread.posts();\r
1377\r
1378 makePost(101);\r
1379 posts.add(post);\r
1380 post.set('threadId', 2);\r
1381 expect(posts.getCount()).toBe(0);\r
1382 expect(otherPosts.first()).toBe(post);\r
1383 });\r
1384\r
1385 it("should set the many record if it exists in the session", function() {\r
1386 var otherThread = new Thread({\r
1387 id: 2\r
1388 }, session);\r
1389\r
1390 makePost(101);\r
1391 expect(post.getThread()).toBe(thread);\r
1392 post.set('threadId', 2);\r
1393 var threadName = Post.associations.thread.getInstanceName();\r
1394 expect(post[threadName]).toBe(otherThread);\r
1395 expect(post.getThread()).toBe(otherThread);\r
1396 });\r
1397\r
1398 it("should not create the record if the existing key does not exist", function() {\r
1399 makePost(101);\r
1400 posts.add(post);\r
1401 post.set('threadId', 2);\r
1402 expect(session.peekRecord('Thread', 2)).toBeNull();\r
1403 });\r
1404\r
1405 it("should not create the store on an existing record", function() {\r
1406 var otherThread = new Thread({\r
1407 id: 2\r
1408 }, session);\r
1409 var name = otherThread.associations.posts.getStoreName();\r
1410\r
1411 makePost(101);\r
1412 posts.add(post);\r
1413 post.set('threadId', 2);\r
1414 expect(otherThread[name]).toBeUndefined();\r
1415 });\r
1416\r
1417 it("should not add if an existing store is loading", function() {\r
1418 var otherThread = new Thread({\r
1419 id: 2\r
1420 }, session);\r
1421 var otherPosts = otherThread.posts();\r
1422 otherPosts.load();\r
1423\r
1424 makePost(101);\r
1425 posts.add(post);\r
1426 post.set('threadId', 2);\r
1427 expect(posts.getCount()).toBe(0);\r
1428 expect(otherPosts.getCount()).toBe(0);\r
1429 });\r
1430 }\r
1431 });\r
1432 });\r
1433 \r
1434 describe("the many", function() {\r
1435 var posts;\r
1436 function makeThread() {\r
1437 thread = new Thread({\r
1438 id: 3\r
1439 }, session);\r
1440 }\r
1441 \r
1442 var thread;\r
1443 \r
1444 afterEach(function() {\r
1445 posts = thread = null;\r
1446 });\r
1447 \r
1448 it("should return a store", function() {\r
1449 definePost();\r
1450 makeThread();\r
1451 expect(thread.posts().isStore).toBe(true); \r
1452 });\r
1453 \r
1454 it("should set the appropriate model type", function() {\r
1455 definePost();\r
1456 makeThread();\r
1457 expect(thread.posts().model).toBe(Post); \r
1458 });\r
1459\r
1460 if (withSession) {\r
1461 it("should set the session on the store", function() {\r
1462 definePost();\r
1463 makeThread();\r
1464 expect(thread.posts().getSession()).toBe(session);\r
1465 });\r
1466 }\r
1467 \r
1468 it("should return the same store instance on multiple calls", function() {\r
1469 definePost();\r
1470 makeThread();\r
1471 var s = thread.posts();\r
1472 expect(thread.posts()).toBe(s);\r
1473 });\r
1474 \r
1475 it("should apply the storeConfig", function() {\r
1476 definePost({\r
1477 inverse: {\r
1478 storeConfig: {\r
1479 autoLoad: true\r
1480 }\r
1481 }\r
1482 });\r
1483 makeThread();\r
1484 posts = thread.posts();\r
1485 expect(posts.getAutoLoad()).toBe(true);\r
1486 posts.destroy();\r
1487 });\r
1488\r
1489 it("should add a filter on the store", function() {\r
1490 definePost();\r
1491 makeThread();\r
1492 var s = thread.posts(),\r
1493 filter = s.getFilters().first();\r
1494\r
1495 expect(filter.getProperty()).toBe('threadId');\r
1496 expect(filter.getValue()).toBe(3);\r
1497 });\r
1498 \r
1499 describe("autoLoad", function() {\r
1500 it("should not load the store by default", function() {\r
1501 definePost();\r
1502 makeThread();\r
1503 var spy = spyOn(Ext.data.ProxyStore.prototype, 'load').andReturn();\r
1504 thread.posts();\r
1505 expect(spy.callCount).toBe(0); \r
1506 }); \r
1507 \r
1508 it("should load the store if configured with autoLoad: true", function() {\r
1509 definePost({\r
1510 inverse: {\r
1511 autoLoad: true\r
1512 }\r
1513 }); \r
1514 \r
1515 makeThread();\r
1516 var spy = spyOn(Ext.data.ProxyStore.prototype, 'load').andReturn();\r
1517 thread.posts();\r
1518 expect(spy.callCount).toBe(1); \r
1519 });\r
1520 });\r
1521 \r
1522 describe("store modification", function() {\r
1523 \r
1524 beforeEach(function() {\r
1525 definePost();\r
1526 });\r
1527\r
1528 describe("loading", function() {\r
1529 var postData;\r
1530 \r
1531 beforeEach(function() {\r
1532 postData = [{id: 101, threadId: 3}, {id: 102, threadId: 3}, {id: 103, threadId: 3}];\r
1533 });\r
1534\r
1535 it("should set the owner instance when loading", function() {\r
1536 makeThread();\r
1537 var posts = thread.posts();\r
1538 \r
1539 posts.load();\r
1540 complete(postData);\r
1541 \r
1542 var readSpy = spyOn(Post.getProxy(), 'read');\r
1543 expect(posts.getAt(0).getThread()).toBe(thread);\r
1544 expect(posts.getAt(1).getThread()).toBe(thread);\r
1545 expect(posts.getAt(2).getThread()).toBe(thread);\r
1546 expect(readSpy).not.toHaveBeenCalled();\r
1547 });\r
1548 \r
1549 it("should set the owner instance when loading via nested loading", function() {\r
1550 thread = Thread.load(3);\r
1551 complete({\r
1552 id: 3,\r
1553 posts: postData\r
1554 });\r
1555 \r
1556 var posts = thread.posts();\r
1557 \r
1558 var readSpy = spyOn(Post.getProxy(), 'read');\r
1559 expect(posts.getAt(0).getThread()).toBe(thread);\r
1560 expect(posts.getAt(1).getThread()).toBe(thread);\r
1561 expect(posts.getAt(2).getThread()).toBe(thread);\r
1562 expect(readSpy).not.toHaveBeenCalled();\r
1563 });\r
1564 });\r
1565 \r
1566 describe("adding", function() {\r
1567 beforeEach(function() {\r
1568 makeThread();\r
1569 });\r
1570\r
1571 it("should default to the key to the primaryKey", function() {\r
1572 var posts = thread.posts(),\r
1573 post;\r
1574\r
1575 posts.load();\r
1576 complete([]);\r
1577 post = posts.add({})[0];\r
1578 expect(post.get('threadId')).toBe(3);\r
1579 });\r
1580 \r
1581 it("should set the primaryKey onto the foreignKey on add", function() {\r
1582 var posts = thread.posts(),\r
1583 post;\r
1584\r
1585 posts.load();\r
1586 complete([]);\r
1587 post = posts.add({\r
1588 threadId: 1\r
1589 })[0];\r
1590 expect(post.get('threadId')).toBe(3);\r
1591 });\r
1592\r
1593 it("should set the owner instance when adding", function() {\r
1594 var posts = thread.posts();\r
1595 \r
1596 posts.load();\r
1597 complete([]);\r
1598 post = posts.add({})[0];\r
1599 \r
1600 var readSpy = spyOn(Post.getProxy(), 'read');\r
1601 expect(post.getThread()).toBe(thread);\r
1602 expect(readSpy).not.toHaveBeenCalled();\r
1603 });\r
1604\r
1605 it("should set the owner instance when adding when the record already has the FK", function() {\r
1606 var posts = thread.posts(),\r
1607 post = posts.add({\r
1608 id: 101,\r
1609 threadId: thread.getId()\r
1610 })[0];\r
1611 expect(post.getThread()).toBe(thread);\r
1612 });\r
1613\r
1614 it("should have the key & instance set in the add event", function() {\r
1615 var posts = thread.posts(),\r
1616 id, rec;\r
1617\r
1618 post = new Post({}, session);\r
1619 posts.on('add', function() {\r
1620 id = post.get('threadId');\r
1621 rec = post.getThread();\r
1622 });\r
1623 posts.add(post);\r
1624 expect(id).toBe(thread.getId());\r
1625 expect(rec).toBe(thread);\r
1626 });\r
1627 });\r
1628\r
1629 describe("removing", function() {\r
1630 beforeEach(function() {\r
1631 makeThread();\r
1632 });\r
1633\r
1634 it("should set the key to null when removing an item", function() {\r
1635 var posts = thread.posts(),\r
1636 post;\r
1637\r
1638 posts.load();\r
1639 complete([{id: 12, threadId: 3}]);\r
1640 post = posts.first();\r
1641\r
1642 posts.remove(post);\r
1643 expect(post.get('threadId')).toBeNull();\r
1644 });\r
1645\r
1646 it("should set the key to null when removing all items", function() {\r
1647 var posts = thread.posts(),\r
1648 post1, post2, post3;\r
1649\r
1650 posts.load();\r
1651 complete([{id: 11, threadId: 3}, {id: 12, threadId: 3}, {id: 13, threadId: 3}]);\r
1652\r
1653 post1 = posts.getAt(0);\r
1654 post2 = posts.getAt(1);\r
1655 post3 = posts.getAt(2);\r
1656\r
1657 posts.removeAll();\r
1658 expect(post1.get('threadId')).toBeNull();\r
1659 expect(post2.get('threadId')).toBeNull();\r
1660 expect(post3.get('threadId')).toBeNull();\r
1661 });\r
1662\r
1663 it("should not modify the store when removing the an item", function() {\r
1664 var posts = thread.posts(),\r
1665 post;\r
1666 \r
1667 posts.load();\r
1668 complete([{id: 12, threadId: 3}]);\r
1669 post = posts.first();\r
1670 \r
1671 posts.remove(post);\r
1672 expect(thread.posts()).toBe(posts);\r
1673 expect(post.getThread()).toBeNull();\r
1674 });\r
1675 \r
1676 it("should not modify the store when removing the all items", function() {\r
1677 var posts = thread.posts(),\r
1678 post1, post2, post3;\r
1679 \r
1680 posts.load();\r
1681 complete([{id: 11, threadId: 3}, {id: 12, threadId: 3}, {id: 13, threadId: 3}]);\r
1682 \r
1683 post1 = posts.getAt(0);\r
1684 post2 = posts.getAt(1);\r
1685 post3 = posts.getAt(2);\r
1686 \r
1687 posts.removeAll();\r
1688 expect(post1.getThread()).toBeNull();\r
1689 expect(post2.getThread()).toBeNull();\r
1690 expect(post3.getThread()).toBeNull();\r
1691 expect(thread.posts()).toBe(posts);\r
1692 });\r
1693\r
1694 it("should have the key & instance cleared in the remove event", function() {\r
1695 var posts = thread.posts(),\r
1696 id, rec;\r
1697\r
1698 posts.load();\r
1699 complete([{id: 11, threadId: 3}, {id: 12, threadId: 3}, {id: 13, threadId: 3}]);\r
1700 post = posts.first();\r
1701 posts.on('remove', function() {\r
1702 id = post.get('threadId');\r
1703 rec = post.getThread();\r
1704 });\r
1705 posts.remove(post);\r
1706 expect(id).toBeNull();\r
1707 expect(rec).toBeNull();\r
1708 });\r
1709 });\r
1710 });\r
1711\r
1712 describe("reload", function() {\r
1713 beforeEach(function() {\r
1714 definePost();\r
1715 makeThread();\r
1716 });\r
1717\r
1718 it("should reload an existing store", function() {\r
1719 thread.posts();\r
1720 spy = spyOn(Post.getProxy(), 'read');\r
1721 thread.posts({\r
1722 reload: true\r
1723 });\r
1724 expect(spy).toHaveBeenCalled();\r
1725 });\r
1726\r
1727 it("should not trigger an existing load if already loading", function() {\r
1728 posts = thread.posts({});\r
1729 expect(posts.isLoading()).toBe(true);\r
1730 spy = spyOn(Post.getProxy(), 'read');\r
1731 thread.posts({\r
1732 reload: true\r
1733 });\r
1734 expect(spy).not.toHaveBeenCalled();\r
1735 });\r
1736 });\r
1737\r
1738 describe("calling while during a load", function() {\r
1739 beforeEach(function() {\r
1740 definePost();\r
1741 makeThread();\r
1742 });\r
1743\r
1744 it("should not trigger a second load", function() {\r
1745 thread.posts({});\r
1746 spy = spyOn(Post.getProxy(), 'read');\r
1747 thread.posts({});\r
1748 expect(spy).not.toHaveBeenCalled();\r
1749 });\r
1750\r
1751 it("should not trigger any callback until load completes", function() {\r
1752 thread.posts({});\r
1753 thread.posts({\r
1754 success: spy,\r
1755 callback: spy\r
1756 });\r
1757 expect(spy).not.toHaveBeenCalled();\r
1758 });\r
1759\r
1760 it("should trigger the callbacks once loaded", function() {\r
1761 thread.posts({});\r
1762 thread.posts({\r
1763 success: spy,\r
1764 callback: spy\r
1765 });\r
1766 complete([]);\r
1767 expect(spy.callCount).toBe(2);\r
1768 });\r
1769 });\r
1770\r
1771 describe("callbacks", function() {\r
1772 beforeEach(function() {\r
1773 definePost();\r
1774 makeThread();\r
1775 });\r
1776\r
1777 describe("when not triggering a load", function() {\r
1778 beforeEach(function() {\r
1779 thread.posts();\r
1780 });\r
1781\r
1782 it("should call the callbacks before the function returns", function() {\r
1783 thread.posts(spy);\r
1784 expect(spy).toHaveBeenCalled();\r
1785 spy.reset();\r
1786 thread.posts({\r
1787 success: spy\r
1788 });\r
1789 expect(spy).toHaveBeenCalled();\r
1790 spy.reset();\r
1791 thread.posts({\r
1792 callback: spy\r
1793 });\r
1794 expect(spy).toHaveBeenCalled();\r
1795 });\r
1796\r
1797 it("should accept a function as the callback and default the scope to the model", function() {\r
1798 posts = thread.posts(spy);\r
1799 var call = spy.mostRecentCall;\r
1800 expect(call.args[0]).toBe(posts);\r
1801 expect(call.args[1]).toBeNull();\r
1802 expect(call.args[2]).toBe(true);\r
1803 expect(call.object).toBe(thread);\r
1804 });\r
1805 \r
1806 it("should accept a function with a scope", function() {\r
1807 var o = {};\r
1808 thread.posts(spy, o);\r
1809 expect(spy.mostRecentCall.object).toBe(o); \r
1810 });\r
1811 \r
1812 it("should accept an options object with success and default the scope to the model", function() {\r
1813 posts = thread.posts({\r
1814 success: spy\r
1815 }); \r
1816 var call = spy.mostRecentCall; \r
1817 expect(call.args[0]).toBe(posts);\r
1818 expect(call.args[1]).toBeNull();\r
1819 expect(call.object).toBe(thread); \r
1820 });\r
1821\r
1822 it("should accept an options object with success and a scope", function() {\r
1823 var o = {},\r
1824 call;\r
1825\r
1826 thread.posts({\r
1827 scope: o,\r
1828 success: spy\r
1829 }); \r
1830 call = spy.mostRecentCall; \r
1831 expect(call.object).toBe(o); \r
1832 });\r
1833\r
1834 it("should accept an options object with callback and default the scope to the model", function() {\r
1835 posts = thread.posts({\r
1836 callback: spy\r
1837 }); \r
1838 var call = spy.mostRecentCall; \r
1839 expect(call.args[0]).toBe(posts);\r
1840 expect(call.args[1]).toBeNull();\r
1841 expect(call.args[2]).toBe(true);\r
1842 expect(call.object).toBe(thread); \r
1843 });\r
1844 \r
1845 it("should accept an options object with callback and a scope", function() {\r
1846 var o = {},\r
1847 call;\r
1848\r
1849 thread.posts({\r
1850 scope: o,\r
1851 callback: spy\r
1852 }); \r
1853 call = spy.mostRecentCall; \r
1854 expect(call.object).toBe(o); \r
1855 });\r
1856 });\r
1857\r
1858 describe("when triggering a load", function() {\r
1859 it("should not trigger any callbacks until the load completes", function() {\r
1860 thread.posts(spy);\r
1861 thread.posts({\r
1862 success: spy\r
1863 });\r
1864 thread.posts({\r
1865 failure: spy\r
1866 });\r
1867 thread.posts({\r
1868 callback: spy\r
1869 });\r
1870 expect(spy).not.toHaveBeenCalled();\r
1871\r
1872 });\r
1873\r
1874 describe("when successful", function() {\r
1875 it("should accept a function as the callback and default the scope to the model", function() {\r
1876 posts = thread.posts(spy);\r
1877 complete([]);\r
1878 var call = spy.mostRecentCall;\r
1879 expect(call.args[0]).toBe(posts);\r
1880 expect(call.args[1].isOperation).toBe(true);\r
1881 expect(call.args[2]).toBe(true);\r
1882 expect(call.object).toBe(thread);\r
1883 });\r
1884 \r
1885 it("should accept a function with a scope", function() {\r
1886 var o = {};\r
1887 thread.posts(spy, o);\r
1888 complete([]);\r
1889 expect(spy.mostRecentCall.object).toBe(o); \r
1890 });\r
1891 \r
1892 it("should accept an options object with success and default the scope to the model", function() {\r
1893 posts = thread.posts({\r
1894 success: spy\r
1895 }); \r
1896 complete([]);\r
1897 var call = spy.mostRecentCall; \r
1898 expect(call.args[0]).toBe(posts);\r
1899 expect(call.args[1].isOperation).toBe(true);\r
1900 expect(call.object).toBe(thread); \r
1901 });\r
1902\r
1903 it("should accept an options object with success and a scope", function() {\r
1904 var o = {},\r
1905 call;\r
1906\r
1907 thread.posts({\r
1908 scope: o,\r
1909 success: spy\r
1910 }); \r
1911 complete([]);\r
1912 call = spy.mostRecentCall; \r
1913 expect(call.object).toBe(o); \r
1914 });\r
1915\r
1916 it("should accept an options object with callback and default the scope to the model", function() {\r
1917 posts = thread.posts({\r
1918 callback: spy\r
1919 }); \r
1920 complete([]);\r
1921 var call = spy.mostRecentCall; \r
1922 expect(call.args[0]).toBe(posts);\r
1923 expect(call.args[1].isOperation).toBe(true);\r
1924 expect(call.args[2]).toBe(true);\r
1925 expect(call.object).toBe(thread); \r
1926 });\r
1927 \r
1928 it("should accept an options object with callback and a scope", function() {\r
1929 var o = {},\r
1930 call;\r
1931\r
1932 thread.posts({\r
1933 scope: o,\r
1934 callback: spy\r
1935 }); \r
1936 complete([]);\r
1937 call = spy.mostRecentCall; \r
1938 expect(call.object).toBe(o); \r
1939 });\r
1940 });\r
1941\r
1942 describe("when failed", function() {\r
1943 it("should accept a function as the callback and default the scope to the model", function() {\r
1944 posts = thread.posts(spy);\r
1945 complete(null, 500);\r
1946 var call = spy.mostRecentCall;\r
1947 expect(call.args[0]).toBe(posts);\r
1948 expect(call.args[1].isOperation).toBe(true);\r
1949 expect(call.args[2]).toBe(false);\r
1950 expect(call.object).toBe(thread);\r
1951 });\r
1952 \r
1953 it("should accept a function with a scope", function() {\r
1954 var o = {};\r
1955 thread.posts(spy, o);\r
1956 complete(null, 500);\r
1957 expect(spy.mostRecentCall.object).toBe(o); \r
1958 });\r
1959 \r
1960 it("should accept an options object with failure and default the scope to the model", function() {\r
1961 posts = thread.posts({\r
1962 failure: spy\r
1963 }); \r
1964 complete(null, 500);\r
1965 var call = spy.mostRecentCall; \r
1966 expect(call.args[0]).toBe(posts);\r
1967 expect(call.args[1].isOperation).toBe(true);\r
1968 expect(call.object).toBe(thread); \r
1969 });\r
1970\r
1971 it("should accept an options object with failure and a scope", function() {\r
1972 var o = {},\r
1973 call;\r
1974\r
1975 thread.posts({\r
1976 scope: o,\r
1977 failure: spy\r
1978 }); \r
1979 complete(null, 500);\r
1980 call = spy.mostRecentCall; \r
1981 expect(call.object).toBe(o); \r
1982 });\r
1983\r
1984 it("should accept an options object with callback and default the scope to the model", function() {\r
1985 posts = thread.posts({\r
1986 callback: spy\r
1987 }); \r
1988 complete(null, 500);\r
1989 var call = spy.mostRecentCall; \r
1990 expect(call.args[0]).toBe(posts);\r
1991 expect(call.args[1].isOperation).toBe(true);\r
1992 expect(call.args[2]).toBe(false);\r
1993 expect(call.object).toBe(thread); \r
1994 });\r
1995 \r
1996 it("should accept an options object with callback and a scope", function() {\r
1997 var o = {},\r
1998 call;\r
1999\r
2000 thread.posts({\r
2001 scope: o,\r
2002 callback: spy\r
2003 }); \r
2004 complete(null, 500);\r
2005 call = spy.mostRecentCall; \r
2006 expect(call.object).toBe(o); \r
2007 });\r
2008 });\r
2009 });\r
2010 });\r
2011\r
2012 if (withSession) {\r
2013 describe("local store modifications with loading", function() {\r
2014 var data;\r
2015\r
2016 beforeEach(function() {\r
2017 definePost();\r
2018 makeThread();\r
2019 posts = thread.posts();\r
2020\r
2021 data = [{\r
2022 id: 101,\r
2023 threadId: 3\r
2024 }, {\r
2025 id: 102,\r
2026 threadId: 3\r
2027 }, {\r
2028 id: 103,\r
2029 threadId: 3\r
2030 }];\r
2031 });\r
2032\r
2033 it("should exclude records with local foreign key changes", function() {\r
2034 posts.load();\r
2035 complete(data);\r
2036 var rec = session.getRecord('Post', 102);\r
2037 posts.removeAt(1);\r
2038 expect(rec.get('threadId')).toBeNull();\r
2039 posts.load();\r
2040 complete(data);\r
2041 expect(posts.getCount()).toBe(2);\r
2042 expect(posts.indexOf(rec)).toBe(-1);\r
2043 });\r
2044\r
2045 it("should append records with the key that were not attached", function() {\r
2046 posts.load();\r
2047 complete(data);\r
2048 var rec = session.getRecord('Post', 104);\r
2049 complete({\r
2050 id: 104\r
2051 });\r
2052 posts.add(rec);\r
2053 expect(rec.get('threadId')).toBe(3);\r
2054 posts.load();\r
2055 complete(data);\r
2056 expect(posts.getCount()).toBe(4);\r
2057 expect(posts.indexOf(rec)).toBe(3);\r
2058 });\r
2059\r
2060 it("should include records added to the session with a matching key", function() {\r
2061 posts.load();\r
2062 complete(data);\r
2063\r
2064 var p1 = new Post({\r
2065 id: 104,\r
2066 threadId: 3\r
2067 }, session);\r
2068\r
2069 var p2 = new Post({\r
2070 id: 105,\r
2071 threadId: 7\r
2072 }, session);\r
2073\r
2074 expect(posts.indexOf(p1)).toBe(3);\r
2075 expect(posts.indexOf(p2)).toBe(-1);\r
2076 });\r
2077 });\r
2078 }\r
2079 });\r
2080 });\r
2081 }\r
2082 createSuite(false);\r
2083 createSuite(true);\r
2084 });\r
2085\r
2086 describe("dropping", function() {\r
2087 function createSuite(withSession) {\r
2088 var session, post, thread, storeData;\r
2089\r
2090 beforeEach(function() {\r
2091 if (withSession) {\r
2092 session = new Ext.data.Session();\r
2093 }\r
2094\r
2095 storeData = [{\r
2096 id: 1,\r
2097 posts: [{\r
2098 id: 101,\r
2099 threadId: 1\r
2100 }, {\r
2101 id: 102,\r
2102 threadId: 1\r
2103 }, {\r
2104 id: 103,\r
2105 threadId: 1\r
2106 }]\r
2107 }];\r
2108 });\r
2109\r
2110 afterEach(function() {\r
2111 if (withSession) {\r
2112 session.destroy();\r
2113 session = null;\r
2114 }\r
2115 storeData = post = thread = null;\r
2116 });\r
2117\r
2118 function makePost(id, threadId) {\r
2119 var data = {\r
2120 id: id,\r
2121 threadId: threadId\r
2122 };\r
2123\r
2124 // Session will be null if withSession is false\r
2125 post = new Post(data, session);\r
2126 }\r
2127\r
2128 function makeThread(id) {\r
2129 // Session will be null if withSession = false\r
2130 thread = new Thread({\r
2131 id: id\r
2132 }, session);\r
2133 }\r
2134\r
2135 function makeStore(data) {\r
2136 var store = new Ext.data.Store({\r
2137 model: Thread,\r
2138 // Session will be null if withSession = false\r
2139 session: session\r
2140 });\r
2141 store.loadRawData(data || storeData);\r
2142 return store;\r
2143 }\r
2144\r
2145 describe(withSession ? "with session" : "without session", function() {\r
2146 describe("the one", function() {\r
2147 beforeEach(function() {\r
2148 definePost();\r
2149 });\r
2150\r
2151 describe("inverse not loaded", function() {\r
2152 it("should not create the the inverse record", function() {\r
2153 makePost(101, 1);\r
2154 post.drop();\r
2155 expect(threadCalled).toBe(false);\r
2156 });\r
2157\r
2158 \r
2159 it("should clear the foreign key", function() {\r
2160 makePost(101, 1);\r
2161 post.drop();\r
2162 expect(post.get('threadId')).toBeNull();\r
2163 });\r
2164 });\r
2165\r
2166 describe("inverse loaded", function() {\r
2167 var store, posts;\r
2168\r
2169 beforeEach(function() {\r
2170 store = makeStore();\r
2171 thread = store.first();\r
2172 posts = thread.posts();\r
2173 post = posts.first();\r
2174 });\r
2175\r
2176 afterEach(function() {\r
2177 store.destroy();\r
2178 store = posts = null;\r
2179 });\r
2180\r
2181 it("should remove from the store", function() {\r
2182 expect(posts.getCount()).toBe(3);\r
2183 post.drop();\r
2184 expect(posts.getCount()).toBe(2);\r
2185 expect(posts.indexOf(post)).toBe(-1);\r
2186 });\r
2187\r
2188 it("should clear the foreign key", function() {\r
2189 post.drop();\r
2190 expect(post.get('threadId')).toBeNull();\r
2191 });\r
2192\r
2193 it("should not return the inverse record", function() {\r
2194 expect(post.getThread()).toBe(thread);\r
2195 post.drop();\r
2196 expect(post.getThread()).toBeNull();\r
2197 });\r
2198 });\r
2199 });\r
2200\r
2201 describe("the many", function() {\r
2202 describe("inverse not loaded", function() {\r
2203 beforeEach(function() {\r
2204 definePost();\r
2205 });\r
2206\r
2207 it("should not attempt to load the store", function() {\r
2208 makeThread(1);\r
2209 var spy = spyOn(Post.getProxy(), 'read');\r
2210 thread.drop();\r
2211 expect(spy).not.toHaveBeenCalled();\r
2212 });\r
2213 });\r
2214\r
2215 describe("inverse loaded", function() {\r
2216 var store;\r
2217\r
2218 afterEach(function() {\r
2219 if (store) {\r
2220 store.destroy();\r
2221 }\r
2222 store = null;\r
2223 });\r
2224\r
2225 describe("no parent/child relationship", function() {\r
2226 beforeEach(function() {\r
2227 definePost();\r
2228 });\r
2229\r
2230 it("should not raise an exception with an empty store", function() {\r
2231 store = makeStore([{\r
2232 id: 1\r
2233 }]);\r
2234 thread = store.first();\r
2235 expect(function() {\r
2236 thread.drop();\r
2237 }).not.toThrow();\r
2238 });\r
2239\r
2240 it("should remove all children from the store", function() {\r
2241 store = makeStore();\r
2242 thread = store.first();\r
2243 var posts = thread.posts(),\r
2244 allPosts = posts.getRange(),\r
2245 spy = jasmine.createSpy();\r
2246\r
2247 posts.on('clear', spy);\r
2248 thread.drop();\r
2249 expect(spy.mostRecentCall.args[1]).toEqual(allPosts);\r
2250 });\r
2251\r
2252 it("should clear the foreign key for each child", function() {\r
2253 store = makeStore();\r
2254 thread = store.first();\r
2255 var posts = thread.posts(),\r
2256 allPosts = posts.getRange();\r
2257\r
2258 thread.drop();\r
2259 expect(allPosts[0].get('threadId')).toBeNull();\r
2260 expect(allPosts[1].get('threadId')).toBeNull();\r
2261 expect(allPosts[2].get('threadId')).toBeNull();\r
2262 });\r
2263\r
2264 it("should not drop the child records", function() {\r
2265 store = makeStore();\r
2266 thread = store.first();\r
2267 var posts = thread.posts(),\r
2268 allPosts = posts.getRange();\r
2269\r
2270 thread.drop();\r
2271 expect(allPosts[0].dropped).toBe(false);\r
2272 expect(allPosts[1].dropped).toBe(false);\r
2273 expect(allPosts[2].dropped).toBe(false);\r
2274 });\r
2275\r
2276 it("should clear the owner on the inverse", function() {\r
2277 store = makeStore();\r
2278 thread = store.first();\r
2279 var posts = thread.posts(),\r
2280 allPosts = posts.getRange();\r
2281\r
2282 thread.drop();\r
2283\r
2284 expect(allPosts[0].getThread()).toBeNull();\r
2285 expect(allPosts[1].getThread()).toBeNull();\r
2286 expect(allPosts[2].getThread()).toBeNull();\r
2287 });\r
2288 });\r
2289\r
2290 describe("as a parent", function() {\r
2291 var posts, allPosts;\r
2292\r
2293 function createDefaults() {\r
2294 store = makeStore();\r
2295 thread = store.first();\r
2296 posts = thread.posts();\r
2297 allPosts = posts.getRange();\r
2298 post = posts.first();\r
2299 }\r
2300\r
2301 beforeEach(function() {\r
2302 definePost({\r
2303 type: null,\r
2304 parent: 'Thread'\r
2305 });\r
2306 });\r
2307\r
2308 afterEach(function() {\r
2309 posts = allPosts = null;\r
2310 });\r
2311\r
2312 it("should not raise an exception with an empty store", function() {\r
2313 store = makeStore([{\r
2314 id: 1\r
2315 }]);\r
2316 thread = store.first();\r
2317 expect(function() {\r
2318 thread.drop();\r
2319 }).not.toThrow();\r
2320 });\r
2321\r
2322 it("should drop each child in the store and remove it", function() {\r
2323 var spy = jasmine.createSpy();\r
2324 createDefaults();\r
2325\r
2326 posts.on('clear', spy);\r
2327 thread.drop();\r
2328 expect(allPosts[0].dropped).toBe(true);\r
2329 expect(allPosts[1].dropped).toBe(true);\r
2330 expect(allPosts[2].dropped).toBe(true);\r
2331 expect(spy.mostRecentCall.args[1]).toEqual(allPosts);\r
2332 });\r
2333\r
2334 it("should clear the foreign key for each child", function() {\r
2335 createDefaults();\r
2336\r
2337 thread.drop();\r
2338 expect(allPosts[0].get('threadId')).toBeNull();\r
2339 expect(allPosts[1].get('threadId')).toBeNull();\r
2340 expect(allPosts[2].get('threadId')).toBeNull();\r
2341 });\r
2342\r
2343 it("should clear the owner on the inverse", function() {\r
2344 createDefaults();\r
2345\r
2346 thread.drop();\r
2347\r
2348 expect(allPosts[0].getThread()).toBeNull();\r
2349 expect(allPosts[1].getThread()).toBeNull();\r
2350 expect(allPosts[2].getThread()).toBeNull();\r
2351 });\r
2352\r
2353 describe("dropping the child", function() {\r
2354 it("should drop a child when removing it from the store", function() {\r
2355 createDefaults();\r
2356 posts.remove(post);\r
2357 Ext.data.Model.schema.processKeyChecks(true);\r
2358 expect(post.dropped).toBe(true);\r
2359 });\r
2360\r
2361 it("should drop a child when changing the foreign key", function() {\r
2362 createDefaults();\r
2363 post.set('threadId', null);\r
2364 Ext.data.Model.schema.processKeyChecks(true);\r
2365 expect(post.dropped).toBe(true);\r
2366 expect(posts.indexOf(post)).toBe(-1);\r
2367 });\r
2368\r
2369 it("should drop a child when nulling out via the setter", function() {\r
2370 createDefaults();\r
2371 post.setThread(null);\r
2372 Ext.data.Model.schema.processKeyChecks(true);\r
2373 expect(post.dropped).toBe(true);\r
2374 expect(posts.indexOf(post)).toBe(-1);\r
2375 });\r
2376\r
2377 if (withSession) {\r
2378 it("should drop the child even if the store is not created", function() {\r
2379 thread = new Thread({\r
2380 id: 1\r
2381 }, session);\r
2382\r
2383 var post1 = new Post({\r
2384 id: 101,\r
2385 threadId: 1\r
2386 }, session);\r
2387\r
2388 var post2 = new Post({\r
2389 id: 102,\r
2390 threadId: 1\r
2391 }, session);\r
2392\r
2393 var post3 = new Post({\r
2394 id: 103,\r
2395 threadId: 1\r
2396 }, session);\r
2397\r
2398 var post4 = new Post({\r
2399 id: 104,\r
2400 threadId: 2\r
2401 });\r
2402\r
2403 thread.drop();\r
2404 expect(post1.dropped).toBe(true);\r
2405 expect(post2.dropped).toBe(true);\r
2406 expect(post3.dropped).toBe(true);\r
2407 expect(post4.dropped).toBe(false);\r
2408 });\r
2409 }\r
2410 });\r
2411\r
2412 describe("not dropping the child", function() {\r
2413 it("should not drop the child when setting a new record", function() {\r
2414 createDefaults();\r
2415 var other = new Thread({\r
2416 id: 2\r
2417 }, session);\r
2418 post.setThread(other);\r
2419 expect(post.dropped).toBe(false);\r
2420 });\r
2421\r
2422 it("should not drop the child when setting a new key", function() {\r
2423 createDefaults();\r
2424 post.setThread(2);\r
2425 expect(post.dropped).toBe(false);\r
2426 });\r
2427\r
2428 it("should not drop the child when adding to a new store", function() {\r
2429 createDefaults();\r
2430\r
2431 var otherThread = new Thread({\r
2432 id: 2\r
2433 }, session);\r
2434 posts.remove(post);\r
2435\r
2436 otherThread.posts().add(post);\r
2437 Ext.data.Model.schema.processKeyChecks(true);\r
2438 expect(post.dropped).toBe(false);\r
2439 });\r
2440\r
2441 it("should not drop the child when setting the foreign key", function() {\r
2442 createDefaults();\r
2443\r
2444 posts.remove(post);\r
2445 post.set('threadId', 2);\r
2446 Ext.data.Model.schema.processKeyChecks(true);\r
2447 expect(post.dropped).toBe(false);\r
2448 });\r
2449\r
2450 it("should not drop the child when setting a new parent", function() {\r
2451 createDefaults();\r
2452\r
2453 var otherThread = new Thread({\r
2454 id: 2\r
2455 }, session);\r
2456\r
2457 posts.remove(post);\r
2458 post.setThread(otherThread);\r
2459 Ext.data.Model.schema.processKeyChecks(true);\r
2460 expect(post.dropped).toBe(false);\r
2461 });\r
2462 });\r
2463 });\r
2464 });\r
2465 });\r
2466 });\r
2467 }\r
2468 createSuite(false);\r
2469 createSuite(true);\r
2470 });\r
2471});