1

I'm trying to test a timeout, but I'm having trouble with the scope of the element. I'm new to writing JS tests.

In the it( 'should have timer defined at start' ) test, my variable logs as undefined. Why is that?

I thought isolateScope() would pull the scope of the directive in?

My test looks like this:

https://plnkr.co/edit/IMrPd9g6HFSkgHizFF9p?p=preview

describe( 'Testing timeout', function(){
  var scope, $timeout, element;

  beforeEach(inject( function ($rootScope, $compile, _$timeout_, $injector ){
            scope = $rootScope.$new();
            $timeout = _$timeout_;

            scope.onWarning = function(){
                scope.warned = true;
            };

            element = '<div timeout on-warning="onWarning" on-timeout="onTimeout"></div>';
            element = $compile(element)(scope);

            scope.$apply();
            scope.$digest();

  }));

  it( 'should have timer defined at start', function(){
    console.log( element.isolateScope() , scope )
    expect( element.isolateScope().timeoutService.timer ).not.toBeFalsy;
  });

  it( 'should have run warning function', function(){
    $timeout.flush( 5000 );
    expect( scope.warned ).toBe( true );
  });

});

My directive looks like this:

app.directive( 'timeout', function( timeoutService ){
  return {
    restrict: "A",
    scope: {
                onWarning: "&", // what function to fire when showing a warning
            },
            link: function( scope, elem, attr ){
                scope.timeoutService = timeoutService;
                if( !scope.onWarning ){ 
                    throw new Error( "Must provide on-warning for timeout directive." );
                }
                //console.log( scope.onWarning, scope.onTimeout );
                // register timeouts and warnings with the service
                timeoutService.onWarning = scope.onWarning; 
            }
  }
}); 

app.service( 'timeoutService', function( $timeout ){
  var _this = this;
  var timer = null;
  var time = 5000;

  this.startTimer = function(){
    timer = $timeout( function(){
      if( _this.onWarning ){
        _this.onWarning()();
      }
    }, time)
  }

  this.startTimer();

})

Maybe I'm testing incorrectly?

2 Answers 2

1

You need to call

beforeEach(module('plunker'));

In your 'Testing timeout' describe block. You were only calling it in the other describe block

Sign up to request clarification or add additional context in comments.

Comments

0

I would just look at scope, since the angular team has already tested the $compile service well enough you know that this is what's being used for the directive's scope. To tell the truth, I usually just use a Controller defined outside the directive and test that. There doesn't seem to be anything in your directive that needs you to compile it to test it, except that you're using the link function instead of a controller.

Your directive is not attaching the timeoutService to its scope, which is good. So you can't look at it (and you shouldn't!).

AngularJS already has the $timer service. Wrapping it again in your own service is both unnecessary and error prone, so I wouldn't do this. Plus, it has no idea about the onWarning function passed to your directive. Instead, just call $timeout from your directive and check that it has no pending calls or that the data is in the right state after $timeout.flush(); Make sure to test that you canceled any pending timeouts on $destroy.

5 Comments

the example I created is a little simplified, but what the purpose of this directive is is to show a warning message to the user in a div after a certain amount of time of being idle. Which is the purpose of the warning function
Sorry, typo. $timeout is what I meant. You're already using it. You don't need to create another service that's just a wrapper around the existing service and does the same things (tracking existing timeouts, etc.), but in a way that's not as thoroughly tested and written by someone with less experience than the Google team.
Note that I'm very familiar with passing functions into directives to be called at a later time. I was able to look at your code and see what you were trying to do, but you were doing it in a way that's unnecessarily complex and likely to cause you problems over the long haul.
I understand this is unnecessarily complex, but I want a reusable directive which is independent from everything else and can be pulled in as a dependency.
You're already using $timeout in your service, and your directive depends on the service. So by going through the unnecessary service, you're adding a dependency that you could eliminate. But whatever.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.