PageRenderTime 4ms CodeModel.GetById 426ms app.highlight 321ms RepoModel.GetById 363ms app.codeStats 0ms

/test/collection.js

https://github.com/jetleigh/backbone
JavaScript | 451 lines | 408 code | 42 blank | 1 comment | 19 complexity | 2c0c675d306c4a5b4614e489a4d1200f 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(a, b) {
 23      return a.id > b.id ? -1 : 1;
 24    };
 25    col.sort();
 26    equals(col.first(), a, "a should be first");
 27    equals(col.last(), d, "d should be last");
 28    col.comparator = function(model) { return model.id; };
 29    col.sort();
 30    equals(col.first(), d, "d should be first");
 31    equals(col.last(), a, "a should be last");
 32    equals(col.length, 4);
 33  });
 34
 35  test("Collection: get, getByCid", function() {
 36    equals(col.get(0), d);
 37    equals(col.get(2), b);
 38    equals(col.getByCid(col.first().cid), col.first());
 39  });
 40
 41  test("Collection: get with non-default ids", function() {
 42    var col = new Backbone.Collection();
 43    var MongoModel = Backbone.Model.extend({
 44      idAttribute: '_id'
 45    });
 46    var model = new MongoModel({_id: 100});
 47    col.add(model);
 48    equals(col.get(100), model);
 49    model.set({_id: 101});
 50    equals(col.get(101), model);
 51  });
 52
 53  test("Collection: add model with attributes modified by set", function() {
 54    var CustomSetModel = Backbone.Model.extend({
 55      defaults: {
 56        number_as_string: null //presence of defaults forces extend
 57      },
 58
 59      validate: function (attributes) {
 60        if (!_.isString(attributes.num_as_string)) {
 61          return 'fail';
 62        }
 63      },
 64
 65      set: function (attributes, options) {
 66        if (attributes.num_as_string) {
 67          attributes.num_as_string = attributes.num_as_string.toString();
 68        }
 69        Backbone.Model.prototype.set.call(this, attributes, options);
 70      }
 71    });
 72
 73    var CustomSetCollection = Backbone.Collection.extend({
 74      model: CustomSetModel
 75    });
 76    var col = new CustomSetCollection([{ num_as_string: 2 }]);
 77    equals(col.length, 1);
 78  });
 79
 80  test("Collection: update index when id changes", function() {
 81    var col = new Backbone.Collection();
 82    col.add([
 83      {id : 0, name : 'one'},
 84      {id : 1, name : 'two'}
 85    ]);
 86    var one = col.get(0);
 87    equals(one.get('name'), 'one');
 88    one.set({id : 101});
 89    equals(col.get(0), null);
 90    equals(col.get(101).get('name'), 'one');
 91  });
 92
 93  test("Collection: at", function() {
 94    equals(col.at(2), b);
 95  });
 96
 97  test("Collection: pluck", function() {
 98    equals(col.pluck('label').join(' '), 'd c b a');
 99  });
