Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test an Angular directive that uses jQuery

I am having trouble writing unit tests for some of my Angular directives. Particularly, those that use jQuery inside the directive. I have contrived an minimal example below that illustrates my issue.

This silly directive binds the click event to the element. When clicked, it hides the element. According to Angular, elements passed into directives will be wrapped as jQuery elements. if jQuery is available it will use jQuery, otherwise it will use Angular's jQuery Lite. Indeed, if I use this directive in a browser with jQuery included, the directive works and will hide the clicked element.

angular.module('myApp').directive('clickhide', function() { return {
    link: function(scope, element, attrs) { 
        element.bind('click', function(e) {
            element.hide();
            scope.$digest();
        });
    }
}});

Here is the Karma unit test spec for this directive:

describe('clickhide', function() {
    var scope, elm;

    beforeEach(module('MyApp'));

    beforeEach(inject(function($rootScope, $compile) {
        scope = $rootScope;
        elm = angular.element('<div clickhide></div>');
        $compile(elm)(scope);
        scope.$digest();
    }));

    it('should do a click', function() {
        elm[0].click();
        //OOPS: undefined is not a function error
        expect($(elm).find(":hidden").length).toBe(1);
    });
});

When I run this test, it fails with an error of "undefined is not a function" meaning that jQuery was not loaded and Angular used jQuery Lite instead, which doesn't define hide().

I have found two ways to work around this.

  1. Don't use jQuery. Requires rewriting my directives which I don't want to do.

  2. Explicitly wrap elements in my directives to make them jQuery elements: $(element).hide(). Also requires modifications to my directives, which I would prefer not to do. If there is no alternative, I can certainly do this.

I feel like it should be possible to get Angular to automatically use jQuery inside a directive when unit testing like it does when using in a web browser. I believe the key point is this line from Angular's documentation:

To use jQuery, simply load it before DOMContentLoaded event fired.

Its not clear to me when DOMContentLoaded happens when running unit tests. My Karma config file includes jQuery as the first file, before angular:

module.exports = function(config) {
  config.set({
    basePath: '../../',
    frameworks: ['jasmine', 'ng-scenario'],
    files: [
      'app/bower_components/jquery/dist/jquery.min.js',
      'app/bower_components/angular/angular.js',
      'app/bower_components/angular-route/angular-route.js',
      'app/bower_components/angular-sanitize/angular-sanitize.js',
      'app/bower_components/angular-mocks/angular-mocks.js',
      ...
    ],
    exclude: [],
    reporters: ['progress'],
    port: 9876,
    runnerPort: 9100,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    captureTimeout: 60000,
    singleRun: false
  });
};

Do I need to inject jQuery into the test? Maybe this is a limitation of Karma? Anyone know?

like image 517
Jake Avatar asked Jul 25 '14 16:07

Jake


1 Answers

Try to override $ in beforeEach block of the test.

beforeEach(function() {
  $.fn.hide = function() {
    this.css( "display", "none" )
  };
});
like image 131
Chen Eshchar Avatar answered Oct 21 '22 02:10

Chen Eshchar