angularjsangularjs-scopemocha.jsdom-eventsjqlite

Trigger click event on an AngularJS directive in Mocha test suite


I have a regular angular app with a directive. This directive contains an element with a ng-click="clickFunction()" call. All works well when I click that element. I now need to write a test for this click, making sure that this function was actually run when the element was clicked - this is what I'm having trouble with.

Here's a jsfiddle to illustrate my issue: http://jsfiddle.net/miphe/v0ged3vb/

The controller contains a function clickFunction() which should be called on click. The unit test should imitate a click on the directive's element and thus trigger the call to that function.

The clickFunction is mocked with sinonjs so that I can check whether it was called or not. That test fails, meaning there was no click.

What am I doing wrong here?

I've seen the answer to similar questions like Testing JavaScript Click Event with Sinon but I do not want to use full jQuery, and I believe I'm mocking (spying on) the correct function.


Here's the js from the fiddle above (for those who prefer to see it here):

angular.js, angular-mocks.js is loaded as well.

// App
var myApp = angular.module('myApp',[]);

myApp.controller('MyCtrl', function($scope) {
    $scope.person = 'Mr';
    $scope.clickFunction = function() {
        // Some important functionality
    };
});

myApp.directive('pers', function() {
    return {
        restrict: 'E',
        template: '<h2 ng-click="clickFunction()" ng-model="person">Person</h2>',
    };
});

// Test suite
describe('Pers directive', function() {
    var $scope, $controller, template = '<pers></pers>', compiled;
    beforeEach(module('myApp'));

    beforeEach(inject(function($rootScope, $controller, $compile) {
        $scope = $rootScope.$new();
        ctrl = $controller('MyCtrl', {$scope: $scope});
        compiled = $compile(template)($scope);

        // Do I need to run a $scope.$apply() here?
        console.log($scope.$apply); // This is a function, apparently.
        //$scope.$apply();            // But running it breaks this function.
    })); 

    it('should render directive', function() {
        el = compiled.find('h2');
        expect(el.length).to.equal(1);
    });

    it('should run clickFunction() when clicked', function() {
        el = compiled.find('h2');
        sinon.spy($scope, 'clickFunction');

        // Here's the problem! How can I trigger a click?
        //el.trigger('click');
        //el.triggerHandler('click');
        expect($scope.clickFunction.calledOnce).to.be.true
    });
});

// Run tests
mocha.run();

Solution

  • Turns out the problem was quite hidden.

    Firstly the $scope.$digest and $scope.$apply functions broke the beforeEach function which ultimately led to the whole solution.

    Solution

    Do not mix angular versions.

    That was the whole problem, and gave me quite obscure errors.

    Thanks to Foxandxss from the #AngularJS IRC channel on freenode.


    The way to trigger events on the directive with jQlite was simply:

    someElement.triggerHandler('click');