/test/collection.js
JavaScript | 345 lines | 314 code | 30 blank | 1 comment | 16 complexity | cae9a650ea497899f41b647e4eb45320 MD5 | raw file
1$(document).ready(function() {
2
3 module("Backbone.Collection");
4
5 window.lastRequest = null;
6
7 Backbone.sync = function() {
8 lastRequest = _.toArray(arguments);
9 };
10
11 var a = new Backbone.Model({id: 3, label: 'a'});
12 var b = new Backbone.Model({id: 2, label: 'b'});
13 var c = new Backbone.Model({id: 1, label: 'c'});
14 var d = new Backbone.Model({id: 0, label: 'd'});
15 var e = null;
16 var col = new Backbone.Collection([a,b,c,d]);
17 var otherCol = new Backbone.Collection();
18
19 test("Collection: new and sort", function() {
20 equals(col.first(), a, "a should be first");
21 equals(col.last(), d, "d should be last");
22 col.comparator = function(model) { return model.id; };
23 col.sort();
24 equals(col.first(), d, "d should be first");
25 equals(col.last(), a, "a should be last");
26 equals(col.length, 4);
27 });
28
29 test("Collection: get, getByCid", function() {
30 equals(col.get(0), d);
31 equals(col.get(2), b);
32 equals(col.getByCid(col.first().cid), col.first());
33 });
34
35 test("Collection: get with non-default ids", function() {
36 var col = new Backbone.Collection();
37 var MongoModel = Backbone.Model.extend({
38 idAttribute: '_id'
39 });
40 var model = new MongoModel({_id: 100});
41 col.add(model);
42 equals(col.get(100), model);
43 model.set({_id: 101});
44 equals(col.get(101), model);
45 });
46
47 test("Collection: update index when id changes", function() {
48 var col = new Backbone.Collection();
49 col.add([
50 {id : 0, name : 'one'},
51 {id : 1, name : 'two'}
52 ]);
53 var one = col.get(0);
54 equals(one.get('name'), 'one');
55 one.set({id : 101});
56 equals(col.get(0), null);
57 equals(col.get(101).get('name'), 'one');
58 });
59
60 test("Collection: at", function() {
61 equals(col.at(2), b);
62 });
63
64 test("Collection: pluck", function() {
65 equals(col.pluck('label').join(' '), 'd c b a');
66 });
67
68 test("Collection: add", function() {
69 var added = opts = secondAdded = null;
70 e = new Backbone.Model({id: 10, label : 'e'});
71 otherCol.add(e);
72 otherCol.bind('add', function() {
73 secondAdded = true;
74 });
75 col.bind('add', function(model, collection, options){
76 added = model.get('label');
77 opts = options;
78 });
79 col.add(e, {amazing: true});
80 equals(added, 'e');
81 equals(col.length, 5);
82 equals(col.last(), e);
83 equals(otherCol.length, 1);
84 equals(secondAdded, null);
85 ok(opts.amazing);
86
87 var f = new Backbone.Model({id: 20, label : 'f'});
88 var g = new Backbone.Model({id: 21, label : 'g'});
89 var h = new Backbone.Model({id: 22, label : 'h'});
90 var atCol = new Backbone.Collection([f, g, h]);
91 equals(atCol.length, 3);
92 atCol.add(e, {at: 1});
93 equals(atCol.length, 4);
94 equals(atCol.at(1), e);
95 equals(atCol.last(), h);
96 });
97
98 test("Collection: add model to collection twice", function() {
99 try {
100 // no id, same cid
101 var a2 = new Backbone.Model({label: a.label});
102 a2.cid = a.cid;
103 col.add(a2);
104 ok(false, "duplicate; expected add to fail");
105 } catch (e) {
106 equals(e.message, "Can't add the same model to a set twice,3");
107 }
108 });
109
110 test("Collection: add model to multiple collections", function() {
111 var counter = 0;
112 var e = new Backbone.Model({id: 10, label : 'e'});
113 e.bind('add', function(model, collection) {
114 counter++;
115 equals(e, model);
116 if (counter > 1) {
117 equals(collection, colF);
118 } else {
119 equals(collection, colE);
120 }
121 });
122 var colE = new Backbone.Collection([]);
123 colE.bind('add', function(model, collection) {
124 equals(e, model);
125 equals(colE, collection);
126 });
127 var colF = new Backbone.Collection([]);
128 colF.bind('add', function(model, collection) {
129 equals(e, model);
130 equals(colF, collection);
131 });
132 colE.add(e);
133 equals(e.collection, colE);
134 colF.add(e);
135 equals(e.collection, colE);
136 });
137
138 test("Collection: remove", function() {
139 var removed = otherRemoved = null;
140 col.bind('remove', function(model){ removed = model.get('label'); });
141 otherCol.bind('remove', function(){ otherRemoved = true; });
142 col.remove(e);
143 equals(removed, 'e');
144 equals(col.length, 4);
145 equals(col.first(), d);
146 equals(otherRemoved, null);
147 });
148
149 test("Collection: events are unbound on remove", function() {
150 var counter = 0;
151 var dj = new Backbone.Model();
152 var emcees = new Backbone.Collection([dj]);
153 emcees.bind('change', function(){ counter++; });
154 dj.set({name : 'Kool'});
155 equals(counter, 1);
156 emcees.reset([]);
157 equals(dj.collection, undefined);
158 dj.set({name : 'Shadow'});
159 equals(counter, 1);
160 });
161
162 test("Collection: remove in multiple collections", function() {
163 var modelData = {
164 id : 5,
165 title : 'Othello'
166 };
167 var passed = false;
168 var e = new Backbone.Model(modelData);
169 var f = new Backbone.Model(modelData);
170 f.bind('remove', function() {
171 passed = true;
172 });
173 var colE = new Backbone.Collection([e]);
174 var colF = new Backbone.Collection([f]);
175 ok(e != f);
176 ok(colE.length == 1);
177 ok(colF.length == 1);
178 colE.remove(e);
179 equals(passed, false);
180 ok(colE.length == 0);
181 colF.remove(e);
182 ok(colF.length == 0);
183 equals(passed, true);
184 });
185
186 test("Collection: remove same model in multiple collection", function() {
187 var counter = 0;
188 var e = new Backbone.Model({id: 5, title: 'Othello'});
189 e.bind('remove', function(model, collection) {
190 counter++;
191 equals(e, model);
192 if (counter > 1) {
193 equals(collection, colE);
194 } else {
195 equals(collection, colF);
196 }
197 });
198 var colE = new Backbone.Collection([e]);
199 colE.bind('remove', function(model, collection) {
200 equals(e, model);
201 equals(colE, collection);
202 });
203 var colF = new Backbone.Collection([e]);
204 colF.bind('remove', function(model, collection) {
205 equals(e, model);
206 equals(colF, collection);
207 });
208 equals(colE, e.collection);
209 colF.remove(e);
210 ok(colF.length == 0);
211 ok(colE.length == 1);
212 equals(counter, 1);
213 equals(colE, e.collection);
214 colE.remove(e);
215 equals(null, e.collection);
216 ok(colE.length == 0);
217 equals(counter, 2);
218 });
219
220 test("Collection: model destroy removes from all collections", function() {
221 var e = new Backbone.Model({id: 5, title: 'Othello'});
222 e.sync = function(method, model, options) { options.success({}); };
223 var colE = new Backbone.Collection([e]);
224 var colF = new Backbone.Collection([e]);
225 e.destroy();
226 ok(colE.length == 0);
227 ok(colF.length == 0);
228 equals(null, e.collection);
229 });
230
231 test("Colllection: non-persisted model destroy removes from all collections", function() {
232 var e = new Backbone.Model({title: 'Othello'});
233 e.sync = function(method, model, options) { throw "should not be called"; };
234 var colE = new Backbone.Collection([e]);
235 var colF = new Backbone.Collection([e]);
236 e.destroy();
237 ok(colE.length == 0);
238 ok(colF.length == 0);
239 equals(null, e.collection);
240 });
241
242 test("Collection: fetch", function() {
243 col.fetch();
244 equals(lastRequest[0], 'read');
245 equals(lastRequest[1], col);
246 });
247
248 test("Collection: create", function() {
249 var model = col.create({label: 'f'});
250 equals(lastRequest[0], 'create');
251 equals(lastRequest[1], model);
252 equals(model.get('label'), 'f');
253 equals(model.collection, col);
254 });
255
256 test("Collection: create enforces validation", function() {
257 var ValidatingModel = Backbone.Model.extend({
258 validate: function(attrs) {
259 return "fail";
260 }
261 });
262 var ValidatingCollection = Backbone.Collection.extend({
263 model: ValidatingModel
264 });
265 var col = new ValidatingCollection();
266 equals(col.create({"foo":"bar"}),false);
267 });
268
269 test("Collection: a failing create runs the error callback", function() {
270 var ValidatingModel = Backbone.Model.extend({
271 validate: function(attrs) {
272 return "fail";
273 }
274 });
275 var ValidatingCollection = Backbone.Collection.extend({
276 model: ValidatingModel
277 });
278 var flag = false;
279 var callback = function(model, error) { flag = true; };
280 var col = new ValidatingCollection();
281 col.create({"foo":"bar"}, { error: callback });
282 equals(flag, true);
283 });
284
285 test("collection: initialize", function() {
286 var Collection = Backbone.Collection.extend({
287 initialize: function() {
288 this.one = 1;
289 }
290 });
291 var coll = new Collection;
292 equals(coll.one, 1);
293 });
294
295 test("Collection: toJSON", function() {
296 equals(JSON.stringify(col), '[{"id":0,"label":"d"},{"id":1,"label":"c"},{"id":2,"label":"b"},{"id":3,"label":"a"}]');
297 });
298
299 test("Collection: Underscore methods", function() {
300 equals(col.map(function(model){ return model.get('label'); }).join(' '), 'd c b a');
301 equals(col.any(function(model){ return model.id === 100; }), false);
302 equals(col.any(function(model){ return model.id === 0; }), true);
303 equals(col.indexOf(b), 2);
304 equals(col.size(), 4);
305 equals(col.rest().length, 3);
306 ok(!_.include(col.rest()), a);
307 ok(!_.include(col.rest()), d);
308 ok(!col.isEmpty());
309 ok(!_.include(col.without(d)), d);
310 equals(col.max(function(model){ return model.id; }).id, 3);
311 equals(col.min(function(model){ return model.id; }).id, 0);
312 same(col.chain()
313 .filter(function(o){ return o.id % 2 === 0; })
314 .map(function(o){ return o.id * 2; })
315 .value(),
316 [0, 4]);
317 });
318
319 test("Collection: reset", function() {
320 var resetCount = 0;
321 var models = col.models;
322 col.bind('reset', function() { resetCount += 1; });
323 col.reset([]);
324 equals(resetCount, 1);
325 equals(col.length, 0);
326 equals(col.last(), null);
327 col.reset(models);
328 equals(resetCount, 2);
329 equals(col.length, 4);
330 equals(col.last(), a);
331 col.reset(_.map(models, function(m){ return m.attributes; }));
332 equals(resetCount, 3);
333 equals(col.length, 4);
334 ok(col.last() !== a);
335 ok(_.isEqual(col.last().attributes, a.attributes));
336 });
337
338 test("Collection: trigger custom events on models", function() {
339 var fired = null;
340 a.bind("custom", function() { fired = true; });
341 a.trigger("custom");
342 equals(fired, true);
343 });
344
345});