fix(ngMock/$httpBackend): fail if a url is provided but is undefined

While the `url` parameter is optional for `$httpBackend.when`,
`$httpBackend.expect` and related shortcut methods, it should not have the
value of `undefined` if it has been provided.

This change ensures that an error is thrown in those cases.

Closes #8442
Closes #8462
Closes #10934
Closes #12777

BREAKING CHANGE

It is no longer valid to explicitly pass `undefined` as the `url` argument
to any of the `$httpBackend.when...()` and `$httpBackend.expect...()`
methods.

While this argument is optional, it must have a defined value if it is
provided.

Previously passing an explicit `undefined` value was ignored but this
lead to invalid tests passing unexpectedly.
This commit is contained in:
Peter Bacon Darwin
2015-09-07 13:33:51 +01:00
parent f467dc3dd5
commit 7551b8975a
2 changed files with 137 additions and 23 deletions
+46 -23
View File
@@ -1423,7 +1423,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* Creates a new backend definition.
*
* @param {string} method HTTP method.
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
* data string and returns true if the data is as expected.
@@ -1445,6 +1445,9 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* the `requestHandler` object for possible overrides.
*/
$httpBackend.when = function(method, url, data, headers, keys) {
assertArgDefined(arguments, 1, 'url');
var definition = new MockHttpExpectation(method, url, data, headers, keys),
chain = {
respond: function(status, data, headers, statusText) {
@@ -1472,7 +1475,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* @description
* Creates a new backend definition for GET requests. For more info see `when()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
@@ -1487,7 +1490,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* @description
* Creates a new backend definition for HEAD requests. For more info see `when()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
@@ -1502,7 +1505,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* @description
* Creates a new backend definition for DELETE requests. For more info see `when()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
@@ -1517,7 +1520,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* @description
* Creates a new backend definition for POST requests. For more info see `when()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
* data string and returns true if the data is as expected.
@@ -1534,7 +1537,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* @description
* Creates a new backend definition for PUT requests. For more info see `when()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
* data string and returns true if the data is as expected.
@@ -1551,7 +1554,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* @description
* Creates a new backend definition for JSONP requests. For more info see `when()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
@@ -1614,7 +1617,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* Creates a new request expectation.
*
* @param {string} method HTTP method.
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
* receives data string and returns true if the data is as expected, or Object if request body
@@ -1637,6 +1640,9 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* the `requestHandler` object for possible overrides.
*/
$httpBackend.expect = function(method, url, data, headers, keys) {
assertArgDefined(arguments, 1, 'url');
var expectation = new MockHttpExpectation(method, url, data, headers, keys),
chain = {
respond: function(status, data, headers, statusText) {
@@ -1655,7 +1661,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* @description
* Creates a new request expectation for GET requests. For more info see `expect()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {Object=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
@@ -1670,7 +1676,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* @description
* Creates a new request expectation for HEAD requests. For more info see `expect()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {Object=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
@@ -1685,7 +1691,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* @description
* Creates a new request expectation for DELETE requests. For more info see `expect()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {Object=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
@@ -1700,7 +1706,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* @description
* Creates a new request expectation for POST requests. For more info see `expect()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
* receives data string and returns true if the data is as expected, or Object if request body
@@ -1718,7 +1724,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* @description
* Creates a new request expectation for PUT requests. For more info see `expect()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
* receives data string and returns true if the data is as expected, or Object if request body
@@ -1736,7 +1742,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* @description
* Creates a new request expectation for PATCH requests. For more info see `expect()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
* receives data string and returns true if the data is as expected, or Object if request body
@@ -1754,7 +1760,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* @description
* Creates a new request expectation for JSONP requests. For more info see `expect()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives an url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives an url
* and returns true if the url matches the current definition.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
@@ -1870,18 +1876,35 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
function createShortMethods(prefix) {
angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) {
$httpBackend[prefix + method] = function(url, headers, keys) {
assertArgDefined(arguments, 0, 'url');
// Change url to `null` if `undefined` to stop it throwing an exception further down
if (angular.isUndefined(url)) url = null;
return $httpBackend[prefix](method, url, undefined, headers, keys);
};
});
angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
$httpBackend[prefix + method] = function(url, data, headers, keys) {
assertArgDefined(arguments, 0, 'url');
// Change url to `null` if `undefined` to stop it throwing an exception further down
if (angular.isUndefined(url)) url = null;
return $httpBackend[prefix](method, url, data, headers, keys);
};
});
}
}
function assertArgDefined(args, index, name) {
if (args.length > index && angular.isUndefined(args[index])) {
throw new Error("Undefined argument `" + name + "`; the argument is provided but not defined");
}
}
function MockHttpExpectation(method, url, data, headers, keys) {
function getUrlParams(u) {
@@ -2462,7 +2485,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
* Creates a new backend definition.
*
* @param {string} method HTTP method.
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp)=} data HTTP request body.
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
@@ -2494,7 +2517,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
* @description
* Creates a new backend definition for GET requests. For more info see `when()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
@@ -2511,7 +2534,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
* @description
* Creates a new backend definition for HEAD requests. For more info see `when()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
@@ -2528,7 +2551,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
* @description
* Creates a new backend definition for DELETE requests. For more info see `when()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
@@ -2545,7 +2568,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
* @description
* Creates a new backend definition for POST requests. For more info see `when()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp)=} data HTTP request body.
* @param {(Object|function(Object))=} headers HTTP headers.
@@ -2563,7 +2586,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
* @description
* Creates a new backend definition for PUT requests. For more info see `when()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp)=} data HTTP request body.
* @param {(Object|function(Object))=} headers HTTP headers.
@@ -2581,7 +2604,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
* @description
* Creates a new backend definition for PATCH requests. For more info see `when()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp)=} data HTTP request body.
* @param {(Object|function(Object))=} headers HTTP headers.
@@ -2599,7 +2622,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
* @description
* Creates a new backend definition for JSONP requests. For more info see `when()`.
*
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
* {@link ngMock.$httpBackend $httpBackend mock}.
+91
View File
@@ -1173,6 +1173,97 @@ describe('ngMock', function() {
});
it('should not error if the url is not provided', function() {
expect(function() {
hb.when('GET');
hb.whenGET();
hb.whenPOST();
hb.whenPUT();
hb.whenPATCH();
hb.whenDELETE();
hb.whenHEAD();
hb.expect('GET');
hb.expectGET();
hb.expectPOST();
hb.expectPUT();
hb.expectPATCH();
hb.expectDELETE();
hb.expectHEAD();
}).not.toThrow();
});
it('should error if the url is undefined', function() {
expect(function() {
hb.when('GET', undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.whenGET(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.whenDELETE(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.whenJSONP(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.whenHEAD(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.whenPATCH(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.whenPOST(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.whenPUT(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.expect('GET', undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.expectGET(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.expectDELETE(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.expectJSONP(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.expectHEAD(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.expectPATCH(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.expectPOST(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
expect(function() {
hb.expectPUT(undefined);
}).toThrowError('Undefined argument `url`; the argument is provided but not defined');
});
it('should preserve the order of requests', function() {
hb.when('GET', '/url1').respond(200, 'first');
hb.when('GET', '/url2').respond(201, 'second');