PageRenderTime 918ms CodeModel.GetById 202ms app.highlight 466ms RepoModel.GetById 238ms app.codeStats 0ms

/out/components/lodash/vendor/backbone/test/collection.js

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