diff --git a/README.md b/README.md index 1cb4c29..1b5564c 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ contacts.fetch() if (_.isError(data)) console.error(data) else - console.log('Failed to fetch collection'); + console.log('Failed to fetch collection:', data.response.statusText); }); ``` @@ -83,7 +83,7 @@ contact.fetch() if (_.isError(data)) console.error(data) else - console.log('Failed to fetch contact'); + console.log('Failed to fetch contact:', data.response.statusText); }); ``` @@ -105,7 +105,7 @@ Backbone.Async adds additional events when synchronizing a Model or Collection. ```javascript var Contacts = Backbone.Async.Collection.extend({ - url: 'http://example.com/contacts' + url: '/contacts' }); var contacts = new Contacts(); @@ -147,7 +147,7 @@ The *after:fetch* event handler receives the exact same object provided to the f ```javascript var Contact = Backbone.Async.Contact.extend({ - urlRoot: 'http://example.com/contacts' + urlRoot: '/contacts' }); var contact = new Contact({id: 1}); @@ -194,6 +194,77 @@ The *before:fetch* and *before:destroy* event handlers will receive an object co The *before:save* event handler argument also includes an additional property named *attrs* with the attributes being saved. All *after:[event]* handlers receive the exact same object provided to the fulfilled and rejection callbacks plus a *success* argument. +
+###Storage + +
+The Storage class provides an additional wrapper for Model and Collection classes. + +```javascript +var Note = Backbone.Async.Model.extend({ + //... +}); + +var Notes = Backbone.Async.Collection.extend({ + //... +}); + +//extend the Storage class +var NotesStore = Backbone.Async.Storage.extend({ + Model: Note, + Collection: Notes +}); + +var storage = new NotesStore(); +``` + +
+**Get model** +```javascript +//obtain model with ID 1 +storage.get(1, {option: 'value'}) +.then(function(data) { + var note = storage.collection.get(1); //returns model + + //the 'get' method can be called more than once + //the returned model will be stored internally + //the 'response' property will not be set if the object was alreasy loaded + if (data.response) { + console.log('A request has been made'); + } +}) +.catch(function(data_or_err) { + if (_.isError(data_or_err)) + console.error(err); + else + console.log(data.statusText); +}); +``` + +
+**Get collection** + +```javascript +//fetch all models +storage.fetch({option: 'value'}) +.then(function(data) { + storage.isLoaded === true; //returns true + + //same response rule applies to 'fetch' + if (data.response) { + console.log('A request has been made'); + } +}) +.catch(function(data_or_err) { + if (_.isError(data_or_err)) + console.error(err); + else + console.log(data.statusText); +}); +``` + +The Storage class also triggers before/after events when *get* and *fetch* are called. +
###License diff --git a/dist/backbone.async.js b/dist/backbone.async.js index 245b69c..63a8a9e 100644 --- a/dist/backbone.async.js +++ b/dist/backbone.async.js @@ -93,9 +93,11 @@ return methods.reduce(wrapMethod(Base.prototype), {}); }; + //create namespace var Async = Backbone.Async = Backbone.Async || {}; Async.VERSION = '1.1.0'; + //extend Model and Collection prototypes Async.Model = Backbone.Model.extend( buildPrototype(Backbone.Model, ['fetch', 'save', 'destroy']) ); @@ -111,8 +113,7 @@ if (options) { if (options.Collection) this.Collection = options.Collection; - if (options.Model) - this.Model = options.Model; + if (options.Model) this.Model = options.Model; else if (options.Collection && options.Collection.model) this.Model = options.Collection.model; } @@ -132,8 +133,7 @@ }); } - if (!this.Collection) - throw new Error('No Collection class defined'); + if (!this.Collection) throw new Error('No Collection class defined'); options = options || {}; var self = this; @@ -158,20 +158,16 @@ get: function(id, options) { if (this.collection) { var value = this.collection.get(id); - var data = { - model: value, - options: options - }; - - if (!value) return Promise.reject(data); - return Promise.resolve(data); + if (value) { + return Promise.resolve({ + model: value, + options: options + }); + } } - if (!this.Model) - throw new Error('No Model class defined'); - - if (!this.Collection) - throw new Error('No Collection class defined'); + if (!this.Model) throw new Error('No Model class defined'); + if (!this.Collection) throw new Error('No Collection class defined'); options = options || {}; var self = this; @@ -186,6 +182,9 @@ if (mustTrigger) self.trigger('after:get', data, true); if (!self.collection) self.collection = new self.Collection(); self.collection.push(model); + self.listenTo(model, 'after:destroy', function(data, success) { + if (success) self.collection.remove(data.model, {silent: true}); + }); resolve(data); }) .catch(function(data) { diff --git a/dist/backbone.async.min.js b/dist/backbone.async.min.js index 66ddb2c..cf2542a 100644 --- a/dist/backbone.async.min.js +++ b/dist/backbone.async.min.js @@ -1 +1 @@ -!function(a,b){"function"==typeof define&&define.amd?define(["backbone","underscore"],function(c,d){return b(a,c,d)}):"undefined"!=typeof exports?module.exports=b(a,require("backbone"),require("underscore")):b(a,a.Backbone,a._)}(this,function(a,b,c){var d=function(a,b,c){return function(d,e,f){a&&a.apply(d,arguments);var g={response:e,options:f};g[c.collection?"collection":"model"]=d,b(g),"undefined"!=typeof f.silent&&f.silent||d.trigger("after:"+c.method,g,c.success)}},e=function(a,b,d,e){var f,g;return"save"===a?null===b||"object"==typeof b?(f=b,g=d):b&&((f={})[b]=d,g=e):g=b,g=g?c.extend({},g):{},{attrs:f,options:g}},f=function(a){return function(f,g){return f[g]=function(f,h,i){var j=this,k=e(g,f,h,i),l=k.options,m=k.attrs,n=l.success,o=l.error,p={method:g,collection:a===b.Collection.prototype};return new Promise(function(b,e){l.success=d(n,b,c.extend({success:!0},p)),l.error=d(o,e,c.extend({success:!1},p)),p.collection?("undefined"!=typeof l.silent&&l.silent||j.trigger("before:"+g,{collection:j,options:l}),a[g].call(j,l)):("undefined"!=typeof l.silent&&l.silent||j.trigger("before:"+g,"save"===g?{model:j,options:l,attrs:m}:{model:j,options:l}),a[g].apply(j,"save"===g?[m,l]:[l]))})},f}},g=function(a,b){return b.reduce(f(a.prototype),{})},h=b.Async=b.Async||{};h.VERSION="1.1.0",h.Model=b.Model.extend(g(b.Model,["fetch","save","destroy"])),h.Collection=b.Collection.extend(g(b.Collection,["fetch"]));var i=h.Storage=function(a){this.isLoaded=!1,a&&(a.Collection&&(this.Collection=a.Collection),a.Model?this.Model=a.Model:a.Collection&&a.Collection.model&&(this.Model=a.Collection.model)),i.prototype.initialize.apply(this,arguments)};return c.extend(i.prototype,b.Events,{initialize:function(){},fetch:function(a){if(this.isLoaded)return Promise.resolve({collection:this.collection,options:a});if(!this.Collection)throw new Error("No Collection class defined");a=a||{};var b=this,c="undefined"==typeof a.silent||!a.silent;return new Promise(function(d,e){b.collection||(b.collection=new b.Collection),c&&b.trigger("before:fetch",{collection:b.collection,options:a}),b.collection.fetch(a).then(function(a){c&&b.trigger("after:fetch",a,!0),b.isLoaded=!0,d(a)})["catch"](function(a){c&&b.trigger("after:fetch",a,!1),e(a)})})},get:function(a,b){if(this.collection){var c=this.collection.get(a),d={model:c,options:b};return c?Promise.resolve(d):Promise.reject(d)}if(!this.Model)throw new Error("No Model class defined");if(!this.Collection)throw new Error("No Collection class defined");b=b||{};var e=this,f="undefined"==typeof b.silent||!b.silent;return new Promise(function(c,d){var g=new e.Model;g.set(e.Model.idAttribute||"id",a),f&&e.trigger("before:get",{model:g,options:b}),g.fetch(b).then(function(a){f&&e.trigger("after:get",a,!0),e.collection||(e.collection=new e.Collection),e.collection.push(g),c(a)})["catch"](function(a){f&&e.trigger("after:get",a,!1),d(a)})})}}),i.extend=b.Model.extend,h}); \ No newline at end of file +!function(a,b){"function"==typeof define&&define.amd?define(["backbone","underscore"],function(c,d){return b(a,c,d)}):"undefined"!=typeof exports?module.exports=b(a,require("backbone"),require("underscore")):b(a,a.Backbone,a._)}(this,function(a,b,c){var d=function(a,b,c){return function(d,e,f){a&&a.apply(d,arguments);var g={response:e,options:f};g[c.collection?"collection":"model"]=d,b(g),"undefined"!=typeof f.silent&&f.silent||d.trigger("after:"+c.method,g,c.success)}},e=function(a,b,d,e){var f,g;return"save"===a?null===b||"object"==typeof b?(f=b,g=d):b&&((f={})[b]=d,g=e):g=b,g=g?c.extend({},g):{},{attrs:f,options:g}},f=function(a){return function(f,g){return f[g]=function(f,h,i){var j=this,k=e(g,f,h,i),l=k.options,m=k.attrs,n=l.success,o=l.error,p={method:g,collection:a===b.Collection.prototype};return new Promise(function(b,e){l.success=d(n,b,c.extend({success:!0},p)),l.error=d(o,e,c.extend({success:!1},p)),p.collection?("undefined"!=typeof l.silent&&l.silent||j.trigger("before:"+g,{collection:j,options:l}),a[g].call(j,l)):("undefined"!=typeof l.silent&&l.silent||j.trigger("before:"+g,"save"===g?{model:j,options:l,attrs:m}:{model:j,options:l}),a[g].apply(j,"save"===g?[m,l]:[l]))})},f}},g=function(a,b){return b.reduce(f(a.prototype),{})},h=b.Async=b.Async||{};h.VERSION="1.1.0",h.Model=b.Model.extend(g(b.Model,["fetch","save","destroy"])),h.Collection=b.Collection.extend(g(b.Collection,["fetch"]));var i=h.Storage=function(a){this.isLoaded=!1,a&&(a.Collection&&(this.Collection=a.Collection),a.Model?this.Model=a.Model:a.Collection&&a.Collection.model&&(this.Model=a.Collection.model)),i.prototype.initialize.apply(this,arguments)};return c.extend(i.prototype,b.Events,{initialize:function(){},fetch:function(a){if(this.isLoaded)return Promise.resolve({collection:this.collection,options:a});if(!this.Collection)throw new Error("No Collection class defined");a=a||{};var b=this,c="undefined"==typeof a.silent||!a.silent;return new Promise(function(d,e){b.collection||(b.collection=new b.Collection),c&&b.trigger("before:fetch",{collection:b.collection,options:a}),b.collection.fetch(a).then(function(a){c&&b.trigger("after:fetch",a,!0),b.isLoaded=!0,d(a)})["catch"](function(a){c&&b.trigger("after:fetch",a,!1),e(a)})})},get:function(a,b){if(this.collection){var c=this.collection.get(a);if(c)return Promise.resolve({model:c,options:b})}if(!this.Model)throw new Error("No Model class defined");if(!this.Collection)throw new Error("No Collection class defined");b=b||{};var d=this,e="undefined"==typeof b.silent||!b.silent;return new Promise(function(c,f){var g=new d.Model;g.set(d.Model.idAttribute||"id",a),e&&d.trigger("before:get",{model:g,options:b}),g.fetch(b).then(function(a){e&&d.trigger("after:get",a,!0),d.collection||(d.collection=new d.Collection),d.collection.push(g),d.listenTo(g,"after:destroy",function(a,b){b&&d.collection.remove(a.model,{silent:!0})}),c(a)})["catch"](function(a){e&&d.trigger("after:get",a,!1),f(a)})})}}),i.extend=b.Model.extend,h}); \ No newline at end of file diff --git a/test/specs/storage.js b/test/specs/storage.js index 2ecbb7f..cf5a3c4 100644 --- a/test/specs/storage.js +++ b/test/specs/storage.js @@ -269,5 +269,565 @@ describe("ASync.Storage tests", function() { server.respond(); }); + + it('must call callbacks in order', function(done) { + var values = [ + {id: 1, name: 'Curly'}, + {id: 2, name: 'Larry'}, + {id: 3, name: 'Moe'} + ]; + + server.respondWith( + 'GET', + '/contacts', + [ + 200, + {"Content-Type": "application/json"}, + JSON.stringify(values) + ] + ); + + var callback1 = sinon.spy(); + var callback2 = sinon.spy(); + + var storage = new FIXTURES.ContactsStorage(); + storage.fetch({test: true, silent:false}) + .then(callback1) + .then(callback2) + .then(function(data) { + expect(callback1.called).to.be.true; + expect(callback1.calledOnce).to.be.true; + expect(callback2.called).to.be.true; + expect(callback2.calledOnce).to.be.true; + expect(callback1.calledBefore(callback2)).to.be.true; + done(); + }) + .catch(function(err) { done(err); }); + + server.respond(); + }); + + it('must call event handlers', function(done) { + var values = [ + {id: 1, name: 'Curly'}, + {id: 2, name: 'Larry'}, + {id: 3, name: 'Moe'} + ]; + + server.respondWith( + 'GET', + '/contacts', + [ + 200, + {"Content-Type": "application/json"}, + JSON.stringify(values) + ] + ); + + var storage = new FIXTURES.ContactsStorage(); + var obj = _.extend({}, Backbone.Events); + var beforeCallback = sinon.spy(); + var afterCallback = sinon.spy(); + obj.listenTo(storage, 'before:fetch', beforeCallback); + obj.listenTo(storage, 'after:fetch', afterCallback); + + storage.fetch({test: true, silent:false}) + .then(function(data) { + expect(beforeCallback.called).to.be.true; + expect(afterCallback.called).to.be.true; + expect(beforeCallback.calledBefore(afterCallback)).to.be.true; + + var dataArg = beforeCallback.args[0][0]; + expect(dataArg).to.be.a('object'); + expect(dataArg).to.have.property('collection'); + expect(dataArg).to.have.property('options'); + + expect(dataArg.options).to.have.property('test'); + expect(dataArg.options).to.have.property('silent'); + expect(dataArg.options.test).to.be.true; + expect(dataArg.options.silent).to.be.false; + + dataArg = afterCallback.args[0][0]; + expect(dataArg).to.be.a('object'); + expect(dataArg).to.have.property('collection'); + expect(dataArg).to.have.property('options'); + expect(dataArg).to.have.property('response'); + expect(dataArg.collection).to.be.deep.equal(storage.collection); + expect(dataArg.collection.length).to.equal(3); + expect(dataArg.options).to.have.property('test'); + expect(dataArg.options).to.have.property('silent'); + expect(dataArg.options.test).to.be.true; + expect(dataArg.options.silent).to.be.false; + expect(dataArg.response).to.be.deep.equal(values); + + var success = afterCallback.args[0][1]; + expect(success).to.be.true; + done(); + }) + .catch(function(err) { done(err); }); + + server.respond(); + }); + + it('must not call event handlers', function(done) { + var values = [ + {id: 1, name: 'Curly'}, + {id: 2, name: 'Larry'}, + {id: 3, name: 'Moe'} + ]; + + server.respondWith( + 'GET', + '/contacts', + [ + 200, + {"Content-Type": "application/json"}, + JSON.stringify(values) + ] + ); + + var storage = new FIXTURES.ContactsStorage(); + var obj = _.extend({}, Backbone.Events); + var beforeCallback = sinon.spy(); + var afterCallback = sinon.spy(); + obj.listenTo(storage, 'before:fetch', beforeCallback); + obj.listenTo(storage, 'after:fetch', afterCallback); + + storage.fetch({test: true, silent:true}) + .then(function(data) { + expect(beforeCallback.called).to.be.false; + expect(afterCallback.called).to.be.false; + done(); + }) + .catch(function(err) { done(err); }); + + server.respond(); + }); + + it('must return previous object', function(done) { + var values = [ + {id: 1, name: 'Curly'}, + {id: 2, name: 'Larry'}, + {id: 3, name: 'Moe'} + ]; + + server.respondWith( + 'GET', + '/contacts', + [ + 200, + {"Content-Type": "application/json"}, + JSON.stringify(values) + ] + ); + + var storage = new FIXTURES.ContactsStorage(); + storage.fetch() + .then(function(data) { + expect(data).to.be.a('object'); + expect(data).to.have.property('collection'); + expect(data).to.have.property('response'); + expect(data).to.have.property('options'); + + var obj = _.extend({}, Backbone.Events); + var beforeCallback = sinon.spy(); + var afterCallback = sinon.spy(); + obj.listenTo(storage, 'before:fetch', beforeCallback); + obj.listenTo(storage, 'after:fetch', afterCallback); + + storage.fetch({test: true, silent:false}) + .then(function(data) { + expect(data).to.be.a('object'); + expect(data).to.have.property('collection'); + expect(data).to.not.have.property('response'); + expect(data).to.have.property('options'); + + expect(beforeCallback.called).to.be.false; + expect(afterCallback.called).to.be.false; + + expect(data.collection).to.deep.equal(storage.collection); + expect(storage.isLoaded).to.be.true; + + expect(data.options).to.have.property('test'); + expect(data.options).to.have.property('silent'); + expect(data.options.test).to.be.true; + expect(data.options.silent).to.be.false; + + done(); + }) + .catch(function(err) { done(err); }); + }) + .catch(function(err) { done(err); }); + + server.respond(); + }); + }); + + describe('Fetch by ID fail tests', function() { + it('must call catch', function(done) { + server.respondWith( + 'GET', + '/notes/1', + [ + 404, + null, + '' + ] + ); + + var storage = new FIXTURES.NotesStorage(); + storage.get(1, {test: true, silent: false}) + .then(function() { + }) + .catch(function(data) { + expect(data).to.be.a('object'); + + expect(data).to.have.property('model'); + expect(data).to.have.property('response'); + expect(data).to.have.property('options'); + + expect(data.response).to.be.a('object'); + expect(data.response.status).to.equal(404); + expect(data.response.statusText).to.equal("Not Found"); + + expect(data.options).to.have.property('test'); + expect(data.options).to.have.property('silent'); + expect(data.options.test).to.be.true; + expect(data.options.silent).to.be.false; + + expect(storage.isLoaded).to.be.false; + expect(storage.collection).to.be.undefined; + done(); + }); + + server.respond(); + }); + + it('must call event handlers', function(done) { + server.respondWith( + 'GET', + '/notes/2', + [ + 500, + null, + '' + ] + ); + + var storage = new FIXTURES.NotesStorage(); + var obj = _.extend({}, Backbone.Events); + var beforeCallback = sinon.spy(); + var afterCallback = sinon.spy(); + obj.listenTo(storage, 'before:get', beforeCallback); + obj.listenTo(storage, 'after:get', afterCallback); + + storage.get(2, {test: true, silent: false}) + .then(function() {}) + .catch(function(data) { + expect(beforeCallback.called).to.be.true; + expect(afterCallback.called).to.be.true; + expect(beforeCallback.calledBefore(afterCallback)).to.be.true; + + var dataArg = beforeCallback.args[0][0]; + expect(dataArg).to.be.a('object'); + expect(dataArg).to.have.property('model'); + expect(dataArg).to.have.property('options'); + expect(dataArg.options).to.have.property('test'); + expect(dataArg.options).to.have.property('silent'); + expect(dataArg.options.test).to.be.true; + expect(dataArg.options.silent).to.be.false; + + dataArg = afterCallback.args[0][0]; + expect(dataArg).to.be.a('object'); + expect(dataArg).to.have.property('model'); + expect(dataArg).to.have.property('options'); + expect(dataArg).to.have.property('response'); + expect(dataArg.options).to.have.property('test'); + expect(dataArg.options).to.have.property('silent'); + expect(dataArg.options.test).to.be.true; + expect(dataArg.options.silent).to.be.false; + expect(dataArg.response.status).to.equal(500); + expect(dataArg.response.statusText).to.equal("Internal Server Error"); + + var success = afterCallback.args[0][1]; + expect(success).to.be.false; + + done(); + }); + + server.respond(); + }); + + it('must not call event handlers', function(done) { + server.respondWith( + 'GET', + '/notes/3', + [ + 500, + null, + '' + ] + ); + + var storage = new FIXTURES.NotesStorage(); + var obj = _.extend({}, Backbone.Events); + var beforeCallback = sinon.spy(); + var afterCallback = sinon.spy(); + obj.listenTo(storage, 'before:get', beforeCallback); + obj.listenTo(storage, 'after:get', afterCallback); + + storage.get(3, {test: false, silent: true}) + .then(function() {}) + .catch(function(data) { + expect(beforeCallback.called).to.be.false; + expect(afterCallback.called).to.be.false; + + expect(data.options).to.have.property('test'); + expect(data.options).to.have.property('silent'); + expect(data.options.test).to.be.false; + expect(data.options.silent).to.be.true; + + done(); + }); + + server.respond(); + }); + + it('must throw error', function(done) { + var value = {id: 4, message: 'Hello World'}; + server.respondWith( + 'GET', + '/notes/4', + [ + 200, + null, + JSON.stringify(value) + ] + ); + + var storage = new FIXTURES.NotesStorage(); + storage.get(4) + .then(function(data) { + throw new Error(); + }) + .catch(function(err) { + expect(_.isError(err)).to.be.true; + done(); + }) + + server.respond(); + }); + }); + + describe('Fetch all fail tests', function() { + it('must call catch', function(done) { + server.respondWith( + 'GET', + '/notes', + [ + 404, + null, + '' + ] + ); + + var storage = new FIXTURES.NotesStorage(); + + storage.fetch({test: true, silent: false}) + .then(function() { + + }) + .catch(function(data) { + expect(data).to.be.a('object'); + + expect(data).to.have.property('collection'); + expect(data).to.have.property('response'); + expect(data).to.have.property('options'); + + expect(storage.isLoaded).to.be.false; + expect(storage.collection.length).to.equal(0); + expect(data.response).to.be.a('object'); + expect(data.response.status).to.equal(404); + expect(data.response.statusText).to.equal("Not Found"); + + var options = data.options; + expect(options).to.have.property('test'); + expect(options).to.have.property('silent'); + expect(options.test).to.be.true; + expect(options.silent).to.be.false; + + done(); + }); + + server.respond(); + }); + + it('must call event handlers', function(done) { + server.respondWith( + 'GET', + '/notes', + [ + 500, + null, + '' + ] + ); + + var obj = _.extend({}, Backbone.Events); + var beforeCallback = sinon.spy(); + var afterCallback = sinon.spy(); + var storage = new FIXTURES.NotesStorage(); + obj.listenTo(storage, 'before:fetch', beforeCallback); + obj.listenTo(storage, 'after:fetch', afterCallback); + + storage.fetch({test: true, silent: false}) + .then(function() { + }) + .catch(function(data) { + expect(beforeCallback.called).to.be.true; + expect(afterCallback.called).to.be.true; + expect(beforeCallback.calledBefore(afterCallback)).to.be.true; + + var dataArg = beforeCallback.args[0][0]; + expect(dataArg).to.be.a('object'); + expect(dataArg).to.have.property('collection'); + expect(dataArg).to.have.property('options'); + expect(dataArg.collection.length).to.be.equal(0); + expect(dataArg.options).to.have.property('test'); + expect(dataArg.options).to.have.property('silent'); + expect(dataArg.options.test).to.be.true; + expect(dataArg.options.silent).to.be.false; + + dataArg = afterCallback.args[0][0]; + expect(dataArg).to.be.a('object'); + expect(dataArg).to.have.property('collection'); + expect(dataArg).to.have.property('options'); + expect(dataArg).to.have.property('response'); + expect(dataArg.collection.length).to.be.equal(0); + expect(dataArg.options).to.have.property('test'); + expect(dataArg.options).to.have.property('silent'); + expect(dataArg.options.test).to.be.true; + expect(dataArg.options.silent).to.be.false; + expect(dataArg.response.status).to.equal(500); + expect(dataArg.response.statusText).to.equal("Internal Server Error"); + + var success = afterCallback.args[0][1]; + expect(success).to.be.false; + + expect(storage.collection.length).to.equal(0); + expect(storage.isLoaded).to.be.false; + done(); + }); + + server.respond(); + }); + + it('must not call event handlers', function(done) { + server.respondWith( + 'GET', + '/notes', + [ + 500, + null, + '' + ] + ); + + var obj = _.extend({}, Backbone.Events); + var beforeCallback = sinon.spy(); + var afterCallback = sinon.spy(); + var storage = new FIXTURES.NotesStorage(); + obj.listenTo(storage, 'before:fetch', beforeCallback); + obj.listenTo(storage, 'after:fetch', afterCallback); + + storage.fetch({test: false, silent: true}) + .then(function() {}) + .catch(function(data) { + expect(beforeCallback.called).to.be.false; + expect(afterCallback.called).to.be.false; + done(); + }); + + server.respond(); + }); + + it('must throw error', function(done) { + var values = [ + {id: 1, message: 'Hola'}, + {id: 2, message: 'Hello'}, + {id: 3, message: 'Ciao'} + ]; + + server.respondWith( + 'GET', + '/notes', + [ + 200, + null, + JSON.stringify(values) + ] + ); + + var storage = new FIXTURES.NotesStorage(); + + storage.fetch() + .then(function(data) { + throw new Error(); + }) + .catch(function(err) { + expect(_.isError(err)).to.be.true; + done(); + }); + + server.respond(); + }); + }); + + describe('Delete tests', function() { + it('must remove model', function(done) { + var value = { id: 1, name: "Emmanuel", surname: "Antico"}; + server.respondWith( + 'GET', + '/contacts/1', + [ + 200, + {"Content-Type": "application/json"}, + JSON.stringify(value) + ] + ); + + server.respondWith( + 'DELETE', + '/contacts/1', + [ + 204, + {"Content-Type": "application/json"}, + '' + ] + ); + + var storage = new FIXTURES.ContactsStorage(); + + storage.get(1) + .then(function(data) { + expect(storage.collection.length).to.equal(1); + var contact = storage.collection.get(1); + expect(contact.id).to.equal(1); + + contact.destroy() + .then(function(data) { + expect(storage.collection.length).to.equal(0); + var contact = storage.collection.get(1); + expect(contact).to.be.undefined; + done(); + }) + .catch(function(err) { done(err); }); + + server.respond(); + }) + .catch(function(err) { done(err); }); + + server.respond(); + }) }); }); \ No newline at end of file