'use strict';
describe('navigation', function () {
var elt,
$compile,
$rootScope,
$router,
$compileProvider;
beforeEach(function () {
module('ng');
module('ngComponentRouter');
module(function (_$compileProvider_) {
$compileProvider = _$compileProvider_;
});
inject(function (_$compile_, _$rootScope_, _$router_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
});
registerComponent('userCmp', {
template: '
hello {{userCmp.$routeParams.name}}
'
});
registerComponent('oneCmp', {
template: '{{oneCmp.number}}
',
controller: function () {this.number = 'one'}
});
registerComponent('twoCmp', {
template: '{{twoCmp.number}}
',
controller: function () {this.number = 'two'}
});
});
it('should work in a simple case', function () {
compile('');
$router.config([
{ path: '/', component: 'oneCmp' }
]);
$router.navigateByUrl('/');
$rootScope.$digest();
expect(elt.text()).toBe('one');
});
it('should navigate between components with different parameters', function () {
$router.config([
{ path: '/user/:name', component: 'userCmp' }
]);
compile('');
$router.navigateByUrl('/user/brian');
$rootScope.$digest();
expect(elt.text()).toBe('hello brian');
$router.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;
}
registerComponent('parentCmp', {
template: 'parent { }',
$routeConfig: [
{ path: '/user/:name', component: 'userCmp' }
],
controller: ParentController
});
$router.config([
{ path: '/parent/...', component: 'parentCmp' }
]);
compile('');
$router.navigateByUrl('/parent/user/brian');
$rootScope.$digest();
expect(instanceCount).toBe(1);
expect(elt.text()).toBe('parent { hello brian }');
$router.navigateByUrl('/parent/user/igor');
$rootScope.$digest();
expect(instanceCount).toBe(1);
expect(elt.text()).toBe('parent { hello igor }');
});
it('should work with nested outlets', function () {
registerComponent('childCmp', {
template: '',
$routeConfig: [
{ path: '/b', component: 'oneCmp' }
]
});
$router.config([
{ path: '/a/...', component: 'childCmp' }
]);
compile('');
$router.navigateByUrl('/a/b');
$rootScope.$digest();
expect(elt.text()).toBe('outer { inner { one } }');
});
// TODO: fix this
xit('should work with recursive nested outlets', function () {
registerComponent('recurCmp', {
template: '',
$routeConfig: [
{ path: '/recur', component: 'recurCmp' },
{ path: '/end', component: 'oneCmp' }
]});
$router.config([
{ path: '/recur', component: 'recurCmp' },
{ path: '/', component: 'oneCmp' }
]);
compile('');
$router.navigateByUrl('/recur/recur/end');
$rootScope.$digest();
expect(elt.text()).toBe('root { one }');
});
it('should change location path', inject(function ($location) {
$router.config([
{ path: '/user', component: 'userCmp' }
]);
compile('');
$router.navigateByUrl('/user');
$rootScope.$digest();
expect($location.path()).toBe('/user');
}));
it('should change location to the canonical route', inject(function ($location) {
compile('');
$router.config([
{ path: '/', redirectTo: '/user' },
{ path: '/user', component: 'userCmp' }
]);
$router.navigateByUrl('/');
$rootScope.$digest();
expect($location.path()).toBe('/user');
}));
it('should change location to the canonical route with nested components', inject(function ($location) {
registerComponent('childRouter', {
template: '',
$routeConfig: [
{ path: '/old-child', redirectTo: '/new-child' },
{ path: '/new-child', component: 'oneCmp'},
{ path: '/old-child-two', redirectTo: '/new-child-two' },
{ path: '/new-child-two', component: 'twoCmp'}
]
});
$router.config([
{ path: '/old-parent', redirectTo: '/new-parent' },
{ path: '/new-parent/...', component: 'childRouter' }
]);
compile('');
$router.navigateByUrl('/old-parent/old-child');
$rootScope.$digest();
expect($location.path()).toBe('/new-parent/new-child');
expect(elt.text()).toBe('inner { one }');
$router.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) {
$router.config([
{ path: '/one', component: 'oneCmp' }
]);
compile('');
$location.path('/one');
$rootScope.$digest();
expect(elt.text()).toBe('one');
}));
it('should expose a "navigating" property on $router', inject(function ($q) {
var defer;
registerComponent('pendingActivate', {
$canActivate: function () {
defer = $q.defer();
return defer.promise;
}
});
$router.config([
{ path: '/pending-activate', component: 'pendingActivate' }
]);
compile('');
$router.navigateByUrl('/pending-activate');
$rootScope.$digest();
expect($router.navigating).toBe(true);
defer.resolve();
$rootScope.$digest();
expect($router.navigating).toBe(false);
}));
function registerComponent(name, options) {
var controller = options.controller || function () {};
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
if (options[hookName]) {
controller.prototype[hookName] = options[hookName];
}
});
function factory() {
return {
template: options.template || '',
controllerAs: name,
controller: controller
};
}
if (options.$canActivate) {
factory.$canActivate = options.$canActivate;
}
if (options.$routeConfig) {
factory.$routeConfig = options.$routeConfig;
}
$compileProvider.directive(name, factory);
}
function compile(template) {
elt = $compile('' + template + '
')($rootScope);
$rootScope.$digest();
return elt;
}
});