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

Commit 47583d9

Browse files
gkalpakpetebacondarwin
authored andcommitted
fix($http): avoid Possibly Unhandled Rejection error when the request fails
Calling `promise.finally(...)` creates a new promise. If we don't return this promise, the user won't be able to attach an error handler to it and thus won't be able to prevent a potential PUR error. This commit also improves the test coverage for the increment/decrement `outstandingRequestCount` fix. Closes #13869 Closes #14921
1 parent dc7f625 commit 47583d9

File tree

2 files changed

+211
-15
lines changed

2 files changed

+211
-15
lines changed

src/ng/http.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -978,7 +978,7 @@ function $HttpProvider() {
978978
promise = chainInterceptors(promise, requestInterceptors);
979979
promise = promise.then(serverRequest);
980980
promise = chainInterceptors(promise, responseInterceptors);
981-
promise.finally(completeOutstandingRequest);
981+
promise = promise.finally(completeOutstandingRequest);
982982

983983
if (useLegacyPromise) {
984984
promise.success = function(fn) {

test/ng/httpSpec.js

+210-14
Original file line numberDiff line numberDiff line change
@@ -1896,37 +1896,233 @@ describe('$http', function() {
18961896
expect(paramSerializer({foo: 'foo', bar: ['bar', 'baz']})).toEqual('bar=bar&bar=baz&foo=foo');
18971897
});
18981898
});
1899+
});
18991900

