Built motion from commit 76eb00b9e.|1.0.24
[motion.git] / public / bower_components / lodash / vendor / backbone / test / model.js
1 (function(QUnit) {
2
3   var ProxyModel = Backbone.Model.extend();
4   var Klass = Backbone.Collection.extend({
5     url: function() { return '/collection'; }
6   });
7   var doc, collection;
8
9   QUnit.module('Backbone.Model', {
10
11     beforeEach: function(assert) {
12       doc = new ProxyModel({
13         id: '1-the-tempest',
14         title: 'The Tempest',
15         author: 'Bill Shakespeare',
16         length: 123
17       });
18       collection = new Klass();
19       collection.add(doc);
20     }
21
22   });
23
24   QUnit.test('initialize', function(assert) {
25     assert.expect(3);
26     var Model = Backbone.Model.extend({
27       initialize: function() {
28         this.one = 1;
29         assert.equal(this.collection, collection);
30       }
31     });
32     var model = new Model({}, {collection: collection});
33     assert.equal(model.one, 1);
34     assert.equal(model.collection, collection);
35   });
36
37   QUnit.test('Object.prototype properties are overridden by attributes', function(assert) {
38     assert.expect(1);
39     var model = new Backbone.Model({hasOwnProperty: true});
40     assert.equal(model.get('hasOwnProperty'), true);
41   });
42
43   QUnit.test('initialize with attributes and options', function(assert) {
44     assert.expect(1);
45     var Model = Backbone.Model.extend({
46       initialize: function(attributes, options) {
47         this.one = options.one;
48       }
49     });
50     var model = new Model({}, {one: 1});
51     assert.equal(model.one, 1);
52   });
53
54   QUnit.test('initialize with parsed attributes', function(assert) {
55     assert.expect(1);
56     var Model = Backbone.Model.extend({
57       parse: function(attrs) {
58         attrs.value += 1;
59         return attrs;
60       }
61     });
62     var model = new Model({value: 1}, {parse: true});
63     assert.equal(model.get('value'), 2);
64   });
65
66
67   QUnit.test('preinitialize', function(assert) {
68     assert.expect(2);
69     var Model = Backbone.Model.extend({
70
71       preinitialize: function() {
72         this.one = 1;
73       }
74     });
75     var model = new Model({}, {collection: collection});
76     assert.equal(model.one, 1);
77     assert.equal(model.collection, collection);
78   });
79
80   QUnit.test('preinitialize occurs before the model is set up', function(assert) {
81     assert.expect(6);
82     var Model = Backbone.Model.extend({
83
84       preinitialize: function() {
85         assert.equal(this.collection, undefined);
86         assert.equal(this.cid, undefined);
87         assert.equal(this.id, undefined);
88       }
89     });
90     var model = new Model({id: 'foo'}, {collection: collection});
91     assert.equal(model.collection, collection);
92     assert.equal(model.id, 'foo');
93     assert.notEqual(model.cid, undefined);
94   });
95
96   QUnit.test('parse can return null', function(assert) {
97     assert.expect(1);
98     var Model = Backbone.Model.extend({
99       parse: function(attrs) {
100         attrs.value += 1;
101         return null;
102       }
103     });
104     var model = new Model({value: 1}, {parse: true});
105     assert.equal(JSON.stringify(model.toJSON()), '{}');
106   });
107
108   QUnit.test('url', function(assert) {
109     assert.expect(3);
110     doc.urlRoot = null;
111     assert.equal(doc.url(), '/collection/1-the-tempest');
112     doc.collection.url = '/collection/';
113     assert.equal(doc.url(), '/collection/1-the-tempest');
114     doc.collection = null;
115     assert.raises(function() { doc.url(); });
116     doc.collection = collection;
117   });
118
119   QUnit.test('url when using urlRoot, and uri encoding', function(assert) {
120     assert.expect(2);
121     var Model = Backbone.Model.extend({
122       urlRoot: '/collection'
123     });
124     var model = new Model();
125     assert.equal(model.url(), '/collection');
126     model.set({id: '+1+'});
127     assert.equal(model.url(), '/collection/%2B1%2B');
128   });
129
130   QUnit.test('url when using urlRoot as a function to determine urlRoot at runtime', function(assert) {
131     assert.expect(2);
132     var Model = Backbone.Model.extend({
133       urlRoot: function() {
134         return '/nested/' + this.get('parentId') + '/collection';
135       }
136     });
137
138     var model = new Model({parentId: 1});
139     assert.equal(model.url(), '/nested/1/collection');
140     model.set({id: 2});
141     assert.equal(model.url(), '/nested/1/collection/2');
142   });
143
144   QUnit.test('underscore methods', function(assert) {
145     assert.expect(5);
146     var model = new Backbone.Model({foo: 'a', bar: 'b', baz: 'c'});
147     var model2 = model.clone();
148     assert.deepEqual(model.keys(), ['foo', 'bar', 'baz']);
149     assert.deepEqual(model.values(), ['a', 'b', 'c']);
150     assert.deepEqual(model.invert(), {a: 'foo', b: 'bar', c: 'baz'});
151     assert.deepEqual(model.pick('foo', 'baz'), {foo: 'a', baz: 'c'});
152     assert.deepEqual(model.omit('foo', 'bar'), {baz: 'c'});
153   });
154
155   QUnit.test('chain', function(assert) {
156     var model = new Backbone.Model({a: 0, b: 1, c: 2});
157     assert.deepEqual(model.chain().pick('a', 'b', 'c').values().compact().value(), [1, 2]);
158   });
159
160   QUnit.test('clone', function(assert) {
161     assert.expect(10);
162     var a = new Backbone.Model({foo: 1, bar: 2, baz: 3});
163     var b = a.clone();
164     assert.equal(a.get('foo'), 1);
165     assert.equal(a.get('bar'), 2);
166     assert.equal(a.get('baz'), 3);
167     assert.equal(b.get('foo'), a.get('foo'), 'Foo should be the same on the clone.');
168     assert.equal(b.get('bar'), a.get('bar'), 'Bar should be the same on the clone.');
169     assert.equal(b.get('baz'), a.get('baz'), 'Baz should be the same on the clone.');
170     a.set({foo: 100});
171     assert.equal(a.get('foo'), 100);
172     assert.equal(b.get('foo'), 1, 'Changing a parent attribute does not change the clone.');
173
174     var foo = new Backbone.Model({p: 1});
175     var bar = new Backbone.Model({p: 2});
176     bar.set(foo.clone().attributes, {unset: true});
177     assert.equal(foo.get('p'), 1);
178     assert.equal(bar.get('p'), undefined);
179   });
180
181   QUnit.test('isNew', function(assert) {
182     assert.expect(6);
183     var a = new Backbone.Model({foo: 1, bar: 2, baz: 3});
184     assert.ok(a.isNew(), 'it should be new');
185     a = new Backbone.Model({foo: 1, bar: 2, baz: 3, id: -5});
186     assert.ok(!a.isNew(), 'any defined ID is legal, negative or positive');
187     a = new Backbone.Model({foo: 1, bar: 2, baz: 3, id: 0});
188     assert.ok(!a.isNew(), 'any defined ID is legal, including zero');
189     assert.ok(new Backbone.Model().isNew(), 'is true when there is no id');
190     assert.ok(!new Backbone.Model({id: 2}).isNew(), 'is false for a positive integer');
191     assert.ok(!new Backbone.Model({id: -5}).isNew(), 'is false for a negative integer');
192   });
193
194   QUnit.test('get', function(assert) {
195     assert.expect(2);
196     assert.equal(doc.get('title'), 'The Tempest');
197     assert.equal(doc.get('author'), 'Bill Shakespeare');
198   });
199
200   QUnit.test('escape', function(assert) {
201     assert.expect(5);
202     assert.equal(doc.escape('title'), 'The Tempest');
203     doc.set({audience: 'Bill & Bob'});
204     assert.equal(doc.escape('audience'), 'Bill & Bob');
205     doc.set({audience: 'Tim > Joan'});
206     assert.equal(doc.escape('audience'), 'Tim > Joan');
207     doc.set({audience: 10101});
208     assert.equal(doc.escape('audience'), '10101');
209     doc.unset('audience');
210     assert.equal(doc.escape('audience'), '');
211   });
212
213   QUnit.test('has', function(assert) {
214     assert.expect(10);
215     var model = new Backbone.Model();
216
217     assert.strictEqual(model.has('name'), false);
218
219     model.set({
220       '0': 0,
221       '1': 1,
222       'true': true,
223       'false': false,
224       'empty': '',
225       'name': 'name',
226       'null': null,
227       'undefined': undefined
228     });
229
230     assert.strictEqual(model.has('0'), true);
231     assert.strictEqual(model.has('1'), true);
232     assert.strictEqual(model.has('true'), true);
233     assert.strictEqual(model.has('false'), true);
234     assert.strictEqual(model.has('empty'), true);
235     assert.strictEqual(model.has('name'), true);
236
237     model.unset('name');
238
239     assert.strictEqual(model.has('name'), false);
240     assert.strictEqual(model.has('null'), false);
241     assert.strictEqual(model.has('undefined'), false);
242   });
243
244   QUnit.test('matches', function(assert) {
245     assert.expect(4);
246     var model = new Backbone.Model();
247
248     assert.strictEqual(model.matches({name: 'Jonas', cool: true}), false);
249
250     model.set({name: 'Jonas', cool: true});
251
252     assert.strictEqual(model.matches({name: 'Jonas'}), true);
253     assert.strictEqual(model.matches({name: 'Jonas', cool: true}), true);
254     assert.strictEqual(model.matches({name: 'Jonas', cool: false}), false);
255   });
256
257   QUnit.test('matches with predicate', function(assert) {
258     var model = new Backbone.Model({a: 0});
259
260     assert.strictEqual(model.matches(function(attr) {
261       return attr.a > 1 && attr.b != null;
262     }), false);
263
264     model.set({a: 3, b: true});
265
266     assert.strictEqual(model.matches(function(attr) {
267       return attr.a > 1 && attr.b != null;
268     }), true);
269   });
270
271   QUnit.test('set and unset', function(assert) {
272     assert.expect(8);
273     var a = new Backbone.Model({id: 'id', foo: 1, bar: 2, baz: 3});
274     var changeCount = 0;
275     a.on('change:foo', function() { changeCount += 1; });
276     a.set({foo: 2});
277     assert.equal(a.get('foo'), 2, 'Foo should have changed.');
278     assert.equal(changeCount, 1, 'Change count should have incremented.');
279     // set with value that is not new shouldn't fire change event
280     a.set({foo: 2});
281     assert.equal(a.get('foo'), 2, 'Foo should NOT have changed, still 2');
282     assert.equal(changeCount, 1, 'Change count should NOT have incremented.');
283
284     a.validate = function(attrs) {
285       assert.equal(attrs.foo, void 0, 'validate:true passed while unsetting');
286     };
287     a.unset('foo', {validate: true});
288     assert.equal(a.get('foo'), void 0, 'Foo should have changed');
289     delete a.validate;
290     assert.equal(changeCount, 2, 'Change count should have incremented for unset.');
291
292     a.unset('id');
293     assert.equal(a.id, undefined, 'Unsetting the id should remove the id property.');
294   });
295
296   QUnit.test('#2030 - set with failed validate, followed by another set triggers change', function(assert) {
297     var attr = 0, main = 0, error = 0;
298     var Model = Backbone.Model.extend({
299       validate: function(attrs) {
300         if (attrs.x > 1) {
301           error++;
302           return 'this is an error';
303         }
304       }
305     });
306     var model = new Model({x: 0});
307     model.on('change:x', function() { attr++; });
308     model.on('change', function() { main++; });
309     model.set({x: 2}, {validate: true});
310     model.set({x: 1}, {validate: true});
311     assert.deepEqual([attr, main, error], [1, 1, 1]);
312   });
313
314   QUnit.test('set triggers changes in the correct order', function(assert) {
315     var value = null;
316     var model = new Backbone.Model;
317     model.on('last', function(){ value = 'last'; });
318     model.on('first', function(){ value = 'first'; });
319     model.trigger('first');
320     model.trigger('last');
321     assert.equal(value, 'last');
322   });
323
324   QUnit.test('set falsy values in the correct order', function(assert) {
325     assert.expect(2);
326     var model = new Backbone.Model({result: 'result'});
327     model.on('change', function() {
328       assert.equal(model.changed.result, void 0);
329       assert.equal(model.previous('result'), false);
330     });
331     model.set({result: void 0}, {silent: true});
332     model.set({result: null}, {silent: true});
333     model.set({result: false}, {silent: true});
334     model.set({result: void 0});
335   });
336
337   QUnit.test('nested set triggers with the correct options', function(assert) {
338     var model = new Backbone.Model();
339     var o1 = {};
340     var o2 = {};
341     var o3 = {};
342     model.on('change', function(__, options) {
343       switch (model.get('a')) {
344         case 1:
345           assert.equal(options, o1);
346           return model.set('a', 2, o2);
347         case 2:
348           assert.equal(options, o2);
349           return model.set('a', 3, o3);
350         case 3:
351           assert.equal(options, o3);
352       }
353     });
354     model.set('a', 1, o1);
355   });
356
357   QUnit.test('multiple unsets', function(assert) {
358     assert.expect(1);
359     var i = 0;
360     var counter = function(){ i++; };
361     var model = new Backbone.Model({a: 1});
362     model.on('change:a', counter);
363     model.set({a: 2});
364     model.unset('a');
365     model.unset('a');
366     assert.equal(i, 2, 'Unset does not fire an event for missing attributes.');
367   });
368
369   QUnit.test('unset and changedAttributes', function(assert) {
370     assert.expect(1);
371     var model = new Backbone.Model({a: 1});
372     model.on('change', function() {
373       assert.ok('a' in model.changedAttributes(), 'changedAttributes should contain unset properties');
374     });
375     model.unset('a');
376   });
377
378   QUnit.test('using a non-default id attribute.', function(assert) {
379     assert.expect(5);
380     var MongoModel = Backbone.Model.extend({idAttribute: '_id'});
381     var model = new MongoModel({id: 'eye-dee', _id: 25, title: 'Model'});
382     assert.equal(model.get('id'), 'eye-dee');
383     assert.equal(model.id, 25);
384     assert.equal(model.isNew(), false);
385     model.unset('_id');
386     assert.equal(model.id, undefined);
387     assert.equal(model.isNew(), true);
388   });
389
390   QUnit.test('setting an alternative cid prefix', function(assert) {
391     assert.expect(4);
392     var Model = Backbone.Model.extend({
393       cidPrefix: 'm'
394     });
395     var model = new Model();
396
397     assert.equal(model.cid.charAt(0), 'm');
398
399     model = new Backbone.Model();
400     assert.equal(model.cid.charAt(0), 'c');
401
402     var Collection = Backbone.Collection.extend({
403       model: Model
404     });
405     var col = new Collection([{id: 'c5'}, {id: 'c6'}, {id: 'c7'}]);
406
407     assert.equal(col.get('c6').cid.charAt(0), 'm');
408     col.set([{id: 'c6', value: 'test'}], {
409       merge: true,
410       add: true,
411       remove: false
412     });
413     assert.ok(col.get('c6').has('value'));
414   });
415
416   QUnit.test('set an empty string', function(assert) {
417     assert.expect(1);
418     var model = new Backbone.Model({name: 'Model'});
419     model.set({name: ''});
420     assert.equal(model.get('name'), '');
421   });
422
423   QUnit.test('setting an object', function(assert) {
424     assert.expect(1);
425     var model = new Backbone.Model({
426       custom: {foo: 1}
427     });
428     model.on('change', function() {
429       assert.ok(1);
430     });
431     model.set({
432       custom: {foo: 1} // no change should be fired
433     });
434     model.set({
435       custom: {foo: 2} // change event should be fired
436     });
437   });
438
439   QUnit.test('clear', function(assert) {
440     assert.expect(3);
441     var changed;
442     var model = new Backbone.Model({id: 1, name: 'Model'});
443     model.on('change:name', function(){ changed = true; });
444     model.on('change', function() {
445       var changedAttrs = model.changedAttributes();
446       assert.ok('name' in changedAttrs);
447     });
448     model.clear();
449     assert.equal(changed, true);
450     assert.equal(model.get('name'), undefined);
451   });
452
453   QUnit.test('defaults', function(assert) {
454     assert.expect(9);
455     var Defaulted = Backbone.Model.extend({
456       defaults: {
457         one: 1,
458         two: 2
459       }
460     });
461     var model = new Defaulted({two: undefined});
462     assert.equal(model.get('one'), 1);
463     assert.equal(model.get('two'), 2);
464     model = new Defaulted({two: 3});
465     assert.equal(model.get('one'), 1);
466     assert.equal(model.get('two'), 3);
467     Defaulted = Backbone.Model.extend({
468       defaults: function() {
469         return {
470           one: 3,
471           two: 4
472         };
473       }
474     });
475     model = new Defaulted({two: undefined});
476     assert.equal(model.get('one'), 3);
477     assert.equal(model.get('two'), 4);
478     Defaulted = Backbone.Model.extend({
479       defaults: {hasOwnProperty: true}
480     });
481     model = new Defaulted();
482     assert.equal(model.get('hasOwnProperty'), true);
483     model = new Defaulted({hasOwnProperty: undefined});
484     assert.equal(model.get('hasOwnProperty'), true);
485     model = new Defaulted({hasOwnProperty: false});
486     assert.equal(model.get('hasOwnProperty'), false);
487   });
488
489   QUnit.test('change, hasChanged, changedAttributes, previous, previousAttributes', function(assert) {
490     assert.expect(9);
491     var model = new Backbone.Model({name: 'Tim', age: 10});
492     assert.deepEqual(model.changedAttributes(), false);
493     model.on('change', function() {
494       assert.ok(model.hasChanged('name'), 'name changed');
495       assert.ok(!model.hasChanged('age'), 'age did not');
496       assert.ok(_.isEqual(model.changedAttributes(), {name: 'Rob'}), 'changedAttributes returns the changed attrs');
497       assert.equal(model.previous('name'), 'Tim');
498       assert.ok(_.isEqual(model.previousAttributes(), {name: 'Tim', age: 10}), 'previousAttributes is correct');
499     });
500     assert.equal(model.hasChanged(), false);
501     assert.equal(model.hasChanged(undefined), false);
502     model.set({name: 'Rob'});
503     assert.equal(model.get('name'), 'Rob');
504   });
505
506   QUnit.test('changedAttributes', function(assert) {
507     assert.expect(3);
508     var model = new Backbone.Model({a: 'a', b: 'b'});
509     assert.deepEqual(model.changedAttributes(), false);
510     assert.equal(model.changedAttributes({a: 'a'}), false);
511     assert.equal(model.changedAttributes({a: 'b'}).a, 'b');
512   });
513
514   QUnit.test('change with options', function(assert) {
515     assert.expect(2);
516     var value;
517     var model = new Backbone.Model({name: 'Rob'});
518     model.on('change', function(m, options) {
519       value = options.prefix + m.get('name');
520     });
521     model.set({name: 'Bob'}, {prefix: 'Mr. '});
522     assert.equal(value, 'Mr. Bob');
523     model.set({name: 'Sue'}, {prefix: 'Ms. '});
524     assert.equal(value, 'Ms. Sue');
525   });
526
527   QUnit.test('change after initialize', function(assert) {
528     assert.expect(1);
529     var changed = 0;
530     var attrs = {id: 1, label: 'c'};
531     var obj = new Backbone.Model(attrs);
532     obj.on('change', function() { changed += 1; });
533     obj.set(attrs);
534     assert.equal(changed, 0);
535   });
536
537   QUnit.test('save within change event', function(assert) {
538     assert.expect(1);
539     var env = this;
540     var model = new Backbone.Model({firstName: 'Taylor', lastName: 'Swift'});
541     model.url = '/test';
542     model.on('change', function() {
543       model.save();
544       assert.ok(_.isEqual(env.syncArgs.model, model));
545     });
546     model.set({lastName: 'Hicks'});
547   });
548
549   QUnit.test('validate after save', function(assert) {
550     assert.expect(2);
551     var lastError, model = new Backbone.Model();
552     model.validate = function(attrs) {
553       if (attrs.admin) return "Can't change admin status.";
554     };
555     model.sync = function(method, m, options) {
556       options.success.call(this, {admin: true});
557     };
558     model.on('invalid', function(m, error) {
559       lastError = error;
560     });
561     model.save(null);
562
563     assert.equal(lastError, "Can't change admin status.");
564     assert.equal(model.validationError, "Can't change admin status.");
565   });
566
567   QUnit.test('save', function(assert) {
568     assert.expect(2);
569     doc.save({title: 'Henry V'});
570     assert.equal(this.syncArgs.method, 'update');
571     assert.ok(_.isEqual(this.syncArgs.model, doc));
572   });
573
574   QUnit.test('save, fetch, destroy triggers error event when an error occurs', function(assert) {
575     assert.expect(3);
576     var model = new Backbone.Model();
577     model.on('error', function() {
578       assert.ok(true);
579     });
580     model.sync = function(method, m, options) {
581       options.error();
582     };
583     model.save({data: 2, id: 1});
584     model.fetch();
585     model.destroy();
586   });
587
588   QUnit.test('#3283 - save, fetch, destroy calls success with context', function(assert) {
589     assert.expect(3);
590     var model = new Backbone.Model();
591     var obj = {};
592     var options = {
593       context: obj,
594       success: function() {
595         assert.equal(this, obj);
596       }
597     };
598     model.sync = function(method, m, opts) {
599       opts.success.call(opts.context);
600     };
601     model.save({data: 2, id: 1}, options);
602     model.fetch(options);
603     model.destroy(options);
604   });
605
606   QUnit.test('#3283 - save, fetch, destroy calls error with context', function(assert) {
607     assert.expect(3);
608     var model = new Backbone.Model();
609     var obj = {};
610     var options = {
611       context: obj,
612       error: function() {
613         assert.equal(this, obj);
614       }
615     };
616     model.sync = function(method, m, opts) {
617       opts.error.call(opts.context);
618     };
619     model.save({data: 2, id: 1}, options);
620     model.fetch(options);
621     model.destroy(options);
622   });
623
624   QUnit.test('#3470 - save and fetch with parse false', function(assert) {
625     assert.expect(2);
626     var i = 0;
627     var model = new Backbone.Model();
628     model.parse = function() {
629       assert.ok(false);
630     };
631     model.sync = function(method, m, options) {
632       options.success({i: ++i});
633     };
634     model.fetch({parse: false});
635     assert.equal(model.get('i'), i);
636     model.save(null, {parse: false});
637     assert.equal(model.get('i'), i);
638   });
639
640   QUnit.test('save with PATCH', function(assert) {
641     doc.clear().set({id: 1, a: 1, b: 2, c: 3, d: 4});
642     doc.save();
643     assert.equal(this.syncArgs.method, 'update');
644     assert.equal(this.syncArgs.options.attrs, undefined);
645
646     doc.save({b: 2, d: 4}, {patch: true});
647     assert.equal(this.syncArgs.method, 'patch');
648     assert.equal(_.size(this.syncArgs.options.attrs), 2);
649     assert.equal(this.syncArgs.options.attrs.d, 4);
650     assert.equal(this.syncArgs.options.attrs.a, undefined);
651     assert.equal(this.ajaxSettings.data, '{"b":2,"d":4}');
652   });
653
654   QUnit.test('save with PATCH and different attrs', function(assert) {
655     doc.clear().save({b: 2, d: 4}, {patch: true, attrs: {B: 1, D: 3}});
656     assert.equal(this.syncArgs.options.attrs.D, 3);
657     assert.equal(this.syncArgs.options.attrs.d, undefined);
658     assert.equal(this.ajaxSettings.data, '{"B":1,"D":3}');
659     assert.deepEqual(doc.attributes, {b: 2, d: 4});
660   });
661
662   QUnit.test('save in positional style', function(assert) {
663     assert.expect(1);
664     var model = new Backbone.Model();
665     model.sync = function(method, m, options) {
666       options.success();
667     };
668     model.save('title', 'Twelfth Night');
669     assert.equal(model.get('title'), 'Twelfth Night');
670   });
671
672   QUnit.test('save with non-object success response', function(assert) {
673     assert.expect(2);
674     var model = new Backbone.Model();
675     model.sync = function(method, m, options) {
676       options.success('', options);
677       options.success(null, options);
678     };
679     model.save({testing: 'empty'}, {
680       success: function(m) {
681         assert.deepEqual(m.attributes, {testing: 'empty'});
682       }
683     });
684   });
685
686   QUnit.test('save with wait and supplied id', function(assert) {
687     var Model = Backbone.Model.extend({
688       urlRoot: '/collection'
689     });
690     var model = new Model();
691     model.save({id: 42}, {wait: true});
692     assert.equal(this.ajaxSettings.url, '/collection/42');
693   });
694
695   QUnit.test('save will pass extra options to success callback', function(assert) {
696     assert.expect(1);
697     var SpecialSyncModel = Backbone.Model.extend({
698       sync: function(method, m, options) {
699         _.extend(options, {specialSync: true});
700         return Backbone.Model.prototype.sync.call(this, method, m, options);
701       },
702       urlRoot: '/test'
703     });
704
705     var model = new SpecialSyncModel();
706
707     var onSuccess = function(m, response, options) {
708       assert.ok(options.specialSync, 'Options were passed correctly to callback');
709     };
710
711     model.save(null, {success: onSuccess});
712     this.ajaxSettings.success();
713   });
714
715   QUnit.test('fetch', function(assert) {
716     assert.expect(2);
717     doc.fetch();
718     assert.equal(this.syncArgs.method, 'read');
719     assert.ok(_.isEqual(this.syncArgs.model, doc));
720   });
721
722   QUnit.test('fetch will pass extra options to success callback', function(assert) {
723     assert.expect(1);
724     var SpecialSyncModel = Backbone.Model.extend({
725       sync: function(method, m, options) {
726         _.extend(options, {specialSync: true});
727         return Backbone.Model.prototype.sync.call(this, method, m, options);
728       },
729       urlRoot: '/test'
730     });
731
732     var model = new SpecialSyncModel();
733
734     var onSuccess = function(m, response, options) {
735       assert.ok(options.specialSync, 'Options were passed correctly to callback');
736     };
737
738     model.fetch({success: onSuccess});
739     this.ajaxSettings.success();
740   });
741
742   QUnit.test('destroy', function(assert) {
743     assert.expect(3);
744     doc.destroy();
745     assert.equal(this.syncArgs.method, 'delete');
746     assert.ok(_.isEqual(this.syncArgs.model, doc));
747
748     var newModel = new Backbone.Model;
749     assert.equal(newModel.destroy(), false);
750   });
751
752   QUnit.test('destroy will pass extra options to success callback', function(assert) {
753     assert.expect(1);
754     var SpecialSyncModel = Backbone.Model.extend({
755       sync: function(method, m, options) {
756         _.extend(options, {specialSync: true});
757         return Backbone.Model.prototype.sync.call(this, method, m, options);
758       },
759       urlRoot: '/test'
760     });
761
762     var model = new SpecialSyncModel({id: 'id'});
763
764     var onSuccess = function(m, response, options) {
765       assert.ok(options.specialSync, 'Options were passed correctly to callback');
766     };
767
768     model.destroy({success: onSuccess});
769     this.ajaxSettings.success();
770   });
771
772   QUnit.test('non-persisted destroy', function(assert) {
773     assert.expect(1);
774     var a = new Backbone.Model({foo: 1, bar: 2, baz: 3});
775     a.sync = function() { throw 'should not be called'; };
776     a.destroy();
777     assert.ok(true, 'non-persisted model should not call sync');
778   });
779
780   QUnit.test('validate', function(assert) {
781     var lastError;
782     var model = new Backbone.Model();
783     model.validate = function(attrs) {
784       if (attrs.admin !== this.get('admin')) return "Can't change admin status.";
785     };
786     model.on('invalid', function(m, error) {
787       lastError = error;
788     });
789     var result = model.set({a: 100});
790     assert.equal(result, model);
791     assert.equal(model.get('a'), 100);
792     assert.equal(lastError, undefined);
793     result = model.set({admin: true});
794     assert.equal(model.get('admin'), true);
795     result = model.set({a: 200, admin: false}, {validate: true});
796     assert.equal(lastError, "Can't change admin status.");
797     assert.equal(result, false);
798     assert.equal(model.get('a'), 100);
799   });
800
801   QUnit.test('validate on unset and clear', function(assert) {
802     assert.expect(6);
803     var error;
804     var model = new Backbone.Model({name: 'One'});
805     model.validate = function(attrs) {
806       if (!attrs.name) {
807         error = true;
808         return 'No thanks.';
809       }
810     };
811     model.set({name: 'Two'});
812     assert.equal(model.get('name'), 'Two');
813     assert.equal(error, undefined);
814     model.unset('name', {validate: true});
815     assert.equal(error, true);
816     assert.equal(model.get('name'), 'Two');
817     model.clear({validate: true});
818     assert.equal(model.get('name'), 'Two');
819     delete model.validate;
820     model.clear();
821     assert.equal(model.get('name'), undefined);
822   });
823
824   QUnit.test('validate with error callback', function(assert) {
825     assert.expect(8);
826     var lastError, boundError;
827     var model = new Backbone.Model();
828     model.validate = function(attrs) {
829       if (attrs.admin) return "Can't change admin status.";
830     };
831     model.on('invalid', function(m, error) {
832       boundError = true;
833     });
834     var result = model.set({a: 100}, {validate: true});
835     assert.equal(result, model);
836     assert.equal(model.get('a'), 100);
837     assert.equal(model.validationError, null);
838     assert.equal(boundError, undefined);
839     result = model.set({a: 200, admin: true}, {validate: true});
840     assert.equal(result, false);
841     assert.equal(model.get('a'), 100);
842     assert.equal(model.validationError, "Can't change admin status.");
843     assert.equal(boundError, true);
844   });
845
846   QUnit.test('defaults always extend attrs (#459)', function(assert) {
847     assert.expect(2);
848     var Defaulted = Backbone.Model.extend({
849       defaults: {one: 1},
850       initialize: function(attrs, opts) {
851         assert.equal(this.attributes.one, 1);
852       }
853     });
854     var providedattrs = new Defaulted({});
855     var emptyattrs = new Defaulted();
856   });
857
858   QUnit.test('Inherit class properties', function(assert) {
859     assert.expect(6);
860     var Parent = Backbone.Model.extend({
861       instancePropSame: function() {},
862       instancePropDiff: function() {}
863     }, {
864       classProp: function() {}
865     });
866     var Child = Parent.extend({
867       instancePropDiff: function() {}
868     });
869
870     var adult = new Parent;
871     var kid   = new Child;
872
873     assert.equal(Child.classProp, Parent.classProp);
874     assert.notEqual(Child.classProp, undefined);
875
876     assert.equal(kid.instancePropSame, adult.instancePropSame);
877     assert.notEqual(kid.instancePropSame, undefined);
878
879     assert.notEqual(Child.prototype.instancePropDiff, Parent.prototype.instancePropDiff);
880     assert.notEqual(Child.prototype.instancePropDiff, undefined);
881   });
882
883   QUnit.test("Nested change events don't clobber previous attributes", function(assert) {
884     assert.expect(4);
885     new Backbone.Model()
886     .on('change:state', function(m, newState) {
887       assert.equal(m.previous('state'), undefined);
888       assert.equal(newState, 'hello');
889       // Fire a nested change event.
890       m.set({other: 'whatever'});
891     })
892     .on('change:state', function(m, newState) {
893       assert.equal(m.previous('state'), undefined);
894       assert.equal(newState, 'hello');
895     })
896     .set({state: 'hello'});
897   });
898
899   QUnit.test('hasChanged/set should use same comparison', function(assert) {
900     assert.expect(2);
901     var changed = 0, model = new Backbone.Model({a: null});
902     model.on('change', function() {
903       assert.ok(this.hasChanged('a'));
904     })
905     .on('change:a', function() {
906       changed++;
907     })
908     .set({a: undefined});
909     assert.equal(changed, 1);
910   });
911
912   QUnit.test('#582, #425, change:attribute callbacks should fire after all changes have occurred', function(assert) {
913     assert.expect(9);
914     var model = new Backbone.Model;
915
916     var assertion = function() {
917       assert.equal(model.get('a'), 'a');
918       assert.equal(model.get('b'), 'b');
919       assert.equal(model.get('c'), 'c');
920     };
921
922     model.on('change:a', assertion);
923     model.on('change:b', assertion);
924     model.on('change:c', assertion);
925
926     model.set({a: 'a', b: 'b', c: 'c'});
927   });
928
929   QUnit.test('#871, set with attributes property', function(assert) {
930     assert.expect(1);
931     var model = new Backbone.Model();
932     model.set({attributes: true});
933     assert.ok(model.has('attributes'));
934   });
935
936   QUnit.test('set value regardless of equality/change', function(assert) {
937     assert.expect(1);
938     var model = new Backbone.Model({x: []});
939     var a = [];
940     model.set({x: a});
941     assert.ok(model.get('x') === a);
942   });
943
944   QUnit.test('set same value does not trigger change', function(assert) {
945     assert.expect(0);
946     var model = new Backbone.Model({x: 1});
947     model.on('change change:x', function() { assert.ok(false); });
948     model.set({x: 1});
949     model.set({x: 1});
950   });
951
952   QUnit.test('unset does not fire a change for undefined attributes', function(assert) {
953     assert.expect(0);
954     var model = new Backbone.Model({x: undefined});
955     model.on('change:x', function(){ assert.ok(false); });
956     model.unset('x');
957   });
958
959   QUnit.test('set: undefined values', function(assert) {
960     assert.expect(1);
961     var model = new Backbone.Model({x: undefined});
962     assert.ok('x' in model.attributes);
963   });
964
965   QUnit.test('hasChanged works outside of change events, and true within', function(assert) {
966     assert.expect(6);
967     var model = new Backbone.Model({x: 1});
968     model.on('change:x', function() {
969       assert.ok(model.hasChanged('x'));
970       assert.equal(model.get('x'), 1);
971     });
972     model.set({x: 2}, {silent: true});
973     assert.ok(model.hasChanged());
974     assert.equal(model.hasChanged('x'), true);
975     model.set({x: 1});
976     assert.ok(model.hasChanged());
977     assert.equal(model.hasChanged('x'), true);
978   });
979
980   QUnit.test('hasChanged gets cleared on the following set', function(assert) {
981     assert.expect(4);
982     var model = new Backbone.Model;
983     model.set({x: 1});
984     assert.ok(model.hasChanged());
985     model.set({x: 1});
986     assert.ok(!model.hasChanged());
987     model.set({x: 2});
988     assert.ok(model.hasChanged());
989     model.set({});
990     assert.ok(!model.hasChanged());
991   });
992
993   QUnit.test('save with `wait` succeeds without `validate`', function(assert) {
994     assert.expect(1);
995     var model = new Backbone.Model();
996     model.url = '/test';
997     model.save({x: 1}, {wait: true});
998     assert.ok(this.syncArgs.model === model);
999   });
1000
1001   QUnit.test("save without `wait` doesn't set invalid attributes", function(assert) {
1002     var model = new Backbone.Model();
1003     model.validate = function() { return 1; };
1004     model.save({a: 1});
1005     assert.equal(model.get('a'), void 0);
1006   });
1007
1008   QUnit.test("save doesn't validate twice", function(assert) {
1009     var model = new Backbone.Model();
1010     var times = 0;
1011     model.sync = function() {};
1012     model.validate = function() { ++times; };
1013     model.save({});
1014     assert.equal(times, 1);
1015   });
1016
1017   QUnit.test('`hasChanged` for falsey keys', function(assert) {
1018     assert.expect(2);
1019     var model = new Backbone.Model();
1020     model.set({x: true}, {silent: true});
1021     assert.ok(!model.hasChanged(0));
1022     assert.ok(!model.hasChanged(''));
1023   });
1024
1025   QUnit.test('`previous` for falsey keys', function(assert) {
1026     assert.expect(2);
1027     var model = new Backbone.Model({'0': true, '': true});
1028     model.set({'0': false, '': false}, {silent: true});
1029     assert.equal(model.previous(0), true);
1030     assert.equal(model.previous(''), true);
1031   });
1032
1033   QUnit.test('`save` with `wait` sends correct attributes', function(assert) {
1034     assert.expect(5);
1035     var changed = 0;
1036     var model = new Backbone.Model({x: 1, y: 2});
1037     model.url = '/test';
1038     model.on('change:x', function() { changed++; });
1039     model.save({x: 3}, {wait: true});
1040     assert.deepEqual(JSON.parse(this.ajaxSettings.data), {x: 3, y: 2});
1041     assert.equal(model.get('x'), 1);
1042     assert.equal(changed, 0);
1043     this.syncArgs.options.success({});
1044     assert.equal(model.get('x'), 3);
1045     assert.equal(changed, 1);
1046   });
1047
1048   QUnit.test("a failed `save` with `wait` doesn't leave attributes behind", function(assert) {
1049     assert.expect(1);
1050     var model = new Backbone.Model;
1051     model.url = '/test';
1052     model.save({x: 1}, {wait: true});
1053     assert.equal(model.get('x'), void 0);
1054   });
1055
1056   QUnit.test('#1030 - `save` with `wait` results in correct attributes if success is called during sync', function(assert) {
1057     assert.expect(2);
1058     var model = new Backbone.Model({x: 1, y: 2});
1059     model.sync = function(method, m, options) {
1060       options.success();
1061     };
1062     model.on('change:x', function() { assert.ok(true); });
1063     model.save({x: 3}, {wait: true});
1064     assert.equal(model.get('x'), 3);
1065   });
1066
1067   QUnit.test('save with wait validates attributes', function(assert) {
1068     var model = new Backbone.Model();
1069     model.url = '/test';
1070     model.validate = function() { assert.ok(true); };
1071     model.save({x: 1}, {wait: true});
1072   });
1073
1074   QUnit.test('save turns on parse flag', function(assert) {
1075     var Model = Backbone.Model.extend({
1076       sync: function(method, m, options) { assert.ok(options.parse); }
1077     });
1078     new Model().save();
1079   });
1080
1081   QUnit.test("nested `set` during `'change:attr'`", function(assert) {
1082     assert.expect(2);
1083     var events = [];
1084     var model = new Backbone.Model();
1085     model.on('all', function(event) { events.push(event); });
1086     model.on('change', function() {
1087       model.set({z: true}, {silent: true});
1088     });
1089     model.on('change:x', function() {
1090       model.set({y: true});
1091     });
1092     model.set({x: true});
1093     assert.deepEqual(events, ['change:y', 'change:x', 'change']);
1094     events = [];
1095     model.set({z: true});
1096     assert.deepEqual(events, []);
1097   });
1098
1099   QUnit.test('nested `change` only fires once', function(assert) {
1100     assert.expect(1);
1101     var model = new Backbone.Model();
1102     model.on('change', function() {
1103       assert.ok(true);
1104       model.set({x: true});
1105     });
1106     model.set({x: true});
1107   });
1108
1109   QUnit.test("nested `set` during `'change'`", function(assert) {
1110     assert.expect(6);
1111     var count = 0;
1112     var model = new Backbone.Model();
1113     model.on('change', function() {
1114       switch (count++) {
1115         case 0:
1116           assert.deepEqual(this.changedAttributes(), {x: true});
1117           assert.equal(model.previous('x'), undefined);
1118           model.set({y: true});
1119           break;
1120         case 1:
1121           assert.deepEqual(this.changedAttributes(), {x: true, y: true});
1122           assert.equal(model.previous('x'), undefined);
1123           model.set({z: true});
1124           break;
1125         case 2:
1126           assert.deepEqual(this.changedAttributes(), {x: true, y: true, z: true});
1127           assert.equal(model.previous('y'), undefined);
1128           break;
1129         default:
1130           assert.ok(false);
1131       }
1132     });
1133     model.set({x: true});
1134   });
1135
1136   QUnit.test('nested `change` with silent', function(assert) {
1137     assert.expect(3);
1138     var count = 0;
1139     var model = new Backbone.Model();
1140     model.on('change:y', function() { assert.ok(false); });
1141     model.on('change', function() {
1142       switch (count++) {
1143         case 0:
1144           assert.deepEqual(this.changedAttributes(), {x: true});
1145           model.set({y: true}, {silent: true});
1146           model.set({z: true});
1147           break;
1148         case 1:
1149           assert.deepEqual(this.changedAttributes(), {x: true, y: true, z: true});
1150           break;
1151         case 2:
1152           assert.deepEqual(this.changedAttributes(), {z: false});
1153           break;
1154         default:
1155           assert.ok(false);
1156       }
1157     });
1158     model.set({x: true});
1159     model.set({z: false});
1160   });
1161
1162   QUnit.test('nested `change:attr` with silent', function(assert) {
1163     assert.expect(0);
1164     var model = new Backbone.Model();
1165     model.on('change:y', function(){ assert.ok(false); });
1166     model.on('change', function() {
1167       model.set({y: true}, {silent: true});
1168       model.set({z: true});
1169     });
1170     model.set({x: true});
1171   });
1172
1173   QUnit.test('multiple nested changes with silent', function(assert) {
1174     assert.expect(1);
1175     var model = new Backbone.Model();
1176     model.on('change:x', function() {
1177       model.set({y: 1}, {silent: true});
1178       model.set({y: 2});
1179     });
1180     model.on('change:y', function(m, val) {
1181       assert.equal(val, 2);
1182     });
1183     model.set({x: true});
1184   });
1185
1186   QUnit.test('multiple nested changes with silent', function(assert) {
1187     assert.expect(1);
1188     var changes = [];
1189     var model = new Backbone.Model();
1190     model.on('change:b', function(m, val) { changes.push(val); });
1191     model.on('change', function() {
1192       model.set({b: 1});
1193     });
1194     model.set({b: 0});
1195     assert.deepEqual(changes, [0, 1]);
1196   });
1197
1198   QUnit.test('basic silent change semantics', function(assert) {
1199     assert.expect(1);
1200     var model = new Backbone.Model;
1201     model.set({x: 1});
1202     model.on('change', function(){ assert.ok(true); });
1203     model.set({x: 2}, {silent: true});
1204     model.set({x: 1});
1205   });
1206
1207   QUnit.test('nested set multiple times', function(assert) {
1208     assert.expect(1);
1209     var model = new Backbone.Model();
1210     model.on('change:b', function() {
1211       assert.ok(true);
1212     });
1213     model.on('change:a', function() {
1214       model.set({b: true});
1215       model.set({b: true});
1216     });
1217     model.set({a: true});
1218   });
1219
1220   QUnit.test('#1122 - clear does not alter options.', function(assert) {
1221     assert.expect(1);
1222     var model = new Backbone.Model();
1223     var options = {};
1224     model.clear(options);
1225     assert.ok(!options.unset);
1226   });
1227
1228   QUnit.test('#1122 - unset does not alter options.', function(assert) {
1229     assert.expect(1);
1230     var model = new Backbone.Model();
1231     var options = {};
1232     model.unset('x', options);
1233     assert.ok(!options.unset);
1234   });
1235
1236   QUnit.test('#1355 - `options` is passed to success callbacks', function(assert) {
1237     assert.expect(3);
1238     var model = new Backbone.Model();
1239     var opts = {
1240       success: function( m, resp, options ) {
1241         assert.ok(options);
1242       }
1243     };
1244     model.sync = function(method, m, options) {
1245       options.success();
1246     };
1247     model.save({id: 1}, opts);
1248     model.fetch(opts);
1249     model.destroy(opts);
1250   });
1251
1252   QUnit.test("#1412 - Trigger 'sync' event.", function(assert) {
1253     assert.expect(3);
1254     var model = new Backbone.Model({id: 1});
1255     model.sync = function(method, m, options) { options.success(); };
1256     model.on('sync', function(){ assert.ok(true); });
1257     model.fetch();
1258     model.save();
1259     model.destroy();
1260   });
1261
1262   QUnit.test('#1365 - Destroy: New models execute success callback.', function(assert) {
1263     var done = assert.async();
1264     assert.expect(2);
1265     new Backbone.Model()
1266     .on('sync', function() { assert.ok(false); })
1267     .on('destroy', function(){ assert.ok(true); })
1268     .destroy({success: function(){
1269       assert.ok(true);
1270       done();
1271     }});
1272   });
1273
1274   QUnit.test('#1433 - Save: An invalid model cannot be persisted.', function(assert) {
1275     assert.expect(1);
1276     var model = new Backbone.Model;
1277     model.validate = function(){ return 'invalid'; };
1278     model.sync = function(){ assert.ok(false); };
1279     assert.strictEqual(model.save(), false);
1280   });
1281
1282   QUnit.test("#1377 - Save without attrs triggers 'error'.", function(assert) {
1283     assert.expect(1);
1284     var Model = Backbone.Model.extend({
1285       url: '/test/',
1286       sync: function(method, m, options){ options.success(); },
1287       validate: function(){ return 'invalid'; }
1288     });
1289     var model = new Model({id: 1});
1290     model.on('invalid', function(){ assert.ok(true); });
1291     model.save();
1292   });
1293
1294   QUnit.test('#1545 - `undefined` can be passed to a model constructor without coersion', function(assert) {
1295     var Model = Backbone.Model.extend({
1296       defaults: {one: 1},
1297       initialize: function(attrs, opts) {
1298         assert.equal(attrs, undefined);
1299       }
1300     });
1301     var emptyattrs = new Model();
1302     var undefinedattrs = new Model(undefined);
1303   });
1304
1305   QUnit.test('#1478 - Model `save` does not trigger change on unchanged attributes', function(assert) {
1306     var done = assert.async();
1307     assert.expect(0);
1308     var Model = Backbone.Model.extend({
1309       sync: function(method, m, options) {
1310         setTimeout(function(){
1311           options.success();
1312           done();
1313         }, 0);
1314       }
1315     });
1316     new Model({x: true})
1317     .on('change:x', function(){ assert.ok(false); })
1318     .save(null, {wait: true});
1319   });
1320
1321   QUnit.test('#1664 - Changing from one value, silently to another, back to original triggers a change.', function(assert) {
1322     assert.expect(1);
1323     var model = new Backbone.Model({x: 1});
1324     model.on('change:x', function() { assert.ok(true); });
1325     model.set({x: 2}, {silent: true});
1326     model.set({x: 3}, {silent: true});
1327     model.set({x: 1});
1328   });
1329
1330   QUnit.test('#1664 - multiple silent changes nested inside a change event', function(assert) {
1331     assert.expect(2);
1332     var changes = [];
1333     var model = new Backbone.Model();
1334     model.on('change', function() {
1335       model.set({a: 'c'}, {silent: true});
1336       model.set({b: 2}, {silent: true});
1337       model.unset('c', {silent: true});
1338     });
1339     model.on('change:a change:b change:c', function(m, val) { changes.push(val); });
1340     model.set({a: 'a', b: 1, c: 'item'});
1341     assert.deepEqual(changes, ['a', 1, 'item']);
1342     assert.deepEqual(model.attributes, {a: 'c', b: 2});
1343   });
1344
1345   QUnit.test('#1791 - `attributes` is available for `parse`', function(assert) {
1346     var Model = Backbone.Model.extend({
1347       parse: function() { this.has('a'); } // shouldn't throw an error
1348     });
1349     var model = new Model(null, {parse: true});
1350     assert.expect(0);
1351   });
1352
1353   QUnit.test('silent changes in last `change` event back to original triggers change', function(assert) {
1354     assert.expect(2);
1355     var changes = [];
1356     var model = new Backbone.Model();
1357     model.on('change:a change:b change:c', function(m, val) { changes.push(val); });
1358     model.on('change', function() {
1359       model.set({a: 'c'}, {silent: true});
1360     });
1361     model.set({a: 'a'});
1362     assert.deepEqual(changes, ['a']);
1363     model.set({a: 'a'});
1364     assert.deepEqual(changes, ['a', 'a']);
1365   });
1366
1367   QUnit.test('#1943 change calculations should use _.isEqual', function(assert) {
1368     var model = new Backbone.Model({a: {key: 'value'}});
1369     model.set('a', {key: 'value'}, {silent: true});
1370     assert.equal(model.changedAttributes(), false);
1371   });
1372
1373   QUnit.test('#1964 - final `change` event is always fired, regardless of interim changes', function(assert) {
1374     assert.expect(1);
1375     var model = new Backbone.Model();
1376     model.on('change:property', function() {
1377       model.set('property', 'bar');
1378     });
1379     model.on('change', function() {
1380       assert.ok(true);
1381     });
1382     model.set('property', 'foo');
1383   });
1384
1385   QUnit.test('isValid', function(assert) {
1386     var model = new Backbone.Model({valid: true});
1387     model.validate = function(attrs) {
1388       if (!attrs.valid) return 'invalid';
1389     };
1390     assert.equal(model.isValid(), true);
1391     assert.equal(model.set({valid: false}, {validate: true}), false);
1392     assert.equal(model.isValid(), true);
1393     model.set({valid: false});
1394     assert.equal(model.isValid(), false);
1395     assert.ok(!model.set('valid', false, {validate: true}));
1396   });
1397
1398   QUnit.test('#1179 - isValid returns true in the absence of validate.', function(assert) {
1399     assert.expect(1);
1400     var model = new Backbone.Model();
1401     model.validate = null;
1402     assert.ok(model.isValid());
1403   });
1404
1405   QUnit.test('#1961 - Creating a model with {validate:true} will call validate and use the error callback', function(assert) {
1406     var Model = Backbone.Model.extend({
1407       validate: function(attrs) {
1408         if (attrs.id === 1) return "This shouldn't happen";
1409       }
1410     });
1411     var model = new Model({id: 1}, {validate: true});
1412     assert.equal(model.validationError, "This shouldn't happen");
1413   });
1414
1415   QUnit.test('toJSON receives attrs during save(..., {wait: true})', function(assert) {
1416     assert.expect(1);
1417     var Model = Backbone.Model.extend({
1418       url: '/test',
1419       toJSON: function() {
1420         assert.strictEqual(this.attributes.x, 1);
1421         return _.clone(this.attributes);
1422       }
1423     });
1424     var model = new Model;
1425     model.save({x: 1}, {wait: true});
1426   });
1427
1428   QUnit.test('#2034 - nested set with silent only triggers one change', function(assert) {
1429     assert.expect(1);
1430     var model = new Backbone.Model();
1431     model.on('change', function() {
1432       model.set({b: true}, {silent: true});
1433       assert.ok(true);
1434     });
1435     model.set({a: true});
1436   });
1437
1438   QUnit.test('#3778 - id will only be updated if it is set', function(assert) {
1439     assert.expect(2);
1440     var model = new Backbone.Model({id: 1});
1441     model.id = 2;
1442     model.set({foo: 'bar'});
1443     assert.equal(model.id, 2);
1444     model.set({id: 3});
1445     assert.equal(model.id, 3);
1446   });
1447
1448 })(QUnit);