johannesjo / generator-modular-angular

A truly modular yeoman generator for AngularJS all device apps.
MIT License
21 stars 6 forks source link

How to run karma tests on directives using templateUrl #22

Closed ddvorak closed 8 years ago

ddvorak commented 8 years ago

(Note: I'm learning angular and using your generator as a starting point) I'm trying to build out unit tests for a directive that uses templateUrl. Right now my test is very basic, I have an external template in scripts/ccc-header/ccc-header-d.html and I want to test that an img tag is in that template. Here is the basic test

it('should contain an img tag', inject(function($compile){
  element = angular.element('<ccc-header></ccc-header>');
  element = $compile(element)(scope);
  scope.$digest();
  console.log(element);
  //var elm = element.html();
  //expect(elm.find('img').length).toEqual(1);
}))

When I console log (in debug mode in karma) I just get back: LOG: Object{0: <ccc-header class="ng-scope"></ccc-header>, length: 1}

I've looked everywhere to try to find a solution but am not sure what I'm doing wrong exactly.

Any help would be greatly appreciated.

johannesjo commented 8 years ago

You need to load your templates first. The generator comes bundled with the karma-ng-html2js-preprocessor. You just need to add

beforeEach(module('templates'));

before the tests in question.

See also: http://stackoverflow.com/questions/15214760/unit-testing-angularjs-directive-with-templateurl

ddvorak commented 8 years ago

The generator automatically puts that in for me, to be more clear, this is the entire spec.js file that I am using.

'use strict';

describe('Directive: cccHeader', function() {

    // load the directive's module
    beforeEach(module('studentExchange'));
    beforeEach(module('templates'));

    var element,
        scope;
    //beforeEach(module('scripts/ccc-header/ccc-header-d.html'));
    beforeEach(inject(function($rootScope) {
        scope = $rootScope.$new();

    }));

    it('should contain an img tag', inject(function($compile){
        element = angular.element('<ccc-header></ccc-header>');
        element = $compile(element)(scope);
        scope.$digest();

        //var elm = element.html();
        console.log(element);
        //expect(elm.find('img').length).toEqual(1);
    }))
});

I guess my next question is if I need to alter anything to take into consideration that my config.js file has 'dist' pointed at './../../../../target/classes/static/' ? Do I need to add some prependPrefix value to my karam.conf file?

johannesjo commented 8 years ago

So it's still not working? But it is if you comment in this line

beforeEach(module('scripts/ccc-header/ccc-header-d.html'));

?

I'm not sure if I understand you correctly, but the dist-folder has nothing to do with the karma unit tests configuration. They're run for the dev files.

ddvorak commented 8 years ago

Sorry that comment was in there from me trashing trying to force it to work. No it does not work, it just spits out LOG: Object{0: , length: 1}, the partial with the html is never accessible through the element variable.

johannesjo commented 8 years ago

Could maybe post the contents of your karma-config.js please? Also: where are you're templates stored? Are they inside the app/scripts directory?

ddvorak commented 8 years ago

Templates are stored where the yo moda:d stores them so in app/scripts//-d.html Here's my karma config.

// Karma configuration
'use strict';

module.exports = function(config) {
    config.set({
        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',

        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine'],

        // list of files / patterns to load in the browser
        files: [
            // bower:js
            'app/bower_components/angular/angular.js',
            'app/bower_components/angular-animate/angular-animate.js',
            'app/bower_components/angular-aria/angular-aria.js',
            'app/bower_components/angular-cookies/angular-cookies.js',
            'app/bower_components/angular-resource/angular-resource.js',
            'app/bower_components/angular-sanitize/angular-sanitize.js',
            'app/bower_components/angular-ui-router/release/angular-ui-router.js',
            'app/bower_components/angular-messages/angular-messages.js',
            'app/bower_components/angular-material/angular-material.js',
            'app/bower_components/ng-fab-form/dist/ng-fab-form.js',
            'app/bower_components/lodash/lodash.js',
            'app/bower_components/restangular/dist/restangular.js',
            'app/bower_components/angular-mocks/angular-mocks.js',
            // endbower

            // modules first
            'app/scripts/**/_*.js',
            // all the rest of the files
            'app/scripts/**/*.js',
            // load html as well as required for karma-ng-html2js-preprocessor
            'app/scripts/**/*.html'
        ],

        // list of files to exclude
        exclude: [],

        // 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_DEBUG,

        plugins: [
            'karma-jasmine',
            //'karma-coverage',
            'karma-phantomjs-launcher',
            'karma-ng-html2js-preprocessor'
        ],

        preprocessors: {
            //'**/app/scripts/**/!(*spec).js': 'coverage',
            '**/app/scripts/**/*.html': 'ng-html2js'
        },

        ngHtml2JsPreprocessor: {
            moduleName: 'templates',
            stripPrefix: 'app/',
        },

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

        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['PhantomJS'],

        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: true
    });
};

Also here is my tasks/config.js file

module.exports = (function() {
    'use strict';

    // config vars
    var base = './app';
    var scripts = base + '/scripts';
    var sass = base + '/styles';

    var data = {
        browserSyncPort: 3000,
        cordovaPath: 'cordova',
        defaultPlatform: 'ios',
        excludedBowerComponents: ['es5-shim', 'json3'],
        base: base,
        mainFile: base + '/index.html',
        mainSassFile: sass + '/main.scss',
        routesFiles: base + '/scripts/_routes.js',
        e2eBaseUrl: 'http://localhost:3000/',
        styles: base + '/styles/',
        stylesF: [
            base + '/styles/**/_*.{scss,sass,less}',
            scripts + '/**/*.{scss,sass,less}'
        ],
        stylesAllF: [
            base + '/styles/**/*.{scss,sass,less}',
            scripts + '/**/*.{scss,sass,less}'
        ],
        scripts: base + '/scripts/',
        scriptsF: [
            // modules first
            base + '/scripts/**/_*.js',
            base + '/scripts/**/*.js',
            '!' + base + '/scripts/**/*.spec.js'
        ],
        scriptsAllF: base + '/scripts/**/*.js',
        scriptTestsF: base + '/scripts/**/*.spec.js',
        html: base + '/scripts/',
        htmlF: [
            base + '/scripts/**/*.html'
        ],
        images: base + '/images/',
        imagesF: base + '/images/**/*.*',
        fonts: base + '/fonts/',
        fontsF: base + '/fonts/**/*.*',
        tmp: './.tmp',
        dist: './../../../../target/classes/static/',
        wwwDestination: '',
        karmaConf: './karma.conf.js',
        karmaConfE2E: './karma-e2e.conf.js'
    };

    data.allHtmlF = data.htmlF.slice()
    data.allHtmlF.push(data.mainFile);

    return data;
})();

Really appreciate the help!

johannesjo commented 8 years ago

That's very weird. I just tried your exact same code and it works without a problem. Which Operating System are you on?

I also suppose that you're directive works flawlessly otherwise and there are no JavaScript errors of any sort?

Also what happens if you log the following:

console.log($templateCache.get('scripts/ccc-header/ccc-header-d.html'));
ddvorak commented 8 years ago

YOU DID IT! If I change

    beforeEach(inject(function($rootScope) {
        scope = $rootScope.$new();
    }));

to inject the $templateCache like so:

    beforeEach(inject(function($rootScope, $templateCache) {
        scope = $rootScope.$new();
        console.log($templateCache.get('scripts/ccc-header/ccc-header-d.html'));

    }));

I get the actual html now! Also, this makes it available in all my tests so they are all now passing. YOU ARE AMAZING I LOVE YOU FOREVER!

johannesjo commented 8 years ago

@ddvorak I'm happy that it works, but it's really weird. That shouldn't be necessary nor should it fix the problem. :)