PageRenderTime 45ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/test/model.js

https://github.com/addyosmani/backbone
JavaScript | 773 lines | 688 code | 82 blank | 3 comment | 12 complexity | dcaa95b93170f0c0935933a382adc9bf MD5 | raw file
  1. $(document).ready(function() {
  2. // Variable to catch the last request.
  3. var lastRequest = null;
  4. // Variable to catch ajax params.
  5. var ajaxParams = null;
  6. var sync = Backbone.sync;
  7. var ajax = $.ajax;
  8. var urlRoot = null;
  9. module("Backbone.Model", {
  10. setup: function() {
  11. Backbone.sync = function(method, model, options) {
  12. lastRequest = {
  13. method: method,
  14. model: model,
  15. options: options
  16. };
  17. sync.apply(this, arguments);
  18. };
  19. $.ajax = function(params) { ajaxParams = params; };
  20. urlRoot = Backbone.Model.prototype.urlRoot;
  21. Backbone.Model.prototype.urlRoot = '/';
  22. },
  23. teardown: function() {
  24. Backbone.sync = sync;
  25. $.ajax = ajax;
  26. Backbone.Model.prototype.urlRoot = urlRoot;
  27. }
  28. });
  29. var attrs = {
  30. id : '1-the-tempest',
  31. title : "The Tempest",
  32. author : "Bill Shakespeare",
  33. length : 123
  34. };
  35. var proxy = Backbone.Model.extend();
  36. var doc = new proxy(attrs);
  37. var klass = Backbone.Collection.extend({
  38. url : function() { return '/collection'; }
  39. });
  40. var collection = new klass();
  41. collection.add(doc);
  42. test("Model: initialize", function() {
  43. var Model = Backbone.Model.extend({
  44. initialize: function() {
  45. this.one = 1;
  46. equal(this.collection, collection);
  47. }
  48. });
  49. var model = new Model({}, {collection: collection});
  50. equal(model.one, 1);
  51. equal(model.collection, collection);
  52. });
  53. test("Model: initialize with attributes and options", function() {
  54. var Model = Backbone.Model.extend({
  55. initialize: function(attributes, options) {
  56. this.one = options.one;
  57. }
  58. });
  59. var model = new Model({}, {one: 1});
  60. equal(model.one, 1);
  61. });
  62. test("Model: initialize with parsed attributes", function() {
  63. var Model = Backbone.Model.extend({
  64. parse: function(obj) {
  65. obj.value += 1;
  66. return obj;
  67. }
  68. });
  69. var model = new Model({value: 1}, {parse: true});
  70. equal(model.get('value'), 2);
  71. });
  72. test("Model: url", function() {
  73. equal(doc.url(), '/collection/1-the-tempest');
  74. doc.collection.url = '/collection/';
  75. equal(doc.url(), '/collection/1-the-tempest');
  76. doc.collection = null;
  77. doc.urlRoot = null;
  78. raises(function() { doc.url(); });
  79. doc.collection = collection;
  80. });
  81. test("Model: url when using urlRoot, and uri encoding", function() {
  82. var Model = Backbone.Model.extend({
  83. urlRoot: '/collection'
  84. });
  85. var model = new Model();
  86. equal(model.url(), '/collection');
  87. model.set({id: '+1+'});
  88. equal(model.url(), '/collection/%2B1%2B');
  89. });
  90. test("Model: url when using urlRoot as a function to determine urlRoot at runtime", function() {
  91. var Model = Backbone.Model.extend({
  92. urlRoot: function() {
  93. return '/nested/' + this.get('parent_id') + '/collection';
  94. }
  95. });
  96. var model = new Model({parent_id: 1});
  97. equal(model.url(), '/nested/1/collection');
  98. model.set({id: 2});
  99. equal(model.url(), '/nested/1/collection/2');
  100. });
  101. test("Model: clone", function() {
  102. attrs = { 'foo': 1, 'bar': 2, 'baz': 3};
  103. a = new Backbone.Model(attrs);
  104. b = a.clone();
  105. equal(a.get('foo'), 1);
  106. equal(a.get('bar'), 2);
  107. equal(a.get('baz'), 3);
  108. equal(b.get('foo'), a.get('foo'), "Foo should be the same on the clone.");
  109. equal(b.get('bar'), a.get('bar'), "Bar should be the same on the clone.");
  110. equal(b.get('baz'), a.get('baz'), "Baz should be the same on the clone.");
  111. a.set({foo : 100});
  112. equal(a.get('foo'), 100);
  113. equal(b.get('foo'), 1, "Changing a parent attribute does not change the clone.");
  114. });
  115. test("Model: isNew", function() {
  116. attrs = { 'foo': 1, 'bar': 2, 'baz': 3};
  117. a = new Backbone.Model(attrs);
  118. ok(a.isNew(), "it should be new");
  119. attrs = { 'foo': 1, 'bar': 2, 'baz': 3, 'id': -5 };
  120. a = new Backbone.Model(attrs);
  121. ok(!a.isNew(), "any defined ID is legal, negative or positive");
  122. attrs = { 'foo': 1, 'bar': 2, 'baz': 3, 'id': 0 };
  123. a = new Backbone.Model(attrs);
  124. ok(!a.isNew(), "any defined ID is legal, including zero");
  125. ok( new Backbone.Model({ }).isNew(), "is true when there is no id");
  126. ok(!new Backbone.Model({ 'id': 2 }).isNew(), "is false for a positive integer");
  127. ok(!new Backbone.Model({ 'id': -5 }).isNew(), "is false for a negative integer");
  128. });
  129. test("Model: get", function() {
  130. equal(doc.get('title'), 'The Tempest');
  131. equal(doc.get('author'), 'Bill Shakespeare');
  132. });
  133. test("Model: escape", function() {
  134. equal(doc.escape('title'), 'The Tempest');
  135. doc.set({audience: 'Bill & Bob'});
  136. equal(doc.escape('audience'), 'Bill & Bob');
  137. doc.set({audience: 'Tim > Joan'});
  138. equal(doc.escape('audience'), 'Tim > Joan');
  139. doc.set({audience: 10101});
  140. equal(doc.escape('audience'), '10101');
  141. doc.unset('audience');
  142. equal(doc.escape('audience'), '');
  143. });
  144. test("Model: has", function() {
  145. attrs = {};
  146. a = new Backbone.Model(attrs);
  147. equal(a.has("name"), false);
  148. _([true, "Truth!", 1, false, '', 0]).each(function(value) {
  149. a.set({'name': value});
  150. equal(a.has("name"), true);
  151. });
  152. a.unset('name');
  153. equal(a.has('name'), false);
  154. _([null, undefined]).each(function(value) {
  155. a.set({'name': value});
  156. equal(a.has("name"), false);
  157. });
  158. });
  159. test("Model: set and unset", function() {
  160. expect(8);
  161. attrs = {id: 'id', foo: 1, bar: 2, baz: 3};
  162. a = new Backbone.Model(attrs);
  163. var changeCount = 0;
  164. a.on("change:foo", function() { changeCount += 1; });
  165. a.set({'foo': 2});
  166. ok(a.get('foo') == 2, "Foo should have changed.");
  167. ok(changeCount == 1, "Change count should have incremented.");
  168. a.set({'foo': 2}); // set with value that is not new shouldn't fire change event
  169. ok(a.get('foo') == 2, "Foo should NOT have changed, still 2");
  170. ok(changeCount == 1, "Change count should NOT have incremented.");
  171. a.validate = function(attrs) {
  172. equal(attrs.foo, void 0, "don't ignore values when unsetting");
  173. };
  174. a.unset('foo');
  175. equal(a.get('foo'), void 0, "Foo should have changed");
  176. delete a.validate;
  177. ok(changeCount == 2, "Change count should have incremented for unset.");
  178. a.unset('id');
  179. equal(a.id, undefined, "Unsetting the id should remove the id property.");
  180. });
  181. test("Model: multiple unsets", function() {
  182. var i = 0;
  183. var counter = function(){ i++; };
  184. var model = new Backbone.Model({a: 1});
  185. model.on("change:a", counter);
  186. model.set({a: 2});
  187. model.unset('a');
  188. model.unset('a');
  189. equal(i, 2, 'Unset does not fire an event for missing attributes.');
  190. });
  191. test("Model: unset and changedAttributes", function() {
  192. var model = new Backbone.Model({a: 1});
  193. model.unset('a', {silent: true});
  194. var changedAttributes = model.changedAttributes();
  195. ok('a' in changedAttributes, 'changedAttributes should contain unset properties');
  196. changedAttributes = model.changedAttributes();
  197. ok('a' in changedAttributes, 'changedAttributes should contain unset properties when running changedAttributes again after an unset.');
  198. });
  199. test("Model: using a non-default id attribute.", function() {
  200. var MongoModel = Backbone.Model.extend({idAttribute : '_id'});
  201. var model = new MongoModel({id: 'eye-dee', _id: 25, title: 'Model'});
  202. equal(model.get('id'), 'eye-dee');
  203. equal(model.id, 25);
  204. equal(model.isNew(), false);
  205. model.unset('_id');
  206. equal(model.id, undefined);
  207. equal(model.isNew(), true);
  208. });
  209. test("Model: set an empty string", function() {
  210. var model = new Backbone.Model({name : "Model"});
  211. model.set({name : ''});
  212. equal(model.get('name'), '');
  213. });
  214. test("Model: clear", function() {
  215. var changed;
  216. var model = new Backbone.Model({id: 1, name : "Model"});
  217. model.on("change:name", function(){ changed = true; });
  218. model.on("change", function() {
  219. var changedAttrs = model.changedAttributes();
  220. ok('name' in changedAttrs);
  221. });
  222. model.clear();
  223. equal(changed, true);
  224. equal(model.get('name'), undefined);
  225. });
  226. test("Model: defaults", function() {
  227. var Defaulted = Backbone.Model.extend({
  228. defaults: {
  229. "one": 1,
  230. "two": 2
  231. }
  232. });
  233. var model = new Defaulted({two: null});
  234. equal(model.get('one'), 1);
  235. equal(model.get('two'), null);
  236. Defaulted = Backbone.Model.extend({
  237. defaults: function() {
  238. return {
  239. "one": 3,
  240. "two": 4
  241. };
  242. }
  243. });
  244. var model = new Defaulted({two: null});
  245. equal(model.get('one'), 3);
  246. equal(model.get('two'), null);
  247. });
  248. test("Model: change, hasChanged, changedAttributes, previous, previousAttributes", function() {
  249. var model = new Backbone.Model({name : "Tim", age : 10});
  250. equal(model.changedAttributes(), false);
  251. model.on('change', function() {
  252. ok(model.hasChanged('name'), 'name changed');
  253. ok(!model.hasChanged('age'), 'age did not');
  254. ok(_.isEqual(model.changedAttributes(), {name : 'Rob'}), 'changedAttributes returns the changed attrs');
  255. equal(model.previous('name'), 'Tim');
  256. ok(_.isEqual(model.previousAttributes(), {name : "Tim", age : 10}), 'previousAttributes is correct');
  257. });
  258. model.set({name : 'Rob'}, {silent : true});
  259. equal(model.hasChanged(), true);
  260. equal(model.hasChanged('name'), true);
  261. model.change();
  262. equal(model.get('name'), 'Rob');
  263. });
  264. test("Model: changedAttributes", function() {
  265. var model = new Backbone.Model({a: 'a', b: 'b'});
  266. equal(model.changedAttributes(), false);
  267. equal(model.changedAttributes({a: 'a'}), false);
  268. equal(model.changedAttributes({a: 'b'}).a, 'b');
  269. });
  270. test("Model: change with options", function() {
  271. var value;
  272. var model = new Backbone.Model({name: 'Rob'});
  273. model.on('change', function(model, options) {
  274. value = options.prefix + model.get('name');
  275. });
  276. model.set({name: 'Bob'}, {silent: true});
  277. model.change({prefix: 'Mr. '});
  278. equal(value, 'Mr. Bob');
  279. model.set({name: 'Sue'}, {prefix: 'Ms. '});
  280. equal(value, 'Ms. Sue');
  281. });
  282. test("Model: change after initialize", function () {
  283. var changed = 0;
  284. var attrs = {id: 1, label: 'c'};
  285. var obj = new Backbone.Model(attrs);
  286. obj.on('change', function() { changed += 1; });
  287. obj.set(attrs);
  288. equal(changed, 0);
  289. });
  290. test("Model: save within change event", function () {
  291. var model = new Backbone.Model({firstName : "Taylor", lastName: "Swift"});
  292. model.on('change', function () {
  293. model.save();
  294. ok(_.isEqual(lastRequest.model, model));
  295. });
  296. model.set({lastName: 'Hicks'});
  297. });
  298. test("Model: validate after save", function() {
  299. var lastError, model = new Backbone.Model();
  300. model.validate = function(attrs) {
  301. if (attrs.admin) return "Can't change admin status.";
  302. };
  303. model.sync = function(method, model, options) {
  304. options.success.call(this, {admin: true});
  305. };
  306. model.save(null, {error: function(model, error) {
  307. lastError = error;
  308. }});
  309. equal(lastError, "Can't change admin status.");
  310. });
  311. test("Model: isValid", function() {
  312. var model = new Backbone.Model({valid: true});
  313. model.validate = function(attrs) {
  314. if (!attrs.valid) return "invalid";
  315. };
  316. equal(model.isValid(), true);
  317. equal(model.set({valid: false}), false);
  318. equal(model.isValid(), true);
  319. ok(model.set('valid', false, {silent: true}));
  320. equal(model.isValid(), false);
  321. });
  322. test("Model: save", function() {
  323. doc.save({title : "Henry V"});
  324. equal(lastRequest.method, 'update');
  325. ok(_.isEqual(lastRequest.model, doc));
  326. });
  327. test("Model: save in positional style", function() {
  328. var model = new Backbone.Model();
  329. model.sync = function(method, model, options) {
  330. options.success();
  331. };
  332. model.save('title', 'Twelfth Night');
  333. equal(model.get('title'), 'Twelfth Night');
  334. });
  335. test("Model: fetch", function() {
  336. doc.fetch();
  337. equal(lastRequest.method, 'read');
  338. ok(_.isEqual(lastRequest.model, doc));
  339. });
  340. test("Model: destroy", function() {
  341. doc.destroy();
  342. equal(lastRequest.method, 'delete');
  343. ok(_.isEqual(lastRequest.model, doc));
  344. });
  345. test("Model: non-persisted destroy", function() {
  346. attrs = { 'foo': 1, 'bar': 2, 'baz': 3};
  347. a = new Backbone.Model(attrs);
  348. a.sync = function() { throw "should not be called"; };
  349. a.destroy();
  350. ok(true, "non-persisted model should not call sync");
  351. });
  352. test("Model: validate", function() {
  353. var lastError;
  354. var model = new Backbone.Model();
  355. model.validate = function(attrs) {
  356. if (attrs.admin != this.get('admin')) return "Can't change admin status.";
  357. };
  358. model.on('error', function(model, error) {
  359. lastError = error;
  360. });
  361. var result = model.set({a: 100});
  362. equal(result, model);
  363. equal(model.get('a'), 100);
  364. equal(lastError, undefined);
  365. result = model.set({admin: true}, {silent: true});
  366. equal(model.get('admin'), true);
  367. result = model.set({a: 200, admin: false});
  368. equal(lastError, "Can't change admin status.");
  369. equal(result, false);
  370. equal(model.get('a'), 100);
  371. });
  372. test("Model: validate on unset and clear", function() {
  373. var error;
  374. var model = new Backbone.Model({name: "One"});
  375. model.validate = function(attrs) {
  376. if (!attrs.name) {
  377. error = true;
  378. return "No thanks.";
  379. }
  380. };
  381. model.set({name: "Two"});
  382. equal(model.get('name'), 'Two');
  383. equal(error, undefined);
  384. model.unset('name');
  385. equal(error, true);
  386. equal(model.get('name'), 'Two');
  387. model.clear();
  388. equal(model.get('name'), 'Two');
  389. delete model.validate;
  390. model.clear();
  391. equal(model.get('name'), undefined);
  392. });
  393. test("Model: validate with error callback", function() {
  394. var lastError, boundError;
  395. var model = new Backbone.Model();
  396. model.validate = function(attrs) {
  397. if (attrs.admin) return "Can't change admin status.";
  398. };
  399. var callback = function(model, error) {
  400. lastError = error;
  401. };
  402. model.on('error', function(model, error) {
  403. boundError = true;
  404. });
  405. var result = model.set({a: 100}, {error: callback});
  406. equal(result, model);
  407. equal(model.get('a'), 100);
  408. equal(lastError, undefined);
  409. equal(boundError, undefined);
  410. result = model.set({a: 200, admin: true}, {error: callback});
  411. equal(result, false);
  412. equal(model.get('a'), 100);
  413. equal(lastError, "Can't change admin status.");
  414. equal(boundError, undefined);
  415. });
  416. test("Model: defaults always extend attrs (#459)", function() {
  417. var Defaulted = Backbone.Model.extend({
  418. defaults: {one: 1},
  419. initialize : function(attrs, opts) {
  420. equal(this.attributes.one, 1);
  421. }
  422. });
  423. var providedattrs = new Defaulted({});
  424. var emptyattrs = new Defaulted();
  425. });
  426. test("Model: Inherit class properties", function() {
  427. var Parent = Backbone.Model.extend({
  428. instancePropSame: function() {},
  429. instancePropDiff: function() {}
  430. }, {
  431. classProp: function() {}
  432. });
  433. var Child = Parent.extend({
  434. instancePropDiff: function() {}
  435. });
  436. var adult = new Parent;
  437. var kid = new Child;
  438. equal(Child.classProp, Parent.classProp);
  439. notEqual(Child.classProp, undefined);
  440. equal(kid.instancePropSame, adult.instancePropSame);
  441. notEqual(kid.instancePropSame, undefined);
  442. notEqual(Child.prototype.instancePropDiff, Parent.prototype.instancePropDiff);
  443. notEqual(Child.prototype.instancePropDiff, undefined);
  444. });
  445. test("Model: Nested change events don't clobber previous attributes", function() {
  446. var A = Backbone.Model.extend({
  447. initialize: function() {
  448. this.on("change:state", function(a, newState) {
  449. equal(a.previous('state'), undefined);
  450. equal(newState, 'hello');
  451. // Fire a nested change event.
  452. this.set({ other: "whatever" });
  453. });
  454. }
  455. });
  456. var B = Backbone.Model.extend({
  457. initialize: function() {
  458. this.get("a").on("change:state", function(a, newState) {
  459. equal(a.previous('state'), undefined);
  460. equal(newState, 'hello');
  461. });
  462. }
  463. });
  464. var a = new A();
  465. var b = new B({a: a});
  466. a.set({state: 'hello'});
  467. });
  468. test("hasChanged/set should use same comparison", function() {
  469. expect(2);
  470. var changed = 0, model = new Backbone.Model({a: null});
  471. model.on('change', function() {
  472. ok(this.hasChanged('a'));
  473. })
  474. .on('change:a', function() {
  475. changed++;
  476. })
  477. .set({a: undefined});
  478. equal(changed, 1);
  479. });
  480. test("#582, #425, change:attribute callbacks should fire after all changes have occurred", 9, function() {
  481. var model = new Backbone.Model;
  482. var assertion = function() {
  483. equal(model.get('a'), 'a');
  484. equal(model.get('b'), 'b');
  485. equal(model.get('c'), 'c');
  486. };
  487. model.on('change:a', assertion);
  488. model.on('change:b', assertion);
  489. model.on('change:c', assertion);
  490. model.set({a: 'a', b: 'b', c: 'c'});
  491. });
  492. test("#871, set with attributes property", function() {
  493. var model = new Backbone.Model();
  494. model.set({attributes: true});
  495. ok(model.has('attributes'));
  496. });
  497. test("set value regardless of equality/change", function() {
  498. var model = new Backbone.Model({x: []});
  499. var a = [];
  500. model.set({x: a});
  501. ok(model.get('x') === a);
  502. });
  503. test("unset fires change for undefined attributes", 1, function() {
  504. var model = new Backbone.Model({x: undefined});
  505. model.on('change:x', function(){ ok(true); });
  506. model.unset('x');
  507. });
  508. test("set: undefined values", function() {
  509. var model = new Backbone.Model({x: undefined});
  510. ok('x' in model.attributes);
  511. });
  512. test("change fires change:attr", 1, function() {
  513. var model = new Backbone.Model({x: 1});
  514. model.set({x: 2}, {silent: true});
  515. model.on('change:x', function(){ ok(true); });
  516. model.change();
  517. });
  518. test("hasChanged is false after original values are set", function() {
  519. var model = new Backbone.Model({x: 1});
  520. model.on('change:x', function(){ ok(false); });
  521. model.set({x: 2}, {silent: true});
  522. ok(model.hasChanged());
  523. model.set({x: 1}, {silent: true});
  524. ok(!model.hasChanged());
  525. });
  526. test("save with `wait` succeeds without `validate`", function() {
  527. var model = new Backbone.Model();
  528. model.save({x: 1}, {wait: true});
  529. ok(lastRequest.model === model);
  530. });
  531. test("`hasChanged` for falsey keys", function() {
  532. var model = new Backbone.Model();
  533. model.set({x: true}, {silent: true});
  534. ok(!model.hasChanged(0));
  535. ok(!model.hasChanged(''));
  536. });
  537. test("`previous` for falsey keys", function() {
  538. var model = new Backbone.Model({0: true, '': true});
  539. model.set({0: false, '': false}, {silent: true});
  540. equal(model.previous(0), true);
  541. equal(model.previous(''), true);
  542. });
  543. test("`save` with `wait` sends correct attributes", function() {
  544. var changed = 0;
  545. var model = new Backbone.Model({x: 1, y: 2});
  546. model.on('change:x', function() { changed++; });
  547. model.save({x: 3}, {wait: true});
  548. deepEqual(JSON.parse(ajaxParams.data), {x: 3, y: 2});
  549. equal(model.get('x'), 1);
  550. equal(changed, 0);
  551. lastRequest.options.success({});
  552. equal(model.get('x'), 3);
  553. equal(changed, 1);
  554. });
  555. test("nested `set` during `'change:attr'`", function() {
  556. var events = [];
  557. var model = new Backbone.Model();
  558. model.on('all', function(event) { events.push(event); });
  559. model.on('change', function() {
  560. model.set({z: true}, {silent:true});
  561. });
  562. model.on('change:x', function() {
  563. model.set({y: true});
  564. });
  565. model.set({x: true});
  566. deepEqual(events, ['change:y', 'change:x', 'change']);
  567. events = [];
  568. model.change();
  569. deepEqual(events, ['change:z', 'change']);
  570. });
  571. test("nested `change` only fires once", 1, function() {
  572. var model = new Backbone.Model();
  573. model.on('change', function() {
  574. ok(true);
  575. model.change();
  576. });
  577. model.set({x: true});
  578. });
  579. test("no `'change'` event if no changes", function() {
  580. var model = new Backbone.Model();
  581. model.on('change', function() { ok(false); });
  582. model.change();
  583. });
  584. test("nested `set` during `'change'`", 6, function() {
  585. var count = 0;
  586. var model = new Backbone.Model();
  587. model.on('change', function() {
  588. switch(count++) {
  589. case 0:
  590. deepEqual(this.changedAttributes(), {x: true});
  591. equal(model.previous('x'), undefined);
  592. model.set({y: true});
  593. break;
  594. case 1:
  595. deepEqual(this.changedAttributes(), {y: true});
  596. equal(model.previous('x'), true);
  597. model.set({z: true});
  598. break;
  599. case 2:
  600. deepEqual(this.changedAttributes(), {z: true});
  601. equal(model.previous('y'), true);
  602. break;
  603. default:
  604. ok(false);
  605. }
  606. });
  607. model.set({x: true});
  608. });
  609. test("nested `'change'` with silent", 3, function() {
  610. var count = 0;
  611. var model = new Backbone.Model();
  612. model.on('change:y', function() { ok(true); });
  613. model.on('change', function() {
  614. switch(count++) {
  615. case 0:
  616. deepEqual(this.changedAttributes(), {x: true});
  617. model.set({y: true}, {silent: true});
  618. break;
  619. case 1:
  620. deepEqual(this.changedAttributes(), {y: true, z: true});
  621. break;
  622. default:
  623. ok(false);
  624. }
  625. });
  626. model.set({x: true});
  627. model.set({z: true});
  628. });
  629. test("nested `'change:attr'` with silent", 1, function() {
  630. var model = new Backbone.Model();
  631. model.on('change:y', function(){ ok(true); });
  632. model.on('change', function() {
  633. model.set({y: true}, {silent: true});
  634. model.set({z: true});
  635. });
  636. model.set({x: true});
  637. });
  638. test("multiple nested changes with silent", 1, function() {
  639. var model = new Backbone.Model();
  640. model.on('change:x', function() {
  641. model.set({y: 1}, {silent: true});
  642. model.set({y: 2});
  643. });
  644. model.on('change:y', function(model, val) {
  645. equal(val, 2);
  646. });
  647. model.set({x: true});
  648. model.change();
  649. });
  650. test("multiple nested changes with silent", function() {
  651. var changes = [];
  652. var model = new Backbone.Model();
  653. model.on('change:b', function(model, val) { changes.push(val); });
  654. model.on('change', function() {
  655. model.set({b: 1});
  656. model.set({b: 2}, {silent: true});
  657. });
  658. model.set({b: 0});
  659. deepEqual(changes, [0, 1, 1]);
  660. model.change();
  661. deepEqual(changes, [0, 1, 1, 2, 1]);
  662. });
  663. test("nested set multiple times", 1, function() {
  664. var model = new Backbone.Model();
  665. model.on('change:b', function() {
  666. ok(true);
  667. });
  668. model.on('change:a', function() {
  669. model.set({b: true});
  670. model.set({b: true});
  671. });
  672. model.set({a: true});
  673. });
  674. test("Backbone.wrapError triggers `'error'`", 12, function() {
  675. var resp = {};
  676. var options = {};
  677. var model = new Backbone.Model();
  678. model.on('error', error);
  679. var callback = Backbone.wrapError(null, model, options);
  680. callback(model, resp);
  681. callback(resp);
  682. callback = Backbone.wrapError(error, model, options);
  683. callback(model, resp);
  684. callback(resp);
  685. function error(_model, _resp, _options) {
  686. ok(model === _model);
  687. ok(resp === _resp);
  688. ok(options === _options);
  689. }
  690. });
  691. });