PageRenderTime 66ms CodeModel.GetById 2ms app.highlight 56ms RepoModel.GetById 1ms app.codeStats 0ms

/node_modules/ender/test/node_modules/backbone/test/collection.js

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