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];
 | 
						|
      }
 | 
						|
    });
 | 
						|
  }
 | 
						|
});
 | 
						|
 |