From 4ae46814ff4e7c0bbcdbbefc0a97277283a84065 Mon Sep 17 00:00:00 2001 From: Sylvester Keil Date: Tue, 26 Feb 2013 10:22:12 +0100 Subject: feat(http): support request/response promise chaining myApp.factory('myAroundInterceptor', function($rootScope, $timeout) { return function(configPromise, responsePromise) { return { request: configPromise.then(function(config) { return config }); response: responsePromise.then(function(response) { return 'ha!'; } }); } myApp.config(function($httpProvider){ $httpProvider.aroundInterceptors.push('myAroundInterceptor'); }); --- test/ng/httpSpec.js | 288 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 281 insertions(+), 7 deletions(-) (limited to 'test/ng/httpSpec.js') diff --git a/test/ng/httpSpec.js b/test/ng/httpSpec.js index 2dd14192..e6d1cf4f 100644 --- a/test/ng/httpSpec.js +++ b/test/ng/httpSpec.js @@ -12,7 +12,7 @@ describe('$http', function() { $exceptionHandlerProvider.mode('log'); })); - afterEach(inject(function($exceptionHandler, $httpBackend) { + afterEach(inject(function($exceptionHandler, $httpBackend, $rootScope) { forEach($exceptionHandler.errors, function(e) { dump('Unhandled exception: ', e) }); @@ -21,13 +21,150 @@ describe('$http', function() { throw 'Unhandled exceptions trapped in $exceptionHandler!'; } + $rootScope.$digest(); $httpBackend.verifyNoOutstandingExpectation(); })); describe('$httpProvider', function() { - describe('interceptors', function() { + it('should accept injected rejected response interceptor', function() { + var wasCalled = false; + module(function($httpProvider, $provide) { + $httpProvider.responseInterceptors.push('injectedInterceptor'); + $provide.factory('injectedInterceptor', ['$q', function($q) { + return function(promise) { + return promise.then(null, function authInterceptor(response) { + wasCalled = true; + expect(response.status).toEqual(401); + return $q.reject(response); + }); + }; + }]); + }); + inject(function($http, $httpBackend) { + $httpBackend.expect('GET', '/url').respond(401); + $http({method: 'GET', url: '/url'}); + $httpBackend.flush(); + expect(wasCalled).toEqual(true); + }); + }); + + + it('should chain request, requestReject, response and responseReject interceptors', function() { + module(function($httpProvider) { + var savedConfig, savedResponse; + $httpProvider.interceptors.push(function($q) { + return { + request: function(config) { + config.url += '/1'; + savedConfig = config; + return $q.reject('/2'); + } + }; + }); + $httpProvider.interceptors.push(function($q) { + return { + requestError: function(error) { + savedConfig.url += error; + return $q.when(savedConfig); + } + }; + }); + $httpProvider.interceptors.push(function() { + return { + responseError: function(rejection) { + savedResponse.data += rejection; + return savedResponse; + } + }; + }); + $httpProvider.interceptors.push(function($q) { + return { + response: function(response) { + response.data += ':1'; + savedResponse = response + return $q.reject(':2'); + } + }; + }); + }); + inject(function($http, $httpBackend, $rootScope) { + var response; + $httpBackend.expect('GET', '/url/1/2').respond('response'); + $http({method: 'GET', url: '/url'}).then(function(r) { + response = r; + }); + $rootScope.$apply(); + $httpBackend.flush(); + expect(response.data).toEqual('response:1:2'); + }); + }); + + + it('should verify order of execution', function() { + module(function($httpProvider) { + $httpProvider.interceptors.push(function($q) { + return { + request: function(config) { + config.url += '/outer'; + return config; + }, + response: function(response) { + response.data = '{' + response.data + '} outer'; + return response; + } + }; + }); + $httpProvider.interceptors.push(function($q) { + return { + request: function(config) { + config.url += '/inner'; + return config; + }, + response: function(response) { + response.data = '{' + response.data + '} inner'; + return response; + } + }; + }); + $httpProvider.responseInterceptors.push(function($q) { + return function(promise) { + var defer = $q.defer(); + + promise.then(function(response) { + response.data = '[' + response.data + '] legacy-1'; + defer.resolve(response); + }); + return defer.promise; + }; + }); + $httpProvider.responseInterceptors.push(function($q) { + return function(promise) { + var defer = $q.defer(); + + promise.then(function(response) { + response.data = '[' + response.data + '] legacy-2'; + defer.resolve(response); + }); + return defer.promise; + }; + }); + }); + inject(function($http, $httpBackend) { + var response; + $httpBackend.expect('GET', '/url/outer/inner').respond('response'); + $http({method: 'GET', url: '/url'}).then(function(r) { + response = r; + }); + $httpBackend.flush(); + expect(response.data).toEqual('{{[[response] legacy-1] legacy-2} inner} outer'); + }); + }); + }); + + + describe('response interceptors', function() { it('should default to an empty array', module(function($httpProvider) { expect($httpProvider.responseInterceptors).toEqual([]); @@ -44,7 +181,7 @@ describe('$http', function() { data: response.data + '?', status: 209, headers: response.headers, - config: response.config + request: response.config }); return deferred.promise; }); @@ -100,6 +237,136 @@ describe('$http', function() { }); }); }); + + + describe('request interceptors', function() { + it('should pass request config as a promise', function() { + var run = false; + module(function($httpProvider) { + $httpProvider.interceptors.push(function() { + return { + request: function(config) { + expect(config.url).toEqual('/url'); + expect(config.data).toEqual({one: "two"}); + expect(config.headers.foo).toEqual('bar'); + run = true; + return config; + } + }; + }); + }); + inject(function($http, $httpBackend, $rootScope) { + $httpBackend.expect('POST', '/url').respond(''); + $http({method: 'POST', url: '/url', data: {one: 'two'}, headers: {foo: 'bar'}}); + $rootScope.$apply(); + expect(run).toEqual(true); + }); + }); + + it('should allow manipulation of request', function() { + module(function($httpProvider) { + $httpProvider.interceptors.push(function() { + return { + request: function(config) { + config.url = '/intercepted'; + config.headers.foo = 'intercepted'; + return config; + } + }; + }); + }); + inject(function($http, $httpBackend, $rootScope) { + $httpBackend.expect('GET', '/intercepted', null, function (headers) { + return headers.foo === 'intercepted'; + }).respond(''); + $http.get('/url'); + $rootScope.$apply(); + }); + }); + + it('should reject the http promise if an interceptor fails', function() { + var reason = new Error('interceptor failed'); + module(function($httpProvider) { + $httpProvider.interceptors.push(function($q) { + return { + request: function(promise) { + return $q.reject(reason); + } + }; + }); + }); + inject(function($http, $httpBackend, $rootScope) { + var success = jasmine.createSpy(), error = jasmine.createSpy(); + $http.get('/url').then(success, error); + $rootScope.$apply(); + expect(success).not.toHaveBeenCalled(); + expect(error).toHaveBeenCalledWith(reason); + }); + }); + + it('should not manipulate the passed-in config', function() { + module(function($httpProvider) { + $httpProvider.interceptors.push(function() { + return { + request: function(config) { + config.url = '/intercepted'; + config.headers.foo = 'intercepted'; + return config; + } + }; + }); + }); + inject(function($http, $httpBackend, $rootScope) { + var config = { method: 'get', url: '/url', headers: { foo: 'bar'} }; + $httpBackend.expect('GET', '/intercepted').respond(''); + $http.get('/url'); + $rootScope.$apply(); + expect(config.method).toEqual('get'); + expect(config.url).toEqual('/url'); + expect(config.headers.foo).toEqual('bar') + }); + }); + + it('should support interceptors defined as services', function() { + module(function($provide, $httpProvider) { + $provide.factory('myInterceptor', function() { + return { + request: function(config) { + config.url = '/intercepted'; + return config; + } + }; + }); + $httpProvider.interceptors.push('myInterceptor'); + }); + inject(function($http, $httpBackend, $rootScope) { + $httpBackend.expect('POST', '/intercepted').respond(''); + $http.post('/url'); + $rootScope.$apply(); + }); + }); + + it('should support complex interceptors based on promises', function() { + module(function($provide, $httpProvider) { + $provide.factory('myInterceptor', function($q, $rootScope) { + return { + request: function(config) { + return $q.when('/intercepted').then(function(intercepted) { + config.url = intercepted; + return config; + }); + } + }; + }); + $httpProvider.interceptors.push('myInterceptor'); + }); + inject(function($http, $httpBackend, $rootScope) { + $httpBackend.expect('POST', '/intercepted').respond(''); + $http.post('/two'); + $rootScope.$apply(); + }); + }); + }); }); @@ -938,7 +1205,7 @@ describe('$http', function() { $http({method: 'GET', url: '/url'}); // Notice no cache given in config. $httpBackend.flush(); - // Second should be served from cache, without sending request to server. + // Second should be served from cache, without sending request to server. $http({method: 'get', url: '/url'}).success(callback); $rootScope.$digest(); @@ -1004,6 +1271,7 @@ describe('$http', function() { expect($http.pendingRequests.length).toBe(0); $http({method: 'get', url: '/some'}); + $rootScope.$digest(); expect($http.pendingRequests.length).toBe(1); $httpBackend.flush(); @@ -1016,13 +1284,16 @@ describe('$http', function() { $http({method: 'get', url: '/cached', cache: true}); $http({method: 'get', url: '/cached', cache: true}); + $rootScope.$digest(); expect($http.pendingRequests.length).toBe(2); $httpBackend.flush(); expect($http.pendingRequests.length).toBe(0); $http({method: 'get', url: '/cached', cache: true}); - expect($http.pendingRequests.length).toBe(1); + spyOn($http.pendingRequests, 'push').andCallThrough(); + $rootScope.$digest(); + expect($http.pendingRequests.push).toHaveBeenCalledOnce(); $rootScope.$apply(); expect($http.pendingRequests.length).toBe(0); @@ -1035,6 +1306,7 @@ describe('$http', function() { expect($http.pendingRequests.length).toBe(0); }); + $rootScope.$digest(); expect($http.pendingRequests.length).toBe(1); $httpBackend.flush(); }); @@ -1071,10 +1343,11 @@ describe('$http', function() { $provide.value('$httpBackend', $httpBackend); }); - inject(function($http) { + inject(function($http, $rootScope) { $http({ method: 'GET', url: 'some.html', timeout: 12345, withCredentials: true, responseType: 'json' }); + $rootScope.$digest(); expect($httpBackend).toHaveBeenCalledOnce(); }); @@ -1093,11 +1366,12 @@ describe('$http', function() { $provide.value('$httpBackend', $httpBackend); }); - inject(function($http) { + inject(function($http, $rootScope) { $http.defaults.withCredentials = true; $http({ method: 'GET', url: 'some.html', timeout: 12345, responseType: 'json' }); + $rootScope.$digest(); expect($httpBackend).toHaveBeenCalledOnce(); }); -- cgit v1.2.3