Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

feat($http): add xhr statusText to completeRequest callback #6713

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions src/ng/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ function $HttpProvider() {
* - **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.
* - **statusText** – `{string}` – HTTP status text of the response.
*
* @property {Array.<Object>} pendingRequests Array of config objects for currently pending
* requests. This is primarily meant to be used for debugging purposes.
Expand Down Expand Up @@ -945,9 +946,9 @@ function $HttpProvider() {
} else {
// serving from cache
if (isArray(cachedResp)) {
resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2]));
resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2]), cachedResp[3]);
} else {
resolvePromise(cachedResp, 200, {});
resolvePromise(cachedResp, 200, {}, 'OK');
}
}
} else {
Expand All @@ -971,33 +972,34 @@ function $HttpProvider() {
* - resolves the raw $http promise
* - calls $apply
*/
function done(status, response, headersString) {
function done(status, response, headersString, statusText) {
if (cache) {
if (isSuccess(status)) {
cache.put(url, [status, response, parseHeaders(headersString)]);
cache.put(url, [status, response, parseHeaders(headersString), statusText]);
} else {
// remove promise from the cache
cache.remove(url);
}
}

resolvePromise(response, status, headersString);
resolvePromise(response, status, headersString, statusText);
if (!$rootScope.$$phase) $rootScope.$apply();
}


/**
* Resolves the raw $http promise.
*/
function resolvePromise(response, status, headers) {
function resolvePromise(response, status, headers, statusText) {
// normalize internal statuses to 0
status = Math.max(status, 0);

(isSuccess(status) ? deferred.resolve : deferred.reject)({
data: response,
status: status,
headers: headersGetter(headers),
config: config
config: config,
statusText : statusText
});
}

Expand Down
10 changes: 6 additions & 4 deletions src/ng/httpBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
completeRequest(callback,
status || xhr.status,
response,
responseHeaders);
responseHeaders,
xhr.statusText || '');
}
};

Expand Down Expand Up @@ -135,7 +136,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
xhr && xhr.abort();
}

function completeRequest(callback, status, response, headersString) {
function completeRequest(callback, status, response, headersString, statusText) {
// cancel timeout and subsequent timeout promise resolution
timeoutId && $browserDefer.cancel(timeoutId);
jsonpDone = xhr = null;
Expand All @@ -148,9 +149,10 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
}

// normalize IE bug (http://bugs.jquery.com/ticket/1450)
status = status == 1223 ? 204 : status;
status = status === 1223 ? 204 : status;
statusText = statusText || '';

callback(status, response, headersString);
callback(status, response, headersString, statusText);
$browser.$$completeOutstandingRequest(noop);
}
};
Expand Down
46 changes: 25 additions & 21 deletions src/ngMock/angular-mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -1090,12 +1090,12 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
responsesPush = angular.bind(responses, responses.push),
copy = angular.copy;

function createResponse(status, data, headers) {
function createResponse(status, data, headers, statusText) {
if (angular.isFunction(status)) return status;

return function() {
return angular.isNumber(status)
? [status, data, headers]
? [status, data, headers, statusText]
: [200, status, data];
};
}
Expand All @@ -1120,7 +1120,8 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
function handleResponse() {
var response = wrapped.response(method, url, data, headers);
xhr.$$respHeaders = response[2];
callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders());
callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
copy(response[3] || ''));
}

