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