7

I am trying to test a Directive which uses external template. I tried all the following solutions with no luck:

I created a test directive (a simple div) and tested it using an inline 'template' and external 'templateUrl'. The inline solution works while the external doesn't:

   angular.module('AdUnit').directive('actionButton',function($location){
        return{
            scope:{
                actionName: '@'
            },
            restrict: 'E',
            //template: "<div ng-click='click()'>action button</div>",
            templateUrl: '/staticfiles/adunit/html/directives/actionButtonTemplate.html',
            controller: ['$scope', function($scope){
                $scope.click = function(){
                    $scope.$emit('ACTION_CLICK', $scope.actionName);
                }
            }]

        }
    });



    describe("Unit: Testing action button directive", function() {
    var elm, scope, linkFn;
    beforeEach(
        module('AdUnit')
    );

    beforeEach(module('/staticfiles/adunit/html/directives/actionButtonTemplate.html'));

    beforeEach(inject(function($rootScope, $compile) {
        elm = angular.element('<action-button action-name="post-action-0"></action-button>');
        scope = $rootScope;
        linkFn = $compile(elm);
        linkFn(scope);
        scope.$digest(); // have to digest to bring html from templateCache
        console.log('post compile',elm.html());// <== the html here still have {{}}
    }));

    it('should show a thumb',function() {

            console.log('post link',elm.html());// <== the html is bound

            expect(elm.text()).toBe("action button");

    });
});

My Karma config file:

  module.exports = function(config) {
  config.set({

    // base path, that will be used to resolve files and exclude
    basePath: '',


    // frameworks to use
    frameworks: ['jasmine'],


    // list of files / patterns to load in the browser
    files: [
        'http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js',
        'http://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js',
        'http://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular-route.js',
        'http://code.angularjs.org/1.0.6/angular-mocks.js',
        '../html/*.html',
        '../html/directives/*.html',
        '../js/adUnit.js',
        '../js/controllers/*.js',
        '../js/directives/*.js',
        '../js/services/*.js',
        '../*.js',
         '../**.*.js',
         '**/*.tests.js'
    ],

    preprocessors : {
        '../html/**/*.html': ['ng-html2js']
    },

     /* ngHtml2JsPreprocessor: {
          'AdUnit': '/staticfiles/adunit/html/directives/actionButtonTemplate.html'
          *//*moduleName: '/staticfiles/adunit/html/directives/internalPlayerTemplate.html'*//*
      },*/

    // list of files to exclude
    exclude: [

    ],


    // test results reporter to use
    // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
    reporters: ['progress'],


    // web server port
    port: 9876,


    // enable / disable colors in the output (reporters and logs)
    colors: true,


    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,


    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,


    // Start these browsers, currently available:
    // - Chrome
    // - ChromeCanary
    // - Firefox
    // - Opera (has to be installed with `npm install karma-opera-launcher`)
    // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)
    // - PhantomJS
    // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)
    browsers: ['Chrome'],


    // If browser does not capture in given timeout [ms], kill it
    captureTimeout: 60000,


    // Continuous Integration mode
    // if true, it capture browsers, run tests and exit
    singleRun: false
  });
};

I keep getting the following error:

Failed to instantiate module /staticfiles/adunit/html/directives/actionButtonTemplate.html due to:
Error: [$injector:nomod]

Any help will be appreciated!

EDIT: @MK Safi's answer solved my problem. I was missing the following:

 ngHtml2JsPreprocessor: {
     'moduleName': 'Templates',

     // Function that transforms the path to look exactly like
     // you have it in templateUrl in your Angular code
     //
     // Mine looks like this
     cacheIdFromPath: function(filepath) {
         return filepath.match(/\/staticfiles\/adunit\/html\/directives\/.*\.html/);
     }
  },

and before each test:

 beforeEach(module('Templates'));

