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

Commit 1d2414c

Browse files
jimlyndoncaitp
authored andcommitted
feat($http): add xhr statusText to completeRequest callback
Makes xhr status text accessible is $http success/error callback. See www.w3.org/TR/XMLHttpRequest/#dom-xmlhttprequest-statustext Closes #2335 Closes #2665 Closes #6713
1 parent 9f62d9d commit 1d2414c

File tree

6 files changed

+100
-42
lines changed

6 files changed

+100
-42
lines changed

src/ng/http.js

+9-7
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ function $HttpProvider() {
571571
* - **status** – `{number}` – HTTP status code of the response.
572572
* - **headers** – `{function([headerName])}` – Header getter function.
573573
* - **config** – `{Object}` – The configuration object that was used to generate the request.
574+
* - **statusText** – `{string}` – HTTP status text of the response.
574575
*
575576
* @property {Array.<Object>} pendingRequests Array of config objects for currently pending
576577
* requests. This is primarily meant to be used for debugging purposes.
@@ -945,9 +946,9 @@ function $HttpProvider() {
945946
} else {
946947
// serving from cache
947948
if (isArray(cachedResp)) {
948-
resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2]));
949+
resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2]), cachedResp[3]);
949950
} else {
950-
resolvePromise(cachedResp, 200, {});
951+
resolvePromise(cachedResp, 200, {}, 'OK');
951952
}
952953
}
953954
} else {
@@ -971,33 +972,34 @@ function $HttpProvider() {
971972
* - resolves the raw $http promise
972973
* - calls $apply
973974
*/
974-
function done(status, response, headersString) {
975+
function done(status, response, headersString, statusText) {
975976
if (cache) {
976977
if (isSuccess(status)) {
977-
cache.put(url, [status, response, parseHeaders(headersString)]);
978+
cache.put(url, [status, response, parseHeaders(headersString), statusText]);
978979
} else {
979980
// remove promise from the cache
980981
cache.remove(url);
981982
}
982983
}
983984

984-
resolvePromise(response, status, headersString);
985+
resolvePromise(response, status, headersString, statusText);
985986
if (!$rootScope.$$phase) $rootScope.$apply();
986987
}
987988

988989

989990
/**
990991
* Resolves the raw $http promise.
991992
*/
992-
function resolvePromise(response, status, headers) {
993+
function resolvePromise(response, status, headers, statusText) {
993994
// normalize internal statuses to 0
994995
status = Math.max(status, 0);
995996

996997
(isSuccess(status) ? deferred.resolve : deferred.reject)({
997998
data: response,
998999
status: status,
9991000
headers: headersGetter(headers),
1000-
config: config
1001+
config: config,
1002+
statusText : statusText
10011003
});
10021004
}
10031005

src/ng/httpBackend.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
9494
completeRequest(callback,
9595
status || xhr.status,
9696
response,
97-
responseHeaders);
97+
responseHeaders,
98+
xhr.statusText || '');
9899
}
99100
};
100101

@@ -135,7 +136,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
135136
xhr && xhr.abort();
136137
}
137138

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

150151
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
151-
status = status == 1223 ? 204 : status;
152+
status = status === 1223 ? 204 : status;
153+
statusText = statusText || '';
152154

153-
callback(status, response, headersString);
155+
callback(status, response, headersString, statusText);
154156
$browser.$$completeOutstandingRequest(noop);
155157
}
156158
};

src/ngMock/angular-mocks.js

+25-21
Original file line numberDiff line numberDiff line change
@@ -1090,12 +1090,12 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
10901090
responsesPush = angular.bind(responses, responses.push),
10911091
copy = angular.copy;
10921092

1093-
function createResponse(status, data, headers) {
1093+
function createResponse(status, data, headers, statusText) {
10941094
if (angular.isFunction(status)) return status;
10951095

10961096
return function() {
10971097
return angular.isNumber(status)
1098-
? [status, data, headers]
1098+
? [status, data, headers, statusText]
10991099
: [200, status, data];
11001100
};
11011101
}
@@ -1120,7 +1120,8 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
11201120
function handleResponse() {
11211121
var response = wrapped.response(method, url, data, headers);
11221122
xhr.$$respHeaders = response[2];
1123-
callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders());
1123+
callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
1124+
copy(response[3] || ''));
11241125
}
11251126