function handleTimeout() {
Expand Down Expand Up @@ -1188,16 +1189,17 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
* request is handled.
*
* - respond –
* `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
* – The respond method takes a set of static data to be returned or a function that can return
* an array containing response status (number), response data (string) and response headers
* (Object).
* `{function([status,] data[, headers, statusText])
* | function(function(method, url, data, headers)}`
* – The respond method takes a set of static data to be returned or a function that can
* return an array containing response status (number), response data (string), response
* headers (Object), and the text for the status (string).
*/
$httpBackend.when = function(method, url, data, headers) {
var definition = new MockHttpExpectation(method, url, data, headers),
chain = {
respond: function(status, data, headers) {
definition.response = createResponse(status, data, headers);
respond: function(status, data, headers, statusText) {
definition.response = createResponse(status, data, headers, statusText);
}
};

Expand Down Expand Up @@ -1312,17 +1314,18 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
* request is handled.
*
* - respond –
* `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
* – The respond method takes a set of static data to be returned or a function that can return
* an array containing response status (number), response data (string) and response headers
* (Object).
* `{function([status,] data[, headers, statusText])
* | function(function(method, url, data, headers)}`
* – The respond method takes a set of static data to be returned or a function that can
* return an array containing response status (number), response data (string), response
* headers (Object), and the text for the status (string).
*/
$httpBackend.expect = function(method, url, data, headers) {
var expectation = new MockHttpExpectation(method, url, data, headers);
expectations.push(expectation);
return {
respond: function(status, data, headers) {
expectation.response = createResponse(status, data, headers);
respond: function (status, data, headers, statusText) {
expectation.response = createResponse(status, data, headers, statusText);
}
};
};
Expand Down Expand Up @@ -1833,13 +1836,14 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
* control how a matched request is handled.
*
* - respond –
* `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
* `{function([status,] data[, headers, statusText])
* | function(function(method, url, data, headers)}`
* – The respond method takes a set of static data to be returned or a function that can return
* an array containing response status (number), response data (string) and response headers
* (Object).
* - passThrough – `{function()}` – Any request matching a backend definition with `passThrough`
* handler will be passed through to the real backend (an XHR request will be made to the
* server.)
* an array containing response status (number), response data (string), response headers
* (Object), and the text for the status (string).
* - passThrough – `{function()}` – Any request matching a backend definition with
* `passThrough` handler will be passed through to the real backend (an XHR request will be made
* to the server.)
*/

/**
Expand Down
26 changes: 26 additions & 0 deletions test/ng/httpBackendSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,32 @@ describe('$httpBackend', function() {
expect(xhr.$$data).toBe(null);
});

it('should call completion function with xhr.statusText if present', function() {
callback.andCallFake(function(status, response, headers, statusText) {
expect(statusText).toBe('OK');
});

$backend('GET', '/some-url', null, callback);
xhr = MockXhr.$$lastInstance;
xhr.statusText = 'OK';
xhr.readyState = 4;
xhr.onreadystatechange();
expect(callback).toHaveBeenCalledOnce();
});

it('should call completion function with empty string if not present', function() {
callback.andCallFake(function(status, response, headers, statusText) {
expect(statusText).toBe('');
});

$backend('GET', '/some-url', null, callback);
xhr = MockXhr.$$lastInstance;
xhr.readyState = 4;
xhr.onreadystatechange();
expect(callback).toHaveBeenCalledOnce();
});


it('should normalize IE\'s 1223 status code into 204', function() {
callback.andCallFake(function(status) {
expect(status).toBe(204);
Expand Down
24 changes: 24 additions & 0 deletions test/ng/httpSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,30 @@ describe('$http', function() {
});


it('should pass statusText in response object when a request is successful', function() {
$httpBackend.expect('GET', '/url').respond(200, 'SUCCESS', {}, 'OK');
$http({url: '/url', method: 'GET'}).then(function(response) {
expect(response.statusText).toBe('OK');
callback();
});

$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
});


it('should pass statusText in response object when a request fails', function() {
$httpBackend.expect('GET', '/url').respond(404, 'ERROR', {}, 'Not Found');
$http({url: '/url', method: 'GET'}).then(null, function(response) {
expect(response.statusText).toBe('Not Found');
callback();
});

$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
});


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) {
Expand Down
20 changes: 10 additions & 10 deletions test/ngMock/angular-mocksSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1068,29 +1068,29 @@ describe('ngMock', function() {
hb.flush();

expect(callback.callCount).toBe(2);
expect(callback.argsForCall[0]).toEqual([201, 'second', '']);
expect(callback.argsForCall[1]).toEqual([200, 'first', '']);
expect(callback.argsForCall[0]).toEqual([201, 'second', '', '']);
expect(callback.argsForCall[1]).toEqual([200, 'first', '', '']);
});


describe('respond()', function() {
it('should take values', function() {
hb.expect('GET', '/url1').respond(200, 'first', {'header': 'val'});
hb.expect('GET', '/url1').respond(200, 'first', {'header': 'val'}, 'OK');
hb('GET', '/url1', undefined, callback);
hb.flush();

expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val');
expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val', 'OK');
});

it('should take function', function() {
hb.expect('GET', '/some').respond(function(m, u, d, h) {
return [301, m + u + ';' + d + ';a=' + h.a, {'Connection': 'keep-alive'}];
hb.expect('GET', '/some').respond(function (m, u, d, h) {
return [301, m + u + ';' + d + ';a=' + h.a, {'Connection': 'keep-alive'}, 'Moved Permanently'];
});

hb('GET', '/some', 'data', callback, {a: 'b'});
hb.flush();

expect(callback).toHaveBeenCalledOnceWith(301, 'GET/some;data;a=b', 'Connection: keep-alive');
expect(callback).toHaveBeenCalledOnceWith(301, 'GET/some;data;a=b', 'Connection: keep-alive', 'Moved Permanently');
});

it('should default status code to 200', function() {
Expand Down Expand Up @@ -1119,8 +1119,8 @@ describe('ngMock', function() {
hb.flush();

expect(callback.callCount).toBe(2);
expect(callback.argsForCall[0]).toEqual([200, 'first', '']);
expect(callback.argsForCall[1]).toEqual([200, 'second', '']);
expect(callback.argsForCall[0]).toEqual([200, 'first', '', '']);
expect(callback.argsForCall[1]).toEqual([200, 'second', '', '']);
});
});

Expand Down Expand Up @@ -1415,7 +1415,7 @@ describe('ngMock', function() {
hb[shortcut]('/foo').respond('bar');
hb(method, '/foo', undefined, callback);
hb.flush();
expect(callback).toHaveBeenCalledOnceWith(200, 'bar', '');
expect(callback).toHaveBeenCalledOnceWith(200, 'bar', '', '');
});
});
});
Expand Down