100
101  test("Collection: add", function() {
102    var added = opts = secondAdded = null;
103    e = new Backbone.Model({id: 10, label : 'e'});
104    otherCol.add(e);
105    otherCol.bind('add', function() {
106      secondAdded = true;
107    });
108    col.bind('add', function(model, collection, options){
109      added = model.get('label');
110      opts = options;
111    });
112    col.add(e, {amazing: true});
113    equals(added, 'e');
114    equals(col.length, 5);
115    equals(col.last(), e);
116    equals(otherCol.length, 1);
117    equals(secondAdded, null);
118    ok(opts.amazing);
119
120    var f = new Backbone.Model({id: 20, label : 'f'});
121    var g = new Backbone.Model({id: 21, label : 'g'});
122    var h = new Backbone.Model({id: 22, label : 'h'});
123    var atCol = new Backbone.Collection([f, g, h]);
124    equals(atCol.length, 3);
125    atCol.add(e, {at: 1});
126    equals(atCol.length, 4);
127    equals(atCol.at(1), e);
128    equals(atCol.last(), h);
129  });
130
131  test("Collection: add multiple models", function() {
132    var col = new Backbone.Collection([{at: 0}, {at: 1}, {at: 9}]);
133    col.add([{at: 2}, {at: 3}, {at: 4}, {at: 5}, {at: 6}, {at: 7}, {at: 8}], {at: 2});
134    for (var i = 0; i <= 5; i++) {
135      equals(col.at(i).get('at'), i);
136    }
137  });
138
139  test("Collection: can't add model to collection twice", function() {
140    try {
141      // no id, same cid
142      var a2 = new Backbone.Model({label: a.label});
143      a2.cid = a.cid;
144      col.add(a2);
145      ok(false, "duplicate; expected add to fail");
146    } catch (e) {
147      equals(e.message, "Can't add the same model to a set twice");
148    }
149  });
150
151  test("Collection: can't add different model with same id to collection twice", function() {
152    var col = new Backbone.Collection;
153    try {
154      col.add({id: 101});
155      col.add({id: 101});
156      ok(false, "duplicate; expected add to fail");
157    } catch (e) {
158      equals(e.message, "Can't add the same model to a set twice");
159    }
160  });
161
162  test("Collection: add model to multiple collections", function() {
163    var counter = 0;
164    var e = new Backbone.Model({id: 10, label : 'e'});
165    e.bind('add', function(model, collection) {
166      counter++;
167      equals(e, model);
168      if (counter > 1) {
169        equals(collection, colF);
170      } else {
171        equals(collection, colE);
172      }
173    });
174    var colE = new Backbone.Collection([]);
175    colE.bind('add', function(model, collection) {
176      equals(e, model);
177      equals(colE, collection);
178    });
179    var colF = new Backbone.Collection([]);
180    colF.bind('add', function(model, collection) {
181      equals(e, model);
182      equals(colF, collection);
183    });
184    colE.add(e);
185    equals(e.collection, colE);
186    colF.add(e);
187    equals(e.collection, colE);
188  });
189
190  test("Collection: add model with parse", function() {
191    var Model = Backbone.Model.extend({
192      parse: function(obj) {
193        obj.value += 1;
194        return obj;
195      }
196    });
197
198    var Col = Backbone.Collection.extend({model: Model});
199    var col = new Col;
200    col.add({value: 1}, {parse: true});
201    equals(col.at(0).get('value'), 2);
202  });
203
204  test("Collection: add model to collection with sort()-style comparator", function() {
205    var col = new Backbone.Collection;
206    col.comparator = function(a, b) {
207      return a.get('name') < b.get('name') ? -1 : 1;
208    };
209    var tom = new Backbone.Model({name: 'Tom'});
210    var rob = new Backbone.Model({name: 'Rob'});
211    var tim = new Backbone.Model({name: 'Tim'});
212    col.add(tom);
213    col.add(rob);
214    col.add(tim);
215    equals(col.indexOf(rob), 0);
216    equals(col.indexOf(tim), 1);
217    equals(col.indexOf(tom), 2);
218  });
219
220  test("Collection: comparator that depends on `this`", function() {
221    var col = new Backbone.Collection;
222    col.negative = function(num) {
223      return -num;
224    };
225    col.comparator = function(a) {
226      return this.negative(a.id);
227    };
228    col.add([{id: 1}, {id: 2}, {id: 3}]);
229    equals(col.pluck('id').join(' '), '3 2 1');
230  });
231
232  test("Collection: remove", function() {
233    var removed = otherRemoved = null;
234    col.bind('remove', function(model){ removed = model.get('label'); });
235    otherCol.bind('remove', function(){ otherRemoved = true; });
236    col.remove(e);
237    equals(removed, 'e');
238    equals(col.length, 4);
239    equals(col.first(), d);
240    equals(otherRemoved, null);
241  });
242
243  test("Collection: events are unbound on remove", function() {
244    var counter = 0;
245    var dj = new Backbone.Model();
246    var emcees = new Backbone.Collection([dj]);
247    emcees.bind('change', function(){ counter++; });
248    dj.set({name : 'Kool'});
249    equals(counter, 1);
250    emcees.reset([]);
251    equals(dj.collection, undefined);
252    dj.set({name : 'Shadow'});
253    equals(counter, 1);
254  });
255
256  test("Collection: remove in multiple collections", function() {
257    var modelData = {
258      id : 5,
259      title : 'Othello'
260    };
261    var passed = false;
262    var e = new Backbone.Model(modelData);
263    var f = new Backbone.Model(modelData);
264    f.bind('remove', function() {
265      passed = true;
266    });
267    var colE = new Backbone.Collection([e]);
268    var colF = new Backbone.Collection([f]);
269    ok(e != f);
270    ok(colE.length == 1);
271    ok(colF.length == 1);
272    colE.remove(e);
273    equals(passed, false);
274    ok(colE.length == 0);
275    colF.remove(e);
276    ok(colF.length == 0);
277    equals(passed, true);
278  });
279
280  test("Collection: remove same model in multiple collection", function() {
281    var counter = 0;
282    var e = new Backbone.Model({id: 5, title: 'Othello'});
283    e.bind('remove', function(model, collection) {
284      counter++;
285      equals(e, model);
286      if (counter > 1) {
287        equals(collection, colE);
288      } else {
289        equals(collection, colF);
290      }
291    });
292    var colE = new Backbone.Collection([e]);
293    colE.bind('remove', function(model, collection) {
294      equals(e, model);
295      equals(colE, collection);
296    });
297    var colF = new Backbone.Collection([e]);
298    colF.bind('remove', function(model, collection) {
299      equals(e, model);
300      equals(colF, collection);
301    });
302    equals(colE, e.collection);
303    colF.remove(e);
304    ok(colF.length == 0);
305    ok(colE.length == 1);
306    equals(counter, 1);
307    equals(colE, e.collection);
308    colE.remove(e);
309    equals(null, e.collection);
310    ok(colE.length == 0);
311    equals(counter, 2);
312  });
313
314  test("Collection: model destroy removes from all collections", function() {
315    var e = new Backbone.Model({id: 5, title: 'Othello'});
316    e.sync = function(method, model, options) { options.success({}); };
317    var colE = new Backbone.Collection([e]);
318    var colF = new Backbone.Collection([e]);
319    e.destroy();
320    ok(colE.length == 0);
321    ok(colF.length == 0);
322    equals(undefined, e.collection);
323  });
324
325  test("Colllection: non-persisted model destroy removes from all collections", function() {
326    var e = new Backbone.Model({title: 'Othello'});
327    e.sync = function(method, model, options) { throw "should not be called"; };
328    var colE = new Backbone.Collection([e]);
329    var colF = new Backbone.Collection([e]);
330    e.destroy();
331    ok(colE.length == 0);
332    ok(colF.length == 0);
333    equals(undefined, e.collection);
334  });
335
336  test("Collection: fetch", function() {
337    col.fetch();
338    equals(lastRequest[0], 'read');
339    equals(lastRequest[1], col);
340    equals(lastRequest[2].parse, true);
341
342    col.fetch({parse: false});
343    equals(lastRequest[2].parse, false);
344  });
345
346  test("Collection: create", function() {
347    var model = col.create({label: 'f'});
348    equals(lastRequest[0], 'create');
349    equals(lastRequest[1], model);
350    equals(model.get('label'), 'f');
351    equals(model.collection, col);
352  });
353
354  test("Collection: create enforces validation", function() {
355    var ValidatingModel = Backbone.Model.extend({
356      validate: function(attrs) {
357        return "fail";
358      }
359    });
360    var ValidatingCollection = Backbone.Collection.extend({
361      model: ValidatingModel
362    });
363    var col = new ValidatingCollection();
364    equals(col.create({"foo":"bar"}),false);
365  });
366
367  test("Collection: a failing create runs the error callback", function() {
368    var ValidatingModel = Backbone.Model.extend({
369      validate: function(attrs) {
370        return "fail";
371      }
372    });
373    var ValidatingCollection = Backbone.Collection.extend({
374      model: ValidatingModel
375    });
376    var flag = false;
377    var callback = function(model, error) { flag = true; };
378    var col = new ValidatingCollection();
379    col.create({"foo":"bar"}, { error: callback });
380    equals(flag, true);
381  });
382
383  test("collection: initialize", function() {
384    var Collection = Backbone.Collection.extend({
385      initialize: function() {
386        this.one = 1;
387      }
388    });
389    var coll = new Collection;
390    equals(coll.one, 1);
391  });
392
393  test("Collection: toJSON", function() {
394    equals(JSON.stringify(col), '[{"id":0,"label":"d"},{"id":1,"label":"c"},{"id":2,"label":"b"},{"id":3,"label":"a"}]');
395  });
396
397  test("Collection: Underscore methods", function() {
398    equals(col.map(function(model){ return model.get('label'); }).join(' '), 'd c b a');
399    equals(col.any(function(model){ return model.id === 100; }), false);
400    equals(col.any(function(model){ return model.id === 0; }), true);
401    equals(col.indexOf(b), 2);
402    equals(col.size(), 4);
403    equals(col.rest().length, 3);
404    ok(!_.include(col.rest()), a);
405    ok(!_.include(col.rest()), d);
406    ok(!col.isEmpty());
407    ok(!_.include(col.without(d)), d);
408    equals(col.max(function(model){ return model.id; }).id, 3);
409    equals(col.min(function(model){ return model.id; }).id, 0);
410    same(col.chain()
411            .filter(function(o){ return o.id % 2 === 0; })
412            .map(function(o){ return o.id * 2; })
413            .value(),
414         [0, 4]);
415  });
416
417  test("Collection: reset", function() {
418    var resetCount = 0;
419    var models = col.models;
420    col.bind('reset', function() { resetCount += 1; });
421    col.reset([]);
422    equals(resetCount, 1);
423    equals(col.length, 0);
424    equals(col.last(), null);
425    col.reset(models);
426    equals(resetCount, 2);
427    equals(col.length, 4);
428    equals(col.last(), a);
429    col.reset(_.map(models, function(m){ return m.attributes; }));
430    equals(resetCount, 3);
431    equals(col.length, 4);
432    ok(col.last() !== a);
433    ok(_.isEqual(col.last().attributes, a.attributes));
434  });
435
436  test("Collection: trigger custom events on models", function() {
437    var fired = null;
438    a.bind("custom", function() { fired = true; });
439    a.trigger("custom");
440    equals(fired, true);
441  });
442
443  test("Collection: add does not alter arguments", function(){
444    var attrs = {};
445    var models = [attrs];
446    new Backbone.Collection().add(models);
447    equal(models.length, 1);
448    ok(attrs === models[0]);
449  });
450
451});