Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't find source maps for Karma + Jasmine + TypeScript + Webpack

I'm trying to test (with coverage) my TypeScript application using Karma, Jasmine, and Webpack. With the following, I'm able to successfully run tests, but am unable to generate coverage properly. I'm using karma-remap-coverage (https://github.com/sshev/karma-remap-coverage) and it seems simple enough.

It looks as though something interesting is happening (and I'm getting some kind of coverage report) but with a few tweaks here and there, the numbers change drastically and I can never actually load the sourcemaps.

Here's the basic setup:

I have a src directory that contains 10 .ts files. Only one has a corresponding .spec file at the moment.

The spec file is pretty simple and was just enough to prove that I could run tests:

import ComponentToTest from './componentToTest';

describe('ComponentToTest', () => {

  it('should run a test', () => {
      expect(1+1).toBe(2);
  });

  it('should be able to invoke the a method', () => {
      spyOn(ComponentToTest, 'foo').and.callThrough();
      ComponentToTest.foo('testing foo');
      expect(ComponentToTest.foo).toHaveBeenCalled();
  });

});

This works like a charm when paired with my tsconfig.json file:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "noImplicitAny": false,
    "sourceMap": true,
    "lib": ["es6", "dom"],
    "experimentalDecorators": true
  },
  "exclude": [
    "node_modules"
  ]
}

and karma.conf.js file:

module.exports = config => config.set({

    frameworks: ['jasmine'],

    mime: { 'text/x-typescript': ['ts','tsx'] },

    // if I make this a generic './src/**/*.ts' it seems to freeze up
    // without throwing any errors or running any tests, but that seems
    // like a separate issue...
    files: [
        './src/lib/componentToTest.ts',
        './src/lib/componentToTest.spec.ts'
    ],

    preprocessors: {
        './src/**/*.spec.ts': ['webpack'],
        './src/**/*.ts': ['webpack', 'sourcemap', 'coverage']
    },

    webpack: {
        devtool: "source-map",
        module: {
            rules: [
                {
                    test: /\.ts?$/,
                    loader: 'ts-loader',
                    exclude: /node_modules/
                }
            ]
        },
        resolve: {
            extensions: [".ts", ".js"]
        }
    },

    webpackMiddleware: {
        quiet: true,
        stats: {
            colors: true
        }
    },

    // add both "karma-coverage" and "karma-remap-coverage" reporters
    reporters: ['progress', 'coverage', 'remap-coverage'],

    // save interim raw coverage report in memory
    coverageReporter: {
        type: 'in-memory'
    },

    // define where to save final remaped coverage reports
    remapCoverageReporter: {
        'text-summary': null,
        html: './coverage/html',
        cobertura: './coverage/cobertura.xml'
    },

    colors: true,

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

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

});

And finally, I'm launching the tests with a simple Gulp task:

gulp.task('test', function (done) {
  new Server({
    configFile: __dirname + '/karma.conf.js',
    singleRun: true
  }, (exitCode) => {
     done();
     process.exit(exitCode);
  }).start();
});

When run, I get an output that seems (mostly) promising:

Chrome 58.0.3029 (Mac OS X 10.12.3): Executed 1 of 2 SUCCESS (0 secs / 0.002 secs)
Chrome 58.0.3029 (Mac OS X 10.12.3): Executed 2 of 2 SUCCESS (0.026 secs / 0.004 secs)
[Error: Could not find source map for: "app/src/lib/componentToTest.ts"]
[Error: Could not find source map for: "app/src/lib/componentToTest.spec.ts"]

========================= Coverage summary =========================
Statements   : 43.69% ( 322/737 )
Branches     : 15.7% ( 38/242 )
Functions    : 35.47% ( 61/172 )
Lines        : 44.91% ( 322/717 )
====================================================================

So something is happening! Which makes me feel like I'm close. When I browse to my coverage report in a browser, I see both the .spec.ts file and the .ts file listed (which is again, getting closer) but I'm not quite there for a couple of reasons:

  1. The .spec.ts file is being included in the coverage report. Since this is the test file, I do not want to include it.
  2. Source maps are not being properly generated - this is clear from the errors in the console and also from the inability to browse to the specific file's coverage report.

I do feel like I'm pretty darn close. Is there anything simple that I'm missing or suggestions?

Update:

I realized I was using an older version of Node and thought that may be causing some issues. I upgraded to 6.11.0 and while that didn't solve anything, it did provide slightly more context:

