Skip to content

Commit 1abbdd2

Browse files
committed
fix(ngMock/$httpBackend): correctly ignore query params in {expect,when}Route
Fixes angular#14173
1 parent a7de87d commit 1abbdd2

File tree

5 files changed

+98
-87
lines changed

5 files changed

+98
-87
lines changed

Diff for: angularFiles.js

+2
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ var angularFiles = {
130130
],
131131
'ngRoute': [
132132
'src/shallowCopy.js',
133+
'src/routeToRegExp.js',
133134
'src/ngRoute/route.js',
134135
'src/ngRoute/routeParams.js',
135136
'src/ngRoute/directive/ngView.js'
@@ -139,6 +140,7 @@ var angularFiles = {
139140
'src/ngSanitize/filter/linky.js'
140141
],
141142
'ngMock': [
143+
'src/routeToRegExp.js',
142144
'src/ngMock/angular-mocks.js',
143145
'src/ngMock/browserTrigger.js'
144146
],

Diff for: src/ngMock/angular-mocks.js

+29-42
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
/* global routeToRegExp: false */
4+
35
/**
46
* @ngdoc object
57
* @name angular.mock
@@ -1481,8 +1483,9 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
14811483
* ```
14821484
* – The respond method takes a set of static data to be returned or a function that can
14831485
* return an array containing response status (number), response data (Array|Object|string),
1484-
* response headers (Object), and the text for the status (string). The respond method returns
1485-
* the `requestHandler` object for possible overrides.
1486+
* response headers (Object), HTTP status text (string), and XMLHttpRequest status (string:
1487+
* `complete`, `error`, `timeout` or `abort`). The respond method returns the `requestHandler`
1488+
* object for possible overrides.
14861489
*/
14871490
$httpBackend.when = function(method, url, data, headers, keys) {
14881491

@@ -1663,38 +1666,21 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
16631666
* See {@link ngMock.$httpBackend#when `when`} for more info.
16641667
*/
16651668
$httpBackend.whenRoute = function(method, url) {
1666-
var pathObj = parseRoute(url);
1667-
return $httpBackend.when(method, pathObj.regexp, undefined, undefined, pathObj.keys);
1668-
};
1669-
1670-
function parseRoute(url) {
1671-
var ret = {
1672-
regexp: url
1673-
},
1674-
keys = ret.keys = [];
1675-
1676-
if (!url || !angular.isString(url)) return ret;
1677-
1678-
url = url
1679-
.replace(/([().])/g, '\\$1')
1680-
.replace(/(\/)?:(\w+)([?*])?/g, function(_, slash, key, option) {
1681-
var optional = option === '?' ? option : null;
1682-
var star = option === '*' ? option : null;
1683-
keys.push({ name: key, optional: !!optional });
1684-
slash = slash || '';
1685-
return ''
1686-
+ (optional ? '' : slash)
1687-
+ '(?:'
1688-
+ (optional ? slash : '')
1689-
+ (star && '(.+?)' || '([^/]+)')
1690-
+ (optional || '')
1691-
+ ')'
1692-
+ (optional || '');
1693-
})
1694-
.replace(/([/$*])/g, '\\$1');
1695-
1696-
ret.regexp = new RegExp('^' + url, 'i');
1697-
return ret;
1669+
var quacksLikeRegExp = parseRoute(url);
1670+
return $httpBackend.when(method, quacksLikeRegExp, undefined, undefined, quacksLikeRegExp.keys);
1671+
};
1672+
1673+
function parseRoute(route) {
1674+
var parsed = routeToRegExp(route, {caseInsensitiveMatch: true});
1675+
return {
1676+
test: function(url) {
1677+
return parsed.regexp.test(url.replace(/[?#].*/, ''));
1678+
},
1679+
exec: function(url) {
1680+
return parsed.regexp.exec(url.replace(/[?#].*/, ''));
1681+
},
1682+
keys: parsed.keys
1683+
};
16981684
}
16991685

17001686
/**
@@ -1717,14 +1703,15 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
17171703
* order to change how a matched request is handled.
17181704
*
17191705
* - respond –
1720-
* ```
1721-
* { function([status,] data[, headers, statusText])
1722-
* | function(function(method, url, data, headers, params)}
1723-
* ```
1706+
* ```js
1707+
* {function([status,] data[, headers, statusText])
1708+
* | function(function(method, url, data, headers, params)}
1709+
* ```
17241710
* – The respond method takes a set of static data to be returned or a function that can
17251711
* return an array containing response status (number), response data (Array|Object|string),
1726-
* response headers (Object), and the text for the status (string). The respond method returns
1727-
* the `requestHandler` object for possible overrides.
1712+
* response headers (Object), HTTP status text (string), and XMLHttpRequest status (string:
1713+
* `complete`, `error`, `timeout` or `abort`). The respond method returns the `requestHandler`
1714+
* object for possible overrides.
17281715
*/
17291716
$httpBackend.expect = function(method, url, data, headers, keys) {
17301717

@@ -1876,8 +1863,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
18761863
* See {@link ngMock.$httpBackend#expect `expect`} for more info.
18771864
*/
18781865
$httpBackend.expectRoute = function(method, url) {
1879-
var pathObj = parseRoute(url);
1880-
return $httpBackend.expect(method, pathObj.regexp, undefined, undefined, pathObj.keys);
1866+
var quacksLikeRegExp = parseRoute(url);
1867+
return $httpBackend.expect(method, quacksLikeRegExp, undefined, undefined, quacksLikeRegExp.keys);
18811868
};
18821869

18831870

Diff for: src/ngRoute/route.js

+2-43
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ function $RouteProvider() {
210210
}
211211
routes[path] = angular.extend(
212212
routeCopy,
213-
path && pathRegExp(path, routeCopy)
213+
path && routeToRegExp(path, routeCopy)
214214
);
215215

216216
// create redirection for trailing slashes
@@ -221,7 +221,7 @@ function $RouteProvider() {
221221

222222
routes[redirectPath] = angular.extend(
223223
{redirectTo: path},
224-
pathRegExp(redirectPath, routeCopy)
224+
routeToRegExp(redirectPath, routeCopy)
225225
);
226226
}
227227

@@ -239,47 +239,6 @@ function $RouteProvider() {
239239
*/
240240
this.caseInsensitiveMatch = false;
241241

242-
/**
243-
* @param path {string} path
244-
* @param opts {Object} options
245-
* @return {?Object}
246-
*
247-
* @description
248-
* Normalizes the given path, returning a regular expression
249-
* and the original path.
250-
*
251-
* Inspired by pathRexp in visionmedia/express/lib/utils.js.
252-
*/
253-
function pathRegExp(path, opts) {
254-
var insensitive = opts.caseInsensitiveMatch,
255-
ret = {
256-
originalPath: path,
257-
regexp: path
258-
},
259-
keys = ret.keys = [];
260-
261-
path = path
262-
.replace(/([().])/g, '\\$1')
263-
.replace(/(\/)?:(\w+)(\*\?|[?*])?/g, function(_, slash, key, option) {
264-
var optional = (option === '?' || option === '*?') ? '?' : null;
265-
var star = (option === '*' || option === '*?') ? '*' : null;
266-
keys.push({ name: key, optional: !!optional });
267-
slash = slash || '';
268-
return ''
269-
+ (optional ? '' : slash)
270-
+ '(?:'
271-
+ (optional ? slash : '')
272-
+ (star && '(.+?)' || '([^/]+)')
273-
+ (optional || '')
274-
+ ')'
275-
+ (optional || '');
276-
})
277-
.replace(/([/$*])/g, '\\$1');
278-
279-
ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
280-
return ret;
281-
}
282-
283242
/**
284243
* @ngdoc method
285244
* @name $routeProvider#otherwise

Diff for: src/routeToRegExp.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
3+
/* global routeToRegExp: true */
4+
5+
/**
6+
* @param path {string} path
7+
* @param opts {Object} options
8+
* @return {?Object}
9+
*
10+
* @description
11+
* Normalizes the given path, returning a regular expression
12+
* and the original path.
13+
*
14+
* Inspired by pathRexp in visionmedia/express/lib/utils.js.
15+
*/
16+
function routeToRegExp(path, opts) {
17+
if (!path || !angular.isString(path)) path = '';
18+
19+
var insensitive = opts && opts.caseInsensitiveMatch,
20+
ret = { originalPath: path },
21+
keys = ret.keys = [];
22+
23+
path = path
24+
.replace(/([().])/g, '\\$1')
25+
.replace(/(\/)?:(\w+)(\*\?|[?*])?/g, function(_, slash, key, option) {
26+
var optional = (option === '?' || option === '*?') ? '?' : null;
27+
var star = option === '*' || option === '*?';
28+
keys.push({ name: key, optional: !!optional });
29+
slash = slash || '';
30+
return ''
31+
+ (optional ? '' : slash)
32+
+ '(?:'
33+
+ (optional ? slash : '')
34+
+ (star ? '(.+?)' : '([^/]+)')
35+
+ (optional || '')
36+
+ ')'
37+
+ (optional || '');
38+
})
39+
.replace(/([/$*])/g, '\\$1');
40+
41+
ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
42+
return ret;
43+
}

Diff for: test/ngMock/angular-mocksSpec.js

+22-2
Original file line numberDiff line numberDiff line change
@@ -1943,10 +1943,30 @@ describe('ngMock', function() {
19431943
);
19441944
they('should ignore query param when matching in ' + routeShortcut + ' $prop method', methods,
19451945
function() {
1946-
hb[routeShortcut](this, '/route/:id').respond('path');
1946+
var paramsSpy = jasmine.createSpy('paramsSpy');
1947+
hb[routeShortcut](this, '/route/:id').respond(function(method, url, data, headers, params) {
1948+
paramsSpy(params);
1949+
// status, response, headers, statusText, xhrStatus
1950+
return [200, 'path', { 'x-header': 'foo' }, 'OK', 'complete'];
1951+
});
19471952
hb(this, '/route/123?q=str&foo=bar', undefined, callback);
19481953
hb.flush();
1949-
expect(callback).toHaveBeenCalledOnceWith(200, 'path', '', '', 'complete');
1954+
expect(callback).toHaveBeenCalledOnceWith(200, 'path', 'x-header: foo', 'OK', 'complete');
1955+
expect(paramsSpy).toHaveBeenCalledOnceWith({id: '123', q: 'str', foo: 'bar'});
1956+
}
1957+
);
1958+
they('should ignore query param when matching eager parameters in ' + routeShortcut + ' $prop method', methods,
1959+
function() {
1960+
var paramsSpy = jasmine.createSpy('paramsSpy');
1961+
hb[routeShortcut](this, '/route/:id*').respond(function(method, url, data, headers, params) {
1962+
paramsSpy(params);
1963+
// status, response, headers, statusText, xhrStatus
1964+
return [200, 'path', { 'x-header': 'foo' }, 'OK', 'complete'];
1965+
});
1966+
hb(this, '/route/123/456?q=str&foo=bar', undefined, callback);
1967+
hb.flush();
1968+
expect(callback).toHaveBeenCalledOnceWith(200, 'path', 'x-header: foo', 'OK', 'complete');
1969+
expect(paramsSpy).toHaveBeenCalledOnceWith({id: '123/456', q: 'str', foo: 'bar'});
19501970
}
19511971
);
19521972
});

0 commit comments

Comments
 (0)