538 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			538 lines
		
	
	
		
			14 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 lifecycle', function () {
 | |
|   var elt,
 | |
|     $compile,
 | |
|     $q,
 | |
|     $rootScope,
 | |
|     $rootRouter,
 | |
|     $compileProvider;
 | |
| 
 | |
|   beforeEach(function () {
 | |
|     module('ng');
 | |
|     module('ngComponentRouter');
 | |
|     module(function (_$compileProvider_) {
 | |
|       $compileProvider = _$compileProvider_;
 | |
|     });
 | |
| 
 | |
|     inject(function (_$compile_, _$q_, _$rootScope_, _$rootRouter_) {
 | |
|       $compile = _$compile_;
 | |
|       $q = _$q_;
 | |
|       $rootScope = _$rootScope_;
 | |
|       $rootRouter = _$rootRouter_;
 | |
|     });
 | |
| 
 | |
|     registerComponent('oneCmp', {
 | |
|       template: '<div>{{oneCmp.number}}</div>',
 | |
|       controller: function () {this.number = 'one';}
 | |
|     });
 | |
|     registerComponent('twoCmp', {
 | |
|       template: '<div><a ng-link="[\'/Two\']">{{twoCmp.number}}</a></div>',
 | |
|       controller: function () {this.number = 'two';}
 | |
|     });
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should run the activate hook of controllers', function () {
 | |
|     var spy = jasmine.createSpy('activate');
 | |
|     registerComponent('activateCmp', {
 | |
|       template: '<p>hello</p>',
 | |
|       $routerOnActivate: spy
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/a', component: 'activateCmp' }
 | |
|     ]);
 | |
|     compile('<div>outer { <div ng-outlet></div> }</div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/a');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(spy).toHaveBeenCalled();
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should pass instruction into the activate hook of a controller', function () {
 | |
|     var spy = jasmine.createSpy('activate');
 | |
|     registerComponent('userCmp', {
 | |
|       $routerOnActivate: spy
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/user/:name', component: 'userCmp' }
 | |
|     ]);
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/user/brian');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(spy).toHaveBeenCalledWith(instructionFor('userCmp'), undefined);
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should pass previous instruction into the activate hook of a controller', function () {
 | |
|     var spy = jasmine.createSpy('activate');
 | |
|     var activate = registerComponent('activateCmp', {
 | |
|       template: 'hi',
 | |
|       $routerOnActivate: spy
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/user/:name', component: 'oneCmp' },
 | |
|       { path: '/post/:id', component: 'activateCmp' }
 | |
|     ]);
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/user/brian');
 | |
|     $rootScope.$digest();
 | |
|     $rootRouter.navigateByUrl('/post/123');
 | |
|     $rootScope.$digest();
 | |
|     expect(spy).toHaveBeenCalledWith(instructionFor('activateCmp'),
 | |
|                                      instructionFor('oneCmp'));
 | |
|   });
 | |
| 
 | |
