PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/test/collection.js

https://github.com/gotomypc/backbone
JavaScript | 671 lines | 607 code | 64 blank | 0 comment | 24 complexity | 2e31957ee31012fe304b8c8b3c797101 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("Collection: new and sort", 7, function() {
  16. equal(col.first(), a, "a should be first");
  17. equal(col.last(), d, "d should be last");
  18. col.comparator = function(a, b) {
  19. return a.id > b.id ? -1 : 1;
  20. };
  21. col.sort();
  22. equal(col.first(), a, "a should be first");
  23. equal(col.last(), d, "d should be last");
  24. col.comparator = function(model) { return model.id; };
  25. col.sort();
  26. equal(col.first(), d, "d should be first");
  27. equal(col.last(), a, "a should be last");
  28. equal(col.length, 4);
  29. });
  30. test("Collection: new and parse", 3, function() {
  31. var Collection = Backbone.Collection.extend({
  32. parse : function(data) {
  33. return _.filter(data, function(datum) {
  34. return datum.a % 2 === 0;
  35. });
  36. }
  37. });
  38. var models = [{a: 1}, {a: 2}, {a: 3}, {a: 4}];
  39. var collection = new Collection(models, {parse: true});
  40. strictEqual(collection.length, 2);
  41. strictEqual(collection.first().get('a'), 2);
  42. strictEqual(collection.last().get('a'), 4);
  43. });
  44. test("Collection: get, getByCid", 3, function() {
  45. equal(col.get(0), d);
  46. equal(col.get(2), b);
  47. equal(col.getByCid(col.first().cid), col.first());
  48. });
  49. test("Collection: get with non-default ids", 2, function() {
  50. var col = new Backbone.Collection();
  51. var MongoModel = Backbone.Model.extend({
  52. idAttribute: '_id'
  53. });
  54. var model = new MongoModel({_id: 100});
  55. col.push(model);
  56. equal(col.get(100), model);
  57. model.set({_id: 101});
  58. equal(col.get(101), model);
  59. });
  60. test("Collection: update index when id changes", 3, function() {
  61. var col = new Backbone.Collection();
  62. col.add([
  63. {id : 0, name : 'one'},
  64. {id : 1, name : 'two'}
  65. ]);
  66. var one = col.get(0);
  67. equal(one.get('name'), 'one');
  68. one.set({id : 101});
  69. equal(col.get(0), null);
  70. equal(col.get(101).get('name'), 'one');
  71. });
  72. test("Collection: at", 1, function() {
  73. equal(col.at(2), c);
  74. });
  75. test("Collection: pluck", 1, function() {
  76. equal(col.pluck('label').join(' '), 'a b c d');
  77. });
  78. test("Collection: add", 11, function() {
  79. var added, opts, secondAdded;
  80. added = opts = secondAdded = null;
  81. e = new Backbone.Model({id: 10, label : 'e'});
  82. otherCol.add(e);
  83. otherCol.on('add', function() {
  84. secondAdded = true;
  85. });
  86. col.on('add', function(model, collection, options){
  87. added = model.get('label');
  88. equal(options.index, 4);
  89. opts = options;
  90. });
  91. col.add(e, {amazing: true});
  92. equal(added, 'e');
  93. equal(col.length, 5);
  94. equal(col.last(), e);
  95. equal(otherCol.length, 1);
  96. equal(secondAdded, null);
  97. ok(opts.amazing);
  98. var f = new Backbone.Model({id: 20, label : 'f'});
  99. var g = new Backbone.Model({id: 21, label : 'g'});
  100. var h = new Backbone.Model({id: 22, label : 'h'});
  101. var atCol = new Backbone.Collection([f, g, h]);
  102. equal(atCol.length, 3);
  103. atCol.add(e, {at: 1});
  104. equal(atCol.length, 4);
  105. equal(atCol.at(1), e);
  106. equal(atCol.last(), h);
  107. });
  108. test("Collection: add multiple models", 6, function() {
  109. var col = new Backbone.Collection([{at: 0}, {at: 1}, {at: 9}]);
  110. col.add([{at: 2}, {at: 3}, {at: 4}, {at: 5}, {at: 6}, {at: 7}, {at: 8}], {at: 2});
  111. for (var i = 0; i <= 5; i++) {
  112. equal(col.at(i).get('at'), i);
  113. }
  114. });
  115. test("Collection: add; at should have preference over comparator", 1, function() {
  116. var Col = Backbone.Collection.extend({
  117. comparator: function(a,b) {
  118. return a.id > b.id ? -1 : 1;
  119. }
  120. });
  121. var col = new Col([{id: 2}, {id: 3}]);
  122. col.add(new Backbone.Model({id: 1}), {at: 1});
  123. equal(col.pluck('id').join(' '), '3 1 2');
  124. });
  125. test("Collection: can't add model to collection twice", function() {
  126. var col = new Backbone.Collection([{id: 1}, {id: 2}, {id: 1}, {id: 2}, {id: 3}]);
  127. equal(col.pluck('id').join(' '), '1 2 3');
  128. });
  129. test("Collection: can't add different model with same id to collection twice", 1, function() {
  130. var col = new Backbone.Collection;
  131. col.unshift({id: 101});
  132. col.add({id: 101});
  133. equal(col.length, 1);
  134. });
  135. test("Collection: merge in duplicate models with {merge: true}", 3, function() {
  136. var col = new Backbone.Collection;
  137. col.add([{id: 1, name: 'Moe'}, {id: 2, name: 'Curly'}, {id: 3, name: 'Larry'}]);
  138. col.add({id: 1, name: 'Moses'});
  139. equal(col.first().get('name'), 'Moe');
  140. col.add({id: 1, name: 'Moses'}, {merge: true});
  141. equal(col.first().get('name'), 'Moses');
  142. col.add({id: 1, name: 'Tim'}, {merge: true, silent: true});
  143. equal(col.first().get('name'), 'Tim');
  144. });
  145. test("Collection: add model to multiple collections", 10, function() {
  146. var counter = 0;
  147. var e = new Backbone.Model({id: 10, label : 'e'});
  148. e.on('add', function(model, collection) {
  149. counter++;
  150. equal(e, model);
  151. if (counter > 1) {
  152. equal(collection, colF);
  153. } else {
  154. equal(collection, colE);
  155. }
  156. });
  157. var colE = new Backbone.Collection([]);
  158. colE.on('add', function(model, collection) {
  159. equal(e, model);
  160. equal(colE, collection);
  161. });
  162. var colF = new Backbone.Collection([]);
  163. colF.on('add', function(model, collection) {
  164. equal(e, model);
  165. equal(colF, collection);
  166. });
  167. colE.add(e);
  168. equal(e.collection, colE);
  169. colF.add(e);
  170. equal(e.collection, colE);
  171. });
  172. test("Collection: add model with parse", 1, function() {
  173. var Model = Backbone.Model.extend({
  174. parse: function(obj) {
  175. obj.value += 1;
  176. return obj;
  177. }
  178. });
  179. var Col = Backbone.Collection.extend({model: Model});
  180. var col = new Col;
  181. col.add({value: 1}, {parse: true});
  182. equal(col.at(0).get('value'), 2);
  183. });
  184. test("Collection: add model to collection with sort()-style comparator", 3, function() {
  185. var col = new Backbone.Collection;
  186. col.comparator = function(a, b) {
  187. return a.get('name') < b.get('name') ? -1 : 1;
  188. };
  189. var tom = new Backbone.Model({name: 'Tom'});
  190. var rob = new Backbone.Model({name: 'Rob'});
  191. var tim = new Backbone.Model({name: 'Tim'});
  192. col.add(tom);
  193. col.add(rob);
  194. col.add(tim);
  195. equal(col.indexOf(rob), 0);
  196. equal(col.indexOf(tim), 1);
  197. equal(col.indexOf(tom), 2);
  198. });
  199. test("Collection: comparator that depends on `this`", 1, function() {
  200. var col = new Backbone.Collection;
  201. col.negative = function(num) {
  202. return -num;
  203. };
  204. col.comparator = function(a) {
  205. return this.negative(a.id);
  206. };
  207. col.add([{id: 1}, {id: 2}, {id: 3}]);
  208. equal(col.pluck('id').join(' '), '3 2 1');
  209. });
  210. test("Collection: remove", 5, function() {
  211. var removed = null;
  212. var otherRemoved = null;
  213. col.on('remove', function(model, col, options) {
  214. removed = model.get('label');
  215. equal(options.index, 3);
  216. });
  217. otherCol.on('remove', function(model, col, options) {
  218. otherRemoved = true;
  219. });
  220. col.remove(d);
  221. equal(removed, 'd');
  222. equal(col.length, 3);
  223. equal(col.first(), a);
  224. equal(otherRemoved, null);
  225. });
  226. test("Collection: shift and pop", 2, function() {
  227. var col = new Backbone.Collection([{a: 'a'}, {b: 'b'}, {c: 'c'}]);
  228. equal(col.shift().get('a'), 'a');
  229. equal(col.pop().get('c'), 'c');
  230. });
  231. test("Collection: slice", 2, function() {
  232. var col = new Backbone.Collection([{a: 'a'}, {b: 'b'}, {c: 'c'}]);
  233. var array = col.slice(1, 3);
  234. equal(array.length, 2);
  235. equal(array[0].get('b'), 'b');
  236. });
  237. test("Collection: events are unbound on remove", 3, function() {
  238. var counter = 0;
  239. var dj = new Backbone.Model();
  240. var emcees = new Backbone.Collection([dj]);
  241. emcees.on('change', function(){ counter++; });
  242. dj.set({name : 'Kool'});
  243. equal(counter, 1);
  244. emcees.reset([]);
  245. equal(dj.collection, undefined);
  246. dj.set({name : 'Shadow'});
  247. equal(counter, 1);
  248. });
  249. test("Collection: remove in multiple collections", 7, function() {
  250. var modelData = {
  251. id : 5,
  252. title : 'Othello'
  253. };
  254. var passed = false;
  255. var e = new Backbone.Model(modelData);
  256. var f = new Backbone.Model(modelData);
  257. f.on('remove', function() {
  258. passed = true;
  259. });
  260. var colE = new Backbone.Collection([e]);
  261. var colF = new Backbone.Collection([f]);
  262. ok(e != f);
  263. ok(colE.length == 1);
  264. ok(colF.length == 1);
  265. colE.remove(e);
  266. equal(passed, false);
  267. ok(colE.length == 0);
  268. colF.remove(e);
  269. ok(colF.length == 0);
  270. equal(passed, true);
  271. });
  272. test("Collection: remove same model in multiple collection", 16, function() {
  273. var counter = 0;
  274. var e = new Backbone.Model({id: 5, title: 'Othello'});
  275. e.on('remove', function(model, collection) {
  276. counter++;
  277. equal(e, model);
  278. if (counter > 1) {
  279. equal(collection, colE);
  280. } else {
  281. equal(collection, colF);
  282. }
  283. });
  284. var colE = new Backbone.Collection([e]);
  285. colE.on('remove', function(model, collection) {
  286. equal(e, model);
  287. equal(colE, collection);
  288. });
  289. var colF = new Backbone.Collection([e]);
  290. colF.on('remove', function(model, collection) {
  291. equal(e, model);
  292. equal(colF, collection);
  293. });
  294. equal(colE, e.collection);
  295. colF.remove(e);
  296. ok(colF.length == 0);
  297. ok(colE.length == 1);
  298. equal(counter, 1);
  299. equal(colE, e.collection);
  300. colE.remove(e);
  301. equal(null, e.collection);
  302. ok(colE.length == 0);
  303. equal(counter, 2);
  304. });
  305. test("Collection: model destroy removes from all collections", 3, function() {
  306. var e = new Backbone.Model({id: 5, title: 'Othello'});
  307. e.sync = function(method, model, options) { options.success({}); };
  308. var colE = new Backbone.Collection([e]);
  309. var colF = new Backbone.Collection([e]);
  310. e.destroy();
  311. ok(colE.length == 0);
  312. ok(colF.length == 0);
  313. equal(undefined, e.collection);
  314. });
  315. test("Colllection: non-persisted model destroy removes from all collections", 3, function() {
  316. var e = new Backbone.Model({title: 'Othello'});
  317. e.sync = function(method, model, options) { throw "should not be called"; };
  318. var colE = new Backbone.Collection([e]);
  319. var colF = new Backbone.Collection([e]);
  320. e.destroy();
  321. ok(colE.length == 0);
  322. ok(colF.length == 0);
  323. equal(undefined, e.collection);
  324. });
  325. test("Collection: fetch", 4, function() {
  326. var collection = new Backbone.Collection;
  327. collection.url = '/test';
  328. collection.fetch();
  329. equal(this.syncArgs.method, 'read');
  330. equal(this.syncArgs.model, collection);
  331. equal(this.syncArgs.options.parse, true);
  332. collection.fetch({parse: false});
  333. equal(this.syncArgs.options.parse, false);
  334. });
  335. test("Collection: create", 4, function() {
  336. var collection = new Backbone.Collection;
  337. collection.url = '/test';
  338. var model = collection.create({label: 'f'}, {wait: true});
  339. equal(this.syncArgs.method, 'create');
  340. equal(this.syncArgs.model, model);
  341. equal(model.get('label'), 'f');
  342. equal(model.collection, collection);
  343. });
  344. test("Collection: create enforces validation", 1, function() {
  345. var ValidatingModel = Backbone.Model.extend({
  346. validate: function(attrs) {
  347. return "fail";
  348. }
  349. });
  350. var ValidatingCollection = Backbone.Collection.extend({
  351. model: ValidatingModel
  352. });
  353. var col = new ValidatingCollection();
  354. equal(col.create({"foo":"bar"}), false);
  355. });
  356. test("Collection: a failing create runs the error callback", 1, function() {
  357. var ValidatingModel = Backbone.Model.extend({
  358. validate: function(attrs) {
  359. return "fail";
  360. }
  361. });
  362. var ValidatingCollection = Backbone.Collection.extend({
  363. model: ValidatingModel
  364. });
  365. var flag = false;
  366. var callback = function(model, error) { flag = true; };
  367. var col = new ValidatingCollection();
  368. col.create({"foo":"bar"}, { error: callback });
  369. equal(flag, true);
  370. });
  371. test("collection: initialize", 1, function() {
  372. var Collection = Backbone.Collection.extend({
  373. initialize: function() {
  374. this.one = 1;
  375. }
  376. });
  377. var coll = new Collection;
  378. equal(coll.one, 1);
  379. });
  380. test("Collection: toJSON", 1, function() {
  381. equal(JSON.stringify(col), '[{"id":3,"label":"a"},{"id":2,"label":"b"},{"id":1,"label":"c"},{"id":0,"label":"d"}]');
  382. });
  383. test("Collection: where", 6, function() {
  384. var coll = new Backbone.Collection([
  385. {a: 1},
  386. {a: 1},
  387. {a: 1, b: 2},
  388. {a: 2, b: 2},
  389. {a: 3}
  390. ]);
  391. equal(coll.where({a: 1}).length, 3);
  392. equal(coll.where({a: 2}).length, 1);
  393. equal(coll.where({a: 3}).length, 1);
  394. equal(coll.where({b: 1}).length, 0);
  395. equal(coll.where({b: 2}).length, 2);
  396. equal(coll.where({a: 1, b: 2}).length, 1);
  397. });
  398. test("Collection: Underscore methods", 13, function() {
  399. equal(col.map(function(model){ return model.get('label'); }).join(' '), 'a b c d');
  400. equal(col.any(function(model){ return model.id === 100; }), false);
  401. equal(col.any(function(model){ return model.id === 0; }), true);
  402. equal(col.indexOf(b), 1);
  403. equal(col.size(), 4);
  404. equal(col.rest().length, 3);
  405. ok(!_.include(col.rest()), a);
  406. ok(!_.include(col.rest()), d);
  407. ok(!col.isEmpty());
  408. ok(!_.include(col.without(d)), d);
  409. equal(col.max(function(model){ return model.id; }).id, 3);
  410. equal(col.min(function(model){ return model.id; }).id, 0);
  411. deepEqual(col.chain()
  412. .filter(function(o){ return o.id % 2 === 0; })
  413. .map(function(o){ return o.id * 2; })
  414. .value(),
  415. [4, 0]);
  416. });
  417. test("Collection: reset", 10, function() {
  418. var resetCount = 0;
  419. var models = col.models;
  420. col.on('reset', function() { resetCount += 1; });
  421. col.reset([]);
  422. equal(resetCount, 1);
  423. equal(col.length, 0);
  424. equal(col.last(), null);
  425. col.reset(models);
  426. equal(resetCount, 2);
  427. equal(col.length, 4);
  428. equal(col.last(), d);
  429. col.reset(_.map(models, function(m){ return m.attributes; }));
  430. equal(resetCount, 3);
  431. equal(col.length, 4);
  432. ok(col.last() !== d);
  433. ok(_.isEqual(col.last().attributes, d.attributes));
  434. });
  435. test("Collection: reset passes caller options", 3, function() {
  436. var Model = Backbone.Model.extend({
  437. initialize: function(attrs, options) {
  438. this.model_parameter = options.model_parameter;
  439. }
  440. });
  441. var col = new (Backbone.Collection.extend({ model: Model }))();
  442. col.reset([{ astring: "green", anumber: 1 }, { astring: "blue", anumber: 2 }], { model_parameter: 'model parameter' });
  443. equal(col.length, 2);
  444. col.each(function(model) {
  445. equal(model.model_parameter, 'model parameter');
  446. });
  447. });
  448. test("Collection: trigger custom events on models", 1, function() {
  449. var fired = null;
  450. a.on("custom", function() { fired = true; });
  451. a.trigger("custom");
  452. equal(fired, true);
  453. });
  454. test("Collection: add does not alter arguments", 2, function(){
  455. var attrs = {};
  456. var models = [attrs];
  457. new Backbone.Collection().add(models);
  458. equal(models.length, 1);
  459. ok(attrs === models[0]);
  460. });
  461. test("#714: access `model.collection` in a brand new model.", 2, function() {
  462. var collection = new Backbone.Collection;
  463. collection.url = '/test';
  464. var Model = Backbone.Model.extend({
  465. set: function(attrs) {
  466. equal(attrs.prop, 'value');
  467. equal(this.collection, collection);
  468. return this;
  469. }
  470. });
  471. collection.model = Model;
  472. collection.create({prop: 'value'});
  473. });
  474. test("#574, remove its own reference to the .models array.", 2, function() {
  475. var col = new Backbone.Collection([
  476. {id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}, {id: 6}
  477. ]);
  478. equal(col.length, 6);
  479. col.remove(col.models);
  480. equal(col.length, 0);
  481. });
  482. test("#861, adding models to a collection which do not pass validation", 1, function() {
  483. raises(function() {
  484. var Model = Backbone.Model.extend({
  485. validate: function(attrs) {
  486. if (attrs.id == 3) return "id can't be 3";
  487. }
  488. });
  489. var Collection = Backbone.Collection.extend({
  490. model: Model
  491. });
  492. var col = new Collection;
  493. col.add([{id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}, {id: 6}]);
  494. }, function(e) {
  495. return e.message === "Can't add an invalid model to a collection";
  496. });
  497. });
  498. test("Collection: index with comparator", 4, function() {
  499. var counter = 0;
  500. var col = new Backbone.Collection([{id: 2}, {id: 4}], {
  501. comparator: function(model){ return model.id; }
  502. }).on('add', function(model, colleciton, options){
  503. if (model.id == 1) {
  504. equal(options.index, 0);
  505. equal(counter++, 0);
  506. }
  507. if (model.id == 3) {
  508. equal(options.index, 2);
  509. equal(counter++, 1);
  510. }
  511. });
  512. col.add([{id: 3}, {id: 1}]);
  513. });
  514. test("Collection: throwing during add leaves consistent state", 4, function() {
  515. var col = new Backbone.Collection();
  516. col.on('test', function() { ok(false); });
  517. col.model = Backbone.Model.extend({
  518. validate: function(attrs){ if (!attrs.valid) return 'invalid'; }
  519. });
  520. var model = new col.model({id: 1, valid: true});
  521. raises(function() { col.add([model, {id: 2}]); });
  522. model.trigger('test');
  523. ok(!col.getByCid(model.cid));
  524. ok(!col.get(1));
  525. equal(col.length, 0);
  526. });
  527. test("Collection: multiple copies of the same model", 3, function() {
  528. var col = new Backbone.Collection();
  529. var model = new Backbone.Model();
  530. col.add([model, model]);
  531. equal(col.length, 1);
  532. col.add([{id: 1}, {id: 1}]);
  533. equal(col.length, 2);
  534. equal(col.last().id, 1);
  535. });
  536. test("#964 - collection.get return inconsistent", 2, function() {
  537. var c = new Backbone.Collection();
  538. ok(c.get(null) === undefined);
  539. ok(c.get() === undefined);
  540. });
  541. test("#1112 - passing options.model sets collection.model", 2, function() {
  542. var Model = Backbone.Model.extend({});
  543. var c = new Backbone.Collection([{id: 1}], {model: Model});
  544. ok(c.model === Model);
  545. ok(c.at(0) instanceof Model);
  546. });
  547. test("null and undefined are invalid ids.", 2, function() {
  548. var model = new Backbone.Model({id: 1});
  549. var collection = new Backbone.Collection([model]);
  550. model.set({id: null});
  551. ok(!collection.get('null'));
  552. model.set({id: 1});
  553. model.set({id: undefined});
  554. ok(!collection.get('undefined'));
  555. });
  556. test("Collection: falsy comparator", 4, function(){
  557. var Col = Backbone.Collection.extend({
  558. comparator: function(model){ return model.id; }
  559. });
  560. var col = new Col();
  561. var colFalse = new Col(null, {comparator: false});
  562. var colNull = new Col(null, {comparator: null});
  563. var colUndefined = new Col(null, {comparator: undefined});
  564. ok(col.comparator);
  565. ok(!colFalse.comparator);
  566. ok(!colNull.comparator);
  567. ok(colUndefined.comparator);
  568. });
  569. test("#1355 - `options` is passed to success callbacks", 2, function(){
  570. var m = new Backbone.Model({x:1});
  571. var col = new Backbone.Collection();
  572. var opts = {
  573. success: function(collection, resp, options){
  574. ok(options);
  575. }
  576. };
  577. col.sync = m.sync = function( method, collection, options ){
  578. options.success();
  579. };
  580. col.fetch(opts);
  581. col.create(m, opts);
  582. });
  583. test("#1412 - Trigger 'sync' event.", 2, function() {
  584. var collection = new Backbone.Collection;
  585. collection.url = '/test';
  586. collection.on('sync', function() { ok(true); });
  587. Backbone.ajax = function(settings){ settings.success(); };
  588. collection.fetch();
  589. collection.create({id: 1});
  590. });
  591. test("#1447 - create with wait adds model.", function() {
  592. var collection = new Backbone.Collection;
  593. var model = new Backbone.Model;
  594. model.sync = function(method, model, options){ options.success(); };
  595. collection.on('add', function(){ ok(true); });
  596. collection.create(model, {wait: true});
  597. });
  598. test("#1448 - add sorts collection after merge.", function() {
  599. var collection = new Backbone.Collection([
  600. {id: 1, x: 1},
  601. {id: 2, x: 2}
  602. ]);
  603. collection.comparator = function(model){ return model.get('x'); };
  604. collection.add({id: 1, x: 3}, {merge: true});
  605. deepEqual(collection.pluck('id'), [2, 1]);
  606. });
  607. });