Jeanluca Scaljeri
Jeanluca Scaljeri

Reputation: 29109

Angular: How to spy on $element.on

I have a directive which does something like this in its link function

angular.module('myApp')
    .directive('barFoo', function() {
        return {
            restrict: 'E',
            link: function (scope, element) {
                element.on('click', ....);
            }
        };
    });

Now I would like to verify in my unit test that it calls on correctly

element = angular.element('<bar-foo></bar-foo>');
$compile(element)(scope);
spyOn(element, 'on');
...
expect(element.on).toHaveBeenCalled();

It tells me the spy is not called. From what I've found on the web, angular/jquery creates a new wrapper around the DOM element every time. Meaning the the element inside my directive is not the same as the element in my spec file. Most likely (not verified) the element[0] probably are the same. I've also tried to spy on angular.element

var mockEl = { on: angular.noop };
spyOn(angular, 'element').andReturn(mockEl);
spyOn(mockEl, 'on');

but that seems to break more than it fixes (I also need functions like isloateScope for example).

Anyway, is there some easy way I can spy on the on function of the element used inside a directive?

Upvotes: 1

Views: 3601

Answers (3)

sean helvey
sean helvey

Reputation: 27

If you've compiled your element in in a beforeEach like this:

myEl = '<div my-el>MY EL</div>';
scope = $rootScope.$new();
directiveElement = $compile(myEl)(scope);
scope.$digest();

Try replacing mousedown with the event you are testing like this:

expect(directiveElement.on.mousedown).toBeDefined;

Upvotes: 0

Estus Flask
Estus Flask

Reputation: 222379

Link function can be tested separately

element = angular.element('<bar-foo');
spyOn(element, 'on');
BarFooDirective[0].link(scope, element);
expect(element.on).toHaveBeenCalled();

if it is simple enough (stays away from attrs, required controllers, dependencies), otherwise the spec will cause more problems than it can solve.

Otherwise, it can be tested like it is done by the framework:

element = angular.element('<bar-foo');
expect(angular.element._data(element[0]).events.click).toBeDefined();

For real-world directive which may have more than one click listener defined in either itself or child directives it is not enough to make sure that listeners exist. Generally you may want to encapsulate internal functions, but anonymous click handler can also be exposed to scope for the purpose of testing:

element = angular.element('<bar-foo');
expect(angular.element._data(element[0]).events.click).toContain(scope.onClick);

Upvotes: 2

Alexandr Lazarev
Alexandr Lazarev

Reputation: 12882

Well, it's not necessary to test on method. You can test event bindings with the help of triggerHandler()

link: function (scope, element) {
    element.on('click', 'something to heppen');
}

Test:

element = angular.element('<bar-foo></bar-foo>');
$compile(element)(scope); 
$(element[0]).triggerHandler('click');
expect('something binded to click event to happen');

Upvotes: 1

Related Questions