Skip to content

Commit 2f7f2a7

Browse files
committed
fix: Report invalid state name redirection - #8
1 parent 01bcaf7 commit 2f7f2a7

File tree

2 files changed

+106
-8
lines changed

2 files changed

+106
-8
lines changed

src/angular-ui-router-default.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@ angular.module(moduleName, ['ui.router'])
3030

3131
function fetchTarget(): ng.IPromise<any> {
3232
var target = $state.get(nextState, $state.$current);
33-
nextState = (target || {}).name;
33+
34+
if (!target) {
35+
// default specification is invalid, let ui-router report the problem...
36+
return transitionTo.call($delegate, nextState, nextParams, nextOptions);
37+
}
38+
39+
nextState = target.name;
3440

3541
var absRedirectPromise = getAbstractRedirect(target);
3642
pendingPromise = absRedirectPromise;

test/angular-ui-router-default.spec.ts

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe('navigating to state', function() {
5656
}); // with abstract = false
5757

5858

59-
describe("with abstract = true", function() {
59+
describe("with abstract = true and no default", function() {
6060

6161
beforeEach(mock.module(function($stateProvider: ng.ui.IStateProvider) {
6262
$stateProvider
@@ -89,14 +89,44 @@ describe('navigating to state', function() {
8989
}).toThrowError("Cannot transition to abstract state 'base.abstract'");
9090
}));
9191

92-
}); // with abstract = true
92+
}); // with abstract = true and no default
9393

9494
let members = ["abstract", "default"];
9595

9696
for (let member of members) {
9797

9898
describe("abstract with " + member + " =", () => {
9999

100+
describe("INVALID_STATE", function() {
101+
beforeEach(mock.module(function($stateProvider: ng.ui.IStateProvider) {
102+
$stateProvider.state('base', {
103+
abstract: true,
104+
[member]: 'INVALID_STATE'
105+
});
106+
}));
107+
108+
it("should throw an informative error", mock.inject(function($state: IStateService, $rootScope: ng.IRootScopeService) {
109+
expect(function() {
110+
$state.go('base'); $rootScope.$digest();
111+
}).toThrowError(/^Could not resolve.*INVALID_STATE/);
112+
}));
113+
}); // invalid
114+
115+
describe(".INVALID_STATE", function() {
116+
beforeEach(mock.module(function($stateProvider: ng.ui.IStateProvider) {
117+
$stateProvider.state('base', {
118+
abstract: true,
119+
[member]: '.INVALID_STATE'
120+
});
121+
}));
122+
123+
it("should throw an informative error", mock.inject(function($state: IStateService, $rootScope: ng.IRootScopeService) {
124+
expect(function() {
125+
$state.go('base'); $rootScope.$digest();
126+
}).toThrowError(/^Could not resolve.*\.INVALID_STATE/);
127+
}));
128+
}); // .invalid
129+
100130
describe("'.child'", function() {
101131

102132
beforeEach(mock.module(function($stateProvider: ng.ui.IStateProvider) {
@@ -199,7 +229,23 @@ describe('navigating to state', function() {
199229
expect($state.current.name).toEqual('base.abstract.child2');
200230
}));
201231

202-
}); // with abstract = function() { return ...; }
232+
it("should throw an informative error for INVALID_STATE", mock.inject(function($state: IStateService, $rootScope: ng.IRootScopeService) {
233+
$state.go('base'); $rootScope.$digest();
234+
state = 'INVALID_STATE';
235+
expect(function() {
236+
$state.go('base.abstract'); $rootScope.$digest();
237+
}).toThrowError(/^Could not resolve.*INVALID_STATE/);
238+
}));
239+
240+
it("should throw an informative error for .INVALID_STATE", mock.inject(function($state: IStateService, $rootScope: ng.IRootScopeService) {
241+
$state.go('base'); $rootScope.$digest();
242+
state = '.INVALID_STATE';
243+
expect(function() {
244+
$state.go('base.abstract'); $rootScope.$digest();
245+
}).toThrowError(/^Could not resolve.*\.INVALID_STATE/);
246+
}));
247+
248+
}); // () => state
203249

204250
describe("['$rootScope', function($rootScope) => $rootScope.state]", function() {
205251

@@ -232,6 +278,22 @@ describe('navigating to state', function() {
232278
expect($state.current.name).toEqual('base.abstract.child2');
233279
}));
234280

281+
it("should throw an informative error for INVALID_STATE", mock.inject(function($state: IStateService, $rootScope: ng.IRootScopeService & StateScope) {
282+
$state.go('base'); $rootScope.$digest();
283+
$rootScope.state = 'INVALID_STATE';
284+
expect(function() {
285+
$state.go('base.abstract'); $rootScope.$digest();
286+
}).toThrowError(/^Could not resolve.*INVALID_STATE/);
287+
}));
288+
289+
it("should throw an informative error for .INVALID_STATE", mock.inject(function($state: IStateService, $rootScope: ng.IRootScopeService & StateScope) {
290+
$state.go('base'); $rootScope.$digest();
291+
$rootScope.state = '.INVALID_STATE';
292+
expect(function() {
293+
$state.go('base.abstract'); $rootScope.$digest();
294+
}).toThrowError(/^Could not resolve.*\.INVALID_STATE/);
295+
}));
296+
235297
}); // with abstract = ['$rootScope', function($rootScope: ng.IRootScopeService) { return ...; }]
236298

237299
describe("() => IPromise<string> that resolves", function() {
@@ -241,18 +303,31 @@ describe('navigating to state', function() {
241303
$stateProvider
242304
.state('base', {
243305
})
306+
.state('base.child', {
307+
})
244308
.state('base.abstract', {
245309
abstract: true,
246310
[member]: ['$q', '$rootScope', function($q: ng.IQService, $rootScope: ng.IRootScopeService) {
247311
var defer = $q.defer();
248312
setTimeout(function(){
249313
defer.resolve('base.abstract.child');
250314
$rootScope.$digest();
251-
});
315+
}, 5);
252316
return defer.promise;
253317
}]
254318
})
255319
.state('base.abstract.child', {
320+
abstract: true,
321+
[member]: ['$q', '$rootScope', function($q: ng.IQService, $rootScope: ng.IRootScopeService) {
322+
var defer = $q.defer();
323+
setTimeout(function(){
324+
defer.resolve('.grandchild');
325+
$rootScope.$digest();
326+
}, 5);
327+
return defer.promise;
328+
}]
329+
})
330+
.state('base.abstract.child.grandchild', {
256331
})
257332
.state('base.abstract2', {
258333
abstract: true,
@@ -261,7 +336,7 @@ describe('navigating to state', function() {
261336
setTimeout(function(){
262337
defer.resolve('base.abstract2.child');
263338
$rootScope.$digest();
264-
});
339+
}, 1);
265340
return defer.promise;
266341
}]
267342
})
@@ -273,7 +348,24 @@ describe('navigating to state', function() {
273348
mock.inject(function($state: IStateService, $rootScope: ng.IRootScopeService) {
274349
$state.go('base.abstract')
275350
.then(function() {
276-
expect($state.current.name).toBe('base.abstract.child');
351+
expect($state.current.name).toBe('base.abstract.child.grandchild');
352+
})
353+
.catch(function() {
354+
throw new Error('Should not be here');
355+
})
356+
.finally(done);
357+
358+
$rootScope.$digest();
359+
});
360+
});
361+
362+
it("should work for relative states", function(done) {
363+
mock.inject(function($state: IStateService, $rootScope: ng.IRootScopeService) {
364+
$state.go('base.child');
365+
$rootScope.$digest();
366+
$state.go('^.abstract')
367+
.then(function() {
368+
expect($state.current.name).toBe('base.abstract.child.grandchild');
277369
})
278370
.catch(function() {
279371
throw new Error('Should not be here');
@@ -336,7 +428,7 @@ describe('navigating to state', function() {
336428
setTimeout(function(){
337429
defer.reject('This is a rejection');
338430
$rootScope.$apply();
339-
});
431+
}, 5);
340432
return defer.promise;
341433
}]
342434
})

0 commit comments

Comments
 (0)