javascript - Test an Angular directive that uses jQuery - Stack Overflow

I am having trouble writing unit tests for some of my Angular directives. Particularly, those that use

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, $pile) {
        scope = $rootScope;
        elm = angular.element('<div clickhide></div>');
        $pile(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_ponents/jquery/dist/jquery.min.js',
      'app/bower_ponents/angular/angular.js',
      'app/bower_ponents/angular-route/angular-route.js',
      'app/bower_ponents/angular-sanitize/angular-sanitize.js',
      'app/bower_ponents/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?

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, $pile) {
        scope = $rootScope;
        elm = angular.element('<div clickhide></div>');
        $pile(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_ponents/jquery/dist/jquery.min.js',
      'app/bower_ponents/angular/angular.js',
      'app/bower_ponents/angular-route/angular-route.js',
      'app/bower_ponents/angular-sanitize/angular-sanitize.js',
      'app/bower_ponents/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?

Share Improve this question edited Aug 9, 2014 at 3:10 Jake asked Jul 25, 2014 at 16:37 JakeJake 1,1851 gold badge14 silver badges26 bronze badges 13
  • Could you try expect(elm.find(":hidden").length).toBe(1);? – glepretre Commented Jul 28, 2014 at 9:07
  • It doesn't even get to that line because it has an error on the line above because of the thing I described. – Jake Commented Jul 29, 2014 at 15:12
  • oh ok. How have you tried to load jQuery in your app? – glepretre Commented Jul 30, 2014 at 7:07
  • In my karma config file, I load jQuery as the first file in the "files" section and jQuery works. It loads. But for some reason angular still uses jQuery Lite as the default element wrapper. – Jake Commented Jul 31, 2014 at 15:08
  • 1 Not yet. I would like to but haven't tried too hard to figure it out. In the meantime, I use integration testing (Protractor) to cover my directives. – Jake Commented Oct 31, 2014 at 21:44
 |  Show 8 more ments

2 Answers 2

Reset to default 2

Try to override $ in beforeEach block of the test.

beforeEach(function() {
  $.fn.hide = function() {
    this.css( "display", "none" )
  };
});

I was able to resolve similar issue by loading jquery before angular. Also if $ is failing for you, You can always use angular.element('') in place of $

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745361548a4624383.html

相关推荐

  • javascript - Test an Angular directive that uses jQuery - Stack Overflow

    I am having trouble writing unit tests for some of my Angular directives. Particularly, those that use

    4小时前
    20

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信