Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeError: Cannot read property 'defaultPrevented' of undefined

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

like image 583
Alex C Avatar asked Mar 28 '14 19:03

Alex C


1 Answers

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
  })
});
like image 107
chronicsalsa Avatar answered Nov 16 '22 15:11

chronicsalsa