368 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			368 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * @license
 | |
|  * Copyright Google Inc. All Rights Reserved.
 | |
|  *
 | |
|  * Use of this source code is governed by an MIT-style license that can be
 | |
|  * found in the LICENSE file at https://angular.io/license
 | |
|  */
 | |
| 
 | |
| 'use strict';
 | |
| 
 | |
| describe('navigation', function () {
 | |
| 
 | |
|   var elt,
 | |
|       $compile,
 | |
|       $rootScope,
 | |
|       $rootRouter,
 | |
|       $compileProvider;
 | |
| 
 | |
|   beforeEach(function () {
 | |
|     module('ng');
 | |
|     module('ngComponentRouter');
 | |
|     module(function (_$compileProvider_) {
 | |
|       $compileProvider = _$compileProvider_;
 | |
|     });
 | |
| 
 | |
|     inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
 | |
|       $compile = _$compile_;
 | |
|       $rootScope = _$rootScope_;
 | |
|       $rootRouter = _$rootRouter_;
 | |
|     });
 | |
| 
 | |
|     registerDirective('userCmp', {
 | |
|       template: '<div>hello {{userCmp.$routeParams.name}}</div>',
 | |
|       $routerOnActivate: function(next) {
 | |
|         this.$routeParams = next.params;
 | |
|       }
 | |
|     });
 | |
|     registerDirective('oneCmp', {
 | |
|       template: '<div>{{oneCmp.number}}</div>',
 | |
|       controller: function () {this.number = 'one'}
 | |
|     });
 | |
|     registerDirective('twoCmp', {
 | |
|       template: '<div>{{twoCmp.number}}</div>',
 | |
|       controller: function () {this.number = 'two'}
 | |
|     });
 | |
|     registerComponent('threeCmp', {
 | |
|       template: '<div>{{$ctrl.number}}</div>',
 | |
|       controller: function () {this.number = 'three'}
 | |
|     });
 | |
|     registerComponent('getParams', {
 | |
|       template: '<div>{{$ctrl.params.x}}</div>',
 | |
|       controller: function () {
 | |
|         this.$routerOnActivate = function(next) {
 | |
|           this.params = next.params;
 | |
|         };
 | |
|       }
 | |
|     })
 | |
|   });
 | |
| 
 | |
|   it('should work in a simple case', function () {
 | |
|     compile('<ng-outlet></ng-outlet>');
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/', component: 'oneCmp' }
 | |
|     ]);
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(elt.text()).toBe('one');
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should work with components created by the `mod.component()` helper', function () {
 | |
|     compile('<ng-outlet></ng-outlet>');
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/', component: 'threeCmp' }
 | |
|     ]);
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(elt.text()).toBe('three');
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should navigate between components with different parameters', function () {
 | |
|     $rootRouter.config([
 | |
|       { path: '/user/:name', component: 'userCmp' }
 | |
|     ]);
 | |
|     compile('<ng-outlet></ng-outlet>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/user/brian');
 | |
|     $rootScope.$digest();
 | |
|     expect(elt.text()).toBe('hello brian');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/user/igor');
 | |
|     $rootScope.$digest();
 | |
|     expect(elt.text()).toBe('hello igor');
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should reuse a parent when navigating between child components with different parameters', function () {
 | |
|     var instanceCount = 0;
 | |
|     function ParentController() {
 | |
|       instanceCount += 1;
 | |
|     }
 | |
|     registerDirective('parentCmp', {
 | |
|       template: 'parent { <ng-outlet></ng-outlet> }',
 | |
|       $routeConfig: [
 | |
|         { path: '/user/:name', component: 'userCmp' }
 | |
|       ],
 | |
|       controller: ParentController
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/parent/...', component: 'parentCmp' }
 | |
|     ]);
 | |
|     compile('<ng-outlet></ng-outlet>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/parent/user/brian');
 | |
|     $rootScope.$digest();
 | |
