From 97d348e9a5855df22afcd20fcdabe9c307a5d12b Mon Sep 17 00:00:00 2001 From: Arjan van Wijk Date: Tue, 12 Jul 2016 00:30:21 +0200 Subject: [PATCH] Add sourcemap support for coverage by using remap-istanbul Added remap-istanbul and all supporting karma plugins to correctly remap the coverage results back to their TypeScript source. Since all generations happens in memory by Webpack, we needed inline sourcemap support, provided by the Karma plugins, the Webpack devtool setting, and the webpack typescript loader. The normal `ts-loader` doesn't support inline sourcemaps, so we switched to `awesome-typescript-loader`. We created a new `tsconfig.test.json` that is the same as the normal `tsconfig.json` but enables inline sourcemaps by adding `"inlineSourceMap": true`. The webpack dist builds have also switched to `awesome-typescript-loader` to keep the configuration consistent. To make the `awesome-typescript-loader` correctly work with const enums, we needed to enable `preserveConstEnums` property in the `tsconfig.json`. We also switched he `test/index.js` to `index.ts` and included `webpack-env` in the `typings.json` so all wepack requires work in TypeScript. To have correct coverage reports consisting of _all_ the source files, we've added a `Dummy.ts` file in the `src/` folder that is not referenced by any tests, but must show up in the coverage results. If this is not the case, we know our test setup is incorrect. re #15 --- config/karma.conf.js | 91 +++++++++++++++++------------------ config/tsconfig.test.json | 26 ++++++++++ config/webpack.config.js | 5 +- config/webpack.config.test.js | 57 ++++++++++++++++++++++ package.json | 12 +++-- src/lib/Dummy.ts | 30 ++++++++++++ test/{index.js => index.ts} | 12 ++++- test/tsconfig.json | 16 ------ tsconfig.json | 16 +++--- typings.json | 3 ++ 10 files changed, 192 insertions(+), 76 deletions(-) create mode 100644 config/tsconfig.test.json create mode 100644 config/webpack.config.test.js create mode 100644 src/lib/Dummy.ts rename test/{index.js => index.ts} (51%) delete mode 100644 test/tsconfig.json diff --git a/config/karma.conf.js b/config/karma.conf.js index 1b56a2c..c27215e 100644 --- a/config/karma.conf.js +++ b/config/karma.conf.js @@ -1,88 +1,87 @@ -var path = require('path'); -var webpackConfig = require('./webpack.config')(); - -webpackConfig.module.postLoaders = [ - // instrument only testing sources with Istanbul - { - test: /\.ts$/, - include: path.resolve('src/'), - loader: 'istanbul-instrumenter' - } -]; - -module.exports = function (config) +/** + * Please see Karma config file reference for better understanding: + * http://karma-runner.github.io/latest/config/configuration-file.html + */ +module.exports = function(config) { config.set({ // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '../', - - // frameworks to use + // List of test frameworks we will use. Most of them are provided by separate packages (adapters). // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['mocha', 'chai'], + frameworks: ['mocha', 'chai', 'source-map-support'], - // list of files / patterns to load in the browser + // Entry point / test environment builder is also written in TypeScript. files: [ - 'test/index.js' + './test/index.ts' ], - // list of files to exclude exclude: [], - // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { - 'test/index.js': ['webpack'], - 'src/*.ts': ['webpack','coverage'] // only for webstorm, real coverage is injected as webpack-loader + './src/**/*.ts': [ + 'webpack', + 'sourcemap', + 'coverage' + ], + './test/**/*.ts': [ + 'webpack' + ] }, - webpack: { - module : webpackConfig.module, - resolve : webpackConfig.resolve - }, + webpack: require('./webpack.config.test')(), + // Make dev server silent. + webpackServer: { noInfo: true }, - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['progress', 'coverage'], + // A lot of plugins are available for test results reporting. + // You can find them here: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['progress', 'coverage', 'karma-remap-istanbul'], + // Simple summary (printed to the console) and JSON file which we will remap back to TypeScript. coverageReporter: { dir: 'coverage', reporters: [ // reporters not supporting the `file` property - { type: 'json', dir: 'coverage', subdir: '.' }, - { type: 'lcov', dir: 'coverage', subdir: '.' }, + { type: 'json', subdir: '.' }, + // { type: 'lcov', subdir: '.' }, { type: 'text' } ] }, - - // web server port - port: 9876, - - - // enable / disable colors in the output (reporters and logs) - colors: true, - + // Map code coverage result back to TypeScript using `karma-remap-istanbul`. + remapIstanbulReporter: { + src: 'coverage/coverage-final.json', + reports: { + lcovonly: 'coverage/lcov.info', + html: 'coverage/report' + }, + timeoutNotCreated: 5000, + timeoutNoMoreFiles: 1000 + }, // 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 // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher browsers: ['PhantomJS'], + // web server port + port: 9876, + + // enable / disable colors in the output (reporters and logs) + colors: true, + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: true, // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits @@ -92,4 +91,4 @@ module.exports = function (config) // how many browser should be started simultaneous concurrency: Infinity }) -} +}; diff --git a/config/tsconfig.test.json b/config/tsconfig.test.json new file mode 100644 index 0000000..f9c11a3 --- /dev/null +++ b/config/tsconfig.test.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "target": "es5", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "preserveConstEnums": true, + "noImplicitAny": false, + "removeComments": false, + "noEmitHelpers": true, + "sourceMap": false, + "inlineSourceMap": true + }, + "exclude": [ + "node_modules" + ], + "filesGlob": [ + "../src/**/*.ts", + "../typings/index.d.ts" + ], + "awesomeTypescriptLoaderOptions": { + "resolveGlobs": false, + "forkChecker": true + } +} diff --git a/config/webpack.config.js b/config/webpack.config.js index 2a1a6f1..aafb6d4 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -40,7 +40,10 @@ module.exports = function() { test: /\.ts$/, exclude: /node_modules/, - loader: 'ts' + loader: 'awesome-typescript-loader', + query: { + tsconfig: 'tsconfig.json' + } } ] }, diff --git a/config/webpack.config.test.js b/config/webpack.config.test.js new file mode 100644 index 0000000..13e96d1 --- /dev/null +++ b/config/webpack.config.test.js @@ -0,0 +1,57 @@ +/*eslint-disable */ +var webpack = require('webpack'); +var path = require('path'); + +module.exports = function() +{ + return { + /** + * Inline source maps, generated by TypeScript compiler, will be used. + */ + devtool: 'inline-source-map', + + resolve: { + extensions: ['', '.ts', '.js'] + }, + // entry is the "main" source file we want to include/import + entry: './test/index.ts', + + verbose: true, + + module: { + loaders: [ + /** + * Unlike ts-loader, awesome-typescript-loader doesn't allow to override TS compiler options + * in query params. We use separate `tsconfig.test.json` file, which only differs in one thing: + * inline source maps. They are used for code coverage report. + * + * See project repository for details / configuration reference: + * https://github.com/s-panferov/awesome-typescript-loader + */ + { + test: /\.ts$/, + exclude: /node_modules/, + loader: 'awesome-typescript-loader', + query: { + tsconfig: 'config/tsconfig.test.json' + } + } + ], + postLoaders: [ + /** + * Instruments TS source files for subsequent code coverage. + * See https://github.com/deepsweet/istanbul-instrumenter-loader + */ + { + test: /\.ts$/, + loader: 'istanbul-instrumenter-loader', + exclude: [ + /node_modules/, + /test/, + /Spec\.ts$/ + ] + } + ] + }, + }; +}; diff --git a/package.json b/package.json index dd01c01..579f88b 100644 --- a/package.json +++ b/package.json @@ -47,23 +47,27 @@ "url": "https://github.com/mediamonks/seng-boilerplate.git" }, "devDependencies": { + "awesome-typescript-loader": "^2.0.1", "chai": "^3.5.0", "coveralls": "^2.11.6", "istanbul": "^0.4.3", - "istanbul-instrumenter-loader": "^0.1.3", - "karma": "^0.13.19", + "istanbul-instrumenter-loader": "^0.2.0", + "karma": "^0.13.22", "karma-chai": "^0.1.0", - "karma-coverage": "^0.5.3", + "karma-coverage": "^1.0.0", "karma-mocha": "^1.1.1", "karma-phantomjs-launcher": "^1.0.0", + "karma-remap-istanbul": "^0.1.1", + "karma-source-map-support": "^1.1.0", + "karma-sourcemap-loader": "^0.3.7", "karma-webpack": "^1.7.0", "marked": "^0.3.5", "mocha": "^2.5.3", "npm-run-all": "^2.2.0", "phantomjs-prebuilt": "^2.1.3", "pre-push": "^0.1.1", + "remap-istanbul": "^0.6.4", "rimraf": "^2.5.2", - "ts-loader": "^0.8.0", "tslint": "^3.3.0", "typedoc": "^0.4.3", "typescript": "^1.8.0", diff --git a/src/lib/Dummy.ts b/src/lib/Dummy.ts new file mode 100644 index 0000000..0e11da3 --- /dev/null +++ b/src/lib/Dummy.ts @@ -0,0 +1,30 @@ +/** + * Test file for code coverage checks + * This file is not covered by any tests, but should show up in code coverage + * results as a very low coverage percentage. + * + * @namespace example + * @class Dummy + * @constructor + */ +export default class Dummy +{ + /** + * Returns a value! + * + * @method foo + * @param {string} str The input string + * @returns {string} + */ + public foo(str?:string):string + { + if (typeof str == 'undefined') + { + return 'baz'; + } + else + { + return str + 'bar'; + } + } +} diff --git a/test/index.js b/test/index.ts similarity index 51% rename from test/index.js rename to test/index.ts index 354a35d..1c31b1a 100644 --- a/test/index.js +++ b/test/index.ts @@ -1,9 +1,17 @@ // require all test files -const testsContext = require.context('./', true, /Spec\.ts$/); +const testsContext = require.context( + './', + true, + /Spec\.ts/ +); testsContext.keys().forEach(testsContext); // require all source files -const sourcesContext = require.context('../src/', true, /\.ts$/); +const sourcesContext = require.context( + '../src/', + true, + /\.ts$/ +); sourcesContext.keys().forEach(sourcesContext); diff --git a/test/tsconfig.json b/test/tsconfig.json deleted file mode 100644 index 9d716dc..0000000 --- a/test/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es5", - "noImplicitAny": true, - "suppressImplicitAnyIndexErrors": true, - "removeComments": false, - "preserveConstEnums": true, - "sourceMap": false, - "experimentalDecorators": true - }, - "files": [ - "../typings/index.d.ts", - "./ExampleSpec.ts" - ] -} diff --git a/tsconfig.json b/tsconfig.json index a0074ba..303cb7a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,15 +1,17 @@ { "compilerOptions": { - "target": "es5", "module": "commonjs", - "noImplicitAny": true, - "suppressImplicitAnyIndexErrors": true, - "removeComments": false, + "moduleResolution": "node", + "target": "es5", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, "preserveConstEnums": true, + "noImplicitAny": false, + "removeComments": false, + "noEmitHelpers": true, "sourceMap": false, - "experimentalDecorators": true, - "outDir": "./", - "moduleResolution": "node" + "inlineSourceMap": false, + "outDir": "./" }, "files": [ "./typings/index.d.ts", diff --git a/typings.json b/typings.json index 353abcf..b4930ed 100644 --- a/typings.json +++ b/typings.json @@ -4,5 +4,8 @@ }, "devDependencies": { "chai": "registry:npm/chai#3.5.0+20160415060238" + }, + "globalDependencies": { + "webpack-env": "registry:dt/webpack-env#1.12.2+20160316155526" } }