PageRenderTime 47ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/test/collection.js

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