Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS testing directives with Karma, Jasmine and ngHtml2JsPreprocessor: template not loaded

I'm testing a custom AngularJS directive using Karma + Jasmine and the ngHtml2JsPreprocessor plugin in order to serve my directive template, but I don't understand why my directive seems to be unable to access to the template (the compiled element is empty). I defined the templateUrl in the directive this way:

templateUrl: 'templates/angular/mywidget.html'

my Karma config is the following (the relevant part of it):

        basePath: '../../main/webapp/static/',
        files: [
            {pattern: 'libs/angular/angular.js', watch: false},
            {pattern: 'libs/angular-resource/angular-resource.js', watch: false},
            {pattern: 'libs/angular-mocks/angular-mocks.js', watch: false},
            {pattern: 'libs/angular-ngkit/js/ngkit.js', watch: false},
            {pattern: 'libs/jquery/dist/jquery.js', watch: false},
            'templates/angular/*.html',
            'js/angular/**/*.js',
            '../../../test/js/spec/angular/*.js'
        ],
        preprocessors: {
            'templates/angular/*.html': ['ng-html2js']
        },
        ngHtml2JsPreprocessor: {
            moduleName: 'templates'
        },
        browsers: [
            'PhantomJS'
        ],
        plugins: [
            'karma-phantomjs-launcher',
            'karma-jasmine',
            'karma-ng-html2js-preprocessor'
        ],

and in my test:

beforeEach(module('templates'));
beforeEach(module('ngResource'));
beforeEach(module('mywidget'));

the "funny" part is that if I test the template cache, the templates gets loaded and compiled:

beforeEach(inject(function(_$templateCache_) {
    var template = _$templateCache_.get('templates/angular/mywidget.html');
}));

template exists! Why my directive can't use it? (in the browser it works perfectly). If I replace templateUrl with an inline template in my directive, it gets rendered properly in the test... but I have to use an external template, is not acceptable to put it inline... I'm stuck!! Any smart idea?

UPDATE (what I've tried so far):

  1. write a new directive with no logic (to exclude that the problem was in the implementation), simply:

    angular.module('modulename', []). directive('foo', function() { return { templateUrl: 'path/to/template.html' } });

  2. simplify the template content to:

    <div>hello world</div>

  3. change the template path

  4. change the template name

  5. change the location of karma config file

  6. removes extra dependencies like "ngResource" and similar

  7. reinstalling all the bower dependencies (and cleaning the cache)

  8. reinstalling all the npm packages

  9. run tests from the command line (I was running test in Intellij Idea using the Karma plugin)

  10. Pounding my fists against the desk while throwing a Fibonacci sequences of blasphemies that would have killed the pope if he had listened to it!

(the latest one usually brings me to the solution... but not this time)

UPDATE 2:

I bypassed the problem by defining my directive in this way:

directive('mywidget', function($templateCache) {
        var config = {}; // directive definition (where you define "link", "restrict"...)
        var templateUrl = 'templates/angular/mywidget.html';
        var cachedTemplate = $templateCache.get(templateUrl);
        if (cache) {
            config.template = cachedTemplate;
        }
        else {
            config.templateUrl = templateUrl;
        }
        return config;
});

By using this trick, my directive get rendered properly in tests. A this point it sounds a bug in AngularJS... it fails somehow to load the template from the cache. I'm really disappointed :(

like image 732
daveoncode Avatar asked Nov 25 '14 09:11

daveoncode


2 Answers

YEAAAAAAAAAAH!!! I finally solved this crazy issue!!! The problem was that I was using different versions of angular dependecies (angular-mocks, angular-resource...) it turned out that If you are using, let's say, angular 1.2.27, then you MUST also be sure to use angular-mocks 1.2.27, angular-resource 1.2.27 and so on because otherwise there may be conflicts like the one I faced. Of course it wasn't my intention to use different versions, but somehow by installing my dependencies via bower I get these libraries "unaligned".

like image 166
daveoncode Avatar answered Nov 04 '22 01:11

daveoncode


Try appending the compiled element to the document body with

angular.element(document.body).append(element);

You can put it in a beforeEach block:

beforeEach(function() {
  inject(function(_$compile_, _$rootScope_) {
    var compile, element, rootScope, scope;
    compile = _$compile_;
    rootScope = _$rootScope_;
    scope = rootScope;
    element = angular.element('<hello-directive></hello-directive>');
    compile(element)(scope);
    angular.element(document.body).append(element);
    scope.$apply();
  });
});
like image 1
kabebop Avatar answered Nov 04 '22 01:11

kabebop