You're using ui-router to manage Angular 1.x routing states.
You have a parent state that should always navigate to a child state, but without relying on ui-router-extras like $deepStateRedirect
. This is to avoid deep linking into the child state. Some logins need to operate this way, for instance, so the Terms of Service acknowledgement cannot be skipped.
This is an example ui-router state hierarchy that defines a series of states used for login pages.
$stateProvider
.state('login', {
url: '/login',
templateUrl: 'app/login/login.html',
controller: 'LoginController',
controllerAs: 'loginVm'
})
.state('login.tos', {
templateUrl: 'app/login/tos.html',
controller: 'TosController',
controllerAs: 'tosVm'
})
.state('login.credentials', {
templateUrl: 'app/login/credentials.html',
controller: 'credentialsController',
controllerAs: 'credVm'
});
Note the lack of urls for the child states. This prevents directly navigating to any child state.
Also note that $deepStateRedirect
(part of ui-router-extras would allow a configuration like the following to automatically navigate from the parent login
state to the child login.tos
state,
$stateProvider
.state('login', {
url: '/login',
templateUrl: 'app/login/login.html',
controller: 'LoginController',
controllerAs: 'loginVm',
deepStateRedirect: {
default: {
state: 'login.tos'
}
}
})
...
The problem with using $deepStateRedirect
in this case is it also allows a browser refresh, for instance, to remain in any child state like login.credentials
, which may not be desirable. (The same might be true of any sequential wizard, where navigating away and then back to the wizard should always start at the beginning.)
Without $deepStateRedirect
, therefore, it is expected that the LoginController
will be responsible for issuing $state.go('login.tos');
whenever the login
state is activated.
In order to test such a controller-mitigated state transition, the LoginController
must be instantiated by ui-router during testing. The following test harness involves compiling a simple HTML snippet that provides the <div ui-view />
tag required by ui-router. (Inspired by ui-router unit tests)
it('should chain from the parent state to the proper child state', function() {
$httpBackend.when('GET', function() {return true;}).respond(200);
$compile('<div> <div ui-view/> </div>')($rootScope);
$state.go('login');
$rootScope.$digest();
expect($state.is('login.terms')).to.be.true;
};
Note the use of $httpBackend
to mock responses to any request the LoginController
may make. If your controller doesn't make such requests, this mock isn't necessary.