Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I call triggerHandler with a specific fake 'event' parameter?

I'm trying to test that on a link click 'preventDefault' is called. However, I have difficulties to replace a real 'event' object with one I can spy on:

Here is how I trigger the click event:

var e = jasmine.createSpyObj('e', [ 'preventDefault' ]);
$element.triggerHandler('click', [e]);

However, when the directive code is being run, the event element is not replaced with a fake one:

$element.on('click', function(event) {
    console.log(event);
}

I tried different ways of adding a second parameter to triggerHandler - as an array, as an object, just some line, etc. Neither worked.. It is also not that many examples of triggerHandler together with additional parameters, so I feel a little bit lost...

Thanks in advance!

like image 982
Anelook Avatar asked Mar 11 '14 17:03

Anelook


2 Answers

We also ran into this issue when we wanted to pass an event for testing that had a specific value for the which property.

As c0bra pointed out, the triggerHandler function creates its own dummy event object which it then passes on to the event handler. However, you can take advantage of the fact that the triggerHandler function will extend the dummy event with an event object passed in if there is a valued type property on the event: https://github.com/angular/angular.js/blob/v1.6.5/src/jqLite.js#L1043

triggerHandler: function(element, event, extraParameters) {
    ...
    // If a custom event was provided then extend our dummy event with it
    if (event.type) {
        dummyEvent = extend(dummyEvent, event);
    }
    ...
}

After inspecting the Angular code, we found that when you call element.triggerHandler from your test, it will first call an anonymous function which then calls Angular's triggerHandler function. When Angular's triggerHandler function is called, it is passed the element as the first argument, then the first argument that you passed to triggerHandler in your test as the second argument, then your second argument that you passed to triggerHandler as the third etc. https://github.com/angular/angular.js/blob/v1.6.5/src/jqLite.js#L1068

What that then means is you can pass an object to triggerHandler in your test as the first argument, and that object's properties will be appended to the dummy event object (or overwritten if there was already a property on the dummy object with the same name), thus allowing you to pass specific values and add spies for testing.

Note that the event type will need to be passed to triggerHandler as the type property on your object so as to satisfy the if condition mentioned above. As an example, this is how we used triggerHandler in our test:

element.triggerHandler({
    type: 'keydown',
    which: 13
});
like image 155
Lisa Avatar answered Nov 18 '22 14:11

Lisa


The docs say that triggerHandler() passes a dummy object to the handler: http://docs.angularjs.org/api/ng/function/angular.element

If you check the source, you can see that triggerHandler() creates its own event object, and then passes your second argument as the event data, not the actual event object:

https://github.com/angular/angular.js/blob/master/src/jqLite.js#L882

Relevant code:

var event = [{
  preventDefault: noop,
  stopPropagation: noop
}];

forEach(eventFns, function(fn) {
  fn.apply(element, event.concat(eventData));
});

I've used jQuery's internal event simulator for creating my own events. That may work for you: http://wingkaiwan.com/2012/09/23/triggering-mouse-events-with-jquery-simulate-when-testing-in-javascript/

like image 2
c0bra Avatar answered Nov 18 '22 14:11

c0bra