mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
feat($http): add promise support
quite messy, some tests are missing, contains an experimental jasmine DI support)
This commit is contained in:
parent
bf8e0540f8
commit
dbd880cc0a
7 changed files with 234 additions and 422 deletions
|
|
@ -135,7 +135,7 @@
|
|||
var oldIt = window.it;
|
||||
window.it = function(name, fn){
|
||||
fn.exclusive = 1; // run anything under ddescribe
|
||||
jasmine.getEnv().it(name, fn);
|
||||
oldIt(name, fn);
|
||||
};
|
||||
try {
|
||||
fn.call(this);
|
||||
|
|
|
|||
|
|
@ -111,7 +111,8 @@ ResourceFactory.prototype = {
|
|||
method: action.method,
|
||||
url: route.url(extend({}, extractParams(data), action.params || {}, params)),
|
||||
data: data
|
||||
}).on('success', function(response, status) {
|
||||
}).then(function(response) {
|
||||
response = response.data;
|
||||
if (response) {
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
|
|
@ -123,9 +124,7 @@ ResourceFactory.prototype = {
|
|||
}
|
||||
}
|
||||
(success||noop)(value);
|
||||
});
|
||||
|
||||
if (error) future.on('error', error);
|
||||
}, error);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -48,18 +48,6 @@ function transform(data, fns, param) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name angular.module.ng.$http
|
||||
* @requires $httpBacked
|
||||
* @requires $browser
|
||||
* @requires $exceptionHandler
|
||||
* @requires $cacheFactory
|
||||
*
|
||||
* @property {Array.<XhrFuture>} pendingRequests Array of pending requests.
|
||||
*
|
||||
* @description
|
||||
*/
|
||||
function $HttpProvider() {
|
||||
var JSON_START = /^\s*(\[|\{[^\{])/,
|
||||
JSON_END = /[\}\]]\s*$/,
|
||||
|
|
@ -93,14 +81,75 @@ function $HttpProvider() {
|
|||
}
|
||||
};
|
||||
|
||||
this.$get = ['$httpBackend', '$browser', '$exceptionHandler', '$cacheFactory', '$rootScope',
|
||||
function($httpBackend, $browser, $exceptionHandler, $cacheFactory, $rootScope) {
|
||||
this.$get = ['$httpBackend', '$browser', '$exceptionHandler', '$cacheFactory', '$rootScope', '$q',
|
||||
function($httpBackend, $browser, $exceptionHandler, $cacheFactory, $rootScope, $q) {
|
||||
|
||||
var defaultCache = $cacheFactory('$http');
|
||||
|
||||
// the actual service
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.module.ng.$http
|
||||
* @requires $httpBacked
|
||||
* @requires $browser
|
||||
* @requires $exceptionHandler
|
||||
* @requires $cacheFactory
|
||||
*
|
||||
* @param {object} config Object describing the request to be made and how it should be processed.
|
||||
* The object has following properties:
|
||||
*
|
||||
* - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
|
||||
* - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
|
||||
* - **data** – `{string|Object}` – Data to be sent as the request message data.
|
||||
* - **headers** – `{Object}` – Map of strings representing HTTP headers to send to the server.
|
||||
* - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
|
||||
* GET request, otherwise if a cache instance built with $cacheFactory, this cache will be
|
||||
* used for caching.
|
||||
*
|
||||
* @returns {HttpPromise} Returns a promise object with the standard `then` method and two http
|
||||
* specific methods: `success` and `error`. The `then` method takes two arguments a success and
|
||||
* an error callback which will be called with a response object. The `success` and `error`
|
||||
* methods take a single argument - a function that will be called when the request succeeds or
|
||||
* fails respectively. The arguments passed into these functions are destructured representation
|
||||
* of the response object passed into the `then` method. The response object has these
|
||||
* properties:
|
||||
*
|
||||
* - **data** – `{string|Object}` – The response body transformed with the transform functions.
|
||||
* - **status** – `{number}` – HTTP status code of the response.
|
||||
* - **headers** – `{function([headerName])}` – Header getter function.
|
||||
* - **config** – `{Object}` – The configuration object that was used to generate the request.
|
||||
*
|
||||
* @property {Array.<Object>} pendingRequests Array of config objects for pending requests.
|
||||
* This is primarily meant to be used for debugging purposes.
|
||||
*
|
||||
* @description
|
||||
* $http is a service through which XHR and JSONP requests can be made.
|
||||
*/
|
||||
function $http(config) {
|
||||
return new XhrFuture().send(config);
|
||||
var req = new XhrFuture().send(config),
|
||||
deferredResp = $q.defer(),
|
||||
promise = deferredResp.promise;
|
||||
|
||||
promise.success = function(fn) {
|
||||
promise.then(function(response) {
|
||||
fn(response.data, response.status, response.headers, config);
|
||||
});
|
||||
return promise;
|
||||
};
|
||||
|
||||
promise.error = function(fn) {
|
||||
promise.then(null, function(response) {
|
||||
fn(response.data, response.status, response.headers, config);
|
||||
});
|
||||
return promise;
|
||||
};
|
||||
|
||||
req.on('success', function(data, status, headers) {
|
||||
deferredResp.resolve({data: data, status: status, headers: headers, config: config});
|
||||
}).on('error', function(data, status, headers) {
|
||||
deferredResp.reject({data: data, status: status, headers: headers, config: config});
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
$http.pendingRequests = [];
|
||||
|
|
@ -115,7 +164,7 @@ function $HttpProvider() {
|
|||
*
|
||||
* @param {string} url Relative or absolute URL specifying the destination of the request
|
||||
* @param {Object=} config Optional configuration object
|
||||
* @returns {XhrFuture} Future object
|
||||
* @returns {HttpPromise} Future object
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -128,7 +177,7 @@ function $HttpProvider() {
|
|||
*
|
||||
* @param {string} url Relative or absolute URL specifying the destination of the request
|
||||
* @param {Object=} config Optional configuration object
|
||||
* @returns {XhrFuture} Future object
|
||||
* @returns {HttpPromise} Future object
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -154,7 +203,7 @@ function $HttpProvider() {
|
|||
*
|
||||
* @param {string} url Relative or absolute URL specifying the destination of the request
|
||||
* @param {Object=} config Optional configuration object
|
||||
* @returns {XhrFuture} Future object
|
||||
* @returns {HttpPromise} Future object
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -183,7 +232,7 @@ function $HttpProvider() {
|
|||
* @param {string} url Relative or absolute URL specifying the destination of the request
|
||||
* @param {*} data Request content
|
||||
* @param {Object=} config Optional configuration object
|
||||
* @returns {XhrFuture} Future object
|
||||
* @returns {HttpPromise} Future object
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -260,7 +309,9 @@ function $HttpProvider() {
|
|||
}
|
||||
|
||||
fireCallbacks(response, status);
|
||||
rawRequest = null;
|
||||
// TODO(i): we can't null the rawRequest because we might need to be able to call
|
||||
// rawRequest.getAllResponseHeaders from a promise
|
||||
// rawRequest = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -284,7 +335,7 @@ function $HttpProvider() {
|
|||
response = transform(response, cfg.transformResponse || $config.transformResponse, rawRequest);
|
||||
|
||||
var idx; // remove from pending requests
|
||||
if ((idx = indexOf($http.pendingRequests, self)) !== -1)
|
||||
if ((idx = indexOf($http.pendingRequests, cfg)) !== -1)
|
||||
$http.pendingRequests.splice(idx, 1);
|
||||
|
||||
// normalize internal statuses to 0
|
||||
|
|
@ -331,7 +382,7 @@ function $HttpProvider() {
|
|||
* Retry the request
|
||||
*
|
||||
* @param {Object=} config Optional config object to extend the original configuration
|
||||
* @returns {XhrFuture}
|
||||
* @returns {HttpPromise}
|
||||
*/
|
||||
this.retry = function(config) {
|
||||
if (rawRequest) throw 'Can not retry request. Abort pending request first.';
|
||||
|
|
@ -377,8 +428,7 @@ function $HttpProvider() {
|
|||
rawRequest = $httpBackend(cfg.method, cfg.url, data, done, headers, cfg.timeout);
|
||||
}
|
||||
|
||||
$rootScope.$broadcast('$http.request', self);
|
||||
$http.pendingRequests.push(self);
|
||||
$http.pendingRequests.push(cfg);
|
||||
return self;
|
||||
};
|
||||
|
||||
|
|
@ -416,7 +466,7 @@ function $HttpProvider() {
|
|||
* .on('abort', function(){});
|
||||
*
|
||||
* @param {string} pattern Status code pattern with "x" for any number
|
||||
* @param {function(*, number, Object)} callback Function to be called when response arrives
|
||||
* @param {function(*, number, function)} callback Function to be called when response arrives
|
||||
* @returns {XhrFuture}
|
||||
*/
|
||||
this.on = function(pattern, callback) {
|
||||
|
|
|
|||
|
|
@ -109,8 +109,7 @@ angularWidget('ng:include', function(element){
|
|||
});
|
||||
this.$watch(function() {return changeCounter;}, function(scope) {
|
||||
var src = scope.$eval(srcExp),
|
||||
useScope = scope.$eval(scopeExp),
|
||||
fromCache;
|
||||
useScope = scope.$eval(scopeExp);
|
||||
|
||||
function clearContent() {
|
||||
childScope = null;
|
||||
|
|
@ -121,7 +120,7 @@ angularWidget('ng:include', function(element){
|
|||
releaseScopes.pop().$destroy();
|
||||
}
|
||||
if (src) {
|
||||
$http.get(src, {cache: $templateCache}).on('success', function(response) {
|
||||
$http.get(src, {cache: $templateCache}).success(function(response) {
|
||||
element.html(response);
|
||||
if (useScope) {
|
||||
childScope = useScope;
|
||||
|
|
@ -131,7 +130,7 @@ angularWidget('ng:include', function(element){
|
|||
compiler.compile(element)(childScope);
|
||||
$autoScroll();
|
||||
scope.$eval(onloadExp);
|
||||
}).on('error', clearContent);
|
||||
}).error(clearContent);
|
||||
} else {
|
||||
clearContent();
|
||||
}
|
||||
|
|
@ -572,23 +571,21 @@ angularWidget('ng:view', function(element) {
|
|||
});
|
||||
|
||||
this.$watch(function() {return changeCounter;}, function(scope, newChangeCounter) {
|
||||
var template = $route.current && $route.current.template,
|
||||
fromCache;
|
||||
var template = $route.current && $route.current.template;
|
||||
|
||||
function clearContent() {
|
||||
element.html('');
|
||||
}
|
||||
|
||||
if (template) {
|
||||
// xhr's callback must be async, see commit history for more info
|
||||
$http.get(template, {cache: $templateCache}).on('success', function(response) {
|
||||
$http.get(template, {cache: $templateCache}).success(function(response) {
|
||||
// ignore callback if another route change occured since
|
||||
if (newChangeCounter == changeCounter) {
|
||||
element.html(response);
|
||||
compiler.compile(element)($route.current.scope);
|
||||
$autoScroll();
|
||||
}
|
||||
}).on('error', clearContent);
|
||||
}).error(clearContent);
|
||||
} else {
|
||||
clearContent();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,13 +261,14 @@ describe("resource", function() {
|
|||
errorCB;
|
||||
|
||||
beforeEach(function() {
|
||||
errorCB = jasmine.createSpy('error').andCallFake(function(response, status) {
|
||||
expect(response).toBe(ERROR_RESPONSE);
|
||||
expect(status).toBe(ERROR_CODE);
|
||||
errorCB = jasmine.createSpy('error').andCallFake(function(response) {
|
||||
expect(response.data).toBe(ERROR_RESPONSE);
|
||||
expect(response.status).toBe(ERROR_CODE);
|
||||
});
|
||||
});
|
||||
|
||||
it('should call the error callback if provided on non 2xx response', inject(function($httpBackend) {
|
||||
it('should call the error callback if provided on non 2xx response',
|
||||
inject(function($httpBackend, $rootScope) {
|
||||
$httpBackend.expect('GET', '/CreditCard/123').respond(ERROR_CODE, ERROR_RESPONSE);
|
||||
|
||||
CreditCard.get({id:123}, callback, errorCB);
|
||||
|
|
|
|||
|
|
@ -1,26 +1,34 @@
|
|||
'use strict';
|
||||
|
||||
// TODO(vojta): refactor these tests to use new inject() syntax
|
||||
describe('$http', function() {
|
||||
var $rootScope, $http, $httpBackend, callback;
|
||||
|
||||
var $http, $browser, $exceptionHandler, $httpBackend,
|
||||
scope, callback, future, callback;
|
||||
beforeEach(inject(
|
||||
function($exceptionHandlerProvider) {
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
},
|
||||
['$rootScope', '$http', '$httpBackend', function($rs, $h, $hb) {
|
||||
$rootScope = $rs;
|
||||
$http = $h;
|
||||
$httpBackend = $hb;
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
$injector.get('$exceptionHandlerProvider').mode('log');
|
||||
scope = $injector.get('$rootScope');
|
||||
$http = $injector.get('$http');
|
||||
$browser = $injector.get('$browser');
|
||||
$httpBackend = $injector.get('$httpBackend');
|
||||
$exceptionHandler = $injector.get('$exceptionHandler');
|
||||
spyOn(scope, '$apply');
|
||||
callback = jasmine.createSpy('callback');
|
||||
}));
|
||||
spyOn($rootScope, '$apply').andCallThrough();
|
||||
callback = jasmine.createSpy('done');
|
||||
}]
|
||||
));
|
||||
|
||||
|
||||
afterEach(inject(function($exceptionHandler, $httpBackend) {
|
||||
forEach($exceptionHandler.errors, function(e) {
|
||||
dump('Unhandled exception: ', e)
|
||||
});
|
||||
|
||||
if ($exceptionHandler.errors.length) {
|
||||
throw 'Unhandled exceptions trapped in $exceptionHandler!';
|
||||
}
|
||||
|
||||
afterEach(function() {
|
||||
if ($exceptionHandler.errors.length) throw $exceptionHandler.errors;
|
||||
$httpBackend.verifyNoOutstandingExpectation();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should do basic request', function() {
|
||||
|
|
@ -40,54 +48,81 @@ describe('$http', function() {
|
|||
|
||||
describe('callbacks', function() {
|
||||
|
||||
function throwing(name) {
|
||||
return function() {
|
||||
throw name;
|
||||
};
|
||||
}
|
||||
it('should pass in the response object when a request is successful', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(207, 'my content', {'content-encoding': 'smurf'});
|
||||
$http({url: '/url', method: 'GET'}).then(function(response) {
|
||||
expect(response.data).toBe('my content');
|
||||
expect(response.status).toBe(207);
|
||||
expect(response.headers()).toEqual({'content-encoding': 'smurf'});
|
||||
expect(response.config.url).toBe('/url');
|
||||
callback();
|
||||
});
|
||||
|
||||
it('should log exceptions', function() {
|
||||
$httpBackend.expect('GET', '/url1').respond(200, 'content');
|
||||
$httpBackend.expect('GET', '/url2').respond(400, '');
|
||||
|
||||
$http({url: '/url1', method: 'GET'}).on('200', throwing('exception in success callback'));
|
||||
$http({url: '/url2', method: 'GET'}).on('400', throwing('exception in error callback'));
|
||||
$httpBackend.flush();
|
||||
|
||||
expect($exceptionHandler.errors.shift()).toContain('exception in success callback');
|
||||
expect($exceptionHandler.errors.shift()).toContain('exception in error callback');
|
||||
});
|
||||
|
||||
|
||||
it('should log more exceptions', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(500, '');
|
||||
$http({url: '/url', method: 'GET'}).
|
||||
on('500', throwing('exception in error callback')).
|
||||
on('5xx', throwing('exception in error callback'));
|
||||
$httpBackend.flush();
|
||||
|
||||
expect($exceptionHandler.errors.length).toBe(2);
|
||||
$exceptionHandler.errors = [];
|
||||
});
|
||||
|
||||
|
||||
it('should get response as first param', function() {
|
||||
$httpBackend.expect('GET', '/url').respond('some-content');
|
||||
$http({url: '/url', method: 'GET'}).on('200', callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
expect(callback.mostRecentCall.args[0]).toBe('some-content');
|
||||
});
|
||||
|
||||
|
||||
it('should get status code as second param', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(250, 'some-content');
|
||||
$http({url: '/url', method: 'GET'}).on('2xx', callback);
|
||||
$httpBackend.flush();
|
||||
it('should pass in the response object when a request failed', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(543, 'bad error', {'request-id': '123'});
|
||||
$http({url: '/url', method: 'GET'}).then(null, function(response) {
|
||||
expect(response.data).toBe('bad error');
|
||||
expect(response.status).toBe(543);
|
||||
expect(response.headers()).toEqual({'request-id': '123'});
|
||||
expect(response.config.url).toBe('/url');
|
||||
callback();
|
||||
});
|
||||
|
||||
$httpBackend.flush();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
expect(callback.mostRecentCall.args[1]).toBe(250);
|
||||
});
|
||||
|
||||
|
||||
describe('success', function() {
|
||||
it('should allow http specific callbacks to be registered via "success"', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(207, 'my content', {'content-encoding': 'smurf'});
|
||||
$http({url: '/url', method: 'GET'}).success(function(data, status, headers, config) {
|
||||
expect(data).toBe('my content');
|
||||
expect(status).toBe(207);
|
||||
expect(headers()).toEqual({'content-encoding': 'smurf'});
|
||||
expect(config.url).toBe('/url');
|
||||
callback();
|
||||
});
|
||||
|
||||
$httpBackend.flush();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
it('should return the original http promise', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(207, 'my content', {'content-encoding': 'smurf'});
|
||||
var httpPromise = $http({url: '/url', method: 'GET'});
|
||||
expect(httpPromise.success(callback)).toBe(httpPromise);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('error', function() {
|
||||
it('should allow http specific callbacks to be registered via "error"', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(543, 'bad error', {'request-id': '123'});
|
||||
$http({url: '/url', method: 'GET'}).error(function(data, status, headers, config) {
|
||||
expect(data).toBe('bad error');
|
||||
expect(status).toBe(543);
|
||||
expect(headers()).toEqual({'request-id': '123'});
|
||||
expect(config.url).toBe('/url');
|
||||
callback();
|
||||
});
|
||||
|
||||
$httpBackend.flush();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
it('should return the original http promise', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(543, 'bad error', {'request-id': '123'});
|
||||
var httpPromise = $http({url: '/url', method: 'GET'});
|
||||
expect(httpPromise.error(callback)).toBe(httpPromise);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -96,11 +131,11 @@ describe('$http', function() {
|
|||
|
||||
it('should return single header', function() {
|
||||
$httpBackend.expect('GET', '/url').respond('', {'date': 'date-val'});
|
||||
callback.andCallFake(function(r, s, header) {
|
||||
expect(header('date')).toBe('date-val');
|
||||
callback.andCallFake(function(r) {
|
||||
expect(r.headers('date')).toBe('date-val');
|
||||
});
|
||||
|
||||
$http({url: '/url', method: 'GET'}).on('200', callback);
|
||||
$http({url: '/url', method: 'GET'}).then(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
|
@ -109,12 +144,12 @@ describe('$http', function() {
|
|||
|
||||
it('should return null when single header does not exist', function() {
|
||||
$httpBackend.expect('GET', '/url').respond('', {'Some-Header': 'Fake'});
|
||||
callback.andCallFake(function(r, s, header) {
|
||||
header(); // we need that to get headers parsed first
|
||||
expect(header('nothing')).toBe(null);
|
||||
callback.andCallFake(function(r) {
|
||||
r.headers(); // we need that to get headers parsed first
|
||||
expect(r.headers('nothing')).toBe(null);
|
||||
});
|
||||
|
||||
$http({url: '/url', method: 'GET'}).on('200', callback);
|
||||
$http({url: '/url', method: 'GET'}).then(callback)
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
|
@ -122,12 +157,16 @@ describe('$http', function() {
|
|||
|
||||
|
||||
it('should return all headers as object', function() {
|
||||
$httpBackend.expect('GET', '/url').respond('', {'content-encoding': 'gzip', 'server': 'Apache'});
|
||||
callback.andCallFake(function(r, s, header) {
|
||||
expect(header()).toEqual({'content-encoding': 'gzip', 'server': 'Apache'});
|
||||
$httpBackend.expect('GET', '/url').respond('', {
|
||||
'content-encoding': 'gzip',
|
||||
'server': 'Apache'
|
||||
});
|
||||
|
||||
$http({url: '/url', method: 'GET'}).on('200', callback);
|
||||
callback.andCallFake(function(r) {
|
||||
expect(r.headers()).toEqual({'content-encoding': 'gzip', 'server': 'Apache'});
|
||||
});
|
||||
|
||||
$http({url: '/url', method: 'GET'}).then(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
|
@ -135,12 +174,12 @@ describe('$http', function() {
|
|||
|
||||
|
||||
it('should return empty object for jsonp request', function() {
|
||||
callback.andCallFake(function(r, s, headers) {
|
||||
expect(headers()).toEqual({});
|
||||
callback.andCallFake(function(r) {
|
||||
expect(r.headers()).toEqual({});
|
||||
});
|
||||
|
||||
$httpBackend.expect('JSONP', '/some').respond(200);
|
||||
$http({url: '/some', method: 'JSONP'}).on('200', callback);
|
||||
$http({url: '/some', method: 'JSONP'}).then(callback);
|
||||
$httpBackend.flush();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
|
@ -288,7 +327,7 @@ describe('$http', function() {
|
|||
});
|
||||
|
||||
|
||||
it('should set the XSRF cookie into a XSRF header', function() {
|
||||
it('should set the XSRF cookie into a XSRF header', inject(function($browser) {
|
||||
function checkXSRF(secret) {
|
||||
return function(headers) {
|
||||
return headers['X-XSRF-TOKEN'] == secret;
|
||||
|
|
@ -307,7 +346,7 @@ describe('$http', function() {
|
|||
$http({url: '/url', method: 'DELETE', headers: {}});
|
||||
|
||||
$httpBackend.flush();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -404,264 +443,13 @@ describe('$http', function() {
|
|||
});
|
||||
|
||||
|
||||
describe('future', function() {
|
||||
|
||||
describe('abort', function() {
|
||||
|
||||
var future, rawXhrObject;
|
||||
|
||||
beforeEach(function() {
|
||||
$httpBackend.when('GET', '/url').respond('');
|
||||
future = $http({method: 'GET', url: '/url'});
|
||||
rawXhrObject = MockXhr.$$lastInstance;
|
||||
spyOn(rawXhrObject, 'abort');
|
||||
});
|
||||
|
||||
it('should return itself to allow chaining', function() {
|
||||
expect(future.abort()).toBe(future);
|
||||
});
|
||||
|
||||
it('should allow aborting the request', function() {
|
||||
future.abort();
|
||||
|
||||
expect(rawXhrObject.abort).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
it('should not abort already finished request', function() {
|
||||
$httpBackend.flush();
|
||||
|
||||
future.abort();
|
||||
expect(rawXhrObject.abort).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('retry', function() {
|
||||
|
||||
var future;
|
||||
|
||||
beforeEach(function() {
|
||||
$httpBackend.expect('HEAD', '/url-x').respond('');
|
||||
future = $http({method: 'HEAD', url: '/url-x'}).on('2xx', callback);
|
||||
});
|
||||
|
||||
it('should retry last request with same callbacks', function() {
|
||||
$httpBackend.flush();
|
||||
callback.reset();
|
||||
|
||||
$httpBackend.expect('HEAD', '/url-x').respond('');
|
||||
future.retry();
|
||||
$httpBackend.flush();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
it('should return itself to allow chaining', function() {
|
||||
$httpBackend.flush();
|
||||
|
||||
$httpBackend.expect('HEAD', '/url-x').respond('');
|
||||
expect(future.retry()).toBe(future);
|
||||
});
|
||||
|
||||
|
||||
it('should throw error when pending request', function() {
|
||||
expect(future.retry).toThrow('Can not retry request. Abort pending request first.');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('on', function() {
|
||||
|
||||
var future;
|
||||
|
||||
function expectToMatch(status, pattern) {
|
||||
expectToNotMatch(status, pattern, true);
|
||||
}
|
||||
|
||||
function expectToNotMatch(status, pattern, match) {
|
||||
callback.reset();
|
||||
future = $http({method: 'GET', url: '/' + status});
|
||||
future.on(pattern, callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
if (match) expect(callback).toHaveBeenCalledOnce();
|
||||
else expect(callback).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
$httpBackend.when('GET').respond(function(m, url) {
|
||||
return [parseInt(url.substr(1), 10), '', {}];
|
||||
});
|
||||
});
|
||||
|
||||
it('should return itself to allow chaining', function() {
|
||||
future = $http({method: 'GET', url: '/url'});
|
||||
expect(future.on('200', noop)).toBe(future);
|
||||
});
|
||||
|
||||
|
||||
it('should call exact status code callback', function() {
|
||||
expectToMatch(205, '205');
|
||||
});
|
||||
|
||||
|
||||
it('should match 2xx', function() {
|
||||
expectToMatch(200, '2xx');
|
||||
expectToMatch(201, '2xx');
|
||||
expectToMatch(266, '2xx');
|
||||
|
||||
expectToNotMatch(400, '2xx');
|
||||
expectToNotMatch(300, '2xx');
|
||||
});
|
||||
|
||||
|
||||
it('should match 20x', function() {
|
||||
expectToMatch(200, '20x');
|
||||
expectToMatch(201, '20x');
|
||||
expectToMatch(205, '20x');
|
||||
|
||||
expectToNotMatch(210, '20x');
|
||||
expectToNotMatch(301, '20x');
|
||||
expectToNotMatch(404, '20x');
|
||||
expectToNotMatch(501, '20x');
|
||||
});
|
||||
|
||||
|
||||
it('should match 2x1', function() {
|
||||
expectToMatch(201, '2x1');
|
||||
expectToMatch(211, '2x1');
|
||||
expectToMatch(251, '2x1');
|
||||
|
||||
expectToNotMatch(210, '2x1');
|
||||
expectToNotMatch(301, '2x1');
|
||||
expectToNotMatch(400, '2x1');
|
||||
});
|
||||
|
||||
|
||||
it('should match xxx', function() {
|
||||
expectToMatch(200, 'xxx');
|
||||
expectToMatch(210, 'xxx');
|
||||
expectToMatch(301, 'xxx');
|
||||
expectToMatch(406, 'xxx');
|
||||
expectToMatch(510, 'xxx');
|
||||
});
|
||||
|
||||
|
||||
it('should call all matched callbacks', function() {
|
||||
var no = jasmine.createSpy('wrong');
|
||||
$http({method: 'GET', url: '/205'}).
|
||||
on('xxx', callback).
|
||||
on('2xx', callback).
|
||||
on('205', callback).
|
||||
on('3xx', no).
|
||||
on('2x1', no).
|
||||
on('4xx', no);
|
||||
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(callback.callCount).toBe(3);
|
||||
expect(no).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should allow list of status patterns', function() {
|
||||
expectToMatch(201, '2xx,3xx');
|
||||
expectToMatch(301, '2xx,3xx');
|
||||
expectToNotMatch(405, '2xx,3xx');
|
||||
});
|
||||
|
||||
|
||||
it('should preserve the order of listeners', function() {
|
||||
var log = '';
|
||||
|
||||
$http({method: 'GET', url: '/201'}).
|
||||
on('2xx', function() {log += '1';}).
|
||||
on('201', function() {log += '2';}).
|
||||
on('2xx', function() {log += '3';});
|
||||
|
||||
$httpBackend.flush();
|
||||
expect(log).toBe('123');
|
||||
});
|
||||
|
||||
|
||||
it('should know "success" alias', function() {
|
||||
expectToMatch(200, 'success');
|
||||
expectToMatch(201, 'success');
|
||||
expectToMatch(250, 'success');
|
||||
|
||||
expectToNotMatch(403, 'success');
|
||||
expectToNotMatch(501, 'success');
|
||||
});
|
||||
|
||||
|
||||
it('should know "error" alias', function() {
|
||||
expectToMatch(401, 'error');
|
||||
expectToMatch(500, 'error');
|
||||
expectToMatch(0, 'error');
|
||||
|
||||
expectToNotMatch(201, 'error');
|
||||
expectToNotMatch(200, 'error');
|
||||
});
|
||||
|
||||
|
||||
it('should know "always" alias', function() {
|
||||
expectToMatch(200, 'always');
|
||||
expectToMatch(201, 'always');
|
||||
expectToMatch(250, 'always');
|
||||
expectToMatch(300, 'always');
|
||||
expectToMatch(302, 'always');
|
||||
expectToMatch(404, 'always');
|
||||
expectToMatch(501, 'always');
|
||||
expectToMatch(0, 'always');
|
||||
expectToMatch(-1, 'always');
|
||||
expectToMatch(-2, 'always');
|
||||
});
|
||||
|
||||
|
||||
it('should not call "2xx" when 0 status code', function() {
|
||||
expectToNotMatch(0, '2xx');
|
||||
});
|
||||
|
||||
it('should normalize internal statuses -1, -2 to 0', function() {
|
||||
callback.andCallFake(function(response, status) {
|
||||
expect(status).toBe(0);
|
||||
});
|
||||
|
||||
$http({method: 'GET', url: '/0'}).on('always', callback);
|
||||
$http({method: 'GET', url: '/-1'}).on('always', callback);
|
||||
$http({method: 'GET', url: '/-2'}).on('always', callback);
|
||||
|
||||
$httpBackend.flush();
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(callback.callCount).toBe(3);
|
||||
});
|
||||
|
||||
it('should match "timeout" when -1 internal status', function() {
|
||||
expectToMatch(-1, 'timeout');
|
||||
});
|
||||
|
||||
it('should match "abort" when 0 status', function() {
|
||||
expectToMatch(0, 'abort');
|
||||
});
|
||||
|
||||
it('should match "error" when 0, -1, or -2', function() {
|
||||
expectToMatch(0, 'error');
|
||||
expectToMatch(-1, 'error');
|
||||
expectToMatch(-2, 'error');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('scope.$apply', function() {
|
||||
|
||||
it('should $apply after success callback', function() {
|
||||
$httpBackend.when('GET').respond(200);
|
||||
$http({method: 'GET', url: '/some'});
|
||||
$httpBackend.flush();
|
||||
expect(scope.$apply).toHaveBeenCalledOnce();
|
||||
expect($rootScope.$apply).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -669,30 +457,20 @@ describe('$http', function() {
|
|||
$httpBackend.when('GET').respond(404);
|
||||
$http({method: 'GET', url: '/some'});
|
||||
$httpBackend.flush();
|
||||
expect(scope.$apply).toHaveBeenCalledOnce();
|
||||
expect($rootScope.$apply).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
it('should $apply even if exception thrown during callback', function() {
|
||||
it('should $apply even if exception thrown during callback', inject(function($exceptionHandler){
|
||||
$httpBackend.when('GET').respond(200);
|
||||
callback.andThrow('error in callback');
|
||||
|
||||
$http({method: 'GET', url: '/some'}).on('200', callback);
|
||||
$http({method: 'GET', url: '/some'}).then(callback);
|
||||
$httpBackend.flush();
|
||||
expect(scope.$apply).toHaveBeenCalledOnce();
|
||||
expect($rootScope.$apply).toHaveBeenCalledOnce();
|
||||
|
||||
$exceptionHandler.errors = [];
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should broadcast $http.request', function() {
|
||||
$httpBackend.when('GET').respond(200);
|
||||
scope.$on('$http.request', callback);
|
||||
var xhrFuture = $http({method: 'GET', url: '/whatever'});
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
expect(callback.mostRecentCall.args[1]).toBe(xhrFuture);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -722,7 +500,7 @@ describe('$http', function() {
|
|||
|
||||
it('should deserialize json objects', function() {
|
||||
$httpBackend.expect('GET', '/url').respond('{"foo":"bar","baz":23}');
|
||||
$http({method: 'GET', url: '/url'}).on('200', callback);
|
||||
$http({method: 'GET', url: '/url'}).success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
|
@ -732,7 +510,7 @@ describe('$http', function() {
|
|||
|
||||
it('should deserialize json arrays', function() {
|
||||
$httpBackend.expect('GET', '/url').respond('[1, "abc", {"foo":"bar"}]');
|
||||
$http({method: 'GET', url: '/url'}).on('200', callback);
|
||||
$http({method: 'GET', url: '/url'}).success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
|
@ -742,7 +520,7 @@ describe('$http', function() {
|
|||
|
||||
it('should deserialize json with security prefix', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(')]}\',\n[1, "abc", {"foo":"bar"}]');
|
||||
$http({method: 'GET', url: '/url'}).on('200', callback);
|
||||
$http({method: 'GET', url: '/url'}).success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
|
@ -752,7 +530,7 @@ describe('$http', function() {
|
|||
|
||||
it('should deserialize json with security prefix ")]}\'"', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(')]}\'\n\n[1, "abc", {"foo":"bar"}]');
|
||||
$http({method: 'GET', url: '/url'}).on('200', callback);
|
||||
$http({method: 'GET', url: '/url'}).success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
|
@ -762,7 +540,7 @@ describe('$http', function() {
|
|||
|
||||
it('should not deserialize tpl beginning with ng expression', function() {
|
||||
$httpBackend.expect('GET', '/url').respond('{{some}}');
|
||||
$http.get('/url').on('200', callback);
|
||||
$http.get('/url').success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
|
@ -776,8 +554,7 @@ describe('$http', function() {
|
|||
function second(d) {return d + '2';}
|
||||
|
||||
$httpBackend.expect('POST', '/url').respond('0');
|
||||
$http({method: 'POST', url: '/url', transformResponse: [first, second]}).
|
||||
on('200', callback);
|
||||
$http({method: 'POST', url: '/url', transformResponse: [first, second]}).success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
|
@ -801,15 +578,15 @@ describe('$http', function() {
|
|||
$httpBackend.flush();
|
||||
}
|
||||
|
||||
it('should cache GET request when cache is provided', function() {
|
||||
it('should cache GET request when cache is provided', inject(function($browser) {
|
||||
doFirstCacheRequest();
|
||||
|
||||
$http({method: 'get', url: '/url', cache: cache}).on('200', callback);
|
||||
$http({method: 'get', url: '/url', cache: cache}).success(callback);
|
||||
$browser.defer.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
expect(callback.mostRecentCall.args[0]).toBe('content');
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should not cache when cache is not provided', function() {
|
||||
|
|
@ -831,7 +608,7 @@ describe('$http', function() {
|
|||
|
||||
it('should always call callback asynchronously', function() {
|
||||
doFirstCacheRequest();
|
||||
$http({method: 'get', url: '/url', cache: cache}).on('200', callback);
|
||||
$http({method: 'get', url: '/url', cache: cache}).then(callback);
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
|
@ -841,7 +618,7 @@ describe('$http', function() {
|
|||
doFirstCacheRequest('POST');
|
||||
|
||||
$httpBackend.expect('POST', '/url').respond('content2');
|
||||
$http({method: 'POST', url: '/url', cache: cache}).on('200', callback);
|
||||
$http({method: 'POST', url: '/url', cache: cache}).success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
|
@ -853,7 +630,7 @@ describe('$http', function() {
|
|||
doFirstCacheRequest('PUT');
|
||||
|
||||
$httpBackend.expect('PUT', '/url').respond('content2');
|
||||
$http({method: 'PUT', url: '/url', cache: cache}).on('200', callback);
|
||||
$http({method: 'PUT', url: '/url', cache: cache}).success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
|
@ -865,7 +642,7 @@ describe('$http', function() {
|
|||
doFirstCacheRequest('DELETE');
|
||||
|
||||
$httpBackend.expect('DELETE', '/url').respond(206);
|
||||
$http({method: 'DELETE', url: '/url', cache: cache}).on('206', callback);
|
||||
$http({method: 'DELETE', url: '/url', cache: cache}).success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
|
@ -876,7 +653,7 @@ describe('$http', function() {
|
|||
doFirstCacheRequest('GET', 404);
|
||||
|
||||
$httpBackend.expect('GET', '/url').respond('content2');
|
||||
$http({method: 'GET', url: '/url', cache: cache}).on('200', callback);
|
||||
$http({method: 'GET', url: '/url', cache: cache}).success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
|
@ -884,29 +661,29 @@ describe('$http', function() {
|
|||
});
|
||||
|
||||
|
||||
it('should cache the headers as well', function() {
|
||||
it('should cache the headers as well', inject(function($browser) {
|
||||
doFirstCacheRequest('GET', 200, {'content-encoding': 'gzip', 'server': 'Apache'});
|
||||
callback.andCallFake(function(r, s, headers) {
|
||||
expect(headers()).toEqual({'content-encoding': 'gzip', 'server': 'Apache'});
|
||||
expect(headers('server')).toBe('Apache');
|
||||
});
|
||||
|
||||
$http({method: 'GET', url: '/url', cache: cache}).on('200', callback);
|
||||
$http({method: 'GET', url: '/url', cache: cache}).success(callback);
|
||||
$browser.defer.flush();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should cache status code as well', function() {
|
||||
it('should cache status code as well', inject(function($browser) {
|
||||
doFirstCacheRequest('GET', 201);
|
||||
callback.andCallFake(function(r, status, h) {
|
||||
expect(status).toBe(201);
|
||||
});
|
||||
|
||||
$http({method: 'get', url: '/url', cache: cache}).on('2xx', callback);
|
||||
$http({method: 'get', url: '/url', cache: cache}).success(callback);
|
||||
$browser.defer.flush();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should use cache even if request fired before first response is back', function() {
|
||||
|
|
@ -917,8 +694,8 @@ describe('$http', function() {
|
|||
expect(status).toBe(201);
|
||||
});
|
||||
|
||||
$http({method: 'GET', url: '/url', cache: cache}).on('always', callback);
|
||||
$http({method: 'GET', url: '/url', cache: cache}).on('always', callback);
|
||||
$http({method: 'GET', url: '/url', cache: cache}).success(callback);
|
||||
$http({method: 'GET', url: '/url', cache: cache}).success(callback);
|
||||
|
||||
$httpBackend.flush();
|
||||
expect(callback).toHaveBeenCalled();
|
||||
|
|
@ -941,19 +718,7 @@ describe('$http', function() {
|
|||
});
|
||||
|
||||
|
||||
it('should remove the request when aborted', function() {
|
||||
$httpBackend.when('GET').respond(0);
|
||||
future = $http({method: 'get', url: '/x'});
|
||||
expect($http.pendingRequests.length).toBe(1);
|
||||
|
||||
future.abort();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect($http.pendingRequests.length).toBe(0);
|
||||
});
|
||||
|
||||
|
||||
it('should update pending requests even when served from cache', function() {
|
||||
it('should update pending requests even when served from cache', inject(function($browser) {
|
||||
$httpBackend.when('GET').respond(200);
|
||||
|
||||
$http({method: 'get', url: '/cached', cache: true});
|
||||
|
|
@ -968,12 +733,12 @@ describe('$http', function() {
|
|||
|
||||
$browser.defer.flush();
|
||||
expect($http.pendingRequests.length).toBe(0);
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should remove the request before firing callbacks', function() {
|
||||
$httpBackend.when('GET').respond(200);
|
||||
$http({method: 'get', url: '/url'}).on('xxx', function() {
|
||||
$http({method: 'get', url: '/url'}).success(function() {
|
||||
expect($http.pendingRequests.length).toBe(0);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
describe("widget", function() {
|
||||
describe('widget', function() {
|
||||
describe('ng:switch', inject(function($rootScope, $compile) {
|
||||
it('should switch on value change', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
|
|
|
|||
Loading…
Reference in a new issue