1900-
describe('$browser\'s outstandingRequestCount', function() {
1901-
var incOutstandingRequestCountSpy, completeOutstandingRequestSpy;
19021901

1903-
beforeEach(inject(function($browser) {
1904-
incOutstandingRequestCountSpy
1905-
= spyOn($browser, '$$incOutstandingRequestCount').andCallThrough();
1906-
completeOutstandingRequestSpy
1907-
= spyOn($browser, '$$completeOutstandingRequest').andCallThrough();
1908-
}));
1902+
describe('$browser\'s outstandingRequestCount', function() {
1903+
var $http;
1904+
var $httpBackend;
1905+
var $rootScope;
1906+
var incOutstandingRequestCountSpy;
1907+
var completeOutstandingRequestSpy;
19091908

1910-
it('should update $browser outstandingRequestCount on success', function() {
1911-
$httpBackend.when('GET').respond(200);
19121909

1910+
describe('without interceptors', function() {
1911+
beforeEach(setupServicesAndSpies);
1912+
1913+
1914+
it('should immediately call `$browser.$$incOutstandingRequestCount()`', function() {
19131915
expect(incOutstandingRequestCountSpy).not.toHaveBeenCalled();
19141916
$http.get('');
19151917
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
1918+
});
1919+
1920+
1921+
it('should call `$browser.$$completeOutstandingRequest()` on success', function() {
1922+
$httpBackend.when('GET').respond(200);
1923+
1924+
$http.get('');
1925+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
19161926
$httpBackend.flush();
19171927
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
19181928
});
19191929

1920-
it('should update $browser outstandingRequestCount on error', function() {
1930+
1931+
it('should call `$browser.$$completeOutstandingRequest()` on error', function() {
19211932
$httpBackend.when('GET').respond(500);
19221933

1923-
expect(incOutstandingRequestCountSpy).not.toHaveBeenCalled();
1924-
$http.get('');
1925-
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
1934+
$http.get('').catch(noop);
1935+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
19261936
$httpBackend.flush();
19271937
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
19281938
});
1939+
1940+
1941+
it('should increment/decrement `outstandingRequestCount` on error in `transformRequest`',
1942+
inject(function($exceptionHandler) {
1943+
expect(incOutstandingRequestCountSpy).not.toHaveBeenCalled();
1944+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
1945+
1946+
$http.get('', {transformRequest: function() { throw new Error(); }}).catch(noop);
1947+
1948+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
1949+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
1950+
1951+
$rootScope.$digest();
1952+
1953+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
1954+
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
1955+
1956+
expect($exceptionHandler.errors).toEqual([jasmine.any(Error)]);
1957+
$exceptionHandler.errors = [];
1958+
})
1959+
);
1960+
1961+
1962+
it('should increment/decrement `outstandingRequestCount` on error in `transformResponse`',
1963+
inject(function($exceptionHandler) {
1964+
expect(incOutstandingRequestCountSpy).not.toHaveBeenCalled();
1965+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
1966+
1967+
$httpBackend.when('GET').respond(200);
1968+
$http.get('', {transformResponse: function() { throw new Error(); }}).catch(noop);
1969+
1970+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
1971+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
1972+
1973+
$httpBackend.flush();
1974+
1975+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
1976+
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
1977+
1978+
expect($exceptionHandler.errors).toEqual([jasmine.any(Error)]);
1979+
$exceptionHandler.errors = [];
1980+
})
1981+
);
19291982
});
1983+
1984+
1985+
describe('with interceptors', function() {
1986+
var reqInterceptorDeferred;
1987+
var resInterceptorDeferred;
1988+
var reqInterceptorFulfilled;
1989+
var resInterceptorFulfilled;
1990+
1991+
beforeEach(module(function($httpProvider) {
1992+
reqInterceptorDeferred = null;
1993+
resInterceptorDeferred = null;
1994+
reqInterceptorFulfilled = false;
1995+
resInterceptorFulfilled = false;
1996+
1997+
$httpProvider.interceptors.push(function($q) {
1998+
return {
1999+
request: function(config) {
2000+
return (reqInterceptorDeferred = $q.defer()).
2001+
promise.
2002+
finally(function() { reqInterceptorFulfilled = true; }).
2003+
then(valueFn(config));
2004+
},
2005+
response: function() {
2006+
return (resInterceptorDeferred = $q.defer()).
2007+
promise.
2008+
finally(function() { resInterceptorFulfilled = true; });
2009+
}
2010+
};
2011+
});
2012+
}));
2013+
2014+
beforeEach(setupServicesAndSpies);
2015+
2016+
beforeEach(function() {
2017+
$httpBackend.when('GET').respond(200);
2018+
});
2019+
2020+
2021+
it('should increment/decrement `outstandingRequestCount` before/after async interceptors',
2022+
function() {
2023+
expect(reqInterceptorFulfilled).toBe(false);
2024+
expect(resInterceptorFulfilled).toBe(false);
2025+
expect(incOutstandingRequestCountSpy).not.toHaveBeenCalled();
2026+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
2027+
2028+
$http.get('');
2029+
$rootScope.$digest();
2030+
2031+
expect(reqInterceptorFulfilled).toBe(false);
2032+
expect(resInterceptorFulfilled).toBe(false);
2033+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2034+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
2035+
2036+
reqInterceptorDeferred.resolve();
2037+
$httpBackend.flush();
2038+
2039+
expect(reqInterceptorFulfilled).toBe(true);
2040+
expect(resInterceptorFulfilled).toBe(false);
2041+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2042+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
2043+
2044+
resInterceptorDeferred.resolve();
2045+
$rootScope.$digest();
2046+
2047+
expect(reqInterceptorFulfilled).toBe(true);
2048+
expect(resInterceptorFulfilled).toBe(true);
2049+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2050+
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
2051+
}
2052+
);
2053+
2054+
2055+
it('should increment/decrement `outstandingRequestCount` on error in request interceptor',
2056+
function() {
2057+
expect(reqInterceptorFulfilled).toBe(false);
2058+
expect(incOutstandingRequestCountSpy).not.toHaveBeenCalled();
2059+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
2060+
2061+
$http.get('').catch(noop);
2062+
$rootScope.$digest();
2063+
2064+
expect(reqInterceptorFulfilled).toBe(false);
2065+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2066+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
2067+
2068+
reqInterceptorDeferred.reject();
2069+
$rootScope.$digest();
2070+
2071+
expect(reqInterceptorFulfilled).toBe(true);
2072+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2073+
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
2074+
}
2075+
);
2076+
2077+
2078+
it('should increment/decrement `outstandingRequestCount` on error in response interceptor',
2079+
function() {
2080+
expect(reqInterceptorFulfilled).toBe(false);
2081+
expect(resInterceptorFulfilled).toBe(false);
2082+
expect(incOutstandingRequestCountSpy).not.toHaveBeenCalled();
2083+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
2084+
2085+
$http.get('').catch(noop);
2086+
$rootScope.$digest();
2087+
2088+
expect(reqInterceptorFulfilled).toBe(false);
2089+
expect(resInterceptorFulfilled).toBe(false);
2090+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2091+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
2092+
2093+
reqInterceptorDeferred.resolve();
2094+
$httpBackend.flush();
2095+
2096+
expect(reqInterceptorFulfilled).toBe(true);
2097+
expect(resInterceptorFulfilled).toBe(false);
2098+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2099+
expect(completeOutstandingRequestSpy).not.toHaveBeenCalled();
2100+
2101+
resInterceptorDeferred.reject();
2102+
$rootScope.$digest();
2103+
2104+
expect(reqInterceptorFulfilled).toBe(true);
2105+
expect(resInterceptorFulfilled).toBe(true);
2106+
expect(incOutstandingRequestCountSpy).toHaveBeenCalledOnce();
2107+
expect(completeOutstandingRequestSpy).toHaveBeenCalledOnce();
2108+
}
2109+
);
2110+
});
2111+
2112+
2113+
// Helpers
2114+
function setupServicesAndSpies() {
2115+
inject(function($browser, _$http_, _$httpBackend_, _$rootScope_) {
2116+
$http = _$http_;
2117+
$httpBackend = _$httpBackend_;
2118+
$rootScope = _$rootScope_;
2119+
2120+
incOutstandingRequestCountSpy =
2121+
spyOn($browser, '$$incOutstandingRequestCount').and.callThrough();
2122+
completeOutstandingRequestSpy =
2123+
spyOn($browser, '$$completeOutstandingRequest').and.callThrough();
2124+
});
2125+
}
19302126
});
19312127

19322128

0 commit comments

Comments
 (0)