it is important for the regular expression to point to the same path as the directive's "templateUrl", since html2js will cache those templates using this path (see html2js for more details about that)

Community
  • 1
  • 1
Yaniv Efraim
  • 6,633
  • 7
  • 53
  • 96
  • I don't use karma nor htm2js (testem and grunt-angular-templates here), but if html2js creates a templateCache attached to a module like `app`, you just need to import that module into the test with `module()` – Jesus Rodriguez Jan 12 '14 at 12:12
  • I am loading the module like you said: beforeEach(module('/staticfiles/adunit/html/directives/actionButtonTemplate.html')); (see in my code example). I also see the wrapped html template downloaded as js file on network sniffing – Yaniv Efraim Jan 12 '14 at 12:50

1 Answers1

7

I have this setup correctly in my tests and your setup looks right, except for a few things.

Make the following changes to your Karma config file:

ngHtml2JsPreprocessor = {
  'moduleName': 'Templates',

  // Function that transforms the path to look exactly like 
  // you have it in templateUrl in your Angular code
  //
  // Mine looks like this
  cacheIdFromPath: function(filepath) {
    return filepath.match(/views\/.*/)[0]
  }
}

Then in your test's beforeEach include the Templates module that you specified in your Karma config above: module('Templates')

beforeEach(function() {
  module('Templates')
})
M.K. Safi
  • 6,560
  • 10
  • 40
  • 58
  • no luck... I added: ngHtml2JsPreprocessor: { 'moduleName': 'Templates', // Mine looks like this cacheIdFromPath: function(filepath) { return filepath.match(/html\/.*/)[0] } }, and the beforeEach(function() { module('Templates') }). I see the html templates loaded as js files. I now get: Unexpected request: GET /staticfiles/adunit/html/directives/actionButtonTemplate.html No more request expected – Yaniv Efraim Jan 12 '14 at 12:58
  • 1
    @Yaniv Your templateUrl in your directive is this `'/staticfiles/adunit/html/directives/actionButtonTemplate.html'`. But your `cacheIdFromPath` function probably returns this: `'html/directives/actionButtonTemplate.html'`. They don't match. Make them match. – M.K. Safi Jan 12 '14 at 13:08
  • This is finally working. Thanks man!! One last question - this is only worked for me with hard coded cacheIdFromPath: cacheIdFromPath: function(filepath) { return "/staticfiles/adunit/html/directives/internalPlayerTemplate.html" }. How can I refer the regex to match exactly what "templateUrl" points to (for all directives...) – Yaniv Efraim Jan 12 '14 at 13:44
  • 1
    You're welcome! Re: your other question. You could `console.log(filepath)` inside `cacheIdFromPath` and program your `cacheIdFromPath` to handle all cases. My guess is having the following return value in your `cacheIdFromPath` function might do it: `return filepath.match(/\/staticfiles\/adunit\/html\/.*/)[0]` – M.K. Safi Jan 12 '14 at 13:59
  • 1
    Great. What I did not understand is the "[0]" in the end of the regular expression. I removed it and now it works for all directive templates under "directives" library: cacheIdFromPath: function(filepath) { return filepath.match(/\/staticfiles\/adunit\/html\/directives\/.*\.html/); } – Yaniv Efraim Jan 12 '14 at 14:20
  • I get the following: errors: Error: [$injector:modulerr] Failed to instantiate module Templates due to: Error: [$injector:nomod] Module 'Templates' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument. http://errors.angularjs.org/1.2.12/$injector/nomod?p0=Templates – user730009 Mar 13 '14 at 17:39
  • Thanks, that took me all afternoon trying to get this working, your answer finally pointed me in the right direction. – Stephen Simpson Jul 02 '14 at 15:02
  • took me long time to sort out it, this answer saved me! Just one detail more. People does not forget to include your .js bower files that your APP is using (app/vendor/bower_components/...). You need to add all of them. – Thiago C. S Ventura Aug 09 '14 at 10:37