|   describe('activate hook with promise', () => {
 | |
|     var activateDeferred;
 | |
|     beforeEach(() => {
 | |
|       activateDeferred = $q.defer();
 | |
|       var activate = registerComponent('activateCmp', {
 | |
|         template: 'hi',
 | |
|         $routerOnActivate: function() {
 | |
|           return activateDeferred.promise;
 | |
|         }
 | |
|       });
 | |
| 
 | |
|       $rootRouter.config([
 | |
|         { path: '/user/:name', component: 'oneCmp' },
 | |
|         { path: '/post', component: 'activateCmp' },
 | |
|       ]);
 | |
|       compile('<div ng-outlet></div>');
 | |
| 
 | |
|       $rootRouter.navigateByUrl('/user/fabian');
 | |
|       $rootScope.$digest();
 | |
|       $rootRouter.navigateByUrl('/post');
 | |
|       $rootScope.$digest();
 | |
|     });
 | |
| 
 | |
|     it('should update the view once the promise gets resolved', () => {
 | |
|       expect(elt.text()).toBe('one');
 | |
| 
 | |
|       activateDeferred.resolve();
 | |
|       $rootScope.$digest();
 | |
|       expect(elt.text()).toBe('hi');
 | |
|     });
 | |
| 
 | |
|     it('should update the view once the promise gets rejected', () => {
 | |
|       expect(elt.text()).toBe('one');
 | |
| 
 | |
|       activateDeferred.reject();
 | |
|       $rootScope.$digest();
 | |
|       expect(elt.text()).toBe('hi');
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   it('should inject $scope into the controller constructor', function () {
 | |
|     var injectedScope;
 | |
|     registerComponent('userCmp', {
 | |
|       template: '',
 | |
|       controller: function ($scope) {
 | |
|         injectedScope = $scope;
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/user', component: 'userCmp' }
 | |
|     ]);
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/user');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(injectedScope).toBeDefined();
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should run the deactivate hook of controllers', function () {
 | |
|     var spy = jasmine.createSpy('deactivate');
 | |
|     registerComponent('deactivateCmp', {
 | |
|       $routerOnDeactivate: spy
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/a', component: 'deactivateCmp' },
 | |
|       { path: '/b', component: 'oneCmp' }
 | |
|     ]);
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/a');
 | |
|     $rootScope.$digest();
 | |
|     $rootRouter.navigateByUrl('/b');
 | |
|     $rootScope.$digest();
 | |
|     expect(spy).toHaveBeenCalled();
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should pass instructions into the deactivate hook of controllers', function () {
 | |
|     var spy = jasmine.createSpy('deactivate');
 | |
|     registerComponent('deactivateCmp', {
 | |
|       $routerOnDeactivate: spy
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/user/:name', component: 'deactivateCmp' },
 | |
|       { path: '/post/:id', component: 'oneCmp' }
 | |
|     ]);
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/user/brian');
 | |
|     $rootScope.$digest();
 | |
|     $rootRouter.navigateByUrl('/post/123');
 | |
|     $rootScope.$digest();
 | |
|     expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
 | |
|                                      instructionFor('deactivateCmp'));
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should run the deactivate hook before the activate hook', function () {
 | |
|     var log = [];
 | |
| 
 | |
|     registerComponent('activateCmp', {
 | |
|       $routerOnActivate: function () {
 | |
|         log.push('activate');
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     registerComponent('deactivateCmp', {
 | |
|       $routerOnDeactivate: function () {
 | |
|         log.push('deactivate');
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/a', component: 'deactivateCmp' },
 | |
|       { path: '/b', component: 'activateCmp' }
 | |
|     ]);
 | |
|     compile('outer { <div ng-outlet></div> }');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/a');
 | |
|     $rootScope.$digest();
 | |
|     $rootRouter.navigateByUrl('/b');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(log).toEqual(['deactivate', 'activate']);
 | |
|   });
 | |
| 
 | |
|   it('should reuse a component when the routerCanReuse hook returns true', function () {
 | |
|     var log = [];
 | |
|     var cmpInstanceCount = 0;
 | |
| 
 | |
|     function ReuseCmp() {
 | |
|       cmpInstanceCount++;
 | |
|     }
 | |
| 
 | |
|     registerComponent('reuseCmp', {
 | |
|       template: 'reuse {<ng-outlet></ng-outlet>}',
 | |
|       $routeConfig: [
 | |
|         {path: '/a', component: 'oneCmp'},
 | |
|         {path: '/b', component: 'twoCmp'}
 | |
|       ],
 | |
|       controller: ReuseCmp,
 | |
|       $routerCanReuse: function () {
 | |
|         return true;
 | |
|       },
 | |
|       $routerOnReuse: function (next, prev) {
 | |
|         log.push('reuse: ' + prev.urlPath + ' -> ' + next.urlPath);
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/on-reuse/:number/...', component: 'reuseCmp' },
 | |
|       { path: '/two', component: 'twoCmp', name: 'Two'}
 | |
|     ]);
 | |
|     compile('outer { <div ng-outlet></div> }');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/on-reuse/1/a');
 | |
|     $rootScope.$digest();
 | |
|     expect(log).toEqual([]);
 | |
|     expect(cmpInstanceCount).toBe(1);
 | |
|     expect(elt.text()).toBe('outer { reuse {one} }');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/on-reuse/2/b');
 | |
|     $rootScope.$digest();
 | |
|     expect(log).toEqual(['reuse: on-reuse/1 -> on-reuse/2']);
 | |
|     expect(cmpInstanceCount).toBe(1);
 | |
|     expect(elt.text()).toBe('outer { reuse {two} }');
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should not reuse a component when the routerCanReuse hook returns false', function () {
 | |
|     var log = [];
 | |
|     var cmpInstanceCount = 0;
 | |
| 
 | |
|     function NeverReuseCmp() {
 | |
|       cmpInstanceCount++;
 | |
|     }
 | |
|     registerComponent('reuseCmp', {
 | |
|       template: 'reuse {<ng-outlet></ng-outlet>}',
 | |
|       $routeConfig: [
 | |
|         {path: '/a', component: 'oneCmp'},
 | |
|         {path: '/b', component: 'twoCmp'}
 | |
|       ],
 | |
|       controller: NeverReuseCmp,
 | |
|       $routerCanReuse: function () {
 | |
|         return false;
 | |
|       },
 | |
|       $routerOnReuse: function (next, prev) {
 | |
|         log.push('reuse: ' + prev.urlPath + ' -> ' + next.urlPath);
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/never-reuse/:number/...', component: 'reuseCmp' },
 | |
|       { path: '/two', component: 'twoCmp', name: 'Two'}
 | |
|     ]);
 | |
|     compile('outer { <div ng-outlet></div> }');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/never-reuse/1/a');
 | |
|     $rootScope.$digest();
 | |
|     expect(log).toEqual([]);
 | |
|     expect(cmpInstanceCount).toBe(1);
 | |
|     expect(elt.text()).toBe('outer { reuse {one} }');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/never-reuse/2/b');
 | |
|     $rootScope.$digest();
 | |
|     expect(log).toEqual([]);
 | |
|     expect(cmpInstanceCount).toBe(2);
 | |
|     expect(elt.text()).toBe('outer { reuse {two} }');
 | |
|   });
 | |
| 
 | |
| 
 | |
|   // TODO: need to solve getting ahold of canActivate hook
 | |
|   it('should not activate a component when canActivate returns false', function () {
 | |
|     var canActivateSpy = jasmine.createSpy('canActivate').and.returnValue(false);
 | |
|     var spy = jasmine.createSpy('activate');
 | |
|     registerComponent('activateCmp', {
 | |
|       $canActivate: canActivateSpy,
 | |
|       $routerOnActivate: spy
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/a', component: 'activateCmp' }
 | |
|     ]);
 | |
|     compile('outer { <div ng-outlet></div> }');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/a');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(spy).not.toHaveBeenCalled();
 | |
|     expect(elt.text()).toBe('outer {  }');
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should activate a component when canActivate returns true', function () {
 | |
|     var activateSpy = jasmine.createSpy('activate');
 | |
|     var canActivateSpy = jasmine.createSpy('canActivate').and.returnValue(true);
 | |
|     registerComponent('activateCmp', {
 | |
|       template: 'hi',
 | |
|       $canActivate: canActivateSpy,
 | |
|       $routerOnActivate: activateSpy
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/a', component: 'activateCmp' }
 | |
|     ]);
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/a');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(canActivateSpy).toHaveBeenCalled();
 | |
|     expect(activateSpy).toHaveBeenCalled();
 | |
|     expect(elt.text()).toBe('hi');
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should activate a component when canActivate returns a resolved promise', inject(function ($q) {
 | |
|     var spy = jasmine.createSpy('activate');
 | |
|     registerComponent('activateCmp', {
 | |
|       template: 'hi',
 | |
|       $canActivate: function () {
 | |
|         return $q.when(true);
 | |
|       },
 | |
|       $routerOnActivate: spy
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/a', component: 'activateCmp' }
 | |
|     ]);
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/a');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(spy).toHaveBeenCalled();
 | |
|     expect(elt.text()).toBe('hi');
 | |
|   }));
 | |
| 
 | |
| 
 | |
|   it('should inject into the canActivate hook of controllers', inject(function ($http) {
 | |
|     var spy = jasmine.createSpy('canActivate').and.returnValue(true);
 | |
|     registerComponent('activateCmp', {
 | |
|       $canActivate: spy
 | |
|     });
 | |
| 
 | |
|     spy.$inject = ['$nextInstruction', '$http'];
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/user/:name', component: 'activateCmp' }
 | |
|     ]);
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/user/brian');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(spy).toHaveBeenCalled();
 | |
|     var args = spy.calls.mostRecent().args;
 | |
|     expect(args[0].params).toEqual(jasmine.objectContaining({name: 'brian'}));
 | |
|     expect(args[1]).toBe($http);
 | |
|   }));
 | |
| 
 | |
| 
 | |
|   it('should not navigate when routerCanDeactivate returns false', function () {
 | |
|     registerComponent('activateCmp', {
 | |
|       template: 'hi',
 | |
|       $routerCanDeactivate: function () {
 | |
|         return false;
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/a', component: 'activateCmp' },
 | |
|       { path: '/b', component: 'oneCmp' }
 | |
|     ]);
 | |
|     compile('outer { <div ng-outlet></div> }');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/a');
 | |
|     $rootScope.$digest();
 | |
|     expect(elt.text()).toBe('outer { hi }');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/b');
 | |
|     $rootScope.$digest();
 | |
|     expect(elt.text()).toBe('outer { hi }');
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should navigate when routerCanDeactivate returns true', function () {
 | |
|     registerComponent('activateCmp', {
 | |
|       template: 'hi',
 | |
|       $routerCanDeactivate: function () {
 | |
|         return true;
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/a', component: 'activateCmp' },
 | |
|       { path: '/b', component: 'oneCmp' }
 | |
|     ]);
 | |
|     compile('outer { <div ng-outlet></div> }');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/a');
 | |
|     $rootScope.$digest();
 | |
|     expect(elt.text()).toBe('outer { hi }');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/b');
 | |
|     $rootScope.$digest();
 | |
|     expect(elt.text()).toBe('outer { one }');
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should activate a component when canActivate returns true', function () {
 | |
|     var spy = jasmine.createSpy('activate');
 | |
|     registerComponent('activateCmp', {
 | |
|       template: 'hi',
 | |
|       $canActivate: function () {
 | |
|         return true;
 | |
|       },
 | |
|       $routerOnActivate: spy
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/a', component: 'activateCmp' }
 | |
|     ]);
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/a');
 | |
|     $rootScope.$digest();
 | |
| 
 | |
|     expect(spy).toHaveBeenCalled();
 | |
|     expect(elt.text()).toBe('hi');
 | |
|   });
 | |
| 
 | |
| 
 | |
|   it('should pass instructions into the routerCanDeactivate hook of controllers', function () {
 | |
|     var spy = jasmine.createSpy('routerCanDeactivate').and.returnValue(true);
 | |
|     registerComponent('deactivateCmp', {
 | |
|       $routerCanDeactivate: spy
 | |
|     });
 | |
| 
 | |
|     $rootRouter.config([
 | |
|       { path: '/user/:name', component: 'deactivateCmp' },
 | |
|       { path: '/post/:id', component: 'oneCmp' }
 | |
|     ]);
 | |
|     compile('<div ng-outlet></div>');
 | |
| 
 | |
|     $rootRouter.navigateByUrl('/user/brian');
 | |
|     $rootScope.$digest();
 | |
|     $rootRouter.navigateByUrl('/post/123');
 | |
|     $rootScope.$digest();
 | |
|     expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
 | |
|                                      instructionFor('deactivateCmp'));
 | |
|   });
 | |
| 
 | |
| 
 | |
|   function registerComponent(name, options) {
 | |
|     var controller = options.controller || function () {};
 | |
| 
 | |
|     ['$routerOnActivate', '$routerOnDeactivate', '$routerOnReuse', '$routerCanReuse', '$routerCanDeactivate'].forEach(function (hookName) {
 | |
|       if (options[hookName]) {
 | |
|         controller.prototype[hookName] = options[hookName];
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     function factory() {
 | |
|       return {
 | |
|         template: options.template || '',
 | |
|         controllerAs: name,
 | |
|         controller: controller
 | |
|       };
 | |
|     }
 | |
| 
 | |
|     if (options.$canActivate) {
 | |
|       controller.$canActivate = options.$canActivate;
 | |
|     }
 | |
|     if (options.$routeConfig) {
 | |
|       controller.$routeConfig = options.$routeConfig;
 | |
|     }
 | |
| 
 | |
|     $compileProvider.directive(name, factory);
 | |
|   }
 | |
| 
 | |
|   function compile(template) {
 | |
|     elt = $compile('<div>' + template + '</div>')($rootScope);
 | |
|     $rootScope.$digest();
 | |
|     return elt;
 | |
|   }
 | |
| 
 | |
|   function instructionFor(componentType) {
 | |
|     return jasmine.objectContaining({componentType: componentType});
 | |
|   }
 | |
| });
 |