From f16bd2f747ed94547eabdc4c337775a22a365255 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Wed, 15 Feb 2012 15:45:07 -0800 Subject: refactor($route): move when/otherwise to provider --- src/service/route.js | 145 ++++----- test/service/routeParamsSpec.js | 24 +- test/service/routeSpec.js | 677 ++++++++++++++++++++++------------------ test/widgetsSpec.js | 244 ++++++++------- 4 files changed, 592 insertions(+), 498 deletions(-) diff --git a/src/service/route.js b/src/service/route.js index 932e26d5..7cdcfd05 100644 --- a/src/service/route.js +++ b/src/service/route.js @@ -63,6 +63,78 @@ */ function $RouteProvider(){ + var routes = {}; + + /** + * @ngdoc method + * @name angular.module.ng.$route#when + * @methodOf angular.module.ng.$route + * + * @param {string} path Route path (matched against `$location.hash`) + * @param {Object} route Mapping information to be assigned to `$route.current` on route + * match. + * + * Object properties: + * + * - `controller` – `{function()=}` – Controller fn that should be associated with newly + * created scope. + * - `template` – `{string=}` – path to an html template that should be used by + * {@link angular.module.ng.$compileProvider.directive.ng:view ng:view} or + * {@link angular.module.ng.$compileProvider.directive.ng:include ng:include} widgets. + * - `redirectTo` – {(string|function())=} – value to update + * {@link angular.module.ng.$location $location} path with and trigger route redirection. + * + * If `redirectTo` is a function, it will be called with the following parameters: + * + * - `{Object.}` - route parameters extracted from the current + * `$location.path()` by applying the current route template. + * - `{string}` - current `$location.path()` + * - `{Object}` - current `$location.search()` + * + * The custom `redirectTo` function is expected to return a string which will be used + * to update `$location.path()` and `$location.search()`. + * + * - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search() + * changes. + * + * If the option is set to false and url in the browser changes, then + * $routeUpdate event is emited on the current route scope. You can use this event to + * react to {@link angular.module.ng.$routeParams} changes: + * + * function MyCtrl($route, $routeParams) { + * this.$on('$routeUpdate', function() { + * // do stuff with $routeParams + * }); + * } + * + * @returns {Object} route object + * + * @description + * Adds a new route definition to the `$route` service. + */ + this.when = function(path, route) { + var routeDef = routes[path]; + if (!routeDef) routeDef = routes[path] = {reloadOnSearch: true}; + if (route) extend(routeDef, route); // TODO(im): what the heck? merge two route definitions? + return routeDef; + }; + + /** + * @ngdoc method + * @name angular.module.ng.$route#otherwise + * @methodOf angular.module.ng.$route + * + * @description + * Sets route definition that will be used on route change when no other route definition + * is matched. + * + * @param {Object} params Mapping information to be assigned to `$route.current`. + */ + this.otherwise = function(params) { + this.when(null, params); + }; + + this.$get = ['$rootScope', '$location', '$routeParams', '$controller', function( $rootScope, $location, $routeParams, $controller) { /** @@ -112,8 +184,7 @@ function $RouteProvider(){ * instance of the Controller. */ - var routes = {}, - matcher = switchRouteMatcher, + var matcher = switchRouteMatcher, parentScope = $rootScope, dirty = 0, forceReload = false, @@ -136,76 +207,6 @@ function $RouteProvider(){ if (scope) parentScope = scope; }, - /** - * @ngdoc method - * @name angular.module.ng.$route#when - * @methodOf angular.module.ng.$route - * - * @param {string} path Route path (matched against `$location.hash`) - * @param {Object} route Mapping information to be assigned to `$route.current` on route - * match. - * - * Object properties: - * - * - `controller` – `{function()=}` – Controller fn that should be associated with newly - * created scope. - * - `template` – `{string=}` – path to an html template that should be used by - * {@link angular.module.ng.$compileProvider.directive.ng:view ng:view} or - * {@link angular.module.ng.$compileProvider.directive.ng:include ng:include} widgets. - * - `redirectTo` – {(string|function())=} – value to update - * {@link angular.module.ng.$location $location} path with and trigger route redirection. - * - * If `redirectTo` is a function, it will be called with the following parameters: - * - * - `{Object.}` - route parameters extracted from the current - * `$location.path()` by applying the current route template. - * - `{string}` - current `$location.path()` - * - `{Object}` - current `$location.search()` - * - * The custom `redirectTo` function is expected to return a string which will be used - * to update `$location.path()` and `$location.search()`. - * - * - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search() - * changes. - * - * If the option is set to false and url in the browser changes, then - * $routeUpdate event is emited on the current route scope. You can use this event to - * react to {@link angular.module.ng.$routeParams} changes: - * - * function MyCtrl($route, $routeParams) { - * this.$on('$routeUpdate', function() { - * // do stuff with $routeParams - * }); - * } - * - * @returns {Object} route object - * - * @description - * Adds a new route definition to the `$route` service. - */ - when: function(path, route) { - var routeDef = routes[path]; - if (!routeDef) routeDef = routes[path] = {reloadOnSearch: true}; - if (route) extend(routeDef, route); // TODO(im): what the heck? merge two route definitions? - dirty++; - return routeDef; - }, - - /** - * @ngdoc method - * @name angular.module.ng.$route#otherwise - * @methodOf angular.module.ng.$route - * - * @description - * Sets route definition that will be used on route change when no other route definition - * is matched. - * - * @param {Object} params Mapping information to be assigned to `$route.current`. - */ - otherwise: function(params) { - $route.when(null, params); - }, - /** * @ngdoc method * @name angular.module.ng.$route#reload diff --git a/test/service/routeParamsSpec.js b/test/service/routeParamsSpec.js index d4088767..d1b2ecb1 100644 --- a/test/service/routeParamsSpec.js +++ b/test/service/routeParamsSpec.js @@ -1,16 +1,20 @@ 'use strict'; describe('$routeParams', function() { - it('should publish the params into a service', inject(function($rootScope, $route, $location, $routeParams) { - $route.when('/foo'); - $route.when('/bar/:barId'); + it('should publish the params into a service', function() { + module(function($routeProvider) { + $routeProvider.when('/foo'); + $routeProvider.when('/bar/:barId'); + }); - $location.path('/foo').search('a=b'); - $rootScope.$digest(); - expect($routeParams).toEqual({a:'b'}); + inject(function($rootScope, $route, $location, $routeParams) { + $location.path('/foo').search('a=b'); + $rootScope.$digest(); + expect($routeParams).toEqual({a:'b'}); - $location.path('/bar/123').search('x=abc'); - $rootScope.$digest(); - expect($routeParams).toEqual({barId:'123', x:'abc'}); - })); + $location.path('/bar/123').search('x=abc'); + $rootScope.$digest(); + expect($routeParams).toEqual({barId:'123', x:'abc'}); + }); + }); }); diff --git a/test/service/routeSpec.js b/test/service/routeSpec.js index d6f7d753..6c6828bc 100644 --- a/test/service/routeSpec.js +++ b/test/service/routeSpec.js @@ -1,7 +1,7 @@ 'use strict'; describe('$route', function() { - it('should route and fire change event', inject(function($route, $location, $rootScope) { + it('should route and fire change event', function() { var log = '', lastRoute, nextRoute; @@ -9,367 +9,415 @@ describe('$route', function() { function BookChapter() { log += ';'; } - - $route.when('/Book/:book/Chapter/:chapter', - {controller: BookChapter, template: 'Chapter.html'}); - $route.when('/Blank'); - $rootScope.$on('$beforeRouteChange', function(event, next, current) { - log += 'before();'; - expect(current).toBe($route.current); - lastRoute = current; - nextRoute = next; - }); - $rootScope.$on('$afterRouteChange', function(event, current, last) { - log += 'after();'; - expect(current).toBe($route.current); - expect(lastRoute).toBe(last); - expect(nextRoute).toBe(current); + module(function($routeProvider) { + $routeProvider.when('/Book/:book/Chapter/:chapter', + {controller: BookChapter, template: 'Chapter.html'}); + $routeProvider.when('/Blank'); }); + inject(function($route, $location, $rootScope) { + $rootScope.$on('$beforeRouteChange', function(event, next, current) { + log += 'before();'; + expect(current).toBe($route.current); + lastRoute = current; + nextRoute = next; + }); + $rootScope.$on('$afterRouteChange', function(event, current, last) { + log += 'after();'; + expect(current).toBe($route.current); + expect(lastRoute).toBe(last); + expect(nextRoute).toBe(current); + }); - $location.path('/Book/Moby/Chapter/Intro').search('p=123'); - $rootScope.$digest(); - expect(log).toEqual('before();;after();'); - expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', p:'123'}); - var lastId = $route.current.scope.$id; - - log = ''; - $location.path('/Blank').search('ignore'); - $rootScope.$digest(); - expect(log).toEqual('before();after();'); - expect($route.current.params).toEqual({ignore:true}); - expect($route.current.scope.$id).not.toEqual(lastId); + $location.path('/Book/Moby/Chapter/Intro').search('p=123'); + $rootScope.$digest(); + expect(log).toEqual('before();;after();'); + expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', p:'123'}); + var lastId = $route.current.scope.$id; - log = ''; - $location.path('/NONE'); - $rootScope.$digest(); - expect(log).toEqual('before();after();'); - expect($route.current).toEqual(null); + log = ''; + $location.path('/Blank').search('ignore'); + $rootScope.$digest(); + expect(log).toEqual('before();after();'); + expect($route.current.params).toEqual({ignore:true}); + expect($route.current.scope.$id).not.toEqual(lastId); - $route.when('/NONE', {template:'instant update'}); - $rootScope.$digest(); - expect($route.current.template).toEqual('instant update'); - })); + log = ''; + $location.path('/NONE'); + $rootScope.$digest(); + expect(log).toEqual('before();after();'); + expect($route.current).toEqual(null); + }); + }); - it('should match a route that contains special chars in the path', - inject(function($route, $location, $rootScope) { - $route.when('/$test.23/foo(bar)/:baz', {template: 'test.html'}); + it('should match a route that contains special chars in the path', function() { + module(function($routeProvider) { + $routeProvider.when('/$test.23/foo(bar)/:baz', {template: 'test.html'}); + }); + inject(function($route, $location, $rootScope) { - $location.path('/test'); - $rootScope.$digest(); - expect($route.current).toBeUndefined(); + $location.path('/test'); + $rootScope.$digest(); + expect($route.current).toBeUndefined(); - $location.path('/$testX23/foo(bar)/222'); - $rootScope.$digest(); - expect($route.current).toBeUndefined(); + $location.path('/$testX23/foo(bar)/222'); + $rootScope.$digest(); + expect($route.current).toBeUndefined(); - $location.path('/$test.23/foo(bar)/222'); - $rootScope.$digest(); - expect($route.current).toBeDefined(); + $location.path('/$test.23/foo(bar)/222'); + $rootScope.$digest(); + expect($route.current).toBeDefined(); - $location.path('/$test.23/foo\\(bar)/222'); - $rootScope.$digest(); - expect($route.current).toBeUndefined(); - })); + $location.path('/$test.23/foo\\(bar)/222'); + $rootScope.$digest(); + expect($route.current).toBeUndefined(); + }); + }); - it('should change route even when only search param changes', - inject(function($route, $location, $rootScope) { - var callback = jasmine.createSpy('onRouteChange'); + it('should change route even when only search param changes', function() { + module(function($routeProvider) { + $routeProvider.when('/test', {template: 'test.html'}); + }); - $route.when('/test', {template: 'test.html'}); - $rootScope.$on('$beforeRouteChange', callback); - $location.path('/test'); - $rootScope.$digest(); - callback.reset(); + inject(function($route, $location, $rootScope) { + var callback = jasmine.createSpy('onRouteChange'); - $location.search({any: true}); - $rootScope.$digest(); + $rootScope.$on('$beforeRouteChange', callback); + $location.path('/test'); + $rootScope.$digest(); + callback.reset(); - expect(callback).toHaveBeenCalled(); - })); + $location.search({any: true}); + $rootScope.$digest(); + expect(callback).toHaveBeenCalled(); + }); + }); - it('should allow routes to be defined with just templates without controllers', - inject(function($route, $location, $rootScope) { - var onChangeSpy = jasmine.createSpy('onChange'); - $route.when('/foo', {template: 'foo.html'}); - $rootScope.$on('$beforeRouteChange', onChangeSpy); - expect($route.current).toBeUndefined(); - expect(onChangeSpy).not.toHaveBeenCalled(); + it('should allow routes to be defined with just templates without controllers', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {template: 'foo.html'}); + }); - $location.path('/foo'); - $rootScope.$digest(); + inject(function($route, $location, $rootScope) { + var onChangeSpy = jasmine.createSpy('onChange'); - expect($route.current.template).toEqual('foo.html'); - expect($route.current.controller).toBeUndefined(); - expect(onChangeSpy).toHaveBeenCalled(); - })); + $rootScope.$on('$beforeRouteChange', onChangeSpy); + expect($route.current).toBeUndefined(); + expect(onChangeSpy).not.toHaveBeenCalled(); + $location.path('/foo'); + $rootScope.$digest(); - it('should handle unknown routes with "otherwise" route definition', - inject(function($route, $location, $rootScope) { - var onChangeSpy = jasmine.createSpy('onChange'); + expect($route.current.template).toEqual('foo.html'); + expect($route.current.controller).toBeUndefined(); + expect(onChangeSpy).toHaveBeenCalled(); + }); + }); - function NotFoundCtrl($scope) {$scope.notFoundProp = 'not found!';} - $route.when('/foo', {template: 'foo.html'}); - $route.otherwise({template: '404.html', controller: NotFoundCtrl}); - $rootScope.$on('$beforeRouteChange', onChangeSpy); - expect($route.current).toBeUndefined(); - expect(onChangeSpy).not.toHaveBeenCalled(); + it('should handle unknown routes with "otherwise" route definition', function() { + function NotFoundCtrl($scope) { + $scope.notFoundProp = 'not found!'; + } - $location.path('/unknownRoute'); - $rootScope.$digest(); + module(function($routeProvider){ + $routeProvider.when('/foo', {template: 'foo.html'}); + $routeProvider.otherwise({template: '404.html', controller: NotFoundCtrl}); + }); - expect($route.current.template).toBe('404.html'); - expect($route.current.controller).toBe(NotFoundCtrl); - expect($route.current.scope.notFoundProp).toBe('not found!'); - expect(onChangeSpy).toHaveBeenCalled(); + inject(function($route, $location, $rootScope) { + var onChangeSpy = jasmine.createSpy('onChange'); - onChangeSpy.reset(); - $location.path('/foo'); - $rootScope.$digest(); + $rootScope.$on('$beforeRouteChange', onChangeSpy); + expect($route.current).toBeUndefined(); + expect(onChangeSpy).not.toHaveBeenCalled(); - expect($route.current.template).toEqual('foo.html'); - expect($route.current.controller).toBeUndefined(); - expect($route.current.scope.notFoundProp).toBeUndefined(); - expect(onChangeSpy).toHaveBeenCalled(); - })); + $location.path('/unknownRoute'); + $rootScope.$digest(); + expect($route.current.template).toBe('404.html'); + expect($route.current.controller).toBe(NotFoundCtrl); + expect($route.current.scope.notFoundProp).toBe('not found!'); + expect(onChangeSpy).toHaveBeenCalled(); - it('should $destroy old routes', inject(function($route, $location, $rootScope) { - $route.when('/foo', {template: 'foo.html', controller: function() {this.name = 'FOO';}}); - $route.when('/bar', {template: 'bar.html', controller: function() {this.name = 'BAR';}}); - $route.when('/baz', {template: 'baz.html'}); + onChangeSpy.reset(); + $location.path('/foo'); + $rootScope.$digest(); - expect($rootScope.$childHead).toEqual(null); + expect($route.current.template).toEqual('foo.html'); + expect($route.current.controller).toBeUndefined(); + expect($route.current.scope.notFoundProp).toBeUndefined(); + expect(onChangeSpy).toHaveBeenCalled(); + }); + }); - $location.path('/foo'); - $rootScope.$digest(); - expect($rootScope.$$childHead.$id).toBeTruthy(); - expect($rootScope.$$childHead.$id).toEqual($rootScope.$$childTail.$id); - $location.path('/bar'); - $rootScope.$digest(); - expect($rootScope.$$childHead.$id).toBeTruthy(); - expect($rootScope.$$childHead.$id).toEqual($rootScope.$$childTail.$id); + it('should $destroy old routes', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {template: 'foo.html', controller: function() {this.name = 'FOO';}}); + $routeProvider.when('/bar', {template: 'bar.html', controller: function() {this.name = 'BAR';}}); + $routeProvider.when('/baz', {template: 'baz.html'}); + }); - $location.path('/baz'); - $rootScope.$digest(); - expect($rootScope.$$childHead.$id).toBeTruthy(); - expect($rootScope.$$childHead.$id).toEqual($rootScope.$$childTail.$id); + inject(function($route, $location, $rootScope) { + expect($rootScope.$childHead).toEqual(null); - $location.path('/'); - $rootScope.$digest(); - expect($rootScope.$$childHead).toEqual(null); - expect($rootScope.$$childTail).toEqual(null); - })); + $location.path('/foo'); + $rootScope.$digest(); + expect($rootScope.$$childHead.$id).toBeTruthy(); + expect($rootScope.$$childHead.$id).toEqual($rootScope.$$childTail.$id); + $location.path('/bar'); + $rootScope.$digest(); + expect($rootScope.$$childHead.$id).toBeTruthy(); + expect($rootScope.$$childHead.$id).toEqual($rootScope.$$childTail.$id); - it('should infer arguments in injection', inject(function($route, $location, $rootScope) { - var injectedRoute; - $route.when('/test', {controller: function($route) {injectedRoute = $route;}}); - $location.path('/test'); - $rootScope.$digest(); - expect(injectedRoute).toBe($route); - })); + $location.path('/baz'); + $rootScope.$digest(); + expect($rootScope.$$childHead.$id).toBeTruthy(); + expect($rootScope.$$childHead.$id).toEqual($rootScope.$$childTail.$id); + $location.path('/'); + $rootScope.$digest(); + expect($rootScope.$$childHead).toEqual(null); + expect($rootScope.$$childTail).toEqual(null); + }); + }); - describe('redirection', function() { - it('should support redirection via redirectTo property by updating $location', - inject(function($route, $location, $rootScope) { - var onChangeSpy = jasmine.createSpy('onChange'); - $route.when('/', {redirectTo: '/foo'}); - $route.when('/foo', {template: 'foo.html'}); - $route.when('/bar', {template: 'bar.html'}); - $route.when('/baz', {redirectTo: '/bar'}); - $route.otherwise({template: '404.html'}); - $rootScope.$on('$beforeRouteChange', onChangeSpy); - expect($route.current).toBeUndefined(); - expect(onChangeSpy).not.toHaveBeenCalled(); + it('should infer arguments in injection', function() { + var injectedRoute; + module(function($routeProvider) { + $routeProvider.when('/test', {controller: function($route) {injectedRoute = $route;}}); + }); - $location.path('/'); + inject(function($route, $location, $rootScope) { + $location.path('/test'); $rootScope.$digest(); - expect($location.path()).toBe('/foo'); - expect($route.current.template).toBe('foo.html'); - expect(onChangeSpy.callCount).toBe(2); + expect(injectedRoute).toBe($route); + }); + }); - onChangeSpy.reset(); - $location.path('/baz'); - $rootScope.$digest(); - expect($location.path()).toBe('/bar'); - expect($route.current.template).toBe('bar.html'); - expect(onChangeSpy.callCount).toBe(2); - })); + describe('redirection', function() { + it('should support redirection via redirectTo property by updating $location', function() { + module(function($routeProvider) { + $routeProvider.when('/', {redirectTo: '/foo'}); + $routeProvider.when('/foo', {template: 'foo.html'}); + $routeProvider.when('/bar', {template: 'bar.html'}); + $routeProvider.when('/baz', {redirectTo: '/bar'}); + $routeProvider.otherwise({template: '404.html'}); + }); - it('should interpolate route vars in the redirected path from original path', - inject(function($route, $location, $rootScope) { - $route.when('/foo/:id/foo/:subid/:extraId', {redirectTo: '/bar/:id/:subid/23'}); - $route.when('/bar/:id/:subid/:subsubid', {template: 'bar.html'}); + inject(function($route, $location, $rootScope) { + var onChangeSpy = jasmine.createSpy('onChange'); - $location.path('/foo/id1/foo/subid3/gah'); - $rootScope.$digest(); + $rootScope.$on('$beforeRouteChange', onChangeSpy); + expect($route.current).toBeUndefined(); + expect(onChangeSpy).not.toHaveBeenCalled(); - expect($location.path()).toEqual('/bar/id1/subid3/23'); - expect($location.search()).toEqual({extraId: 'gah'}); - expect($route.current.template).toEqual('bar.html'); - })); + $location.path('/'); + $rootScope.$digest(); + expect($location.path()).toBe('/foo'); + expect($route.current.template).toBe('foo.html'); + expect(onChangeSpy.callCount).toBe(2); + onChangeSpy.reset(); + $location.path('/baz'); + $rootScope.$digest(); + expect($location.path()).toBe('/bar'); + expect($route.current.template).toBe('bar.html'); + expect(onChangeSpy.callCount).toBe(2); + }); + }); - it('should interpolate route vars in the redirected path from original search', - inject(function($route, $location, $rootScope) { - $route.when('/bar/:id/:subid/:subsubid', {template: 'bar.html'}); - $route.when('/foo/:id/:extra', {redirectTo: '/bar/:id/:subid/99'}); - $location.path('/foo/id3/eId').search('subid=sid1&appended=true'); - $rootScope.$digest(); + it('should interpolate route vars in the redirected path from original path', function() { + module(function($routeProvider) { + $routeProvider.when('/foo/:id/foo/:subid/:extraId', {redirectTo: '/bar/:id/:subid/23'}); + $routeProvider.when('/bar/:id/:subid/:subsubid', {template: 'bar.html'}); + }); - expect($location.path()).toEqual('/bar/id3/sid1/99'); - expect($location.search()).toEqual({appended: 'true', extra: 'eId'}); - expect($route.current.template).toEqual('bar.html'); - })); + inject(function($route, $location, $rootScope) { + $location.path('/foo/id1/foo/subid3/gah'); + $rootScope.$digest(); + expect($location.path()).toEqual('/bar/id1/subid3/23'); + expect($location.search()).toEqual({extraId: 'gah'}); + expect($route.current.template).toEqual('bar.html'); + }); + }); - it('should allow custom redirectTo function to be used', - inject(function($route, $location, $rootScope) { - $route.when('/bar/:id/:subid/:subsubid', {template: 'bar.html'}); - $route.when('/foo/:id', {redirectTo: customRedirectFn}); - $location.path('/foo/id3').search('subid=sid1&appended=true'); - $rootScope.$digest(); + it('should interpolate route vars in the redirected path from original search', function() { + module(function($routeProvider) { + $routeProvider.when('/bar/:id/:subid/:subsubid', {template: 'bar.html'}); + $routeProvider.when('/foo/:id/:extra', {redirectTo: '/bar/:id/:subid/99'}); + }); + + inject(function($route, $location, $rootScope) { + $location.path('/foo/id3/eId').search('subid=sid1&appended=true'); + $rootScope.$digest(); + + expect($location.path()).toEqual('/bar/id3/sid1/99'); + expect($location.search()).toEqual({appended: 'true', extra: 'eId'}); + expect($route.current.template).toEqual('bar.html'); + }); + }); - expect($location.path()).toEqual('/custom'); + it('should allow custom redirectTo function to be used', function() { function customRedirectFn(routePathParams, path, search) { expect(routePathParams).toEqual({id: 'id3'}); - expect(path).toEqual($location.path()); - expect(search).toEqual($location.search()); + expect(path).toEqual('/foo/id3'); + expect(search).toEqual({ subid: 'sid1', appended: 'true' }); return '/custom'; } - })); + module(function($routeProvider){ + $routeProvider.when('/bar/:id/:subid/:subsubid', {template: 'bar.html'}); + $routeProvider.when('/foo/:id', {redirectTo: customRedirectFn}); + }); - it('should replace the url when redirecting', inject(function($route, $location, $rootScope) { - $route.when('/bar/:id', {template: 'bar.html'}); - $route.when('/foo/:id/:extra', {redirectTo: '/bar/:id'}); + inject(function($route, $location, $rootScope) { + $location.path('/foo/id3').search('subid=sid1&appended=true'); + $rootScope.$digest(); - var replace; - $rootScope.$watch(function() { - if (isUndefined(replace)) replace = $location.$$replace; + expect($location.path()).toEqual('/custom'); }); + }); - $location.path('/foo/id3/eId'); - $rootScope.$digest(); - expect($location.path()).toEqual('/bar/id3'); - expect(replace).toBe(true); - })); + it('should replace the url when redirecting', function() { + module(function($routeProvider) { + $routeProvider.when('/bar/:id', {template: 'bar.html'}); + $routeProvider.when('/foo/:id/:extra', {redirectTo: '/bar/:id'}); + }); + inject(function($route, $location, $rootScope) { + var replace; + $rootScope.$watch(function() { + if (isUndefined(replace)) replace = $location.$$replace; + }); + + $location.path('/foo/id3/eId'); + $rootScope.$digest(); + + expect($location.path()).toEqual('/bar/id3'); + expect(replace).toBe(true); + }); + }); }); describe('reloadOnSearch', function() { - it('should reload a route when reloadOnSearch is enabled and .search() changes', - inject(function($route, $location, $rootScope, $routeParams) { + it('should reload a route when reloadOnSearch is enabled and .search() changes', function() { var reloaded = jasmine.createSpy('route reload'); - $route.when('/foo', {controller: FooCtrl}); - $rootScope.$on('$beforeRouteChange', reloaded); - function FooCtrl() { reloaded(); } - $location.path('/foo'); - $rootScope.$digest(); - expect(reloaded).toHaveBeenCalled(); - expect($routeParams).toEqual({}); - reloaded.reset(); + module(function($routeProvider) { + $routeProvider.when('/foo', {controller: FooCtrl}); + }); - // trigger reload - $location.search({foo: 'bar'}); - $rootScope.$digest(); - expect(reloaded).toHaveBeenCalled(); - expect($routeParams).toEqual({foo:'bar'}); - })); + inject(function($route, $location, $rootScope, $routeParams) { + $rootScope.$on('$beforeRouteChange', reloaded); + $location.path('/foo'); + $rootScope.$digest(); + expect(reloaded).toHaveBeenCalled(); + expect($routeParams).toEqual({}); + reloaded.reset(); + // trigger reload + $location.search({foo: 'bar'}); + $rootScope.$digest(); + expect(reloaded).toHaveBeenCalled(); + expect($routeParams).toEqual({foo:'bar'}); + }); + }); - it('should not reload a route when reloadOnSearch is disabled and only .search() changes', - inject(function($route, $location, $rootScope) { + + it('should not reload a route when reloadOnSearch is disabled and only .search() changes', function() { var reloaded = jasmine.createSpy('route reload'), routeUpdateEvent = jasmine.createSpy('route reload'); - $route.when('/foo', {controller: FooCtrl, reloadOnSearch: false}); - $rootScope.$on('$beforeRouteChange', reloaded); - function FooCtrl($scope) { reloaded(); $scope.$on('$routeUpdate', routeUpdateEvent); } - expect(reloaded).not.toHaveBeenCalled(); + module(function($routeProvider) { + $routeProvider.when('/foo', {controller: FooCtrl, reloadOnSearch: false}); + }); - $location.path('/foo'); - $rootScope.$digest(); - expect(reloaded).toHaveBeenCalled(); - expect(routeUpdateEvent).not.toHaveBeenCalled(); - reloaded.reset(); + inject(function($route, $location, $rootScope) { + $rootScope.$on('$beforeRouteChange', reloaded); - // don't trigger reload - $location.search({foo: 'bar'}); - $rootScope.$digest(); - expect(reloaded).not.toHaveBeenCalled(); - expect(routeUpdateEvent).toHaveBeenCalled(); - })); + expect(reloaded).not.toHaveBeenCalled(); + $location.path('/foo'); + $rootScope.$digest(); + expect(reloaded).toHaveBeenCalled(); + expect(routeUpdateEvent).not.toHaveBeenCalled(); + reloaded.reset(); - it('should reload reloadOnSearch route when url differs only in route path param', - inject(function($route, $location, $rootScope) { + // don't trigger reload + $location.search({foo: 'bar'}); + $rootScope.$digest(); + expect(reloaded).not.toHaveBeenCalled(); + expect(routeUpdateEvent).toHaveBeenCalled(); + }); + }); + + + it('should reload reloadOnSearch route when url differs only in route path param', function() { var reloaded = jasmine.createSpy('routeReload'), onRouteChange = jasmine.createSpy('onRouteChange'); - $route.when('/foo/:fooId', {controller: FooCtrl, reloadOnSearch: false}); - $rootScope.$on('$beforeRouteChange', onRouteChange); - function FooCtrl() { reloaded(); } - expect(reloaded).not.toHaveBeenCalled(); - expect(onRouteChange).not.toHaveBeenCalled(); + module(function($routeProvider) { + $routeProvider.when('/foo/:fooId', {controller: FooCtrl, reloadOnSearch: false}); + }); - $location.path('/foo/aaa'); - $rootScope.$digest(); - expect(reloaded).toHaveBeenCalled(); - expect(onRouteChange).toHaveBeenCalled(); - reloaded.reset(); - onRouteChange.reset(); + inject(function($route, $location, $rootScope) { + $rootScope.$on('$beforeRouteChange', onRouteChange); - $location.path('/foo/bbb'); - $rootScope.$digest(); - expect(reloaded).toHaveBeenCalled(); - expect(onRouteChange).toHaveBeenCalled(); - reloaded.reset(); - onRouteChange.reset(); + expect(reloaded).not.toHaveBeenCalled(); + expect(onRouteChange).not.toHaveBeenCalled(); - $location.search({foo: 'bar'}); - $rootScope.$digest(); - expect(reloaded).not.toHaveBeenCalled(); - expect(onRouteChange).not.toHaveBeenCalled(); - })); + $location.path('/foo/aaa'); + $rootScope.$digest(); + expect(reloaded).toHaveBeenCalled(); + expect(onRouteChange).toHaveBeenCalled(); + reloaded.reset(); + onRouteChange.reset(); + $location.path('/foo/bbb'); + $rootScope.$digest(); + expect(reloaded).toHaveBeenCalled(); + expect(onRouteChange).toHaveBeenCalled(); + reloaded.reset(); + onRouteChange.reset(); - it('should update params when reloadOnSearch is disabled and .search() changes', - inject(function($route, $location, $rootScope) { - var routeParams = jasmine.createSpy('routeParams'); + $location.search({foo: 'bar'}); + $rootScope.$digest(); + expect(reloaded).not.toHaveBeenCalled(); + expect(onRouteChange).not.toHaveBeenCalled(); + }); + }); - $route.when('/foo', {controller: FooCtrl}); - $route.when('/bar/:barId', {controller: FooCtrl, reloadOnSearch: false}); - function FooCtrl($scope) { + it('should update params when reloadOnSearch is disabled and .search() changes', function() { + var routeParams = jasmine.createSpy('routeParams'); + + function FooCtrl($scope, $route) { $scope.$watch(function() { return $route.current.params; }, function(value) { @@ -377,33 +425,39 @@ describe('$route', function() { }); } - expect(routeParams).not.toHaveBeenCalled(); + module(function($routeProvider) { + $routeProvider.when('/foo', {controller: FooCtrl}); + $routeProvider.when('/bar/:barId', {controller: FooCtrl, reloadOnSearch: false}); + }); - $location.path('/foo'); - $rootScope.$digest(); - expect(routeParams).toHaveBeenCalledWith({}); - routeParams.reset(); + inject(function($route, $location, $rootScope) { + expect(routeParams).not.toHaveBeenCalled(); - // trigger reload - $location.search({foo: 'bar'}); - $rootScope.$digest(); - expect(routeParams).toHaveBeenCalledWith({foo: 'bar'}); - routeParams.reset(); + $location.path('/foo'); + $rootScope.$digest(); + expect(routeParams).toHaveBeenCalledWith({}); + routeParams.reset(); - $location.path('/bar/123').search({}); - $rootScope.$digest(); - expect(routeParams).toHaveBeenCalledWith({barId: '123'}); - routeParams.reset(); + // trigger reload + $location.search({foo: 'bar'}); + $rootScope.$digest(); + expect(routeParams).toHaveBeenCalledWith({foo: 'bar'}); + routeParams.reset(); - // don't trigger reload - $location.search({foo: 'bar'}); - $rootScope.$digest(); - expect(routeParams).toHaveBeenCalledWith({barId: '123', foo: 'bar'}); - })); + $location.path('/bar/123').search({}); + $rootScope.$digest(); + expect(routeParams).toHaveBeenCalledWith({barId: '123'}); + routeParams.reset(); + + // don't trigger reload + $location.search({foo: 'bar'}); + $rootScope.$digest(); + expect(routeParams).toHaveBeenCalledWith({barId: '123', foo: 'bar'}); + }); + }); - it('should $destroy scope after update and reload', - inject(function($route, $location, $rootScope) { + it('should $destroy scope after update and reload', function() { // this is a regression of bug, where $route doesn't copy scope when only updating var log = []; @@ -422,48 +476,55 @@ describe('$route', function() { }; } - $route.when('/foo', {controller: createController('foo'), reloadOnSearch: false}); - $route.when('/bar', {controller: createController('bar')}); + module(function($routeProvider) { + $routeProvider.when('/foo', {controller: createController('foo'), reloadOnSearch: false}); + $routeProvider.when('/bar', {controller: createController('bar')}); + }); - $location.url('/foo'); - $rootScope.$digest(); - expect(log).toEqual(['init-foo']); + inject(function($route, $location, $rootScope) { + $location.url('/foo'); + $rootScope.$digest(); + expect(log).toEqual(['init-foo']); - $location.search({q: 'some'}); - $rootScope.$digest(); - expect(log).toEqual(['init-foo', 'route-update']); + $location.search({q: 'some'}); + $rootScope.$digest(); + expect(log).toEqual(['init-foo', 'route-update']); - $location.url('/bar'); - $rootScope.$digest(); - expect(log).toEqual(['init-foo', 'route-update', 'destroy-foo', 'init-bar']); - })); + $location.url('/bar'); + $rootScope.$digest(); + expect(log).toEqual(['init-foo', 'route-update', 'destroy-foo', 'init-bar']); + }); + }); describe('reload', function() { - it('should reload even if reloadOnSearch is false', - inject(function($route, $location, $rootScope, $routeParams) { + it('should reload even if reloadOnSearch is false', function() { var count = 0; - $route.when('/bar/:barId', {controller: FooCtrl, reloadOnSearch: false}); - function FooCtrl() { count ++; } - $location.path('/bar/123'); - $rootScope.$digest(); - expect($routeParams).toEqual({barId:'123'}); - expect(count).toEqual(1); - - $location.path('/bar/123').search('a=b'); - $rootScope.$digest(); - expect($routeParams).toEqual({barId:'123', a:'b'}); - expect(count).toEqual(1); + module(function($routeProvider) { + $routeProvider.when('/bar/:barId', {controller: FooCtrl, reloadOnSearch: false}); + }); - $route.reload(); - $rootScope.$digest(); - expect($routeParams).toEqual({barId:'123', a:'b'}); - expect(count).toEqual(2); - })); + inject(function($route, $location, $rootScope, $routeParams) { + $location.path('/bar/123'); + $rootScope.$digest(); + expect($routeParams).toEqual({barId:'123'}); + expect(count).toEqual(1); + + $location.path('/bar/123').search('a=b'); + $rootScope.$digest(); + expect($routeParams).toEqual({barId:'123', a:'b'}); + expect(count).toEqual(1); + + $route.reload(); + $rootScope.$digest(); + expect($routeParams).toEqual({barId:'123', a:'b'}); + expect(count).toEqual(2); + }); + }); }); }); }); diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js index 6f0e3731..e762e7c0 100644 --- a/test/widgetsSpec.js +++ b/test/widgetsSpec.js @@ -618,8 +618,10 @@ describe('widget', function() { describe('ng:view', function() { - beforeEach(inject(function($rootScope, $compile) { - element = $compile('')($rootScope); + beforeEach(module(function() { + return function($rootScope, $compile) { + element = $compile('')($rootScope); + }; })); @@ -631,71 +633,82 @@ describe('widget', function() { })); - it('should load content via xhr when route changes', - inject(function($rootScope, $compile, $httpBackend, $location, $route) { - $route.when('/foo', {template: 'myUrl1'}); - $route.when('/bar', {template: 'myUrl2'}); + it('should load content via xhr when route changes', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {template: 'myUrl1'}); + $routeProvider.when('/bar', {template: 'myUrl2'}); + }); - expect(element.text()).toEqual(''); + inject(function($rootScope, $compile, $httpBackend, $location, $route) { + expect(element.text()).toEqual(''); - $location.path('/foo'); - $httpBackend.expect('GET', 'myUrl1').respond('
{{1+3}}
'); - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toEqual('4'); + $location.path('/foo'); + $httpBackend.expect('GET', 'myUrl1').respond('
{{1+3}}
'); + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toEqual('4'); - $location.path('/bar'); - $httpBackend.expect('GET', 'myUrl2').respond('angular is da best'); - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toEqual('angular is da best'); - })); + $location.path('/bar'); + $httpBackend.expect('GET', 'myUrl2').respond('angular is da best'); + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toEqual('angular is da best'); + }); + }); - it('should remove all content when location changes to an unknown route', - inject(function($rootScope, $compile, $location, $httpBackend, $route) { - $route.when('/foo', {template: 'myUrl1'}); + it('should remove all content when location changes to an unknown route', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {template: 'myUrl1'}); + }); - $location.path('/foo'); - $httpBackend.expect('GET', 'myUrl1').respond('
{{1+3}}
'); - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toEqual('4'); + inject(function($rootScope, $compile, $location, $httpBackend, $route) { + $location.path('/foo'); + $httpBackend.expect('GET', 'myUrl1').respond('
{{1+3}}
'); + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toEqual('4'); - $location.path('/unknown'); - $rootScope.$digest(); - expect(element.text()).toEqual(''); - })); + $location.path('/unknown'); + $rootScope.$digest(); + expect(element.text()).toEqual(''); + }); + }); - it('should chain scopes and propagate evals to the child scope', - inject(function($rootScope, $compile, $location, $httpBackend, $route) { - $route.when('/foo', {template: 'myUrl1'}); - $rootScope.parentVar = 'parent'; + it('should chain scopes and propagate evals to the child scope', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {template: 'myUrl1'}); + }); - $location.path('/foo'); - $httpBackend.expect('GET', 'myUrl1').respond('
{{parentVar}}
'); - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toEqual('parent'); + inject(function($rootScope, $compile, $location, $httpBackend, $route) { + $rootScope.parentVar = 'parent'; - $rootScope.parentVar = 'new parent'; - $rootScope.$digest(); - expect(element.text()).toEqual('new parent'); - })); + $location.path('/foo'); + $httpBackend.expect('GET', 'myUrl1').respond('
{{parentVar}}
'); + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toEqual('parent'); + + $rootScope.parentVar = 'new parent'; + $rootScope.$digest(); + expect(element.text()).toEqual('new parent'); + }); + }); it('should be possible to nest ng:view in ng:include', inject(function() { // TODO(vojta): refactor this test dealoc(element); - var injector = angular.injector(['ng', 'ngMock']); + var injector = angular.injector(['ng', 'ngMock', function($routeProvider) { + $routeProvider.when('/foo', {controller: angular.noop, template: 'viewPartial.html'}); + }]); var myApp = injector.get('$rootScope'); var $httpBackend = injector.get('$httpBackend'); $httpBackend.expect('GET', 'includePartial.html').respond('view: '); injector.get('$location').path('/foo'); var $route = injector.get('$route'); - $route.when('/foo', {controller: angular.noop, template: 'viewPartial.html'}); element = injector.get('$compile')( '
' + @@ -714,94 +727,109 @@ describe('widget', function() { it('should initialize view template after the view controller was initialized even when ' + - 'templates were cached', - inject(function($rootScope, $compile, $location, $httpBackend, $route) { - //this is a test for a regression that was introduced by making the ng:view cache sync - - $route.when('/foo', {controller: ParentCtrl, template: 'viewPartial.html'}); - $rootScope.log = []; - + 'templates were cached', function() { + //this is a test for a regression that was introduced by making the ng:view cache sync function ParentCtrl($scope) { - $scope.log.push('parent'); + $scope.log.push('parent'); } - $rootScope.ChildCtrl = function($scope) { - $scope.log.push('child'); - }; + module(function($routeProvider) { + $routeProvider.when('/foo', {controller: ParentCtrl, template: 'viewPartial.html'}); + }); - $location.path('/foo'); - $httpBackend.expect('GET', 'viewPartial.html'). - respond('
' + - '
' + - '
'); - $rootScope.$apply(); - $httpBackend.flush(); - expect($rootScope.log).toEqual(['parent', 'init', 'child']); + inject(function($rootScope, $compile, $location, $httpBackend, $route) { + $rootScope.log = []; - $location.path('/'); - $rootScope.$apply(); - expect($rootScope.log).toEqual(['parent', 'init', 'child']); + $rootScope.ChildCtrl = function($scope) { + $scope.log.push('child'); + }; - $rootScope.log = []; - $location.path('/foo'); - $rootScope.$apply(); + $location.path('/foo'); + $httpBackend.expect('GET', 'viewPartial.html'). + respond('
' + + '
' + + '
'); + $rootScope.$apply(); + $httpBackend.flush(); - expect($rootScope.log).toEqual(['parent', 'init', 'child']); - })); + expect($rootScope.log).toEqual(['parent', 'init', 'child']); + + $location.path('/'); + $rootScope.$apply(); + expect($rootScope.log).toEqual(['parent', 'init', 'child']); + + $rootScope.log = []; + $location.path('/foo'); + $rootScope.$apply(); + + expect($rootScope.log).toEqual(['parent', 'init', 'child']); + }); + }); it('should discard pending xhr callbacks if a new route is requested before the current ' + - 'finished loading', inject(function($route, $rootScope, $location, $httpBackend) { + 'finished loading', function() { // this is a test for a bad race condition that affected feedback - $route.when('/foo', {template: 'myUrl1'}); - $route.when('/bar', {template: 'myUrl2'}); + module(function($routeProvider) { + $routeProvider.when('/foo', {template: 'myUrl1'}); + $routeProvider.when('/bar', {template: 'myUrl2'}); + }); - expect(element.text()).toEqual(''); + inject(function($route, $rootScope, $location, $httpBackend) { + expect(element.text()).toEqual(''); - $location.path('/foo'); - $httpBackend.expect('GET', 'myUrl1').respond('
{{1+3}}
'); - $rootScope.$digest(); - $location.path('/bar'); - $httpBackend.expect('GET', 'myUrl2').respond('
{{1+1}}
'); - $rootScope.$digest(); - $httpBackend.flush(); // now that we have two requests pending, flush! + $location.path('/foo'); + $httpBackend.expect('GET', 'myUrl1').respond('
{{1+3}}
'); + $rootScope.$digest(); + $location.path('/bar'); + $httpBackend.expect('GET', 'myUrl2').respond('
{{1+1}}
'); + $rootScope.$digest(); + $httpBackend.flush(); // now that we have two requests pending, flush! - expect(element.text()).toEqual('2'); - })); + expect(element.text()).toEqual('2'); + }); + }); - it('should clear the content when error during xhr request', - inject(function($route, $location, $rootScope, $httpBackend) { - $route.when('/foo', {controller: noop, template: 'myUrl1'}); + it('should clear the content when error during xhr request', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {controller: noop, template: 'myUrl1'}); + }); - $location.path('/foo'); - $httpBackend.expect('GET', 'myUrl1').respond(404, ''); - element.text('content'); + inject(function($route, $location, $rootScope, $httpBackend) { + $location.path('/foo'); + $httpBackend.expect('GET', 'myUrl1').respond(404, ''); + element.text('content'); - $rootScope.$digest(); - $httpBackend.flush(); - - expect(element.text()).toBe(''); - })); + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toBe(''); + }); + }); - it('should be async even if served from cache', - inject(function($route, $rootScope, $location, $templateCache, $browser) { - $templateCache.put('myUrl1', [200, 'my partial', {}]); - $route.when('/foo', {controller: noop, template: 'myUrl1'}); - $location.path('/foo'); - var called = 0; - // we want to assert only during first watch - $rootScope.$watch(function() { - if (!called++) expect(element.text()).toBe(''); + it('should be async even if served from cache', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {controller: noop, template: 'myUrl1'}); }); - $rootScope.$digest(); - expect(element.text()).toBe('my partial'); - })); + inject(function($route, $rootScope, $location, $templateCache, $browser) { + $templateCache.put('myUrl1', [200, 'my partial', {}]); + $location.path('/foo'); + + var called = 0; + // we want to assert only during first watch + $rootScope.$watch(function() { + if (!called++) expect(element.text()).toBe(''); + }); + + $rootScope.$digest(); + expect(element.text()).toBe('my partial'); + }); + }); }); -- cgit v1.2.3