diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js index 25f276e2acd4..64891d8e093a 100644 --- a/src/ngMock/angular-mocks.js +++ b/src/ngMock/angular-mocks.js @@ -1125,7 +1125,76 @@ angular.mock.dump = function(object) { $httpBackend.flush(); }); }); - ``` + ``` + * + * # Expectations and definitions with param matching + * + * The $httpBackend `when` and `expect` methods permit matching requests based on string, regex, + * or function. When using a regex, you can provide an array of key names to receive the url matches + * in the `respond` method's function parameters. This also applies to the `when` and `expect` + * shortcut methods. + * + ```js + $httpBackend.expect('GET', /\/user\/(.+)/, undefined, undefined, ['id']) + .respond(function(method, url, data, headers, params) { + // for requested url of '/user/1234' params is {id: '1234'} + + return [200, MockUserList[Number(params.id)]]; + }); + + $httpBackend.whenPATCH(/\/user\/(.+)\/article\/(.+)/, undefined, undefined, ['user', 'article']) + .respond(function(method, url, data, headers, params) { + // for url of '/user/1234/article/567' params is {user: '1234', article: '567'} + + angular.merge(MockArticleList[Number(params.article)], JSON.parse( data )); + MockUserList[Number(params.user)].lastUpdate = new Date(); + return [201, MockArticleList[Number(params.article)]]; + }); + ``` + * + * By default, query parameters on requested urls are parsed onto the params object, so a requsted url + * of `/list?q=searchstr&orderby=-name` would set params to be `{q: 'searchstr', orderby: '-name'}` + * + * For extra convenience, `whenRoute` and `expectRoute` shortcuts are available. These methods offer colon + * deliminated matching of solely the requrest url path, ignoring the query string. This allows declarations + * similar to how application routes are configured with `$routeProvider`. Because these methods convert + * the definition url to regex, declaration order is important. Combined with query parameter parsing, + * the following is possible: + * + ```js + $httpBackend.whenRoute('GET', '/users/:id') + .respond(function(method, url, data, headers, params) { + return [200, MockUserList[Number(params.id)]]; + }); + + $httpBackend.whenRoute('GET', '/users') + .respond(function(method, url, data, headers, params) { + var userList = angular.copy(MockUserList), + defaultSort = 'lastName', + count, pages, isPrevious, isNext; + + // paged api response '/v1/users?page=2' + params.page = Number(params.page) || 1; + + // query for last names '/v1/users?q=Archer' + if (params.q) { + userList = $filter('filter')({lastName: params.q}); + } + + pages = Math.ceil(userList.length / pagingLength); + isPrevious = params.page > 1; + isNext = params.page < pages; + + return [200, { + count: userList.length, + previous: isPrevious, + next: isNext, + // sort field -> '/v1/users?sortBy=firstName' + results: $filter('orderBy')(userList, params.sortBy || defaultSort) + .splice((params.page - 1) * pagingLength, pagingLength) + }]; + }); + ``` */ angular.mock.$HttpBackendProvider = function() { this.$get = ['$rootScope', '$timeout', createHttpBackendMock]; @@ -1182,7 +1251,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { return handleResponse; function handleResponse() { - var response = wrapped.response(method, url, data, headers); + var response = wrapped.response(method, url, data, headers, wrapped.params(url)); xhr.$$respHeaders = response[2]; callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(), copy(response[3] || '')); @@ -1251,20 +1320,21 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * data string and returns true if the data is as expected. * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header * object and returns true if the headers match 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 * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. * * - respond – * `{function([status,] data[, headers, statusText]) - * | function(function(method, url, data, headers)}` + * | function(function(method, url, data, headers, params)}` * – 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). The respond method returns the * `requestHandler` object for possible overrides. */ - $httpBackend.when = function(method, url, data, headers) { - var definition = new MockHttpExpectation(method, url, data, headers), + $httpBackend.when = function(method, url, data, headers, keys) { + var definition = new MockHttpExpectation(method, url, data, headers, keys), chain = { respond: function(status, data, headers, statusText) { definition.passThrough = undefined; @@ -1294,6 +1364,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * @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. * @returns {requestHandler} Returns an object with `respond` method that controls how a matched * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. @@ -1308,6 +1379,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * @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. * @returns {requestHandler} Returns an object with `respond` method that controls how a matched * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. @@ -1322,6 +1394,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * @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. * @returns {requestHandler} Returns an object with `respond` method that controls how a matched * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. @@ -1338,6 +1411,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives * data string and returns true if the data is as expected. * @param {(Object|function(Object))=} headers HTTP headers. + * @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 * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. @@ -1354,6 +1428,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives * data string and returns true if the data is as expected. * @param {(Object|function(Object))=} headers HTTP headers. + * @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 * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. @@ -1367,12 +1442,59 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * * @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 * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. */ createShortMethods('when'); + /** + * @ngdoc method + * @name $httpBackend#whenRoute + * @description + * Creates a new backend definition that compares only with the requested route. + * + * @param {string} method HTTP method. + * @param {string} url HTTP url string that supports colon param matching. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. See #when for more info. + */ + $httpBackend.whenRoute = function(method, url) { + var pathObj = parseRoute(url); + return $httpBackend.when(method, pathObj.regexp, undefined, undefined, pathObj.keys); + }; + + function parseRoute(url) { + var ret = { + regexp: url + }, + keys = ret.keys = []; + + if (!url || !angular.isString(url)) return ret; + + url = url + .replace(/([().])/g, '\\$1') + .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) { + var optional = option === '?' ? option : null; + var star = option === '*' ? option : null; + keys.push({ name: key, optional: !!optional }); + slash = slash || ''; + return '' + + (optional ? '' : slash) + + '(?:' + + (optional ? slash : '') + + (star && '(.+?)' || '([^/]+)') + + (optional || '') + + ')' + + (optional || ''); + }) + .replace(/([\/$\*])/g, '\\$1'); + + ret.regexp = new RegExp('^' + url, 'i'); + return ret; + } /** * @ngdoc method @@ -1388,20 +1510,21 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * is in JSON format. * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header * object and returns true if the headers match the current expectation. + * @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 * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. * * - respond – * `{function([status,] data[, headers, statusText]) - * | function(function(method, url, data, headers)}` + * | function(function(method, url, data, headers, params)}` * – 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). The respond method returns the * `requestHandler` object for possible overrides. */ - $httpBackend.expect = function(method, url, data, headers) { - var expectation = new MockHttpExpectation(method, url, data, headers), + $httpBackend.expect = function(method, url, data, headers, keys) { + var expectation = new MockHttpExpectation(method, url, data, headers, keys), chain = { respond: function(status, data, headers, statusText) { expectation.response = createResponse(status, data, headers, statusText); @@ -1413,7 +1536,6 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { return chain; }; - /** * @ngdoc method * @name $httpBackend#expectGET @@ -1423,6 +1545,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * @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. * @returns {requestHandler} Returns an object with `respond` method that controls how a matched * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. See #expect for more info. @@ -1437,6 +1560,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * @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. * @returns {requestHandler} Returns an object with `respond` method that controls how a matched * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. @@ -1451,6 +1575,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * @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. * @returns {requestHandler} Returns an object with `respond` method that controls how a matched * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. @@ -1468,6 +1593,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * receives data string and returns true if the data is as expected, or Object if request body * is in JSON format. * @param {Object=} headers HTTP headers. + * @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 * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. @@ -1485,6 +1611,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * receives data string and returns true if the data is as expected, or Object if request body * is in JSON format. * @param {Object=} headers HTTP headers. + * @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 * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. @@ -1502,6 +1629,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * receives data string and returns true if the data is as expected, or Object if request body * is in JSON format. * @param {Object=} headers HTTP headers. + * @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 * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. @@ -1515,12 +1643,30 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * * @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 * request is handled. You can save this object for later use and invoke `respond` again in * order to change how a matched request is handled. */ createShortMethods('expect'); + /** + * @ngdoc method + * @name $httpBackend#expectRoute + * @description + * Creates a new request expectation that compares only with the requested route. + * + * @param {string} method HTTP method. + * @param {string} url HTTP url string that supports colon param matching. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. See #expect for more info. + */ + $httpBackend.expectRoute = function(method, url) { + var pathObj = parseRoute(url); + return $httpBackend.expect(method, pathObj.regexp, undefined, undefined, pathObj.keys); + }; + /** * @ngdoc method @@ -1610,20 +1756,20 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { function createShortMethods(prefix) { angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) { - $httpBackend[prefix + method] = function(url, headers) { - return $httpBackend[prefix](method, url, undefined, headers); + $httpBackend[prefix + method] = function(url, headers, keys) { + return $httpBackend[prefix](method, url, undefined, headers, keys); }; }); angular.forEach(['PUT', 'POST', 'PATCH'], function(method) { - $httpBackend[prefix + method] = function(url, data, headers) { - return $httpBackend[prefix](method, url, data, headers); + $httpBackend[prefix + method] = function(url, data, headers, keys) { + return $httpBackend[prefix](method, url, data, headers, keys); }; }); } } -function MockHttpExpectation(method, url, data, headers) { +function MockHttpExpectation(method, url, data, headers, keys) { this.data = data; this.headers = headers; @@ -1662,6 +1808,59 @@ function MockHttpExpectation(method, url, data, headers) { this.toString = function() { return method + ' ' + url; }; + + this.params = function(u) { + return angular.extend(parseQuery(), pathParams()); + + function pathParams() { + var keyObj = {}; + if (!url || !angular.isFunction(url.test) || !keys || keys.length === 0) return keyObj; + + var m = url.exec(u); + if (!m) return keyObj; + for (var i = 1, len = m.length; i < len; ++i) { + var key = keys[i - 1]; + var val = m[i]; + if (key && val) { + keyObj[key.name || key] = val; + } + } + + return keyObj; + } + + function parseQuery() { + var obj = {}, key_value, key, + queryStr = u.indexOf('?') > -1 + ? u.substring(u.indexOf('?') + 1) + : ""; + + angular.forEach(queryStr.split('&'), function(keyValue) { + if (keyValue) { + key_value = keyValue.replace(/\+/g,'%20').split('='); + key = tryDecodeURIComponent(key_value[0]); + if (angular.isDefined(key)) { + var val = angular.isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true; + if (!hasOwnProperty.call(obj, key)) { + obj[key] = val; + } else if (angular.isArray(obj[key])) { + obj[key].push(val); + } else { + obj[key] = [obj[key],val]; + } + } + } + }); + return obj; + } + function tryDecodeURIComponent(value) { + try { + return decodeURIComponent(value); + } catch (e) { + // Ignore any invalid uri component + } + } + }; } function createMockXhr() { @@ -1986,13 +2185,15 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { * @param {(string|RegExp)=} data HTTP request body. * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header * object and returns true if the headers match the current definition. + * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on + * {@link ngMock.$httpBackend $httpBackend mock}. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that * control how a matched request is handled. You can save this object for later use and invoke * `respond` or `passThrough` again in order to change how a matched request is handled. * * - respond – * `{function([status,] data[, headers, statusText]) - * | function(function(method, url, data, headers)}` + * | function(function(method, url, data, headers, params)}` * – 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). @@ -2012,6 +2213,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { * @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 + * {@link ngMock.$httpBackend $httpBackend mock}. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that * control how a matched request is handled. You can save this object for later use and invoke * `respond` or `passThrough` again in order to change how a matched request is handled. @@ -2027,6 +2230,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { * @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 + * {@link ngMock.$httpBackend $httpBackend mock}. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that * control how a matched request is handled. You can save this object for later use and invoke * `respond` or `passThrough` again in order to change how a matched request is handled. @@ -2042,6 +2247,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { * @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 + * {@link ngMock.$httpBackend $httpBackend mock}. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that * control how a matched request is handled. You can save this object for later use and invoke * `respond` or `passThrough` again in order to change how a matched request is handled. @@ -2058,6 +2265,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { * and returns true if the url matches the current definition. * @param {(string|RegExp)=} data HTTP request body. * @param {(Object|function(Object))=} headers HTTP headers. + * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on + * {@link ngMock.$httpBackend $httpBackend mock}. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that * control how a matched request is handled. You can save this object for later use and invoke * `respond` or `passThrough` again in order to change how a matched request is handled. @@ -2074,6 +2283,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { * and returns true if the url matches the current definition. * @param {(string|RegExp)=} data HTTP request body. * @param {(Object|function(Object))=} headers HTTP headers. + * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on + * {@link ngMock.$httpBackend $httpBackend mock}. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that * control how a matched request is handled. You can save this object for later use and invoke * `respond` or `passThrough` again in order to change how a matched request is handled. @@ -2090,6 +2301,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { * and returns true if the url matches the current definition. * @param {(string|RegExp)=} data HTTP request body. * @param {(Object|function(Object))=} headers HTTP headers. + * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on + * {@link ngMock.$httpBackend $httpBackend mock}. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that * control how a matched request is handled. You can save this object for later use and invoke * `respond` or `passThrough` again in order to change how a matched request is handled. @@ -2104,6 +2317,21 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { * * @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}. + * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. + */ +/** + * @ngdoc method + * @name $httpBackend#whenRoute + * @module ngMockE2E + * @description + * Creates a new backend definition that compares only with the requested route. + * + * @param {string} method HTTP method. + * @param {string} url HTTP url string that supports colon param matching. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that * control how a matched request is handled. You can save this object for later use and invoke * `respond` or `passThrough` again in order to change how a matched request is handled. diff --git a/test/ngMock/angular-mocksSpec.js b/test/ngMock/angular-mocksSpec.js index 293136bf818c..0204eb412057 100644 --- a/test/ngMock/angular-mocksSpec.js +++ b/test/ngMock/angular-mocksSpec.js @@ -963,6 +963,11 @@ describe('ngMock', function() { expect(typeof hb.whenHEAD).toBe("function"); }); + it('should provide "route" shortcuts for expect and when', function() { + expect(typeof hb.whenRoute).toBe("function"); + expect(typeof hb.expectRoute).toBe("function"); + }); + it('should respond with first matched definition', function() { hb.when('GET', '/url1').respond(200, 'content', {}); @@ -1143,14 +1148,38 @@ describe('ngMock', function() { }); 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'}, 'Moved Permanently']; + hb.expect('GET', '/some?q=s').respond(function(m, u, d, h, p) { + return [301, m + u + ';' + d + ';a=' + h.a + ';q=' + p.q, {'Connection': 'keep-alive'}, 'Moved Permanently']; + }); + + hb('GET', '/some?q=s', 'data', callback, {a: 'b'}); + hb.flush(); + + expect(callback).toHaveBeenCalledOnceWith(301, 'GET/some?q=s;data;a=b;q=s', 'Connection: keep-alive', 'Moved Permanently'); + }); + + it('should decode query parameters in respond() function', function() { + hb.expect('GET', '/url?query=l%E2%80%A2ng%20string%20w%2F%20spec%5Eal%20char%24&id=1234&orderBy=-name') + .respond(function(m, u, d, h, p) { + return [200, "id=" + p.id + ";orderBy=" + p.orderBy + ";query=" + p.query]; + }); + + hb('GET', '/url?query=l%E2%80%A2ng%20string%20w%2F%20spec%5Eal%20char%24&id=1234&orderBy=-name', null, callback); + hb.flush(); + + expect(callback).toHaveBeenCalledOnceWith(200, 'id=1234;orderBy=-name;query=l•ng string w/ spec^al char$', '', ''); + }); + + it('should include regex captures in respond() params when keys provided', function() { + hb.expect('GET', /\/(.+)\/article\/(.+)/, undefined, undefined, ['id', 'name']) + .respond(function(m, u, d, h, p) { + return [200, "id=" + p.id + ";name=" + p.name]; }); - hb('GET', '/some', 'data', callback, {a: 'b'}); + hb('GET', '/1234/article/cool-angular-article', null, callback); hb.flush(); - expect(callback).toHaveBeenCalledOnceWith(301, 'GET/some;data;a=b', 'Connection: keep-alive', 'Moved Permanently'); + expect(callback).toHaveBeenCalledOnceWith(200, 'id=1234;name=cool-angular-article', '', ''); }); it('should default response headers to ""', function() { @@ -1515,6 +1544,35 @@ describe('ngMock', function() { }); }); + describe('expectRoute/whenRoute shortcuts', function() { + angular.forEach(['expectRoute', 'whenRoute'], function(routeShortcut) { + var methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'JSONP']; + they('should provide ' + routeShortcut + ' shortcut with $prop method', methods, + function() { + hb[routeShortcut](this, '/route').respond('path'); + hb(this, '/route', undefined, callback); + hb.flush(); + expect(callback).toHaveBeenCalledOnceWith(200, 'path', '', ''); + } + ); + they('should match colon deliminated parameters in ' + routeShortcut + ' $prop method', methods, + function() { + hb[routeShortcut](this, '/route/:id/path/:s_id').respond('path'); + hb(this, '/route/123/path/456', undefined, callback); + hb.flush(); + expect(callback).toHaveBeenCalledOnceWith(200, 'path', '', ''); + } + ); + they('should ignore query param when matching in ' + routeShortcut + ' $prop method', methods, + function() { + hb[routeShortcut](this, '/route/:id').respond('path'); + hb(this, '/route/123?q=str&foo=bar', undefined, callback); + hb.flush(); + expect(callback).toHaveBeenCalledOnceWith(200, 'path', '', ''); + } + ); + }); + }); describe('MockHttpExpectation', function() { /* global MockHttpExpectation */