|     expect(instanceCount).toBe(1);
 | |
|     expect(elt.text()).toBe('parent { hello brian }');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/parent/user/igor');
 | |
|     $rootScope.$digest();
 | |
|     expect(instanceCount).toBe(1);
 | |
|     expect(elt.text()).toBe('parent { hello igor }');
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should work with nested outlets', function () {
 | |
|     registerDirective('childCmp', {
 | |
|       template: '<div>inner { <div ng-outlet></div> }</div>',
 | |
|       $routeConfig: [
 | |
|         { path: '/b', component: 'oneCmp' }
 | |
|       ]
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/a/...', component: 'childCmp' }
 | |
|     ]);
 | |
|     compile('<div>outer { <div ng-outlet></div> }</div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/a/b');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(elt.text()).toBe('outer { inner { one } }');
 | |
|   });
 | |
| 
 | |
|   it('should work when parent route has empty path', inject(function ($location) {
 | |
|     registerComponent('childCmp', {
 | |
|       template: '<div>inner { <div ng-outlet></div> }</div>',
 | |
|       $routeConfig: [
 | |
|         { path: '/b', component: 'oneCmp' }
 | |
|       ]
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/...', component: 'childCmp' }
 | |
|     ]);
 | |
|     compile('<div>outer { <div ng-outlet></div> }</div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/b');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(elt.text()).toBe('outer { inner { one } }');
 | |
|     expect($location.path()).toBe('/b');
 | |
|   }));
 | |
| 
 | |
| 
 | |
|   it('should work with recursive nested outlets', function () {
 | |
|     registerDirective('recurCmp', {
 | |
|       template: '<div>recur { <div ng-outlet></div> }</div>',
 | |
|       $routeConfig: [
 | |
|         { path: '/recur', component: 'recurCmp' },
 | |
|         { path: '/end', component: 'oneCmp' }
 | |
|       ]});
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/recur', component: 'recurCmp' },
 | |
|       { path: '/', component: 'oneCmp' }
 | |
|     ]);
 | |
| 
 | |
|     compile('<div>root { <div ng-outlet></div> }</div>');
 | |
|     $rootRouter.navigateByUrl('/recur/recur/end');
 | |
|     $rootScope.$digest();
 | |
|     expect(elt.text()).toBe('root { one }');
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should change location path', inject(function ($location) {
 | |
|     $rootRouter.config([
 | |
|       { path: '/user', component: 'userCmp' }
 | |
|     ]);
 | |
| 
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/user');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect($location.path()).toBe('/user');
 | |
|   }));
 | |
| 
 | |
| 
 | |
|   it('should pass through query terms to the location', inject(function ($location) {
 | |
|     $rootRouter.config([
 | |
|       { path: '/user', component: 'userCmp' }
 | |
|     ]);
 | |
| 
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/user?x=y');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect($location.path()).toBe('/user');
 | |
|     expect($location.search()).toEqual({ x: 'y'});
 | |
|   }));
 | |
| 
 | |
| 
 | |
|   it('should change location to the canonical route', inject(function ($location) {
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/',     redirectTo: ['/User'] },
 | |
|       { path: '/user', component:  'userCmp', name: 'User' }
 | |
|     ]);
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect($location.path()).toBe('/user');
 | |
|   }));
 | |
| 
 | |
| 
 | |
|   it('should change location to the canonical route with nested components', inject(function ($location) {
 | |
|     registerDirective('childRouter', {
 | |
|       template: '<div>inner { <div ng-outlet></div> }</div>',
 | |
|       $routeConfig: [
 | |
|         { path: '/new-child', component: 'oneCmp', name: 'NewChild'},
 | |
|         { path: '/new-child-two', component: 'twoCmp', name: 'NewChildTwo'}
 | |
|       ]
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/old-parent/old-child', redirectTo: ['/NewParent', 'NewChild'] },
 | |
|       { path: '/old-parent/old-child-two', redirectTo: ['/NewParent', 'NewChildTwo'] },
 | |
|       { path: '/new-parent/...', component:  'childRouter', name: 'NewParent' }
 | |
|     ]);
 | |
| 
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/old-parent/old-child');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect($location.path()).toBe('/new-parent/new-child');
 | |
|     expect(elt.text()).toBe('inner { one }');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/old-parent/old-child-two');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect($location.path()).toBe('/new-parent/new-child-two');
 | |
|     expect(elt.text()).toBe('inner { two }');
 | |
|   }));
 | |
