diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b1bda867c94..fd565a69ea07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - `[jest-haste-map]` [**BREAKING**] Remove name from hash in `HasteMap.getCacheFilePath` ([#7218](https://github.com/facebook/jest/pull/7218)) - `[jest-runtime]` Remove `cacheDirectory` from `ignorePattern` for `HasteMap` if not necessary ([#7166](https://github.com/facebook/jest/pull/7166)) - `[jest-validate]` Add syntax to validate multiple permitted types ([#7207](https://github.com/facebook/jest/pull/7207)) +- `[jest-config]` Accept an array as as well as a string for `testRegex`([#7209]https://github.com/facebook/jest/pull/7209)) - `[babel-preset-jest]` [**BREAKING**] Export a function instead of an object for Babel 7 compatibility ([#7203](https://github.com/facebook/jest/pull/7203)) ### Fixes diff --git a/TestUtils.js b/TestUtils.js index 3cd6e43ecb3e..8ec57581b94f 100644 --- a/TestUtils.js +++ b/TestUtils.js @@ -109,7 +109,7 @@ const DEFAULT_PROJECT_CONFIG: ProjectConfig = { testLocationInResults: false, testMatch: [], testPathIgnorePatterns: [], - testRegex: '.test.js$', + testRegex: [/\.test\.js$/], testRunner: 'jest-jasmine2', testURL: '', timers: 'real', diff --git a/docs/Configuration.md b/docs/Configuration.md index 4cb938e87af2..bef4ec88cbbd 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -804,7 +804,7 @@ The glob patterns Jest uses to detect test files. By default it looks for `.js` See the [micromatch](https://github.com/jonschlinkert/micromatch) package for details of the patterns you can specify. -See also [`testRegex` [string]](#testregex-string), but note that you cannot specify both options. +See also [`testRegex` [string | Array]](#testregex-string), but note that you cannot specify both options. ### `testPathIgnorePatterns` [array] @@ -814,11 +814,11 @@ An array of regexp pattern strings that are matched against all test paths befor These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/build/", "/node_modules/"]`. -### `testRegex` [string] +### `testRegex` [string | Array] Default: `(/__tests__/.*|(\\.|/)(test|spec))\\.jsx?$` -The pattern Jest uses to detect test files. By default it looks for `.js` and `.jsx` files inside of `__tests__` folders, as well as any files with a suffix of `.test` or `.spec` (e.g. `Component.test.js` or `Component.spec.js`). It will also find files called `test.js` or `spec.js`. See also [`testMatch` [array]](#testmatch-array-string), but note that you cannot specify both options. +The pattern or patterns Jest uses to detect test files. By default it looks for `.js` and `.jsx` files inside of `__tests__` folders, as well as any files with a suffix of `.test` or `.spec` (e.g. `Component.test.js` or `Component.spec.js`). It will also find files called `test.js` or `spec.js`. See also [`testMatch` [array]](#testmatch-array-string), but note that you cannot specify both options. The following is a visualization of the default regex: diff --git a/e2e/__tests__/__snapshots__/show_config.test.js.snap b/e2e/__tests__/__snapshots__/show_config.test.js.snap index 64bb26ca8e28..2e4b59ee8531 100644 --- a/e2e/__tests__/__snapshots__/show_config.test.js.snap +++ b/e2e/__tests__/__snapshots__/show_config.test.js.snap @@ -58,7 +58,7 @@ exports[`--showConfig outputs config info and exits 1`] = ` \\"testPathIgnorePatterns\\": [ \\"/node_modules/\\" ], - \\"testRegex\\": \\"\\", + \\"testRegex\\": [], \\"testRunner\\": \\"<>/jest-jasmine2/build/index.js\\", \\"testURL\\": \\"http://localhost\\", \\"timers\\": \\"real\\", diff --git a/packages/jest-cli/src/SearchSource.js b/packages/jest-cli/src/SearchSource.js index 020425067b5a..af000440a0c0 100644 --- a/packages/jest-cli/src/SearchSource.js +++ b/packages/jest-cli/src/SearchSource.js @@ -51,13 +51,12 @@ const globsToMatcher = (globs: ?Array) => { return path => micromatch([path], globs, {dot: true}).length > 0; }; -const regexToMatcher = (testRegex: string) => { - if (!testRegex) { +const regexToMatcher = (testRegex: Array) => { + if (!testRegex.length) { return () => true; } - const regex = new RegExp(testRegex); - return path => regex.test(path); + return path => testRegex.some(e => e.test(path)); }; const toTests = (context, tests) => diff --git a/packages/jest-cli/src/__tests__/runJestWithCoverage.test.js b/packages/jest-cli/src/__tests__/runJestWithCoverage.test.js index 4081497655ba..247fcf2c7b11 100644 --- a/packages/jest-cli/src/__tests__/runJestWithCoverage.test.js +++ b/packages/jest-cli/src/__tests__/runJestWithCoverage.test.js @@ -51,7 +51,7 @@ jest.mock( }, ); -const config = {roots: [], testPathIgnorePatterns: [], testRegex: ''}; +const config = {roots: [], testPathIgnorePatterns: [], testRegex: []}; let globalConfig; const defaults = { changedFilesPromise: Promise.resolve({ diff --git a/packages/jest-cli/src/__tests__/watch.test.js b/packages/jest-cli/src/__tests__/watch.test.js index 3607381f684d..4acc234db99e 100644 --- a/packages/jest-cli/src/__tests__/watch.test.js +++ b/packages/jest-cli/src/__tests__/watch.test.js @@ -105,7 +105,7 @@ describe('Watch mode flows', () => { let stdin; beforeEach(() => { - const config = {roots: [], testPathIgnorePatterns: [], testRegex: ''}; + const config = {roots: [], testPathIgnorePatterns: [], testRegex: []}; pipe = {write: jest.fn()}; globalConfig = {watch: true}; hasteMapInstances = [{on: () => {}}]; diff --git a/packages/jest-cli/src/cli/args.js b/packages/jest-cli/src/cli/args.js index c758ff14e56e..b374adeba2ef 100644 --- a/packages/jest-cli/src/cli/args.js +++ b/packages/jest-cli/src/cli/args.js @@ -576,8 +576,9 @@ export const options = { type: 'array', }, testRegex: { - description: 'The regexp pattern Jest uses to detect test files.', - type: 'string', + description: + 'A string or array of string regexp patterns that Jest uses to detect test files.', + type: 'array', }, testResultsProcessor: { description: diff --git a/packages/jest-cli/src/lib/__tests__/__snapshots__/init.test.js.snap b/packages/jest-cli/src/lib/__tests__/__snapshots__/init.test.js.snap index adf63d20d15c..9ec33fc8447b 100644 --- a/packages/jest-cli/src/lib/__tests__/__snapshots__/init.test.js.snap +++ b/packages/jest-cli/src/lib/__tests__/__snapshots__/init.test.js.snap @@ -164,8 +164,8 @@ module.exports = { // \\"/node_modules/\\" // ], - // The regexp pattern Jest uses to detect test files - // testRegex: \\"\\", + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], // This option allows the use of a custom results processor // testResultsProcessor: null, diff --git a/packages/jest-config/src/Defaults.js b/packages/jest-config/src/Defaults.js index fefe239a189a..1b549e1059e1 100644 --- a/packages/jest-config/src/Defaults.js +++ b/packages/jest-config/src/Defaults.js @@ -70,7 +70,7 @@ export default ({ testLocationInResults: false, testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).js?(x)'], testPathIgnorePatterns: [NODE_MODULES_REGEXP], - testRegex: '', + testRegex: [], testResultsProcessor: null, testRunner: 'jasmine2', testURL: 'http://localhost', diff --git a/packages/jest-config/src/Descriptions.js b/packages/jest-config/src/Descriptions.js index fc8cafb8e785..6989be5e1dad 100644 --- a/packages/jest-config/src/Descriptions.js +++ b/packages/jest-config/src/Descriptions.js @@ -71,7 +71,8 @@ export default ({ testMatch: 'The glob patterns Jest uses to detect test files', testPathIgnorePatterns: 'An array of regexp pattern strings that are matched against all test paths, matched tests are skipped', - testRegex: 'The regexp pattern Jest uses to detect test files', + testRegex: + 'The regexp pattern or array of patterns that Jest uses to detect test files', testResultsProcessor: 'This option allows the use of a custom results processor', testRunner: 'This option allows use of a custom test runner', diff --git a/packages/jest-config/src/ValidConfig.js b/packages/jest-config/src/ValidConfig.js index edd84404cff3..8b675b90b7c8 100644 --- a/packages/jest-config/src/ValidConfig.js +++ b/packages/jest-config/src/ValidConfig.js @@ -10,6 +10,7 @@ import type {InitialOptions} from 'types/Config'; import {replacePathSepForRegex} from 'jest-regex-util'; +import {MultipleValidOptions} from 'jest-validate'; import {NODE_MODULES} from './constants'; const NODE_MODULES_REGEXP = replacePathSepForRegex(NODE_MODULES); @@ -100,7 +101,10 @@ export default ({ testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).js?(x)'], testNamePattern: 'test signature', testPathIgnorePatterns: [NODE_MODULES_REGEXP], - testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.jsx?$', + testRegex: MultipleValidOptions( + '(/__tests__/.*|(\\.|/)(test|spec))\\.jsx?$', + ['/__tests__/\\.test\\.jsx?$', '/__tests__/\\.spec\\.jsx?$'], + ), testResultsProcessor: 'processor-node-module', testRunner: 'jasmine2', testURL: 'http://localhost', diff --git a/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap b/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap index 19770435b020..ef754afc331b 100644 --- a/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap +++ b/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap @@ -67,6 +67,17 @@ exports[`testMatch throws if testRegex and testMatch are both specified 1`] = ` " `; +exports[`testMatch throws if testRegex is provided an invalid regex string 1`] = ` +"Validation Error: + +Error parsing configuration for testRegex: \\"foo(bar\\" could not be parsed. +Error: Invalid regular expression: /foo(bar/: Unterminated group + + Configuration Documentation: + https://jestjs.io/docs/configuration.html +" +`; + exports[`testPathPattern ignores invalid regular expressions and logs a warning 1`] = `" Invalid testPattern a( supplied. Running all tests instead."`; exports[`testPathPattern --testPathPattern ignores invalid regular expressions and logs a warning 1`] = `" Invalid testPattern a( supplied. Running all tests instead."`; diff --git a/packages/jest-config/src/__tests__/normalize.test.js b/packages/jest-config/src/__tests__/normalize.test.js index 801ab1f0318b..ff45c3036203 100644 --- a/packages/jest-config/src/__tests__/normalize.test.js +++ b/packages/jest-config/src/__tests__/normalize.test.js @@ -804,6 +804,42 @@ describe('Upgrade help', () => { }); }); +describe('testRegex', () => { + it('testRegex empty string is mapped to empty array', () => { + const {options} = normalize( + { + rootDir: '/root', + testRegex: '', + }, + {}, + ); + + expect(options.testRegex).toEqual([]); + }); + it('testRegex string is mapped to array of RegExp objects', () => { + const {options} = normalize( + { + rootDir: '/root', + testRegex: '.*', + }, + {}, + ); + + expect(options.testRegex).toEqual([/.*/]); + }); + it('testRegex array is mapped to array of RegExp objects', () => { + const {options} = normalize( + { + rootDir: '/root', + testRegex: ['.*', 'foo\\.bar'], + }, + {}, + ); + + expect(options.testRegex).toEqual([/.*/, /foo\.bar/]); + }); +}); + describe('testMatch', () => { it('testMatch default not applied if testRegex is set', () => { const {options} = normalize( @@ -826,7 +862,7 @@ describe('testMatch', () => { {}, ); - expect(options.testRegex).toBe(''); + expect(options.testRegex).toEqual([]); }); it('throws if testRegex and testMatch are both specified', () => { @@ -842,6 +878,18 @@ describe('testMatch', () => { }).toThrowErrorMatchingSnapshot(); }); + it('throws if testRegex is provided an invalid regex string', () => { + expect(() => { + normalize( + { + rootDir: '/root', + testRegex: 'foo(bar', + }, + {}, + ); + }).toThrowErrorMatchingSnapshot(); + }); + it('normalizes testMatch', () => { const {options} = normalize( { diff --git a/packages/jest-config/src/normalize.js b/packages/jest-config/src/normalize.js index a7da8a1a41d7..0bbd7af6b80b 100644 --- a/packages/jest-config/src/normalize.js +++ b/packages/jest-config/src/normalize.js @@ -547,7 +547,23 @@ export default function normalize(options: InitialOptions, argv: Argv) { ); break; case 'testRegex': - value = options[key] && replacePathSepForRegex(options[key]); + const valueOrEmptyArray = options[key] || []; + const valueArray = Array.isArray(valueOrEmptyArray) + ? valueOrEmptyArray + : [valueOrEmptyArray]; + + value = valueArray.map(replacePathSepForRegex).map(pattern => { + try { + return new RegExp(pattern); + } catch (err) { + throw createConfigError( + `Error parsing configuration for ${chalk.bold( + key, + )}: "${pattern}" could not be parsed.\n` + + `Error: ${err.message}`, + ); + } + }); break; case 'moduleFileExtensions': { value = options[key]; @@ -701,14 +717,14 @@ export default function normalize(options: InitialOptions, argv: Argv) { } } - if (options.testRegex && options.testMatch) { + if (newOptions.testRegex.length && options.testMatch) { throw createConfigError( ` Configuration options ${chalk.bold('testMatch')} and` + ` ${chalk.bold('testRegex')} cannot be used together.`, ); } - if (options.testRegex && !options.testMatch) { + if (newOptions.testRegex.length && !options.testMatch) { // Prevent the default testMatch conflicting with any explicitly // configured `testRegex` value newOptions.testMatch = []; diff --git a/packages/jest-editor-support/src/Settings.js b/packages/jest-editor-support/src/Settings.js index d9cac96c91a4..d11da69ab998 100644 --- a/packages/jest-editor-support/src/Settings.js +++ b/packages/jest-editor-support/src/Settings.js @@ -28,7 +28,7 @@ import {createProcess} from './Process'; type Glob = string; type ConfigRepresentation = { - testRegex: string, + testRegex: string | Array, testMatch: Array, }; @@ -59,7 +59,7 @@ export default class Settings extends EventEmitter { // Defaults for a Jest project this.settings = { testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).js?(x)'], - testRegex: '(/__tests__/.*|\\.(test|spec))\\.jsx?$', + testRegex: ['(/__tests__/.*|\\.(test|spec))\\.jsx?$'], }; this.configs = [this.settings]; diff --git a/packages/jest-runtime/src/__tests__/should_instrument.test.js b/packages/jest-runtime/src/__tests__/should_instrument.test.js index a6891c48f15d..9003579b427d 100644 --- a/packages/jest-runtime/src/__tests__/should_instrument.test.js +++ b/packages/jest-runtime/src/__tests__/should_instrument.test.js @@ -29,7 +29,13 @@ describe('should_instrument', () => { it('when testRegex provided and file is not a test file', () => { testShouldInstrument('source_file.js', defaultOptions, { - testRegex: '.*\\.(test)\\.(js)$', + testRegex: [/.*\.(test)\\.(js)$'/], + }); + }); + + it('when more than one testRegex is provided and filename is not a test file', () => { + testShouldInstrument('source_file.js', defaultOptions, { + testRegex: [/.*\_(test)\.(js)$/, /.*\.(test)\.(js)$/, /never/], }); }); @@ -88,7 +94,7 @@ describe('should_instrument', () => { testShouldInstrument('do/collect/sum.coverage.test.js', defaultOptions, { forceCoverageMatch: ['**/*.(coverage).(test).js'], rootDir: '/', - testRegex: '.*\\.(test)\\.(js)$', + testRegex: ['.*\\.(test)\\.(js)$'], }); }); }); @@ -115,7 +121,13 @@ describe('should_instrument', () => { it('when testRegex provided and filename is a test file', () => { testShouldInstrument(defaultFilename, defaultOptions, { - testRegex: '.*\\.(test)\\.(js)$', + testRegex: [/.*\.(test)\.(js)$/], + }); + }); + + it('when more than one testRegex is provided and filename matches one of the patterns', () => { + testShouldInstrument(defaultFilename, defaultOptions, { + testRegex: [/.*\_(test)\.(js)$/, /.*\.(test)\.(js)$/, /never/], }); }); diff --git a/packages/jest-runtime/src/should_instrument.js b/packages/jest-runtime/src/should_instrument.js index 7d9f76e6ee90..1f0317f8d715 100644 --- a/packages/jest-runtime/src/should_instrument.js +++ b/packages/jest-runtime/src/should_instrument.js @@ -35,7 +35,10 @@ export default function shouldInstrument( return true; } - if (config.testRegex && filename.match(config.testRegex)) { + if ( + config.testRegex && + config.testRegex.some(regex => regex.test(filename)) + ) { return false; } diff --git a/types/Argv.js b/types/Argv.js index ef157f155dfd..cc552d883996 100644 --- a/types/Argv.js +++ b/types/Argv.js @@ -81,7 +81,7 @@ export type Argv = {| testNamePattern: string, testPathIgnorePatterns: Array, testPathPattern: Array, - testRegex: string, + testRegex: string | Array, testResultsProcessor: ?string, testRunner: string, testURL: string, diff --git a/types/Config.js b/types/Config.js index b08323e950bb..569c49b55d61 100644 --- a/types/Config.js +++ b/types/Config.js @@ -74,7 +74,7 @@ export type DefaultOptions = {| testLocationInResults: boolean, testMatch: Array, testPathIgnorePatterns: Array, - testRegex: string, + testRegex: Array, testResultsProcessor: ?string, testRunner: ?string, testURL: string, @@ -165,7 +165,7 @@ export type InitialOptions = { testNamePattern?: string, testPathDirs?: Array, testPathIgnorePatterns?: Array, - testRegex?: string, + testRegex?: string | Array, testResultsProcessor?: ?string, testRunner?: string, testURL?: string, @@ -282,7 +282,7 @@ export type ProjectConfig = {| testMatch: Array, testLocationInResults: boolean, testPathIgnorePatterns: Array, - testRegex: string, + testRegex: Array, testRunner: string, testURL: string, timers: 'real' | 'fake',