TypeError: Cannot read property 'defaultPrevented' of undefined

All we need is an easy explanation of the problem, so here it is.

I am getting this error when I do the following test:

it('should call pauseAnimationInterval if in focus', inject(function(SearchBoxData, intervalManager, $timeout){
  SearchBoxData.init_array = [];
  SearchBoxData.inFocus = true;
  SearchBoxData.init(intervalManager);
  console.log(intervalManager.pauseTimeout);
  console.log(intervalManager.pauseTimeoutTime);
  console.log($timeout);
  $timeout.flush(intervalManager.pauseTimeoutTime+1);
  expect(rootScope.$broadcast).toHaveBeenCalledWith('onPauseInterval', intervalManager.loopIndex);
}));

It breaks on $timeout.flush(intervalManager.pauseTimeoutTime+1);
The $timeout method is called in the intervalManager.pauseAnimationInterval() which is inside SearchBoxData.init(intervalManager):

intervalManager.pauseAnimationInterval = function (){

  intervalManager.safeCancel(intervalManager.continueInterval);
  intervalManager.safeCancel(intervalManager.initInterval);
  intervalManager.initInterval = null;
  intervalManager.continueInterval = null;

  intervalManager.pauseTimeout = $timeout(function () {
    if(intervalManager.inFocus === true){
      intervalManager.loopIndex += 1;
      if(intervalManager.loopIndex >= intervalManager.maxLoopIndex){
        intervalManager.loopIndex = 0;
      }
      $rootScope.$broadcast("onPauseInterval", intervalManager.loopIndex);
      intervalManager.continueAnimationInterval();
    }else{
      // Important condition: retry after the timeout if no focus
      // main reason of glitch
      intervalManager.pauseAnimationInterval();
    }
  }, intervalManager.pauseTimeoutTime);

};

I was not getting this error before, I am not sure what I did wrong.

Update: Here is a stacktrace with unminified version of angularJS:

at /Users/foo/projects/bar/vendor/assets/javascripts/angular.js:9268:88
at Scope.$eval (/Users/foo/projects/bar/vendor/assets/javascripts/angular.js:11986:28)
at Scope.$digest (/Users/foo/projects/bar/vendor/assets/javascripts/angular.js:11812:31)
at Scope.$apply (/Users/foo/projects/bar/vendor/assets/javascripts/angular.js:12092:24)
at Object.fn (/Users/foo/projects/bar/vendor/assets/javascripts/angular.js:13627:36)
at Function.self.defer.flush (/Users/foo/projects/bar/spec/javascripts/lib/angular/angular-mocks.js:126:32)
at Function.$delegate.flush (/Users/foo/projects/bar/spec/javascripts/lib/angular/angular-mocks.js:1689:20)
at null.<anonymous> (/Users/foo/projects/bar/spec/javascripts/unit/services/searchboxdata_service_spec.js:71:16)

Update 2: I traced it back to the injection of the SearchBoxData factory. I am not sure what is wrong with it. Simply injecting it causes the error whenever I create a $timeout instance and use the flush method.

Update 3: I noticed this happens if I inject either $location or $route in one of the dependencies (indirect ones, it happens with some basic service that I use everywhere).

Update 4: Here is a plunker to reproduce it! http://plnkr.co/edit/7XweXI?p=info

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Scope events which are $broadcast()’d or $emit()’d are synchronous, and return the event object when they’re finished.

Some things in Angular depend on the returned event object, such as ngRoute.

So what you can do to fix this is simply add andCallThrough() when you create your spy, so that it calls the original function and yields a return value.

Alternatively, if you don’t want to use the real $broadcast, you can use andCallFake() to supply a fake function which can return anything you want.

Example:

beforeEach(function(){
  module('MyApp');
  inject(function($injector){
    rootScope = $injector.get('$rootScope');
    spyOn(rootScope,'$broadcast').andCallThrough(); // will prevent the reference error
  })
});

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply