From 8e870bd0c9dc939df03633a0a5a301c66bb38716 Mon Sep 17 00:00:00 2001 From: emaphp Date: Sat, 21 Mar 2015 20:51:32 -0300 Subject: [PATCH] fix: not sending the correct value to before:event handlers in Storage class --- dist/backbone.async.js | 35 +++----- dist/backbone.async.min.js | 2 +- test/specs/storage.js | 178 ++++++++++++++++++++++++++++++++++++- 3 files changed, 192 insertions(+), 23 deletions(-) diff --git a/dist/backbone.async.js b/dist/backbone.async.js index e6b1359..245b69c 100644 --- a/dist/backbone.async.js +++ b/dist/backbone.async.js @@ -135,39 +135,35 @@ if (!this.Collection) throw new Error('No Collection class defined'); + options = options || {}; var self = this; var mustTrigger = (typeof(options.silent) === 'undefined') || !options.silent; return new Promise(function(resolve, reject) { - self.collection = new self.Collection(); - if (mustTrigger) - self.trigger('before:fetch', self.collection, options); - self.collection.fetch(options || {}) + if (!self.collection) self.collection = new self.Collection(); + if (mustTrigger) self.trigger('before:fetch', { collection: self.collection, options: options}); + self.collection.fetch(options) .then(function(data) { - if (mustTrigger) - self.trigger('after:fetch', data, true); + if (mustTrigger) self.trigger('after:fetch', data, true); self.isLoaded = true; resolve(data); }) .catch(function(data) { - if (mustTrigger) - self.trigger('after:fetch', data, false); + if (mustTrigger) self.trigger('after:fetch', data, false); reject(data); }); }); }, get: function(id, options) { - if (this.isLoaded) { + if (this.collection) { var value = this.collection.get(id); var data = { model: value, options: options }; - if (!value) - return Promise.reject(data); - + if (!value) return Promise.reject(data); return Promise.resolve(data); } @@ -177,26 +173,23 @@ if (!this.Collection) throw new Error('No Collection class defined'); + options = options || {}; var self = this; var mustTrigger = (typeof(options.silent) === 'undefined') || !options.silent; return new Promise(function(resolve, reject) { var model = new self.Model(); model.set(self.Model.idAttribute || 'id', id); - if (mustTrigger) - self.trigger('before:get', model, options); - model.fetch(options || {}) + if (mustTrigger) self.trigger('before:get', { model: model, options: options }); + model.fetch(options) .then(function(data) { - if (mustTrigger) - self.trigger('after:get', data, true); - if (!self.collection) - self.collection = new self.Collection(); + if (mustTrigger) self.trigger('after:get', data, true); + if (!self.collection) self.collection = new self.Collection(); self.collection.push(model); resolve(data); }) .catch(function(data) { - if (mustTrigger) - self.trigger('after:get', data, false); + if (mustTrigger) self.trigger('after:get', data, false); reject(data); }); }); diff --git a/dist/backbone.async.min.js b/dist/backbone.async.min.js index 41bf070..66ddb2c 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");var b=this,c="undefined"==typeof a.silent||!a.silent;return new Promise(function(d,e){b.collection=new b.Collection,c&&b.trigger("before:fetch",b.collection,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.isLoaded){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");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",g,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),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 diff --git a/test/specs/storage.js b/test/specs/storage.js index f306859..2ecbb7f 100644 --- a/test/specs/storage.js +++ b/test/specs/storage.js @@ -7,7 +7,7 @@ describe("ASync.Storage tests", function() { server.restore(); }); - describe('Fetch test', function() { + describe('Fetch by ID tests', function() { it('must fetch by id', function(done) { var value = { id: 1, name: "Emmanuel", surname: "Antico"}; server.respondWith( @@ -25,6 +25,7 @@ describe("ASync.Storage tests", function() { .then(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.model.attributes).to.deep.equal(value); @@ -34,6 +35,8 @@ describe("ASync.Storage tests", function() { expect(model.attributes).to.deep.equal(value); expect(storage.isLoaded).to.be.false; + expect(data.response).to.deep.equal(value); + expect(data.options).to.be.a('object'); expect(data.options).to.have.property('test'); expect(data.options).to.have.property('silent'); @@ -48,6 +51,179 @@ describe("ASync.Storage tests", function() { server.respond(); }); + it('must invoke callbacks in order', function(done) { + var value = { id: 2, name: "Emmanuel", surname: "Antico"}; + server.respondWith( + 'GET', + '/contacts/2', + [ + 200, + {"Content-Type": "application/json"}, + JSON.stringify(value) + ] + ); + + var callback1 = sinon.spy(); + var callback2 = sinon.spy(); + + var storage = new FIXTURES.ContactsStorage(); + storage.get(2) + .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 value = { id: 3, name: "Emmanuel", surname: "Antico"}; + server.respondWith( + 'GET', + '/contacts/3', + [ + 200, + {"Content-Type": "application/json"}, + JSON.stringify(value) + ] + ); + + var storage = new FIXTURES.ContactsStorage(); + 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: 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('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.response).to.deep.equal(value); + expect(dataArg.model.attributes).to.be.deep.equal(value); + + 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; + + 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 value = { id: 4, name: "Emmanuel", surname: "Antico"}; + server.respondWith( + 'GET', + '/contacts/4', + [ + 200, + {"Content-Type": "application/json"}, + JSON.stringify(value) + ] + ); + + var storage = new FIXTURES.ContactsStorage(); + 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(4, {test: false, 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 value = { id: 5, name: "Emmanuel", surname: "Antico"}; + server.respondWith( + 'GET', + '/contacts/5', + [ + 200, + {"Content-Type": "application/json"}, + JSON.stringify(value) + ] + ); + + var storage = new FIXTURES.ContactsStorage(); + storage.get(5) + .then(function(data) { + 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(5, {test: true, silent: false}) + .then(function(data) { + expect(data).to.be.a('object'); + expect(data).to.have.property('model'); + 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(storage.collection.models.length).to.equal(1); + var model = storage.collection.get(5); + expect(model.attributes).to.deep.equal(value); + expect(storage.isLoaded).to.be.false; + + expect(data.options).to.be.a('object'); + 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 all tests', function() { it('must fetch collection', function(done) { var values = [ {id: 1, name: 'Curly'},