diff --git a/src/transition/transition.ts b/src/transition/transition.ts index a142c668..6ba36c58 100644 --- a/src/transition/transition.ts +++ b/src/transition/transition.ts @@ -551,6 +551,11 @@ export class Transition implements IHookRegistry { error() { let state: State = this.$to(); + let redirects = 0, trans: Transition = this; + while((trans = trans.redirectedFrom()) != null) { + if (++redirects > 20) return `Too many Transition redirects (20+)`; + } + if (state.self.abstract) return `Cannot transition to abstract state '${state.name}'`; if (!Param.validates(state.parameters(), this.params())) diff --git a/test/core/stateServiceSpec.ts b/test/core/stateServiceSpec.ts index 112af09f..a219acde 100644 --- a/test/core/stateServiceSpec.ts +++ b/test/core/stateServiceSpec.ts @@ -62,6 +62,21 @@ describe('stateService', function () { .then(done, done); })); + it('should error after 20+ redirects', (done) => { + let errors = []; + $transitions.onEnter({ entering: "D" }, trans => trans.router.stateService.target('D')); + $transitions.onError({}, trans => { errors.push(trans.error()) }); + + $state.defaultErrorHandler(function() {}); + + $state.go("D").catch(err => { + expect(errors.length).toBe(21); + expect(err.message).toContain('Too many Transition redirects'); + done(); + }); + }) + + it("should not update the URL in response to synchronizing URL", ((done) => { $loc.setUrl('/a/b/c'); spyOn($loc, 'setUrl').and.callThrough();