Built motion from commit 1038d87.|0.0.141
[motion.git] / public / bower_components / lodash / vendor / backbone / test / collection.js
1 (function() {
2
3   var a, b, c, d, e, col, otherCol;
4
5   QUnit.module('Backbone.Collection', {
6
7     beforeEach: function(assert) {
8       a         = new Backbone.Model({id: 3, label: 'a'});
9       b         = new Backbone.Model({id: 2, label: 'b'});
10       c         = new Backbone.Model({id: 1, label: 'c'});
11       d         = new Backbone.Model({id: 0, label: 'd'});
12       e         = null;
13       col       = new Backbone.Collection([a, b, c, d]);
14       otherCol  = new Backbone.Collection();
15     }
16
17   });
18
19   QUnit.test('new and sort', function(assert) {
20     assert.expect(6);
21     var counter = 0;
22     col.on('sort', function(){ counter++; });
23     assert.deepEqual(col.pluck('label'), ['a', 'b', 'c', 'd']);
24     col.comparator = function(m1, m2) {
25       return m1.id > m2.id ? -1 : 1;
26     };
27     col.sort();
28     assert.equal(counter, 1);
29     assert.deepEqual(col.pluck('label'), ['a', 'b', 'c', 'd']);
30     col.comparator = function(model) { return model.id; };
31     col.sort();
32     assert.equal(counter, 2);
33     assert.deepEqual(col.pluck('label'), ['d', 'c', 'b', 'a']);
34     assert.equal(col.length, 4);
35   });
36
37   QUnit.test('String comparator.', function(assert) {
38     assert.expect(1);
39     var collection = new Backbone.Collection([
40       {id: 3},
41       {id: 1},
42       {id: 2}
43     ], {comparator: 'id'});
44     assert.deepEqual(collection.pluck('id'), [1, 2, 3]);
45   });
46
47   QUnit.test('new and parse', function(assert) {
48     assert.expect(3);
49     var Collection = Backbone.Collection.extend({
50       parse: function(data) {
51         return _.filter(data, function(datum) {
52           return datum.a % 2 === 0;
53         });
54       }
55     });
56     var models = [{a: 1}, {a: 2}, {a: 3}, {a: 4}];
57     var collection = new Collection(models, {parse: true});
58     assert.strictEqual(collection.length, 2);
59     assert.strictEqual(collection.first().get('a'), 2);
60     assert.strictEqual(collection.last().get('a'), 4);
61   });
62
63   QUnit.test('clone preserves model and comparator', function(assert) {
64     assert.expect(3);
65     var Model = Backbone.Model.extend();
66     var comparator = function(model){ return model.id; };
67
68     var collection = new Backbone.Collection([{id: 1}], {
69       model: Model,
70       comparator: comparator
71     }).clone();
72     collection.add({id: 2});
73     assert.ok(collection.at(0) instanceof Model);
74     assert.ok(collection.at(1) instanceof Model);
75     assert.strictEqual(collection.comparator, comparator);
76   });
77
78   QUnit.test('get', function(assert) {
79     assert.expect(6);
80     assert.equal(col.get(0), d);
81     assert.equal(col.get(d.clone()), d);
82     assert.equal(col.get(2), b);
83     assert.equal(col.get({id: 1}), c);
84     assert.equal(col.get(c.clone()), c);
85     assert.equal(col.get(col.first().cid), col.first());
86   });
87
88   QUnit.test('get with non-default ids', function(assert) {
89     assert.expect(5);
90     var MongoModel = Backbone.Model.extend({idAttribute: '_id'});
91     var model = new MongoModel({_id: 100});
92     var collection = new Backbone.Collection([model], {model: MongoModel});
93     assert.equal(collection.get(100), model);
94     assert.equal(collection.get(model.cid), model);
95     assert.equal(collection.get(model), model);
96     assert.equal(collection.get(101), void 0);
97
98     var collection2 = new Backbone.Collection();
99     collection2.model = MongoModel;
100     collection2.add(model.attributes);
101     assert.equal(collection2.get(model.clone()), collection2.first());
102   });
103
104   QUnit.test('get with "undefined" id', function(assert) {
105     var collection = new Backbone.Collection([{id: 1}, {id: 'undefined'}]);
106     assert.equal(collection.get(1).id, 1);
107   });
108
109   QUnit.test('has', function(assert) {
110     assert.expect(15);
111     assert.ok(col.has(a));
112     assert.ok(col.has(b));
113     assert.ok(col.has(c));
114     assert.ok(col.has(d));
115     assert.ok(col.has(a.id));
116     assert.ok(col.has(b.id));
117     assert.ok(col.has(c.id));
118     assert.ok(col.has(d.id));
119     assert.ok(col.has(a.cid));
120     assert.ok(col.has(b.cid));
121     assert.ok(col.has(c.cid));
122     assert.ok(col.has(d.cid));
123     var outsider = new Backbone.Model({id: 4});
124     assert.notOk(col.has(outsider));
125     assert.notOk(col.has(outsider.id));
126     assert.notOk(col.has(outsider.cid));
127   });
128
129   QUnit.test('update index when id changes', function(assert) {
130     assert.expect(4);
131     var collection = new Backbone.Collection();
132     collection.add([
133       {id: 0, name: 'one'},
134       {id: 1, name: 'two'}
135     ]);
136     var one = collection.get(0);
137     assert.equal(one.get('name'), 'one');
138     collection.on('change:name', function(model) { assert.ok(this.get(model)); });
139     one.set({name: 'dalmatians', id: 101});
140     assert.equal(collection.get(0), null);
141     assert.equal(collection.get(101).get('name'), 'dalmatians');
142   });
143
144   QUnit.test('at', function(assert) {
145     assert.expect(2);
146     assert.equal(col.at(2), c);
147     assert.equal(col.at(-2), c);
148   });
149
150   QUnit.test('pluck', function(assert) {
151     assert.expect(1);
152     assert.equal(col.pluck('label').join(' '), 'a b c d');
153   });
154
155   QUnit.test('add', function(assert) {
156     assert.expect(14);
157     var added, opts, secondAdded;
158     added = opts = secondAdded = null;
159     e = new Backbone.Model({id: 10, label: 'e'});
160     otherCol.add(e);
161     otherCol.on('add', function() {
162       secondAdded = true;
163     });
164     col.on('add', function(model, collection, options){
165       added = model.get('label');
166       opts = options;
167     });
168     col.add(e, {amazing: true});
169     assert.equal(added, 'e');
170     assert.equal(col.length, 5);
171     assert.equal(col.last(), e);
172     assert.equal(otherCol.length, 1);
173     assert.equal(secondAdded, null);
174     assert.ok(opts.amazing);
175
176     var f = new Backbone.Model({id: 20, label: 'f'});
177     var g = new Backbone.Model({id: 21, label: 'g'});
178     var h = new Backbone.Model({id: 22, label: 'h'});
179     var atCol = new Backbone.Collection([f, g, h]);
180     assert.equal(atCol.length, 3);
181     atCol.add(e, {at: 1});
182     assert.equal(atCol.length, 4);
183     assert.equal(atCol.at(1), e);
184     assert.equal(atCol.last(), h);
185
186     var coll = new Backbone.Collection(new Array(2));
187     var addCount = 0;
188     coll.on('add', function(){
189       addCount += 1;
190     });
191     coll.add([undefined, f, g]);
192     assert.equal(coll.length, 5);
193     assert.equal(addCount, 3);
194     coll.add(new Array(4));
195     assert.equal(coll.length, 9);
196     assert.equal(addCount, 7);
197   });
198
199   QUnit.test('add multiple models', function(assert) {
200     assert.expect(6);
201     var collection = new Backbone.Collection([{at: 0}, {at: 1}, {at: 9}]);
202     collection.add([{at: 2}, {at: 3}, {at: 4}, {at: 5}, {at: 6}, {at: 7}, {at: 8}], {at: 2});
203     for (var i = 0; i <= 5; i++) {
204       assert.equal(collection.at(i).get('at'), i);
205     }
206   });
207
208   QUnit.test('add; at should have preference over comparator', function(assert) {
209     assert.expect(1);
210     var Col = Backbone.Collection.extend({
211       comparator: function(m1, m2) {
212         return m1.id > m2.id ? -1 : 1;
213       }
214     });
215
216     var collection = new Col([{id: 2}, {id: 3}]);
217     collection.add(new Backbone.Model({id: 1}), {at: 1});
218
219     assert.equal(collection.pluck('id').join(' '), '3 1 2');
220   });
221
222   QUnit.test('add; at should add to the end if the index is out of bounds', function(assert) {
223     assert.expect(1);
224     var collection = new Backbone.Collection([{id: 2}, {id: 3}]);
225     collection.add(new Backbone.Model({id: 1}), {at: 5});
226
227     assert.equal(collection.pluck('id').join(' '), '2 3 1');
228   });
229
230   QUnit.test("can't add model to collection twice", function(assert) {
231     var collection = new Backbone.Collection([{id: 1}, {id: 2}, {id: 1}, {id: 2}, {id: 3}]);
232     assert.equal(collection.pluck('id').join(' '), '1 2 3');
233   });
234
235   QUnit.test("can't add different model with same id to collection twice", function(assert) {
236     assert.expect(1);
237     var collection = new Backbone.Collection;
238     collection.unshift({id: 101});
239     collection.add({id: 101});
240     assert.equal(collection.length, 1);
241   });
242
243   QUnit.test('merge in duplicate models with {merge: true}', function(assert) {
244     assert.expect(3);
245     var collection = new Backbone.Collection;
246     collection.add([{id: 1, name: 'Moe'}, {id: 2, name: 'Curly'}, {id: 3, name: 'Larry'}]);
247     collection.add({id: 1, name: 'Moses'});
248     assert.equal(collection.first().get('name'), 'Moe');
249     collection.add({id: 1, name: 'Moses'}, {merge: true});
250     assert.equal(collection.first().get('name'), 'Moses');
251     collection.add({id: 1, name: 'Tim'}, {merge: true, silent: true});
252     assert.equal(collection.first().get('name'), 'Tim');
253   });
254
255   QUnit.test('add model to multiple collections', function(assert) {
256     assert.expect(10);
257     var counter = 0;
258     var m = new Backbone.Model({id: 10, label: 'm'});
259     m.on('add', function(model, collection) {
260       counter++;
261       assert.equal(m, model);
262       if (counter > 1) {
263         assert.equal(collection, col2);
264       } else {
265         assert.equal(collection, col1);
266       }
267     });
268     var col1 = new Backbone.Collection([]);
269     col1.on('add', function(model, collection) {
270       assert.equal(m, model);
271       assert.equal(col1, collection);
272     });
273     var col2 = new Backbone.Collection([]);
274     col2.on('add', function(model, collection) {
275       assert.equal(m, model);
276       assert.equal(col2, collection);
277     });
278     col1.add(m);
279     assert.equal(m.collection, col1);
280     col2.add(m);
281     assert.equal(m.collection, col1);
282   });
283
284   QUnit.test('add model with parse', function(assert) {
285     assert.expect(1);
286     var Model = Backbone.Model.extend({
287       parse: function(obj) {
288         obj.value += 1;
289         return obj;
290       }
291     });
292
293     var Col = Backbone.Collection.extend({model: Model});
294     var collection = new Col;
295     collection.add({value: 1}, {parse: true});
296     assert.equal(collection.at(0).get('value'), 2);
297   });
298
299   QUnit.test('add with parse and merge', function(assert) {
300     var collection = new Backbone.Collection();
301     collection.parse = function(attrs) {
302       return _.map(attrs, function(model) {
303         if (model.model) return model.model;
304         return model;
305       });
306     };
307     collection.add({id: 1});
308     collection.add({model: {id: 1, name: 'Alf'}}, {parse: true, merge: true});
309     assert.equal(collection.first().get('name'), 'Alf');
310   });
311
312   QUnit.test('add model to collection with sort()-style comparator', function(assert) {
313     assert.expect(3);
314     var collection = new Backbone.Collection;
315     collection.comparator = function(m1, m2) {
316       return m1.get('name') < m2.get('name') ? -1 : 1;
317     };
318     var tom = new Backbone.Model({name: 'Tom'});
319     var rob = new Backbone.Model({name: 'Rob'});
320     var tim = new Backbone.Model({name: 'Tim'});
321     collection.add(tom);
322     collection.add(rob);
323     collection.add(tim);
324     assert.equal(collection.indexOf(rob), 0);
325     assert.equal(collection.indexOf(tim), 1);
326     assert.equal(collection.indexOf(tom), 2);
327   });
328
329   QUnit.test('comparator that depends on `this`', function(assert) {
330     assert.expect(2);
331     var collection = new Backbone.Collection;
332     collection.negative = function(num) {
333       return -num;
334     };
335     collection.comparator = function(model) {
336       return this.negative(model.id);
337     };
338     collection.add([{id: 1}, {id: 2}, {id: 3}]);
339     assert.deepEqual(collection.pluck('id'), [3, 2, 1]);
340     collection.comparator = function(m1, m2) {
341       return this.negative(m2.id) - this.negative(m1.id);
342     };
343     collection.sort();
344     assert.deepEqual(collection.pluck('id'), [1, 2, 3]);
345   });
346
347   QUnit.test('remove', function(assert) {
348     assert.expect(12);
349     var removed = null;
350     var result = null;
351     col.on('remove', function(model, collection, options) {
352       removed = model.get('label');
353       assert.equal(options.index, 3);
354       assert.equal(collection.get(model), undefined, '#3693: model cannot be fetched from collection');
355     });
356     result = col.remove(d);
357     assert.equal(removed, 'd');
358     assert.strictEqual(result, d);
359     //if we try to remove d again, it's not going to actually get removed
360     result = col.remove(d);
361     assert.strictEqual(result, undefined);
362     assert.equal(col.length, 3);
363     assert.equal(col.first(), a);
364     col.off();
365     result = col.remove([c, d]);
366     assert.equal(result.length, 1, 'only returns removed models');
367     assert.equal(result[0], c, 'only returns removed models');
368     result = col.remove([c, b]);
369     assert.equal(result.length, 1, 'only returns removed models');
370     assert.equal(result[0], b, 'only returns removed models');
371     result = col.remove([]);
372     assert.deepEqual(result, [], 'returns empty array when nothing removed');
373   });
374
375   QUnit.test('add and remove return values', function(assert) {
376     assert.expect(13);
377     var Even = Backbone.Model.extend({
378       validate: function(attrs) {
379         if (attrs.id % 2 !== 0) return 'odd';
380       }
381     });
382     var collection = new Backbone.Collection;
383     collection.model = Even;
384
385     var list = collection.add([{id: 2}, {id: 4}], {validate: true});
386     assert.equal(list.length, 2);
387     assert.ok(list[0] instanceof Backbone.Model);
388     assert.equal(list[1], collection.last());
389     assert.equal(list[1].get('id'), 4);
390
391     list = collection.add([{id: 3}, {id: 6}], {validate: true});
392     assert.equal(collection.length, 3);
393     assert.equal(list[0], false);
394     assert.equal(list[1].get('id'), 6);
395
396     var result = collection.add({id: 6});
397     assert.equal(result.cid, list[1].cid);
398
399     result = collection.remove({id: 6});
400     assert.equal(collection.length, 2);
401     assert.equal(result.id, 6);
402
403     list = collection.remove([{id: 2}, {id: 8}]);
404     assert.equal(collection.length, 1);
405     assert.equal(list[0].get('id'), 2);
406     assert.equal(list[1], null);
407   });
408
409   QUnit.test('shift and pop', function(assert) {
410     assert.expect(2);
411     var collection = new Backbone.Collection([{a: 'a'}, {b: 'b'}, {c: 'c'}]);
412     assert.equal(collection.shift().get('a'), 'a');
413     assert.equal(collection.pop().get('c'), 'c');
414   });
415
416   QUnit.test('slice', function(assert) {
417     assert.expect(2);
418     var collection = new Backbone.Collection([{a: 'a'}, {b: 'b'}, {c: 'c'}]);
419     var array = collection.slice(1, 3);
420     assert.equal(array.length, 2);
421     assert.equal(array[0].get('b'), 'b');
422   });
423
424   QUnit.test('events are unbound on remove', function(assert) {
425     assert.expect(3);
426     var counter = 0;
427     var dj = new Backbone.Model();
428     var emcees = new Backbone.Collection([dj]);
429     emcees.on('change', function(){ counter++; });
430     dj.set({name: 'Kool'});
431     assert.equal(counter, 1);
432     emcees.reset([]);
433     assert.equal(dj.collection, undefined);
434     dj.set({name: 'Shadow'});
435     assert.equal(counter, 1);
436   });
437
438   QUnit.test('remove in multiple collections', function(assert) {
439     assert.expect(7);
440     var modelData = {
441       id: 5,
442       title: 'Othello'
443     };
444     var passed = false;
445     var m1 = new Backbone.Model(modelData);
446     var m2 = new Backbone.Model(modelData);
447     m2.on('remove', function() {
448       passed = true;
449     });
450     var col1 = new Backbone.Collection([m1]);
451     var col2 = new Backbone.Collection([m2]);
452     assert.notEqual(m1, m2);
453     assert.ok(col1.length === 1);
454     assert.ok(col2.length === 1);
455     col1.remove(m1);
456     assert.equal(passed, false);
457     assert.ok(col1.length === 0);
458     col2.remove(m1);
459     assert.ok(col2.length === 0);
460     assert.equal(passed, true);
461   });
462
463   QUnit.test('remove same model in multiple collection', function(assert) {
464     assert.expect(16);
465     var counter = 0;
466     var m = new Backbone.Model({id: 5, title: 'Othello'});
467     m.on('remove', function(model, collection) {
468       counter++;
469       assert.equal(m, model);
470       if (counter > 1) {
471         assert.equal(collection, col1);
472       } else {
473         assert.equal(collection, col2);
474       }
475     });
476     var col1 = new Backbone.Collection([m]);
477     col1.on('remove', function(model, collection) {
478       assert.equal(m, model);
479       assert.equal(col1, collection);
480     });
481     var col2 = new Backbone.Collection([m]);
482     col2.on('remove', function(model, collection) {
483       assert.equal(m, model);
484       assert.equal(col2, collection);
485     });
486     assert.equal(col1, m.collection);
487     col2.remove(m);
488     assert.ok(col2.length === 0);
489     assert.ok(col1.length === 1);
490     assert.equal(counter, 1);
491     assert.equal(col1, m.collection);
492     col1.remove(m);
493     assert.equal(null, m.collection);
494     assert.ok(col1.length === 0);
495     assert.equal(counter, 2);
496   });
497
498   QUnit.test('model destroy removes from all collections', function(assert) {
499     assert.expect(3);
500     var m = new Backbone.Model({id: 5, title: 'Othello'});
501     m.sync = function(method, model, options) { options.success(); };
502     var col1 = new Backbone.Collection([m]);
503     var col2 = new Backbone.Collection([m]);
504     m.destroy();
505     assert.ok(col1.length === 0);
506     assert.ok(col2.length === 0);
507     assert.equal(undefined, m.collection);
508   });
509
510   QUnit.test('Collection: non-persisted model destroy removes from all collections', function(assert) {
511     assert.expect(3);
512     var m = new Backbone.Model({title: 'Othello'});
513     m.sync = function(method, model, options) { throw 'should not be called'; };
514     var col1 = new Backbone.Collection([m]);
515     var col2 = new Backbone.Collection([m]);
516     m.destroy();
517     assert.ok(col1.length === 0);
518     assert.ok(col2.length === 0);
519     assert.equal(undefined, m.collection);
520   });
521
522   QUnit.test('fetch', function(assert) {
523     assert.expect(4);
524     var collection = new Backbone.Collection;
525     collection.url = '/test';
526     collection.fetch();
527     assert.equal(this.syncArgs.method, 'read');
528     assert.equal(this.syncArgs.model, collection);
529     assert.equal(this.syncArgs.options.parse, true);
530
531     collection.fetch({parse: false});
532     assert.equal(this.syncArgs.options.parse, false);
533   });
534
535   QUnit.test('fetch with an error response triggers an error event', function(assert) {
536     assert.expect(1);
537     var collection = new Backbone.Collection();
538     collection.on('error', function() {
539       assert.ok(true);
540     });
541     collection.sync = function(method, model, options) { options.error(); };
542     collection.fetch();
543   });
544
545   QUnit.test('#3283 - fetch with an error response calls error with context', function(assert) {
546     assert.expect(1);
547     var collection = new Backbone.Collection();
548     var obj = {};
549     var options = {
550       context: obj,
551       error: function() {
552         assert.equal(this, obj);
553       }
554     };
555     collection.sync = function(method, model, opts) {
556       opts.error.call(opts.context);
557     };
558     collection.fetch(options);
559   });
560
561   QUnit.test('ensure fetch only parses once', function(assert) {
562     assert.expect(1);
563     var collection = new Backbone.Collection;
564     var counter = 0;
565     collection.parse = function(models) {
566       counter++;
567       return models;
568     };
569     collection.url = '/test';
570     collection.fetch();
571     this.syncArgs.options.success([]);
572     assert.equal(counter, 1);
573   });
574
575   QUnit.test('create', function(assert) {
576     assert.expect(4);
577     var collection = new Backbone.Collection;
578     collection.url = '/test';
579     var model = collection.create({label: 'f'}, {wait: true});
580     assert.equal(this.syncArgs.method, 'create');
581     assert.equal(this.syncArgs.model, model);
582     assert.equal(model.get('label'), 'f');
583     assert.equal(model.collection, collection);
584   });
585
586   QUnit.test('create with validate:true enforces validation', function(assert) {
587     assert.expect(3);
588     var ValidatingModel = Backbone.Model.extend({
589       validate: function(attrs) {
590         return 'fail';
591       }
592     });
593     var ValidatingCollection = Backbone.Collection.extend({
594       model: ValidatingModel
595     });
596     var collection = new ValidatingCollection();
597     collection.on('invalid', function(coll, error, options) {
598       assert.equal(error, 'fail');
599       assert.equal(options.validationError, 'fail');
600     });
601     assert.equal(collection.create({'foo': 'bar'}, {validate: true}), false);
602   });
603
604   QUnit.test('create will pass extra options to success callback', function(assert) {
605     assert.expect(1);
606     var Model = Backbone.Model.extend({
607       sync: function(method, model, options) {
608         _.extend(options, {specialSync: true});
609         return Backbone.Model.prototype.sync.call(this, method, model, options);
610       }
611     });
612
613     var Collection = Backbone.Collection.extend({
614       model: Model,
615       url: '/test'
616     });
617
618     var collection = new Collection;
619
620     var success = function(model, response, options) {
621       assert.ok(options.specialSync, 'Options were passed correctly to callback');
622     };
623
624     collection.create({}, {success: success});
625     this.ajaxSettings.success();
626   });
627
628   QUnit.test('create with wait:true should not call collection.parse', function(assert) {
629     assert.expect(0);
630     var Collection = Backbone.Collection.extend({
631       url: '/test',
632       parse: function() {
633         assert.ok(false);
634       }
635     });
636
637     var collection = new Collection;
638
639     collection.create({}, {wait: true});
640     this.ajaxSettings.success();
641   });
642
643   QUnit.test('a failing create returns model with errors', function(assert) {
644     var ValidatingModel = Backbone.Model.extend({
645       validate: function(attrs) {
646         return 'fail';
647       }
648     });
649     var ValidatingCollection = Backbone.Collection.extend({
650       model: ValidatingModel
651     });
652     var collection = new ValidatingCollection();
653     var m = collection.create({foo: 'bar'});
654     assert.equal(m.validationError, 'fail');
655     assert.equal(collection.length, 1);
656   });
657
658   QUnit.test('initialize', function(assert) {
659     assert.expect(1);
660     var Collection = Backbone.Collection.extend({
661       initialize: function() {
662         this.one = 1;
663       }
664     });
665     var coll = new Collection;
666     assert.equal(coll.one, 1);
667   });
668
669   QUnit.test('toJSON', function(assert) {
670     assert.expect(1);
671     assert.equal(JSON.stringify(col), '[{"id":3,"label":"a"},{"id":2,"label":"b"},{"id":1,"label":"c"},{"id":0,"label":"d"}]');
672   });
673
674   QUnit.test('where and findWhere', function(assert) {
675     assert.expect(8);
676     var model = new Backbone.Model({a: 1});
677     var coll = new Backbone.Collection([
678       model,
679       {a: 1},
680       {a: 1, b: 2},
681       {a: 2, b: 2},
682       {a: 3}
683     ]);
684     assert.equal(coll.where({a: 1}).length, 3);
685     assert.equal(coll.where({a: 2}).length, 1);
686     assert.equal(coll.where({a: 3}).length, 1);
687     assert.equal(coll.where({b: 1}).length, 0);
688     assert.equal(coll.where({b: 2}).length, 2);
689     assert.equal(coll.where({a: 1, b: 2}).length, 1);
690     assert.equal(coll.findWhere({a: 1}), model);
691     assert.equal(coll.findWhere({a: 4}), void 0);
692   });
693
694   QUnit.test('Underscore methods', function(assert) {
695     assert.expect(21);
696     assert.equal(col.map(function(model){ return model.get('label'); }).join(' '), 'a b c d');
697     assert.equal(col.some(function(model){ return model.id === 100; }), false);
698     assert.equal(col.some(function(model){ return model.id === 0; }), true);
699     assert.equal(col.reduce(function(m1, m2) {return m1.id > m2.id ? m1 : m2;}).id, 3);
700     assert.equal(col.reduceRight(function(m1, m2) {return m1.id > m2.id ? m1 : m2;}).id, 3);
701     assert.equal(col.indexOf(b), 1);
702     assert.equal(col.size(), 4);
703     assert.equal(col.rest().length, 3);
704     assert.ok(!_.includes(col.rest(), a));
705     assert.ok(_.includes(col.rest(), d));
706     assert.ok(!col.isEmpty());
707     assert.ok(!_.includes(col.without(d), d));
708
709     var wrapped = col.chain();
710     assert.equal(wrapped.map('id').max().value(), 3);
711     assert.equal(wrapped.map('id').min().value(), 0);
712     assert.deepEqual(wrapped
713       .filter(function(o){ return o.id % 2 === 0; })
714       .map(function(o){ return o.id * 2; })
715       .value(),
716       [4, 0]);
717     assert.deepEqual(col.difference([c, d]), [a, b]);
718     assert.ok(col.includes(col.sample()));
719
720     var first = col.first();
721     assert.deepEqual(col.groupBy(function(model){ return model.id; })[first.id], [first]);
722     assert.deepEqual(col.countBy(function(model){ return model.id; }), {0: 1, 1: 1, 2: 1, 3: 1});
723     assert.deepEqual(col.sortBy(function(model){ return model.id; })[0], col.at(3));
724     assert.ok(col.indexBy('id')[first.id] === first);
725   });
726
727   QUnit.test('Underscore methods with object-style and property-style iteratee', function(assert) {
728     assert.expect(26);
729     var model = new Backbone.Model({a: 4, b: 1, e: 3});
730     var coll = new Backbone.Collection([
731       {a: 1, b: 1},
732       {a: 2, b: 1, c: 1},
733       {a: 3, b: 1},
734       model
735     ]);
736     assert.equal(coll.find({a: 0}), undefined);
737     assert.deepEqual(coll.find({a: 4}), model);
738     assert.equal(coll.find('d'), undefined);
739     assert.deepEqual(coll.find('e'), model);
740     assert.equal(coll.filter({a: 0}), false);
741     assert.deepEqual(coll.filter({a: 4}), [model]);
742     assert.equal(coll.some({a: 0}), false);
743     assert.equal(coll.some({a: 1}), true);
744     assert.equal(coll.reject({a: 0}).length, 4);
745     assert.deepEqual(coll.reject({a: 4}), _.without(coll.models, model));
746     assert.equal(coll.every({a: 0}), false);
747     assert.equal(coll.every({b: 1}), true);
748     assert.deepEqual(coll.partition({a: 0})[0], []);
749     assert.deepEqual(coll.partition({a: 0})[1], coll.models);
750     assert.deepEqual(coll.partition({a: 4})[0], [model]);
751     assert.deepEqual(coll.partition({a: 4})[1], _.without(coll.models, model));
752     assert.deepEqual(coll.map({a: 2}), [false, true, false, false]);
753     assert.deepEqual(coll.map('a'), [1, 2, 3, 4]);
754     assert.deepEqual(coll.sortBy('a')[3], model);
755     assert.deepEqual(coll.sortBy('e')[0], model);
756     assert.deepEqual(coll.countBy({a: 4}), {'false': 3, 'true': 1});
757     assert.deepEqual(coll.countBy('d'), {'undefined': 4});
758     assert.equal(coll.findIndex({b: 1}), 0);
759     assert.equal(coll.findIndex({b: 9}), -1);
760     assert.equal(coll.findLastIndex({b: 1}), 3);
761     assert.equal(coll.findLastIndex({b: 9}), -1);
762   });
763
764   QUnit.test('reset', function(assert) {
765     assert.expect(16);
766
767     var resetCount = 0;
768     var models = col.models;
769     col.on('reset', function() { resetCount += 1; });
770     col.reset([]);
771     assert.equal(resetCount, 1);
772     assert.equal(col.length, 0);
773     assert.equal(col.last(), null);
774     col.reset(models);
775     assert.equal(resetCount, 2);
776     assert.equal(col.length, 4);
777     assert.equal(col.last(), d);
778     col.reset(_.map(models, function(m){ return m.attributes; }));
779     assert.equal(resetCount, 3);
780     assert.equal(col.length, 4);
781     assert.ok(col.last() !== d);
782     assert.ok(_.isEqual(col.last().attributes, d.attributes));
783     col.reset();
784     assert.equal(col.length, 0);
785     assert.equal(resetCount, 4);
786
787     var f = new Backbone.Model({id: 20, label: 'f'});
788     col.reset([undefined, f]);
789     assert.equal(col.length, 2);
790     assert.equal(resetCount, 5);
791
792     col.reset(new Array(4));
793     assert.equal(col.length, 4);
794     assert.equal(resetCount, 6);
795   });
796
797   QUnit.test('reset with different values', function(assert) {
798     var collection = new Backbone.Collection({id: 1});
799     collection.reset({id: 1, a: 1});
800     assert.equal(collection.get(1).get('a'), 1);
801   });
802
803   QUnit.test('same references in reset', function(assert) {
804     var model = new Backbone.Model({id: 1});
805     var collection = new Backbone.Collection({id: 1});
806     collection.reset(model);
807     assert.equal(collection.get(1), model);
808   });
809
810   QUnit.test('reset passes caller options', function(assert) {
811     assert.expect(3);
812     var Model = Backbone.Model.extend({
813       initialize: function(attrs, options) {
814         this.modelParameter = options.modelParameter;
815       }
816     });
817     var collection = new (Backbone.Collection.extend({model: Model}))();
818     collection.reset([{astring: 'green', anumber: 1}, {astring: 'blue', anumber: 2}], {modelParameter: 'model parameter'});
819     assert.equal(collection.length, 2);
820     collection.each(function(model) {
821       assert.equal(model.modelParameter, 'model parameter');
822     });
823   });
824
825   QUnit.test('reset does not alter options by reference', function(assert) {
826     assert.expect(2);
827     var collection = new Backbone.Collection([{id: 1}]);
828     var origOpts = {};
829     collection.on('reset', function(coll, opts){
830       assert.equal(origOpts.previousModels, undefined);
831       assert.equal(opts.previousModels[0].id, 1);
832     });
833     collection.reset([], origOpts);
834   });
835
836   QUnit.test('trigger custom events on models', function(assert) {
837     assert.expect(1);
838     var fired = null;
839     a.on('custom', function() { fired = true; });
840     a.trigger('custom');
841     assert.equal(fired, true);
842   });
843
844   QUnit.test('add does not alter arguments', function(assert) {
845     assert.expect(2);
846     var attrs = {};
847     var models = [attrs];
848     new Backbone.Collection().add(models);
849     assert.equal(models.length, 1);
850     assert.ok(attrs === models[0]);
851   });
852
853   QUnit.test('#714: access `model.collection` in a brand new model.', function(assert) {
854     assert.expect(2);
855     var collection = new Backbone.Collection;
856     collection.url = '/test';
857     var Model = Backbone.Model.extend({
858       set: function(attrs) {
859         assert.equal(attrs.prop, 'value');
860         assert.equal(this.collection, collection);
861         return this;
862       }
863     });
864     collection.model = Model;
865     collection.create({prop: 'value'});
866   });
867
868   QUnit.test('#574, remove its own reference to the .models array.', function(assert) {
869     assert.expect(2);
870     var collection = new Backbone.Collection([
871       {id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}, {id: 6}
872     ]);
873     assert.equal(collection.length, 6);
874     collection.remove(collection.models);
875     assert.equal(collection.length, 0);
876   });
877
878   QUnit.test('#861, adding models to a collection which do not pass validation, with validate:true', function(assert) {
879     assert.expect(2);
880     var Model = Backbone.Model.extend({
881       validate: function(attrs) {
882         if (attrs.id === 3) return "id can't be 3";
883       }
884     });
885
886     var Collection = Backbone.Collection.extend({
887       model: Model
888     });
889
890     var collection = new Collection;
891     collection.on('invalid', function() { assert.ok(true); });
892
893     collection.add([{id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}, {id: 6}], {validate: true});
894     assert.deepEqual(collection.pluck('id'), [1, 2, 4, 5, 6]);
895   });
896
897   QUnit.test('Invalid models are discarded with validate:true.', function(assert) {
898     assert.expect(5);
899     var collection = new Backbone.Collection;
900     collection.on('test', function() { assert.ok(true); });
901     collection.model = Backbone.Model.extend({
902       validate: function(attrs){ if (!attrs.valid) return 'invalid'; }
903     });
904     var model = new collection.model({id: 1, valid: true});
905     collection.add([model, {id: 2}], {validate: true});
906     model.trigger('test');
907     assert.ok(collection.get(model.cid));
908     assert.ok(collection.get(1));
909     assert.ok(!collection.get(2));
910     assert.equal(collection.length, 1);
911   });
912
913   QUnit.test('multiple copies of the same model', function(assert) {
914     assert.expect(3);
915     var collection = new Backbone.Collection();
916     var model = new Backbone.Model();
917     collection.add([model, model]);
918     assert.equal(collection.length, 1);
919     collection.add([{id: 1}, {id: 1}]);
920     assert.equal(collection.length, 2);
921     assert.equal(collection.last().id, 1);
922   });
923
924   QUnit.test('#964 - collection.get return inconsistent', function(assert) {
925     assert.expect(2);
926     var collection = new Backbone.Collection();
927     assert.ok(collection.get(null) === undefined);
928     assert.ok(collection.get() === undefined);
929   });
930
931   QUnit.test('#1112 - passing options.model sets collection.model', function(assert) {
932     assert.expect(2);
933     var Model = Backbone.Model.extend({});
934     var collection = new Backbone.Collection([{id: 1}], {model: Model});
935     assert.ok(collection.model === Model);
936     assert.ok(collection.at(0) instanceof Model);
937   });
938
939   QUnit.test('null and undefined are invalid ids.', function(assert) {
940     assert.expect(2);
941     var model = new Backbone.Model({id: 1});
942     var collection = new Backbone.Collection([model]);
943     model.set({id: null});
944     assert.ok(!collection.get('null'));
945     model.set({id: 1});
946     model.set({id: undefined});
947     assert.ok(!collection.get('undefined'));
948   });
949
950   QUnit.test('falsy comparator', function(assert) {
951     assert.expect(4);
952     var Col = Backbone.Collection.extend({
953       comparator: function(model){ return model.id; }
954     });
955     var collection = new Col();
956     var colFalse = new Col(null, {comparator: false});
957     var colNull = new Col(null, {comparator: null});
958     var colUndefined = new Col(null, {comparator: undefined});
959     assert.ok(collection.comparator);
960     assert.ok(!colFalse.comparator);
961     assert.ok(!colNull.comparator);
962     assert.ok(colUndefined.comparator);
963   });
964
965   QUnit.test('#1355 - `options` is passed to success callbacks', function(assert) {
966     assert.expect(2);
967     var m = new Backbone.Model({x: 1});
968     var collection = new Backbone.Collection();
969     var opts = {
970       opts: true,
971       success: function(coll, resp, options) {
972         assert.ok(options.opts);
973       }
974     };
975     collection.sync = m.sync = function( method, coll, options ){
976       options.success({});
977     };
978     collection.fetch(opts);
979     collection.create(m, opts);
980   });
981
982   QUnit.test("#1412 - Trigger 'request' and 'sync' events.", function(assert) {
983     assert.expect(4);
984     var collection = new Backbone.Collection;
985     collection.url = '/test';
986     Backbone.ajax = function(settings){ settings.success(); };
987
988     collection.on('request', function(obj, xhr, options) {
989       assert.ok(obj === collection, "collection has correct 'request' event after fetching");
990     });
991     collection.on('sync', function(obj, response, options) {
992       assert.ok(obj === collection, "collection has correct 'sync' event after fetching");
993     });
994     collection.fetch();
995     collection.off();
996
997     collection.on('request', function(obj, xhr, options) {
998       assert.ok(obj === collection.get(1), "collection has correct 'request' event after one of its models save");
999     });
1000     collection.on('sync', function(obj, response, options) {
1001       assert.ok(obj === collection.get(1), "collection has correct 'sync' event after one of its models save");
1002     });
1003     collection.create({id: 1});
1004     collection.off();
1005   });
1006
1007   QUnit.test('#3283 - fetch, create calls success with context', function(assert) {
1008     assert.expect(2);
1009     var collection = new Backbone.Collection;
1010     collection.url = '/test';
1011     Backbone.ajax = function(settings) {
1012       settings.success.call(settings.context);
1013     };
1014     var obj = {};
1015     var options = {
1016       context: obj,
1017       success: function() {
1018         assert.equal(this, obj);
1019       }
1020     };
1021
1022     collection.fetch(options);
1023     collection.create({id: 1}, options);
1024   });
1025
1026   QUnit.test('#1447 - create with wait adds model.', function(assert) {
1027     assert.expect(1);
1028     var collection = new Backbone.Collection;
1029     var model = new Backbone.Model;
1030     model.sync = function(method, m, options){ options.success(); };
1031     collection.on('add', function(){ assert.ok(true); });
1032     collection.create(model, {wait: true});
1033   });
1034
1035   QUnit.test('#1448 - add sorts collection after merge.', function(assert) {
1036     assert.expect(1);
1037     var collection = new Backbone.Collection([
1038       {id: 1, x: 1},
1039       {id: 2, x: 2}
1040     ]);
1041     collection.comparator = function(model){ return model.get('x'); };
1042     collection.add({id: 1, x: 3}, {merge: true});
1043     assert.deepEqual(collection.pluck('id'), [2, 1]);
1044   });
1045
1046   QUnit.test('#1655 - groupBy can be used with a string argument.', function(assert) {
1047     assert.expect(3);
1048     var collection = new Backbone.Collection([{x: 1}, {x: 2}]);
1049     var grouped = collection.groupBy('x');
1050     assert.strictEqual(_.keys(grouped).length, 2);
1051     assert.strictEqual(grouped[1][0].get('x'), 1);
1052     assert.strictEqual(grouped[2][0].get('x'), 2);
1053   });
1054
1055   QUnit.test('#1655 - sortBy can be used with a string argument.', function(assert) {
1056     assert.expect(1);
1057     var collection = new Backbone.Collection([{x: 3}, {x: 1}, {x: 2}]);
1058     var values = _.map(collection.sortBy('x'), function(model) {
1059       return model.get('x');
1060     });
1061     assert.deepEqual(values, [1, 2, 3]);
1062   });
1063
1064   QUnit.test('#1604 - Removal during iteration.', function(assert) {
1065     assert.expect(0);
1066     var collection = new Backbone.Collection([{}, {}]);
1067     collection.on('add', function() {
1068       collection.at(0).destroy();
1069     });
1070     collection.add({}, {at: 0});
1071   });
1072
1073   QUnit.test('#1638 - `sort` during `add` triggers correctly.', function(assert) {
1074     var collection = new Backbone.Collection;
1075     collection.comparator = function(model) { return model.get('x'); };
1076     var added = [];
1077     collection.on('add', function(model) {
1078       model.set({x: 3});
1079       collection.sort();
1080       added.push(model.id);
1081     });
1082     collection.add([{id: 1, x: 1}, {id: 2, x: 2}]);
1083     assert.deepEqual(added, [1, 2]);
1084   });
1085
1086   QUnit.test('fetch parses models by default', function(assert) {
1087     assert.expect(1);
1088     var model = {};
1089     var Collection = Backbone.Collection.extend({
1090       url: 'test',
1091       model: Backbone.Model.extend({
1092         parse: function(resp) {
1093           assert.strictEqual(resp, model);
1094         }
1095       })
1096     });
1097     new Collection().fetch();
1098     this.ajaxSettings.success([model]);
1099   });
1100
1101   QUnit.test("`sort` shouldn't always fire on `add`", function(assert) {
1102     assert.expect(1);
1103     var collection = new Backbone.Collection([{id: 1}, {id: 2}, {id: 3}], {
1104       comparator: 'id'
1105     });
1106     collection.sort = function(){ assert.ok(true); };
1107     collection.add([]);
1108     collection.add({id: 1});
1109     collection.add([{id: 2}, {id: 3}]);
1110     collection.add({id: 4});
1111   });
1112
1113   QUnit.test('#1407 parse option on constructor parses collection and models', function(assert) {
1114     assert.expect(2);
1115     var model = {
1116       namespace: [{id: 1}, {id: 2}]
1117     };
1118     var Collection = Backbone.Collection.extend({
1119       model: Backbone.Model.extend({
1120         parse: function(m) {
1121           m.name = 'test';
1122           return m;
1123         }
1124       }),
1125       parse: function(m) {
1126         return m.namespace;
1127       }
1128     });
1129     var collection = new Collection(model, {parse: true});
1130
1131     assert.equal(collection.length, 2);
1132     assert.equal(collection.at(0).get('name'), 'test');
1133   });
1134
1135   QUnit.test('#1407 parse option on reset parses collection and models', function(assert) {
1136     assert.expect(2);
1137     var model = {
1138       namespace: [{id: 1}, {id: 2}]
1139     };
1140     var Collection = Backbone.Collection.extend({
1141       model: Backbone.Model.extend({
1142         parse: function(m) {
1143           m.name = 'test';
1144           return m;
1145         }
1146       }),
1147       parse: function(m) {
1148         return m.namespace;
1149       }
1150     });
1151     var collection = new Collection();
1152     collection.reset(model, {parse: true});
1153
1154     assert.equal(collection.length, 2);
1155     assert.equal(collection.at(0).get('name'), 'test');
1156   });
1157
1158
1159   QUnit.test('Reset includes previous models in triggered event.', function(assert) {
1160     assert.expect(1);
1161     var model = new Backbone.Model();
1162     var collection = new Backbone.Collection([model]);
1163     collection.on('reset', function(coll, options) {
1164       assert.deepEqual(options.previousModels, [model]);
1165     });
1166     collection.reset([]);
1167   });
1168
1169   QUnit.test('set', function(assert) {
1170     var m1 = new Backbone.Model();
1171     var m2 = new Backbone.Model({id: 2});
1172     var m3 = new Backbone.Model();
1173     var collection = new Backbone.Collection([m1, m2]);
1174
1175     // Test add/change/remove events
1176     collection.on('add', function(model) {
1177       assert.strictEqual(model, m3);
1178     });
1179     collection.on('change', function(model) {
1180       assert.strictEqual(model, m2);
1181     });
1182     collection.on('remove', function(model) {
1183       assert.strictEqual(model, m1);
1184     });
1185
1186     // remove: false doesn't remove any models
1187     collection.set([], {remove: false});
1188     assert.strictEqual(collection.length, 2);
1189
1190     // add: false doesn't add any models
1191     collection.set([m1, m2, m3], {add: false});
1192     assert.strictEqual(collection.length, 2);
1193
1194     // merge: false doesn't change any models
1195     collection.set([m1, {id: 2, a: 1}], {merge: false});
1196     assert.strictEqual(m2.get('a'), void 0);
1197
1198     // add: false, remove: false only merges existing models
1199     collection.set([m1, {id: 2, a: 0}, m3, {id: 4}], {add: false, remove: false});
1200     assert.strictEqual(collection.length, 2);
1201     assert.strictEqual(m2.get('a'), 0);
1202
1203     // default options add/remove/merge as appropriate
1204     collection.set([{id: 2, a: 1}, m3]);
1205     assert.strictEqual(collection.length, 2);
1206     assert.strictEqual(m2.get('a'), 1);
1207
1208     // Test removing models not passing an argument
1209     collection.off('remove').on('remove', function(model) {
1210       assert.ok(model === m2 || model === m3);
1211     });
1212     collection.set([]);
1213     assert.strictEqual(collection.length, 0);
1214
1215     // Test null models on set doesn't clear collection
1216     collection.off();
1217     collection.set([{id: 1}]);
1218     collection.set();
1219     assert.strictEqual(collection.length, 1);
1220   });
1221
1222   QUnit.test('set with only cids', function(assert) {
1223     assert.expect(3);
1224     var m1 = new Backbone.Model;
1225     var m2 = new Backbone.Model;
1226     var collection = new Backbone.Collection;
1227     collection.set([m1, m2]);
1228     assert.equal(collection.length, 2);
1229     collection.set([m1]);
1230     assert.equal(collection.length, 1);
1231     collection.set([m1, m1, m1, m2, m2], {remove: false});
1232     assert.equal(collection.length, 2);
1233   });
1234
1235   QUnit.test('set with only idAttribute', function(assert) {
1236     assert.expect(3);
1237     var m1 = {_id: 1};
1238     var m2 = {_id: 2};
1239     var Col = Backbone.Collection.extend({
1240       model: Backbone.Model.extend({
1241         idAttribute: '_id'
1242       })
1243     });
1244     var collection = new Col;
1245     collection.set([m1, m2]);
1246     assert.equal(collection.length, 2);
1247     collection.set([m1]);
1248     assert.equal(collection.length, 1);
1249     collection.set([m1, m1, m1, m2, m2], {remove: false});
1250     assert.equal(collection.length, 2);
1251   });
1252
1253   QUnit.test('set + merge with default values defined', function(assert) {
1254     var Model = Backbone.Model.extend({
1255       defaults: {
1256         key: 'value'
1257       }
1258     });
1259     var m = new Model({id: 1});
1260     var collection = new Backbone.Collection([m], {model: Model});
1261     assert.equal(collection.first().get('key'), 'value');
1262
1263     collection.set({id: 1, key: 'other'});
1264     assert.equal(collection.first().get('key'), 'other');
1265
1266     collection.set({id: 1, other: 'value'});
1267     assert.equal(collection.first().get('key'), 'other');
1268     assert.equal(collection.length, 1);
1269   });
1270
1271   QUnit.test('merge without mutation', function(assert) {
1272     var Model = Backbone.Model.extend({
1273       initialize: function(attrs, options) {
1274         if (attrs.child) {
1275           this.set('child', new Model(attrs.child, options), options);
1276         }
1277       }
1278     });
1279     var Collection = Backbone.Collection.extend({model: Model});
1280     var data = [{id: 1, child: {id: 2}}];
1281     var collection = new Collection(data);
1282     assert.equal(collection.first().id, 1);
1283     collection.set(data);
1284     assert.equal(collection.first().id, 1);
1285     collection.set([{id: 2, child: {id: 2}}].concat(data));
1286     assert.deepEqual(collection.pluck('id'), [2, 1]);
1287   });
1288
1289   QUnit.test('`set` and model level `parse`', function(assert) {
1290     var Model = Backbone.Model.extend({});
1291     var Collection = Backbone.Collection.extend({
1292       model: Model,
1293       parse: function(res) { return _.map(res.models, 'model'); }
1294     });
1295     var model = new Model({id: 1});
1296     var collection = new Collection(model);
1297     collection.set({models: [
1298       {model: {id: 1}},
1299       {model: {id: 2}}
1300     ]}, {parse: true});
1301     assert.equal(collection.first(), model);
1302   });
1303
1304   QUnit.test('`set` data is only parsed once', function(assert) {
1305     var collection = new Backbone.Collection();
1306     collection.model = Backbone.Model.extend({
1307       parse: function(data) {
1308         assert.equal(data.parsed, void 0);
1309         data.parsed = true;
1310         return data;
1311       }
1312     });
1313     collection.set({}, {parse: true});
1314   });
1315
1316   QUnit.test('`set` matches input order in the absence of a comparator', function(assert) {
1317     var one = new Backbone.Model({id: 1});
1318     var two = new Backbone.Model({id: 2});
1319     var three = new Backbone.Model({id: 3});
1320     var collection = new Backbone.Collection([one, two, three]);
1321     collection.set([{id: 3}, {id: 2}, {id: 1}]);
1322     assert.deepEqual(collection.models, [three, two, one]);
1323     collection.set([{id: 1}, {id: 2}]);
1324     assert.deepEqual(collection.models, [one, two]);
1325     collection.set([two, three, one]);
1326     assert.deepEqual(collection.models, [two, three, one]);
1327     collection.set([{id: 1}, {id: 2}], {remove: false});
1328     assert.deepEqual(collection.models, [two, three, one]);
1329     collection.set([{id: 1}, {id: 2}, {id: 3}], {merge: false});
1330     assert.deepEqual(collection.models, [one, two, three]);
1331     collection.set([three, two, one, {id: 4}], {add: false});
1332     assert.deepEqual(collection.models, [one, two, three]);
1333   });
1334
1335   QUnit.test('#1894 - Push should not trigger a sort', function(assert) {
1336     assert.expect(0);
1337     var Collection = Backbone.Collection.extend({
1338       comparator: 'id',
1339       sort: function() { assert.ok(false); }
1340     });
1341     new Collection().push({id: 1});
1342   });
1343
1344   QUnit.test('#2428 - push duplicate models, return the correct one', function(assert) {
1345     assert.expect(1);
1346     var collection = new Backbone.Collection;
1347     var model1 = collection.push({id: 101});
1348     var model2 = collection.push({id: 101});
1349     assert.ok(model2.cid === model1.cid);
1350   });
1351
1352   QUnit.test('`set` with non-normal id', function(assert) {
1353     var Collection = Backbone.Collection.extend({
1354       model: Backbone.Model.extend({idAttribute: '_id'})
1355     });
1356     var collection = new Collection({_id: 1});
1357     collection.set([{_id: 1, a: 1}], {add: false});
1358     assert.equal(collection.first().get('a'), 1);
1359   });
1360
1361   QUnit.test('#1894 - `sort` can optionally be turned off', function(assert) {
1362     assert.expect(0);
1363     var Collection = Backbone.Collection.extend({
1364       comparator: 'id',
1365       sort: function() { assert.ok(false); }
1366     });
1367     new Collection().add({id: 1}, {sort: false});
1368   });
1369
1370   QUnit.test('#1915 - `parse` data in the right order in `set`', function(assert) {
1371     var collection = new (Backbone.Collection.extend({
1372       parse: function(data) {
1373         assert.strictEqual(data.status, 'ok');
1374         return data.data;
1375       }
1376     }));
1377     var res = {status: 'ok', data: [{id: 1}]};
1378     collection.set(res, {parse: true});
1379   });
1380
1381   QUnit.test('#1939 - `parse` is passed `options`', function(assert) {
1382     var done = assert.async();
1383     assert.expect(1);
1384     var collection = new (Backbone.Collection.extend({
1385       url: '/',
1386       parse: function(data, options) {
1387         assert.strictEqual(options.xhr.someHeader, 'headerValue');
1388         return data;
1389       }
1390     }));
1391     var ajax = Backbone.ajax;
1392     Backbone.ajax = function(params) {
1393       _.defer(params.success, []);
1394       return {someHeader: 'headerValue'};
1395     };
1396     collection.fetch({
1397       success: function() { done(); }
1398     });
1399     Backbone.ajax = ajax;
1400   });
1401
1402   QUnit.test('fetch will pass extra options to success callback', function(assert) {
1403     assert.expect(1);
1404     var SpecialSyncCollection = Backbone.Collection.extend({
1405       url: '/test',
1406       sync: function(method, collection, options) {
1407         _.extend(options, {specialSync: true});
1408         return Backbone.Collection.prototype.sync.call(this, method, collection, options);
1409       }
1410     });
1411
1412     var collection = new SpecialSyncCollection();
1413
1414     var onSuccess = function(coll, resp, options) {
1415       assert.ok(options.specialSync, 'Options were passed correctly to callback');
1416     };
1417
1418     collection.fetch({success: onSuccess});
1419     this.ajaxSettings.success();
1420   });
1421
1422   QUnit.test('`add` only `sort`s when necessary', function(assert) {
1423     assert.expect(2);
1424     var collection = new (Backbone.Collection.extend({
1425       comparator: 'a'
1426     }))([{id: 1}, {id: 2}, {id: 3}]);
1427     collection.on('sort', function() { assert.ok(true); });
1428     collection.add({id: 4}); // do sort, new model
1429     collection.add({id: 1, a: 1}, {merge: true}); // do sort, comparator change
1430     collection.add({id: 1, b: 1}, {merge: true}); // don't sort, no comparator change
1431     collection.add({id: 1, a: 1}, {merge: true}); // don't sort, no comparator change
1432     collection.add(collection.models); // don't sort, nothing new
1433     collection.add(collection.models, {merge: true}); // don't sort
1434   });
1435
1436   QUnit.test('`add` only `sort`s when necessary with comparator function', function(assert) {
1437     assert.expect(3);
1438     var collection = new (Backbone.Collection.extend({
1439       comparator: function(m1, m2) {
1440         return m1.get('a') > m2.get('a') ? 1 : (m1.get('a') < m2.get('a') ? -1 : 0);
1441       }
1442     }))([{id: 1}, {id: 2}, {id: 3}]);
1443     collection.on('sort', function() { assert.ok(true); });
1444     collection.add({id: 4}); // do sort, new model
1445     collection.add({id: 1, a: 1}, {merge: true}); // do sort, model change
1446     collection.add({id: 1, b: 1}, {merge: true}); // do sort, model change
1447     collection.add({id: 1, a: 1}, {merge: true}); // don't sort, no model change
1448     collection.add(collection.models); // don't sort, nothing new
1449     collection.add(collection.models, {merge: true}); // don't sort
1450   });
1451
1452   QUnit.test('Attach options to collection.', function(assert) {
1453     assert.expect(2);
1454     var Model = Backbone.Model;
1455     var comparator = function(){};
1456
1457     var collection = new Backbone.Collection([], {
1458       model: Model,
1459       comparator: comparator
1460     });
1461
1462     assert.ok(collection.model === Model);
1463     assert.ok(collection.comparator === comparator);
1464   });
1465
1466   QUnit.test('Pass falsey for `models` for empty Col with `options`', function(assert) {
1467     assert.expect(9);
1468     var opts = {a: 1, b: 2};
1469     _.forEach([undefined, null, false], function(falsey) {
1470       var Collection = Backbone.Collection.extend({
1471         initialize: function(models, options) {
1472           assert.strictEqual(models, falsey);
1473           assert.strictEqual(options, opts);
1474         }
1475       });
1476
1477       var collection = new Collection(falsey, opts);
1478       assert.strictEqual(collection.length, 0);
1479     });
1480   });
1481
1482   QUnit.test('`add` overrides `set` flags', function(assert) {
1483     var collection = new Backbone.Collection();
1484     collection.once('add', function(model, coll, options) {
1485       coll.add({id: 2}, options);
1486     });
1487     collection.set({id: 1});
1488     assert.equal(collection.length, 2);
1489   });
1490
1491   QUnit.test('#2606 - Collection#create, success arguments', function(assert) {
1492     assert.expect(1);
1493     var collection = new Backbone.Collection;
1494     collection.url = 'test';
1495     collection.create({}, {
1496       success: function(model, resp, options) {
1497         assert.strictEqual(resp, 'response');
1498       }
1499     });
1500     this.ajaxSettings.success('response');
1501   });
1502
1503   QUnit.test('#2612 - nested `parse` works with `Collection#set`', function(assert) {
1504
1505     var Job = Backbone.Model.extend({
1506       constructor: function() {
1507         this.items = new Items();
1508         Backbone.Model.apply(this, arguments);
1509       },
1510       parse: function(attrs) {
1511         this.items.set(attrs.items, {parse: true});
1512         return _.omit(attrs, 'items');
1513       }
1514     });
1515
1516     var Item = Backbone.Model.extend({
1517       constructor: function() {
1518         this.subItems = new Backbone.Collection();
1519         Backbone.Model.apply(this, arguments);
1520       },
1521       parse: function(attrs) {
1522         this.subItems.set(attrs.subItems, {parse: true});
1523         return _.omit(attrs, 'subItems');
1524       }
1525     });
1526
1527     var Items = Backbone.Collection.extend({
1528       model: Item
1529     });
1530
1531     var data = {
1532       name: 'JobName',
1533       id: 1,
1534       items: [{
1535         id: 1,
1536         name: 'Sub1',
1537         subItems: [
1538           {id: 1, subName: 'One'},
1539           {id: 2, subName: 'Two'}
1540         ]
1541       }, {
1542         id: 2,
1543         name: 'Sub2',
1544         subItems: [
1545           {id: 3, subName: 'Three'},
1546           {id: 4, subName: 'Four'}
1547         ]
1548       }]
1549     };
1550
1551     var newData = {
1552       name: 'NewJobName',
1553       id: 1,
1554       items: [{
1555         id: 1,
1556         name: 'NewSub1',
1557         subItems: [
1558           {id: 1, subName: 'NewOne'},
1559           {id: 2, subName: 'NewTwo'}
1560         ]
1561       }, {
1562         id: 2,
1563         name: 'NewSub2',
1564         subItems: [
1565           {id: 3, subName: 'NewThree'},
1566           {id: 4, subName: 'NewFour'}
1567         ]
1568       }]
1569     };
1570
1571     var job = new Job(data, {parse: true});
1572     assert.equal(job.get('name'), 'JobName');
1573     assert.equal(job.items.at(0).get('name'), 'Sub1');
1574     assert.equal(job.items.length, 2);
1575     assert.equal(job.items.get(1).subItems.get(1).get('subName'), 'One');
1576     assert.equal(job.items.get(2).subItems.get(3).get('subName'), 'Three');
1577     job.set(job.parse(newData, {parse: true}));
1578     assert.equal(job.get('name'), 'NewJobName');
1579     assert.equal(job.items.at(0).get('name'), 'NewSub1');
1580     assert.equal(job.items.length, 2);
1581     assert.equal(job.items.get(1).subItems.get(1).get('subName'), 'NewOne');
1582     assert.equal(job.items.get(2).subItems.get(3).get('subName'), 'NewThree');
1583   });
1584
1585   QUnit.test('_addReference binds all collection events & adds to the lookup hashes', function(assert) {
1586     assert.expect(9);
1587
1588     var calls = {add: 0, remove: 0};
1589
1590     var Collection = Backbone.Collection.extend({
1591
1592       _addReference: function(model) {
1593         Backbone.Collection.prototype._addReference.apply(this, arguments);
1594         calls.add++;
1595         assert.equal(model, this._byId[model.id]);
1596         assert.equal(model, this._byId[model.cid]);
1597         assert.equal(model._events.all.length, 1);
1598       },
1599
1600       _removeReference: function(model) {
1601         Backbone.Collection.prototype._removeReference.apply(this, arguments);
1602         calls.remove++;
1603         assert.equal(this._byId[model.id], void 0);
1604         assert.equal(this._byId[model.cid], void 0);
1605         assert.equal(model.collection, void 0);
1606         assert.equal(model._events, void 0);
1607       }
1608
1609     });
1610
1611     var collection = new Collection();
1612     var model = collection.add({id: 1});
1613     collection.remove(model);
1614
1615     assert.equal(calls.add, 1);
1616     assert.equal(calls.remove, 1);
1617   });
1618
1619   QUnit.test('Do not allow duplicate models to be `add`ed or `set`', function(assert) {
1620     var collection = new Backbone.Collection();
1621
1622     collection.add([{id: 1}, {id: 1}]);
1623     assert.equal(collection.length, 1);
1624     assert.equal(collection.models.length, 1);
1625
1626     collection.set([{id: 1}, {id: 1}]);
1627     assert.equal(collection.length, 1);
1628     assert.equal(collection.models.length, 1);
1629   });
1630
1631   QUnit.test('#3020: #set with {add: false} should not throw.', function(assert) {
1632     assert.expect(2);
1633     var collection = new Backbone.Collection;
1634     collection.set([{id: 1}], {add: false});
1635     assert.strictEqual(collection.length, 0);
1636     assert.strictEqual(collection.models.length, 0);
1637   });
1638
1639   QUnit.test('create with wait, model instance, #3028', function(assert) {
1640     assert.expect(1);
1641     var collection = new Backbone.Collection();
1642     var model = new Backbone.Model({id: 1});
1643     model.sync = function(){
1644       assert.equal(this.collection, collection);
1645     };
1646     collection.create(model, {wait: true});
1647   });
1648
1649   QUnit.test('modelId', function(assert) {
1650     var Stooge = Backbone.Model.extend();
1651     var StoogeCollection = Backbone.Collection.extend({model: Stooge});
1652
1653     // Default to using `Collection::model::idAttribute`.
1654     assert.equal(StoogeCollection.prototype.modelId({id: 1}), 1);
1655     Stooge.prototype.idAttribute = '_id';
1656     assert.equal(StoogeCollection.prototype.modelId({_id: 1}), 1);
1657   });
1658
1659   QUnit.test('Polymorphic models work with "simple" constructors', function(assert) {
1660     var A = Backbone.Model.extend();
1661     var B = Backbone.Model.extend();
1662     var C = Backbone.Collection.extend({
1663       model: function(attrs) {
1664         return attrs.type === 'a' ? new A(attrs) : new B(attrs);
1665       }
1666     });
1667     var collection = new C([{id: 1, type: 'a'}, {id: 2, type: 'b'}]);
1668     assert.equal(collection.length, 2);
1669     assert.ok(collection.at(0) instanceof A);
1670     assert.equal(collection.at(0).id, 1);
1671     assert.ok(collection.at(1) instanceof B);
1672     assert.equal(collection.at(1).id, 2);
1673   });
1674
1675   QUnit.test('Polymorphic models work with "advanced" constructors', function(assert) {
1676     var A = Backbone.Model.extend({idAttribute: '_id'});
1677     var B = Backbone.Model.extend({idAttribute: '_id'});
1678     var C = Backbone.Collection.extend({
1679       model: Backbone.Model.extend({
1680         constructor: function(attrs) {
1681           return attrs.type === 'a' ? new A(attrs) : new B(attrs);
1682         },
1683
1684         idAttribute: '_id'
1685       })
1686     });
1687     var collection = new C([{_id: 1, type: 'a'}, {_id: 2, type: 'b'}]);
1688     assert.equal(collection.length, 2);
1689     assert.ok(collection.at(0) instanceof A);
1690     assert.equal(collection.at(0), collection.get(1));
1691     assert.ok(collection.at(1) instanceof B);
1692     assert.equal(collection.at(1), collection.get(2));
1693
1694     C = Backbone.Collection.extend({
1695       model: function(attrs) {
1696         return attrs.type === 'a' ? new A(attrs) : new B(attrs);
1697       },
1698
1699       modelId: function(attrs) {
1700         return attrs.type + '-' + attrs.id;
1701       }
1702     });
1703     collection = new C([{id: 1, type: 'a'}, {id: 1, type: 'b'}]);
1704     assert.equal(collection.length, 2);
1705     assert.ok(collection.at(0) instanceof A);
1706     assert.equal(collection.at(0), collection.get('a-1'));
1707     assert.ok(collection.at(1) instanceof B);
1708     assert.equal(collection.at(1), collection.get('b-1'));
1709   });
1710
1711   QUnit.test('Collection with polymorphic models receives default id from modelId', function(assert) {
1712     assert.expect(6);
1713     // When the polymorphic models use 'id' for the idAttribute, all is fine.
1714     var C1 = Backbone.Collection.extend({
1715       model: function(attrs) {
1716         return new Backbone.Model(attrs);
1717       }
1718     });
1719     var c1 = new C1({id: 1});
1720     assert.equal(c1.get(1).id, 1);
1721     assert.equal(c1.modelId({id: 1}), 1);
1722
1723     // If the polymorphic models define their own idAttribute,
1724     // the modelId method should be overridden, for the reason below.
1725     var M = Backbone.Model.extend({
1726       idAttribute: '_id'
1727     });
1728     var C2 = Backbone.Collection.extend({
1729       model: function(attrs) {
1730         return new M(attrs);
1731       }
1732     });
1733     var c2 = new C2({'_id': 1});
1734     assert.equal(c2.get(1), void 0);
1735     assert.equal(c2.modelId(c2.at(0).attributes), void 0);
1736     var m = new M({'_id': 2});
1737     c2.add(m);
1738     assert.equal(c2.get(2), void 0);
1739     assert.equal(c2.modelId(m.attributes), void 0);
1740   });
1741
1742   QUnit.test('#3039: adding at index fires with correct at', function(assert) {
1743     assert.expect(3);
1744     var collection = new Backbone.Collection([{at: 0}, {at: 4}]);
1745     collection.on('add', function(model, coll, options) {
1746       assert.equal(model.get('at'), options.index);
1747     });
1748     collection.add([{at: 1}, {at: 2}, {at: 3}], {at: 1});
1749   });
1750
1751   QUnit.test('#3039: index is not sent when at is not specified', function(assert) {
1752     assert.expect(2);
1753     var collection = new Backbone.Collection([{at: 0}]);
1754     collection.on('add', function(model, coll, options) {
1755       assert.equal(undefined, options.index);
1756     });
1757     collection.add([{at: 1}, {at: 2}]);
1758   });
1759
1760   QUnit.test('#3199 - Order changing should trigger a sort', function(assert) {
1761     assert.expect(1);
1762     var one = new Backbone.Model({id: 1});
1763     var two = new Backbone.Model({id: 2});
1764     var three = new Backbone.Model({id: 3});
1765     var collection = new Backbone.Collection([one, two, three]);
1766     collection.on('sort', function() {
1767       assert.ok(true);
1768     });
1769     collection.set([{id: 3}, {id: 2}, {id: 1}]);
1770   });
1771
1772   QUnit.test('#3199 - Adding a model should trigger a sort', function(assert) {
1773     assert.expect(1);
1774     var one = new Backbone.Model({id: 1});
1775     var two = new Backbone.Model({id: 2});
1776     var three = new Backbone.Model({id: 3});
1777     var collection = new Backbone.Collection([one, two, three]);
1778     collection.on('sort', function() {
1779       assert.ok(true);
1780     });
1781     collection.set([{id: 1}, {id: 2}, {id: 3}, {id: 0}]);
1782   });
1783
1784   QUnit.test('#3199 - Order not changing should not trigger a sort', function(assert) {
1785     assert.expect(0);
1786     var one = new Backbone.Model({id: 1});
1787     var two = new Backbone.Model({id: 2});
1788     var three = new Backbone.Model({id: 3});
1789     var collection = new Backbone.Collection([one, two, three]);
1790     collection.on('sort', function() {
1791       assert.ok(false);
1792     });
1793     collection.set([{id: 1}, {id: 2}, {id: 3}]);
1794   });
1795
1796   QUnit.test('add supports negative indexes', function(assert) {
1797     assert.expect(1);
1798     var collection = new Backbone.Collection([{id: 1}]);
1799     collection.add([{id: 2}, {id: 3}], {at: -1});
1800     collection.add([{id: 2.5}], {at: -2});
1801     collection.add([{id: 0.5}], {at: -6});
1802     assert.equal(collection.pluck('id').join(','), '0.5,1,2,2.5,3');
1803   });
1804
1805   QUnit.test('#set accepts options.at as a string', function(assert) {
1806     assert.expect(1);
1807     var collection = new Backbone.Collection([{id: 1}, {id: 2}]);
1808     collection.add([{id: 3}], {at: '1'});
1809     assert.deepEqual(collection.pluck('id'), [1, 3, 2]);
1810   });
1811
1812   QUnit.test('adding multiple models triggers `update` event once', function(assert) {
1813     assert.expect(1);
1814     var collection = new Backbone.Collection;
1815     collection.on('update', function() { assert.ok(true); });
1816     collection.add([{id: 1}, {id: 2}, {id: 3}]);
1817   });
1818
1819   QUnit.test('removing models triggers `update` event once', function(assert) {
1820     assert.expect(1);
1821     var collection = new Backbone.Collection([{id: 1}, {id: 2}, {id: 3}]);
1822     collection.on('update', function() { assert.ok(true); });
1823     collection.remove([{id: 1}, {id: 2}]);
1824   });
1825
1826   QUnit.test('remove does not trigger `update` when nothing removed', function(assert) {
1827     assert.expect(0);
1828     var collection = new Backbone.Collection([{id: 1}, {id: 2}]);
1829     collection.on('update', function() { assert.ok(false); });
1830     collection.remove([{id: 3}]);
1831   });
1832
1833   QUnit.test('set triggers `set` event once', function(assert) {
1834     assert.expect(1);
1835     var collection = new Backbone.Collection([{id: 1}, {id: 2}]);
1836     collection.on('update', function() { assert.ok(true); });
1837     collection.set([{id: 1}, {id: 3}]);
1838   });
1839
1840   QUnit.test('set does not trigger `update` event when nothing added nor removed', function(assert) {
1841     var collection = new Backbone.Collection([{id: 1}, {id: 2}]);
1842     collection.on('update', function(coll, options) {
1843       assert.equal(options.changes.added.length, 0);
1844       assert.equal(options.changes.removed.length, 0);
1845       assert.equal(options.changes.merged.length, 2);
1846     });
1847     collection.set([{id: 1}, {id: 2}]);
1848   });
1849
1850   QUnit.test('#3610 - invoke collects arguments', function(assert) {
1851     assert.expect(3);
1852     var Model = Backbone.Model.extend({
1853       method: function(x, y, z) {
1854         assert.equal(x, 1);
1855         assert.equal(y, 2);
1856         assert.equal(z, 3);
1857       }
1858     });
1859     var Collection = Backbone.Collection.extend({
1860       model: Model
1861     });
1862     var collection = new Collection([{id: 1}]);
1863     collection.invoke('method', 1, 2, 3);
1864   });
1865
1866   QUnit.test('#3662 - triggering change without model will not error', function(assert) {
1867     assert.expect(1);
1868     var collection = new Backbone.Collection([{id: 1}]);
1869     var model = collection.first();
1870     collection.on('change', function(m) {
1871       assert.equal(m, undefined);
1872     });
1873     model.trigger('change');
1874   });
1875
1876   QUnit.test('#3871 - falsy parse result creates empty collection', function(assert) {
1877     var collection = new (Backbone.Collection.extend({
1878       parse: function(data, options) {}
1879     }));
1880     collection.set('', {parse: true});
1881     assert.equal(collection.length, 0);
1882   });
1883
1884   QUnit.test("#3711 - remove's `update` event returns one removed model", function(assert) {
1885     var model = new Backbone.Model({id: 1, title: 'First Post'});
1886     var collection = new Backbone.Collection([model]);
1887     collection.on('update', function(context, options) {
1888       var changed = options.changes;
1889       assert.deepEqual(changed.added, []);
1890       assert.deepEqual(changed.merged, []);
1891       assert.strictEqual(changed.removed[0], model);
1892     });
1893     collection.remove(model);
1894   });
1895
1896   QUnit.test("#3711 - remove's `update` event returns multiple removed models", function(assert) {
1897     var model = new Backbone.Model({id: 1, title: 'First Post'});
1898     var model2 = new Backbone.Model({id: 2, title: 'Second Post'});
1899     var collection = new Backbone.Collection([model, model2]);
1900     collection.on('update', function(context, options) {
1901       var changed = options.changes;
1902       assert.deepEqual(changed.added, []);
1903       assert.deepEqual(changed.merged, []);
1904       assert.ok(changed.removed.length === 2);
1905
1906       assert.ok(changed.removed.indexOf(model) > -1 && changed.removed.indexOf(model2) > -1);
1907     });
1908     collection.remove([model, model2]);
1909   });
1910
1911   QUnit.test("#3711 - set's `update` event returns one added model", function(assert) {
1912     var model = new Backbone.Model({id: 1, title: 'First Post'});
1913     var collection = new Backbone.Collection();
1914     collection.on('update', function(context, options) {
1915       var addedModels = options.changes.added;
1916       assert.ok(addedModels.length === 1);
1917       assert.strictEqual(addedModels[0], model);
1918     });
1919     collection.set(model);
1920   });
1921
1922   QUnit.test("#3711 - set's `update` event returns multiple added models", function(assert) {
1923     var model = new Backbone.Model({id: 1, title: 'First Post'});
1924     var model2 = new Backbone.Model({id: 2, title: 'Second Post'});
1925     var collection = new Backbone.Collection();
1926     collection.on('update', function(context, options) {
1927       var addedModels = options.changes.added;
1928       assert.ok(addedModels.length === 2);
1929       assert.strictEqual(addedModels[0], model);
1930       assert.strictEqual(addedModels[1], model2);
1931     });
1932     collection.set([model, model2]);
1933   });
1934
1935   QUnit.test("#3711 - set's `update` event returns one removed model", function(assert) {
1936     var model = new Backbone.Model({id: 1, title: 'First Post'});
1937     var model2 = new Backbone.Model({id: 2, title: 'Second Post'});
1938     var model3 = new Backbone.Model({id: 3, title: 'My Last Post'});
1939     var collection = new Backbone.Collection([model]);
1940     collection.on('update', function(context, options) {
1941       var changed = options.changes;
1942       assert.equal(changed.added.length, 2);
1943       assert.equal(changed.merged.length, 0);
1944       assert.ok(changed.removed.length === 1);
1945       assert.strictEqual(changed.removed[0], model);
1946     });
1947     collection.set([model2, model3]);
1948   });
1949
1950   QUnit.test("#3711 - set's `update` event returns multiple removed models", function(assert) {
1951     var model = new Backbone.Model({id: 1, title: 'First Post'});
1952     var model2 = new Backbone.Model({id: 2, title: 'Second Post'});
1953     var model3 = new Backbone.Model({id: 3, title: 'My Last Post'});
1954     var collection = new Backbone.Collection([model, model2]);
1955     collection.on('update', function(context, options) {
1956       var removedModels = options.changes.removed;
1957       assert.ok(removedModels.length === 2);
1958       assert.strictEqual(removedModels[0], model);
1959       assert.strictEqual(removedModels[1], model2);
1960     });
1961     collection.set([model3]);
1962   });
1963
1964   QUnit.test("#3711 - set's `update` event returns one merged model", function(assert) {
1965     var model = new Backbone.Model({id: 1, title: 'First Post'});
1966     var model2 = new Backbone.Model({id: 2, title: 'Second Post'});
1967     var model2Update = new Backbone.Model({id: 2, title: 'Second Post V2'});
1968     var collection = new Backbone.Collection([model, model2]);
1969     collection.on('update', function(context, options) {
1970       var mergedModels = options.changes.merged;
1971       assert.ok(mergedModels.length === 1);
1972       assert.strictEqual(mergedModels[0].get('title'), model2Update.get('title'));
1973     });
1974     collection.set([model2Update]);
1975   });
1976
1977   QUnit.test("#3711 - set's `update` event returns multiple merged models", function(assert) {
1978     var model = new Backbone.Model({id: 1, title: 'First Post'});
1979     var modelUpdate = new Backbone.Model({id: 1, title: 'First Post V2'});
1980     var model2 = new Backbone.Model({id: 2, title: 'Second Post'});
1981     var model2Update = new Backbone.Model({id: 2, title: 'Second Post V2'});
1982     var collection = new Backbone.Collection([model, model2]);
1983     collection.on('update', function(context, options) {
1984       var mergedModels = options.changes.merged;
1985       assert.ok(mergedModels.length === 2);
1986       assert.strictEqual(mergedModels[0].get('title'), model2Update.get('title'));
1987       assert.strictEqual(mergedModels[1].get('title'), modelUpdate.get('title'));
1988     });
1989     collection.set([model2Update, modelUpdate]);
1990   });
1991
1992   QUnit.test("#3711 - set's `update` event should not be triggered adding a model which already exists exactly alike", function(assert) {
1993     var fired = false;
1994     var model = new Backbone.Model({id: 1, title: 'First Post'});
1995     var collection = new Backbone.Collection([model]);
1996     collection.on('update', function(context, options) {
1997       fired = true;
1998     });
1999     collection.set([model]);
2000     assert.equal(fired, false);
2001   });
2002
2003 })();