angular-ui / ui-router

The de-facto solution to flexible routing with nested views in AngularJS
http://ui-router.github.io/
MIT License
13.54k stars 3k forks source link

How to invoke transition life cycle hooks in unit tests (jasmine) #3554

Closed parky128 closed 6 years ago

parky128 commented 6 years ago

General Query

Please direct general implementation questions to StackOverflow: http://stackoverflow.com/questions/ask?tags=angularjs,angular-ui-router

Please review the Sample Application which highlights common approaches: https://github.com/ui-router/sample-app-ng1

I have recently migrated to 1.0.x, and am struggling to figure out how to unit test (jasmine) $transition life cycle hooks I have registered in my application. I have posted a stackoverflow on this, but had no suggestions yet so thought I'd try here.

Say I have a controller with the following:

$transitions.onStart({ }, () => {
      //my logic here
    });

Previously I was using broadcasting the $stateChangeStart events, etc in my tests to hit my custom logic in the listener callbacks.

Now I am not using these events as per the migration docs, I want to know what is the correct way to hit the life cycle hooks in my tests? I wondered if $state.go('some-state') would work, but it doesnt. I am unsure what I need to do here, can someone please advise?

I have raised a SO question here but not been able to find out yet how others are approaching this.

@christopherthielen gave me some assistance in the gitter room chat, but I havent been able to get any further still.

christopherthielen commented 6 years ago

It looks like you got some help on StackOverflow (recommending you to isolate your unit test to the callback).

If you'd like to test using the router, I think your problem is that the view is never created because your test harness doesn't set up a "root template" for the views to populate.

Your test harness calls $state.go() which should invoke the hook. However, your hooks is created inside the controller for a view. However, none of your views will be activated unless there's a ui-view for them to fill. Try compiling a ui-view in your test harness so the root of your app is actually rendered:

  it('should render the view', inject(function($state) {
    var el = $compile('<div><ui-view></ui-view></div>', $rootScope.$new());
    $state.go('mystate');
    $rootScope.$digest();
    expect($state.current.name).toBe('mystate');
    expect(el.text()).toBe(... mystate view contents ...);
    expect(... transition hook things...);
  }));

Two other things to note:

1) If the controller registers a hook, the hook will not run for that transition, only for subsequent transitions. This is because views are rendered in response to a successful transition.

2) Be sure to deregister hooks when your controller is destroyed (using $scope.on('$destroy') for old school angularjs or $onDestroy for components)