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