From 454deb813c357ff3c76f910ba78654a9b65f6d71 Mon Sep 17 00:00:00 2001 From: Tommy Yu Date: Thu, 7 Dec 2017 12:54:30 +1300 Subject: [PATCH] Use the istanbul loader for coverage report - As the code need to be applied _inside_ the webpack, the relevant loader must be used. - Using a fork of the loader because it can generate html reports without dying in a fire like the official one. - https://github.com/karma-runner/karma-coverage/issues/123 - https://github.com/karma-runner/karma-coverage/issues/278 --- setup.py | 1 + src/calmjs/webpack/dev.py | 36 ++++++++ src/calmjs/webpack/tests/test_dev.py | 92 ++++++++++++++++++++ src/calmjs/webpack/tests/test_integration.py | 26 ++++++ 4 files changed, 155 insertions(+) diff --git a/setup.py b/setup.py index 76ce710..cffb07a 100644 --- a/setup.py +++ b/setup.py @@ -21,6 +21,7 @@ "devDependencies": { "webpack": "~2.6.0", "karma-webpack": "~2.0.0", + "sourcemap-istanbul-instrumenter-loader": "~0.2.0", } } diff --git a/src/calmjs/webpack/dev.py b/src/calmjs/webpack/dev.py index 7bcd098..5b021cc 100644 --- a/src/calmjs/webpack/dev.py +++ b/src/calmjs/webpack/dev.py @@ -21,6 +21,8 @@ from calmjs.dev.karma import BEFORE_KARMA from calmjs.dev.toolchain import TEST_FILENAME_PREFIX from calmjs.dev.toolchain import TEST_FILENAME_PREFIX_DEFAULT + from calmjs.dev.toolchain import TEST_COVERED_TEST_PATHS + from calmjs.dev.toolchain import TEST_COVERED_BUILD_DIR_PATHS except ImportError: # pragma: no cover # Package not available; None is the advice blackhole BEFORE_KARMA = None @@ -30,6 +32,7 @@ from calmjs.webpack.base import WEBPACK_CONFIG from calmjs.webpack.base import WEBPACK_SINGLE_TEST_BUNDLE +from calmjs.webpack.base import DEFAULT_CALMJS_EXPORT_NAME from calmjs.webpack.interrogation import probe_calmjs_webpack_module_names logger = logging.getLogger(__name__) @@ -102,6 +105,38 @@ def _process_tests(spec): return test_files +def _generate_coverage_loader(spec): + # apply the loader to all paths to be covered that require the + # webpack specific loader, as the originals will _not_ be used. + include = [] + loader = { + "loader": "sourcemap-istanbul-instrumenter-loader", + "include": include, + } + + for covered_path in spec.get(TEST_COVERED_TEST_PATHS, []): + # these should already be absolutes, apply directly. + include.append(covered_path) + + for covered_path in spec.get(TEST_COVERED_BUILD_DIR_PATHS, []): + # these will need to be joined with build_dir, as they are + # relative. + include.append(join(spec[BUILD_DIR], covered_path)) + + if include: + return loader + + +def _apply_coverage(spec): + loader = _generate_coverage_loader(spec) + if not loader: + return + config = spec[karma.KARMA_CONFIG] + module = config['webpack']['module'] = config['webpack'].get('module', {}) + loaders = module['loaders'] = module.get('loaders', []) + loaders.append(loader) + + def karma_webpack(spec): """ An advice for the karma runtime before execution of karma that is @@ -192,6 +227,7 @@ def karma_webpack(spec): } test_files = _process_tests(spec) + _apply_coverage(spec) # purge all files files = config['files'] = [] diff --git a/src/calmjs/webpack/tests/test_dev.py b/src/calmjs/webpack/tests/test_dev.py index 279ea1e..91f7aef 100644 --- a/src/calmjs/webpack/tests/test_dev.py +++ b/src/calmjs/webpack/tests/test_dev.py @@ -16,6 +16,7 @@ from calmjs.exc import ToolchainAbort from calmjs.toolchain import Spec from calmjs.utils import pretty_logging +from calmjs.webpack import dev from calmjs.webpack.dev import karma_webpack from calmjs.testing.mocks import StringIO @@ -57,6 +58,97 @@ def import_(name, *a, **kw): @unittest.skipIf(karma is None, 'calmjs.dev or its karma module not available') class KarmaTestcase(unittest.TestCase): + def test_coverage_generation_empty(self): + self.assertIsNone(dev._generate_coverage_loader(Spec())) + + def test_coverage_generation_targets(self): + spec = Spec(test_covered_test_paths=[ + 'some/test/file', + 'some/other/file', + ]) + loader = dev._generate_coverage_loader(spec) + self.assertEqual({ + "loader": "sourcemap-istanbul-instrumenter-loader", + "include": ['some/test/file', 'some/other/file'], + }, loader) + + def test_coverage_generation_build_dir(self): + spec = Spec( + build_dir=mkdtemp(self), + test_covered_build_dir_paths=['afile.js'], + ) + loader = dev._generate_coverage_loader(spec) + self.assertTrue(loader['include'][0].startswith(spec['build_dir'])) + self.assertTrue(loader['include'][0].endswith('afile.js')) + + def test_coverage_generation_all(self): + spec = Spec( + build_dir=mkdtemp(self), + test_covered_build_dir_paths=['afile.js'], + test_covered_test_paths=['some/test/file'], + ) + loader = dev._generate_coverage_loader(spec) + self.assertEqual({ + "loader": "sourcemap-istanbul-instrumenter-loader", + "include": ['some/test/file', join(spec['build_dir'], 'afile.js')], + }, loader) + + def test_apply_coverage_required_missing(self): + spec = Spec( + build_dir=mkdtemp(self), + test_covered_build_dir_paths=['afile.js'], + test_covered_test_paths=['some/test/file'], + ) + with self.assertRaises(KeyError): + dev._apply_coverage(spec) + + def test_apply_coverage_standard(self): + spec = Spec( + karma_config={ + 'webpack': {}, + }, + build_dir=mkdtemp(self), + test_covered_build_dir_paths=['afile.js'], + test_covered_test_paths=['some/test/file'], + ) + dev._apply_coverage(spec) + + self.assertEqual({ + "module": {"loaders": [{ + "loader": "sourcemap-istanbul-instrumenter-loader", + "include": [ + 'some/test/file', join(spec['build_dir'], 'afile.js') + ], + }]}, + }, spec['karma_config']['webpack']) + + def test_apply_coverage_join(self): + spec = Spec( + karma_config={ + 'webpack': { + 'module': { + 'rules': [], + 'loaders': [ + {'loader': 'demo-loader'} + ], + }, + }, + }, + build_dir=mkdtemp(self), + test_covered_build_dir_paths=['afile.js'], + test_covered_test_paths=['some/test/file'], + ) + dev._apply_coverage(spec) + + self.assertEqual({ + "module": {'rules': [], "loaders": [{'loader': 'demo-loader'}, { + "loader": "sourcemap-istanbul-instrumenter-loader", + "include": [ + 'some/test/file', join(spec['build_dir'], 'afile.js') + ], + }]}, + }, spec['karma_config']['webpack']) + def test_karma_setup_empty(self): spec = Spec() with pretty_logging(stream=StringIO()) as s: diff --git a/src/calmjs/webpack/tests/test_integration.py b/src/calmjs/webpack/tests/test_integration.py index 4de3f0c..ba6916e 100644 --- a/src/calmjs/webpack/tests/test_integration.py +++ b/src/calmjs/webpack/tests/test_integration.py @@ -1240,6 +1240,32 @@ def test_karma_test_runner_basic(self): self.assertEqual(e.exception.args[0], 0) self.assertTrue(exists(export_target)) + def test_karma_test_runner_coverage(self): + # utils.stub_stdouts(self) + current_dir = utils.mkdtemp(self) + export_target = join(current_dir, 'example_package.js') + with self.assertRaises(SystemExit) as e: + runtime.main([ + 'karma', '--coverage', + 'webpack', 'example.package', + '--export-target=' + export_target, + ]) + self.assertEqual(e.exception.args[0], 0) + self.assertTrue(exists(export_target)) + + def test_karma_test_runner_coverage_covertests(self): + # utils.stub_stdouts(self) + current_dir = utils.mkdtemp(self) + export_target = join(current_dir, 'example_package.js') + with self.assertRaises(SystemExit) as e: + runtime.main([ + 'karma', '--coverage', '--cover-test', + 'webpack', 'example.package', + '--export-target=' + export_target, + ]) + self.assertEqual(e.exception.args[0], 0) + self.assertTrue(exists(export_target)) + def test_karma_test_runner_standalone_artifact(self): """ what's the purpose of tests if they can't be executed any time,