The errors are being reported by remap-istanbul (no surprise there, really):

CoverageTransformer.addFileCoverage (/app/node_modules/remap-istanbul/lib/CoverageTransformer.js:148:17)

I am using [email protected] which uses [email protected] - it seems like there have been issues with remap-istanbul in the past, but not at the version I'm using.

Also using Webpack 2.6.1 and TypeScript 2.3.2

like image 238
amlyhamm Avatar asked Jun 23 '17 22:06

amlyhamm


1 Answers

Well, after several days of trying different things, I've finally found a solution that works. I'm not sure specifically what was causing the issue in my first post, but here's where I've ended up. This may be helpful for someone else looking for a really simple TypeScript, Karma, Jasmine, Webpack (with coverage) setup.

  • My file structure and spec file stayed the same.
  • My tsconfig.json updated to:

    {
      "compilerOptions": {
        "module": "commonjs",
        "target": "es6",
        "noImplicitAny": false,
        "inlineSourceMap": true, // this line
        "sourceMap": false, // and this one
        "experimentalDecorators": true,
        "lib": ["es6", "dom"]
      },
      "exclude": [
          "node_modules"
      ]
    }
    
  • I switched to using the awesome-typescript-loader instead of ts-loader.

  • And finally, my karma.conf.js file now looks like:

    module.exports = config => config.set({
    
        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',
    
        frameworks: ['jasmine'],
    
        mime: { 'text/x-typescript': ['ts','tsx'] },
    
        files: [
            'node_modules/angular/angular.min.js',
            './src/**/*.ts'
        ],
    
        preprocessors: {
            './src/**/*.ts': ['webpack']
        },
    
        webpack: {
    
            devtool: 'inline-source-map',
            module: {
                rules: [
                    {
                        enforce: 'pre',
                        test: /\.js$/,
                        loader: 'source-map-loader',
                        exclude: [
                            'node_modules',
                            /\.spec\.ts$/
                        ]
                    },
                    {
                        test: /\.ts?$/,
                        use: [
                            {
                                loader: 'awesome-typescript-loader',
                                query: {
                                    /**
                                     * Use inline sourcemaps for "karma-remap-coverage" reporter
                                     */
                                    sourceMap: false,
                                    inlineSourceMap: true,
                                    compilerOptions: {
                                        removeComments: true
                                    }
                                },
                            }
                        ]
                    },
                    {
                        enforce: 'post',
                        test: /\.(js|ts)$/,
                        loader: 'istanbul-instrumenter-loader',
                        exclude: [
                            /node_modules/,
                            /\.spec\.ts$/
                        ]
                    },
                    { test: /\.html$/, loader: 'html-loader' }
                ]
            },
            resolve: {
                extensions: [".ts", ".js", ".html"]
            },
            externals: {
                angular: "angular"
            }
        },
    
        webpackMiddleware: {
            quiet: true,
            stats: {
                colors: true
            }
        },
    
        // add both "karma-coverage" and "karma-remap-coverage" reporters
        reporters: ['progress', 'coverage', 'remap-coverage'],
    
        // save interim raw coverage report in memory
        coverageReporter: {
            type: 'in-memory'
        },
    
        // define where to save final remaped coverage reports
        remapCoverageReporter: {
            'text-summary': null,
            html: './coverage/html',
            cobertura: './coverage/cobertura.xml'
        },
    
        colors: true,
    
        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['Chrome'],
    
        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: true
    
    });
    

Final package versions include:

  • node 4.2.6 (I was also able to get this to work with a newer version of node, but need to be here for other reasons)
  • awesome-typescript-loader 3.1.2
  • istanbul-instrumenter-loader 2.0.0
  • jasmine-core 2.5.2
  • karma 1.6.0
  • karma-chrome-launcher 2.0.0
  • karma-coverage 1.1.1
  • karma-jasmine 1.1.0
  • karma-remap-coverage 0.1.4
  • karma-webpack 2.0.3
  • typescript 2.3.2
  • webpack 2.6.1

Now my tests run, there are no errors in the console, and I have a coverage report of the original TypeScript files!

Lots of credit to the folks who put this together (it ended up guiding quite a bit of my final solution): https://github.com/AngularClass/angular-starter/tree/master/config

like image 103
amlyhamm Avatar answered Sep 17 '22 12:09

amlyhamm