Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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();
like image 460
miphe Avatar asked Oct 31 '14 12:10

miphe


1 Answers

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.

  • In the first fiddle
    • angular.js version 1.3.0
    • angular-mocks.js version 1.1.5
  • In the solved fiddle
    • angular.js version 1.3.0
    • angular-mocks.js version 1.3.0

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');

like image 71
miphe Avatar answered Oct 30 '22 22:10

miphe