11261127
function handleTimeout() {
@@ -1188,16 +1189,17 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
11881189
* request is handled.
11891190
*
11901191
* - respond –
1191-
* `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
1192-
* – The respond method takes a set of static data to be returned or a function that can return
1193-
* an array containing response status (number), response data (string) and response headers
1194-
* (Object).
1192+
* `{function([status,] data[, headers, statusText])
1193+
* | function(function(method, url, data, headers)}`
1194+
* – The respond method takes a set of static data to be returned or a function that can
1195+
* return an array containing response status (number), response data (string), response
1196+
* headers (Object), and the text for the status (string).
11951197
*/
11961198
$httpBackend.when = function(method, url, data, headers) {
11971199
var definition = new MockHttpExpectation(method, url, data, headers),
11981200
chain = {
1199-
respond: function(status, data, headers) {
1200-
definition.response = createResponse(status, data, headers);
1201+
respond: function(status, data, headers, statusText) {
1202+
definition.response = createResponse(status, data, headers, statusText);
12011203
}
12021204
};
12031205

@@ -1312,17 +1314,18 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
13121314
* request is handled.
13131315
*
13141316
* - respond –
1315-
* `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
1316-
* – The respond method takes a set of static data to be returned or a function that can return
1317-
* an array containing response status (number), response data (string) and response headers
1318-
* (Object).
1317+
* `{function([status,] data[, headers, statusText])
1318+
* | function(function(method, url, data, headers)}`
1319+
* – The respond method takes a set of static data to be returned or a function that can
1320+
* return an array containing response status (number), response data (string), response
1321+
* headers (Object), and the text for the status (string).
13191322
*/
13201323
$httpBackend.expect = function(method, url, data, headers) {
13211324
var expectation = new MockHttpExpectation(method, url, data, headers);
13221325
expectations.push(expectation);
13231326
return {
1324-
respond: function(status, data, headers) {
1325-
expectation.response = createResponse(status, data, headers);
1327+
respond: function (status, data, headers, statusText) {
1328+
expectation.response = createResponse(status, data, headers, statusText);
13261329
}
13271330
};
13281331
};
@@ -1833,13 +1836,14 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
18331836
* control how a matched request is handled.
18341837
*
18351838
* - respond –
1836-
* `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
1839+
* `{function([status,] data[, headers, statusText])
1840+
* | function(function(method, url, data, headers)}`
18371841
* – The respond method takes a set of static data to be returned or a function that can return
1838-
* an array containing response status (number), response data (string) and response headers
1839-
* (Object).
1840-
* - passThrough – `{function()}` – Any request matching a backend definition with `passThrough`
1841-
* handler will be passed through to the real backend (an XHR request will be made to the
1842-
* server.)
1842+
* an array containing response status (number), response data (string), response headers
1843+
* (Object), and the text for the status (string).
1844+
* - passThrough – `{function()}` – Any request matching a backend definition with
1845+
* `passThrough` handler will be passed through to the real backend (an XHR request will be made
1846+
* to the server.)
18431847
*/
18441848

18451849
/**

test/ng/httpBackendSpec.js

+26
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,32 @@ describe('$httpBackend', function() {
7676
expect(xhr.$$data).toBe(null);
7777
});
7878

79+
it('should call completion function with xhr.statusText if present', function() {
80+
callback.andCallFake(function(status, response, headers, statusText) {
81+
expect(statusText).toBe('OK');
82+
});
83+
84+
$backend('GET', '/some-url', null, callback);
85+
xhr = MockXhr.$$lastInstance;
86+
xhr.statusText = 'OK';
87+
xhr.readyState = 4;
88+
xhr.onreadystatechange();
89+
expect(callback).toHaveBeenCalledOnce();
90+
});
91+
92+
it('should call completion function with empty string if not present', function() {
93+
callback.andCallFake(function(status, response, headers, statusText) {
94+
expect(statusText).toBe('');
95+
});
96+
97+
$backend('GET', '/some-url', null, callback);
98+
xhr = MockXhr.$$lastInstance;
99+
xhr.readyState = 4;
100+
xhr.onreadystatechange();
101+
expect(callback).toHaveBeenCalledOnce();
102+
});
103+
104+
79105
it('should normalize IE\'s 1223 status code into 204', function() {
80106
callback.andCallFake(function(status) {
81107
expect(status).toBe(204);

test/ng/httpSpec.js

+24
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,30 @@ describe('$http', function() {
481481
});
482482

483483

484+
it('should pass statusText in response object when a request is successful', function() {
485+
$httpBackend.expect('GET', '/url').respond(200, 'SUCCESS', {}, 'OK');
486+
$http({url: '/url', method: 'GET'}).then(function(response) {
487+
expect(response.statusText).toBe('OK');
488+
callback();
489+
});
490+
491+
$httpBackend.flush();
492+
expect(callback).toHaveBeenCalledOnce();
493+
});
494+
495+
496+
it('should pass statusText in response object when a request fails', function() {
497+
$httpBackend.expect('GET', '/url').respond(404, 'ERROR', {}, 'Not Found');
498+
$http({url: '/url', method: 'GET'}).then(null, function(response) {
499+
expect(response.statusText).toBe('Not Found');
500+
callback();
501+
});
502+
503+
$httpBackend.flush();
504+
expect(callback).toHaveBeenCalledOnce();
505+
});
506+
507+
484508
it('should pass in the response object when a request failed', function() {
485509
$httpBackend.expect('GET', '/url').respond(543, 'bad error', {'request-id': '123'});
486510
$http({url: '/url', method: 'GET'}).then(null, function(response) {

test/ngMock/angular-mocksSpec.js

+10-10
Original file line numberDiff line numberDiff line change
@@ -1068,29 +1068,29 @@ describe('ngMock', function() {
10681068
hb.flush();
10691069

10701070
expect(callback.callCount).toBe(2);
1071-
expect(callback.argsForCall[0]).toEqual([201, 'second', '']);
1072-
expect(callback.argsForCall[1]).toEqual([200, 'first', '']);
1071+
expect(callback.argsForCall[0]).toEqual([201, 'second', '', '']);
1072+
expect(callback.argsForCall[1]).toEqual([200, 'first', '', '']);
10731073
});
10741074

10751075

10761076
describe('respond()', function() {
10771077
it('should take values', function() {
1078-
hb.expect('GET', '/url1').respond(200, 'first', {'header': 'val'});
1078+
hb.expect('GET', '/url1').respond(200, 'first', {'header': 'val'}, 'OK');
10791079
hb('GET', '/url1', undefined, callback);
10801080
hb.flush();
10811081

1082-
expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val');
1082+
expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val', 'OK');
10831083
});
10841084

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

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

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

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

11211121
expect(callback.callCount).toBe(2);
1122-
expect(callback.argsForCall[0]).toEqual([200, 'first', '']);
1123-
expect(callback.argsForCall[1]).toEqual([200, 'second', '']);
1122+
expect(callback.argsForCall[0]).toEqual([200, 'first', '', '']);
1123+
expect(callback.argsForCall[1]).toEqual([200, 'second', '', '']);
11241124
});
11251125
});
11261126

@@ -1415,7 +1415,7 @@ describe('ngMock', function() {
14151415
hb[shortcut]('/foo').respond('bar');
14161416
hb(method, '/foo', undefined, callback);
14171417
hb.flush();
1418-
expect(callback).toHaveBeenCalledOnceWith(200, 'bar', '');
1418+
expect(callback).toHaveBeenCalledOnceWith(200, 'bar', '', '');
14191419
});
14201420
});
14211421
});

0 commit comments

Comments
 (0)