| 
 | |
| 
 | |
|   it('should navigate when the location path changes', inject(function ($location) {
 | |
|     $rootRouter.config([
 | |
|       { path: '/one', component: 'oneCmp' }
 | |
|     ]);
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $location.path('/one');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(elt.text()).toBe('one');
 | |
|   }));
 | |
| 
 | |
| 
 | |
|   it('should navigate when the location query changes', inject(function ($location) {
 | |
|     $rootRouter.config([
 | |
|       { path: '/get/params', component: 'getParams' }
 | |
|     ]);
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $location.url('/get/params?x=y');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(elt.text()).toBe('y');
 | |
|   }));
 | |
| 
 | |
| 
 | |
|   it('should expose a "navigating" property on $rootRouter', inject(function ($q) {
 | |
|     var defer;
 | |
|     registerDirective('pendingActivate', {
 | |
|       $canActivate: function () {
 | |
|         defer = $q.defer();
 | |
|         return defer.promise;
 | |
|       }
 | |
|     });
 | |
|     $rootRouter.config([
 | |
|       { path: '/pending-activate', component: 'pendingActivate' }
 | |
|     ]);
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/pending-activate');
 | |
|     $rootScope.$digest();
 | |
|     expect($rootRouter.navigating).toBe(true);
 | |
|     defer.resolve();
 | |
|     $rootScope.$digest();
 | |
|     expect($rootRouter.navigating).toBe(false);
 | |
|   }));
 | |
| 
 | |
|   function registerDirective(name, options) {
 | |
|     var controller = getController(options);
 | |
|     function factory() {
 | |
|       return {
 | |
|         template: options.template || '',
 | |
|         controllerAs: name,
 | |
|         controller: controller
 | |
|       };
 | |
|     }
 | |
|     applyStaticProperties(controller, options);
 | |
|     $compileProvider.directive(name, factory);
 | |
|   }
 | |
| 
 | |
|   function registerComponent(name, options) {
 | |
| 
 | |
|     var definition = {
 | |
|       template: options.template || '',
 | |
|       controller: getController(options),
 | |
|     }
 | |
|     applyStaticProperties(definition.controller, options);
 | |
|     $compileProvider.component(name, definition);
 | |
|   }
 | |
| 
 | |
|   function compile(template) {
 | |
|     elt = $compile('<div>' + template + '</div>')($rootScope);
 | |
|     $rootScope.$digest();
 | |
|     return elt;
 | |
|   }
 | |
| 
 | |
|   function getController(options) {
 | |
|     var controller = options.controller || function () {};
 | |
|     [
 | |
|       '$routerOnActivate', '$routerOnDeactivate',
 | |
|       '$routerOnReuse', '$routerCanReuse',
 | |
|       '$routerCanDeactivate'
 | |
|     ].forEach(function (hookName) {
 | |
|       if (options[hookName]) {
 | |
|         controller.prototype[hookName] = options[hookName];
 | |
|       }
 | |
|     });
 | |
|     return controller;
 | |
|   }
 | |
| 
 | |
|   function applyStaticProperties(target, options) {
 | |
|     ['$canActivate', '$routeConfig'].forEach(function(property) {
 | |
|       if (options[property]) {
 | |
|         target[property] = options[property];
 | |
|       }
 | |
|     });
 | |
|   }
 | |
| });
 | |
| 
 |