PageRenderTime 616ms CodeModel.GetById 100ms app.highlight 395ms RepoModel.GetById 108ms app.codeStats 1ms

/test/backbone/test/collection.js

https://github.com/auggernaut/backbone-query-parameters
JavaScript | 989 lines | 883 code | 99 blank | 7 comment | 10 complexity | accd2096d61c3c0f36818bbbde37899b MD5 | raw file
  1$(document).ready(function() {
  2
  3  var a, b, c, d, e, col, otherCol;
  4
  5  module("Backbone.Collection", _.extend(new Environment, {
  6
  7    setup: function() {
  8      Environment.prototype.setup.apply(this, arguments);
  9
 10      a         = new Backbone.Model({id: 3, label: 'a'});
 11      b         = new Backbone.Model({id: 2, label: 'b'});
 12      c         = new Backbone.Model({id: 1, label: 'c'});
 13      d         = new Backbone.Model({id: 0, label: 'd'});
 14      e         = null;
 15      col       = new Backbone.Collection([a,b,c,d]);
 16      otherCol  = new Backbone.Collection();
 17    }
 18
 19  }));
 20
 21  test("new and sort", 9, function() {
 22    var counter = 0;
 23    col.on('sort', function(){ counter++; });
 24    equal(col.first(), a, "a should be first");
 25    equal(col.last(), d, "d should be last");
 26    col.comparator = function(a, b) {
 27      return a.id > b.id ? -1 : 1;
 28    };
 29    col.sort();
 30    equal(counter, 1);
 31    equal(col.first(), a, "a should be first");
 32    equal(col.last(), d, "d should be last");
 33    col.comparator = function(model) { return model.id; };
 34    col.sort();
 35    equal(counter, 2);
 36    equal(col.first(), d, "d should be first");
 37    equal(col.last(), a, "a should be last");
 38    equal(col.length, 4);
 39  });
 40
 41  test("String comparator.", 1, function() {
 42    var collection = new Backbone.Collection([
 43      {id: 3},
 44      {id: 1},
 45      {id: 2}
 46    ], {comparator: 'id'});
 47    deepEqual(collection.pluck('id'), [1, 2, 3]);
 48  });
 49
 50  test("new and parse", 3, function() {
 51    var Collection = Backbone.Collection.extend({
 52      parse : function(data) {
 53        return _.filter(data, function(datum) {
 54          return datum.a % 2 === 0;
 55        });
 56      }
 57    });
 58    var models = [{a: 1}, {a: 2}, {a: 3}, {a: 4}];
 59    var collection = new Collection(models, {parse: true});
 60    strictEqual(collection.length, 2);
 61    strictEqual(collection.first().get('a'), 2);
 62    strictEqual(collection.last().get('a'), 4);
 63  });
 64
 65  test("get", 5, function() {
 66    equal(col.get(0), d);
 67    equal(col.get(2), b);
 68    equal(col.get({id: 1}), c);
 69    equal(col.get(c.clone()), c);
 70    equal(col.get(col.first().cid), col.first());
 71  });
 72
 73  test("get with non-default ids", 4, function() {
 74    var col = new Backbone.Collection();
 75    var MongoModel = Backbone.Model.extend({
 76      idAttribute: '_id'
 77    });
 78    var model = new MongoModel({_id: 100});
 79    col.push(model);
 80    equal(col.get(100), model);
 81    model.set({_id: 101});
 82    equal(col.get(101), model);
 83
 84    var Col2 = Backbone.Collection.extend({ model: MongoModel });
 85    var col2 = new Col2();
 86    col2.push(model);
 87    equal(col2.get({_id: 101}), model);
 88    equal(col2.get(model.clone()), model);
 89  });
 90
 91  test("update index when id changes", 3, function() {
 92    var col = new Backbone.Collection();
 93    col.add([
 94      {id : 0, name : 'one'},
 95      {id : 1, name : 'two'}
 96    ]);
 97    var one = col.get(0);
 98    equal(one.get('name'), 'one');
 99    one.set({id : 101});
100    equal(col.get(0), null);
101    equal(col.get(101).get('name'), 'one');
102  });
103
104  test("at", 1, function() {
105    equal(col.at(2), c);
106  });
107
108  test("pluck", 1, function() {
109    equal(col.pluck('label').join(' '), 'a b c d');
110  });
111
112  test("add", 10, function() {
113    var added, opts, secondAdded;
114    added = opts = secondAdded = null;
115    e = new Backbone.Model({id: 10, label : 'e'});
116    otherCol.add(e);
117    otherCol.on('add', function() {
118      secondAdded = true;
119    });
120    col.on('add', function(model, collection, options){
121      added = model.get('label');
122      opts = options;
123    });
124    col.add(e, {amazing: true});
125    equal(added, 'e');
126    equal(col.length, 5);
127    equal(col.last(), e);
128    equal(otherCol.length, 1);
129    equal(secondAdded, null);
130    ok(opts.amazing);
131
132    var f = new Backbone.Model({id: 20, label : 'f'});
133    var g = new Backbone.Model({id: 21, label : 'g'});
134    var h = new Backbone.Model({id: 22, label : 'h'});
135    var atCol = new Backbone.Collection([f, g, h]);
136    equal(atCol.length, 3);
137    atCol.add(e, {at: 1});
138    equal(atCol.length, 4);
139    equal(atCol.at(1), e);
140    equal(atCol.last(), h);
141  });
142
143  test("add multiple models", 6, function() {
144    var col = new Backbone.Collection([{at: 0}, {at: 1}, {at: 9}]);
145    col.add([{at: 2}, {at: 3}, {at: 4}, {at: 5}, {at: 6}, {at: 7}, {at: 8}], {at: 2});
146    for (var i = 0; i <= 5; i++) {
147      equal(col.at(i).get('at'), i);
148    }
149  });
150
151  test("add; at should have preference over comparator", 1, function() {
152    var Col = Backbone.Collection.extend({
153      comparator: function(a,b) {
154        return a.id > b.id ? -1 : 1;
155      }
156    });
157
158    var col = new Col([{id: 2}, {id: 3}]);
159    col.add(new Backbone.Model({id: 1}), {at:   1});
160
161    equal(col.pluck('id').join(' '), '3 1 2');
162  });
163
164  test("can't add model to collection twice", function() {
165    var col = new Backbone.Collection([{id: 1}, {id: 2}, {id: 1}, {id: 2}, {id: 3}]);
166    equal(col.pluck('id').join(' '), '1 2 3');
167  });
168
169  test("can't add different model with same id to collection twice", 1, function() {
170    var col = new Backbone.Collection;
171    col.unshift({id: 101});
172    col.add({id: 101});
173    equal(col.length, 1);
174  });
175
176  test("merge in duplicate models with {merge: true}", 3, function() {
177    var col = new Backbone.Collection;
178    col.add([{id: 1, name: 'Moe'}, {id: 2, name: 'Curly'}, {id: 3, name: 'Larry'}]);
179    col.add({id: 1, name: 'Moses'});
180    equal(col.first().get('name'), 'Moe');
181    col.add({id: 1, name: 'Moses'}, {merge: true});
182    equal(col.first().get('name'), 'Moses');
183    col.add({id: 1, name: 'Tim'}, {merge: true, silent: true});
184    equal(col.first().get('name'), 'Tim');
185  });
186
187  test("add model to multiple collections", 10, function() {
188    var counter = 0;
189    var e = new Backbone.Model({id: 10, label : 'e'});
190    e.on('add', function(model, collection) {
191      counter++;
192      equal(e, model);
193      if (counter > 1) {
194        equal(collection, colF);
195      } else {
196        equal(collection, colE);
197      }
198    });
199    var colE = new Backbone.Collection([]);
200    colE.on('add', function(model, collection) {
201      equal(e, model);
202      equal(colE, collection);
203    });
204    var colF = new Backbone.Collection([]);
205    colF.on('add', function(model, collection) {
206      equal(e, model);
207      equal(colF, collection);
208    });
209    colE.add(e);
210    equal(e.collection, colE);
211    colF.add(e);
212    equal(e.collection, colE);
213  });
214
215  test("add model with parse", 1, function() {
216    var Model = Backbone.Model.extend({
217      parse: function(obj) {
218        obj.value += 1;
219        return obj;
220      }
221    });
222
223    var Col = Backbone.Collection.extend({model: Model});
224    var col = new Col;
225    col.add({value: 1}, {parse: true});
226    equal(col.at(0).get('value'), 2);
227  });
228
229  test("add model to collection with sort()-style comparator", 3, function() {
230    var col = new Backbone.Collection;
231    col.comparator = function(a, b) {
232      return a.get('name') < b.get('name') ? -1 : 1;
233    };
234    var tom = new Backbone.Model({name: 'Tom'});
235    var rob = new Backbone.Model({name: 'Rob'});
236    var tim = new Backbone.Model({name: 'Tim'});
237    col.add(tom);
238    col.add(rob);
239    col.add(tim);
240    equal(col.indexOf(rob), 0);
241    equal(col.indexOf(tim), 1);
242    equal(col.indexOf(tom), 2);
243  });
244
245  test("comparator that depends on `this`", 2, function() {
246    var col = new Backbone.Collection;
247    col.negative = function(num) {
248      return -num;
249    };
250    col.comparator = function(a) {
251      return this.negative(a.id);
252    };
253    col.add([{id: 1}, {id: 2}, {id: 3}]);
254    deepEqual(col.pluck('id'), [3, 2, 1]);
255    col.comparator = function(a, b) {
256      return this.negative(b.id) - this.negative(a.id);
257    };
258    col.sort();
259    deepEqual(col.pluck('id'), [1, 2, 3]);
260  });
261
262  test("remove", 5, function() {
263    var removed = null;
264    var otherRemoved = null;
265    col.on('remove', function(model, col, options) {
266      removed = model.get('label');
267      equal(options.index, 3);
268    });
269    otherCol.on('remove', function(model, col, options) {
270      otherRemoved = true;
271    });
272    col.remove(d);
273    equal(removed, 'd');
274    equal(col.length, 3);
275    equal(col.first(), a);
276    equal(otherRemoved, null);
277  });
278
279  test("shift and pop", 2, function() {
280    var col = new Backbone.Collection([{a: 'a'}, {b: 'b'}, {c: 'c'}]);
281    equal(col.shift().get('a'), 'a');
282    equal(col.pop().get('c'), 'c');
283  });
284
285  test("slice", 2, function() {
286    var col = new Backbone.Collection([{a: 'a'}, {b: 'b'}, {c: 'c'}]);
287    var array = col.slice(1, 3);
288    equal(array.length, 2);
289    equal(array[0].get('b'), 'b');
290  });
291
292  test("events are unbound on remove", 3, function() {
293    var counter = 0;
294    var dj = new Backbone.Model();
295    var emcees = new Backbone.Collection([dj]);
296    emcees.on('change', function(){ counter++; });
297    dj.set({name : 'Kool'});
298    equal(counter, 1);
299    emcees.reset([]);
300    equal(dj.collection, undefined);
301    dj.set({name : 'Shadow'});
302    equal(counter, 1);
303  });
304
305  test("remove in multiple collections", 7, function() {
306    var modelData = {
307      id : 5,
308      title : 'Othello'
309    };
310    var passed = false;
311    var e = new Backbone.Model(modelData);
312    var f = new Backbone.Model(modelData);
313    f.on('remove', function() {
314      passed = true;
315    });
316    var colE = new Backbone.Collection([e]);
317    var colF = new Backbone.Collection([f]);
318    ok(e != f);
319    ok(colE.length === 1);
320    ok(colF.length === 1);
321    colE.remove(e);
322    equal(passed, false);
323    ok(colE.length === 0);
324    colF.remove(e);
325    ok(colF.length === 0);
326    equal(passed, true);
327  });
328
329  test("remove same model in multiple collection", 16, function() {
330    var counter = 0;
331    var e = new Backbone.Model({id: 5, title: 'Othello'});
332    e.on('remove', function(model, collection) {
333      counter++;
334      equal(e, model);
335      if (counter > 1) {
336        equal(collection, colE);
337      } else {
338        equal(collection, colF);
339      }
340    });
341    var colE = new Backbone.Collection([e]);
342    colE.on('remove', function(model, collection) {
343      equal(e, model);
344      equal(colE, collection);
345    });
346    var colF = new Backbone.Collection([e]);
347    colF.on('remove', function(model, collection) {
348      equal(e, model);
349      equal(colF, collection);
350    });
351    equal(colE, e.collection);
352    colF.remove(e);
353    ok(colF.length === 0);
354    ok(colE.length === 1);
355    equal(counter, 1);
356    equal(colE, e.collection);
357    colE.remove(e);
358    equal(null, e.collection);
359    ok(colE.length === 0);
360    equal(counter, 2);
361  });
362
363  test("model destroy removes from all collections", 3, function() {
364    var e = new Backbone.Model({id: 5, title: 'Othello'});
365    e.sync = function(method, model, options) {
366      options.success(model, [], options);
367    };
368    var colE = new Backbone.Collection([e]);
369    var colF = new Backbone.Collection([e]);
370    e.destroy();
371    ok(colE.length === 0);
372    ok(colF.length === 0);
373    equal(undefined, e.collection);
374  });
375
376  test("Colllection: non-persisted model destroy removes from all collections", 3, function() {
377    var e = new Backbone.Model({title: 'Othello'});
378    e.sync = function(method, model, options) { throw "should not be called"; };
379    var colE = new Backbone.Collection([e]);
380    var colF = new Backbone.Collection([e]);
381    e.destroy();
382    ok(colE.length === 0);
383    ok(colF.length === 0);
384    equal(undefined, e.collection);
385  });
386
387  test("fetch", 4, function() {
388    var collection = new Backbone.Collection;
389    collection.url = '/test';
390    collection.fetch();
391    equal(this.syncArgs.method, 'read');
392    equal(this.syncArgs.model, collection);
393    equal(this.syncArgs.options.parse, true);
394
395    collection.fetch({parse: false});
396    equal(this.syncArgs.options.parse, false);
397  });
398
399  test("ensure fetch only parses once", 1, function() {
400    var collection = new Backbone.Collection;
401    var counter = 0;
402    collection.parse = function(models) {
403      counter++;
404      return models;
405    };
406    collection.url = '/test';
407    collection.fetch();
408    this.syncArgs.options.success([]);
409    equal(counter, 1);
410  });
411
412  test("create", 4, function() {
413    var collection = new Backbone.Collection;
414    collection.url = '/test';
415    var model = collection.create({label: 'f'}, {wait: true});
416    equal(this.syncArgs.method, 'create');
417    equal(this.syncArgs.model, model);
418    equal(model.get('label'), 'f');
419    equal(model.collection, collection);
420  });
421
422  test("create with validate:true enforces validation", 1, function() {
423    var ValidatingModel = Backbone.Model.extend({
424      validate: function(attrs) {
425        return "fail";
426      }
427    });
428    var ValidatingCollection = Backbone.Collection.extend({
429      model: ValidatingModel
430    });
431    var col = new ValidatingCollection();
432    equal(col.create({"foo":"bar"}, {validate:true}), false);
433  });
434
435  test("a failing create returns model with errors", function() {
436    var ValidatingModel = Backbone.Model.extend({
437      validate: function(attrs) {
438        return "fail";
439      }
440    });
441    var ValidatingCollection = Backbone.Collection.extend({
442      model: ValidatingModel
443    });
444    var col = new ValidatingCollection();
445    var m = col.create({"foo":"bar"});
446    equal(m.validationError, 'fail');
447    equal(col.length, 1);
448  });
449
450  test("initialize", 1, function() {
451    var Collection = Backbone.Collection.extend({
452      initialize: function() {
453        this.one = 1;
454      }
455    });
456    var coll = new Collection;
457    equal(coll.one, 1);
458  });
459
460  test("toJSON", 1, function() {
461    equal(JSON.stringify(col), '[{"id":3,"label":"a"},{"id":2,"label":"b"},{"id":1,"label":"c"},{"id":0,"label":"d"}]');
462  });
463
464  test("where", 6, function() {
465    var coll = new Backbone.Collection([
466      {a: 1},
467      {a: 1},
468      {a: 1, b: 2},
469      {a: 2, b: 2},
470      {a: 3}
471    ]);
472    equal(coll.where({a: 1}).length, 3);
473    equal(coll.where({a: 2}).length, 1);
474    equal(coll.where({a: 3}).length, 1);
475    equal(coll.where({b: 1}).length, 0);
476    equal(coll.where({b: 2}).length, 2);
477    equal(coll.where({a: 1, b: 2}).length, 1);
478  });
479
480  test("Underscore methods", 13, function() {
481    equal(col.map(function(model){ return model.get('label'); }).join(' '), 'a b c d');
482    equal(col.any(function(model){ return model.id === 100; }), false);
483    equal(col.any(function(model){ return model.id === 0; }), true);
484    equal(col.indexOf(b), 1);
485    equal(col.size(), 4);
486    equal(col.rest().length, 3);
487    ok(!_.include(col.rest()), a);
488    ok(!_.include(col.rest()), d);
489    ok(!col.isEmpty());
490    ok(!_.include(col.without(d)), d);
491    equal(col.max(function(model){ return model.id; }).id, 3);
492    equal(col.min(function(model){ return model.id; }).id, 0);
493    deepEqual(col.chain()
494            .filter(function(o){ return o.id % 2 === 0; })
495            .map(function(o){ return o.id * 2; })
496            .value(),
497         [4, 0]);
498  });
499
500  test("reset", 10, function() {
501    var resetCount = 0;
502    var models = col.models;
503    col.on('reset', function() { resetCount += 1; });
504    col.reset([]);
505    equal(resetCount, 1);
506    equal(col.length, 0);
507    equal(col.last(), null);
508    col.reset(models);
509    equal(resetCount, 2);
510    equal(col.length, 4);
511    equal(col.last(), d);
512    col.reset(_.map(models, function(m){ return m.attributes; }));
513    equal(resetCount, 3);
514    equal(col.length, 4);
515    ok(col.last() !== d);
516    ok(_.isEqual(col.last().attributes, d.attributes));
517  });
518
519  test("reset passes caller options", 3, function() {
520    var Model = Backbone.Model.extend({
521      initialize: function(attrs, options) {
522        this.model_parameter = options.model_parameter;
523      }
524    });
525    var col = new (Backbone.Collection.extend({ model: Model }))();
526    col.reset([{ astring: "green", anumber: 1 }, { astring: "blue", anumber: 2 }], { model_parameter: 'model parameter' });
527    equal(col.length, 2);
528    col.each(function(model) {
529      equal(model.model_parameter, 'model parameter');
530    });
531  });
532
533  test("trigger custom events on models", 1, function() {
534    var fired = null;
535    a.on("custom", function() { fired = true; });
536    a.trigger("custom");
537    equal(fired, true);
538  });
539
540  test("add does not alter arguments", 2, function(){
541    var attrs = {};
542    var models = [attrs];
543    new Backbone.Collection().add(models);
544    equal(models.length, 1);
545    ok(attrs === models[0]);
546  });
547
548  test("#714: access `model.collection` in a brand new model.", 2, function() {
549    var collection = new Backbone.Collection;
550    collection.url = '/test';
551    var Model = Backbone.Model.extend({
552      set: function(attrs) {
553        equal(attrs.prop, 'value');
554        equal(this.collection, collection);
555        return this;
556      }
557    });
558    collection.model = Model;
559    collection.create({prop: 'value'});
560  });
561
562  test("#574, remove its own reference to the .models array.", 2, function() {
563    var col = new Backbone.Collection([
564      {id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}, {id: 6}
565    ]);
566    equal(col.length, 6);
567    col.remove(col.models);
568    equal(col.length, 0);
569  });
570
571  test("#861, adding models to a collection which do not pass validation, with validate:true", function() {
572      var Model = Backbone.Model.extend({
573        validate: function(attrs) {
574          if (attrs.id == 3) return "id can't be 3";
575        }
576      });
577
578      var Collection = Backbone.Collection.extend({
579        model: Model
580      });
581
582      var collection = new Collection;
583      collection.on("error", function() { ok(true); });
584
585      collection.add([{id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}, {id: 6}], {validate:true});
586      deepEqual(collection.pluck('id'), [1, 2, 4, 5, 6]);
587  });
588
589  test("Invalid models are discarded with validate:true.", 5, function() {
590    var collection = new Backbone.Collection;
591    collection.on('test', function() { ok(true); });
592    collection.model = Backbone.Model.extend({
593      validate: function(attrs){ if (!attrs.valid) return 'invalid'; }
594    });
595    var model = new collection.model({id: 1, valid: true});
596    collection.add([model, {id: 2}], {validate:true});
597    model.trigger('test');
598    ok(collection.get(model.cid));
599    ok(collection.get(1));
600    ok(!collection.get(2));
601    equal(collection.length, 1);
602  });
603
604  test("multiple copies of the same model", 3, function() {
605    var col = new Backbone.Collection();
606    var model = new Backbone.Model();
607    col.add([model, model]);
608    equal(col.length, 1);
609    col.add([{id: 1}, {id: 1}]);
610    equal(col.length, 2);
611    equal(col.last().id, 1);
612  });
613
614  test("#964 - collection.get return inconsistent", 2, function() {
615    var c = new Backbone.Collection();
616    ok(c.get(null) === undefined);
617    ok(c.get() === undefined);
618  });
619
620  test("#1112 - passing options.model sets collection.model", 2, function() {
621    var Model = Backbone.Model.extend({});
622    var c = new Backbone.Collection([{id: 1}], {model: Model});
623    ok(c.model === Model);
624    ok(c.at(0) instanceof Model);
625  });
626
627  test("null and undefined are invalid ids.", 2, function() {
628    var model = new Backbone.Model({id: 1});
629    var collection = new Backbone.Collection([model]);
630    model.set({id: null});
631    ok(!collection.get('null'));
632    model.set({id: 1});
633    model.set({id: undefined});
634    ok(!collection.get('undefined'));
635  });
636
637  test("falsy comparator", 4, function(){
638    var Col = Backbone.Collection.extend({
639      comparator: function(model){ return model.id; }
640    });
641    var col = new Col();
642    var colFalse = new Col(null, {comparator: false});
643    var colNull = new Col(null, {comparator: null});
644    var colUndefined = new Col(null, {comparator: undefined});
645    ok(col.comparator);
646    ok(!colFalse.comparator);
647    ok(!colNull.comparator);
648    ok(colUndefined.comparator);
649  });
650
651  test("#1355 - `options` is passed to success callbacks", 2, function(){
652    var m = new Backbone.Model({x:1});
653    var col = new Backbone.Collection();
654    var opts = {
655      success: function(collection, resp, options){
656        ok(options);
657      }
658    };
659    col.sync = m.sync = function( method, collection, options ){
660      options.success(collection, [], options);
661    };
662    col.fetch(opts);
663    col.create(m, opts);
664  });
665
666  test("#1412 - Trigger 'sync' event.", 2, function() {
667    var collection = new Backbone.Collection;
668    collection.url = '/test';
669    collection.on('sync', function() { ok(true); });
670    Backbone.ajax = function(settings){ settings.success(); };
671    collection.fetch();
672    collection.create({id: 1});
673  });
674
675  test("#1447 - create with wait adds model.", 1, function() {
676    var collection = new Backbone.Collection;
677    var model = new Backbone.Model;
678    model.sync = function(method, model, options){
679      options.success(model, [], options);
680    };
681    collection.on('add', function(){ ok(true); });
682    collection.create(model, {wait: true});
683  });
684
685  test("#1448 - add sorts collection after merge.", 1, function() {
686    var collection = new Backbone.Collection([
687      {id: 1, x: 1},
688      {id: 2, x: 2}
689    ]);
690    collection.comparator = function(model){ return model.get('x'); };
691    collection.add({id: 1, x: 3}, {merge: true});
692    deepEqual(collection.pluck('id'), [2, 1]);
693  });
694
695  test("#1655 - groupBy can be used with a string argument.", 3, function() {
696    var collection = new Backbone.Collection([{x: 1}, {x: 2}]);
697    var grouped = collection.groupBy('x');
698    strictEqual(_.keys(grouped).length, 2);
699    strictEqual(grouped[1][0].get('x'), 1);
700    strictEqual(grouped[2][0].get('x'), 2);
701  });
702
703  test("#1655 - sortBy can be used with a string argument.", 1, function() {
704    var collection = new Backbone.Collection([{x: 3}, {x: 1}, {x: 2}]);
705    var values = _.map(collection.sortBy('x'), function(model) {
706      return model.get('x');
707    });
708    deepEqual(values, [1, 2, 3]);
709  });
710
711  test("#1604 - Removal during iteration.", 0, function() {
712    var collection = new Backbone.Collection([{}, {}]);
713    collection.on('add', function() {
714      collection.at(0).destroy();
715    });
716    collection.add({}, {at: 0});
717  });
718
719  test("#1638 - `sort` during `add` triggers correctly.", function() {
720    var collection = new Backbone.Collection;
721    collection.comparator = function(model) { return model.get('x'); };
722    var added = [];
723    collection.on('add', function(model) {
724      model.set({x: 3});
725      collection.sort();
726      added.push(model.id);
727    });
728    collection.add([{id: 1, x: 1}, {id: 2, x: 2}]);
729    deepEqual(added, [1, 2]);
730  });
731
732  test("fetch parses models by default", 1, function() {
733    var model = {};
734    var Collection = Backbone.Collection.extend({
735      url: 'test',
736      model: Backbone.Model.extend({
737        parse: function(resp) {
738          strictEqual(resp, model);
739        }
740      })
741    });
742    new Collection().fetch();
743    this.ajaxSettings.success([model]);
744  });
745
746  test("`sort` shouldn't always fire on `add`", 1, function() {
747    var c = new Backbone.Collection([{id: 1}, {id: 2}, {id: 3}], {
748      comparator: 'id'
749    });
750    c.sort = function(){ ok(true); };
751    c.add([]);
752    c.add({id: 1});
753    c.add([{id: 2}, {id: 3}]);
754    c.add({id: 4});
755  });
756
757  test("#1407 parse option on constructor parses collection and models", 2, function() {
758    var model = {
759      namespace : [{id: 1}, {id:2}]
760    };
761    var Collection = Backbone.Collection.extend({
762      model: Backbone.Model.extend({
763        parse: function(model) {
764          model.name = 'test';
765          return model;
766        }
767      }),
768      parse: function(model) {
769        return model.namespace;
770      }
771    });
772    var c = new Collection(model, {parse:true});
773
774    equal(c.length, 2);
775    equal(c.at(0).get('name'), 'test');
776  });
777
778  test("#1407 parse option on reset parses collection and models", 2, function() {
779    var model = {
780      namespace : [{id: 1}, {id:2}]
781    };
782    var Collection = Backbone.Collection.extend({
783      model: Backbone.Model.extend({
784        parse: function(model) {
785          model.name = 'test';
786          return model;
787        }
788      }),
789      parse: function(model) {
790        return model.namespace;
791      }
792    });
793    var c = new Collection();
794        c.reset(model, {parse:true});
795
796    equal(c.length, 2);
797    equal(c.at(0).get('name'), 'test');
798  });
799
800
801  test("Reset includes previous models in triggered event.", 1, function() {
802    var model = new Backbone.Model();
803    var collection = new Backbone.Collection([model])
804    .on('reset', function(collection, options) {
805      deepEqual(options.previousModels, [model]);
806    });
807    collection.reset([]);
808  });
809
810  test("update", function() {
811    var m1 = new Backbone.Model();
812    var m2 = new Backbone.Model({id: 2});
813    var m3 = new Backbone.Model();
814    var c = new Backbone.Collection([m1, m2]);
815
816    // Test add/change/remove events
817    c.on('add', function(model) {
818      strictEqual(model, m3);
819    });
820    c.on('change', function(model) {
821      strictEqual(model, m2);
822    });
823    c.on('remove', function(model) {
824      strictEqual(model, m1);
825    });
826
827    // remove: false doesn't remove any models
828    c.update([], {remove: false});
829    strictEqual(c.length, 2);
830
831    // add: false doesn't add any models
832    c.update([m1, m2, m3], {add: false});
833    strictEqual(c.length, 2);
834
835    // merge: false doesn't change any models
836    c.update([m1, {id: 2, a: 1}], {merge: false});
837    strictEqual(m2.get('a'), void 0);
838
839    // add: false, remove: false only merges existing models
840    c.update([m1, {id: 2, a: 0}, m3, {id: 4}], {add: false, remove: false});
841    strictEqual(c.length, 2);
842    strictEqual(m2.get('a'), 0);
843
844    // default options add/remove/merge as appropriate
845    c.update([{id: 2, a: 1}, m3]);
846    strictEqual(c.length, 2);
847    strictEqual(m2.get('a'), 1);
848
849    // Test removing models not passing an argument
850    c.off('remove').on('remove', function(model) {
851      ok(model === m2 || model === m3);
852    });
853    c.update([]);
854    strictEqual(c.length, 0);
855  });
856
857  test("update with only cids", 3, function() {
858    var m1 = new Backbone.Model;
859    var m2 = new Backbone.Model;
860    var c = new Backbone.Collection;
861    c.update([m1, m2]);
862    equal(c.length, 2);
863    c.update([m1]);
864    equal(c.length, 1);
865    c.update([m1, m1, m1, m2, m2], {remove: false});
866    equal(c.length, 2);
867  });
868
869  test("update with only idAttribute", 3, function() {
870    var m1 = { _id: 1 };
871    var m2 = { _id: 2 };
872    var col = Backbone.Collection.extend({
873      model: Backbone.Model.extend({
874        idAttribute: '_id'
875      })
876    });
877    var c = new col;
878    c.update([m1, m2]);
879    equal(c.length, 2);
880    c.update([m1]);
881    equal(c.length, 1);
882    c.update([m1, m1, m1, m2, m2], {remove: false});
883    equal(c.length, 2);
884  });
885
886  test("update + merge with default values defined", function() {
887    var Model = Backbone.Model.extend({
888      defaults: {
889        key: 'value'
890      }
891    });
892    var m = new Model({id: 1});
893    var col = new Backbone.Collection([m], {model: Model});
894    equal(col.first().get('key'), 'value');
895
896    col.update({id: 1, key: 'other'});
897    equal(col.first().get('key'), 'other');
898
899    col.update({id: 1, other: 'value'});
900    equal(col.first().get('key'), 'other');
901    equal(col.length, 1);
902  });
903
904  test("#1894 - Push should not trigger a sort", 0, function() {
905    var Collection = Backbone.Collection.extend({
906      comparator: 'id',
907      sort: function() {
908        ok(false);
909      }
910    });
911    new Collection().push({id: 1});
912  });
913
914  test("`update` with non-normal id", function() {
915    var Collection = Backbone.Collection.extend({
916      model: Backbone.Model.extend({idAttribute: '_id'})
917    });
918    var collection = new Collection({_id: 1});
919    collection.update([{_id: 1, a: 1}], {add: false});
920    equal(collection.first().get('a'), 1);
921  });
922
923  test("#1894 - `sort` can optionally be turned off", 0, function() {
924    var Collection = Backbone.Collection.extend({
925      comparator: 'id',
926      sort: function() { ok(true); }
927    });
928    new Collection().add({id: 1}, {sort: false});
929  });
930
931  test("#1915 - `parse` data in the right order in `update`", function() {
932    var collection = new (Backbone.Collection.extend({
933      parse: function (data) {
934        strictEqual(data.status, 'ok');
935        return data.data;
936      }
937    }));
938    var res = {status: 'ok', data:[{id: 1}]};
939    collection.update(res, {parse: true});
940  });
941
942  asyncTest("#1939 - `parse` is passed `options`", 1, function () {
943    var collection = new (Backbone.Collection.extend({
944      url: '/',
945      parse: function (data, options) {
946        strictEqual(options.xhr.someHeader, 'headerValue');
947        return data;
948      }
949    }));
950    var ajax = Backbone.ajax;
951    Backbone.ajax = function (params) {
952      _.defer(params.success);
953      return {someHeader: 'headerValue'};
954    };
955    collection.fetch({
956      success: function () { start(); }
957    });
958    Backbone.ajax = ajax;
959  });
960
961  test("`add` only `sort`s when necessary", 2, function () {
962    var collection = new (Backbone.Collection.extend({
963      comparator: 'a'
964    }))([{id: 1}, {id: 2}, {id: 3}]);
965    collection.on('sort', function () { ok(true); });
966    collection.add({id: 4}); // do sort, new model
967    collection.add({id: 1, a: 1}, {merge: true}); // do sort, comparator change
968    collection.add({id: 1, b: 1}, {merge: true}); // don't sort, no comparator change
969    collection.add({id: 1, a: 1}, {merge: true}); // don't sort, no comparator change
970    collection.add(collection.models); // don't sort, nothing new
971    collection.add(collection.models, {merge: true}); // don't sort
972  });
973
974  test("`add` only `sort`s when necessary with comparator function", 3, function () {
975    var collection = new (Backbone.Collection.extend({
976      comparator: function(a, b) {
977        a.get('a') > b.get('a') ? 1 : (a.get('a') < b.get('a') ? -1 : 0);
978      }
979    }))([{id: 1}, {id: 2}, {id: 3}]);
980    collection.on('sort', function () { ok(true); });
981    collection.add({id: 4}); // do sort, new model
982    collection.add({id: 1, a: 1}, {merge: true}); // do sort, model change
983    collection.add({id: 1, b: 1}, {merge: true}); // do sort, model change
984    collection.add({id: 1, a: 1}, {merge: true}); // don't sort, no model change
985    collection.add(collection.models); // don't sort, nothing new
986    collection.add(collection.models, {merge: true}); // don't sort
987  });
988
989});