Search code examples
javascriptangularjsgulpbrowserifykarma-jasmine

How do I get a Browserfied AngularJS application working with Karma?


I'm having trouble getting my browserified AngularJS application running tests with Karma and Jasmine. I use Gulp to build the application, but it's currently not involved in tests (should it be?).

I'm getting this error, I'd like to be using PhantomJS

Chrome 44.0.2403 (Linux 0.0.0) LOG: 'WARNING: Tried to load angular more than once.'

Chrome 44.0.2403 (Linux 0.0.0) ERROR
Uncaught ReferenceError: require is not defined
   at /home/xenoterracide/IdeaProjects/mmp-player/app/js/app.js:3

This is my gulpfile.js that more or less works

'use strict';
var gulp = require( 'gulp' ),
    sourcemaps = require( 'gulp-sourcemaps' ),
    concat = require( 'gulp-concat' ),
    browserSync = require( 'browser-sync' ),
    CacheBuster = require( 'gulp-cachebust' )
    ;

var reload = browserSync.reload;
var cachebust = new CacheBuster();

gulp.task( 'build', [ 'build-css', 'build-js' ], function() {
    return gulp.src( 'app/index.html' )
        .pipe( cachebust.references() )
        .pipe( gulp.dest( 'dist' ) )
        .pipe( reload( { stream: true } ) );
} );

gulp.task( 'serve', [ 'build' ], function() {
    browserSync( {
        server: {
            baseDir: 'dist'
        }
    } );

    gulp.watch( [ '**/*.html', 'css/**/*.*css', 'js/**/*.js' ], { cwd: 'app' }, [ 'build' ] );
} );

gulp.task( 'default', function() {
    // place code for your default task here
} );

gulp.task( 'build-config', function() {
    var nconf = require( 'nconf' ),
        rename = require( 'gulp-rename' ),
        ngConstant = require( 'gulp-ng-constant' );

    nconf.use( 'memory' ).argv()
        .env( {
            separator: '_',
            match: /^MMP/
        } )
        .defaults( {
            musicDbSocketUri: 'http://localhost:8080/socket'
        } );

    ngConstant( {
        name: 'mmpPlayer.config',
        constants: nconf.get(),
        stream: true
    } ).pipe( rename( "config.js" ) )
        .pipe( gulp.dest( 'app/generated' ) );
} );

gulp.task( 'build-css', [ 'clean' ], function() {
    var sass = require( 'gulp-sass' );

    return gulp.src(
        [
            'node_modules/angular-material/angular-material.css',
            'app/css/*.*css'
        ] )
        .pipe( sourcemaps.init() )
        .pipe( sass() )
        .pipe( sourcemaps.write( './' ) )
        .pipe( concat( 'app.css' ) )
        .pipe( cachebust.resources() )
        .pipe( gulp.dest( 'dist/css' ) );
} );

gulp.task( 'build-js', [ 'clean', 'build-config', 'build-template-cache' ], function() {
    var browserify = require( 'browserify' ),
        ngAnnotate = require( 'browserify-ngannotate' ),
        gutil = require( 'gulp-util' ),
        source = require( 'vinyl-source-stream' ),
        buffer = require( 'vinyl-buffer' );

    var b = browserify( {
        basedir: 'app/js',
        entries: 'app.js',
        debug: true,
        paths: [ '../generated', 'config', 'controller', 'service', 'factory' ],
        transform: [ ngAnnotate ]
    } );

    return b.bundle()
        .on( 'error', gutil.log )
        .pipe( source( 'js/app.js' ) )
        .pipe( buffer() )
        .pipe( cachebust.resources() )
        .pipe( sourcemaps.init( { loadMaps: true } ) )
        .pipe( sourcemaps.write( './' ) )
        .pipe( gulp.dest( 'dist' ) );
} );

gulp.task( 'build-template-cache', function() {

    var ngHtml2Js = require( 'gulp-ng-html2js' );

    return gulp.src( 'app/partials/*.html' )
        .pipe( ngHtml2Js( {
            moduleName: 'mmpPlayer',
            prefix: 'partials/'
        } ) )
        .pipe( concat( 'partials.js' ) )
        .pipe( gulp.dest( 'app/generated' ) );
} );

gulp.task( 'clean', function( cb ) {
    var del = require( 'del' );

    del( [ 'dist' ], cb )
} );

Here's my karma.conf.js

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

        basePath: './',
        logLevel: 'LOG_DEBUG',

        browserify: {
            basedir: 'app/js',
            entries: 'app.js',
            debug: true,
            paths: [ '../generated', 'config', 'controller', 'service', 'factory' ]
        },

        files: [
            'node_modules/angular/angular.js',
            'node_modules/angular-mocks/angular-mocks.js',
            'app/js/**/*.js',
            'test/**/*.js'
        ],

        autoWatch: true,

        frameworks: [ 'browserify', 'jasmine' ],

        browsers: [],

        plugins: [
            'karma-jasmine',
            'karma-browserify'
        ],

        phantomjsLauncher: {
            // Have phantomjs exit if a ResourceError is encountered (useful if karma exits without killing phantom)
            exitOnResourceError: true
        }
    } );
};

I wrote this dummy service

'use strict';
require( 'angular' ).module( 'mmpPlayer' ).factory( 'testF', function( ) {
    return "hello";
});

and a corresponding test

describe('testF', function () {

var testF;
beforeEach(module('mmpPlayer'));
beforeEach(inject(function (_testF_) {
  testF = _testF_;
}));

describe('Constructor', function () {

  it('assigns a name', function () {
    expect(testF).toBe('hello');
  });

});

});

I'm still learning a lot about all of these tools and don't feel like I really understand how any of them work (though I'm deeply familiar with similar tools for Java and Perl).

I'd like to get this and more tests passing with PhantomJs (for running CI), what changes do I need to make to accomplish that?


Solution

  • had to update my karma config, largely removed relative pathing and made it look at things from the repository root.

    'use strict';
    module.exports = function( config ) {
        config.set( {
    
            basePath: '',
            logLevel: 'LOG_DEBUG',
    
            preprocessors: {
                'app/js/app.js': [ 'browserify' ],
                'app/js/**/*.js': [ 'browserify' ]
            },
    
            browserify: {
                basedir: '',
                debug: true,
                paths: [ 'app/generated', 'app/js/config', 'app/js/controller', 'app/js/service', 'app/js/factory' ]
            },
    
            files: [
                'app/js/app.js',
                'node_modules/angular-mocks/angular-mocks.js',
                'test/**/*.js'
            ],
    
            autoWatch: true,
    
            frameworks: [ 'browserify', 'jasmine' ],
    
            browsers: [ 'Chrome' ],
    
            plugins: [
                'karma-jasmine',
                'karma-browserify',
                'karma-chrome-launcher'
            ],
    
            reporters: [ 'dots' ],
    
            phantomjsLauncher: {
                // Have phantomjs exit if a ResourceError is encountered (useful if karma exits without killing phantom)
                exitOnResourceError: true
            }
        } );
    };