You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1372 lines
37 KiB
1372 lines
37 KiB
'use strict'; |
|
|
|
Object.defineProperty(exports, '__esModule', { |
|
value: true |
|
}); |
|
exports.default = normalize; |
|
|
|
function _crypto() { |
|
const data = require('crypto'); |
|
|
|
_crypto = function () { |
|
return data; |
|
}; |
|
|
|
return data; |
|
} |
|
|
|
function path() { |
|
const data = _interopRequireWildcard(require('path')); |
|
|
|
path = function () { |
|
return data; |
|
}; |
|
|
|
return data; |
|
} |
|
|
|
function _chalk() { |
|
const data = _interopRequireDefault(require('chalk')); |
|
|
|
_chalk = function () { |
|
return data; |
|
}; |
|
|
|
return data; |
|
} |
|
|
|
function _deepmerge() { |
|
const data = _interopRequireDefault(require('deepmerge')); |
|
|
|
_deepmerge = function () { |
|
return data; |
|
}; |
|
|
|
return data; |
|
} |
|
|
|
function _glob() { |
|
const data = require('glob'); |
|
|
|
_glob = function () { |
|
return data; |
|
}; |
|
|
|
return data; |
|
} |
|
|
|
function _gracefulFs() { |
|
const data = require('graceful-fs'); |
|
|
|
_gracefulFs = function () { |
|
return data; |
|
}; |
|
|
|
return data; |
|
} |
|
|
|
function _micromatch() { |
|
const data = _interopRequireDefault(require('micromatch')); |
|
|
|
_micromatch = function () { |
|
return data; |
|
}; |
|
|
|
return data; |
|
} |
|
|
|
function _jestRegexUtil() { |
|
const data = require('jest-regex-util'); |
|
|
|
_jestRegexUtil = function () { |
|
return data; |
|
}; |
|
|
|
return data; |
|
} |
|
|
|
function _jestResolve() { |
|
const data = _interopRequireWildcard(require('jest-resolve')); |
|
|
|
_jestResolve = function () { |
|
return data; |
|
}; |
|
|
|
return data; |
|
} |
|
|
|
function _jestUtil() { |
|
const data = require('jest-util'); |
|
|
|
_jestUtil = function () { |
|
return data; |
|
}; |
|
|
|
return data; |
|
} |
|
|
|
function _jestValidate() { |
|
const data = require('jest-validate'); |
|
|
|
_jestValidate = function () { |
|
return data; |
|
}; |
|
|
|
return data; |
|
} |
|
|
|
var _Defaults = _interopRequireDefault(require('./Defaults')); |
|
|
|
var _Deprecated = _interopRequireDefault(require('./Deprecated')); |
|
|
|
var _ReporterValidationErrors = require('./ReporterValidationErrors'); |
|
|
|
var _ValidConfig = _interopRequireDefault(require('./ValidConfig')); |
|
|
|
var _color = require('./color'); |
|
|
|
var _constants = require('./constants'); |
|
|
|
var _getMaxWorkers = _interopRequireDefault(require('./getMaxWorkers')); |
|
|
|
var _setFromArgv = _interopRequireDefault(require('./setFromArgv')); |
|
|
|
var _utils = require('./utils'); |
|
|
|
var _validatePattern = _interopRequireDefault(require('./validatePattern')); |
|
|
|
function _interopRequireDefault(obj) { |
|
return obj && obj.__esModule ? obj : {default: obj}; |
|
} |
|
|
|
function _getRequireWildcardCache(nodeInterop) { |
|
if (typeof WeakMap !== 'function') return null; |
|
var cacheBabelInterop = new WeakMap(); |
|
var cacheNodeInterop = new WeakMap(); |
|
return (_getRequireWildcardCache = function (nodeInterop) { |
|
return nodeInterop ? cacheNodeInterop : cacheBabelInterop; |
|
})(nodeInterop); |
|
} |
|
|
|
function _interopRequireWildcard(obj, nodeInterop) { |
|
if (!nodeInterop && obj && obj.__esModule) { |
|
return obj; |
|
} |
|
if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { |
|
return {default: obj}; |
|
} |
|
var cache = _getRequireWildcardCache(nodeInterop); |
|
if (cache && cache.has(obj)) { |
|
return cache.get(obj); |
|
} |
|
var newObj = {}; |
|
var hasPropertyDescriptor = |
|
Object.defineProperty && Object.getOwnPropertyDescriptor; |
|
for (var key in obj) { |
|
if (key !== 'default' && Object.prototype.hasOwnProperty.call(obj, key)) { |
|
var desc = hasPropertyDescriptor |
|
? Object.getOwnPropertyDescriptor(obj, key) |
|
: null; |
|
if (desc && (desc.get || desc.set)) { |
|
Object.defineProperty(newObj, key, desc); |
|
} else { |
|
newObj[key] = obj[key]; |
|
} |
|
} |
|
} |
|
newObj.default = obj; |
|
if (cache) { |
|
cache.set(obj, newObj); |
|
} |
|
return newObj; |
|
} |
|
|
|
/** |
|
* Copyright (c) Facebook, Inc. and its affiliates. 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. |
|
*/ |
|
const ERROR = `${_utils.BULLET}Validation Error`; |
|
const PRESET_EXTENSIONS = ['.json', '.js', '.cjs', '.mjs']; |
|
const PRESET_NAME = 'jest-preset'; |
|
|
|
const createConfigError = message => |
|
new (_jestValidate().ValidationError)( |
|
ERROR, |
|
message, |
|
_utils.DOCUMENTATION_NOTE |
|
); |
|
|
|
function verifyDirectoryExists(path, key) { |
|
try { |
|
const rootStat = (0, _gracefulFs().statSync)(path); |
|
|
|
if (!rootStat.isDirectory()) { |
|
throw createConfigError( |
|
` ${_chalk().default.bold(path)} in the ${_chalk().default.bold( |
|
key |
|
)} option is not a directory.` |
|
); |
|
} |
|
} catch (err) { |
|
if (err instanceof _jestValidate().ValidationError) { |
|
throw err; |
|
} |
|
|
|
if (err.code === 'ENOENT') { |
|
throw createConfigError( |
|
` Directory ${_chalk().default.bold( |
|
path |
|
)} in the ${_chalk().default.bold(key)} option was not found.` |
|
); |
|
} // Not sure in which cases `statSync` can throw, so let's just show the underlying error to the user |
|
|
|
throw createConfigError( |
|
` Got an error trying to find ${_chalk().default.bold( |
|
path |
|
)} in the ${_chalk().default.bold(key)} option.\n\n Error was: ${ |
|
err.message |
|
}` |
|
); |
|
} |
|
} // TS 3.5 forces us to split these into 2 |
|
|
|
const mergeModuleNameMapperWithPreset = (options, preset) => { |
|
if (options['moduleNameMapper'] && preset['moduleNameMapper']) { |
|
options['moduleNameMapper'] = { |
|
...options['moduleNameMapper'], |
|
...preset['moduleNameMapper'], |
|
...options['moduleNameMapper'] |
|
}; |
|
} |
|
}; |
|
|
|
const mergeTransformWithPreset = (options, preset) => { |
|
if (options['transform'] && preset['transform']) { |
|
options['transform'] = { |
|
...options['transform'], |
|
...preset['transform'], |
|
...options['transform'] |
|
}; |
|
} |
|
}; |
|
|
|
const mergeGlobalsWithPreset = (options, preset) => { |
|
if (options['globals'] && preset['globals']) { |
|
options['globals'] = (0, _deepmerge().default)( |
|
preset['globals'], |
|
options['globals'] |
|
); |
|
} |
|
}; |
|
|
|
const setupPreset = async (options, optionsPreset) => { |
|
let preset; |
|
const presetPath = (0, _utils.replaceRootDirInPath)( |
|
options.rootDir, |
|
optionsPreset |
|
); |
|
|
|
const presetModule = _jestResolve().default.findNodeModule( |
|
presetPath.startsWith('.') |
|
? presetPath |
|
: path().join(presetPath, PRESET_NAME), |
|
{ |
|
basedir: options.rootDir, |
|
extensions: PRESET_EXTENSIONS |
|
} |
|
); |
|
|
|
try { |
|
if (!presetModule) { |
|
throw new Error(`Cannot find module '${presetPath}'`); |
|
} // Force re-evaluation to support multiple projects |
|
|
|
try { |
|
delete require.cache[require.resolve(presetModule)]; |
|
} catch {} |
|
|
|
preset = await (0, _jestUtil().requireOrImportModule)(presetModule); |
|
} catch (error) { |
|
if (error instanceof SyntaxError || error instanceof TypeError) { |
|
throw createConfigError( |
|
` Preset ${_chalk().default.bold(presetPath)} is invalid:\n\n ${ |
|
error.message |
|
}\n ${error.stack}` |
|
); |
|
} |
|
|
|
if (error.message.includes('Cannot find module')) { |
|
if (error.message.includes(presetPath)) { |
|
const preset = _jestResolve().default.findNodeModule(presetPath, { |
|
basedir: options.rootDir |
|
}); |
|
|
|
if (preset) { |
|
throw createConfigError( |
|
` Module ${_chalk().default.bold( |
|
presetPath |
|
)} should have "jest-preset.js" or "jest-preset.json" file at the root.` |
|
); |
|
} |
|
|
|
throw createConfigError( |
|
` Preset ${_chalk().default.bold(presetPath)} not found.` |
|
); |
|
} |
|
|
|
throw createConfigError( |
|
` Missing dependency in ${_chalk().default.bold(presetPath)}:\n\n ${ |
|
error.message |
|
}\n ${error.stack}` |
|
); |
|
} |
|
|
|
throw createConfigError( |
|
` An unknown error occurred in ${_chalk().default.bold( |
|
presetPath |
|
)}:\n\n ${error.message}\n ${error.stack}` |
|
); |
|
} |
|
|
|
if (options.setupFiles) { |
|
options.setupFiles = (preset.setupFiles || []).concat(options.setupFiles); |
|
} |
|
|
|
if (options.setupFilesAfterEnv) { |
|
options.setupFilesAfterEnv = (preset.setupFilesAfterEnv || []).concat( |
|
options.setupFilesAfterEnv |
|
); |
|
} |
|
|
|
if (options.modulePathIgnorePatterns && preset.modulePathIgnorePatterns) { |
|
options.modulePathIgnorePatterns = preset.modulePathIgnorePatterns.concat( |
|
options.modulePathIgnorePatterns |
|
); |
|
} |
|
|
|
mergeModuleNameMapperWithPreset(options, preset); |
|
mergeTransformWithPreset(options, preset); |
|
mergeGlobalsWithPreset(options, preset); |
|
return {...preset, ...options}; |
|
}; |
|
|
|
const setupBabelJest = options => { |
|
const transform = options.transform; |
|
let babelJest; |
|
|
|
if (transform) { |
|
const customJSPattern = Object.keys(transform).find(pattern => { |
|
const regex = new RegExp(pattern); |
|
return regex.test('a.js') || regex.test('a.jsx'); |
|
}); |
|
const customTSPattern = Object.keys(transform).find(pattern => { |
|
const regex = new RegExp(pattern); |
|
return regex.test('a.ts') || regex.test('a.tsx'); |
|
}); |
|
[customJSPattern, customTSPattern].forEach(pattern => { |
|
if (pattern) { |
|
const customTransformer = transform[pattern]; |
|
|
|
if (Array.isArray(customTransformer)) { |
|
if (customTransformer[0] === 'babel-jest') { |
|
babelJest = require.resolve('babel-jest'); |
|
customTransformer[0] = babelJest; |
|
} else if (customTransformer[0].includes('babel-jest')) { |
|
babelJest = customTransformer[0]; |
|
} |
|
} else { |
|
if (customTransformer === 'babel-jest') { |
|
babelJest = require.resolve('babel-jest'); |
|
transform[pattern] = babelJest; |
|
} else if (customTransformer.includes('babel-jest')) { |
|
babelJest = customTransformer; |
|
} |
|
} |
|
} |
|
}); |
|
} else { |
|
babelJest = require.resolve('babel-jest'); |
|
options.transform = { |
|
[_constants.DEFAULT_JS_PATTERN]: babelJest |
|
}; |
|
} |
|
}; |
|
|
|
const normalizeCollectCoverageOnlyFrom = (options, key) => { |
|
const initialCollectCoverageFrom = options[key]; |
|
const collectCoverageOnlyFrom = Array.isArray(initialCollectCoverageFrom) |
|
? initialCollectCoverageFrom // passed from argv |
|
: Object.keys(initialCollectCoverageFrom); // passed from options |
|
|
|
return collectCoverageOnlyFrom.reduce((map, filePath) => { |
|
filePath = path().resolve( |
|
options.rootDir, |
|
(0, _utils.replaceRootDirInPath)(options.rootDir, filePath) |
|
); |
|
map[filePath] = true; |
|
return map; |
|
}, Object.create(null)); |
|
}; |
|
|
|
const normalizeCollectCoverageFrom = (options, key) => { |
|
const initialCollectCoverageFrom = options[key]; |
|
let value; |
|
|
|
if (!initialCollectCoverageFrom) { |
|
value = []; |
|
} |
|
|
|
if (!Array.isArray(initialCollectCoverageFrom)) { |
|
try { |
|
value = JSON.parse(initialCollectCoverageFrom); |
|
} catch {} |
|
|
|
if (options[key] && !Array.isArray(value)) { |
|
value = [initialCollectCoverageFrom]; |
|
} |
|
} else { |
|
value = initialCollectCoverageFrom; |
|
} |
|
|
|
if (value) { |
|
value = value.map(filePath => |
|
filePath.replace(/^(!?)(<rootDir>\/)(.*)/, '$1$3') |
|
); |
|
} |
|
|
|
return value; |
|
}; |
|
|
|
const normalizeUnmockedModulePathPatterns = ( |
|
options, |
|
key // _replaceRootDirTags is specifically well-suited for substituting |
|
) => |
|
// <rootDir> in paths (it deals with properly interpreting relative path |
|
// separators, etc). |
|
// |
|
// For patterns, direct global substitution is far more ideal, so we |
|
// special case substitutions for patterns here. |
|
options[key].map(pattern => |
|
(0, _jestRegexUtil().replacePathSepForRegex)( |
|
pattern.replace(/<rootDir>/g, options.rootDir) |
|
) |
|
); |
|
|
|
const normalizePreprocessor = options => { |
|
if (options.scriptPreprocessor && options.transform) { |
|
throw createConfigError(` Options: ${_chalk().default.bold( |
|
'scriptPreprocessor' |
|
)} and ${_chalk().default.bold('transform')} cannot be used together. |
|
Please change your configuration to only use ${_chalk().default.bold( |
|
'transform' |
|
)}.`); |
|
} |
|
|
|
if (options.preprocessorIgnorePatterns && options.transformIgnorePatterns) { |
|
throw createConfigError(` Options ${_chalk().default.bold( |
|
'preprocessorIgnorePatterns' |
|
)} and ${_chalk().default.bold( |
|
'transformIgnorePatterns' |
|
)} cannot be used together. |
|
Please change your configuration to only use ${_chalk().default.bold( |
|
'transformIgnorePatterns' |
|
)}.`); |
|
} |
|
|
|
if (options.scriptPreprocessor) { |
|
options.transform = { |
|
'.*': options.scriptPreprocessor |
|
}; |
|
} |
|
|
|
if (options.preprocessorIgnorePatterns) { |
|
options.transformIgnorePatterns = options.preprocessorIgnorePatterns; |
|
} |
|
|
|
delete options.scriptPreprocessor; |
|
delete options.preprocessorIgnorePatterns; |
|
return options; |
|
}; |
|
|
|
const normalizeMissingOptions = (options, configPath, projectIndex) => { |
|
if (!options.name) { |
|
options.name = (0, _crypto().createHash)('md5') |
|
.update(options.rootDir) // In case we load config from some path that has the same root dir |
|
.update(configPath || '') |
|
.update(String(projectIndex)) |
|
.digest('hex'); |
|
} |
|
|
|
if (!options.setupFiles) { |
|
options.setupFiles = []; |
|
} |
|
|
|
return options; |
|
}; |
|
|
|
const normalizeRootDir = options => { |
|
// Assert that there *is* a rootDir |
|
if (!options.rootDir) { |
|
throw createConfigError( |
|
` Configuration option ${_chalk().default.bold( |
|
'rootDir' |
|
)} must be specified.` |
|
); |
|
} |
|
|
|
options.rootDir = path().normalize(options.rootDir); |
|
|
|
try { |
|
// try to resolve windows short paths, ignoring errors (permission errors, mostly) |
|
options.rootDir = (0, _jestUtil().tryRealpath)(options.rootDir); |
|
} catch { |
|
// ignored |
|
} |
|
|
|
verifyDirectoryExists(options.rootDir, 'rootDir'); |
|
return {...options, rootDir: options.rootDir}; |
|
}; |
|
|
|
const normalizeReporters = options => { |
|
const reporters = options.reporters; |
|
|
|
if (!reporters || !Array.isArray(reporters)) { |
|
return options; |
|
} |
|
|
|
(0, _ReporterValidationErrors.validateReporters)(reporters); |
|
options.reporters = reporters.map(reporterConfig => { |
|
const normalizedReporterConfig = |
|
typeof reporterConfig === 'string' // if reporter config is a string, we wrap it in an array |
|
? // and pass an empty object for options argument, to normalize |
|
// the shape. |
|
[reporterConfig, {}] |
|
: reporterConfig; |
|
const reporterPath = (0, _utils.replaceRootDirInPath)( |
|
options.rootDir, |
|
normalizedReporterConfig[0] |
|
); |
|
|
|
if (reporterPath !== _constants.DEFAULT_REPORTER_LABEL) { |
|
const reporter = _jestResolve().default.findNodeModule(reporterPath, { |
|
basedir: options.rootDir |
|
}); |
|
|
|
if (!reporter) { |
|
throw new (_jestResolve().default.ModuleNotFoundError)( |
|
'Could not resolve a module for a custom reporter.\n' + |
|
` Module name: ${reporterPath}` |
|
); |
|
} |
|
|
|
normalizedReporterConfig[0] = reporter; |
|
} |
|
|
|
return normalizedReporterConfig; |
|
}); |
|
return options; |
|
}; |
|
|
|
const buildTestPathPattern = argv => { |
|
const patterns = []; |
|
|
|
if (argv._) { |
|
patterns.push(...argv._); |
|
} |
|
|
|
if (argv.testPathPattern) { |
|
patterns.push(...argv.testPathPattern); |
|
} |
|
|
|
const replacePosixSep = pattern => { |
|
// yargs coerces positional args into numbers |
|
const patternAsString = pattern.toString(); |
|
|
|
if (path().sep === '/') { |
|
return patternAsString; |
|
} |
|
|
|
return patternAsString.replace(/\//g, '\\\\'); |
|
}; |
|
|
|
const testPathPattern = patterns.map(replacePosixSep).join('|'); |
|
|
|
if ((0, _validatePattern.default)(testPathPattern)) { |
|
return testPathPattern; |
|
} else { |
|
showTestPathPatternError(testPathPattern); |
|
return ''; |
|
} |
|
}; |
|
|
|
const showTestPathPatternError = testPathPattern => { |
|
(0, _jestUtil().clearLine)(process.stdout); // eslint-disable-next-line no-console |
|
|
|
console.log( |
|
_chalk().default.red( |
|
` Invalid testPattern ${testPathPattern} supplied. ` + |
|
'Running all tests instead.' |
|
) |
|
); |
|
}; |
|
|
|
function validateExtensionsToTreatAsEsm(extensionsToTreatAsEsm) { |
|
if (!extensionsToTreatAsEsm || extensionsToTreatAsEsm.length === 0) { |
|
return; |
|
} |
|
|
|
function printConfig(opts) { |
|
const string = opts.map(ext => `'${ext}'`).join(', '); |
|
return _chalk().default.bold(`extensionsToTreatAsEsm: [${string}]`); |
|
} |
|
|
|
const extensionWithoutDot = extensionsToTreatAsEsm.some( |
|
ext => !ext.startsWith('.') |
|
); |
|
|
|
if (extensionWithoutDot) { |
|
throw createConfigError(` Option: ${printConfig( |
|
extensionsToTreatAsEsm |
|
)} includes a string that does not start with a period (${_chalk().default.bold( |
|
'.' |
|
)}). |
|
Please change your configuration to ${printConfig( |
|
extensionsToTreatAsEsm.map(ext => (ext.startsWith('.') ? ext : `.${ext}`)) |
|
)}.`); |
|
} |
|
|
|
if (extensionsToTreatAsEsm.includes('.js')) { |
|
throw createConfigError( |
|
` Option: ${printConfig( |
|
extensionsToTreatAsEsm |
|
)} includes ${_chalk().default.bold( |
|
"'.js'" |
|
)} which is always inferred based on ${_chalk().default.bold( |
|
'type' |
|
)} in its nearest ${_chalk().default.bold('package.json')}.` |
|
); |
|
} |
|
|
|
if (extensionsToTreatAsEsm.includes('.cjs')) { |
|
throw createConfigError( |
|
` Option: ${printConfig( |
|
extensionsToTreatAsEsm |
|
)} includes ${_chalk().default.bold( |
|
"'.cjs'" |
|
)} which is always treated as CommonJS.` |
|
); |
|
} |
|
|
|
if (extensionsToTreatAsEsm.includes('.mjs')) { |
|
throw createConfigError( |
|
` Option: ${printConfig( |
|
extensionsToTreatAsEsm |
|
)} includes ${_chalk().default.bold( |
|
"'.mjs'" |
|
)} which is always treated as an ECMAScript Module.` |
|
); |
|
} |
|
} |
|
|
|
async function normalize( |
|
initialOptions, |
|
argv, |
|
configPath, |
|
projectIndex = Infinity |
|
) { |
|
var _options$haste, _argv$_; |
|
|
|
const {hasDeprecationWarnings} = (0, _jestValidate().validate)( |
|
initialOptions, |
|
{ |
|
comment: _utils.DOCUMENTATION_NOTE, |
|
deprecatedConfig: _Deprecated.default, |
|
exampleConfig: _ValidConfig.default, |
|
recursiveDenylist: [ |
|
'collectCoverageOnlyFrom', // 'coverageThreshold' allows to use 'global' and glob strings on the same |
|
// level, there's currently no way we can deal with such config |
|
'coverageThreshold', |
|
'globals', |
|
'moduleNameMapper', |
|
'testEnvironmentOptions', |
|
'transform' |
|
] |
|
} |
|
); |
|
let options = normalizePreprocessor( |
|
normalizeReporters( |
|
normalizeMissingOptions( |
|
normalizeRootDir((0, _setFromArgv.default)(initialOptions, argv)), |
|
configPath, |
|
projectIndex |
|
) |
|
) |
|
); |
|
|
|
if (options.preset) { |
|
options = await setupPreset(options, options.preset); |
|
} |
|
|
|
if (!options.setupFilesAfterEnv) { |
|
options.setupFilesAfterEnv = []; |
|
} |
|
|
|
if ( |
|
options.setupTestFrameworkScriptFile && |
|
options.setupFilesAfterEnv.length > 0 |
|
) { |
|
throw createConfigError(` Options: ${_chalk().default.bold( |
|
'setupTestFrameworkScriptFile' |
|
)} and ${_chalk().default.bold( |
|
'setupFilesAfterEnv' |
|
)} cannot be used together. |
|
Please change your configuration to only use ${_chalk().default.bold( |
|
'setupFilesAfterEnv' |
|
)}.`); |
|
} |
|
|
|
if (options.setupTestFrameworkScriptFile) { |
|
options.setupFilesAfterEnv.push(options.setupTestFrameworkScriptFile); |
|
} |
|
|
|
options.testEnvironment = (0, _jestResolve().resolveTestEnvironment)({ |
|
requireResolveFunction: require.resolve, |
|
rootDir: options.rootDir, |
|
testEnvironment: |
|
options.testEnvironment || |
|
require.resolve(_Defaults.default.testEnvironment) |
|
}); |
|
|
|
if (!options.roots && options.testPathDirs) { |
|
options.roots = options.testPathDirs; |
|
delete options.testPathDirs; |
|
} |
|
|
|
if (!options.roots) { |
|
options.roots = [options.rootDir]; |
|
} |
|
|
|
if ( |
|
!options.testRunner || |
|
options.testRunner === 'circus' || |
|
options.testRunner === 'jest-circus' |
|
) { |
|
options.testRunner = require.resolve('jest-circus/runner'); |
|
} else if (options.testRunner === 'jasmine2') { |
|
options.testRunner = require.resolve('jest-jasmine2'); |
|
} |
|
|
|
if (!options.coverageDirectory) { |
|
options.coverageDirectory = path().resolve(options.rootDir, 'coverage'); |
|
} |
|
|
|
setupBabelJest(options); // TODO: Type this properly |
|
|
|
const newOptions = {..._Defaults.default}; |
|
|
|
if (options.resolver) { |
|
newOptions.resolver = (0, _utils.resolve)(null, { |
|
filePath: options.resolver, |
|
key: 'resolver', |
|
rootDir: options.rootDir |
|
}); |
|
} |
|
|
|
validateExtensionsToTreatAsEsm(options.extensionsToTreatAsEsm); |
|
|
|
if (options.watchman == null) { |
|
options.watchman = _Defaults.default.watchman; |
|
} |
|
|
|
const optionKeys = Object.keys(options); |
|
optionKeys.reduce((newOptions, key) => { |
|
// The resolver has been resolved separately; skip it |
|
if (key === 'resolver') { |
|
return newOptions; |
|
} // This is cheating, because it claims that all keys of InitialOptions are Required. |
|
// We only really know it's Required for oldOptions[key], not for oldOptions.someOtherKey, |
|
// so oldOptions[key] is the only way it should be used. |
|
|
|
const oldOptions = options; |
|
let value; |
|
|
|
switch (key) { |
|
case 'collectCoverageOnlyFrom': |
|
value = normalizeCollectCoverageOnlyFrom(oldOptions, key); |
|
break; |
|
|
|
case 'setupFiles': |
|
case 'setupFilesAfterEnv': |
|
case 'snapshotSerializers': |
|
{ |
|
const option = oldOptions[key]; |
|
value = |
|
option && |
|
option.map(filePath => |
|
(0, _utils.resolve)(newOptions.resolver, { |
|
filePath, |
|
key, |
|
rootDir: options.rootDir |
|
}) |
|
); |
|
} |
|
break; |
|
|
|
case 'modulePaths': |
|
case 'roots': |
|
{ |
|
const option = oldOptions[key]; |
|
value = |
|
option && |
|
option.map(filePath => |
|
path().resolve( |
|
options.rootDir, |
|
(0, _utils.replaceRootDirInPath)(options.rootDir, filePath) |
|
) |
|
); |
|
} |
|
break; |
|
|
|
case 'collectCoverageFrom': |
|
value = normalizeCollectCoverageFrom(oldOptions, key); |
|
break; |
|
|
|
case 'cacheDirectory': |
|
case 'coverageDirectory': |
|
{ |
|
const option = oldOptions[key]; |
|
value = |
|
option && |
|
path().resolve( |
|
options.rootDir, |
|
(0, _utils.replaceRootDirInPath)(options.rootDir, option) |
|
); |
|
} |
|
break; |
|
|
|
case 'dependencyExtractor': |
|
case 'globalSetup': |
|
case 'globalTeardown': |
|
case 'moduleLoader': |
|
case 'snapshotResolver': |
|
case 'testResultsProcessor': |
|
case 'testRunner': |
|
case 'filter': |
|
{ |
|
const option = oldOptions[key]; |
|
value = |
|
option && |
|
(0, _utils.resolve)(newOptions.resolver, { |
|
filePath: option, |
|
key, |
|
rootDir: options.rootDir |
|
}); |
|
} |
|
break; |
|
|
|
case 'runner': |
|
{ |
|
const option = oldOptions[key]; |
|
value = |
|
option && |
|
(0, _jestResolve().resolveRunner)(newOptions.resolver, { |
|
filePath: option, |
|
requireResolveFunction: require.resolve, |
|
rootDir: options.rootDir |
|
}); |
|
} |
|
break; |
|
|
|
case 'prettierPath': |
|
{ |
|
// We only want this to throw if "prettierPath" is explicitly passed |
|
// from config or CLI, and the requested path isn't found. Otherwise we |
|
// set it to null and throw an error lazily when it is used. |
|
const option = oldOptions[key]; |
|
value = |
|
option && |
|
(0, _utils.resolve)(newOptions.resolver, { |
|
filePath: option, |
|
key, |
|
optional: option === _Defaults.default[key], |
|
rootDir: options.rootDir |
|
}); |
|
} |
|
break; |
|
|
|
case 'moduleNameMapper': |
|
const moduleNameMapper = oldOptions[key]; |
|
value = |
|
moduleNameMapper && |
|
Object.keys(moduleNameMapper).map(regex => { |
|
const item = moduleNameMapper && moduleNameMapper[regex]; |
|
return ( |
|
item && [ |
|
regex, |
|
(0, _utils._replaceRootDirTags)(options.rootDir, item) |
|
] |
|
); |
|
}); |
|
break; |
|
|
|
case 'transform': |
|
const transform = oldOptions[key]; |
|
value = |
|
transform && |
|
Object.keys(transform).map(regex => { |
|
const transformElement = transform[regex]; |
|
return [ |
|
regex, |
|
(0, _utils.resolve)(newOptions.resolver, { |
|
filePath: Array.isArray(transformElement) |
|
? transformElement[0] |
|
: transformElement, |
|
key, |
|
rootDir: options.rootDir |
|
}), |
|
Array.isArray(transformElement) ? transformElement[1] : {} |
|
]; |
|
}); |
|
break; |
|
|
|
case 'coveragePathIgnorePatterns': |
|
case 'modulePathIgnorePatterns': |
|
case 'testPathIgnorePatterns': |
|
case 'transformIgnorePatterns': |
|
case 'watchPathIgnorePatterns': |
|
case 'unmockedModulePathPatterns': |
|
value = normalizeUnmockedModulePathPatterns(oldOptions, key); |
|
break; |
|
|
|
case 'haste': |
|
value = {...oldOptions[key]}; |
|
|
|
if (value.hasteImplModulePath != null) { |
|
const resolvedHasteImpl = (0, _utils.resolve)(newOptions.resolver, { |
|
filePath: (0, _utils.replaceRootDirInPath)( |
|
options.rootDir, |
|
value.hasteImplModulePath |
|
), |
|
key: 'haste.hasteImplModulePath', |
|
rootDir: options.rootDir |
|
}); |
|
value.hasteImplModulePath = resolvedHasteImpl || undefined; |
|
} |
|
|
|
break; |
|
|
|
case 'projects': |
|
value = (oldOptions[key] || []) |
|
.map(project => |
|
typeof project === 'string' |
|
? (0, _utils._replaceRootDirTags)(options.rootDir, project) |
|
: project |
|
) |
|
.reduce((projects, project) => { |
|
// Project can be specified as globs. If a glob matches any files, |
|
// We expand it to these paths. If not, we keep the original path |
|
// for the future resolution. |
|
const globMatches = |
|
typeof project === 'string' ? (0, _glob().sync)(project) : []; |
|
return projects.concat(globMatches.length ? globMatches : project); |
|
}, []); |
|
break; |
|
|
|
case 'moduleDirectories': |
|
case 'testMatch': |
|
{ |
|
const replacedRootDirTags = (0, _utils._replaceRootDirTags)( |
|
(0, _utils.escapeGlobCharacters)(options.rootDir), |
|
oldOptions[key] |
|
); |
|
|
|
if (replacedRootDirTags) { |
|
value = Array.isArray(replacedRootDirTags) |
|
? replacedRootDirTags.map(_jestUtil().replacePathSepForGlob) |
|
: (0, _jestUtil().replacePathSepForGlob)(replacedRootDirTags); |
|
} else { |
|
value = replacedRootDirTags; |
|
} |
|
} |
|
break; |
|
|
|
case 'testRegex': |
|
{ |
|
const option = oldOptions[key]; |
|
value = option |
|
? (Array.isArray(option) ? option : [option]).map( |
|
_jestRegexUtil().replacePathSepForRegex |
|
) |
|
: []; |
|
} |
|
break; |
|
|
|
case 'moduleFileExtensions': { |
|
value = oldOptions[key]; |
|
|
|
if ( |
|
Array.isArray(value) && // If it's the wrong type, it can throw at a later time |
|
(options.runner === undefined || |
|
options.runner === _Defaults.default.runner) && // Only require 'js' for the default jest-runner |
|
!value.includes('js') |
|
) { |
|
const errorMessage = |
|
" moduleFileExtensions must include 'js':\n" + |
|
' but instead received:\n' + |
|
` ${_chalk().default.bold.red(JSON.stringify(value))}`; // If `js` is not included, any dependency Jest itself injects into |
|
// the environment, like jasmine or sourcemap-support, will need to |
|
// `require` its modules with a file extension. This is not plausible |
|
// in the long run, so it's way easier to just fail hard early. |
|
// We might consider throwing if `json` is missing as well, as it's a |
|
// fair assumption from modules that they can do |
|
// `require('some-package/package') without the trailing `.json` as it |
|
// works in Node normally. |
|
|
|
throw createConfigError( |
|
errorMessage + |
|
"\n Please change your configuration to include 'js'." |
|
); |
|
} |
|
|
|
break; |
|
} |
|
|
|
case 'bail': { |
|
const bail = oldOptions[key]; |
|
|
|
if (typeof bail === 'boolean') { |
|
value = bail ? 1 : 0; |
|
} else if (typeof bail === 'string') { |
|
value = 1; // If Jest is invoked as `jest --bail someTestPattern` then need to |
|
// move the pattern from the `bail` configuration and into `argv._` |
|
// to be processed as an extra parameter |
|
|
|
argv._.push(bail); |
|
} else { |
|
value = oldOptions[key]; |
|
} |
|
|
|
break; |
|
} |
|
|
|
case 'displayName': { |
|
const displayName = oldOptions[key]; |
|
/** |
|
* Ensuring that displayName shape is correct here so that the |
|
* reporters can trust the shape of the data |
|
*/ |
|
|
|
if (typeof displayName === 'object') { |
|
const {name, color} = displayName; |
|
|
|
if ( |
|
!name || |
|
!color || |
|
typeof name !== 'string' || |
|
typeof color !== 'string' |
|
) { |
|
const errorMessage = |
|
` Option "${_chalk().default.bold( |
|
'displayName' |
|
)}" must be of type:\n\n` + |
|
' {\n' + |
|
' name: string;\n' + |
|
' color: string;\n' + |
|
' }\n'; |
|
throw createConfigError(errorMessage); |
|
} |
|
|
|
value = oldOptions[key]; |
|
} else { |
|
value = { |
|
color: (0, _color.getDisplayNameColor)(options.runner), |
|
name: displayName |
|
}; |
|
} |
|
|
|
break; |
|
} |
|
|
|
case 'testTimeout': { |
|
if (oldOptions[key] < 0) { |
|
throw createConfigError( |
|
` Option "${_chalk().default.bold( |
|
'testTimeout' |
|
)}" must be a natural number.` |
|
); |
|
} |
|
|
|
value = oldOptions[key]; |
|
break; |
|
} |
|
|
|
case 'automock': |
|
case 'cache': |
|
case 'changedSince': |
|
case 'changedFilesWithAncestor': |
|
case 'clearMocks': |
|
case 'collectCoverage': |
|
case 'coverageProvider': |
|
case 'coverageReporters': |
|
case 'coverageThreshold': |
|
case 'detectLeaks': |
|
case 'detectOpenHandles': |
|
case 'errorOnDeprecated': |
|
case 'expand': |
|
case 'extensionsToTreatAsEsm': |
|
case 'extraGlobals': |
|
case 'globals': |
|
case 'findRelatedTests': |
|
case 'forceCoverageMatch': |
|
case 'forceExit': |
|
case 'injectGlobals': |
|
case 'lastCommit': |
|
case 'listTests': |
|
case 'logHeapUsage': |
|
case 'maxConcurrency': |
|
case 'name': |
|
case 'noStackTrace': |
|
case 'notify': |
|
case 'notifyMode': |
|
case 'onlyChanged': |
|
case 'onlyFailures': |
|
case 'outputFile': |
|
case 'passWithNoTests': |
|
case 'replname': |
|
case 'reporters': |
|
case 'resetMocks': |
|
case 'resetModules': |
|
case 'restoreMocks': |
|
case 'rootDir': |
|
case 'runTestsByPath': |
|
case 'silent': |
|
case 'skipFilter': |
|
case 'skipNodeResolution': |
|
case 'slowTestThreshold': |
|
case 'snapshotFormat': |
|
case 'testEnvironment': |
|
case 'testEnvironmentOptions': |
|
case 'testFailureExitCode': |
|
case 'testLocationInResults': |
|
case 'testNamePattern': |
|
case 'testURL': |
|
case 'timers': |
|
case 'useStderr': |
|
case 'verbose': |
|
case 'watch': |
|
case 'watchAll': |
|
case 'watchman': |
|
value = oldOptions[key]; |
|
break; |
|
|
|
case 'watchPlugins': |
|
value = (oldOptions[key] || []).map(watchPlugin => { |
|
if (typeof watchPlugin === 'string') { |
|
return { |
|
config: {}, |
|
path: (0, _jestResolve().resolveWatchPlugin)( |
|
newOptions.resolver, |
|
{ |
|
filePath: watchPlugin, |
|
requireResolveFunction: require.resolve, |
|
rootDir: options.rootDir |
|
} |
|
) |
|
}; |
|
} else { |
|
return { |
|
config: watchPlugin[1] || {}, |
|
path: (0, _jestResolve().resolveWatchPlugin)( |
|
newOptions.resolver, |
|
{ |
|
filePath: watchPlugin[0], |
|
requireResolveFunction: require.resolve, |
|
rootDir: options.rootDir |
|
} |
|
) |
|
}; |
|
} |
|
}); |
|
break; |
|
} // @ts-expect-error: automock is missing in GlobalConfig, so what |
|
|
|
newOptions[key] = value; |
|
return newOptions; |
|
}, newOptions); |
|
|
|
if ( |
|
options.watchman && |
|
(_options$haste = options.haste) !== null && |
|
_options$haste !== void 0 && |
|
_options$haste.enableSymlinks |
|
) { |
|
throw new (_jestValidate().ValidationError)( |
|
'Validation Error', |
|
'haste.enableSymlinks is incompatible with watchman', |
|
'Either set haste.enableSymlinks to false or do not use watchman' |
|
); |
|
} |
|
|
|
newOptions.roots.forEach((root, i) => { |
|
verifyDirectoryExists(root, `roots[${i}]`); |
|
}); |
|
|
|
try { |
|
// try to resolve windows short paths, ignoring errors (permission errors, mostly) |
|
newOptions.cwd = (0, _jestUtil().tryRealpath)(process.cwd()); |
|
} catch { |
|
// ignored |
|
} |
|
|
|
newOptions.testSequencer = (0, _jestResolve().resolveSequencer)( |
|
newOptions.resolver, |
|
{ |
|
filePath: |
|
options.testSequencer || |
|
require.resolve(_Defaults.default.testSequencer), |
|
requireResolveFunction: require.resolve, |
|
rootDir: options.rootDir |
|
} |
|
); |
|
|
|
if (newOptions.runner === _Defaults.default.runner) { |
|
newOptions.runner = require.resolve(newOptions.runner); |
|
} |
|
|
|
newOptions.nonFlagArgs = |
|
(_argv$_ = argv._) === null || _argv$_ === void 0 |
|
? void 0 |
|
: _argv$_.map(arg => `${arg}`); |
|
newOptions.testPathPattern = buildTestPathPattern(argv); |
|
newOptions.json = !!argv.json; |
|
newOptions.testFailureExitCode = parseInt(newOptions.testFailureExitCode, 10); |
|
|
|
if ( |
|
newOptions.lastCommit || |
|
newOptions.changedFilesWithAncestor || |
|
newOptions.changedSince |
|
) { |
|
newOptions.onlyChanged = true; |
|
} |
|
|
|
if (argv.all) { |
|
newOptions.onlyChanged = false; |
|
newOptions.onlyFailures = false; |
|
} else if (newOptions.testPathPattern) { |
|
// When passing a test path pattern we don't want to only monitor changed |
|
// files unless `--watch` is also passed. |
|
newOptions.onlyChanged = newOptions.watch; |
|
} |
|
|
|
if (!newOptions.onlyChanged) { |
|
newOptions.onlyChanged = false; |
|
} |
|
|
|
if (!newOptions.lastCommit) { |
|
newOptions.lastCommit = false; |
|
} |
|
|
|
if (!newOptions.onlyFailures) { |
|
newOptions.onlyFailures = false; |
|
} |
|
|
|
if (!newOptions.watchAll) { |
|
newOptions.watchAll = false; |
|
} // as unknown since it can happen. We really need to fix the types here |
|
|
|
if (newOptions.moduleNameMapper === _Defaults.default.moduleNameMapper) { |
|
newOptions.moduleNameMapper = []; |
|
} |
|
|
|
newOptions.updateSnapshot = |
|
argv.ci && !argv.updateSnapshot |
|
? 'none' |
|
: argv.updateSnapshot |
|
? 'all' |
|
: 'new'; |
|
newOptions.maxConcurrency = parseInt(newOptions.maxConcurrency, 10); |
|
newOptions.maxWorkers = (0, _getMaxWorkers.default)(argv, options); |
|
|
|
if (newOptions.testRegex.length && options.testMatch) { |
|
throw createConfigError( |
|
` Configuration options ${_chalk().default.bold('testMatch')} and` + |
|
` ${_chalk().default.bold('testRegex')} cannot be used together.` |
|
); |
|
} |
|
|
|
if (newOptions.testRegex.length && !options.testMatch) { |
|
// Prevent the default testMatch conflicting with any explicitly |
|
// configured `testRegex` value |
|
newOptions.testMatch = []; |
|
} // If argv.json is set, coverageReporters shouldn't print a text report. |
|
|
|
if (argv.json) { |
|
newOptions.coverageReporters = (newOptions.coverageReporters || []).filter( |
|
reporter => reporter !== 'text' |
|
); |
|
} // If collectCoverage is enabled while using --findRelatedTests we need to |
|
// avoid having false negatives in the generated coverage report. |
|
// The following: `--findRelatedTests '/rootDir/file1.js' --coverage` |
|
// Is transformed to: `--findRelatedTests '/rootDir/file1.js' --coverage --collectCoverageFrom 'file1.js'` |
|
// where arguments to `--collectCoverageFrom` should be globs (or relative |
|
// paths to the rootDir) |
|
|
|
if (newOptions.collectCoverage && argv.findRelatedTests) { |
|
let collectCoverageFrom = newOptions.nonFlagArgs.map(filename => { |
|
filename = (0, _utils.replaceRootDirInPath)(options.rootDir, filename); |
|
return path().isAbsolute(filename) |
|
? path().relative(options.rootDir, filename) |
|
: filename; |
|
}); // Don't override existing collectCoverageFrom options |
|
|
|
if (newOptions.collectCoverageFrom) { |
|
collectCoverageFrom = collectCoverageFrom.reduce((patterns, filename) => { |
|
if ( |
|
(0, _micromatch().default)( |
|
[ |
|
(0, _jestUtil().replacePathSepForGlob)( |
|
path().relative(options.rootDir, filename) |
|
) |
|
], |
|
newOptions.collectCoverageFrom |
|
).length === 0 |
|
) { |
|
return patterns; |
|
} |
|
|
|
return [...patterns, filename]; |
|
}, newOptions.collectCoverageFrom); |
|
} |
|
|
|
newOptions.collectCoverageFrom = collectCoverageFrom; |
|
} else if (!newOptions.collectCoverageFrom) { |
|
newOptions.collectCoverageFrom = []; |
|
} |
|
|
|
if (!newOptions.findRelatedTests) { |
|
newOptions.findRelatedTests = false; |
|
} |
|
|
|
if (!newOptions.projects) { |
|
newOptions.projects = []; |
|
} |
|
|
|
if (!newOptions.extraGlobals) { |
|
newOptions.extraGlobals = []; |
|
} |
|
|
|
if (!newOptions.forceExit) { |
|
newOptions.forceExit = false; |
|
} |
|
|
|
if (!newOptions.logHeapUsage) { |
|
newOptions.logHeapUsage = false; |
|
} |
|
|
|
return { |
|
hasDeprecationWarnings, |
|
options: newOptions |
|
}; |
|
}
|
|
|