diff --git a/component.json b/component.json index b53ae85..fae6229 100644 --- a/component.json +++ b/component.json @@ -6,7 +6,8 @@ "keywords": [], "dependencies": { "visionmedia/superagent": "*", - "component/emitter": "*" + "component/emitter": "*", + "chaijs/chai": "*" }, "development": { "visionmedia/debug": "0.7.4", @@ -17,4 +18,4 @@ "scripts": [ "index.js" ] -} +} \ No newline at end of file diff --git a/index.js b/index.js index 61af5e3..caa2723 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,7 @@ angular.module('ngSuperagent', ['ng']) var agent = require('superagent'); var Emitter = require('emitter'); - var $q, $timeout, $log; + var $q, $timeout, $log, $scope; Emitter(agent); var requestProvider = {}; @@ -17,10 +17,11 @@ angular.module('ngSuperagent', ['ng']) requestProvider.transforms = []; requestProvider.resolvers = []; - requestProvider.$get = function getService(_$q_, _$timeout_, _$log_) { + requestProvider.$get = function getService(_$q_, _$timeout_, _$log_, _$rootScope_) { $q = _$q_; $timeout = _$timeout_; $log = _$log_; + $scope = _$rootScope_; requestProvider.transform('set', requestProvider.defaults.headers); requestProvider.transform('withCredentials', null, requestProvider.defaults.credentials); @@ -28,8 +29,7 @@ angular.module('ngSuperagent', ['ng']) var methods = ['get', 'head', 'del', 'put', 'post', 'patch']; methods.forEach(function(m) { - if(agent['_'+m]) return; - agent['_'+m] = agent[m]; + agent['_'+m] = agent['_'+m] || agent[m]; agent[m] = function() { var timer; // Prepend defaults.baseUrl to all requests: @@ -56,18 +56,47 @@ angular.module('ngSuperagent', ['ng']) } // Make events available in provider - request.on('request', providerEmit(request, 'request')); - request.on('error', providerEmit(request, 'error')); - request.on('abort', providerEmit(request, 'abort')); - request.on('end', providerEmit(request, 'end')); + request.on('request', providerEmit('request')); + request.on('error', providerEmit('error')); + request.on('abort', providerEmit('abort')); + request.on('end', providerEmit('end')); return request; }; + // Add promise method to requests + agent.Request.prototype.promise = function() { + var deferred = $q.defer(); + var methods = { + reject: $q.reject, + resolve: $q.when + }; + + this.end(function(err, res) { + if(err) { + agent.emit('error', err); + err = $q.reject(err); + } + + var resolution = requestProvider.resolvers.reduce(function(promise, resolvers) { + var success = resolvers[0] ? resolvers[0].bind(methods) : null; + var error = resolvers[1] ? resolvers[1].bind(methods) : null; + return promise.then(success, error); + }, err || $q.when(res)); + + // + agent.emit('end', this); + deferred.resolve(resolution); + $scope.$apply(); + }); + + + return deferred.promise; + }; }); return agent; }; - function providerEmit(ctx, type) { + function providerEmit(type) { return function() { var args = Array.prototype.slice.call(arguments); Array.prototype.unshift.call(args, type); @@ -78,7 +107,6 @@ angular.module('ngSuperagent', ['ng']) requestProvider.transforms.forEach(function(transformer) { if(typeof transformer.condition !== 'undefined' && !transformer.condition) return; - req[transformer.fn].call(req, transformer.params); }); } @@ -96,34 +124,6 @@ angular.module('ngSuperagent', ['ng']) requestProvider.resolvers.push([success, error]); }; - // Add promise method to requests - agent.Request.prototype.promise = function() { - var deferred = $q.defer(); - var methods = { - reject: $q.reject, - resolve: $q.when - }; - - this.end(function(err, res) { - var resolution = requestProvider.resolvers.reduce(function(promise, resolvers) { - var success = resolvers[0] ? resolvers[0].bind(methods) : null; - var error = resolvers[1] ? resolvers[1].bind(methods) : null; - return promise.then(success, error); - }, $q.when([err, res])); - - return deferred.resolve(resolution); - }); - - return deferred.promise; - }; - // Register default resolution. Only superagent errors are rejected. - requestProvider.addResolver(function(args) { - var err = args[0]; - var res = args[1]; - if(err) return this.reject(err); - this.resolve(res); - }); - return requestProvider; }); diff --git a/test/index.html b/test/index.html index 05e6cd3..a93cd78 100644 --- a/test/index.html +++ b/test/index.html @@ -31,8 +31,6 @@ // require('gravy')(mocha.run()); } mocha.run(); - - // angular.module('ngSuperagent').config(function(RequestProvider) { console.log(RequestProvider); }).run(function(Request) { console.log(Request); }) diff --git a/test/server.js b/test/server.js index 98e84a6..2dab85c 100644 --- a/test/server.js +++ b/test/server.js @@ -21,15 +21,20 @@ var app = express() .get('/', function (req, res, next) { res.render('index.html'); }) + .get('/error', function(req, res) { + res.send(500); + }) + .get('/test/error', function(req, res) { + res.json({status: 'error'}); + }) + .all('/test/*', function(req, res) { + res.json({status: 'ok'}); + }) .listen(4334, function () { fs.writeFileSync(__dirname + '/pid.txt', process.pid, 'utf-8'); console.log('Started testing server on port 4334...'); }); -/** - * Send - */ - function send(req, res, next){ var test = 0 == req.url.indexOf('/test/integrations'); var slug = req.url.split('/').pop().slice(0, -3); diff --git a/test/spec/agent.js b/test/spec/agent.js index f3c2f63..24e151c 100644 --- a/test/spec/agent.js +++ b/test/spec/agent.js @@ -1,110 +1,114 @@ require('angular-superagent'); - -var assert = require('assert'); +var chai = require('chai'); +var expect = chai.expect; +var assert = chai.assert; describe('Request', function() { var RequestProvider; + // Initialize module beforeEach(module('ngSuperagent')); - beforeEach(module(function(_RequestProvider_) { - RequestProvider = _RequestProvider_; - })); - beforeEach(inject(function() {})); - var server; var methods = ['get', 'head', 'del', 'put', 'post', 'patch']; - before(function () { - server = sinon.fakeServer.create(); - server.fakeHTTPMethods = true; - }); - after(function () { server.restore(); }); - - beforeEach(function() { - methods.forEach(function(m) { - respond(m.toUpperCase(), '/api/v2', 200, {status: 'ok'}); - }); - function respond(method, path, status, body) { - server.respondWith(method, path, JSON.stringify(body)); - } - }); - - - it('should expose RequestProvider', function() { - assert(RequestProvider); - }); - it('should expose Request service', function() { - inject(function(Request) { - assert(Request !== null); - }); - }); describe('when unconfigured', function() { + // Module config step + beforeEach(angular.mock.module(function(_RequestProvider_) { + RequestProvider = _RequestProvider_; + })); + // Module run step + beforeEach(angular.mock.inject(function(_Request_) { + Request = _Request_; + })); + it('has correct defaults', function() { - assert(RequestProvider.defaults.baseUrl === ''); + assert(!RequestProvider.defaults.baseUrl); + }); + it('should expose RequestProvider', function() { + assert(RequestProvider); + }); + it('should expose Request service', function() { + assert(Request !== null); }); }); describe('when given a baseUrl', function() { - beforeEach(function() { - RequestProvider.defaults.baseUrl = '/api/v2'; - }); - beforeEach(inject(function(_Request_) { + // Module config step + beforeEach(angular.mock.module(function(RequestProvider) { + RequestProvider.defaults.baseUrl = '/test'; + })); + // Module run step + beforeEach(angular.mock.inject(function(_Request_) { Request = _Request_; })); - methods.forEach(function(m) { - it('#'+m + ' is redirected to the api', function() { + it('all methods redirected to the api', function(done) { + methods.forEach(function(m) { var cb = sinon.spy(); - Request[m]('').end(cb); - server.respond(); - sinon.assert.calledOnce(cb); - sinon.assert.calledWithMatch(cb, null, {body: {status: 'ok'}}); + Request[m]('/random').end(function(err, res) { + assert(!err); + expect(res.status).to.eql(200); + done(); + }); }); }); }); - // describe('#promise', function() { - // var cb; - // function validate() { - // server.respond(); - // sinon.assert.calledOnce(cb); - // } - // beforeEach(function() { - // cb = sinon.spy(); - // }); - - // it('is a $q promise', function() { - // var p = Request.get('/ping').promise(); - // expect(p.then).to.be.a('function'); - // expect(p.catch).to.be.a('function'); - // expect(p.finally).to.be.a('function'); + describe('#promise', function() { + var cb; - // p.finally(cb); - // validate(); - // }); - // it('is resolved on HTTP 200 ok', function() { - // Request.get('/ping').promise().then(cb); - // validate(); - // }); - // it('is rejected on HTTP 404', function() { - // Request.get('/notfound').promise().catch(cb); - // validate(); - // }); - // it('is rejected on body.status !== "ok"', function() { - // Request.get('/error').promise().catch(cb); - // validate(); - // }); - // it('resolves promise with body.data', function() { - // Request.get('/data').promise().then(cb); - // // function(res) { - // // // console.log(res.success); - // // }); - // // server.respond(); - // validate(); - // cb.should.have.been.calledWithMatch(testData); - // }); - // }); + it('is a $q promise', function() { + var p = Request.get('/ping').promise(); + expect(p.then).to.be.a('function'); + expect(p.catch).to.be.a('function'); + expect(p.finally).to.be.a('function'); + }); + describe('#addResolve', function() { + // Module config step + beforeEach(angular.mock.module(function(RequestProvider) { + RequestProvider.addResolver(function(res) { + if(res.error || !res.body) return this.reject(res); + if(res.body.status === 'ok') return this.resolve(res); + return this.reject(res); + }); + })); + // Module run step + beforeEach(angular.mock.inject(function(_Request_) { + Request = _Request_; + })); + it('is resolved on HTTP 200 ok', function(done) { + Request.get('/test/ping').promise().then(function(res) { + expect(res.status).to.eql(200); + done(); + }); + }); + it('is rejected on HTTP 404', function(done) { + Request.get('/notfound').promise().catch(function(res) { + expect(res.status).to.eql(404); + done(); + }); + }); + it('is rejected on HTTP 500', function(done) { + Request.get('/error').promise().catch(function() { + done(); + }); + }); + it('is rejected on body.status !== "ok"', function(done) { + Request.get('/test/error').promise().catch(function(res) { + expect(res.body).not.to.eql({status: 'ok'}); + done(); + }); + }); + it('is rejected on CORS error', function(done) { + Request.get('http://mojn.com').promise().then(function() { + done(new Error('Not rejected')); + }, function() { + done(); + }); + }); + }); + }); });