jQuery Trigger Event in AngularJS Karma Test

13,973

Solution 1

I hope this works

  var e = $.Event('keydown');
  e.which = 65;

  $(inputEl).trigger(e); // convert inputEl into a jQuery object first
  // OR
  angular.element(inputEl).triggerHandler(e); // angular.element uses triggerHandler instead of trigger to trigger events

Solution 2

TLDR: load jQuery before AngularJS.

At first, you may think that jQuery is not included. However, it is included. If it were not, the test would fail at $.Event.

You have 2 dependencies on jQuery here:

  1. $.Event, a direct reference; and
  2. $, indirectly referenced by angular.element();

When AngularJS is loaded, it looks for a previously loaded jQuery instance, and in case it does exist, it makes element just an alias to $. Otherwise it loads a reference to jqLite, which does not have a trigger method.

So, I'm sure you do have jQuery loaded, but that does not mean AngularJS will use it. To do so, you must ensure jQuery is loaded (anywhere) before AngularJS.

When angular.element() refers to jqLite, it would never call jQuery, even after jQuery is loaded. So you can only call methods defined on jqLite:

angular.element(inputEl).triggerHandler(e);

However, jqLite's triggerHandler would not perform event bubbling, it's not reliable to mock raising events. jQuery's trigger is way better.

To use jQuery, do this on your HTML:

<script src="jquery.js">
<script src="angular.js">

Or on your karma.conf.js:

files: [
  'lib/jquery-1.10.2.min.js',
  'lib/angular.js',
  'lib/angular-mocks.js',
  'test/**/*Spec.js'
],

Reference:

Suggestions on getting the keydown event to work on the input?

You have found one approach by working-around the problem. It's not a big deal for your unit tests, where you would want a better DOM library, but now you know what's going on.

PS: I really liked answering this one. A question from more than a year, viewed by 6k users, and still no answer to the core problem.

Share:
13,973
Justin Noel
Author by

Justin Noel

Hi, I'm Justin Noel. I'm a Full Stack/Mobile Developer. I dabble in : Firebase, Ionic Framework, StencilJS, React, React Native, AngualrJS, Angular, Node, PHP, LAMP, Cordova, PhoneGap, MongoDB, MySQL. Twitter: https://twitter.com/calendee Web: https://calendee.com

Updated on June 13, 2022

Comments

  • Justin Noel
    Justin Noel almost 2 years

    I'm trying to test a new directive I'm writing. However, I can't seem to get the keydown event to trigger with jQuery inside Karma/Jasmine.

    Here is a simplified version of the test :

    'use strict';
    
    describe('', function() {
    
      var $compile;
      var $scope;
    
      beforeEach(inject(function(_$compile_, _$rootScope_) {
        $compile = _$compile_;
        $scope = _$rootScope_.$new();
      }));
    
      describe('Getting Trigger To Work', function() {
    
        it('Should Trigger a KeyDown Event', function() {
    
          var el = $compile('<form name="testing"><input id="field1" name="testfield" ng-model="result" type="text" ng-minlength="5" ng-maxlength="50"/></form>')($scope);
          console.log(el);
    
          var inputEl = el.find('input');
          console.log(inputEl);
    
          var e = $.Event('keydown');
          e.which = 65;
    
          inputEl.trigger(e);
    
        });
    
      });
    
    });
    

    My karma config includes AngularJS and jQuery. Here's that section:

    //
    files: [
      'lib/angular.js',
      'lib/angular-mocks.js',
      'lib/jquery-1.10.2.min.js',
      'test/**/*Spec.js'
    ],
    

    When I run the test, I get an error that my input element has no trigger method :

    INFO [watcher]: Changed file "/Volumes/Macintosh HD/dev_libraries/val-on-timeout/test/valOnTimeoutSpec.js".
    LOG: Object{0: <form name="testing" class="ng-scope ng-pristine ng-valid"><input id="field1" name="testfield" ng-model="result" type="text" ng-minlength="5" ng-maxlength="50" class="ng-pristine ng-valid"></form>, length: 1}
    LOG: Object{0: <input id="field1" name="testfield" ng-model="result" type="text" ng-minlength="5" ng-maxlength="50" class="ng-pristine ng-valid">, length: 1}
    Chrome 32.0.1700 (Mac OS X 10.8.5)  testing valOnTimeout Should load FAILED
        TypeError: Object [object Object] has no method 'trigger'
            at null.<anonymous> (/Volumes/Macintosh HD/dev_libraries/val-on-timeout/test/valOnTimeoutSpec.js:79:21)
    Chrome 32.0.1700 (Mac OS X 10.8.5): Executed 1 of 1 (1 FAILED) ERROR (0.327 secs / 0.033 secs)
    

    At first, you may think that jQuery is not included. However, it is included. If it were not, the test would fail at $.Event.

    Suggestions on getting the keydown event to work on the input?

  • Justin Noel
    Justin Noel over 10 years
    I got another identical answer via Twitter, but you get the vote here. $(inputEl).trigger(e) did the trick. Thanks