From 8a9a2f87dd40c52a418638ac6167fd46848e89e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Efe=20G=C3=BCrkan=20YALAMAN?= Date: Thu, 14 Dec 2017 18:30:19 +0100 Subject: [PATCH] Add forceCoverageMatch configuration option Add forceCoverageMatch config option to allow collecting coverage even if the file is ignored. This way you can collect coverage from the source files with tests or specific files you ignored manually. --- CHANGELOG.md | 3 +- docs/Configuration.md | 35 ++++ .../__snapshots__/show_config.test.js.snap | 1 + packages/jest-config/src/defaults.js | 1 + packages/jest-config/src/index.js | 1 + packages/jest-config/src/normalize.js | 1 + packages/jest-config/src/valid_config.js | 1 + .../src/__tests__/should_instrument.test.js | 151 ++++++++++++++++++ .../jest-runtime/src/should_instrument.js | 8 + test_utils.js | 1 + types/Config.js | 3 + 11 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 packages/jest-runtime/src/__tests__/should_instrument.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 6572c16d1d59..94e9f2c1fc60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## master -None +* `[jest-config]` Add `forceCoverageMatch` to allow collecting coverage from + ignored files. ([#5081](https://github.com/facebook/jest/pull/5081)) ## jest 22.0.0 diff --git a/docs/Configuration.md b/docs/Configuration.md index e3e8037681fc..42eb4e0195df 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -237,6 +237,41 @@ Jest will fail if: * The `./src/api/very-important-module.js` file has less than 100% coverage. * Every remaining file combined has less than 50% coverage (`global`). +### `forceCoverageMatch` [array] + +Default: `['']` + +Test files are normally ignored from collecting code coverage. With this option, +you can overwrite this behavior and include otherwise ignored files in code coverage. + +For example, if you have tests in source files named with `.t.js` extension as +following: + +```javascript +// sum.t.js + +export function sum(a,b) { + return a + b; +} + +if (process.env.NODE_ENV === 'test') { + test('sum', () => { + expect(sum(1, 2)).toBe(3); + }); +} +``` + +You can collect coverage from those files with setting `forceCoverageMatch`. +```json +{ + ... + "jest": { + "forceCoverageMatch": ["**/*.t.js"] + } +} +``` + + ### `globals` [object] Default: `{}` diff --git a/integration_tests/__tests__/__snapshots__/show_config.test.js.snap b/integration_tests/__tests__/__snapshots__/show_config.test.js.snap index ad5f154fb672..f1eba0383106 100644 --- a/integration_tests/__tests__/__snapshots__/show_config.test.js.snap +++ b/integration_tests/__tests__/__snapshots__/show_config.test.js.snap @@ -13,6 +13,7 @@ exports[`--showConfig outputs config info and exits 1`] = ` \\"/node_modules/\\" ], \\"detectLeaks\\": false, + \\"forceCoverageMatch\\": [], \\"globals\\": {}, \\"haste\\": { \\"providesModuleNodeModules\\": [] diff --git a/packages/jest-config/src/defaults.js b/packages/jest-config/src/defaults.js index ce68eab6a649..3f35af5fd105 100644 --- a/packages/jest-config/src/defaults.js +++ b/packages/jest-config/src/defaults.js @@ -38,6 +38,7 @@ export default ({ coverageReporters: ['json', 'text', 'lcov', 'clover'], detectLeaks: false, expand: false, + forceCoverageMatch: [], globalSetup: null, globalTeardown: null, globals: {}, diff --git a/packages/jest-config/src/index.js b/packages/jest-config/src/index.js index 9f50d4a068e0..7cd8eccbfce5 100644 --- a/packages/jest-config/src/index.js +++ b/packages/jest-config/src/index.js @@ -130,6 +130,7 @@ const getConfigs = ( cwd: options.cwd, detectLeaks: options.detectLeaks, displayName: options.displayName, + forceCoverageMatch: options.forceCoverageMatch, globals: options.globals, haste: options.haste, moduleDirectories: options.moduleDirectories, diff --git a/packages/jest-config/src/normalize.js b/packages/jest-config/src/normalize.js index 749fb078bf02..e7fba4434f83 100644 --- a/packages/jest-config/src/normalize.js +++ b/packages/jest-config/src/normalize.js @@ -459,6 +459,7 @@ export default function normalize(options: InitialOptions, argv: Argv) { case 'expand': case 'globals': case 'findRelatedTests': + case 'forceCoverageMatch': case 'forceExit': case 'listTests': case 'logHeapUsage': diff --git a/packages/jest-config/src/valid_config.js b/packages/jest-config/src/valid_config.js index b2ae2930104d..c1559a7b5419 100644 --- a/packages/jest-config/src/valid_config.js +++ b/packages/jest-config/src/valid_config.js @@ -37,6 +37,7 @@ export default ({ }, displayName: 'project-name', expand: false, + forceCoverageMatch: ['**/*.t.js'], forceExit: false, globalSetup: 'setup.js', globalTeardown: 'teardown.js', diff --git a/packages/jest-runtime/src/__tests__/should_instrument.test.js b/packages/jest-runtime/src/__tests__/should_instrument.test.js new file mode 100644 index 000000000000..48e79734227c --- /dev/null +++ b/packages/jest-runtime/src/__tests__/should_instrument.test.js @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import shouldInstrument from '../should_instrument'; + +describe('should_instrument', () => { + const defaultFilename = 'source_file.test.js'; + const defaultOptions = { + collectCoverage: true, + }; + const defaultConfig = { + rootDir: '/', + }; + + describe('should return true', () => { + const testShouldInstrument = ( + filename = defaultFilename, + options = defaultOptions, + config = defaultConfig, + ) => { + const result = shouldInstrument(filename, options, config); + expect(result).toBe(true); + }; + + it('when testRegex provided and file is not a test file', () => { + testShouldInstrument('source_file.js', defaultOptions, { + testRegex: '.*\\.(test)\\.(js)$', + }); + }); + + it('when testMatch is provided and file is not a test file', () => { + testShouldInstrument('source_file.js', defaultOptions, { + testMatch: ['**/?(*.)(test).js'], + }); + }); + + it('should return true when file is in collectCoverageOnlyFrom when provided', () => { + testShouldInstrument( + 'collect/only/from/here.js', + + { + collectCoverage: true, + collectCoverageOnlyFrom: {'collect/only/from/here.js': true}, + }, + defaultConfig, + ); + }); + + it('should return true when filename matches collectCoverageFrom', () => { + testShouldInstrument( + 'do/collect/coverage.js', + { + collectCoverage: true, + collectCoverageFrom: ['!**/dont/**/*.js', '**/do/**/*.js'], + }, + defaultConfig, + ); + }); + + it('should return true if the file is not in coveragePathIgnorePatterns', () => { + testShouldInstrument('do/collect/coverage.js', defaultOptions, { + coveragePathIgnorePatterns: ['dont'], + rootDir: '/', + }); + }); + + it('should return true if file is a testfile but forceCoverageMatch is set', () => { + testShouldInstrument('do/collect/sum.coverage.test.js', defaultOptions, { + forceCoverageMatch: ['**/*.(coverage).(test).js'], + rootDir: '/', + testRegex: '.*\\.(test)\\.(js)$', + }); + }); + }); + + describe('should return false', () => { + const testShouldInstrument = ( + filename = defaultFilename, + options = defaultOptions, + config = defaultConfig, + ) => { + const result = shouldInstrument(filename, options, config); + expect(result).toBe(false); + }; + + it('if collectCoverage is falsy', () => { + testShouldInstrument( + 'source_file.js', + { + collectCoverage: false, + }, + defaultConfig, + ); + }); + + it('when testRegex provided and filename is a test file', () => { + testShouldInstrument(defaultFilename, defaultOptions, { + testRegex: '.*\\.(test)\\.(js)$', + }); + }); + + it('when testMatch is provided and file is a test file', () => { + testShouldInstrument(defaultFilename, defaultOptions, { + testMatch: ['**/?(*.)(test).js'], + }); + }); + + it('when file is not in collectCoverageOnlyFrom when provided', () => { + testShouldInstrument( + 'source_file.js', + { + collectCoverage: true, + collectCoverageOnlyFrom: {'collect/only/from/here.js': true}, + }, + defaultConfig, + ); + }); + + it('when filename does not match collectCoverageFrom', () => { + testShouldInstrument( + 'dont/collect/coverage.js', + { + collectCoverage: true, + collectCoverageFrom: ['!**/dont/**/*.js', '**/do/**/*.js'], + }, + defaultConfig, + ); + }); + + it('if the file is in coveragePathIgnorePatterns', () => { + testShouldInstrument('dont/collect/coverage.js', defaultOptions, { + coveragePathIgnorePatterns: ['dont'], + rootDir: '/', + }); + }); + + it('if file is in mock patterns', () => { + const filename = + process.platform === 'win32' + ? 'dont\\__mocks__\\collect\\coverage.js' + : 'dont/__mocks__/collect/coverage.js'; + + testShouldInstrument(filename, defaultOptions, defaultConfig); + }); + }); +}); diff --git a/packages/jest-runtime/src/should_instrument.js b/packages/jest-runtime/src/should_instrument.js index 48745ddbbea0..0a663ee56378 100644 --- a/packages/jest-runtime/src/should_instrument.js +++ b/packages/jest-runtime/src/should_instrument.js @@ -27,6 +27,14 @@ export default function shouldInstrument( return false; } + if ( + config.forceCoverageMatch && + config.forceCoverageMatch.length && + micromatch.any(filename, config.forceCoverageMatch) + ) { + return true; + } + if (config.testRegex && filename.match(config.testRegex)) { return false; } diff --git a/test_utils.js b/test_utils.js index dbed2652d5b4..9ae8c272dd59 100644 --- a/test_utils.js +++ b/test_utils.js @@ -70,6 +70,7 @@ const DEFAULT_PROJECT_CONFIG: ProjectConfig = { cwd: '/test_root_dir/', detectLeaks: false, displayName: undefined, + forceCoverageMatch: [], globals: {}, haste: { providesModuleNodeModules: [], diff --git a/types/Config.js b/types/Config.js index e02b2f5fdc80..b9ee6cd422fa 100644 --- a/types/Config.js +++ b/types/Config.js @@ -31,6 +31,7 @@ export type DefaultOptions = {| coveragePathIgnorePatterns: Array, coverageReporters: Array, expand: boolean, + forceCoverageMatch: Array, globals: ConfigGlobals, globalSetup: ?string, globalTeardown: ?string, @@ -86,6 +87,7 @@ export type InitialOptions = { displayName?: string, expand?: boolean, findRelatedTests?: boolean, + forceCoverageMatch?: Array, forceExit?: boolean, json?: boolean, globals?: ConfigGlobals, @@ -212,6 +214,7 @@ export type ProjectConfig = {| cwd: Path, detectLeaks: boolean, displayName: ?string, + forceCoverageMatch: Array, globals: ConfigGlobals, haste: HasteConfig, moduleDirectories: Array,