diff --git a/.gitignore b/.gitignore index 4e7db024f4c0..a28f8c19f009 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ /node_modules -/packages/*/build/ /packages/*/coverage/ /packages/*/node_modules/ diff --git a/package.json b/package.json index 1049d209d7be..418bb1a5e981 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,11 @@ "name": "@jest/monorepo", "private": true, "version": "0.0.0", + "files": [ + "packages/babel-preset-jest/index.js", + "**/bin", + "**/build" + ], "devDependencies": { "@babel/core": "^7.3.4", "@babel/plugin-proposal-class-properties": "^7.3.4", @@ -154,5 +159,8 @@ "jest": "workspace:*", "jest-environment-node": "workspace:*", "react-native": "patch:react-native@0.63.2#./patches/react-native.patch" + }, + "dependencies": { + "module-alias": "^2.2.2" } } diff --git a/packages/babel-jest/build/index.d.ts b/packages/babel-jest/build/index.d.ts new file mode 100644 index 000000000000..50b19e3defcd --- /dev/null +++ b/packages/babel-jest/build/index.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import { TransformOptions } from '@babel/core'; +import type { Transformer } from '@jest/transform'; +declare const transformer: Transformer; +export = transformer; diff --git a/packages/babel-jest/build/index.js b/packages/babel-jest/build/index.js new file mode 100644 index 000000000000..7aa7cc9a7989 --- /dev/null +++ b/packages/babel-jest/build/index.js @@ -0,0 +1,292 @@ +'use strict'; + +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 _core() { + const data = require('@babel/core'); + + _core = function () { + return data; + }; + + return data; +} + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _slash() { + const data = _interopRequireDefault(require('slash')); + + _slash = function () { + return data; + }; + + return data; +} + +var _loadBabelConfig = require('./loadBabelConfig'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 THIS_FILE = fs().readFileSync(__filename); + +const jestPresetPath = require.resolve('babel-preset-jest'); + +const babelIstanbulPlugin = require.resolve('babel-plugin-istanbul'); + +const createTransformer = userOptions => { + var _inputOptions$plugins, _inputOptions$presets; + + const inputOptions = + userOptions !== null && userOptions !== void 0 ? userOptions : {}; + const options = { + ...inputOptions, + caller: { + name: 'babel-jest', + supportsDynamicImport: false, + supportsExportNamespaceFrom: false, + supportsStaticESM: false, + supportsTopLevelAwait: false, + ...inputOptions.caller + }, + compact: false, + plugins: + (_inputOptions$plugins = inputOptions.plugins) !== null && + _inputOptions$plugins !== void 0 + ? _inputOptions$plugins + : [], + presets: ((_inputOptions$presets = inputOptions.presets) !== null && + _inputOptions$presets !== void 0 + ? _inputOptions$presets + : [] + ).concat(jestPresetPath), + sourceMaps: 'both' + }; + + function loadBabelConfig(cwd, filename, transformOptions) { + var _transformOptions$sup, + _transformOptions$sup2, + _transformOptions$sup3, + _transformOptions$sup4; + + // `cwd` first to allow incoming options to override it + const babelConfig = (0, _loadBabelConfig.loadPartialConfig)({ + cwd, + ...options, + caller: { + ...options.caller, + supportsDynamicImport: + (_transformOptions$sup = transformOptions.supportsDynamicImport) !== + null && _transformOptions$sup !== void 0 + ? _transformOptions$sup + : options.caller.supportsDynamicImport, + supportsExportNamespaceFrom: + (_transformOptions$sup2 = + transformOptions.supportsExportNamespaceFrom) !== null && + _transformOptions$sup2 !== void 0 + ? _transformOptions$sup2 + : options.caller.supportsExportNamespaceFrom, + supportsStaticESM: + (_transformOptions$sup3 = transformOptions.supportsStaticESM) !== + null && _transformOptions$sup3 !== void 0 + ? _transformOptions$sup3 + : options.caller.supportsStaticESM, + supportsTopLevelAwait: + (_transformOptions$sup4 = transformOptions.supportsTopLevelAwait) !== + null && _transformOptions$sup4 !== void 0 + ? _transformOptions$sup4 + : options.caller.supportsTopLevelAwait + }, + filename + }); + + if (!babelConfig) { + throw new Error( + `babel-jest: Babel ignores ${_chalk().default.bold( + (0, _slash().default)(path().relative(cwd, filename)) + )} - make sure to include the file in Jest's ${_chalk().default.bold( + 'transformIgnorePatterns' + )} as well.` + ); + } + + return babelConfig; + } + + return { + canInstrument: true, + + getCacheKey(sourceText, sourcePath, transformOptions) { + const {config, configString, instrument} = transformOptions; + const babelOptions = loadBabelConfig( + config.cwd, + sourcePath, + transformOptions + ); + const configPath = [ + babelOptions.config || '', + babelOptions.babelrc || '' + ]; + return (0, _crypto().createHash)('md5') + .update(THIS_FILE) + .update('\0', 'utf8') + .update(JSON.stringify(babelOptions.options)) + .update('\0', 'utf8') + .update(sourceText) + .update('\0', 'utf8') + .update(path().relative(config.rootDir, sourcePath)) + .update('\0', 'utf8') + .update(configString) + .update('\0', 'utf8') + .update(configPath.join('')) + .update('\0', 'utf8') + .update(instrument ? 'instrument' : '') + .update('\0', 'utf8') + .update(process.env.NODE_ENV || '') + .update('\0', 'utf8') + .update(process.env.BABEL_ENV || '') + .digest('hex'); + }, + + process(sourceText, sourcePath, transformOptions) { + const babelOptions = { + ...loadBabelConfig( + transformOptions.config.cwd, + sourcePath, + transformOptions + ).options + }; + + if ( + transformOptions !== null && + transformOptions !== void 0 && + transformOptions.instrument + ) { + babelOptions.auxiliaryCommentBefore = ' istanbul ignore next '; // Copied from jest-runtime transform.js + + babelOptions.plugins = (babelOptions.plugins || []).concat([ + [ + babelIstanbulPlugin, + { + // files outside `cwd` will not be instrumented + cwd: transformOptions.config.rootDir, + exclude: [] + } + ] + ]); + } + + const transformResult = (0, _core().transformSync)( + sourceText, + babelOptions + ); + + if (transformResult) { + const {code, map} = transformResult; + + if (typeof code === 'string') { + return { + code, + map + }; + } + } + + return sourceText; + } + }; +}; + +const transformer = { + ...createTransformer(), + // Assigned here so only the exported transformer has `createTransformer`, + // instead of all created transformers by the function + createTransformer +}; +module.exports = transformer; diff --git a/packages/babel-jest/build/loadBabelConfig.d.ts b/packages/babel-jest/build/loadBabelConfig.d.ts new file mode 100644 index 000000000000..7e667b29ded6 --- /dev/null +++ b/packages/babel-jest/build/loadBabelConfig.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export { loadPartialConfig } from '@babel/core'; diff --git a/packages/babel-jest/build/loadBabelConfig.js b/packages/babel-jest/build/loadBabelConfig.js new file mode 100644 index 000000000000..f91dea5dbbd0 --- /dev/null +++ b/packages/babel-jest/build/loadBabelConfig.js @@ -0,0 +1,21 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'loadPartialConfig', { + enumerable: true, + get: function () { + return _core().loadPartialConfig; + } +}); + +function _core() { + const data = require('@babel/core'); + + _core = function () { + return data; + }; + + return data; +} diff --git a/packages/babel-plugin-jest-hoist/build/index.d.ts b/packages/babel-plugin-jest-hoist/build/index.d.ts new file mode 100644 index 000000000000..00614182c3b2 --- /dev/null +++ b/packages/babel-plugin-jest-hoist/build/index.d.ts @@ -0,0 +1,14 @@ +/** + * 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. + * + */ +import type { PluginObj } from '@babel/core'; +import { Identifier } from '@babel/types'; +declare const _default: () => PluginObj<{ + declareJestObjGetterIdentifier: () => Identifier; + jestObjGetterIdentifier?: Identifier; +}>; +export default _default; diff --git a/packages/babel-plugin-jest-hoist/build/index.js b/packages/babel-plugin-jest-hoist/build/index.js new file mode 100644 index 000000000000..ac96ce5eb148 --- /dev/null +++ b/packages/babel-plugin-jest-hoist/build/index.js @@ -0,0 +1,391 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _template() { + const data = require('@babel/template'); + + _template = function () { + return data; + }; + + return data; +} + +function _types() { + const data = require('@babel/types'); + + _types = function () { + return data; + }; + + return data; +} + +/** + * 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 JEST_GLOBAL_NAME = 'jest'; +const JEST_GLOBALS_MODULE_NAME = '@jest/globals'; +const JEST_GLOBALS_MODULE_JEST_EXPORT_NAME = 'jest'; +const hoistedVariables = new WeakSet(); // We allow `jest`, `expect`, `require`, all default Node.js globals and all +// ES2015 built-ins to be used inside of a `jest.mock` factory. +// We also allow variables prefixed with `mock` as an escape-hatch. + +const ALLOWED_IDENTIFIERS = new Set( + [ + 'Array', + 'ArrayBuffer', + 'Boolean', + 'BigInt', + 'DataView', + 'Date', + 'Error', + 'EvalError', + 'Float32Array', + 'Float64Array', + 'Function', + 'Generator', + 'GeneratorFunction', + 'Infinity', + 'Int16Array', + 'Int32Array', + 'Int8Array', + 'InternalError', + 'Intl', + 'JSON', + 'Map', + 'Math', + 'NaN', + 'Number', + 'Object', + 'Promise', + 'Proxy', + 'RangeError', + 'ReferenceError', + 'Reflect', + 'RegExp', + 'Set', + 'String', + 'Symbol', + 'SyntaxError', + 'TypeError', + 'URIError', + 'Uint16Array', + 'Uint32Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'WeakMap', + 'WeakSet', + 'arguments', + 'console', + 'expect', + 'isNaN', + 'jest', + 'parseFloat', + 'parseInt', + 'exports', + 'require', + 'module', + '__filename', + '__dirname', + 'undefined', + ...Object.getOwnPropertyNames(global) + ].sort() +); +const IDVisitor = { + ReferencedIdentifier(path, {ids}) { + ids.add(path); + }, + + blacklist: ['TypeAnnotation', 'TSTypeAnnotation', 'TSTypeReference'] +}; +const FUNCTIONS = Object.create(null); + +FUNCTIONS.mock = args => { + if (args.length === 1) { + return args[0].isStringLiteral() || args[0].isLiteral(); + } else if (args.length === 2 || args.length === 3) { + const moduleFactory = args[1]; + + if (!moduleFactory.isFunction()) { + throw moduleFactory.buildCodeFrameError( + 'The second argument of `jest.mock` must be an inline function.\n', + TypeError + ); + } + + const ids = new Set(); + const parentScope = moduleFactory.parentPath.scope; // @ts-expect-error: ReferencedIdentifier and blacklist are not known on visitors + + moduleFactory.traverse(IDVisitor, { + ids + }); + + for (const id of ids) { + const {name} = id.node; + let found = false; + let scope = id.scope; + + while (scope !== parentScope) { + if (scope.bindings[name]) { + found = true; + break; + } + + scope = scope.parent; + } + + if (!found) { + let isAllowedIdentifier = + (scope.hasGlobal(name) && ALLOWED_IDENTIFIERS.has(name)) || + /^mock/i.test(name) || // Allow istanbul's coverage variable to pass. + /^(?:__)?cov/.test(name); + + if (!isAllowedIdentifier) { + const binding = scope.bindings[name]; + + if ( + binding !== null && + binding !== void 0 && + binding.path.isVariableDeclarator() + ) { + const {node} = binding.path; + const initNode = node.init; + + if (initNode && binding.constant && scope.isPure(initNode, true)) { + hoistedVariables.add(node); + isAllowedIdentifier = true; + } + } + } + + if (!isAllowedIdentifier) { + throw id.buildCodeFrameError( + 'The module factory of `jest.mock()` is not allowed to ' + + 'reference any out-of-scope variables.\n' + + 'Invalid variable access: ' + + name + + '\n' + + 'Allowed objects: ' + + Array.from(ALLOWED_IDENTIFIERS).join(', ') + + '.\n' + + 'Note: This is a precaution to guard against uninitialized mock ' + + 'variables. If it is ensured that the mock is required lazily, ' + + 'variable names prefixed with `mock` (case insensitive) are permitted.\n', + ReferenceError + ); + } + } + } + + return true; + } + + return false; +}; + +FUNCTIONS.unmock = args => args.length === 1 && args[0].isStringLiteral(); + +FUNCTIONS.deepUnmock = args => args.length === 1 && args[0].isStringLiteral(); + +FUNCTIONS.disableAutomock = FUNCTIONS.enableAutomock = args => + args.length === 0; + +const createJestObjectGetter = (0, _template().statement)` +function GETTER_NAME() { + const { JEST_GLOBALS_MODULE_JEST_EXPORT_NAME } = require("JEST_GLOBALS_MODULE_NAME"); + GETTER_NAME = () => JEST_GLOBALS_MODULE_JEST_EXPORT_NAME; + return JEST_GLOBALS_MODULE_JEST_EXPORT_NAME; +} +`; + +const isJestObject = expression => { + // global + if ( + expression.isIdentifier() && + expression.node.name === JEST_GLOBAL_NAME && + !expression.scope.hasBinding(JEST_GLOBAL_NAME) + ) { + return true; + } // import { jest } from '@jest/globals' + + if ( + expression.referencesImport( + JEST_GLOBALS_MODULE_NAME, + JEST_GLOBALS_MODULE_JEST_EXPORT_NAME + ) + ) { + return true; + } // import * as JestGlobals from '@jest/globals' + + if ( + expression.isMemberExpression() && + !expression.node.computed && + expression.get('object').referencesImport(JEST_GLOBALS_MODULE_NAME, '*') && + expression.node.property.type === 'Identifier' && + expression.node.property.name === JEST_GLOBALS_MODULE_JEST_EXPORT_NAME + ) { + return true; + } + + return false; +}; + +const extractJestObjExprIfHoistable = expr => { + var _FUNCTIONS$propertyNa; + + if (!expr.isCallExpression()) { + return null; + } + + const callee = expr.get('callee'); + const args = expr.get('arguments'); + + if (!callee.isMemberExpression() || callee.node.computed) { + return null; + } + + const object = callee.get('object'); + const property = callee.get('property'); + const propertyName = property.node.name; + const jestObjExpr = isJestObject(object) + ? object // The Jest object could be returned from another call since the functions are all chainable. + : extractJestObjExprIfHoistable(object); + + if (!jestObjExpr) { + return null; + } // Important: Call the function check last + // It might throw an error to display to the user, + // which should only happen if we're already sure it's a call on the Jest object. + + const functionLooksHoistable = + (_FUNCTIONS$propertyNa = FUNCTIONS[propertyName]) === null || + _FUNCTIONS$propertyNa === void 0 + ? void 0 + : _FUNCTIONS$propertyNa.call(FUNCTIONS, args); + return functionLooksHoistable ? jestObjExpr : null; +}; +/* eslint-disable sort-keys */ + +var _default = () => ({ + pre({path: program}) { + this.declareJestObjGetterIdentifier = () => { + if (this.jestObjGetterIdentifier) { + return this.jestObjGetterIdentifier; + } + + this.jestObjGetterIdentifier = program.scope.generateUidIdentifier( + 'getJestObj' + ); + program.unshiftContainer('body', [ + createJestObjectGetter({ + GETTER_NAME: this.jestObjGetterIdentifier.name, + JEST_GLOBALS_MODULE_JEST_EXPORT_NAME, + JEST_GLOBALS_MODULE_NAME + }) + ]); + return this.jestObjGetterIdentifier; + }; + }, + + visitor: { + ExpressionStatement(exprStmt) { + const jestObjExpr = extractJestObjExprIfHoistable( + exprStmt.get('expression') + ); + + if (jestObjExpr) { + jestObjExpr.replaceWith( + (0, _types().callExpression)( + this.declareJestObjGetterIdentifier(), + [] + ) + ); + } + } + }, + + // in `post` to make sure we come after an import transform and can unshift above the `require`s + post({path: program}) { + const self = this; + visitBlock(program); + program.traverse({ + BlockStatement: visitBlock + }); + + function visitBlock(block) { + // use a temporary empty statement instead of the real first statement, which may itself be hoisted + const [varsHoistPoint, callsHoistPoint] = block.unshiftContainer('body', [ + (0, _types().emptyStatement)(), + (0, _types().emptyStatement)() + ]); + block.traverse({ + CallExpression: visitCallExpr, + VariableDeclarator: visitVariableDeclarator, + // do not traverse into nested blocks, or we'll hoist calls in there out to this block + // @ts-expect-error blacklist is not known + blacklist: ['BlockStatement'] + }); + callsHoistPoint.remove(); + varsHoistPoint.remove(); + + function visitCallExpr(callExpr) { + var _self$jestObjGetterId; + + const { + node: {callee} + } = callExpr; + + if ( + (0, _types().isIdentifier)(callee) && + callee.name === + ((_self$jestObjGetterId = self.jestObjGetterIdentifier) === null || + _self$jestObjGetterId === void 0 + ? void 0 + : _self$jestObjGetterId.name) + ) { + const mockStmt = callExpr.getStatementParent(); + + if (mockStmt) { + const mockStmtParent = mockStmt.parentPath; + + if (mockStmtParent.isBlock()) { + const mockStmtNode = mockStmt.node; + mockStmt.remove(); + callsHoistPoint.insertBefore(mockStmtNode); + } + } + } + } + + function visitVariableDeclarator(varDecl) { + if (hoistedVariables.has(varDecl.node)) { + // should be assert function, but it's not. So let's cast below + varDecl.parentPath.assertVariableDeclaration(); + const {kind, declarations} = varDecl.parent; + + if (declarations.length === 1) { + varDecl.parentPath.remove(); + } else { + varDecl.remove(); + } + + varsHoistPoint.insertBefore( + (0, _types().variableDeclaration)(kind, [varDecl.node]) + ); + } + } + } + } +}); +/* eslint-enable */ + +exports.default = _default; diff --git a/packages/diff-sequences/build/index.d.ts b/packages/diff-sequences/build/index.d.ts new file mode 100644 index 000000000000..6ed490e78b0e --- /dev/null +++ b/packages/diff-sequences/build/index.d.ts @@ -0,0 +1,18 @@ +/** + * 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. + * + */ +declare type IsCommon = (aIndex: number, // caller can assume: 0 <= aIndex && aIndex < aLength +bIndex: number) => boolean; +declare type FoundSubsequence = (nCommon: number, // caller can assume: 0 < nCommon +aCommon: number, // caller can assume: 0 <= aCommon && aCommon < aLength +bCommon: number) => void; +export declare type Callbacks = { + foundSubsequence: FoundSubsequence; + isCommon: IsCommon; +}; +declare const _default: (aLength: number, bLength: number, isCommon: IsCommon, foundSubsequence: FoundSubsequence) => void; +export default _default; diff --git a/packages/diff-sequences/build/index.js b/packages/diff-sequences/build/index.js new file mode 100644 index 000000000000..5d565d9d5d63 --- /dev/null +++ b/packages/diff-sequences/build/index.js @@ -0,0 +1,802 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +/** + * 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. + * + */ +// This diff-sequences package implements the linear space variation in +// An O(ND) Difference Algorithm and Its Variations by Eugene W. Myers +// Relationship in notation between Myers paper and this package: +// A is a +// N is aLength, aEnd - aStart, and so on +// x is aIndex, aFirst, aLast, and so on +// B is b +// M is bLength, bEnd - bStart, and so on +// y is bIndex, bFirst, bLast, and so on +// Δ = N - M is negative of baDeltaLength = bLength - aLength +// D is d +// k is kF +// k + Δ is kF = kR - baDeltaLength +// V is aIndexesF or aIndexesR (see comment below about Indexes type) +// index intervals [1, N] and [1, M] are [0, aLength) and [0, bLength) +// starting point in forward direction (0, 0) is (-1, -1) +// starting point in reverse direction (N + 1, M + 1) is (aLength, bLength) +// The “edit graph” for sequences a and b corresponds to items: +// in a on the horizontal axis +// in b on the vertical axis +// +// Given a-coordinate of a point in a diagonal, you can compute b-coordinate. +// +// Forward diagonals kF: +// zero diagonal intersects top left corner +// positive diagonals intersect top edge +// negative diagonals insersect left edge +// +// Reverse diagonals kR: +// zero diagonal intersects bottom right corner +// positive diagonals intersect right edge +// negative diagonals intersect bottom edge +// The graph contains a directed acyclic graph of edges: +// horizontal: delete an item from a +// vertical: insert an item from b +// diagonal: common item in a and b +// +// The algorithm solves dual problems in the graph analogy: +// Find longest common subsequence: path with maximum number of diagonal edges +// Find shortest edit script: path with minimum number of non-diagonal edges +// Input callback function compares items at indexes in the sequences. +// Output callback function receives the number of adjacent items +// and starting indexes of each common subsequence. +// Either original functions or wrapped to swap indexes if graph is transposed. +// Indexes in sequence a of last point of forward or reverse paths in graph. +// Myers algorithm indexes by diagonal k which for negative is bad deopt in V8. +// This package indexes by iF and iR which are greater than or equal to zero. +// and also updates the index arrays in place to cut memory in half. +// kF = 2 * iF - d +// kR = d - 2 * iR +// Division of index intervals in sequences a and b at the middle change. +// Invariant: intervals do not have common items at the start or end. +const pkg = 'diff-sequences'; // for error messages + +const NOT_YET_SET = 0; // small int instead of undefined to avoid deopt in V8 +// Return the number of common items that follow in forward direction. +// The length of what Myers paper calls a “snake” in a forward path. + +const countCommonItemsF = (aIndex, aEnd, bIndex, bEnd, isCommon) => { + let nCommon = 0; + + while (aIndex < aEnd && bIndex < bEnd && isCommon(aIndex, bIndex)) { + aIndex += 1; + bIndex += 1; + nCommon += 1; + } + + return nCommon; +}; // Return the number of common items that precede in reverse direction. +// The length of what Myers paper calls a “snake” in a reverse path. + +const countCommonItemsR = (aStart, aIndex, bStart, bIndex, isCommon) => { + let nCommon = 0; + + while (aStart <= aIndex && bStart <= bIndex && isCommon(aIndex, bIndex)) { + aIndex -= 1; + bIndex -= 1; + nCommon += 1; + } + + return nCommon; +}; // A simple function to extend forward paths from (d - 1) to d changes +// when forward and reverse paths cannot yet overlap. + +const extendPathsF = (d, aEnd, bEnd, bF, isCommon, aIndexesF, iMaxF) => { + // Unroll the first iteration. + let iF = 0; + let kF = -d; // kF = 2 * iF - d + + let aFirst = aIndexesF[iF]; // in first iteration always insert + + let aIndexPrev1 = aFirst; // prev value of [iF - 1] in next iteration + + aIndexesF[iF] += countCommonItemsF( + aFirst + 1, + aEnd, + bF + aFirst - kF + 1, + bEnd, + isCommon + ); // Optimization: skip diagonals in which paths cannot ever overlap. + + const nF = d < iMaxF ? d : iMaxF; // The diagonals kF are odd when d is odd and even when d is even. + + for (iF += 1, kF += 2; iF <= nF; iF += 1, kF += 2) { + // To get first point of path segment, move one change in forward direction + // from last point of previous path segment in an adjacent diagonal. + // In last possible iteration when iF === d and kF === d always delete. + if (iF !== d && aIndexPrev1 < aIndexesF[iF]) { + aFirst = aIndexesF[iF]; // vertical to insert from b + } else { + aFirst = aIndexPrev1 + 1; // horizontal to delete from a + + if (aEnd <= aFirst) { + // Optimization: delete moved past right of graph. + return iF - 1; + } + } // To get last point of path segment, move along diagonal of common items. + + aIndexPrev1 = aIndexesF[iF]; + aIndexesF[iF] = + aFirst + + countCommonItemsF(aFirst + 1, aEnd, bF + aFirst - kF + 1, bEnd, isCommon); + } + + return iMaxF; +}; // A simple function to extend reverse paths from (d - 1) to d changes +// when reverse and forward paths cannot yet overlap. + +const extendPathsR = (d, aStart, bStart, bR, isCommon, aIndexesR, iMaxR) => { + // Unroll the first iteration. + let iR = 0; + let kR = d; // kR = d - 2 * iR + + let aFirst = aIndexesR[iR]; // in first iteration always insert + + let aIndexPrev1 = aFirst; // prev value of [iR - 1] in next iteration + + aIndexesR[iR] -= countCommonItemsR( + aStart, + aFirst - 1, + bStart, + bR + aFirst - kR - 1, + isCommon + ); // Optimization: skip diagonals in which paths cannot ever overlap. + + const nR = d < iMaxR ? d : iMaxR; // The diagonals kR are odd when d is odd and even when d is even. + + for (iR += 1, kR -= 2; iR <= nR; iR += 1, kR -= 2) { + // To get first point of path segment, move one change in reverse direction + // from last point of previous path segment in an adjacent diagonal. + // In last possible iteration when iR === d and kR === -d always delete. + if (iR !== d && aIndexesR[iR] < aIndexPrev1) { + aFirst = aIndexesR[iR]; // vertical to insert from b + } else { + aFirst = aIndexPrev1 - 1; // horizontal to delete from a + + if (aFirst < aStart) { + // Optimization: delete moved past left of graph. + return iR - 1; + } + } // To get last point of path segment, move along diagonal of common items. + + aIndexPrev1 = aIndexesR[iR]; + aIndexesR[iR] = + aFirst - + countCommonItemsR( + aStart, + aFirst - 1, + bStart, + bR + aFirst - kR - 1, + isCommon + ); + } + + return iMaxR; +}; // A complete function to extend forward paths from (d - 1) to d changes. +// Return true if a path overlaps reverse path of (d - 1) changes in its diagonal. + +const extendOverlappablePathsF = ( + d, + aStart, + aEnd, + bStart, + bEnd, + isCommon, + aIndexesF, + iMaxF, + aIndexesR, + iMaxR, + division +) => { + const bF = bStart - aStart; // bIndex = bF + aIndex - kF + + const aLength = aEnd - aStart; + const bLength = bEnd - bStart; + const baDeltaLength = bLength - aLength; // kF = kR - baDeltaLength + // Range of diagonals in which forward and reverse paths might overlap. + + const kMinOverlapF = -baDeltaLength - (d - 1); // -(d - 1) <= kR + + const kMaxOverlapF = -baDeltaLength + (d - 1); // kR <= (d - 1) + + let aIndexPrev1 = NOT_YET_SET; // prev value of [iF - 1] in next iteration + // Optimization: skip diagonals in which paths cannot ever overlap. + + const nF = d < iMaxF ? d : iMaxF; // The diagonals kF = 2 * iF - d are odd when d is odd and even when d is even. + + for (let iF = 0, kF = -d; iF <= nF; iF += 1, kF += 2) { + // To get first point of path segment, move one change in forward direction + // from last point of previous path segment in an adjacent diagonal. + // In first iteration when iF === 0 and kF === -d always insert. + // In last possible iteration when iF === d and kF === d always delete. + const insert = iF === 0 || (iF !== d && aIndexPrev1 < aIndexesF[iF]); + const aLastPrev = insert ? aIndexesF[iF] : aIndexPrev1; + const aFirst = insert + ? aLastPrev // vertical to insert from b + : aLastPrev + 1; // horizontal to delete from a + // To get last point of path segment, move along diagonal of common items. + + const bFirst = bF + aFirst - kF; + const nCommonF = countCommonItemsF( + aFirst + 1, + aEnd, + bFirst + 1, + bEnd, + isCommon + ); + const aLast = aFirst + nCommonF; + aIndexPrev1 = aIndexesF[iF]; + aIndexesF[iF] = aLast; + + if (kMinOverlapF <= kF && kF <= kMaxOverlapF) { + // Solve for iR of reverse path with (d - 1) changes in diagonal kF: + // kR = kF + baDeltaLength + // kR = (d - 1) - 2 * iR + const iR = (d - 1 - (kF + baDeltaLength)) / 2; // If this forward path overlaps the reverse path in this diagonal, + // then this is the middle change of the index intervals. + + if (iR <= iMaxR && aIndexesR[iR] - 1 <= aLast) { + // Unlike the Myers algorithm which finds only the middle “snake” + // this package can find two common subsequences per division. + // Last point of previous path segment is on an adjacent diagonal. + const bLastPrev = bF + aLastPrev - (insert ? kF + 1 : kF - 1); // Because of invariant that intervals preceding the middle change + // cannot have common items at the end, + // move in reverse direction along a diagonal of common items. + + const nCommonR = countCommonItemsR( + aStart, + aLastPrev, + bStart, + bLastPrev, + isCommon + ); + const aIndexPrevFirst = aLastPrev - nCommonR; + const bIndexPrevFirst = bLastPrev - nCommonR; + const aEndPreceding = aIndexPrevFirst + 1; + const bEndPreceding = bIndexPrevFirst + 1; + division.nChangePreceding = d - 1; + + if (d - 1 === aEndPreceding + bEndPreceding - aStart - bStart) { + // Optimization: number of preceding changes in forward direction + // is equal to number of items in preceding interval, + // therefore it cannot contain any common items. + division.aEndPreceding = aStart; + division.bEndPreceding = bStart; + } else { + division.aEndPreceding = aEndPreceding; + division.bEndPreceding = bEndPreceding; + } + + division.nCommonPreceding = nCommonR; + + if (nCommonR !== 0) { + division.aCommonPreceding = aEndPreceding; + division.bCommonPreceding = bEndPreceding; + } + + division.nCommonFollowing = nCommonF; + + if (nCommonF !== 0) { + division.aCommonFollowing = aFirst + 1; + division.bCommonFollowing = bFirst + 1; + } + + const aStartFollowing = aLast + 1; + const bStartFollowing = bFirst + nCommonF + 1; + division.nChangeFollowing = d - 1; + + if (d - 1 === aEnd + bEnd - aStartFollowing - bStartFollowing) { + // Optimization: number of changes in reverse direction + // is equal to number of items in following interval, + // therefore it cannot contain any common items. + division.aStartFollowing = aEnd; + division.bStartFollowing = bEnd; + } else { + division.aStartFollowing = aStartFollowing; + division.bStartFollowing = bStartFollowing; + } + + return true; + } + } + } + + return false; +}; // A complete function to extend reverse paths from (d - 1) to d changes. +// Return true if a path overlaps forward path of d changes in its diagonal. + +const extendOverlappablePathsR = ( + d, + aStart, + aEnd, + bStart, + bEnd, + isCommon, + aIndexesF, + iMaxF, + aIndexesR, + iMaxR, + division +) => { + const bR = bEnd - aEnd; // bIndex = bR + aIndex - kR + + const aLength = aEnd - aStart; + const bLength = bEnd - bStart; + const baDeltaLength = bLength - aLength; // kR = kF + baDeltaLength + // Range of diagonals in which forward and reverse paths might overlap. + + const kMinOverlapR = baDeltaLength - d; // -d <= kF + + const kMaxOverlapR = baDeltaLength + d; // kF <= d + + let aIndexPrev1 = NOT_YET_SET; // prev value of [iR - 1] in next iteration + // Optimization: skip diagonals in which paths cannot ever overlap. + + const nR = d < iMaxR ? d : iMaxR; // The diagonals kR = d - 2 * iR are odd when d is odd and even when d is even. + + for (let iR = 0, kR = d; iR <= nR; iR += 1, kR -= 2) { + // To get first point of path segment, move one change in reverse direction + // from last point of previous path segment in an adjacent diagonal. + // In first iteration when iR === 0 and kR === d always insert. + // In last possible iteration when iR === d and kR === -d always delete. + const insert = iR === 0 || (iR !== d && aIndexesR[iR] < aIndexPrev1); + const aLastPrev = insert ? aIndexesR[iR] : aIndexPrev1; + const aFirst = insert + ? aLastPrev // vertical to insert from b + : aLastPrev - 1; // horizontal to delete from a + // To get last point of path segment, move along diagonal of common items. + + const bFirst = bR + aFirst - kR; + const nCommonR = countCommonItemsR( + aStart, + aFirst - 1, + bStart, + bFirst - 1, + isCommon + ); + const aLast = aFirst - nCommonR; + aIndexPrev1 = aIndexesR[iR]; + aIndexesR[iR] = aLast; + + if (kMinOverlapR <= kR && kR <= kMaxOverlapR) { + // Solve for iF of forward path with d changes in diagonal kR: + // kF = kR - baDeltaLength + // kF = 2 * iF - d + const iF = (d + (kR - baDeltaLength)) / 2; // If this reverse path overlaps the forward path in this diagonal, + // then this is a middle change of the index intervals. + + if (iF <= iMaxF && aLast - 1 <= aIndexesF[iF]) { + const bLast = bFirst - nCommonR; + division.nChangePreceding = d; + + if (d === aLast + bLast - aStart - bStart) { + // Optimization: number of changes in reverse direction + // is equal to number of items in preceding interval, + // therefore it cannot contain any common items. + division.aEndPreceding = aStart; + division.bEndPreceding = bStart; + } else { + division.aEndPreceding = aLast; + division.bEndPreceding = bLast; + } + + division.nCommonPreceding = nCommonR; + + if (nCommonR !== 0) { + // The last point of reverse path segment is start of common subsequence. + division.aCommonPreceding = aLast; + division.bCommonPreceding = bLast; + } + + division.nChangeFollowing = d - 1; + + if (d === 1) { + // There is no previous path segment. + division.nCommonFollowing = 0; + division.aStartFollowing = aEnd; + division.bStartFollowing = bEnd; + } else { + // Unlike the Myers algorithm which finds only the middle “snake” + // this package can find two common subsequences per division. + // Last point of previous path segment is on an adjacent diagonal. + const bLastPrev = bR + aLastPrev - (insert ? kR - 1 : kR + 1); // Because of invariant that intervals following the middle change + // cannot have common items at the start, + // move in forward direction along a diagonal of common items. + + const nCommonF = countCommonItemsF( + aLastPrev, + aEnd, + bLastPrev, + bEnd, + isCommon + ); + division.nCommonFollowing = nCommonF; + + if (nCommonF !== 0) { + // The last point of reverse path segment is start of common subsequence. + division.aCommonFollowing = aLastPrev; + division.bCommonFollowing = bLastPrev; + } + + const aStartFollowing = aLastPrev + nCommonF; // aFirstPrev + + const bStartFollowing = bLastPrev + nCommonF; // bFirstPrev + + if (d - 1 === aEnd + bEnd - aStartFollowing - bStartFollowing) { + // Optimization: number of changes in forward direction + // is equal to number of items in following interval, + // therefore it cannot contain any common items. + division.aStartFollowing = aEnd; + division.bStartFollowing = bEnd; + } else { + division.aStartFollowing = aStartFollowing; + division.bStartFollowing = bStartFollowing; + } + } + + return true; + } + } + } + + return false; +}; // Given index intervals and input function to compare items at indexes, +// divide at the middle change. +// +// DO NOT CALL if start === end, because interval cannot contain common items +// and because this function will throw the “no overlap” error. + +const divide = ( + nChange, + aStart, + aEnd, + bStart, + bEnd, + isCommon, + aIndexesF, + aIndexesR, + division // output +) => { + const bF = bStart - aStart; // bIndex = bF + aIndex - kF + + const bR = bEnd - aEnd; // bIndex = bR + aIndex - kR + + const aLength = aEnd - aStart; + const bLength = bEnd - bStart; // Because graph has square or portrait orientation, + // length difference is minimum number of items to insert from b. + // Corresponding forward and reverse diagonals in graph + // depend on length difference of the sequences: + // kF = kR - baDeltaLength + // kR = kF + baDeltaLength + + const baDeltaLength = bLength - aLength; // Optimization: max diagonal in graph intersects corner of shorter side. + + let iMaxF = aLength; + let iMaxR = aLength; // Initialize no changes yet in forward or reverse direction: + + aIndexesF[0] = aStart - 1; // at open start of interval, outside closed start + + aIndexesR[0] = aEnd; // at open end of interval + + if (baDeltaLength % 2 === 0) { + // The number of changes in paths is 2 * d if length difference is even. + const dMin = (nChange || baDeltaLength) / 2; + const dMax = (aLength + bLength) / 2; + + for (let d = 1; d <= dMax; d += 1) { + iMaxF = extendPathsF(d, aEnd, bEnd, bF, isCommon, aIndexesF, iMaxF); + + if (d < dMin) { + iMaxR = extendPathsR(d, aStart, bStart, bR, isCommon, aIndexesR, iMaxR); + } else if ( + // If a reverse path overlaps a forward path in the same diagonal, + // return a division of the index intervals at the middle change. + extendOverlappablePathsR( + d, + aStart, + aEnd, + bStart, + bEnd, + isCommon, + aIndexesF, + iMaxF, + aIndexesR, + iMaxR, + division + ) + ) { + return; + } + } + } else { + // The number of changes in paths is 2 * d - 1 if length difference is odd. + const dMin = ((nChange || baDeltaLength) + 1) / 2; + const dMax = (aLength + bLength + 1) / 2; // Unroll first half iteration so loop extends the relevant pairs of paths. + // Because of invariant that intervals have no common items at start or end, + // and limitation not to call divide with empty intervals, + // therefore it cannot be called if a forward path with one change + // would overlap a reverse path with no changes, even if dMin === 1. + + let d = 1; + iMaxF = extendPathsF(d, aEnd, bEnd, bF, isCommon, aIndexesF, iMaxF); + + for (d += 1; d <= dMax; d += 1) { + iMaxR = extendPathsR( + d - 1, + aStart, + bStart, + bR, + isCommon, + aIndexesR, + iMaxR + ); + + if (d < dMin) { + iMaxF = extendPathsF(d, aEnd, bEnd, bF, isCommon, aIndexesF, iMaxF); + } else if ( + // If a forward path overlaps a reverse path in the same diagonal, + // return a division of the index intervals at the middle change. + extendOverlappablePathsF( + d, + aStart, + aEnd, + bStart, + bEnd, + isCommon, + aIndexesF, + iMaxF, + aIndexesR, + iMaxR, + division + ) + ) { + return; + } + } + } + /* istanbul ignore next */ + + throw new Error( + `${pkg}: no overlap aStart=${aStart} aEnd=${aEnd} bStart=${bStart} bEnd=${bEnd}` + ); +}; // Given index intervals and input function to compare items at indexes, +// return by output function the number of adjacent items and starting indexes +// of each common subsequence. Divide and conquer with only linear space. +// +// The index intervals are half open [start, end) like array slice method. +// DO NOT CALL if start === end, because interval cannot contain common items +// and because divide function will throw the “no overlap” error. + +const findSubsequences = ( + nChange, + aStart, + aEnd, + bStart, + bEnd, + transposed, + callbacks, + aIndexesF, + aIndexesR, + division // temporary memory, not input nor output +) => { + if (bEnd - bStart < aEnd - aStart) { + // Transpose graph so it has portrait instead of landscape orientation. + // Always compare shorter to longer sequence for consistency and optimization. + transposed = !transposed; + + if (transposed && callbacks.length === 1) { + // Lazily wrap callback functions to swap args if graph is transposed. + const {foundSubsequence, isCommon} = callbacks[0]; + callbacks[1] = { + foundSubsequence: (nCommon, bCommon, aCommon) => { + foundSubsequence(nCommon, aCommon, bCommon); + }, + isCommon: (bIndex, aIndex) => isCommon(aIndex, bIndex) + }; + } + + const tStart = aStart; + const tEnd = aEnd; + aStart = bStart; + aEnd = bEnd; + bStart = tStart; + bEnd = tEnd; + } + + const {foundSubsequence, isCommon} = callbacks[transposed ? 1 : 0]; // Divide the index intervals at the middle change. + + divide( + nChange, + aStart, + aEnd, + bStart, + bEnd, + isCommon, + aIndexesF, + aIndexesR, + division + ); + const { + nChangePreceding, + aEndPreceding, + bEndPreceding, + nCommonPreceding, + aCommonPreceding, + bCommonPreceding, + nCommonFollowing, + aCommonFollowing, + bCommonFollowing, + nChangeFollowing, + aStartFollowing, + bStartFollowing + } = division; // Unless either index interval is empty, they might contain common items. + + if (aStart < aEndPreceding && bStart < bEndPreceding) { + // Recursely find and return common subsequences preceding the division. + findSubsequences( + nChangePreceding, + aStart, + aEndPreceding, + bStart, + bEndPreceding, + transposed, + callbacks, + aIndexesF, + aIndexesR, + division + ); + } // Return common subsequences that are adjacent to the middle change. + + if (nCommonPreceding !== 0) { + foundSubsequence(nCommonPreceding, aCommonPreceding, bCommonPreceding); + } + + if (nCommonFollowing !== 0) { + foundSubsequence(nCommonFollowing, aCommonFollowing, bCommonFollowing); + } // Unless either index interval is empty, they might contain common items. + + if (aStartFollowing < aEnd && bStartFollowing < bEnd) { + // Recursely find and return common subsequences following the division. + findSubsequences( + nChangeFollowing, + aStartFollowing, + aEnd, + bStartFollowing, + bEnd, + transposed, + callbacks, + aIndexesF, + aIndexesR, + division + ); + } +}; + +const validateLength = (name, arg) => { + if (typeof arg !== 'number') { + throw new TypeError(`${pkg}: ${name} typeof ${typeof arg} is not a number`); + } + + if (!Number.isSafeInteger(arg)) { + throw new RangeError(`${pkg}: ${name} value ${arg} is not a safe integer`); + } + + if (arg < 0) { + throw new RangeError(`${pkg}: ${name} value ${arg} is a negative integer`); + } +}; + +const validateCallback = (name, arg) => { + const type = typeof arg; + + if (type !== 'function') { + throw new TypeError(`${pkg}: ${name} typeof ${type} is not a function`); + } +}; // Compare items in two sequences to find a longest common subsequence. +// Given lengths of sequences and input function to compare items at indexes, +// return by output function the number of adjacent items and starting indexes +// of each common subsequence. + +var _default = (aLength, bLength, isCommon, foundSubsequence) => { + validateLength('aLength', aLength); + validateLength('bLength', bLength); + validateCallback('isCommon', isCommon); + validateCallback('foundSubsequence', foundSubsequence); // Count common items from the start in the forward direction. + + const nCommonF = countCommonItemsF(0, aLength, 0, bLength, isCommon); + + if (nCommonF !== 0) { + foundSubsequence(nCommonF, 0, 0); + } // Unless both sequences consist of common items only, + // find common items in the half-trimmed index intervals. + + if (aLength !== nCommonF || bLength !== nCommonF) { + // Invariant: intervals do not have common items at the start. + // The start of an index interval is closed like array slice method. + const aStart = nCommonF; + const bStart = nCommonF; // Count common items from the end in the reverse direction. + + const nCommonR = countCommonItemsR( + aStart, + aLength - 1, + bStart, + bLength - 1, + isCommon + ); // Invariant: intervals do not have common items at the end. + // The end of an index interval is open like array slice method. + + const aEnd = aLength - nCommonR; + const bEnd = bLength - nCommonR; // Unless one sequence consists of common items only, + // therefore the other trimmed index interval consists of changes only, + // find common items in the trimmed index intervals. + + const nCommonFR = nCommonF + nCommonR; + + if (aLength !== nCommonFR && bLength !== nCommonFR) { + const nChange = 0; // number of change items is not yet known + + const transposed = false; // call the original unwrapped functions + + const callbacks = [ + { + foundSubsequence, + isCommon + } + ]; // Indexes in sequence a of last points in furthest reaching paths + // from outside the start at top left in the forward direction: + + const aIndexesF = [NOT_YET_SET]; // from the end at bottom right in the reverse direction: + + const aIndexesR = [NOT_YET_SET]; // Initialize one object as output of all calls to divide function. + + const division = { + aCommonFollowing: NOT_YET_SET, + aCommonPreceding: NOT_YET_SET, + aEndPreceding: NOT_YET_SET, + aStartFollowing: NOT_YET_SET, + bCommonFollowing: NOT_YET_SET, + bCommonPreceding: NOT_YET_SET, + bEndPreceding: NOT_YET_SET, + bStartFollowing: NOT_YET_SET, + nChangeFollowing: NOT_YET_SET, + nChangePreceding: NOT_YET_SET, + nCommonFollowing: NOT_YET_SET, + nCommonPreceding: NOT_YET_SET + }; // Find and return common subsequences in the trimmed index intervals. + + findSubsequences( + nChange, + aStart, + aEnd, + bStart, + bEnd, + transposed, + callbacks, + aIndexesF, + aIndexesR, + division + ); + } + + if (nCommonR !== 0) { + foundSubsequence(nCommonR, aEnd, bEnd); + } + } +}; + +exports.default = _default; diff --git a/packages/expect/build/asymmetricMatchers.d.ts b/packages/expect/build/asymmetricMatchers.d.ts new file mode 100644 index 000000000000..e8b9f6814011 --- /dev/null +++ b/packages/expect/build/asymmetricMatchers.d.ts @@ -0,0 +1,60 @@ +/** + * 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. + * + */ +export declare class AsymmetricMatcher { + protected sample: T; + $$typeof: symbol; + inverse?: boolean; + constructor(sample: T); +} +declare class Any extends AsymmetricMatcher { + constructor(sample: unknown); + asymmetricMatch(other: unknown): boolean; + toString(): string; + getExpectedType(): string; + toAsymmetricMatcher(): string; +} +declare class Anything extends AsymmetricMatcher { + asymmetricMatch(other: unknown): boolean; + toString(): string; + toAsymmetricMatcher(): string; +} +declare class ArrayContaining extends AsymmetricMatcher> { + constructor(sample: Array, inverse?: boolean); + asymmetricMatch(other: Array): boolean; + toString(): string; + getExpectedType(): string; +} +declare class ObjectContaining extends AsymmetricMatcher> { + constructor(sample: Record, inverse?: boolean); + asymmetricMatch(other: any): boolean; + toString(): string; + getExpectedType(): string; +} +declare class StringContaining extends AsymmetricMatcher { + constructor(sample: string, inverse?: boolean); + asymmetricMatch(other: string): boolean; + toString(): string; + getExpectedType(): string; +} +declare class StringMatching extends AsymmetricMatcher { + constructor(sample: string | RegExp, inverse?: boolean); + asymmetricMatch(other: string): boolean; + toString(): string; + getExpectedType(): string; +} +export declare const any: (expectedObject: unknown) => Any; +export declare const anything: () => Anything; +export declare const arrayContaining: (sample: Array) => ArrayContaining; +export declare const arrayNotContaining: (sample: Array) => ArrayContaining; +export declare const objectContaining: (sample: Record) => ObjectContaining; +export declare const objectNotContaining: (sample: Record) => ObjectContaining; +export declare const stringContaining: (expected: string) => StringContaining; +export declare const stringNotContaining: (expected: string) => StringContaining; +export declare const stringMatching: (expected: string | RegExp) => StringMatching; +export declare const stringNotMatching: (expected: string | RegExp) => StringMatching; +export {}; diff --git a/packages/expect/build/asymmetricMatchers.js b/packages/expect/build/asymmetricMatchers.js new file mode 100644 index 000000000000..49a326850775 --- /dev/null +++ b/packages/expect/build/asymmetricMatchers.js @@ -0,0 +1,296 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.stringNotMatching = exports.stringMatching = exports.stringNotContaining = exports.stringContaining = exports.objectNotContaining = exports.objectContaining = exports.arrayNotContaining = exports.arrayContaining = exports.anything = exports.any = exports.AsymmetricMatcher = void 0; + +var _jasmineUtils = require('./jasmineUtils'); + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class AsymmetricMatcher { + constructor(sample) { + _defineProperty(this, 'sample', void 0); + + _defineProperty(this, '$$typeof', void 0); + + _defineProperty(this, 'inverse', void 0); + + this.$$typeof = Symbol.for('jest.asymmetricMatcher'); + this.sample = sample; + } +} + +exports.AsymmetricMatcher = AsymmetricMatcher; + +class Any extends AsymmetricMatcher { + constructor(sample) { + if (typeof sample === 'undefined') { + throw new TypeError( + 'any() expects to be passed a constructor function. ' + + 'Please pass one or use anything() to match any object.' + ); + } + + super(sample); + } + + asymmetricMatch(other) { + if (this.sample == String) { + return typeof other == 'string' || other instanceof String; + } + + if (this.sample == Number) { + return typeof other == 'number' || other instanceof Number; + } + + if (this.sample == Function) { + return typeof other == 'function' || other instanceof Function; + } + + if (this.sample == Object) { + return typeof other == 'object'; + } + + if (this.sample == Boolean) { + return typeof other == 'boolean'; + } + /* global BigInt */ + + if (this.sample == BigInt) { + return typeof other == 'bigint'; + } + + if (this.sample == Symbol) { + return typeof other == 'symbol'; + } + + return other instanceof this.sample; + } + + toString() { + return 'Any'; + } + + getExpectedType() { + if (this.sample == String) { + return 'string'; + } + + if (this.sample == Number) { + return 'number'; + } + + if (this.sample == Function) { + return 'function'; + } + + if (this.sample == Object) { + return 'object'; + } + + if (this.sample == Boolean) { + return 'boolean'; + } + + return (0, _jasmineUtils.fnNameFor)(this.sample); + } + + toAsymmetricMatcher() { + return 'Any<' + (0, _jasmineUtils.fnNameFor)(this.sample) + '>'; + } +} + +class Anything extends AsymmetricMatcher { + asymmetricMatch(other) { + return !(0, _jasmineUtils.isUndefined)(other) && other !== null; + } + + toString() { + return 'Anything'; + } // No getExpectedType method, because it matches either null or undefined. + + toAsymmetricMatcher() { + return 'Anything'; + } +} + +class ArrayContaining extends AsymmetricMatcher { + constructor(sample, inverse = false) { + super(sample); + this.inverse = inverse; + } + + asymmetricMatch(other) { + if (!Array.isArray(this.sample)) { + throw new Error( + `You must provide an array to ${this.toString()}, not '` + + typeof this.sample + + "'." + ); + } + + const result = + this.sample.length === 0 || + (Array.isArray(other) && + this.sample.every(item => + other.some(another => (0, _jasmineUtils.equals)(item, another)) + )); + return this.inverse ? !result : result; + } + + toString() { + return `Array${this.inverse ? 'Not' : ''}Containing`; + } + + getExpectedType() { + return 'array'; + } +} + +class ObjectContaining extends AsymmetricMatcher { + constructor(sample, inverse = false) { + super(sample); + this.inverse = inverse; + } + + asymmetricMatch(other) { + if (typeof this.sample !== 'object') { + throw new Error( + `You must provide an object to ${this.toString()}, not '` + + typeof this.sample + + "'." + ); + } + + let result = true; + + for (const property in this.sample) { + if ( + !(0, _jasmineUtils.hasProperty)(other, property) || + !(0, _jasmineUtils.equals)(this.sample[property], other[property]) + ) { + result = false; + break; + } + } + + return this.inverse ? !result : result; + } + + toString() { + return `Object${this.inverse ? 'Not' : ''}Containing`; + } + + getExpectedType() { + return 'object'; + } +} + +class StringContaining extends AsymmetricMatcher { + constructor(sample, inverse = false) { + if (!(0, _jasmineUtils.isA)('String', sample)) { + throw new Error('Expected is not a string'); + } + + super(sample); + this.inverse = inverse; + } + + asymmetricMatch(other) { + const result = + (0, _jasmineUtils.isA)('String', other) && other.includes(this.sample); + return this.inverse ? !result : result; + } + + toString() { + return `String${this.inverse ? 'Not' : ''}Containing`; + } + + getExpectedType() { + return 'string'; + } +} + +class StringMatching extends AsymmetricMatcher { + constructor(sample, inverse = false) { + if ( + !(0, _jasmineUtils.isA)('String', sample) && + !(0, _jasmineUtils.isA)('RegExp', sample) + ) { + throw new Error('Expected is not a String or a RegExp'); + } + + super(new RegExp(sample)); + this.inverse = inverse; + } + + asymmetricMatch(other) { + const result = + (0, _jasmineUtils.isA)('String', other) && this.sample.test(other); + return this.inverse ? !result : result; + } + + toString() { + return `String${this.inverse ? 'Not' : ''}Matching`; + } + + getExpectedType() { + return 'string'; + } +} + +const any = expectedObject => new Any(expectedObject); + +exports.any = any; + +const anything = () => new Anything(); + +exports.anything = anything; + +const arrayContaining = sample => new ArrayContaining(sample); + +exports.arrayContaining = arrayContaining; + +const arrayNotContaining = sample => new ArrayContaining(sample, true); + +exports.arrayNotContaining = arrayNotContaining; + +const objectContaining = sample => new ObjectContaining(sample); + +exports.objectContaining = objectContaining; + +const objectNotContaining = sample => new ObjectContaining(sample, true); + +exports.objectNotContaining = objectNotContaining; + +const stringContaining = expected => new StringContaining(expected); + +exports.stringContaining = stringContaining; + +const stringNotContaining = expected => new StringContaining(expected, true); + +exports.stringNotContaining = stringNotContaining; + +const stringMatching = expected => new StringMatching(expected); + +exports.stringMatching = stringMatching; + +const stringNotMatching = expected => new StringMatching(expected, true); + +exports.stringNotMatching = stringNotMatching; diff --git a/packages/expect/build/extractExpectedAssertionsErrors.d.ts b/packages/expect/build/extractExpectedAssertionsErrors.d.ts new file mode 100644 index 000000000000..03ccd3ebf8ba --- /dev/null +++ b/packages/expect/build/extractExpectedAssertionsErrors.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + * + */ +import type { Expect } from './types'; +declare const extractExpectedAssertionsErrors: Expect['extractExpectedAssertionsErrors']; +export default extractExpectedAssertionsErrors; diff --git a/packages/expect/build/extractExpectedAssertionsErrors.js b/packages/expect/build/extractExpectedAssertionsErrors.js new file mode 100644 index 000000000000..b3dcbdd58702 --- /dev/null +++ b/packages/expect/build/extractExpectedAssertionsErrors.js @@ -0,0 +1,90 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _jestMatcherUtils = require('jest-matcher-utils'); + +var _jestMatchersObject = require('./jestMatchersObject'); + +/** + * 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 resetAssertionsLocalState = () => { + (0, _jestMatchersObject.setState)({ + assertionCalls: 0, + expectedAssertionsNumber: null, + isExpectingAssertions: false + }); +}; // Create and format all errors related to the mismatched number of `expect` +// calls and reset the matcher's state. + +const extractExpectedAssertionsErrors = () => { + const result = []; + const { + assertionCalls, + expectedAssertionsNumber, + expectedAssertionsNumberError, + isExpectingAssertions, + isExpectingAssertionsError + } = (0, _jestMatchersObject.getState)(); + resetAssertionsLocalState(); + + if ( + typeof expectedAssertionsNumber === 'number' && + assertionCalls !== expectedAssertionsNumber + ) { + const numOfAssertionsExpected = (0, _jestMatcherUtils.EXPECTED_COLOR)( + (0, _jestMatcherUtils.pluralize)('assertion', expectedAssertionsNumber) + ); + expectedAssertionsNumberError.message = + (0, _jestMatcherUtils.matcherHint)( + '.assertions', + '', + String(expectedAssertionsNumber), + { + isDirectExpectCall: true + } + ) + + '\n\n' + + `Expected ${numOfAssertionsExpected} to be called but received ` + + (0, _jestMatcherUtils.RECEIVED_COLOR)( + (0, _jestMatcherUtils.pluralize)('assertion call', assertionCalls || 0) + ) + + '.'; + result.push({ + actual: assertionCalls.toString(), + error: expectedAssertionsNumberError, + expected: expectedAssertionsNumber.toString() + }); + } + + if (isExpectingAssertions && assertionCalls === 0) { + const expected = (0, _jestMatcherUtils.EXPECTED_COLOR)( + 'at least one assertion' + ); + const received = (0, _jestMatcherUtils.RECEIVED_COLOR)('received none'); + isExpectingAssertionsError.message = + (0, _jestMatcherUtils.matcherHint)('.hasAssertions', '', '', { + isDirectExpectCall: true + }) + + '\n\n' + + `Expected ${expected} to be called but ${received}.`; + result.push({ + actual: 'none', + error: isExpectingAssertionsError, + expected: 'at least one' + }); + } + + return result; +}; + +var _default = extractExpectedAssertionsErrors; +exports.default = _default; diff --git a/packages/expect/build/index.d.ts b/packages/expect/build/index.d.ts new file mode 100644 index 000000000000..22c90de906f5 --- /dev/null +++ b/packages/expect/build/index.d.ts @@ -0,0 +1,15 @@ +/** + * 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. + * + */ +import type { Expect, MatcherState as JestMatcherState, Matchers as MatcherInterface } from './types'; +declare const expectExport: Expect; +declare namespace expectExport { + type MatcherState = JestMatcherState; + interface Matchers extends MatcherInterface { + } +} +export = expectExport; diff --git a/packages/expect/build/index.js b/packages/expect/build/index.js new file mode 100644 index 000000000000..c9e6beb46e17 --- /dev/null +++ b/packages/expect/build/index.js @@ -0,0 +1,435 @@ +'use strict'; + +var matcherUtils = _interopRequireWildcard(require('jest-matcher-utils')); + +var _asymmetricMatchers = require('./asymmetricMatchers'); + +var _extractExpectedAssertionsErrors = _interopRequireDefault( + require('./extractExpectedAssertionsErrors') +); + +var _jasmineUtils = require('./jasmineUtils'); + +var _jestMatchersObject = require('./jestMatchersObject'); + +var _matchers = _interopRequireDefault(require('./matchers')); + +var _spyMatchers = _interopRequireDefault(require('./spyMatchers')); + +var _toThrowMatchers = _interopRequireWildcard(require('./toThrowMatchers')); + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Promise = global[Symbol.for('jest-native-promise')] || global.Promise; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class JestAssertionError extends Error { + constructor(...args) { + super(...args); + + _defineProperty(this, 'matcherResult', void 0); + } +} + +const isPromise = obj => + !!obj && + (typeof obj === 'object' || typeof obj === 'function') && + typeof obj.then === 'function'; + +const createToThrowErrorMatchingSnapshotMatcher = function (matcher) { + return function (received, testNameOrInlineSnapshot) { + return matcher.apply(this, [received, testNameOrInlineSnapshot, true]); + }; +}; + +const getPromiseMatcher = (name, matcher) => { + if (name === 'toThrow' || name === 'toThrowError') { + return (0, _toThrowMatchers.createMatcher)(name, true); + } else if ( + name === 'toThrowErrorMatchingSnapshot' || + name === 'toThrowErrorMatchingInlineSnapshot' + ) { + return createToThrowErrorMatchingSnapshotMatcher(matcher); + } + + return null; +}; + +const expect = (actual, ...rest) => { + if (rest.length !== 0) { + throw new Error('Expect takes at most one argument.'); + } + + const allMatchers = (0, _jestMatchersObject.getMatchers)(); + const expectation = { + not: {}, + rejects: { + not: {} + }, + resolves: { + not: {} + } + }; + const err = new JestAssertionError(); + Object.keys(allMatchers).forEach(name => { + const matcher = allMatchers[name]; + const promiseMatcher = getPromiseMatcher(name, matcher) || matcher; + expectation[name] = makeThrowingMatcher(matcher, false, '', actual); + expectation.not[name] = makeThrowingMatcher(matcher, true, '', actual); + expectation.resolves[name] = makeResolveMatcher( + name, + promiseMatcher, + false, + actual, + err + ); + expectation.resolves.not[name] = makeResolveMatcher( + name, + promiseMatcher, + true, + actual, + err + ); + expectation.rejects[name] = makeRejectMatcher( + name, + promiseMatcher, + false, + actual, + err + ); + expectation.rejects.not[name] = makeRejectMatcher( + name, + promiseMatcher, + true, + actual, + err + ); + }); + return expectation; +}; + +const getMessage = message => + (message && message()) || + matcherUtils.RECEIVED_COLOR('No message was specified for this matcher.'); + +const makeResolveMatcher = (matcherName, matcher, isNot, actual, outerErr) => ( + ...args +) => { + const options = { + isNot, + promise: 'resolves' + }; + + if (!isPromise(actual)) { + throw new JestAssertionError( + matcherUtils.matcherErrorMessage( + matcherUtils.matcherHint(matcherName, undefined, '', options), + `${matcherUtils.RECEIVED_COLOR('received')} value must be a promise`, + matcherUtils.printWithType( + 'Received', + actual, + matcherUtils.printReceived + ) + ) + ); + } + + const innerErr = new JestAssertionError(); + return actual.then( + result => + makeThrowingMatcher(matcher, isNot, 'resolves', result, innerErr).apply( + null, + args + ), + reason => { + outerErr.message = + matcherUtils.matcherHint(matcherName, undefined, '', options) + + '\n\n' + + `Received promise rejected instead of resolved\n` + + `Rejected to value: ${matcherUtils.printReceived(reason)}`; + return Promise.reject(outerErr); + } + ); +}; + +const makeRejectMatcher = (matcherName, matcher, isNot, actual, outerErr) => ( + ...args +) => { + const options = { + isNot, + promise: 'rejects' + }; + const actualWrapper = typeof actual === 'function' ? actual() : actual; + + if (!isPromise(actualWrapper)) { + throw new JestAssertionError( + matcherUtils.matcherErrorMessage( + matcherUtils.matcherHint(matcherName, undefined, '', options), + `${matcherUtils.RECEIVED_COLOR( + 'received' + )} value must be a promise or a function returning a promise`, + matcherUtils.printWithType( + 'Received', + actual, + matcherUtils.printReceived + ) + ) + ); + } + + const innerErr = new JestAssertionError(); + return actualWrapper.then( + result => { + outerErr.message = + matcherUtils.matcherHint(matcherName, undefined, '', options) + + '\n\n' + + `Received promise resolved instead of rejected\n` + + `Resolved to value: ${matcherUtils.printReceived(result)}`; + return Promise.reject(outerErr); + }, + reason => + makeThrowingMatcher(matcher, isNot, 'rejects', reason, innerErr).apply( + null, + args + ) + ); +}; + +const makeThrowingMatcher = (matcher, isNot, promise, actual, err) => + function throwingMatcher(...args) { + let throws = true; + const utils = { + ...matcherUtils, + iterableEquality: _utils.iterableEquality, + subsetEquality: _utils.subsetEquality + }; + const matcherContext = { + // When throws is disabled, the matcher will not throw errors during test + // execution but instead add them to the global matcher state. If a + // matcher throws, test execution is normally stopped immediately. The + // snapshot matcher uses it because we want to log all snapshot + // failures in a test. + dontThrow: () => (throws = false), + ...(0, _jestMatchersObject.getState)(), + equals: _jasmineUtils.equals, + error: err, + isNot, + promise, + utils + }; + + const processResult = (result, asyncError) => { + _validateResult(result); + + (0, _jestMatchersObject.getState)().assertionCalls++; + + if ((result.pass && isNot) || (!result.pass && !isNot)) { + // XOR + const message = getMessage(result.message); + let error; + + if (err) { + error = err; + error.message = message; + } else if (asyncError) { + error = asyncError; + error.message = message; + } else { + error = new JestAssertionError(message); // Try to remove this function from the stack trace frame. + // Guard for some environments (browsers) that do not support this feature. + + if (Error.captureStackTrace) { + Error.captureStackTrace(error, throwingMatcher); + } + } // Passing the result of the matcher with the error so that a custom + // reporter could access the actual and expected objects of the result + // for example in order to display a custom visual diff + + error.matcherResult = {...result, message}; + + if (throws) { + throw error; + } else { + (0, _jestMatchersObject.getState)().suppressedErrors.push(error); + } + } + }; + + const handleError = error => { + if ( + matcher[_jestMatchersObject.INTERNAL_MATCHER_FLAG] === true && + !(error instanceof JestAssertionError) && + error.name !== 'PrettyFormatPluginError' && // Guard for some environments (browsers) that do not support this feature. + Error.captureStackTrace + ) { + // Try to remove this and deeper functions from the stack trace frame. + Error.captureStackTrace(error, throwingMatcher); + } + + throw error; + }; + + let potentialResult; + + try { + potentialResult = + matcher[_jestMatchersObject.INTERNAL_MATCHER_FLAG] === true + ? matcher.call(matcherContext, actual, ...args) // It's a trap specifically for inline snapshot to capture this name + : // in the stack trace, so that it can correctly get the custom matcher + // function call. + (function __EXTERNAL_MATCHER_TRAP__() { + return matcher.call(matcherContext, actual, ...args); + })(); + + if (isPromise(potentialResult)) { + const asyncResult = potentialResult; + const asyncError = new JestAssertionError(); + + if (Error.captureStackTrace) { + Error.captureStackTrace(asyncError, throwingMatcher); + } + + return asyncResult + .then(aResult => processResult(aResult, asyncError)) + .catch(handleError); + } else { + const syncResult = potentialResult; + return processResult(syncResult); + } + } catch (error) { + return handleError(error); + } + }; + +expect.extend = matchers => + (0, _jestMatchersObject.setMatchers)(matchers, false, expect); + +expect.anything = _asymmetricMatchers.anything; +expect.any = _asymmetricMatchers.any; +expect.not = { + arrayContaining: _asymmetricMatchers.arrayNotContaining, + objectContaining: _asymmetricMatchers.objectNotContaining, + stringContaining: _asymmetricMatchers.stringNotContaining, + stringMatching: _asymmetricMatchers.stringNotMatching +}; +expect.objectContaining = _asymmetricMatchers.objectContaining; +expect.arrayContaining = _asymmetricMatchers.arrayContaining; +expect.stringContaining = _asymmetricMatchers.stringContaining; +expect.stringMatching = _asymmetricMatchers.stringMatching; + +const _validateResult = result => { + if ( + typeof result !== 'object' || + typeof result.pass !== 'boolean' || + (result.message && + typeof result.message !== 'string' && + typeof result.message !== 'function') + ) { + throw new Error( + 'Unexpected return from a matcher function.\n' + + 'Matcher functions should ' + + 'return an object in the following format:\n' + + ' {message?: string | function, pass: boolean}\n' + + `'${matcherUtils.stringify(result)}' was returned` + ); + } +}; + +function assertions(expected) { + const error = new Error(); + + if (Error.captureStackTrace) { + Error.captureStackTrace(error, assertions); + } + + (0, _jestMatchersObject.getState)().expectedAssertionsNumber = expected; + (0, _jestMatchersObject.getState)().expectedAssertionsNumberError = error; +} + +function hasAssertions(...args) { + const error = new Error(); + + if (Error.captureStackTrace) { + Error.captureStackTrace(error, hasAssertions); + } + + matcherUtils.ensureNoExpected(args[0], '.hasAssertions'); + (0, _jestMatchersObject.getState)().isExpectingAssertions = true; + (0, _jestMatchersObject.getState)().isExpectingAssertionsError = error; +} // add default jest matchers + +(0, _jestMatchersObject.setMatchers)(_matchers.default, true, expect); +(0, _jestMatchersObject.setMatchers)(_spyMatchers.default, true, expect); +(0, _jestMatchersObject.setMatchers)(_toThrowMatchers.default, true, expect); + +expect.addSnapshotSerializer = () => void 0; + +expect.assertions = assertions; +expect.hasAssertions = hasAssertions; +expect.getState = _jestMatchersObject.getState; +expect.setState = _jestMatchersObject.setState; +expect.extractExpectedAssertionsErrors = + _extractExpectedAssertionsErrors.default; +const expectExport = expect; +module.exports = expectExport; diff --git a/packages/expect/build/jasmineUtils.d.ts b/packages/expect/build/jasmineUtils.d.ts new file mode 100644 index 000000000000..1a2f0f6bc622 --- /dev/null +++ b/packages/expect/build/jasmineUtils.d.ts @@ -0,0 +1,8 @@ +import type { Tester } from './types'; +export declare function equals(a: unknown, b: unknown, customTesters?: Array, strictCheck?: boolean): boolean; +export declare function isA(typeName: string, value: unknown): boolean; +export declare function fnNameFor(func: Function): string; +export declare function isUndefined(obj: any): boolean; +export declare function hasProperty(obj: object | null, property: string): boolean; +export declare function isImmutableUnorderedKeyed(maybeKeyed: any): boolean; +export declare function isImmutableUnorderedSet(maybeSet: any): boolean; diff --git a/packages/expect/build/jasmineUtils.js b/packages/expect/build/jasmineUtils.js new file mode 100644 index 000000000000..72e3a7918f22 --- /dev/null +++ b/packages/expect/build/jasmineUtils.js @@ -0,0 +1,316 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.equals = equals; +exports.isA = isA; +exports.fnNameFor = fnNameFor; +exports.isUndefined = isUndefined; +exports.hasProperty = hasProperty; +exports.isImmutableUnorderedKeyed = isImmutableUnorderedKeyed; +exports.isImmutableUnorderedSet = isImmutableUnorderedSet; + +/* +Copyright (c) 2008-2016 Pivotal Labs + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +/* eslint-disable */ +// Extracted out of jasmine 2.5.2 +function equals(a, b, customTesters, strictCheck) { + customTesters = customTesters || []; + return eq(a, b, [], [], customTesters, strictCheck ? hasKey : hasDefinedKey); +} + +const functionToString = Function.prototype.toString; + +function isAsymmetric(obj) { + return !!obj && isA('Function', obj.asymmetricMatch); +} + +function asymmetricMatch(a, b) { + var asymmetricA = isAsymmetric(a), + asymmetricB = isAsymmetric(b); + + if (asymmetricA && asymmetricB) { + return undefined; + } + + if (asymmetricA) { + return a.asymmetricMatch(b); + } + + if (asymmetricB) { + return b.asymmetricMatch(a); + } +} // Equality function lovingly adapted from isEqual in +// [Underscore](http://underscorejs.org) + +function eq(a, b, aStack, bStack, customTesters, hasKey) { + var result = true; + var asymmetricResult = asymmetricMatch(a, b); + + if (asymmetricResult !== undefined) { + return asymmetricResult; + } + + for (var i = 0; i < customTesters.length; i++) { + var customTesterResult = customTesters[i](a, b); + + if (customTesterResult !== undefined) { + return customTesterResult; + } + } + + if (a instanceof Error && b instanceof Error) { + return a.message == b.message; + } + + if (Object.is(a, b)) { + return true; + } // A strict comparison is necessary because `null == undefined`. + + if (a === null || b === null) { + return a === b; + } + + var className = Object.prototype.toString.call(a); + + if (className != Object.prototype.toString.call(b)) { + return false; + } + + switch (className) { + case '[object Boolean]': + case '[object String]': + case '[object Number]': + if (typeof a !== typeof b) { + // One is a primitive, one a `new Primitive()` + return false; + } else if (typeof a !== 'object' && typeof b !== 'object') { + // both are proper primitives + return Object.is(a, b); + } else { + // both are `new Primitive()`s + return Object.is(a.valueOf(), b.valueOf()); + } + + case '[object Date]': + // Coerce dates to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + + case '[object RegExp]': + return a.source === b.source && a.flags === b.flags; + } + + if (typeof a !== 'object' || typeof b !== 'object') { + return false; + } // Use DOM3 method isEqualNode (IE>=9) + + if (isDomNode(a) && isDomNode(b)) { + return a.isEqualNode(b); + } // Used to detect circular references. + + var length = aStack.length; + + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + // circular references at same depth are equal + // circular reference is not equal to non-circular one + if (aStack[length] === a) { + return bStack[length] === b; + } else if (bStack[length] === b) { + return false; + } + } // Add the first object to the stack of traversed objects. + + aStack.push(a); + bStack.push(b); + var size = 0; // Recursively compare objects and arrays. + // Compare array lengths to determine if a deep comparison is necessary. + + if (className == '[object Array]') { + size = a.length; + + if (size !== b.length) { + return false; + } + + while (size--) { + result = eq(a[size], b[size], aStack, bStack, customTesters, hasKey); + + if (!result) { + return false; + } + } + } // Deep compare objects. + + var aKeys = keys(a, className == '[object Array]', hasKey), + key; + size = aKeys.length; // Ensure that both objects contain the same number of properties before comparing deep equality. + + if (keys(b, className == '[object Array]', hasKey).length !== size) { + return false; + } + + while (size--) { + key = aKeys[size]; // Deep compare each member + + result = + hasKey(b, key) && + eq(a[key], b[key], aStack, bStack, customTesters, hasKey); + + if (!result) { + return false; + } + } // Remove the first object from the stack of traversed objects. + + aStack.pop(); + bStack.pop(); + return result; +} + +function keys(obj, isArray, hasKey) { + var allKeys = (function (o) { + var keys = []; + + for (var key in o) { + if (hasKey(o, key)) { + keys.push(key); + } + } + + return keys.concat( + Object.getOwnPropertySymbols(o).filter( + symbol => Object.getOwnPropertyDescriptor(o, symbol).enumerable + ) + ); + })(obj); + + if (!isArray) { + return allKeys; + } + + var extraKeys = []; + + if (allKeys.length === 0) { + return allKeys; + } + + for (var x = 0; x < allKeys.length; x++) { + if ( + typeof allKeys[x] === 'symbol' || + !allKeys[x].match(/^[0-9]+$/) || + Number(allKeys[x]) >= 4294967295 + ) { + extraKeys.push(allKeys[x]); + } + } + + return extraKeys; +} + +function hasDefinedKey(obj, key) { + return hasKey(obj, key) && obj[key] !== undefined; +} + +function hasKey(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +} + +function isA(typeName, value) { + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; +} + +function isDomNode(obj) { + return ( + obj !== null && + typeof obj === 'object' && + typeof obj.nodeType === 'number' && + typeof obj.nodeName === 'string' && + typeof obj.isEqualNode === 'function' + ); +} + +function fnNameFor(func) { + if (func.name) { + return func.name; + } + + const matches = functionToString + .call(func) + .match(/^(?:async)?\s*function\s*\*?\s*([\w$]+)\s*\(/); + return matches ? matches[1] : ''; +} + +function isUndefined(obj) { + return obj === void 0; +} + +function getPrototype(obj) { + if (Object.getPrototypeOf) { + return Object.getPrototypeOf(obj); + } + + if (obj.constructor.prototype == obj) { + return null; + } + + return obj.constructor.prototype; +} + +function hasProperty(obj, property) { + if (!obj) { + return false; + } + + if (Object.prototype.hasOwnProperty.call(obj, property)) { + return true; + } + + return hasProperty(getPrototype(obj), property); +} // SENTINEL constants are from https://github.com/facebook/immutable-js + +const IS_KEYED_SENTINEL = '@@__IMMUTABLE_KEYED__@@'; +const IS_SET_SENTINEL = '@@__IMMUTABLE_SET__@@'; +const IS_ORDERED_SENTINEL = '@@__IMMUTABLE_ORDERED__@@'; + +function isImmutableUnorderedKeyed(maybeKeyed) { + return !!( + maybeKeyed && + maybeKeyed[IS_KEYED_SENTINEL] && + !maybeKeyed[IS_ORDERED_SENTINEL] + ); +} + +function isImmutableUnorderedSet(maybeSet) { + return !!( + maybeSet && + maybeSet[IS_SET_SENTINEL] && + !maybeSet[IS_ORDERED_SENTINEL] + ); +} diff --git a/packages/expect/build/jestMatchersObject.d.ts b/packages/expect/build/jestMatchersObject.d.ts new file mode 100644 index 000000000000..9d722779017a --- /dev/null +++ b/packages/expect/build/jestMatchersObject.d.ts @@ -0,0 +1,13 @@ +/** + * 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. + * + */ +import type { Expect, MatcherState, MatchersObject } from './types'; +export declare const INTERNAL_MATCHER_FLAG: unique symbol; +export declare const getState: () => MatcherState; +export declare const setState: (state: Partial) => void; +export declare const getMatchers: () => MatchersObject; +export declare const setMatchers: (matchers: MatchersObject, isInternal: boolean, expect: Expect) => void; diff --git a/packages/expect/build/jestMatchersObject.js b/packages/expect/build/jestMatchersObject.js new file mode 100644 index 000000000000..782151c3e5e7 --- /dev/null +++ b/packages/expect/build/jestMatchersObject.js @@ -0,0 +1,93 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.setMatchers = exports.getMatchers = exports.setState = exports.getState = exports.INTERNAL_MATCHER_FLAG = void 0; + +var _asymmetricMatchers = require('./asymmetricMatchers'); + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +// Global matchers object holds the list of available matchers and +// the state, that can hold matcher specific values that change over time. +const JEST_MATCHERS_OBJECT = Symbol.for('$$jest-matchers-object'); // Notes a built-in/internal Jest matcher. +// Jest may override the stack trace of Errors thrown by internal matchers. + +const INTERNAL_MATCHER_FLAG = Symbol.for('$$jest-internal-matcher'); +exports.INTERNAL_MATCHER_FLAG = INTERNAL_MATCHER_FLAG; + +if (!global.hasOwnProperty(JEST_MATCHERS_OBJECT)) { + const defaultState = { + assertionCalls: 0, + expectedAssertionsNumber: null, + isExpectingAssertions: false, + suppressedErrors: [] // errors that are not thrown immediately. + }; + Object.defineProperty(global, JEST_MATCHERS_OBJECT, { + value: { + matchers: Object.create(null), + state: defaultState + } + }); +} + +const getState = () => global[JEST_MATCHERS_OBJECT].state; + +exports.getState = getState; + +const setState = state => { + Object.assign(global[JEST_MATCHERS_OBJECT].state, state); +}; + +exports.setState = setState; + +const getMatchers = () => global[JEST_MATCHERS_OBJECT].matchers; + +exports.getMatchers = getMatchers; + +const setMatchers = (matchers, isInternal, expect) => { + Object.keys(matchers).forEach(key => { + const matcher = matchers[key]; + Object.defineProperty(matcher, INTERNAL_MATCHER_FLAG, { + value: isInternal + }); + + if (!isInternal) { + // expect is defined + class CustomMatcher extends _asymmetricMatchers.AsymmetricMatcher { + constructor(inverse = false, ...sample) { + super(sample); + this.inverse = inverse; + } + + asymmetricMatch(other) { + const {pass} = matcher(other, ...this.sample); + return this.inverse ? !pass : pass; + } + + toString() { + return `${this.inverse ? 'not.' : ''}${key}`; + } + + getExpectedType() { + return 'any'; + } + + toAsymmetricMatcher() { + return `${this.toString()}<${this.sample.map(String).join(', ')}>`; + } + } + + expect[key] = (...sample) => new CustomMatcher(false, ...sample); + + if (!expect.not) { + expect.not = {}; + } + + expect.not[key] = (...sample) => new CustomMatcher(true, ...sample); + } + }); + Object.assign(global[JEST_MATCHERS_OBJECT].matchers, matchers); +}; + +exports.setMatchers = setMatchers; diff --git a/packages/expect/build/matchers.d.ts b/packages/expect/build/matchers.d.ts new file mode 100644 index 000000000000..650147309024 --- /dev/null +++ b/packages/expect/build/matchers.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + * + */ +import type { MatchersObject } from './types'; +declare const matchers: MatchersObject; +export default matchers; diff --git a/packages/expect/build/matchers.js b/packages/expect/build/matchers.js new file mode 100644 index 000000000000..ab0f7a223fe1 --- /dev/null +++ b/packages/expect/build/matchers.js @@ -0,0 +1,1373 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _jestGetType = _interopRequireDefault(require('jest-get-type')); + +var _jestMatcherUtils = require('jest-matcher-utils'); + +var _jasmineUtils = require('./jasmineUtils'); + +var _print = require('./print'); + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + * + */ + +/* eslint-disable local/ban-types-eventually */ +// Omit colon and one or more spaces, so can call getLabelPrinter. +const EXPECTED_LABEL = 'Expected'; +const RECEIVED_LABEL = 'Received'; +const EXPECTED_VALUE_LABEL = 'Expected value'; +const RECEIVED_VALUE_LABEL = 'Received value'; // The optional property of matcher context is true if undefined. + +const isExpand = expand => expand !== false; + +const toStrictEqualTesters = [ + _utils.iterableEquality, + _utils.typeEquality, + _utils.sparseArrayEquality +]; +const matchers = { + toBe(received, expected) { + const matcherName = 'toBe'; + const options = { + comment: 'Object.is equality', + isNot: this.isNot, + promise: this.promise + }; + const pass = Object.is(received, expected); + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `Expected: not ${(0, _jestMatcherUtils.printExpected)(expected)}` + : () => { + const expectedType = (0, _jestGetType.default)(expected); + let deepEqualityName = null; + + if (expectedType !== 'map' && expectedType !== 'set') { + // If deep equality passes when referential identity fails, + // but exclude map and set until review of their equality logic. + if ( + (0, _jasmineUtils.equals)( + received, + expected, + toStrictEqualTesters, + true + ) + ) { + deepEqualityName = 'toStrictEqual'; + } else if ( + (0, _jasmineUtils.equals)(received, expected, [ + _utils.iterableEquality + ]) + ) { + deepEqualityName = 'toEqual'; + } + } + + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + (deepEqualityName !== null + ? (0, _jestMatcherUtils.DIM_COLOR)( + `If it should pass with deep equality, replace "${matcherName}" with "${deepEqualityName}"` + ) + '\n\n' + : '') + + (0, _jestMatcherUtils.printDiffOrStringify)( + expected, + received, + EXPECTED_LABEL, + RECEIVED_LABEL, + isExpand(this.expand) + ) + ); + }; // Passing the actual and expected objects so that a custom reporter + // could access them, for example in order to display a custom visual diff, + // or create a different error message + + return { + actual: received, + expected, + message, + name: matcherName, + pass + }; + }, + + toBeCloseTo(received, expected, precision = 2) { + const matcherName = 'toBeCloseTo'; + const secondArgument = arguments.length === 3 ? 'precision' : undefined; + const isNot = this.isNot; + const options = { + isNot, + promise: this.promise, + secondArgument, + secondArgumentColor: arg => arg + }; + + if (typeof expected !== 'number') { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ), + `${(0, _jestMatcherUtils.EXPECTED_COLOR)( + 'expected' + )} value must be a number`, + (0, _jestMatcherUtils.printWithType)( + 'Expected', + expected, + _jestMatcherUtils.printExpected + ) + ) + ); + } + + if (typeof received !== 'number') { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ), + `${(0, _jestMatcherUtils.RECEIVED_COLOR)( + 'received' + )} value must be a number`, + (0, _jestMatcherUtils.printWithType)( + 'Received', + received, + _jestMatcherUtils.printReceived + ) + ) + ); + } + + let pass = false; + let expectedDiff = 0; + let receivedDiff = 0; + + if (received === Infinity && expected === Infinity) { + pass = true; // Infinity - Infinity is NaN + } else if (received === -Infinity && expected === -Infinity) { + pass = true; // -Infinity - -Infinity is NaN + } else { + expectedDiff = Math.pow(10, -precision) / 2; + receivedDiff = Math.abs(expected - received); + pass = receivedDiff < expectedDiff; + } + + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `Expected: not ${(0, _jestMatcherUtils.printExpected)(expected)}\n` + + (receivedDiff === 0 + ? '' + : `Received: ${(0, _jestMatcherUtils.printReceived)( + received + )}\n` + + '\n' + + (0, _print.printCloseTo)( + receivedDiff, + expectedDiff, + precision, + isNot + )) + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `Expected: ${(0, _jestMatcherUtils.printExpected)(expected)}\n` + + `Received: ${(0, _jestMatcherUtils.printReceived)(received)}\n` + + '\n' + + (0, _print.printCloseTo)( + receivedDiff, + expectedDiff, + precision, + isNot + ); + return { + message, + pass + }; + }, + + toBeDefined(received, expected) { + const matcherName = 'toBeDefined'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureNoExpected)(expected, matcherName, options); + const pass = received !== void 0; + + const message = () => + (0, _jestMatcherUtils.matcherHint)(matcherName, undefined, '', options) + + '\n\n' + + `Received: ${(0, _jestMatcherUtils.printReceived)(received)}`; + + return { + message, + pass + }; + }, + + toBeFalsy(received, expected) { + const matcherName = 'toBeFalsy'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureNoExpected)(expected, matcherName, options); + const pass = !received; + + const message = () => + (0, _jestMatcherUtils.matcherHint)(matcherName, undefined, '', options) + + '\n\n' + + `Received: ${(0, _jestMatcherUtils.printReceived)(received)}`; + + return { + message, + pass + }; + }, + + toBeGreaterThan(received, expected) { + const matcherName = 'toBeGreaterThan'; + const isNot = this.isNot; + const options = { + isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureNumbers)( + received, + expected, + matcherName, + options + ); + const pass = received > expected; + + const message = () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `Expected:${isNot ? ' not' : ''} > ${(0, _jestMatcherUtils.printExpected)( + expected + )}\n` + + `Received:${isNot ? ' ' : ''} ${(0, _jestMatcherUtils.printReceived)( + received + )}`; + + return { + message, + pass + }; + }, + + toBeGreaterThanOrEqual(received, expected) { + const matcherName = 'toBeGreaterThanOrEqual'; + const isNot = this.isNot; + const options = { + isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureNumbers)( + received, + expected, + matcherName, + options + ); + const pass = received >= expected; + + const message = () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `Expected:${isNot ? ' not' : ''} >= ${(0, + _jestMatcherUtils.printExpected)(expected)}\n` + + `Received:${isNot ? ' ' : ''} ${(0, + _jestMatcherUtils.printReceived)(received)}`; + + return { + message, + pass + }; + }, + + toBeInstanceOf(received, expected) { + const matcherName = 'toBeInstanceOf'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + + if (typeof expected !== 'function') { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ), + `${(0, _jestMatcherUtils.EXPECTED_COLOR)( + 'expected' + )} value must be a function`, + (0, _jestMatcherUtils.printWithType)( + 'Expected', + expected, + _jestMatcherUtils.printExpected + ) + ) + ); + } + + const pass = received instanceof expected; + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + (0, _print.printExpectedConstructorNameNot)( + 'Expected constructor', + expected + ) + + (typeof received.constructor === 'function' && + received.constructor !== expected + ? (0, _print.printReceivedConstructorNameNot)( + 'Received constructor', + received.constructor, + expected + ) + : '') + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + (0, _print.printExpectedConstructorName)( + 'Expected constructor', + expected + ) + + (_jestGetType.default.isPrimitive(received) || + Object.getPrototypeOf(received) === null + ? `\nReceived value has no prototype\nReceived value: ${(0, + _jestMatcherUtils.printReceived)(received)}` + : typeof received.constructor !== 'function' + ? `\nReceived value: ${(0, _jestMatcherUtils.printReceived)( + received + )}` + : (0, _print.printReceivedConstructorName)( + 'Received constructor', + received.constructor + )); + return { + message, + pass + }; + }, + + toBeLessThan(received, expected) { + const matcherName = 'toBeLessThan'; + const isNot = this.isNot; + const options = { + isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureNumbers)( + received, + expected, + matcherName, + options + ); + const pass = received < expected; + + const message = () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `Expected:${isNot ? ' not' : ''} < ${(0, _jestMatcherUtils.printExpected)( + expected + )}\n` + + `Received:${isNot ? ' ' : ''} ${(0, _jestMatcherUtils.printReceived)( + received + )}`; + + return { + message, + pass + }; + }, + + toBeLessThanOrEqual(received, expected) { + const matcherName = 'toBeLessThanOrEqual'; + const isNot = this.isNot; + const options = { + isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureNumbers)( + received, + expected, + matcherName, + options + ); + const pass = received <= expected; + + const message = () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `Expected:${isNot ? ' not' : ''} <= ${(0, + _jestMatcherUtils.printExpected)(expected)}\n` + + `Received:${isNot ? ' ' : ''} ${(0, + _jestMatcherUtils.printReceived)(received)}`; + + return { + message, + pass + }; + }, + + toBeNaN(received, expected) { + const matcherName = 'toBeNaN'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureNoExpected)(expected, matcherName, options); + const pass = Number.isNaN(received); + + const message = () => + (0, _jestMatcherUtils.matcherHint)(matcherName, undefined, '', options) + + '\n\n' + + `Received: ${(0, _jestMatcherUtils.printReceived)(received)}`; + + return { + message, + pass + }; + }, + + toBeNull(received, expected) { + const matcherName = 'toBeNull'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureNoExpected)(expected, matcherName, options); + const pass = received === null; + + const message = () => + (0, _jestMatcherUtils.matcherHint)(matcherName, undefined, '', options) + + '\n\n' + + `Received: ${(0, _jestMatcherUtils.printReceived)(received)}`; + + return { + message, + pass + }; + }, + + toBeTruthy(received, expected) { + const matcherName = 'toBeTruthy'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureNoExpected)(expected, matcherName, options); + const pass = !!received; + + const message = () => + (0, _jestMatcherUtils.matcherHint)(matcherName, undefined, '', options) + + '\n\n' + + `Received: ${(0, _jestMatcherUtils.printReceived)(received)}`; + + return { + message, + pass + }; + }, + + toBeUndefined(received, expected) { + const matcherName = 'toBeUndefined'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureNoExpected)(expected, matcherName, options); + const pass = received === void 0; + + const message = () => + (0, _jestMatcherUtils.matcherHint)(matcherName, undefined, '', options) + + '\n\n' + + `Received: ${(0, _jestMatcherUtils.printReceived)(received)}`; + + return { + message, + pass + }; + }, + + toContain(received, expected) { + const matcherName = 'toContain'; + const isNot = this.isNot; + const options = { + comment: 'indexOf', + isNot, + promise: this.promise + }; + + if (received == null) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ), + `${(0, _jestMatcherUtils.RECEIVED_COLOR)( + 'received' + )} value must not be null nor undefined`, + (0, _jestMatcherUtils.printWithType)( + 'Received', + received, + _jestMatcherUtils.printReceived + ) + ) + ); + } + + if (typeof received === 'string') { + const wrongTypeErrorMessage = `${(0, _jestMatcherUtils.EXPECTED_COLOR)( + 'expected' + )} value must be a string if ${(0, _jestMatcherUtils.RECEIVED_COLOR)( + 'received' + )} value is a string`; + + if (typeof expected !== 'string') { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + received, + String(expected), + options + ), + wrongTypeErrorMessage, + (0, _jestMatcherUtils.printWithType)( + 'Expected', + expected, + _jestMatcherUtils.printExpected + ) + + '\n' + + (0, _jestMatcherUtils.printWithType)( + 'Received', + received, + _jestMatcherUtils.printReceived + ) + ) + ); + } + + const index = received.indexOf(String(expected)); + const pass = index !== -1; + + const message = () => { + const labelExpected = `Expected ${ + typeof expected === 'string' ? 'substring' : 'value' + }`; + const labelReceived = 'Received string'; + const printLabel = (0, _jestMatcherUtils.getLabelPrinter)( + labelExpected, + labelReceived + ); + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `${printLabel(labelExpected)}${isNot ? 'not ' : ''}${(0, + _jestMatcherUtils.printExpected)(expected)}\n` + + `${printLabel(labelReceived)}${isNot ? ' ' : ''}${ + isNot + ? (0, _print.printReceivedStringContainExpectedSubstring)( + received, + index, + String(expected).length + ) + : (0, _jestMatcherUtils.printReceived)(received) + }` + ); + }; + + return { + message, + pass + }; + } + + const indexable = Array.from(received); + const index = indexable.indexOf(expected); + const pass = index !== -1; + + const message = () => { + const labelExpected = 'Expected value'; + const labelReceived = `Received ${(0, _jestGetType.default)(received)}`; + const printLabel = (0, _jestMatcherUtils.getLabelPrinter)( + labelExpected, + labelReceived + ); + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `${printLabel(labelExpected)}${isNot ? 'not ' : ''}${(0, + _jestMatcherUtils.printExpected)(expected)}\n` + + `${printLabel(labelReceived)}${isNot ? ' ' : ''}${ + isNot && Array.isArray(received) + ? (0, _print.printReceivedArrayContainExpectedItem)(received, index) + : (0, _jestMatcherUtils.printReceived)(received) + }` + + (!isNot && + indexable.findIndex(item => + (0, _jasmineUtils.equals)(item, expected, [_utils.iterableEquality]) + ) !== -1 + ? `\n\n${_jestMatcherUtils.SUGGEST_TO_CONTAIN_EQUAL}` + : '') + ); + }; + + return { + message, + pass + }; + }, + + toContainEqual(received, expected) { + const matcherName = 'toContainEqual'; + const isNot = this.isNot; + const options = { + comment: 'deep equality', + isNot, + promise: this.promise + }; + + if (received == null) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ), + `${(0, _jestMatcherUtils.RECEIVED_COLOR)( + 'received' + )} value must not be null nor undefined`, + (0, _jestMatcherUtils.printWithType)( + 'Received', + received, + _jestMatcherUtils.printReceived + ) + ) + ); + } + + const index = Array.from(received).findIndex(item => + (0, _jasmineUtils.equals)(item, expected, [_utils.iterableEquality]) + ); + const pass = index !== -1; + + const message = () => { + const labelExpected = 'Expected value'; + const labelReceived = `Received ${(0, _jestGetType.default)(received)}`; + const printLabel = (0, _jestMatcherUtils.getLabelPrinter)( + labelExpected, + labelReceived + ); + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `${printLabel(labelExpected)}${isNot ? 'not ' : ''}${(0, + _jestMatcherUtils.printExpected)(expected)}\n` + + `${printLabel(labelReceived)}${isNot ? ' ' : ''}${ + isNot && Array.isArray(received) + ? (0, _print.printReceivedArrayContainExpectedItem)(received, index) + : (0, _jestMatcherUtils.printReceived)(received) + }` + ); + }; + + return { + message, + pass + }; + }, + + toEqual(received, expected) { + const matcherName = 'toEqual'; + const options = { + comment: 'deep equality', + isNot: this.isNot, + promise: this.promise + }; + const pass = (0, _jasmineUtils.equals)(received, expected, [ + _utils.iterableEquality + ]); + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `Expected: not ${(0, _jestMatcherUtils.printExpected)(expected)}\n` + + ((0, _jestMatcherUtils.stringify)(expected) !== + (0, _jestMatcherUtils.stringify)(received) + ? `Received: ${(0, _jestMatcherUtils.printReceived)(received)}` + : '') + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + (0, _jestMatcherUtils.printDiffOrStringify)( + expected, + received, + EXPECTED_LABEL, + RECEIVED_LABEL, + isExpand(this.expand) + ); // Passing the actual and expected objects so that a custom reporter + // could access them, for example in order to display a custom visual diff, + // or create a different error message + + return { + actual: received, + expected, + message, + name: matcherName, + pass + }; + }, + + toHaveLength(received, expected) { + const matcherName = 'toHaveLength'; + const isNot = this.isNot; + const options = { + isNot, + promise: this.promise + }; + + if ( + typeof (received === null || received === void 0 + ? void 0 + : received.length) !== 'number' + ) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ), + `${(0, _jestMatcherUtils.RECEIVED_COLOR)( + 'received' + )} value must have a length property whose value must be a number`, + (0, _jestMatcherUtils.printWithType)( + 'Received', + received, + _jestMatcherUtils.printReceived + ) + ) + ); + } + + (0, _jestMatcherUtils.ensureExpectedIsNonNegativeInteger)( + expected, + matcherName, + options + ); + const pass = received.length === expected; + + const message = () => { + const labelExpected = 'Expected length'; + const labelReceivedLength = 'Received length'; + const labelReceivedValue = `Received ${(0, _jestGetType.default)( + received + )}`; + const printLabel = (0, _jestMatcherUtils.getLabelPrinter)( + labelExpected, + labelReceivedLength, + labelReceivedValue + ); + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `${printLabel(labelExpected)}${isNot ? 'not ' : ''}${(0, + _jestMatcherUtils.printExpected)(expected)}\n` + + (isNot + ? '' + : `${printLabel(labelReceivedLength)}${(0, + _jestMatcherUtils.printReceived)(received.length)}\n`) + + `${printLabel(labelReceivedValue)}${isNot ? ' ' : ''}${(0, + _jestMatcherUtils.printReceived)(received)}` + ); + }; + + return { + message, + pass + }; + }, + + toHaveProperty(received, expectedPath, expectedValue) { + const matcherName = 'toHaveProperty'; + const expectedArgument = 'path'; + const hasValue = arguments.length === 3; + const options = { + isNot: this.isNot, + promise: this.promise, + secondArgument: hasValue ? 'value' : '' + }; + + if (received === null || received === undefined) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + expectedArgument, + options + ), + `${(0, _jestMatcherUtils.RECEIVED_COLOR)( + 'received' + )} value must not be null nor undefined`, + (0, _jestMatcherUtils.printWithType)( + 'Received', + received, + _jestMatcherUtils.printReceived + ) + ) + ); + } + + const expectedPathType = (0, _jestGetType.default)(expectedPath); + + if (expectedPathType !== 'string' && expectedPathType !== 'array') { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + expectedArgument, + options + ), + `${(0, _jestMatcherUtils.EXPECTED_COLOR)( + 'expected' + )} path must be a string or array`, + (0, _jestMatcherUtils.printWithType)( + 'Expected', + expectedPath, + _jestMatcherUtils.printExpected + ) + ) + ); + } + + const expectedPathLength = + typeof expectedPath === 'string' + ? expectedPath.split('.').length + : expectedPath.length; + + if (expectedPathType === 'array' && expectedPathLength === 0) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + expectedArgument, + options + ), + `${(0, _jestMatcherUtils.EXPECTED_COLOR)( + 'expected' + )} path must not be an empty array`, + (0, _jestMatcherUtils.printWithType)( + 'Expected', + expectedPath, + _jestMatcherUtils.printExpected + ) + ) + ); + } + + const result = (0, _utils.getPath)(received, expectedPath); + const {lastTraversedObject, hasEndProp} = result; + const receivedPath = result.traversedPath; + const hasCompletePath = receivedPath.length === expectedPathLength; + const receivedValue = hasCompletePath ? result.value : lastTraversedObject; + const pass = hasValue + ? (0, _jasmineUtils.equals)(result.value, expectedValue, [ + _utils.iterableEquality + ]) + : Boolean(hasEndProp); // theoretically undefined if empty path + // Remove type cast if we rewrite getPath as iterative algorithm. + // Delete this unique report if future breaking change + // removes the edge case that expected value undefined + // also matches absence of a property with the key path. + + if (pass && !hasCompletePath) { + const message = () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + expectedArgument, + options + ) + + '\n\n' + + `Expected path: ${(0, _jestMatcherUtils.printExpected)( + expectedPath + )}\n` + + `Received path: ${(0, _jestMatcherUtils.printReceived)( + expectedPathType === 'array' || receivedPath.length === 0 + ? receivedPath + : receivedPath.join('.') + )}\n\n` + + `Expected value: not ${(0, _jestMatcherUtils.printExpected)( + expectedValue + )}\n` + + `Received value: ${(0, _jestMatcherUtils.printReceived)( + receivedValue + )}\n\n` + + (0, _jestMatcherUtils.DIM_COLOR)( + 'Because a positive assertion passes for expected value undefined if the property does not exist, this negative assertion fails unless the property does exist and has a defined value' + ); + + return { + message, + pass + }; + } + + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + expectedArgument, + options + ) + + '\n\n' + + (hasValue + ? `Expected path: ${(0, _jestMatcherUtils.printExpected)( + expectedPath + )}\n\n` + + `Expected value: not ${(0, _jestMatcherUtils.printExpected)( + expectedValue + )}` + + ((0, _jestMatcherUtils.stringify)(expectedValue) !== + (0, _jestMatcherUtils.stringify)(receivedValue) + ? `\nReceived value: ${(0, _jestMatcherUtils.printReceived)( + receivedValue + )}` + : '') + : `Expected path: not ${(0, _jestMatcherUtils.printExpected)( + expectedPath + )}\n\n` + + `Received value: ${(0, _jestMatcherUtils.printReceived)( + receivedValue + )}`) + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + expectedArgument, + options + ) + + '\n\n' + + `Expected path: ${(0, _jestMatcherUtils.printExpected)( + expectedPath + )}\n` + + (hasCompletePath + ? '\n' + + (0, _jestMatcherUtils.printDiffOrStringify)( + expectedValue, + receivedValue, + EXPECTED_VALUE_LABEL, + RECEIVED_VALUE_LABEL, + isExpand(this.expand) + ) + : `Received path: ${(0, _jestMatcherUtils.printReceived)( + expectedPathType === 'array' || receivedPath.length === 0 + ? receivedPath + : receivedPath.join('.') + )}\n\n` + + (hasValue + ? `Expected value: ${(0, _jestMatcherUtils.printExpected)( + expectedValue + )}\n` + : '') + + `Received value: ${(0, _jestMatcherUtils.printReceived)( + receivedValue + )}`); + return { + message, + pass + }; + }, + + toMatch(received, expected) { + const matcherName = 'toMatch'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + + if (typeof received !== 'string') { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ), + `${(0, _jestMatcherUtils.RECEIVED_COLOR)( + 'received' + )} value must be a string`, + (0, _jestMatcherUtils.printWithType)( + 'Received', + received, + _jestMatcherUtils.printReceived + ) + ) + ); + } + + if ( + !(typeof expected === 'string') && + !(expected && typeof expected.test === 'function') + ) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ), + `${(0, _jestMatcherUtils.EXPECTED_COLOR)( + 'expected' + )} value must be a string or regular expression`, + (0, _jestMatcherUtils.printWithType)( + 'Expected', + expected, + _jestMatcherUtils.printExpected + ) + ) + ); + } + + const pass = + typeof expected === 'string' + ? received.includes(expected) + : new RegExp(expected).test(received); + const message = pass + ? () => + typeof expected === 'string' + ? (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `Expected substring: not ${(0, _jestMatcherUtils.printExpected)( + expected + )}\n` + + `Received string: ${(0, + _print.printReceivedStringContainExpectedSubstring)( + received, + received.indexOf(expected), + expected.length + )}` + : (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `Expected pattern: not ${(0, _jestMatcherUtils.printExpected)( + expected + )}\n` + + `Received string: ${(0, + _print.printReceivedStringContainExpectedResult)( + received, + typeof expected.exec === 'function' + ? expected.exec(received) + : null + )}` + : () => { + const labelExpected = `Expected ${ + typeof expected === 'string' ? 'substring' : 'pattern' + }`; + const labelReceived = 'Received string'; + const printLabel = (0, _jestMatcherUtils.getLabelPrinter)( + labelExpected, + labelReceived + ); + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `${printLabel(labelExpected)}${(0, _jestMatcherUtils.printExpected)( + expected + )}\n` + + `${printLabel(labelReceived)}${(0, _jestMatcherUtils.printReceived)( + received + )}` + ); + }; + return { + message, + pass + }; + }, + + toMatchObject(received, expected) { + const matcherName = 'toMatchObject'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + + if (typeof received !== 'object' || received === null) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ), + `${(0, _jestMatcherUtils.RECEIVED_COLOR)( + 'received' + )} value must be a non-null object`, + (0, _jestMatcherUtils.printWithType)( + 'Received', + received, + _jestMatcherUtils.printReceived + ) + ) + ); + } + + if (typeof expected !== 'object' || expected === null) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ), + `${(0, _jestMatcherUtils.EXPECTED_COLOR)( + 'expected' + )} value must be a non-null object`, + (0, _jestMatcherUtils.printWithType)( + 'Expected', + expected, + _jestMatcherUtils.printExpected + ) + ) + ); + } + + const pass = (0, _jasmineUtils.equals)(received, expected, [ + _utils.iterableEquality, + _utils.subsetEquality + ]); + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `Expected: not ${(0, _jestMatcherUtils.printExpected)(expected)}` + + ((0, _jestMatcherUtils.stringify)(expected) !== + (0, _jestMatcherUtils.stringify)(received) + ? `\nReceived: ${(0, _jestMatcherUtils.printReceived)( + received + )}` + : '') + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + (0, _jestMatcherUtils.printDiffOrStringify)( + expected, + (0, _utils.getObjectSubset)(received, expected), + EXPECTED_LABEL, + RECEIVED_LABEL, + isExpand(this.expand) + ); + return { + message, + pass + }; + }, + + toStrictEqual(received, expected) { + const matcherName = 'toStrictEqual'; + const options = { + comment: 'deep equality', + isNot: this.isNot, + promise: this.promise + }; + const pass = (0, _jasmineUtils.equals)( + received, + expected, + toStrictEqualTesters, + true + ); + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + `Expected: not ${(0, _jestMatcherUtils.printExpected)(expected)}\n` + + ((0, _jestMatcherUtils.stringify)(expected) !== + (0, _jestMatcherUtils.stringify)(received) + ? `Received: ${(0, _jestMatcherUtils.printReceived)(received)}` + : '') + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + (0, _jestMatcherUtils.printDiffOrStringify)( + expected, + received, + EXPECTED_LABEL, + RECEIVED_LABEL, + isExpand(this.expand) + ); // Passing the actual and expected objects so that a custom reporter + // could access them, for example in order to display a custom visual diff, + // or create a different error message + + return { + actual: received, + expected, + message, + name: matcherName, + pass + }; + } +}; +var _default = matchers; +exports.default = _default; diff --git a/packages/expect/build/print.d.ts b/packages/expect/build/print.d.ts new file mode 100644 index 000000000000..9c197516a0e3 --- /dev/null +++ b/packages/expect/build/print.d.ts @@ -0,0 +1,15 @@ +/** + * 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. + * + */ +export declare const printReceivedStringContainExpectedSubstring: (received: string, start: number, length: number) => string; +export declare const printReceivedStringContainExpectedResult: (received: string, result: RegExpExecArray | null) => string; +export declare const printReceivedArrayContainExpectedItem: (received: Array, index: number) => string; +export declare const printCloseTo: (receivedDiff: number, expectedDiff: number, precision: number, isNot: boolean) => string; +export declare const printExpectedConstructorName: (label: string, expected: Function) => string; +export declare const printExpectedConstructorNameNot: (label: string, expected: Function) => string; +export declare const printReceivedConstructorName: (label: string, received: Function) => string; +export declare const printReceivedConstructorNameNot: (label: string, received: Function, expected: Function) => string; diff --git a/packages/expect/build/print.js b/packages/expect/build/print.js new file mode 100644 index 000000000000..4312de4a4006 --- /dev/null +++ b/packages/expect/build/print.js @@ -0,0 +1,126 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.printReceivedConstructorNameNot = exports.printReceivedConstructorName = exports.printExpectedConstructorNameNot = exports.printExpectedConstructorName = exports.printCloseTo = exports.printReceivedArrayContainExpectedItem = exports.printReceivedStringContainExpectedResult = exports.printReceivedStringContainExpectedSubstring = void 0; + +var _jestMatcherUtils = require('jest-matcher-utils'); + +/** + * 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. + * + */ + +/* eslint-disable local/ban-types-eventually */ +// Format substring but do not enclose in double quote marks. +// The replacement is compatible with pretty-format package. +const printSubstring = val => val.replace(/"|\\/g, '\\$&'); + +const printReceivedStringContainExpectedSubstring = (received, start, length) => + (0, _jestMatcherUtils.RECEIVED_COLOR)( + '"' + + printSubstring(received.slice(0, start)) + + (0, _jestMatcherUtils.INVERTED_COLOR)( + printSubstring(received.slice(start, start + length)) + ) + + printSubstring(received.slice(start + length)) + + '"' + ); + +exports.printReceivedStringContainExpectedSubstring = printReceivedStringContainExpectedSubstring; + +const printReceivedStringContainExpectedResult = (received, result) => + result === null + ? (0, _jestMatcherUtils.printReceived)(received) + : printReceivedStringContainExpectedSubstring( + received, + result.index, + result[0].length + ); // The serialized array is compatible with pretty-format package min option. +// However, items have default stringify depth (instead of depth - 1) +// so expected item looks consistent by itself and enclosed in the array. + +exports.printReceivedStringContainExpectedResult = printReceivedStringContainExpectedResult; + +const printReceivedArrayContainExpectedItem = (received, index) => + (0, _jestMatcherUtils.RECEIVED_COLOR)( + '[' + + received + .map((item, i) => { + const stringified = (0, _jestMatcherUtils.stringify)(item); + return i === index + ? (0, _jestMatcherUtils.INVERTED_COLOR)(stringified) + : stringified; + }) + .join(', ') + + ']' + ); + +exports.printReceivedArrayContainExpectedItem = printReceivedArrayContainExpectedItem; + +const printCloseTo = (receivedDiff, expectedDiff, precision, isNot) => { + const receivedDiffString = (0, _jestMatcherUtils.stringify)(receivedDiff); + const expectedDiffString = receivedDiffString.includes('e') // toExponential arg is number of digits after the decimal point. + ? expectedDiff.toExponential(0) + : 0 <= precision && precision < 20 // toFixed arg is number of digits after the decimal point. + ? // It may be a value between 0 and 20 inclusive. + // Implementations may optionally support a larger range of values. + expectedDiff.toFixed(precision + 1) + : (0, _jestMatcherUtils.stringify)(expectedDiff); + return ( + `Expected precision: ${isNot ? ' ' : ''} ${(0, + _jestMatcherUtils.stringify)(precision)}\n` + + `Expected difference: ${isNot ? 'not ' : ''}< ${(0, + _jestMatcherUtils.EXPECTED_COLOR)(expectedDiffString)}\n` + + `Received difference: ${isNot ? ' ' : ''} ${(0, + _jestMatcherUtils.RECEIVED_COLOR)(receivedDiffString)}` + ); +}; + +exports.printCloseTo = printCloseTo; + +const printExpectedConstructorName = (label, expected) => + printConstructorName(label, expected, false, true) + '\n'; + +exports.printExpectedConstructorName = printExpectedConstructorName; + +const printExpectedConstructorNameNot = (label, expected) => + printConstructorName(label, expected, true, true) + '\n'; + +exports.printExpectedConstructorNameNot = printExpectedConstructorNameNot; + +const printReceivedConstructorName = (label, received) => + printConstructorName(label, received, false, false) + '\n'; // Do not call function if received is equal to expected. + +exports.printReceivedConstructorName = printReceivedConstructorName; + +const printReceivedConstructorNameNot = (label, received, expected) => + typeof expected.name === 'string' && + expected.name.length !== 0 && + typeof received.name === 'string' && + received.name.length !== 0 + ? printConstructorName(label, received, true, false) + + ` ${ + Object.getPrototypeOf(received) === expected + ? 'extends' + : 'extends … extends' + } ${(0, _jestMatcherUtils.EXPECTED_COLOR)(expected.name)}` + + '\n' + : printConstructorName(label, received, false, false) + '\n'; + +exports.printReceivedConstructorNameNot = printReceivedConstructorNameNot; + +const printConstructorName = (label, constructor, isNot, isExpected) => + typeof constructor.name !== 'string' + ? `${label} name is not a string` + : constructor.name.length === 0 + ? `${label} name is an empty string` + : `${label}: ${!isNot ? '' : isExpected ? 'not ' : ' '}${ + isExpected + ? (0, _jestMatcherUtils.EXPECTED_COLOR)(constructor.name) + : (0, _jestMatcherUtils.RECEIVED_COLOR)(constructor.name) + }`; diff --git a/packages/expect/build/spyMatchers.d.ts b/packages/expect/build/spyMatchers.d.ts new file mode 100644 index 000000000000..433b443be466 --- /dev/null +++ b/packages/expect/build/spyMatchers.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { MatchersObject } from './types'; +declare const spyMatchers: MatchersObject; +export default spyMatchers; diff --git a/packages/expect/build/spyMatchers.js b/packages/expect/build/spyMatchers.js new file mode 100644 index 000000000000..e7a538f8c6a0 --- /dev/null +++ b/packages/expect/build/spyMatchers.js @@ -0,0 +1,1343 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _jestGetType = _interopRequireDefault(require('jest-get-type')); + +var _jestMatcherUtils = require('jest-matcher-utils'); + +var _jasmineUtils = require('./jasmineUtils'); + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +// The optional property of matcher context is true if undefined. +const isExpand = expand => expand !== false; + +const PRINT_LIMIT = 3; +const NO_ARGUMENTS = 'called with 0 arguments'; + +const printExpectedArgs = expected => + expected.length === 0 + ? NO_ARGUMENTS + : expected.map(arg => (0, _jestMatcherUtils.printExpected)(arg)).join(', '); + +const printReceivedArgs = (received, expected) => + received.length === 0 + ? NO_ARGUMENTS + : received + .map((arg, i) => + Array.isArray(expected) && + i < expected.length && + isEqualValue(expected[i], arg) + ? printCommon(arg) + : (0, _jestMatcherUtils.printReceived)(arg) + ) + .join(', '); + +const printCommon = val => + (0, _jestMatcherUtils.DIM_COLOR)((0, _jestMatcherUtils.stringify)(val)); + +const isEqualValue = (expected, received) => + (0, _jasmineUtils.equals)(expected, received, [_utils.iterableEquality]); + +const isEqualCall = (expected, received) => isEqualValue(expected, received); + +const isEqualReturn = (expected, result) => + result.type === 'return' && isEqualValue(expected, result.value); + +const countReturns = results => + results.reduce((n, result) => (result.type === 'return' ? n + 1 : n), 0); + +const printNumberOfReturns = (countReturns, countCalls) => + `\nNumber of returns: ${(0, _jestMatcherUtils.printReceived)(countReturns)}` + + (countCalls !== countReturns + ? `\nNumber of calls: ${(0, _jestMatcherUtils.printReceived)(countCalls)}` + : ''); + +// Given a label, return a function which given a string, +// right-aligns it preceding the colon in the label. +const getRightAlignedPrinter = label => { + // Assume that the label contains a colon. + const index = label.indexOf(':'); + const suffix = label.slice(index); + return (string, isExpectedCall) => + (isExpectedCall + ? '->' + ' '.repeat(Math.max(0, index - 2 - string.length)) + : ' '.repeat(Math.max(index - string.length))) + + string + + suffix; +}; + +const printReceivedCallsNegative = ( + expected, + indexedCalls, + isOnlyCall, + iExpectedCall +) => { + if (indexedCalls.length === 0) { + return ''; + } + + const label = 'Received: '; + + if (isOnlyCall) { + return label + printReceivedArgs(indexedCalls[0], expected) + '\n'; + } + + const printAligned = getRightAlignedPrinter(label); + return ( + 'Received\n' + + indexedCalls.reduce( + (printed, [i, args]) => + printed + + printAligned(String(i + 1), i === iExpectedCall) + + printReceivedArgs(args, expected) + + '\n', + '' + ) + ); +}; + +const printExpectedReceivedCallsPositive = ( + expected, + indexedCalls, + expand, + isOnlyCall, + iExpectedCall +) => { + const expectedLine = `Expected: ${printExpectedArgs(expected)}\n`; + + if (indexedCalls.length === 0) { + return expectedLine; + } + + const label = 'Received: '; + + if (isOnlyCall && (iExpectedCall === 0 || iExpectedCall === undefined)) { + const received = indexedCalls[0][1]; + + if (isLineDiffableCall(expected, received)) { + // Display diff without indentation. + const lines = [ + (0, _jestMatcherUtils.EXPECTED_COLOR)('- Expected'), + (0, _jestMatcherUtils.RECEIVED_COLOR)('+ Received'), + '' + ]; + const length = Math.max(expected.length, received.length); + + for (let i = 0; i < length; i += 1) { + if (i < expected.length && i < received.length) { + if (isEqualValue(expected[i], received[i])) { + lines.push(` ${printCommon(received[i])},`); + continue; + } + + if (isLineDiffableArg(expected[i], received[i])) { + const difference = (0, _jestMatcherUtils.diff)( + expected[i], + received[i], + { + expand + } + ); + + if ( + typeof difference === 'string' && + difference.includes('- Expected') && + difference.includes('+ Received') + ) { + // Omit annotation in case multiple args have diff. + lines.push(difference.split('\n').slice(3).join('\n') + ','); + continue; + } + } + } + + if (i < expected.length) { + lines.push( + (0, _jestMatcherUtils.EXPECTED_COLOR)( + '- ' + (0, _jestMatcherUtils.stringify)(expected[i]) + ) + ',' + ); + } + + if (i < received.length) { + lines.push( + (0, _jestMatcherUtils.RECEIVED_COLOR)( + '+ ' + (0, _jestMatcherUtils.stringify)(received[i]) + ) + ',' + ); + } + } + + return lines.join('\n') + '\n'; + } + + return expectedLine + label + printReceivedArgs(received, expected) + '\n'; + } + + const printAligned = getRightAlignedPrinter(label); + return ( + expectedLine + + 'Received\n' + + indexedCalls.reduce((printed, [i, received]) => { + const aligned = printAligned(String(i + 1), i === iExpectedCall); + return ( + printed + + ((i === iExpectedCall || iExpectedCall === undefined) && + isLineDiffableCall(expected, received) + ? aligned.replace(': ', '\n') + + printDiffCall(expected, received, expand) + : aligned + printReceivedArgs(received, expected)) + + '\n' + ); + }, '') + ); +}; + +const indentation = 'Received'.replace(/\w/g, ' '); + +const printDiffCall = (expected, received, expand) => + received + .map((arg, i) => { + if (i < expected.length) { + if (isEqualValue(expected[i], arg)) { + return indentation + ' ' + printCommon(arg) + ','; + } + + if (isLineDiffableArg(expected[i], arg)) { + const difference = (0, _jestMatcherUtils.diff)(expected[i], arg, { + expand + }); + + if ( + typeof difference === 'string' && + difference.includes('- Expected') && + difference.includes('+ Received') + ) { + // Display diff with indentation. + // Omit annotation in case multiple args have diff. + return ( + difference + .split('\n') + .slice(3) + .map(line => indentation + line) + .join('\n') + ',' + ); + } + } + } // Display + only if received arg has no corresponding expected arg. + + return ( + indentation + + (i < expected.length + ? ' ' + (0, _jestMatcherUtils.printReceived)(arg) + : (0, _jestMatcherUtils.RECEIVED_COLOR)( + '+ ' + (0, _jestMatcherUtils.stringify)(arg) + )) + + ',' + ); + }) + .join('\n'); + +const isLineDiffableCall = (expected, received) => + expected.some( + (arg, i) => i < received.length && isLineDiffableArg(arg, received[i]) + ); // Almost redundant with function in jest-matcher-utils, +// except no line diff for any strings. + +const isLineDiffableArg = (expected, received) => { + const expectedType = (0, _jestGetType.default)(expected); + const receivedType = (0, _jestGetType.default)(received); + + if (expectedType !== receivedType) { + return false; + } + + if (_jestGetType.default.isPrimitive(expected)) { + return false; + } + + if ( + expectedType === 'date' || + expectedType === 'function' || + expectedType === 'regexp' + ) { + return false; + } + + if (expected instanceof Error && received instanceof Error) { + return false; + } + + if ( + expectedType === 'object' && + typeof expected.asymmetricMatch === 'function' + ) { + return false; + } + + if ( + receivedType === 'object' && + typeof received.asymmetricMatch === 'function' + ) { + return false; + } + + return true; +}; + +const printResult = (result, expected) => + result.type === 'throw' + ? 'function call threw an error' + : result.type === 'incomplete' + ? 'function call has not returned yet' + : isEqualValue(expected, result.value) + ? printCommon(result.value) + : (0, _jestMatcherUtils.printReceived)(result.value); + +// Return either empty string or one line per indexed result, +// so additional empty line can separate from `Number of returns` which follows. +const printReceivedResults = ( + label, + expected, + indexedResults, + isOnlyCall, + iExpectedCall +) => { + if (indexedResults.length === 0) { + return ''; + } + + if (isOnlyCall && (iExpectedCall === 0 || iExpectedCall === undefined)) { + return label + printResult(indexedResults[0][1], expected) + '\n'; + } + + const printAligned = getRightAlignedPrinter(label); + return ( + label.replace(':', '').trim() + + '\n' + + indexedResults.reduce( + (printed, [i, result]) => + printed + + printAligned(String(i + 1), i === iExpectedCall) + + printResult(result, expected) + + '\n', + '' + ) + ); +}; + +const createToBeCalledMatcher = matcherName => + function (received, expected) { + const expectedArgument = ''; + const options = { + isNot: this.isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureNoExpected)(expected, matcherName, options); + ensureMockOrSpy(received, matcherName, expectedArgument, options); + const receivedIsSpy = isSpy(received); + const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); + const count = receivedIsSpy + ? received.calls.count() + : received.mock.calls.length; + const calls = receivedIsSpy + ? received.calls.all().map(x => x.args) + : received.mock.calls; + const pass = count > 0; + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `Expected number of calls: ${(0, _jestMatcherUtils.printExpected)( + 0 + )}\n` + + `Received number of calls: ${(0, _jestMatcherUtils.printReceived)( + count + )}\n\n` + + calls + .reduce((lines, args, i) => { + if (lines.length < PRINT_LIMIT) { + lines.push(`${i + 1}: ${printReceivedArgs(args)}`); + } + + return lines; + }, []) + .join('\n') + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `Expected number of calls: >= ${(0, _jestMatcherUtils.printExpected)( + 1 + )}\n` + + `Received number of calls: ${(0, _jestMatcherUtils.printReceived)( + count + )}`; + return { + message, + pass + }; + }; + +const createToReturnMatcher = matcherName => + function (received, expected) { + const expectedArgument = ''; + const options = { + isNot: this.isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureNoExpected)(expected, matcherName, options); + ensureMock(received, matcherName, expectedArgument, options); + const receivedName = received.getMockName(); // Count return values that correspond only to calls that returned + + const count = received.mock.results.reduce( + (n, result) => (result.type === 'return' ? n + 1 : n), + 0 + ); + const pass = count > 0; + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `Expected number of returns: ${(0, _jestMatcherUtils.printExpected)( + 0 + )}\n` + + `Received number of returns: ${(0, _jestMatcherUtils.printReceived)( + count + )}\n\n` + + received.mock.results + .reduce((lines, result, i) => { + if (result.type === 'return' && lines.length < PRINT_LIMIT) { + lines.push( + `${i + 1}: ${(0, _jestMatcherUtils.printReceived)( + result.value + )}` + ); + } + + return lines; + }, []) + .join('\n') + + (received.mock.calls.length !== count + ? `\n\nReceived number of calls: ${(0, + _jestMatcherUtils.printReceived)(received.mock.calls.length)}` + : '') + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `Expected number of returns: >= ${(0, + _jestMatcherUtils.printExpected)(1)}\n` + + `Received number of returns: ${(0, + _jestMatcherUtils.printReceived)(count)}` + + (received.mock.calls.length !== count + ? `\nReceived number of calls: ${(0, + _jestMatcherUtils.printReceived)(received.mock.calls.length)}` + : ''); + return { + message, + pass + }; + }; + +const createToBeCalledTimesMatcher = matcherName => + function (received, expected) { + const expectedArgument = 'expected'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureExpectedIsNonNegativeInteger)( + expected, + matcherName, + options + ); + ensureMockOrSpy(received, matcherName, expectedArgument, options); + const receivedIsSpy = isSpy(received); + const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); + const count = receivedIsSpy + ? received.calls.count() + : received.mock.calls.length; + const pass = count === expected; + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + `\n\n` + + `Expected number of calls: not ${(0, _jestMatcherUtils.printExpected)( + expected + )}` + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `Expected number of calls: ${(0, _jestMatcherUtils.printExpected)( + expected + )}\n` + + `Received number of calls: ${(0, _jestMatcherUtils.printReceived)( + count + )}`; + return { + message, + pass + }; + }; + +const createToReturnTimesMatcher = matcherName => + function (received, expected) { + const expectedArgument = 'expected'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + (0, _jestMatcherUtils.ensureExpectedIsNonNegativeInteger)( + expected, + matcherName, + options + ); + ensureMock(received, matcherName, expectedArgument, options); + const receivedName = received.getMockName(); // Count return values that correspond only to calls that returned + + const count = received.mock.results.reduce( + (n, result) => (result.type === 'return' ? n + 1 : n), + 0 + ); + const pass = count === expected; + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + `\n\n` + + `Expected number of returns: not ${(0, + _jestMatcherUtils.printExpected)(expected)}` + + (received.mock.calls.length !== count + ? `\n\nReceived number of calls: ${(0, + _jestMatcherUtils.printReceived)(received.mock.calls.length)}` + : '') + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `Expected number of returns: ${(0, _jestMatcherUtils.printExpected)( + expected + )}\n` + + `Received number of returns: ${(0, _jestMatcherUtils.printReceived)( + count + )}` + + (received.mock.calls.length !== count + ? `\nReceived number of calls: ${(0, + _jestMatcherUtils.printReceived)(received.mock.calls.length)}` + : ''); + return { + message, + pass + }; + }; + +const createToBeCalledWithMatcher = matcherName => + function (received, ...expected) { + const expectedArgument = '...expected'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + ensureMockOrSpy(received, matcherName, expectedArgument, options); + const receivedIsSpy = isSpy(received); + const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); + const calls = receivedIsSpy + ? received.calls.all().map(x => x.args) + : received.mock.calls; + const pass = calls.some(call => isEqualCall(expected, call)); + const message = pass + ? () => { + // Some examples of calls that are equal to expected value. + const indexedCalls = []; + let i = 0; + + while (i < calls.length && indexedCalls.length < PRINT_LIMIT) { + if (isEqualCall(expected, calls[i])) { + indexedCalls.push([i, calls[i]]); + } + + i += 1; + } + + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `Expected: not ${printExpectedArgs(expected)}\n` + + (calls.length === 1 && + (0, _jestMatcherUtils.stringify)(calls[0]) === + (0, _jestMatcherUtils.stringify)(expected) + ? '' + : printReceivedCallsNegative( + expected, + indexedCalls, + calls.length === 1 + )) + + `\nNumber of calls: ${(0, _jestMatcherUtils.printReceived)( + calls.length + )}` + ); + } + : () => { + // Some examples of calls that are not equal to expected value. + const indexedCalls = []; + let i = 0; + + while (i < calls.length && indexedCalls.length < PRINT_LIMIT) { + indexedCalls.push([i, calls[i]]); + i += 1; + } + + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + printExpectedReceivedCallsPositive( + expected, + indexedCalls, + isExpand(this.expand), + calls.length === 1 + ) + + `\nNumber of calls: ${(0, _jestMatcherUtils.printReceived)( + calls.length + )}` + ); + }; + return { + message, + pass + }; + }; + +const createToReturnWithMatcher = matcherName => + function (received, expected) { + const expectedArgument = 'expected'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + ensureMock(received, matcherName, expectedArgument, options); + const receivedName = received.getMockName(); + const {calls, results} = received.mock; + const pass = results.some(result => isEqualReturn(expected, result)); + const message = pass + ? () => { + // Some examples of results that are equal to expected value. + const indexedResults = []; + let i = 0; + + while (i < results.length && indexedResults.length < PRINT_LIMIT) { + if (isEqualReturn(expected, results[i])) { + indexedResults.push([i, results[i]]); + } + + i += 1; + } + + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `Expected: not ${(0, _jestMatcherUtils.printExpected)( + expected + )}\n` + + (results.length === 1 && + results[0].type === 'return' && + (0, _jestMatcherUtils.stringify)(results[0].value) === + (0, _jestMatcherUtils.stringify)(expected) + ? '' + : printReceivedResults( + 'Received: ', + expected, + indexedResults, + results.length === 1 + )) + + printNumberOfReturns(countReturns(results), calls.length) + ); + } + : () => { + // Some examples of results that are not equal to expected value. + const indexedResults = []; + let i = 0; + + while (i < results.length && indexedResults.length < PRINT_LIMIT) { + indexedResults.push([i, results[i]]); + i += 1; + } + + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `Expected: ${(0, _jestMatcherUtils.printExpected)(expected)}\n` + + printReceivedResults( + 'Received: ', + expected, + indexedResults, + results.length === 1 + ) + + printNumberOfReturns(countReturns(results), calls.length) + ); + }; + return { + message, + pass + }; + }; + +const createLastCalledWithMatcher = matcherName => + function (received, ...expected) { + const expectedArgument = '...expected'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + ensureMockOrSpy(received, matcherName, expectedArgument, options); + const receivedIsSpy = isSpy(received); + const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); + const calls = receivedIsSpy + ? received.calls.all().map(x => x.args) + : received.mock.calls; + const iLast = calls.length - 1; + const pass = iLast >= 0 && isEqualCall(expected, calls[iLast]); + const message = pass + ? () => { + const indexedCalls = []; + + if (iLast > 0) { + // Display preceding call as context. + indexedCalls.push([iLast - 1, calls[iLast - 1]]); + } + + indexedCalls.push([iLast, calls[iLast]]); + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `Expected: not ${printExpectedArgs(expected)}\n` + + (calls.length === 1 && + (0, _jestMatcherUtils.stringify)(calls[0]) === + (0, _jestMatcherUtils.stringify)(expected) + ? '' + : printReceivedCallsNegative( + expected, + indexedCalls, + calls.length === 1, + iLast + )) + + `\nNumber of calls: ${(0, _jestMatcherUtils.printReceived)( + calls.length + )}` + ); + } + : () => { + const indexedCalls = []; + + if (iLast >= 0) { + if (iLast > 0) { + let i = iLast - 1; // Is there a preceding call that is equal to expected args? + + while (i >= 0 && !isEqualCall(expected, calls[i])) { + i -= 1; + } + + if (i < 0) { + i = iLast - 1; // otherwise, preceding call + } + + indexedCalls.push([i, calls[i]]); + } + + indexedCalls.push([iLast, calls[iLast]]); + } + + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + printExpectedReceivedCallsPositive( + expected, + indexedCalls, + isExpand(this.expand), + calls.length === 1, + iLast + ) + + `\nNumber of calls: ${(0, _jestMatcherUtils.printReceived)( + calls.length + )}` + ); + }; + return { + message, + pass + }; + }; + +const createLastReturnedMatcher = matcherName => + function (received, expected) { + const expectedArgument = 'expected'; + const options = { + isNot: this.isNot, + promise: this.promise + }; + ensureMock(received, matcherName, expectedArgument, options); + const receivedName = received.getMockName(); + const {calls, results} = received.mock; + const iLast = results.length - 1; + const pass = iLast >= 0 && isEqualReturn(expected, results[iLast]); + const message = pass + ? () => { + const indexedResults = []; + + if (iLast > 0) { + // Display preceding result as context. + indexedResults.push([iLast - 1, results[iLast - 1]]); + } + + indexedResults.push([iLast, results[iLast]]); + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `Expected: not ${(0, _jestMatcherUtils.printExpected)( + expected + )}\n` + + (results.length === 1 && + results[0].type === 'return' && + (0, _jestMatcherUtils.stringify)(results[0].value) === + (0, _jestMatcherUtils.stringify)(expected) + ? '' + : printReceivedResults( + 'Received: ', + expected, + indexedResults, + results.length === 1, + iLast + )) + + printNumberOfReturns(countReturns(results), calls.length) + ); + } + : () => { + const indexedResults = []; + + if (iLast >= 0) { + if (iLast > 0) { + let i = iLast - 1; // Is there a preceding result that is equal to expected value? + + while (i >= 0 && !isEqualReturn(expected, results[i])) { + i -= 1; + } + + if (i < 0) { + i = iLast - 1; // otherwise, preceding result + } + + indexedResults.push([i, results[i]]); + } + + indexedResults.push([iLast, results[iLast]]); + } + + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `Expected: ${(0, _jestMatcherUtils.printExpected)(expected)}\n` + + printReceivedResults( + 'Received: ', + expected, + indexedResults, + results.length === 1, + iLast + ) + + printNumberOfReturns(countReturns(results), calls.length) + ); + }; + return { + message, + pass + }; + }; + +const createNthCalledWithMatcher = matcherName => + function (received, nth, ...expected) { + const expectedArgument = 'n'; + const options = { + expectedColor: arg => arg, + isNot: this.isNot, + promise: this.promise, + secondArgument: '...expected' + }; + ensureMockOrSpy(received, matcherName, expectedArgument, options); + + if (!Number.isSafeInteger(nth) || nth < 1) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + expectedArgument, + options + ), + `${expectedArgument} must be a positive integer`, + (0, _jestMatcherUtils.printWithType)( + expectedArgument, + nth, + _jestMatcherUtils.stringify + ) + ) + ); + } + + const receivedIsSpy = isSpy(received); + const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); + const calls = receivedIsSpy + ? received.calls.all().map(x => x.args) + : received.mock.calls; + const length = calls.length; + const iNth = nth - 1; + const pass = iNth < length && isEqualCall(expected, calls[iNth]); + const message = pass + ? () => { + // Display preceding and following calls, + // in case assertions fails because index is off by one. + const indexedCalls = []; + + if (iNth - 1 >= 0) { + indexedCalls.push([iNth - 1, calls[iNth - 1]]); + } + + indexedCalls.push([iNth, calls[iNth]]); + + if (iNth + 1 < length) { + indexedCalls.push([iNth + 1, calls[iNth + 1]]); + } + + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `n: ${nth}\n` + + `Expected: not ${printExpectedArgs(expected)}\n` + + (calls.length === 1 && + (0, _jestMatcherUtils.stringify)(calls[0]) === + (0, _jestMatcherUtils.stringify)(expected) + ? '' + : printReceivedCallsNegative( + expected, + indexedCalls, + calls.length === 1, + iNth + )) + + `\nNumber of calls: ${(0, _jestMatcherUtils.printReceived)( + calls.length + )}` + ); + } + : () => { + // Display preceding and following calls: + // * nearest call that is equal to expected args + // * otherwise, adjacent call + // in case assertions fails because of index, especially off by one. + const indexedCalls = []; + + if (iNth < length) { + if (iNth - 1 >= 0) { + let i = iNth - 1; // Is there a preceding call that is equal to expected args? + + while (i >= 0 && !isEqualCall(expected, calls[i])) { + i -= 1; + } + + if (i < 0) { + i = iNth - 1; // otherwise, adjacent call + } + + indexedCalls.push([i, calls[i]]); + } + + indexedCalls.push([iNth, calls[iNth]]); + + if (iNth + 1 < length) { + let i = iNth + 1; // Is there a following call that is equal to expected args? + + while (i < length && !isEqualCall(expected, calls[i])) { + i += 1; + } + + if (i >= length) { + i = iNth + 1; // otherwise, adjacent call + } + + indexedCalls.push([i, calls[i]]); + } + } else if (length > 0) { + // The number of received calls is fewer than the expected number. + let i = length - 1; // Is there a call that is equal to expected args? + + while (i >= 0 && !isEqualCall(expected, calls[i])) { + i -= 1; + } + + if (i < 0) { + i = length - 1; // otherwise, last call + } + + indexedCalls.push([i, calls[i]]); + } + + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `n: ${nth}\n` + + printExpectedReceivedCallsPositive( + expected, + indexedCalls, + isExpand(this.expand), + calls.length === 1, + iNth + ) + + `\nNumber of calls: ${(0, _jestMatcherUtils.printReceived)( + calls.length + )}` + ); + }; + return { + message, + pass + }; + }; + +const createNthReturnedWithMatcher = matcherName => + function (received, nth, expected) { + const expectedArgument = 'n'; + const options = { + expectedColor: arg => arg, + isNot: this.isNot, + promise: this.promise, + secondArgument: 'expected' + }; + ensureMock(received, matcherName, expectedArgument, options); + + if (!Number.isSafeInteger(nth) || nth < 1) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + expectedArgument, + options + ), + `${expectedArgument} must be a positive integer`, + (0, _jestMatcherUtils.printWithType)( + expectedArgument, + nth, + _jestMatcherUtils.stringify + ) + ) + ); + } + + const receivedName = received.getMockName(); + const {calls, results} = received.mock; + const length = results.length; + const iNth = nth - 1; + const pass = iNth < length && isEqualReturn(expected, results[iNth]); + const message = pass + ? () => { + // Display preceding and following results, + // in case assertions fails because index is off by one. + const indexedResults = []; + + if (iNth - 1 >= 0) { + indexedResults.push([iNth - 1, results[iNth - 1]]); + } + + indexedResults.push([iNth, results[iNth]]); + + if (iNth + 1 < length) { + indexedResults.push([iNth + 1, results[iNth + 1]]); + } + + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `n: ${nth}\n` + + `Expected: not ${(0, _jestMatcherUtils.printExpected)( + expected + )}\n` + + (results.length === 1 && + results[0].type === 'return' && + (0, _jestMatcherUtils.stringify)(results[0].value) === + (0, _jestMatcherUtils.stringify)(expected) + ? '' + : printReceivedResults( + 'Received: ', + expected, + indexedResults, + results.length === 1, + iNth + )) + + printNumberOfReturns(countReturns(results), calls.length) + ); + } + : () => { + // Display preceding and following results: + // * nearest result that is equal to expected value + // * otherwise, adjacent result + // in case assertions fails because of index, especially off by one. + const indexedResults = []; + + if (iNth < length) { + if (iNth - 1 >= 0) { + let i = iNth - 1; // Is there a preceding result that is equal to expected value? + + while (i >= 0 && !isEqualReturn(expected, results[i])) { + i -= 1; + } + + if (i < 0) { + i = iNth - 1; // otherwise, adjacent result + } + + indexedResults.push([i, results[i]]); + } + + indexedResults.push([iNth, results[iNth]]); + + if (iNth + 1 < length) { + let i = iNth + 1; // Is there a following result that is equal to expected value? + + while (i < length && !isEqualReturn(expected, results[i])) { + i += 1; + } + + if (i >= length) { + i = iNth + 1; // otherwise, adjacent result + } + + indexedResults.push([i, results[i]]); + } + } else if (length > 0) { + // The number of received calls is fewer than the expected number. + let i = length - 1; // Is there a result that is equal to expected value? + + while (i >= 0 && !isEqualReturn(expected, results[i])) { + i -= 1; + } + + if (i < 0) { + i = length - 1; // otherwise, last result + } + + indexedResults.push([i, results[i]]); + } + + return ( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + receivedName, + expectedArgument, + options + ) + + '\n\n' + + `n: ${nth}\n` + + `Expected: ${(0, _jestMatcherUtils.printExpected)(expected)}\n` + + printReceivedResults( + 'Received: ', + expected, + indexedResults, + results.length === 1, + iNth + ) + + printNumberOfReturns(countReturns(results), calls.length) + ); + }; + return { + message, + pass + }; + }; + +const spyMatchers = { + lastCalledWith: createLastCalledWithMatcher('lastCalledWith'), + lastReturnedWith: createLastReturnedMatcher('lastReturnedWith'), + nthCalledWith: createNthCalledWithMatcher('nthCalledWith'), + nthReturnedWith: createNthReturnedWithMatcher('nthReturnedWith'), + toBeCalled: createToBeCalledMatcher('toBeCalled'), + toBeCalledTimes: createToBeCalledTimesMatcher('toBeCalledTimes'), + toBeCalledWith: createToBeCalledWithMatcher('toBeCalledWith'), + toHaveBeenCalled: createToBeCalledMatcher('toHaveBeenCalled'), + toHaveBeenCalledTimes: createToBeCalledTimesMatcher('toHaveBeenCalledTimes'), + toHaveBeenCalledWith: createToBeCalledWithMatcher('toHaveBeenCalledWith'), + toHaveBeenLastCalledWith: createLastCalledWithMatcher( + 'toHaveBeenLastCalledWith' + ), + toHaveBeenNthCalledWith: createNthCalledWithMatcher( + 'toHaveBeenNthCalledWith' + ), + toHaveLastReturnedWith: createLastReturnedMatcher('toHaveLastReturnedWith'), + toHaveNthReturnedWith: createNthReturnedWithMatcher('toHaveNthReturnedWith'), + toHaveReturned: createToReturnMatcher('toHaveReturned'), + toHaveReturnedTimes: createToReturnTimesMatcher('toHaveReturnedTimes'), + toHaveReturnedWith: createToReturnWithMatcher('toHaveReturnedWith'), + toReturn: createToReturnMatcher('toReturn'), + toReturnTimes: createToReturnTimesMatcher('toReturnTimes'), + toReturnWith: createToReturnWithMatcher('toReturnWith') +}; + +const isMock = received => + received != null && received._isMockFunction === true; + +const isSpy = received => + received != null && + received.calls != null && + typeof received.calls.all === 'function' && + typeof received.calls.count === 'function'; + +const ensureMockOrSpy = (received, matcherName, expectedArgument, options) => { + if (!isMock(received) && !isSpy(received)) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + expectedArgument, + options + ), + `${(0, _jestMatcherUtils.RECEIVED_COLOR)( + 'received' + )} value must be a mock or spy function`, + (0, _jestMatcherUtils.printWithType)( + 'Received', + received, + _jestMatcherUtils.printReceived + ) + ) + ); + } +}; + +const ensureMock = (received, matcherName, expectedArgument, options) => { + if (!isMock(received)) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + expectedArgument, + options + ), + `${(0, _jestMatcherUtils.RECEIVED_COLOR)( + 'received' + )} value must be a mock function`, + (0, _jestMatcherUtils.printWithType)( + 'Received', + received, + _jestMatcherUtils.printReceived + ) + ) + ); + } +}; + +var _default = spyMatchers; +exports.default = _default; diff --git a/packages/expect/build/toThrowMatchers.d.ts b/packages/expect/build/toThrowMatchers.d.ts new file mode 100644 index 000000000000..d4967c023b71 --- /dev/null +++ b/packages/expect/build/toThrowMatchers.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + * + */ +import type { MatchersObject, RawMatcherFn } from './types'; +export declare const createMatcher: (matcherName: string, fromPromise?: boolean | undefined) => RawMatcherFn; +declare const matchers: MatchersObject; +export default matchers; diff --git a/packages/expect/build/toThrowMatchers.js b/packages/expect/build/toThrowMatchers.js new file mode 100644 index 000000000000..f34f2363dcd2 --- /dev/null +++ b/packages/expect/build/toThrowMatchers.js @@ -0,0 +1,464 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = exports.createMatcher = void 0; + +var _jestMatcherUtils = require('jest-matcher-utils'); + +var _jestMessageUtil = require('jest-message-util'); + +var _print = require('./print'); + +var _utils = require('./utils'); + +/** + * 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. + * + */ + +/* eslint-disable local/ban-types-eventually */ +const DID_NOT_THROW = 'Received function did not throw'; + +const getThrown = e => { + const hasMessage = + e !== null && e !== undefined && typeof e.message === 'string'; + + if (hasMessage && typeof e.name === 'string' && typeof e.stack === 'string') { + return { + hasMessage, + isError: true, + message: e.message, + value: e + }; + } + + return { + hasMessage, + isError: false, + message: hasMessage ? e.message : String(e), + value: e + }; +}; + +const createMatcher = (matcherName, fromPromise) => + function (received, expected) { + const options = { + isNot: this.isNot, + promise: this.promise + }; + let thrown = null; + + if (fromPromise && (0, _utils.isError)(received)) { + thrown = getThrown(received); + } else { + if (typeof received !== 'function') { + if (!fromPromise) { + const placeholder = expected === undefined ? '' : 'expected'; + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + placeholder, + options + ), + `${(0, _jestMatcherUtils.RECEIVED_COLOR)( + 'received' + )} value must be a function`, + (0, _jestMatcherUtils.printWithType)( + 'Received', + received, + _jestMatcherUtils.printReceived + ) + ) + ); + } + } else { + try { + received(); + } catch (e) { + thrown = getThrown(e); + } + } + } + + if (expected === undefined) { + return toThrow(matcherName, options, thrown); + } else if (typeof expected === 'function') { + return toThrowExpectedClass(matcherName, options, thrown, expected); + } else if (typeof expected === 'string') { + return toThrowExpectedString(matcherName, options, thrown, expected); + } else if (expected !== null && typeof expected.test === 'function') { + return toThrowExpectedRegExp(matcherName, options, thrown, expected); + } else if ( + expected !== null && + typeof expected.asymmetricMatch === 'function' + ) { + return toThrowExpectedAsymmetric(matcherName, options, thrown, expected); + } else if (expected !== null && typeof expected === 'object') { + return toThrowExpectedObject(matcherName, options, thrown, expected); + } else { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ), + `${(0, _jestMatcherUtils.EXPECTED_COLOR)( + 'expected' + )} value must be a string or regular expression or class or error`, + (0, _jestMatcherUtils.printWithType)( + 'Expected', + expected, + _jestMatcherUtils.printExpected + ) + ) + ); + } + }; + +exports.createMatcher = createMatcher; +const matchers = { + toThrow: createMatcher('toThrow'), + toThrowError: createMatcher('toThrowError') +}; + +const toThrowExpectedRegExp = (matcherName, options, thrown, expected) => { + const pass = thrown !== null && expected.test(thrown.message); + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + formatExpected('Expected pattern: not ', expected) + + (thrown !== null && thrown.hasMessage + ? formatReceived( + 'Received message: ', + thrown, + 'message', + expected + ) + formatStack(thrown) + : formatReceived('Received value: ', thrown, 'value')) + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + formatExpected('Expected pattern: ', expected) + + (thrown === null + ? '\n' + DID_NOT_THROW + : thrown.hasMessage + ? formatReceived('Received message: ', thrown, 'message') + + formatStack(thrown) + : formatReceived('Received value: ', thrown, 'value')); + return { + message, + pass + }; +}; + +const toThrowExpectedAsymmetric = (matcherName, options, thrown, expected) => { + const pass = thrown !== null && expected.asymmetricMatch(thrown.value); + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + formatExpected('Expected asymmetric matcher: not ', expected) + + '\n' + + (thrown !== null && thrown.hasMessage + ? formatReceived('Received name: ', thrown, 'name') + + formatReceived('Received message: ', thrown, 'message') + + formatStack(thrown) + : formatReceived('Thrown value: ', thrown, 'value')) + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + formatExpected('Expected asymmetric matcher: ', expected) + + '\n' + + (thrown === null + ? DID_NOT_THROW + : thrown.hasMessage + ? formatReceived('Received name: ', thrown, 'name') + + formatReceived('Received message: ', thrown, 'message') + + formatStack(thrown) + : formatReceived('Thrown value: ', thrown, 'value')); + return { + message, + pass + }; +}; + +const toThrowExpectedObject = (matcherName, options, thrown, expected) => { + const pass = thrown !== null && thrown.message === expected.message; + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + formatExpected('Expected message: not ', expected.message) + + (thrown !== null && thrown.hasMessage + ? formatStack(thrown) + : formatReceived('Received value: ', thrown, 'value')) + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + (thrown === null + ? formatExpected('Expected message: ', expected.message) + + '\n' + + DID_NOT_THROW + : thrown.hasMessage + ? (0, _jestMatcherUtils.printDiffOrStringify)( + expected.message, + thrown.message, + 'Expected message', + 'Received message', + true + ) + + '\n' + + formatStack(thrown) + : formatExpected('Expected message: ', expected.message) + + formatReceived('Received value: ', thrown, 'value')); + return { + message, + pass + }; +}; + +const toThrowExpectedClass = (matcherName, options, thrown, expected) => { + const pass = thrown !== null && thrown.value instanceof expected; + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + (0, _print.printExpectedConstructorNameNot)( + 'Expected constructor', + expected + ) + + (thrown !== null && + thrown.value != null && + typeof thrown.value.constructor === 'function' && + thrown.value.constructor !== expected + ? (0, _print.printReceivedConstructorNameNot)( + 'Received constructor', + thrown.value.constructor, + expected + ) + : '') + + '\n' + + (thrown !== null && thrown.hasMessage + ? formatReceived('Received message: ', thrown, 'message') + + formatStack(thrown) + : formatReceived('Received value: ', thrown, 'value')) + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + (0, _print.printExpectedConstructorName)( + 'Expected constructor', + expected + ) + + (thrown === null + ? '\n' + DID_NOT_THROW + : (thrown.value != null && + typeof thrown.value.constructor === 'function' + ? (0, _print.printReceivedConstructorName)( + 'Received constructor', + thrown.value.constructor + ) + : '') + + '\n' + + (thrown.hasMessage + ? formatReceived('Received message: ', thrown, 'message') + + formatStack(thrown) + : formatReceived('Received value: ', thrown, 'value'))); + return { + message, + pass + }; +}; + +const toThrowExpectedString = (matcherName, options, thrown, expected) => { + const pass = thrown !== null && thrown.message.includes(expected); + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + formatExpected('Expected substring: not ', expected) + + (thrown !== null && thrown.hasMessage + ? formatReceived( + 'Received message: ', + thrown, + 'message', + expected + ) + formatStack(thrown) + : formatReceived('Received value: ', thrown, 'value')) + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + undefined, + options + ) + + '\n\n' + + formatExpected('Expected substring: ', expected) + + (thrown === null + ? '\n' + DID_NOT_THROW + : thrown.hasMessage + ? formatReceived('Received message: ', thrown, 'message') + + formatStack(thrown) + : formatReceived('Received value: ', thrown, 'value')); + return { + message, + pass + }; +}; + +const toThrow = (matcherName, options, thrown) => { + const pass = thrown !== null; + const message = pass + ? () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + '', + options + ) + + '\n\n' + + (thrown !== null && thrown.hasMessage + ? formatReceived('Error name: ', thrown, 'name') + + formatReceived('Error message: ', thrown, 'message') + + formatStack(thrown) + : formatReceived('Thrown value: ', thrown, 'value')) + : () => + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + '', + options + ) + + '\n\n' + + DID_NOT_THROW; + return { + message, + pass + }; +}; + +const formatExpected = (label, expected) => + label + (0, _jestMatcherUtils.printExpected)(expected) + '\n'; + +const formatReceived = (label, thrown, key, expected) => { + if (thrown === null) { + return ''; + } + + if (key === 'message') { + const message = thrown.message; + + if (typeof expected === 'string') { + const index = message.indexOf(expected); + + if (index !== -1) { + return ( + label + + (0, _print.printReceivedStringContainExpectedSubstring)( + message, + index, + expected.length + ) + + '\n' + ); + } + } else if (expected instanceof RegExp) { + return ( + label + + (0, _print.printReceivedStringContainExpectedResult)( + message, + typeof expected.exec === 'function' ? expected.exec(message) : null + ) + + '\n' + ); + } + + return label + (0, _jestMatcherUtils.printReceived)(message) + '\n'; + } + + if (key === 'name') { + return thrown.isError + ? label + (0, _jestMatcherUtils.printReceived)(thrown.value.name) + '\n' + : ''; + } + + if (key === 'value') { + return thrown.isError + ? '' + : label + (0, _jestMatcherUtils.printReceived)(thrown.value) + '\n'; + } + + return ''; +}; + +const formatStack = thrown => + thrown === null || !thrown.isError + ? '' + : (0, _jestMessageUtil.formatStackTrace)( + (0, _jestMessageUtil.separateMessageFromStack)(thrown.value.stack) + .stack, + { + rootDir: process.cwd(), + testMatch: [] + }, + { + noStackTrace: false + } + ); + +var _default = matchers; +exports.default = _default; diff --git a/packages/expect/build/types.d.ts b/packages/expect/build/types.d.ts new file mode 100644 index 000000000000..dd30632f2c4e --- /dev/null +++ b/packages/expect/build/types.d.ts @@ -0,0 +1,328 @@ +/** + * 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. + * + */ +import type { Config } from '@jest/types'; +import type * as jestMatcherUtils from 'jest-matcher-utils'; +import { INTERNAL_MATCHER_FLAG } from './jestMatchersObject'; +export declare type SyncExpectationResult = { + pass: boolean; + message: () => string; +}; +export declare type AsyncExpectationResult = Promise; +export declare type ExpectationResult = SyncExpectationResult | AsyncExpectationResult; +export declare type RawMatcherFn = { + (received: any, expected: any, options?: any): ExpectationResult; + [INTERNAL_MATCHER_FLAG]?: boolean; +}; +export declare type ThrowingMatcherFn = (actual: any) => void; +export declare type PromiseMatcherFn = (actual: any) => Promise; +export declare type Tester = (a: any, b: any) => boolean | undefined; +export declare type MatcherState = { + assertionCalls: number; + currentTestName?: string; + dontThrow?: () => void; + error?: Error; + equals: (a: unknown, b: unknown, customTesters?: Array, strictCheck?: boolean) => boolean; + expand?: boolean; + expectedAssertionsNumber?: number | null; + expectedAssertionsNumberError?: Error; + isExpectingAssertions?: boolean; + isExpectingAssertionsError?: Error; + isNot: boolean; + promise: string; + suppressedErrors: Array; + testPath?: Config.Path; + utils: typeof jestMatcherUtils & { + iterableEquality: Tester; + subsetEquality: Tester; + }; +}; +export declare type AsymmetricMatcher = Record; +export declare type MatchersObject = { + [id: string]: RawMatcherFn; +}; +export declare type ExpectedAssertionsErrors = Array<{ + actual: string | number; + error: Error; + expected: string; +}>; +export declare type Expect = { + (actual: T): Matchers; + addSnapshotSerializer(arg0: any): void; + assertions(arg0: number): void; + extend(arg0: any): void; + extractExpectedAssertionsErrors: () => ExpectedAssertionsErrors; + getState(): MatcherState; + hasAssertions(): void; + setState(state: Partial): void; + any(expectedObject: any): AsymmetricMatcher; + anything(): AsymmetricMatcher; + arrayContaining(sample: Array): AsymmetricMatcher; + objectContaining(sample: Record): AsymmetricMatcher; + stringContaining(expected: string): AsymmetricMatcher; + stringMatching(expected: string | RegExp): AsymmetricMatcher; + [id: string]: AsymmetricMatcher; + not: { + [id: string]: AsymmetricMatcher; + }; +}; +interface Constructable { + new (...args: Array): unknown; +} +export interface Matchers { + /** + * Ensures the last call to a mock function was provided specific args. + */ + lastCalledWith(...args: Array): R; + /** + * Ensure that the last call to a mock function has returned a specified value. + */ + lastReturnedWith(value: unknown): R; + /** + * If you know how to test something, `.not` lets you test its opposite. + */ + not: Matchers; + /** + * Ensure that a mock function is called with specific arguments on an Nth call. + */ + nthCalledWith(nthCall: number, ...args: Array): R; + /** + * Ensure that the nth call to a mock function has returned a specified value. + */ + nthReturnedWith(n: number, value: unknown): R; + /** + * Use resolves to unwrap the value of a fulfilled promise so any other + * matcher can be chained. If the promise is rejected the assertion fails. + */ + resolves: Matchers>; + /** + * Unwraps the reason of a rejected promise so any other matcher can be chained. + * If the promise is fulfilled the assertion fails. + */ + rejects: Matchers>; + /** + * Checks that a value is what you expect. It uses `===` to check strict equality. + * Don't use `toBe` with floating-point numbers. + */ + toBe(expected: unknown): R; + /** + * Ensures that a mock function is called. + */ + toBeCalled(): R; + /** + * Ensures that a mock function is called an exact number of times. + */ + toBeCalledTimes(expected: number): R; + /** + * Ensure that a mock function is called with specific arguments. + */ + toBeCalledWith(...args: Array): R; + /** + * Using exact equality with floating point numbers is a bad idea. + * Rounding means that intuitive things fail. + * The default for numDigits is 2. + */ + toBeCloseTo(expected: number, numDigits?: number): R; + /** + * Ensure that a variable is not undefined. + */ + toBeDefined(): R; + /** + * When you don't care what a value is, you just want to + * ensure a value is false in a boolean context. + */ + toBeFalsy(): R; + /** + * For comparing floating point numbers. + */ + toBeGreaterThan(expected: number | bigint): R; + /** + * For comparing floating point numbers. + */ + toBeGreaterThanOrEqual(expected: number | bigint): R; + /** + * Ensure that an object is an instance of a class. + * This matcher uses `instanceof` underneath. + */ + toBeInstanceOf(expected: Function): R; + /** + * For comparing floating point numbers. + */ + toBeLessThan(expected: number | bigint): R; + /** + * For comparing floating point numbers. + */ + toBeLessThanOrEqual(expected: number | bigint): R; + /** + * This is the same as `.toBe(null)` but the error messages are a bit nicer. + * So use `.toBeNull()` when you want to check that something is null. + */ + toBeNull(): R; + /** + * Use when you don't care what a value is, you just want to ensure a value + * is true in a boolean context. In JavaScript, there are six falsy values: + * `false`, `0`, `''`, `null`, `undefined`, and `NaN`. Everything else is truthy. + */ + toBeTruthy(): R; + /** + * Used to check that a variable is undefined. + */ + toBeUndefined(): R; + /** + * Used to check that a variable is NaN. + */ + toBeNaN(): R; + /** + * Used when you want to check that an item is in a list. + * For testing the items in the list, this uses `===`, a strict equality check. + */ + toContain(expected: unknown): R; + /** + * Used when you want to check that an item is in a list. + * For testing the items in the list, this matcher recursively checks the + * equality of all fields, rather than checking for object identity. + */ + toContainEqual(expected: unknown): R; + /** + * Used when you want to check that two objects have the same value. + * This matcher recursively checks the equality of all fields, rather than checking for object identity. + */ + toEqual(expected: unknown): R; + /** + * Ensures that a mock function is called. + */ + toHaveBeenCalled(): R; + /** + * Ensures that a mock function is called an exact number of times. + */ + toHaveBeenCalledTimes(expected: number): R; + /** + * Ensure that a mock function is called with specific arguments. + */ + toHaveBeenCalledWith(...args: Array): R; + /** + * Ensure that a mock function is called with specific arguments on an Nth call. + */ + toHaveBeenNthCalledWith(nthCall: number, ...args: Array): R; + /** + * If you have a mock function, you can use `.toHaveBeenLastCalledWith` + * to test what arguments it was last called with. + */ + toHaveBeenLastCalledWith(...args: Array): R; + /** + * Use to test the specific value that a mock function last returned. + * If the last call to the mock function threw an error, then this matcher will fail + * no matter what value you provided as the expected return value. + */ + toHaveLastReturnedWith(expected: unknown): R; + /** + * Used to check that an object has a `.length` property + * and it is set to a certain numeric value. + */ + toHaveLength(expected: number): R; + /** + * Use to test the specific value that a mock function returned for the nth call. + * If the nth call to the mock function threw an error, then this matcher will fail + * no matter what value you provided as the expected return value. + */ + toHaveNthReturnedWith(nthCall: number, expected: unknown): R; + /** + * Use to check if property at provided reference keyPath exists for an object. + * For checking deeply nested properties in an object you may use dot notation or an array containing + * the keyPath for deep references. + * + * Optionally, you can provide a value to check if it's equal to the value present at keyPath + * on the target object. This matcher uses 'deep equality' (like `toEqual()`) and recursively checks + * the equality of all fields. + * + * @example + * + * expect(houseForSale).toHaveProperty('kitchen.area', 20); + */ + toHaveProperty(keyPath: string | Array, value?: unknown): R; + /** + * Use to test that the mock function successfully returned (i.e., did not throw an error) at least one time + */ + toHaveReturned(): R; + /** + * Use to ensure that a mock function returned successfully (i.e., did not throw an error) an exact number of times. + * Any calls to the mock function that throw an error are not counted toward the number of times the function returned. + */ + toHaveReturnedTimes(expected: number): R; + /** + * Use to ensure that a mock function returned a specific value. + */ + toHaveReturnedWith(expected: unknown): R; + /** + * Check that a string matches a regular expression. + */ + toMatch(expected: string | RegExp): R; + /** + * Used to check that a JavaScript object matches a subset of the properties of an object + */ + toMatchObject(expected: Record | Array): R; + /** + * Ensure that a mock function has returned (as opposed to thrown) at least once. + */ + toReturn(): R; + /** + * Ensure that a mock function has returned (as opposed to thrown) a specified number of times. + */ + toReturnTimes(count: number): R; + /** + * Ensure that a mock function has returned a specified value at least once. + */ + toReturnWith(value: unknown): R; + /** + * Use to test that objects have the same types as well as structure. + */ + toStrictEqual(expected: unknown): R; + /** + * Used to test that a function throws when it is called. + */ + toThrow(error?: string | Constructable | RegExp | Error): R; + /** + * If you want to test that a specific error is thrown inside a function. + */ + toThrowError(error?: string | Constructable | RegExp | Error): R; + /** + * This ensures that a value matches the most recent snapshot with property matchers. + * Check out [the Snapshot Testing guide](https://jestjs.io/docs/en/snapshot-testing) for more information. + */ + toMatchSnapshot(propertyMatchers: Partial, snapshotName?: string): R; + /** + * This ensures that a value matches the most recent snapshot. + * Check out [the Snapshot Testing guide](https://jestjs.io/docs/en/snapshot-testing) for more information. + */ + toMatchSnapshot(snapshotName?: string): R; + /** + * This ensures that a value matches the most recent snapshot with property matchers. + * Instead of writing the snapshot value to a .snap file, it will be written into the source code automatically. + * Check out [the Snapshot Testing guide](https://jestjs.io/docs/en/snapshot-testing) for more information. + */ + toMatchInlineSnapshot(propertyMatchers: Partial, snapshot?: string): R; + /** + * This ensures that a value matches the most recent snapshot with property matchers. + * Instead of writing the snapshot value to a .snap file, it will be written into the source code automatically. + * Check out [the Snapshot Testing guide](https://jestjs.io/docs/en/snapshot-testing) for more information. + */ + toMatchInlineSnapshot(snapshot?: string): R; + /** + * Used to test that a function throws a error matching the most recent snapshot when it is called. + */ + toThrowErrorMatchingSnapshot(): R; + /** + * Used to test that a function throws a error matching the most recent snapshot when it is called. + * Instead of writing the snapshot value to a .snap file, it will be written into the source code automatically. + */ + toThrowErrorMatchingInlineSnapshot(snapshot?: string): R; +} +export {}; diff --git a/packages/expect/build/types.js b/packages/expect/build/types.js new file mode 100644 index 000000000000..7a223e485c13 --- /dev/null +++ b/packages/expect/build/types.js @@ -0,0 +1,3 @@ +'use strict'; + +var _jestMatchersObject = require('./jestMatchersObject'); diff --git a/packages/expect/build/utils.d.ts b/packages/expect/build/utils.d.ts new file mode 100644 index 000000000000..fcb52524255a --- /dev/null +++ b/packages/expect/build/utils.d.ts @@ -0,0 +1,24 @@ +/** + * 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. + * + */ +declare type GetPath = { + hasEndProp?: boolean; + lastTraversedObject: unknown; + traversedPath: Array; + value?: unknown; +}; +export declare const getPath: (object: Record, propertyPath: string | Array) => GetPath; +export declare const getObjectSubset: (object: any, subset: any, seenReferences?: WeakMap) => any; +export declare const iterableEquality: (a: any, b: any, aStack?: Array, bStack?: Array) => boolean | undefined; +export declare const subsetEquality: (object: unknown, subset: unknown) => boolean | undefined; +export declare const typeEquality: (a: any, b: any) => boolean | undefined; +export declare const sparseArrayEquality: (a: unknown, b: unknown) => boolean | undefined; +export declare const partition: (items: T[], predicate: (arg: T) => boolean) => [T[], T[]]; +export declare const isError: (value: unknown) => value is Error; +export declare function emptyObject(obj: unknown): boolean; +export declare const isOneline: (expected: unknown, received: unknown) => boolean; +export {}; diff --git a/packages/expect/build/utils.js b/packages/expect/build/utils.js new file mode 100644 index 000000000000..134476c65ecd --- /dev/null +++ b/packages/expect/build/utils.js @@ -0,0 +1,392 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.emptyObject = emptyObject; +exports.isOneline = exports.isError = exports.partition = exports.sparseArrayEquality = exports.typeEquality = exports.subsetEquality = exports.iterableEquality = exports.getObjectSubset = exports.getPath = void 0; + +var _jestGetType = require('jest-get-type'); + +var _jasmineUtils = require('./jasmineUtils'); + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; + +/** + * Checks if `hasOwnProperty(object, key)` up the prototype chain, stopping at `Object.prototype`. + */ +const hasPropertyInObject = (object, key) => { + const shouldTerminate = + !object || typeof object !== 'object' || object === Object.prototype; + + if (shouldTerminate) { + return false; + } + + return ( + Object.prototype.hasOwnProperty.call(object, key) || + hasPropertyInObject(Object.getPrototypeOf(object), key) + ); +}; + +const getPath = (object, propertyPath) => { + if (!Array.isArray(propertyPath)) { + propertyPath = propertyPath.split('.'); + } + + if (propertyPath.length) { + const lastProp = propertyPath.length === 1; + const prop = propertyPath[0]; + const newObject = object[prop]; + + if (!lastProp && (newObject === null || newObject === undefined)) { + // This is not the last prop in the chain. If we keep recursing it will + // hit a `can't access property X of undefined | null`. At this point we + // know that the chain has broken and we can return right away. + return { + hasEndProp: false, + lastTraversedObject: object, + traversedPath: [] + }; + } + + const result = getPath(newObject, propertyPath.slice(1)); + + if (result.lastTraversedObject === null) { + result.lastTraversedObject = object; + } + + result.traversedPath.unshift(prop); + + if (lastProp) { + // Does object have the property with an undefined value? + // Although primitive values support bracket notation (above) + // they would throw TypeError for in operator (below). + result.hasEndProp = + newObject !== undefined || + (!(0, _jestGetType.isPrimitive)(object) && prop in object); + + if (!result.hasEndProp) { + result.traversedPath.shift(); + } + } + + return result; + } + + return { + lastTraversedObject: null, + traversedPath: [], + value: object + }; +}; // Strip properties from object that are not present in the subset. Useful for +// printing the diff for toMatchObject() without adding unrelated noise. + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +exports.getPath = getPath; + +const getObjectSubset = (object, subset, seenReferences = new WeakMap()) => { + /* eslint-enable @typescript-eslint/explicit-module-boundary-types */ + if (Array.isArray(object)) { + if (Array.isArray(subset) && subset.length === object.length) { + // The map method returns correct subclass of subset. + return subset.map((sub, i) => getObjectSubset(object[i], sub)); + } + } else if (object instanceof Date) { + return object; + } else if (isObject(object) && isObject(subset)) { + if ( + (0, _jasmineUtils.equals)(object, subset, [ + iterableEquality, + subsetEquality + ]) + ) { + // Avoid unnecessary copy which might return Object instead of subclass. + return subset; + } + + const trimmed = {}; + seenReferences.set(object, trimmed); + Object.keys(object) + .filter(key => hasPropertyInObject(subset, key)) + .forEach(key => { + trimmed[key] = seenReferences.has(object[key]) + ? seenReferences.get(object[key]) + : getObjectSubset(object[key], subset[key], seenReferences); + }); + + if (Object.keys(trimmed).length > 0) { + return trimmed; + } + } + + return object; +}; + +exports.getObjectSubset = getObjectSubset; +const IteratorSymbol = Symbol.iterator; + +const hasIterator = object => !!(object != null && object[IteratorSymbol]); +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +const iterableEquality = ( + a, + b, + /* eslint-enable @typescript-eslint/explicit-module-boundary-types */ + aStack = [], + bStack = [] +) => { + if ( + typeof a !== 'object' || + typeof b !== 'object' || + Array.isArray(a) || + Array.isArray(b) || + !hasIterator(a) || + !hasIterator(b) + ) { + return undefined; + } + + if (a.constructor !== b.constructor) { + return false; + } + + let length = aStack.length; + + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + // circular references at same depth are equal + // circular reference is not equal to non-circular one + if (aStack[length] === a) { + return bStack[length] === b; + } + } + + aStack.push(a); + bStack.push(b); + + const iterableEqualityWithStack = (a, b) => + iterableEquality(a, b, [...aStack], [...bStack]); + + if (a.size !== undefined) { + if (a.size !== b.size) { + return false; + } else if ( + (0, _jasmineUtils.isA)('Set', a) || + (0, _jasmineUtils.isImmutableUnorderedSet)(a) + ) { + let allFound = true; + + for (const aValue of a) { + if (!b.has(aValue)) { + let has = false; + + for (const bValue of b) { + const isEqual = (0, _jasmineUtils.equals)(aValue, bValue, [ + iterableEqualityWithStack + ]); + + if (isEqual === true) { + has = true; + } + } + + if (has === false) { + allFound = false; + break; + } + } + } // Remove the first value from the stack of traversed values. + + aStack.pop(); + bStack.pop(); + return allFound; + } else if ( + (0, _jasmineUtils.isA)('Map', a) || + (0, _jasmineUtils.isImmutableUnorderedKeyed)(a) + ) { + let allFound = true; + + for (const aEntry of a) { + if ( + !b.has(aEntry[0]) || + !(0, _jasmineUtils.equals)(aEntry[1], b.get(aEntry[0]), [ + iterableEqualityWithStack + ]) + ) { + let has = false; + + for (const bEntry of b) { + const matchedKey = (0, _jasmineUtils.equals)(aEntry[0], bEntry[0], [ + iterableEqualityWithStack + ]); + let matchedValue = false; + + if (matchedKey === true) { + matchedValue = (0, _jasmineUtils.equals)(aEntry[1], bEntry[1], [ + iterableEqualityWithStack + ]); + } + + if (matchedValue === true) { + has = true; + } + } + + if (has === false) { + allFound = false; + break; + } + } + } // Remove the first value from the stack of traversed values. + + aStack.pop(); + bStack.pop(); + return allFound; + } + } + + const bIterator = b[IteratorSymbol](); + + for (const aValue of a) { + const nextB = bIterator.next(); + + if ( + nextB.done || + !(0, _jasmineUtils.equals)(aValue, nextB.value, [ + iterableEqualityWithStack + ]) + ) { + return false; + } + } + + if (!bIterator.next().done) { + return false; + } // Remove the first value from the stack of traversed values. + + aStack.pop(); + bStack.pop(); + return true; +}; + +exports.iterableEquality = iterableEquality; + +const isObject = a => a !== null && typeof a === 'object'; + +const isObjectWithKeys = a => + isObject(a) && + !(a instanceof Error) && + !(a instanceof Array) && + !(a instanceof Date); + +const subsetEquality = (object, subset) => { + // subsetEquality needs to keep track of the references + // it has already visited to avoid infinite loops in case + // there are circular references in the subset passed to it. + const subsetEqualityWithContext = (seenReferences = new WeakMap()) => ( + object, + subset + ) => { + if (!isObjectWithKeys(subset)) { + return undefined; + } + + return Object.keys(subset).every(key => { + if (isObjectWithKeys(subset[key])) { + if (seenReferences.has(subset[key])) { + return (0, _jasmineUtils.equals)(object[key], subset[key], [ + iterableEquality + ]); + } + + seenReferences.set(subset[key], true); + } + + const result = + object != null && + hasPropertyInObject(object, key) && + (0, _jasmineUtils.equals)(object[key], subset[key], [ + iterableEquality, + subsetEqualityWithContext(seenReferences) + ]); // The main goal of using seenReference is to avoid circular node on tree. + // It will only happen within a parent and its child, not a node and nodes next to it (same level) + // We should keep the reference for a parent and its child only + // Thus we should delete the reference immediately so that it doesn't interfere + // other nodes within the same level on tree. + + seenReferences.delete(subset[key]); + return result; + }); + }; + + return subsetEqualityWithContext()(object, subset); +}; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + +exports.subsetEquality = subsetEquality; + +const typeEquality = (a, b) => { + if (a == null || b == null || a.constructor === b.constructor) { + return undefined; + } + + return false; +}; + +exports.typeEquality = typeEquality; + +const sparseArrayEquality = (a, b) => { + if (!Array.isArray(a) || !Array.isArray(b)) { + return undefined; + } // A sparse array [, , 1] will have keys ["2"] whereas [undefined, undefined, 1] will have keys ["0", "1", "2"] + + const aKeys = Object.keys(a); + const bKeys = Object.keys(b); + return ( + (0, _jasmineUtils.equals)(a, b, [iterableEquality, typeEquality], true) && + (0, _jasmineUtils.equals)(aKeys, bKeys) + ); +}; + +exports.sparseArrayEquality = sparseArrayEquality; + +const partition = (items, predicate) => { + const result = [[], []]; + items.forEach(item => result[predicate(item) ? 0 : 1].push(item)); + return result; +}; // Copied from https://github.com/graingert/angular.js/blob/a43574052e9775cbc1d7dd8a086752c979b0f020/src/Angular.js#L685-L693 + +exports.partition = partition; + +const isError = value => { + switch (Object.prototype.toString.call(value)) { + case '[object Error]': + return true; + + case '[object Exception]': + return true; + + case '[object DOMException]': + return true; + + default: + return value instanceof Error; + } +}; + +exports.isError = isError; + +function emptyObject(obj) { + return obj && typeof obj === 'object' ? !Object.keys(obj).length : false; +} + +const MULTILINE_REGEXP = /[\r\n]/; + +const isOneline = (expected, received) => + typeof expected === 'string' && + typeof received === 'string' && + (!MULTILINE_REGEXP.test(expected) || !MULTILINE_REGEXP.test(received)); + +exports.isOneline = isOneline; diff --git a/packages/jest-changed-files/build/git.d.ts b/packages/jest-changed-files/build/git.d.ts new file mode 100644 index 000000000000..498733b1fc45 --- /dev/null +++ b/packages/jest-changed-files/build/git.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + * + */ +import type { SCMAdapter } from './types'; +declare const adapter: SCMAdapter; +export default adapter; diff --git a/packages/jest-changed-files/build/git.js b/packages/jest-changed-files/build/git.js new file mode 100644 index 000000000000..06b7d9f7c847 --- /dev/null +++ b/packages/jest-changed-files/build/git.js @@ -0,0 +1,168 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _execa() { + const data = _interopRequireDefault(require('execa')); + + _execa = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 findChangedFilesUsingCommand = async (args, cwd) => { + let result; + + try { + result = await (0, _execa().default)('git', args, { + cwd + }); + } catch (e) { + // TODO: Should we keep the original `message`? + e.message = e.stderr; + throw e; + } + + return result.stdout + .split('\n') + .filter(s => s !== '') + .map(changedPath => path().resolve(cwd, changedPath)); +}; + +const adapter = { + findChangedFiles: async (cwd, options) => { + const changedSince = + options && (options.withAncestor ? 'HEAD^' : options.changedSince); + const includePaths = ( + (options && options.includePaths) || + [] + ).map(absoluteRoot => path().normalize(path().relative(cwd, absoluteRoot))); + + if (options && options.lastCommit) { + return findChangedFilesUsingCommand( + ['show', '--name-only', '--pretty=format:', 'HEAD'].concat( + includePaths + ), + cwd + ); + } + + if (changedSince) { + const [committed, staged, unstaged] = await Promise.all([ + findChangedFilesUsingCommand( + ['diff', '--name-only', `${changedSince}...HEAD`].concat( + includePaths + ), + cwd + ), + findChangedFilesUsingCommand( + ['diff', '--cached', '--name-only'].concat(includePaths), + cwd + ), + findChangedFilesUsingCommand( + ['ls-files', '--other', '--modified', '--exclude-standard'].concat( + includePaths + ), + cwd + ) + ]); + return [...committed, ...staged, ...unstaged]; + } + + const [staged, unstaged] = await Promise.all([ + findChangedFilesUsingCommand( + ['diff', '--cached', '--name-only'].concat(includePaths), + cwd + ), + findChangedFilesUsingCommand( + ['ls-files', '--other', '--modified', '--exclude-standard'].concat( + includePaths + ), + cwd + ) + ]); + return [...staged, ...unstaged]; + }, + getRoot: async cwd => { + const options = ['rev-parse', '--show-cdup']; + + try { + const result = await (0, _execa().default)('git', options, { + cwd + }); + return path().resolve(cwd, result.stdout); + } catch { + return null; + } + } +}; +var _default = adapter; +exports.default = _default; diff --git a/packages/jest-changed-files/build/hg.d.ts b/packages/jest-changed-files/build/hg.d.ts new file mode 100644 index 000000000000..498733b1fc45 --- /dev/null +++ b/packages/jest-changed-files/build/hg.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + * + */ +import type { SCMAdapter } from './types'; +declare const adapter: SCMAdapter; +export default adapter; diff --git a/packages/jest-changed-files/build/hg.js b/packages/jest-changed-files/build/hg.js new file mode 100644 index 000000000000..0464c1b715b6 --- /dev/null +++ b/packages/jest-changed-files/build/hg.js @@ -0,0 +1,127 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _execa() { + const data = _interopRequireDefault(require('execa')); + + _execa = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 env = {...process.env, HGPLAIN: '1'}; +const adapter = { + findChangedFiles: async (cwd, options) => { + const includePaths = (options && options.includePaths) || []; + const args = ['status', '-amnu']; + + if (options && options.withAncestor) { + args.push('--rev', `min((!public() & ::.)+.)^`); + } else if (options && options.changedSince) { + args.push('--rev', `ancestor(., ${options.changedSince})`); + } else if (options && options.lastCommit === true) { + args.push('--change', '.'); + } + + args.push(...includePaths); + let result; + + try { + result = await (0, _execa().default)('hg', args, { + cwd, + env + }); + } catch (e) { + // TODO: Should we keep the original `message`? + e.message = e.stderr; + throw e; + } + + return result.stdout + .split('\n') + .filter(s => s !== '') + .map(changedPath => path().resolve(cwd, changedPath)); + }, + getRoot: async cwd => { + try { + const result = await (0, _execa().default)('hg', ['root'], { + cwd, + env + }); + return result.stdout; + } catch { + return null; + } + } +}; +var _default = adapter; +exports.default = _default; diff --git a/packages/jest-changed-files/build/index.d.ts b/packages/jest-changed-files/build/index.d.ts new file mode 100644 index 000000000000..0c134ab8b5a8 --- /dev/null +++ b/packages/jest-changed-files/build/index.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + * + */ +import type { Config } from '@jest/types'; +import type { ChangedFilesPromise, Options, Repos } from './types'; +export type { ChangedFiles, ChangedFilesPromise } from './types'; +export declare const getChangedFilesForRoots: (roots: Array, options: Options) => ChangedFilesPromise; +export declare const findRepos: (roots: Array) => Promise; diff --git a/packages/jest-changed-files/build/index.js b/packages/jest-changed-files/build/index.js new file mode 100644 index 000000000000..b913e9638b92 --- /dev/null +++ b/packages/jest-changed-files/build/index.js @@ -0,0 +1,86 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.findRepos = exports.getChangedFilesForRoots = void 0; + +function _throat() { + const data = _interopRequireDefault(require('throat')); + + _throat = function () { + return data; + }; + + return data; +} + +var _git = _interopRequireDefault(require('./git')); + +var _hg = _interopRequireDefault(require('./hg')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + * + */ +function notEmpty(value) { + return value != null; +} // This is an arbitrary number. The main goal is to prevent projects with +// many roots (50+) from spawning too many processes at once. + +const mutex = (0, _throat().default)(5); + +const findGitRoot = dir => mutex(() => _git.default.getRoot(dir)); + +const findHgRoot = dir => mutex(() => _hg.default.getRoot(dir)); + +const getChangedFilesForRoots = async (roots, options) => { + const repos = await findRepos(roots); + const changedFilesOptions = { + includePaths: roots, + ...options + }; + const gitPromises = Array.from(repos.git).map(repo => + _git.default.findChangedFiles(repo, changedFilesOptions) + ); + const hgPromises = Array.from(repos.hg).map(repo => + _hg.default.findChangedFiles(repo, changedFilesOptions) + ); + const changedFiles = ( + await Promise.all(gitPromises.concat(hgPromises)) + ).reduce((allFiles, changedFilesInTheRepo) => { + for (const file of changedFilesInTheRepo) { + allFiles.add(file); + } + + return allFiles; + }, new Set()); + return { + changedFiles, + repos + }; +}; + +exports.getChangedFilesForRoots = getChangedFilesForRoots; + +const findRepos = async roots => { + const gitRepos = await Promise.all( + roots.reduce((promises, root) => promises.concat(findGitRoot(root)), []) + ); + const hgRepos = await Promise.all( + roots.reduce((promises, root) => promises.concat(findHgRoot(root)), []) + ); + return { + git: new Set(gitRepos.filter(notEmpty)), + hg: new Set(hgRepos.filter(notEmpty)) + }; +}; + +exports.findRepos = findRepos; diff --git a/packages/jest-changed-files/build/types.d.ts b/packages/jest-changed-files/build/types.d.ts new file mode 100644 index 000000000000..9ac5d3d92eb4 --- /dev/null +++ b/packages/jest-changed-files/build/types.d.ts @@ -0,0 +1,28 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export declare type Options = { + lastCommit?: boolean; + withAncestor?: boolean; + changedSince?: string; + includePaths?: Array; +}; +declare type Paths = Set; +export declare type Repos = { + git: Paths; + hg: Paths; +}; +export declare type ChangedFiles = { + repos: Repos; + changedFiles: Paths; +}; +export declare type ChangedFilesPromise = Promise; +export declare type SCMAdapter = { + findChangedFiles: (cwd: Config.Path, options: Options) => Promise>; + getRoot: (cwd: Config.Path) => Promise; +}; +export {}; diff --git a/packages/jest-changed-files/build/types.js b/packages/jest-changed-files/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-changed-files/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-circus/build/eventHandler.d.ts b/packages/jest-circus/build/eventHandler.d.ts new file mode 100644 index 000000000000..666bd207f506 --- /dev/null +++ b/packages/jest-circus/build/eventHandler.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Circus } from '@jest/types'; +declare const eventHandler: Circus.EventHandler; +export default eventHandler; diff --git a/packages/jest-circus/build/eventHandler.js b/packages/jest-circus/build/eventHandler.js new file mode 100644 index 000000000000..e0f028dd5b8e --- /dev/null +++ b/packages/jest-circus/build/eventHandler.js @@ -0,0 +1,294 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _globalErrorHandlers = require('./globalErrorHandlers'); + +var _types = require('./types'); + +var _utils = require('./utils'); + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var jestNow = global[Symbol.for('jest-native-now')] || global.Date.now; + +// TODO: investigate why a shorter (event, state) signature results into TS7006 compiler error +const eventHandler = (event, state) => { + switch (event.name) { + case 'include_test_location_in_result': { + state.includeTestLocationInResult = true; + break; + } + + case 'hook_start': { + event.hook.seenDone = false; + break; + } + + case 'start_describe_definition': { + const {blockName, mode} = event; + const {currentDescribeBlock, currentlyRunningTest} = state; + + if (currentlyRunningTest) { + currentlyRunningTest.errors.push( + new Error( + `Cannot nest a describe inside a test. Describe block "${blockName}" cannot run because it is nested within "${currentlyRunningTest.name}".` + ) + ); + break; + } + + const describeBlock = (0, _utils.makeDescribe)( + blockName, + currentDescribeBlock, + mode + ); + currentDescribeBlock.children.push(describeBlock); + state.currentDescribeBlock = describeBlock; + break; + } + + case 'finish_describe_definition': { + const {currentDescribeBlock} = state; + (0, _utils.invariant)( + currentDescribeBlock, + `currentDescribeBlock must be there` + ); + + if (!(0, _utils.describeBlockHasTests)(currentDescribeBlock)) { + currentDescribeBlock.hooks.forEach(hook => { + hook.asyncError.message = `Invalid: ${hook.type}() may not be used in a describe block containing no tests.`; + state.unhandledErrors.push(hook.asyncError); + }); + } // pass mode of currentDescribeBlock to tests + // but do not when there is already a single test with "only" mode + + const shouldPassMode = !( + currentDescribeBlock.mode === 'only' && + currentDescribeBlock.children.some( + child => child.type === 'test' && child.mode === 'only' + ) + ); + + if (shouldPassMode) { + currentDescribeBlock.children.forEach(child => { + if (child.type === 'test' && !child.mode) { + child.mode = currentDescribeBlock.mode; + } + }); + } + + if ( + !state.hasFocusedTests && + currentDescribeBlock.mode !== 'skip' && + currentDescribeBlock.children.some( + child => child.type === 'test' && child.mode === 'only' + ) + ) { + state.hasFocusedTests = true; + } + + if (currentDescribeBlock.parent) { + state.currentDescribeBlock = currentDescribeBlock.parent; + } + + break; + } + + case 'add_hook': { + const {currentDescribeBlock, currentlyRunningTest, hasStarted} = state; + const {asyncError, fn, hookType: type, timeout} = event; + + if (currentlyRunningTest) { + currentlyRunningTest.errors.push( + new Error( + `Hooks cannot be defined inside tests. Hook of type "${type}" is nested within "${currentlyRunningTest.name}".` + ) + ); + break; + } else if (hasStarted) { + state.unhandledErrors.push( + new Error( + 'Cannot add a hook after tests have started running. Hooks must be defined synchronously.' + ) + ); + break; + } + + const parent = currentDescribeBlock; + currentDescribeBlock.hooks.push({ + asyncError, + fn, + parent, + seenDone: false, + timeout, + type + }); + break; + } + + case 'add_test': { + const {currentDescribeBlock, currentlyRunningTest, hasStarted} = state; + const {asyncError, fn, mode, testName: name, timeout} = event; + + if (currentlyRunningTest) { + currentlyRunningTest.errors.push( + new Error( + `Tests cannot be nested. Test "${name}" cannot run because it is nested within "${currentlyRunningTest.name}".` + ) + ); + break; + } else if (hasStarted) { + state.unhandledErrors.push( + new Error( + 'Cannot add a test after tests have started running. Tests must be defined synchronously.' + ) + ); + break; + } + + const test = (0, _utils.makeTest)( + fn, + mode, + name, + currentDescribeBlock, + timeout, + asyncError + ); + + if (currentDescribeBlock.mode !== 'skip' && test.mode === 'only') { + state.hasFocusedTests = true; + } + + currentDescribeBlock.children.push(test); + currentDescribeBlock.tests.push(test); + break; + } + + case 'hook_failure': { + const {test, describeBlock, error, hook} = event; + const {asyncError, type} = hook; + + if (type === 'beforeAll') { + (0, _utils.invariant)(describeBlock, 'always present for `*All` hooks'); + (0, _utils.addErrorToEachTestUnderDescribe)( + describeBlock, + error, + asyncError + ); + } else if (type === 'afterAll') { + // Attaching `afterAll` errors to each test makes execution flow + // too complicated, so we'll consider them to be global. + state.unhandledErrors.push([error, asyncError]); + } else { + (0, _utils.invariant)(test, 'always present for `*Each` hooks'); + test.errors.push([error, asyncError]); + } + + break; + } + + case 'test_skip': { + event.test.status = 'skip'; + break; + } + + case 'test_todo': { + event.test.status = 'todo'; + break; + } + + case 'test_done': { + event.test.duration = (0, _utils.getTestDuration)(event.test); + event.test.status = 'done'; + state.currentlyRunningTest = null; + break; + } + + case 'test_start': { + state.currentlyRunningTest = event.test; + event.test.startedAt = jestNow(); + event.test.invocations += 1; + break; + } + + case 'test_fn_start': { + event.test.seenDone = false; + break; + } + + case 'test_fn_failure': { + const { + error, + test: {asyncError} + } = event; + event.test.errors.push([error, asyncError]); + break; + } + + case 'test_retry': { + event.test.errors = []; + break; + } + + case 'run_start': { + state.hasStarted = true; + global[_types.TEST_TIMEOUT_SYMBOL] && + (state.testTimeout = global[_types.TEST_TIMEOUT_SYMBOL]); + break; + } + + case 'run_finish': { + break; + } + + case 'setup': { + // Uncaught exception handlers should be defined on the parent process + // object. If defined on the VM's process object they just no op and let + // the parent process crash. It might make sense to return a `dispatch` + // function to the parent process and register handlers there instead, but + // i'm not sure if this is works. For now i just replicated whatever + // jasmine was doing -- dabramov + state.parentProcess = event.parentProcess; + (0, _utils.invariant)(state.parentProcess); + state.originalGlobalErrorHandlers = (0, + _globalErrorHandlers.injectGlobalErrorHandlers)(state.parentProcess); + + if (event.testNamePattern) { + state.testNamePattern = new RegExp(event.testNamePattern, 'i'); + } + + break; + } + + case 'teardown': { + (0, _utils.invariant)(state.originalGlobalErrorHandlers); + (0, _utils.invariant)(state.parentProcess); + (0, _globalErrorHandlers.restoreGlobalErrorHandlers)( + state.parentProcess, + state.originalGlobalErrorHandlers + ); + break; + } + + case 'error': { + // It's very likely for long-running async tests to throw errors. In this + // case we want to catch them and fail the current test. At the same time + // there's a possibility that one test sets a long timeout, that will + // eventually throw after this test finishes but during some other test + // execution, which will result in one test's error failing another test. + // In any way, it should be possible to track where the error was thrown + // from. + state.currentlyRunningTest + ? state.currentlyRunningTest.errors.push(event.error) + : state.unhandledErrors.push(event.error); + break; + } + } +}; + +var _default = eventHandler; +exports.default = _default; diff --git a/packages/jest-circus/build/formatNodeAssertErrors.d.ts b/packages/jest-circus/build/formatNodeAssertErrors.d.ts new file mode 100644 index 000000000000..0e70ecbc58a2 --- /dev/null +++ b/packages/jest-circus/build/formatNodeAssertErrors.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Circus } from '@jest/types'; +declare const formatNodeAssertErrors: (event: Circus.Event, state: Circus.State) => void; +export default formatNodeAssertErrors; diff --git a/packages/jest-circus/build/formatNodeAssertErrors.js b/packages/jest-circus/build/formatNodeAssertErrors.js new file mode 100644 index 000000000000..300a0beebdc2 --- /dev/null +++ b/packages/jest-circus/build/formatNodeAssertErrors.js @@ -0,0 +1,204 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _assert = require('assert'); + +var _chalk = _interopRequireDefault(require('chalk')); + +var _jestMatcherUtils = require('jest-matcher-utils'); + +var _prettyFormat = _interopRequireDefault(require('pretty-format')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 assertOperatorsMap = { + '!=': 'notEqual', + '!==': 'notStrictEqual', + '==': 'equal', + '===': 'strictEqual' +}; +const humanReadableOperators = { + deepEqual: 'to deeply equal', + deepStrictEqual: 'to deeply and strictly equal', + equal: 'to be equal', + notDeepEqual: 'not to deeply equal', + notDeepStrictEqual: 'not to deeply and strictly equal', + notEqual: 'to not be equal', + notStrictEqual: 'not be strictly equal', + strictEqual: 'to strictly be equal' +}; + +const formatNodeAssertErrors = (event, state) => { + if (event.name === 'test_done') { + event.test.errors = event.test.errors.map(errors => { + let error; + + if (Array.isArray(errors)) { + const [originalError, asyncError] = errors; + + if (originalError == null) { + error = asyncError; + } else if (!originalError.stack) { + error = asyncError; + error.message = originalError.message + ? originalError.message + : `thrown: ${(0, _prettyFormat.default)(originalError, { + maxDepth: 3 + })}`; + } else { + error = originalError; + } + } else { + error = errors; + } + + return isAssertionError(error) + ? { + message: assertionErrorMessage(error, { + expand: state.expand + }) + } + : errors; + }); + } +}; + +const getOperatorName = (operator, stack) => { + if (typeof operator === 'string') { + return assertOperatorsMap[operator] || operator; + } + + if (stack.match('.doesNotThrow')) { + return 'doesNotThrow'; + } + + if (stack.match('.throws')) { + return 'throws'; + } // this fallback is only needed for versions older than node 10 + + if (stack.match('.fail')) { + return 'fail'; + } + + return ''; +}; + +const operatorMessage = operator => { + const niceOperatorName = getOperatorName(operator, ''); + const humanReadableOperator = humanReadableOperators[niceOperatorName]; + return typeof operator === 'string' + ? `${humanReadableOperator || niceOperatorName} to:\n` + : ''; +}; + +const assertThrowingMatcherHint = operatorName => + operatorName + ? _chalk.default.dim('assert') + + _chalk.default.dim('.' + operatorName + '(') + + _chalk.default.red('function') + + _chalk.default.dim(')') + : ''; + +const assertMatcherHint = (operator, operatorName, expected) => { + let message = ''; + + if (operator === '==' && expected === true) { + message = + _chalk.default.dim('assert') + + _chalk.default.dim('(') + + _chalk.default.red('received') + + _chalk.default.dim(')'); + } else if (operatorName) { + message = + _chalk.default.dim('assert') + + _chalk.default.dim('.' + operatorName + '(') + + _chalk.default.red('received') + + _chalk.default.dim(', ') + + _chalk.default.green('expected') + + _chalk.default.dim(')'); + } + + return message; +}; + +function assertionErrorMessage(error, options) { + const {expected, actual, generatedMessage, message, operator, stack} = error; + const diffString = (0, _jestMatcherUtils.diff)(expected, actual, options); + const hasCustomMessage = !generatedMessage; + const operatorName = getOperatorName(operator, stack); + const trimmedStack = stack + .replace(message, '') + .replace(/AssertionError(.*)/g, ''); + + if (operatorName === 'doesNotThrow') { + return ( + buildHintString(assertThrowingMatcherHint(operatorName)) + + _chalk.default.reset(`Expected the function not to throw an error.\n`) + + _chalk.default.reset(`Instead, it threw:\n`) + + ` ${(0, _jestMatcherUtils.printReceived)(actual)}` + + _chalk.default.reset( + hasCustomMessage ? '\n\nMessage:\n ' + message : '' + ) + + trimmedStack + ); + } + + if (operatorName === 'throws') { + return ( + buildHintString(assertThrowingMatcherHint(operatorName)) + + _chalk.default.reset(`Expected the function to throw an error.\n`) + + _chalk.default.reset(`But it didn't throw anything.`) + + _chalk.default.reset( + hasCustomMessage ? '\n\nMessage:\n ' + message : '' + ) + + trimmedStack + ); + } + + if (operatorName === 'fail') { + return ( + buildHintString(assertMatcherHint(operator, operatorName, expected)) + + _chalk.default.reset(hasCustomMessage ? 'Message:\n ' + message : '') + + trimmedStack + ); + } + + return ( + buildHintString(assertMatcherHint(operator, operatorName, expected)) + + _chalk.default.reset(`Expected value ${operatorMessage(operator)}`) + + ` ${(0, _jestMatcherUtils.printExpected)(expected)}\n` + + _chalk.default.reset(`Received:\n`) + + ` ${(0, _jestMatcherUtils.printReceived)(actual)}` + + _chalk.default.reset(hasCustomMessage ? '\n\nMessage:\n ' + message : '') + + (diffString ? `\n\nDifference:\n\n${diffString}` : '') + + trimmedStack + ); +} + +function isAssertionError(error) { + return ( + error && + (error instanceof _assert.AssertionError || + error.name === _assert.AssertionError.name || + error.code === 'ERR_ASSERTION') + ); +} + +function buildHintString(hint) { + return hint ? hint + '\n\n' : ''; +} + +var _default = formatNodeAssertErrors; +exports.default = _default; diff --git a/packages/jest-circus/build/globalErrorHandlers.d.ts b/packages/jest-circus/build/globalErrorHandlers.d.ts new file mode 100644 index 000000000000..d25393f4fc79 --- /dev/null +++ b/packages/jest-circus/build/globalErrorHandlers.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Circus } from '@jest/types'; +export declare const injectGlobalErrorHandlers: (parentProcess: NodeJS.Process) => Circus.GlobalErrorHandlers; +export declare const restoreGlobalErrorHandlers: (parentProcess: NodeJS.Process, originalErrorHandlers: Circus.GlobalErrorHandlers) => void; diff --git a/packages/jest-circus/build/globalErrorHandlers.js b/packages/jest-circus/build/globalErrorHandlers.js new file mode 100644 index 000000000000..bc889f355407 --- /dev/null +++ b/packages/jest-circus/build/globalErrorHandlers.js @@ -0,0 +1,51 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.restoreGlobalErrorHandlers = exports.injectGlobalErrorHandlers = void 0; + +var _state = require('./state'); + +/** + * 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 uncaught = error => { + (0, _state.dispatchSync)({ + error, + name: 'error' + }); +}; + +const injectGlobalErrorHandlers = parentProcess => { + const uncaughtException = process.listeners('uncaughtException').slice(); + const unhandledRejection = process.listeners('unhandledRejection').slice(); + parentProcess.removeAllListeners('uncaughtException'); + parentProcess.removeAllListeners('unhandledRejection'); + parentProcess.on('uncaughtException', uncaught); + parentProcess.on('unhandledRejection', uncaught); + return { + uncaughtException, + unhandledRejection + }; +}; + +exports.injectGlobalErrorHandlers = injectGlobalErrorHandlers; + +const restoreGlobalErrorHandlers = (parentProcess, originalErrorHandlers) => { + parentProcess.removeListener('uncaughtException', uncaught); + parentProcess.removeListener('unhandledRejection', uncaught); + + for (const listener of originalErrorHandlers.uncaughtException) { + parentProcess.on('uncaughtException', listener); + } + + for (const listener of originalErrorHandlers.unhandledRejection) { + parentProcess.on('unhandledRejection', listener); + } +}; + +exports.restoreGlobalErrorHandlers = restoreGlobalErrorHandlers; diff --git a/packages/jest-circus/build/index.d.ts b/packages/jest-circus/build/index.d.ts new file mode 100644 index 000000000000..325d26f3b7b2 --- /dev/null +++ b/packages/jest-circus/build/index.d.ts @@ -0,0 +1,50 @@ +/** + * 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. + */ +import type { Circus, Global } from '@jest/types'; +declare type THook = (fn: Circus.HookFn, timeout?: number) => void; +declare const describe: { + (blockName: Circus.BlockName, blockFn: Circus.BlockFn): void; + each: (table: Global.EachTable, ...taggedTemplateData: Global.TemplateData) => (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + only: { + (blockName: Circus.BlockName, blockFn: Circus.BlockFn): void; + each: (table: Global.EachTable, ...taggedTemplateData: Global.TemplateData) => (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + }; + skip: { + (blockName: Circus.BlockName, blockFn: Circus.BlockFn): void; + each: (table: Global.EachTable, ...taggedTemplateData: Global.TemplateData) => (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + }; +}; +declare const beforeEach: THook; +declare const beforeAll: THook; +declare const afterEach: THook; +declare const afterAll: THook; +declare const test: Global.It; +declare const it: Global.It; +export declare type Event = Circus.Event; +export declare type State = Circus.State; +export { afterAll, afterEach, beforeAll, beforeEach, describe, it, test }; +declare const _default: { + afterAll: THook; + afterEach: THook; + beforeAll: THook; + beforeEach: THook; + describe: { + (blockName: string, blockFn: Global.BlockFn): void; + each: (table: Global.EachTable, ...taggedTemplateData: Global.TemplateData) => (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + only: { + (blockName: string, blockFn: Global.BlockFn): void; + each: (table: Global.EachTable, ...taggedTemplateData: Global.TemplateData) => (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + }; + skip: { + (blockName: string, blockFn: Global.BlockFn): void; + each: (table: Global.EachTable, ...taggedTemplateData: Global.TemplateData) => (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + }; + }; + it: Global.It; + test: Global.It; +}; +export default _default; diff --git a/packages/jest-circus/build/index.js b/packages/jest-circus/build/index.js new file mode 100644 index 000000000000..14b5f132e3f2 --- /dev/null +++ b/packages/jest-circus/build/index.js @@ -0,0 +1,187 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = exports.test = exports.it = exports.describe = exports.beforeEach = exports.beforeAll = exports.afterEach = exports.afterAll = void 0; + +var _jestEach = require('jest-each'); + +var _jestUtil = require('jest-util'); + +var _state = require('./state'); + +/** + * 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 describe = (() => { + const describe = (blockName, blockFn) => + _dispatchDescribe(blockFn, blockName, describe); + + const only = (blockName, blockFn) => + _dispatchDescribe(blockFn, blockName, only, 'only'); + + const skip = (blockName, blockFn) => + _dispatchDescribe(blockFn, blockName, skip, 'skip'); + + describe.each = (0, _jestEach.bind)(describe, false); + only.each = (0, _jestEach.bind)(only, false); + skip.each = (0, _jestEach.bind)(skip, false); + describe.only = only; + describe.skip = skip; + return describe; +})(); + +exports.describe = describe; + +const _dispatchDescribe = (blockFn, blockName, describeFn, mode) => { + const asyncError = new _jestUtil.ErrorWithStack(undefined, describeFn); + + if (blockFn === undefined) { + asyncError.message = `Missing second argument. It must be a callback function.`; + throw asyncError; + } + + if (typeof blockFn !== 'function') { + asyncError.message = `Invalid second argument, ${blockFn}. It must be a callback function.`; + throw asyncError; + } + + (0, _state.dispatchSync)({ + asyncError, + blockName, + mode, + name: 'start_describe_definition' + }); + const describeReturn = blockFn(); + + if ((0, _jestUtil.isPromise)(describeReturn)) { + throw new _jestUtil.ErrorWithStack( + 'Returning a Promise from "describe" is not supported. Tests must be defined synchronously.', + describeFn + ); + } else if (describeReturn !== undefined) { + throw new _jestUtil.ErrorWithStack( + 'A "describe" callback must not return a value.', + describeFn + ); + } + + (0, _state.dispatchSync)({ + blockName, + mode, + name: 'finish_describe_definition' + }); +}; + +const _addHook = (fn, hookType, hookFn, timeout) => { + const asyncError = new _jestUtil.ErrorWithStack(undefined, hookFn); + + if (typeof fn !== 'function') { + asyncError.message = + 'Invalid first argument. It must be a callback function.'; + throw asyncError; + } + + (0, _state.dispatchSync)({ + asyncError, + fn, + hookType, + name: 'add_hook', + timeout + }); +}; // Hooks have to pass themselves to the HOF in order for us to trim stack traces. + +const beforeEach = (fn, timeout) => + _addHook(fn, 'beforeEach', beforeEach, timeout); + +exports.beforeEach = beforeEach; + +const beforeAll = (fn, timeout) => + _addHook(fn, 'beforeAll', beforeAll, timeout); + +exports.beforeAll = beforeAll; + +const afterEach = (fn, timeout) => + _addHook(fn, 'afterEach', afterEach, timeout); + +exports.afterEach = afterEach; + +const afterAll = (fn, timeout) => _addHook(fn, 'afterAll', afterAll, timeout); + +exports.afterAll = afterAll; + +const test = (() => { + const test = (testName, fn, timeout) => + _addTest(testName, undefined, fn, test, timeout); + + const skip = (testName, fn, timeout) => + _addTest(testName, 'skip', fn, skip, timeout); + + const only = (testName, fn, timeout) => + _addTest(testName, 'only', fn, test.only, timeout); + + test.todo = (testName, ...rest) => { + if (rest.length > 0 || typeof testName !== 'string') { + throw new _jestUtil.ErrorWithStack( + 'Todo must be called with only a description.', + test.todo + ); + } + + return _addTest(testName, 'todo', () => {}, test.todo); + }; + + const _addTest = (testName, mode, fn, testFn, timeout) => { + const asyncError = new _jestUtil.ErrorWithStack(undefined, testFn); + + if (typeof testName !== 'string') { + asyncError.message = `Invalid first argument, ${testName}. It must be a string.`; + throw asyncError; + } + + if (fn === undefined) { + asyncError.message = + 'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.'; + throw asyncError; + } + + if (typeof fn !== 'function') { + asyncError.message = `Invalid second argument, ${fn}. It must be a callback function.`; + throw asyncError; + } + + return (0, _state.dispatchSync)({ + asyncError, + fn, + mode, + name: 'add_test', + testName, + timeout + }); + }; + + test.each = (0, _jestEach.bind)(test); + only.each = (0, _jestEach.bind)(only); + skip.each = (0, _jestEach.bind)(skip); + test.only = only; + test.skip = skip; + return test; +})(); + +exports.test = test; +const it = test; +exports.it = it; +var _default = { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + it, + test +}; +exports.default = _default; diff --git a/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.d.ts b/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.d.ts new file mode 100644 index 000000000000..9eec5c32868c --- /dev/null +++ b/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.d.ts @@ -0,0 +1,13 @@ +/** + * 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. + */ +import type { JestEnvironment } from '@jest/environment'; +import type { TestResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import type { TestFileEvent } from 'jest-runner'; +import type Runtime from 'jest-runtime'; +declare const jestAdapter: (globalConfig: Config.GlobalConfig, config: Config.ProjectConfig, environment: JestEnvironment, runtime: Runtime, testPath: string, sendMessageToJest?: TestFileEvent<"test-file-start" | "test-file-success" | "test-file-failure" | "test-case-result"> | undefined) => Promise; +export = jestAdapter; diff --git a/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js b/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js new file mode 100644 index 000000000000..e4330e0d8a7f --- /dev/null +++ b/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js @@ -0,0 +1,125 @@ +'use strict'; + +var _jestUtil = require('jest-util'); + +/** + * 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 FRAMEWORK_INITIALIZER = require.resolve('./jestAdapterInit'); + +const jestAdapter = async ( + globalConfig, + config, + environment, + runtime, + testPath, + sendMessageToJest +) => { + const { + initialize, + runAndTransformResultsToJestFormat + } = runtime.requireInternalModule(FRAMEWORK_INITIALIZER); + const {globals, snapshotState} = await initialize({ + config, + environment, + globalConfig, + localRequire: runtime.requireModule.bind(runtime), + parentProcess: process, + sendMessageToJest, + setGlobalsForRuntime: runtime.setGlobalsForRuntime.bind(runtime), + testPath + }); + + if (config.timers === 'fake' || config.timers === 'modern') { + // during setup, this cannot be null (and it's fine to explode if it is) + environment.fakeTimersModern.useFakeTimers(); + } else if (config.timers === 'legacy') { + environment.fakeTimers.useFakeTimers(); + } + + globals.beforeEach(() => { + if (config.resetModules) { + runtime.resetModules(); + } + + if (config.clearMocks) { + runtime.clearAllMocks(); + } + + if (config.resetMocks) { + runtime.resetAllMocks(); + + if (config.timers === 'legacy') { + // during setup, this cannot be null (and it's fine to explode if it is) + environment.fakeTimers.useFakeTimers(); + } + } + + if (config.restoreMocks) { + runtime.restoreAllMocks(); + } + }); + + for (const path of config.setupFilesAfterEnv) { + const esm = runtime.unstable_shouldLoadAsEsm(path); + + if (esm) { + await runtime.unstable_importModule(path); + } else { + runtime.requireModule(path); + } + } + + const esm = runtime.unstable_shouldLoadAsEsm(testPath); + + if (esm) { + await runtime.unstable_importModule(testPath); + } else { + runtime.requireModule(testPath); + } + + const results = await runAndTransformResultsToJestFormat({ + config, + globalConfig, + testPath + }); + + _addSnapshotData(results, snapshotState); // We need to copy the results object to ensure we don't leaks the prototypes + // from the VM. Jasmine creates the result objects in the parent process, we + // should consider doing that for circus as well. + + return (0, _jestUtil.deepCyclicCopy)(results, { + keepPrototype: false + }); +}; + +const _addSnapshotData = (results, snapshotState) => { + results.testResults.forEach(({fullName, status}) => { + if (status === 'pending' || status === 'failed') { + // if test is skipped or failed, we don't want to mark + // its snapshots as obsolete. + snapshotState.markSnapshotsAsCheckedForTest(fullName); + } + }); + const uncheckedCount = snapshotState.getUncheckedCount(); + const uncheckedKeys = snapshotState.getUncheckedKeys(); + + if (uncheckedCount) { + snapshotState.removeUncheckedKeys(); + } + + const status = snapshotState.save(); + results.snapshot.fileDeleted = status.deleted; + results.snapshot.added = snapshotState.added; + results.snapshot.matched = snapshotState.matched; + results.snapshot.unmatched = snapshotState.unmatched; + results.snapshot.updated = snapshotState.updated; + results.snapshot.unchecked = !status.deleted ? uncheckedCount : 0; // Copy the array to prevent memory leaks + + results.snapshot.uncheckedKeys = Array.from(uncheckedKeys); +}; + +module.exports = jestAdapter; diff --git a/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.d.ts b/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.d.ts new file mode 100644 index 000000000000..5deaa2f110ae --- /dev/null +++ b/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.d.ts @@ -0,0 +1,37 @@ +/** + * 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. + */ +/// +import type { JestEnvironment } from '@jest/environment'; +import { TestResult } from '@jest/test-result'; +import type { Config, Global } from '@jest/types'; +import type { TestFileEvent } from 'jest-runner'; +import { SnapshotStateType } from 'jest-snapshot'; +import globals from '..'; +import { Expect } from './jestExpect'; +declare type Process = NodeJS.Process; +interface JestGlobals extends Global.TestFrameworkGlobals { + expect: Expect; +} +export declare const initialize: ({ config, environment, globalConfig, localRequire, parentProcess, sendMessageToJest, setGlobalsForRuntime, testPath, }: { + config: Config.ProjectConfig; + environment: JestEnvironment; + globalConfig: Config.GlobalConfig; + localRequire: (path: Config.Path) => T; + testPath: Config.Path; + parentProcess: Process; + sendMessageToJest?: TestFileEvent<"test-file-start" | "test-file-success" | "test-file-failure" | "test-case-result"> | undefined; + setGlobalsForRuntime: (globals: JestGlobals) => void; +}) => Promise<{ + globals: Global.TestFrameworkGlobals; + snapshotState: SnapshotStateType; +}>; +export declare const runAndTransformResultsToJestFormat: ({ config, globalConfig, testPath, }: { + config: Config.ProjectConfig; + globalConfig: Config.GlobalConfig; + testPath: string; +}) => Promise; +export {}; diff --git a/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js b/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js new file mode 100644 index 000000000000..239dc76b2a32 --- /dev/null +++ b/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js @@ -0,0 +1,292 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.runAndTransformResultsToJestFormat = exports.initialize = void 0; + +var _throat = _interopRequireDefault(require('throat')); + +var _testResult = require('@jest/test-result'); + +var _expect = require('expect'); + +var _jestEach = require('jest-each'); + +var _jestMessageUtil = require('jest-message-util'); + +var _jestSnapshot = require('jest-snapshot'); + +var _ = _interopRequireDefault(require('..')); + +var _run = _interopRequireDefault(require('../run')); + +var _state = require('../state'); + +var _testCaseReportHandler = _interopRequireDefault( + require('../testCaseReportHandler') +); + +var _utils = require('../utils'); + +var _jestExpect = _interopRequireDefault(require('./jestExpect')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 initialize = async ({ + config, + environment, + globalConfig, + localRequire, + parentProcess, + sendMessageToJest, + setGlobalsForRuntime, + testPath +}) => { + if (globalConfig.testTimeout) { + (0, _state.getState)().testTimeout = globalConfig.testTimeout; + } + + const mutex = (0, _throat.default)(globalConfig.maxConcurrency); // @ts-expect-error + + const globalsObject = { + ..._.default, + fdescribe: _.default.describe.only, + fit: _.default.it.only, + xdescribe: _.default.describe.skip, + xit: _.default.it.skip, + xtest: _.default.it.skip + }; + + globalsObject.test.concurrent = (test => { + const concurrent = (testName, testFn, timeout) => { + // For concurrent tests we first run the function that returns promise, and then register a + // normal test that will be waiting on the returned promise (when we start the test, the promise + // will already be in the process of execution). + // Unfortunately at this stage there's no way to know if there are any `.only` tests in the suite + // that will result in this test to be skipped, so we'll be executing the promise function anyway, + // even if it ends up being skipped. + const promise = mutex(() => testFn()); + globalsObject.test(testName, () => promise, timeout); + }; + + const only = (testName, testFn, timeout) => { + const promise = mutex(() => testFn()); // eslint-disable-next-line jest/no-focused-tests + + test.only(testName, () => promise, timeout); + }; + + concurrent.only = only; + concurrent.skip = test.skip; + concurrent.each = (0, _jestEach.bind)(test, false); + concurrent.skip.each = (0, _jestEach.bind)(test.skip, false); + only.each = (0, _jestEach.bind)(test.only, false); + return concurrent; + })(globalsObject.test); + + (0, _state.addEventHandler)(eventHandler); + + if (environment.handleTestEvent) { + (0, _state.addEventHandler)(environment.handleTestEvent.bind(environment)); + } + + const runtimeGlobals = { + ...globalsObject, + expect: (0, _jestExpect.default)(globalConfig) + }; + setGlobalsForRuntime(runtimeGlobals); + + if (config.injectGlobals) { + Object.assign(environment.global, runtimeGlobals); + } + + await (0, _state.dispatch)({ + name: 'setup', + parentProcess, + runtimeGlobals, + testNamePattern: globalConfig.testNamePattern + }); + + if (config.testLocationInResults) { + await (0, _state.dispatch)({ + name: 'include_test_location_in_result' + }); + } // Jest tests snapshotSerializers in order preceding built-in serializers. + // Therefore, add in reverse because the last added is the first tested. + + config.snapshotSerializers + .concat() + .reverse() + .forEach(path => (0, _jestSnapshot.addSerializer)(localRequire(path))); + const {expand, updateSnapshot} = globalConfig; + const snapshotResolver = (0, _jestSnapshot.buildSnapshotResolver)(config); + const snapshotPath = snapshotResolver.resolveSnapshotPath(testPath); + const snapshotState = new _jestSnapshot.SnapshotState(snapshotPath, { + expand, + prettierPath: config.prettierPath, + updateSnapshot + }); // @ts-expect-error: snapshotState is a jest extension of `expect` + + (0, _expect.setState)({ + snapshotState, + testPath + }); + (0, _state.addEventHandler)(handleSnapshotStateAfterRetry(snapshotState)); + + if (sendMessageToJest) { + (0, _state.addEventHandler)( + (0, _testCaseReportHandler.default)(testPath, sendMessageToJest) + ); + } // Return it back to the outer scope (test runner outside the VM). + + return { + globals: globalsObject, + snapshotState + }; +}; + +exports.initialize = initialize; + +const runAndTransformResultsToJestFormat = async ({ + config, + globalConfig, + testPath +}) => { + const runResult = await (0, _run.default)(); + let numFailingTests = 0; + let numPassingTests = 0; + let numPendingTests = 0; + let numTodoTests = 0; + const assertionResults = runResult.testResults.map(testResult => { + let status; + + if (testResult.status === 'skip') { + status = 'pending'; + numPendingTests += 1; + } else if (testResult.status === 'todo') { + status = 'todo'; + numTodoTests += 1; + } else if (testResult.errors.length) { + status = 'failed'; + numFailingTests += 1; + } else { + status = 'passed'; + numPassingTests += 1; + } + + const ancestorTitles = testResult.testPath.filter( + name => name !== _state.ROOT_DESCRIBE_BLOCK_NAME + ); + const title = ancestorTitles.pop(); + return { + ancestorTitles, + duration: testResult.duration, + failureDetails: testResult.errorsDetailed, + failureMessages: testResult.errors, + fullName: title + ? ancestorTitles.concat(title).join(' ') + : ancestorTitles.join(' '), + invocations: testResult.invocations, + location: testResult.location, + numPassingAsserts: 0, + status, + title: testResult.testPath[testResult.testPath.length - 1] + }; + }); + let failureMessage = (0, _jestMessageUtil.formatResultsErrors)( + assertionResults, + config, + globalConfig, + testPath + ); + let testExecError; + + if (runResult.unhandledErrors.length) { + testExecError = { + message: '', + stack: runResult.unhandledErrors.join('\n') + }; + failureMessage = + (failureMessage || '') + + '\n\n' + + runResult.unhandledErrors + .map(err => + (0, _jestMessageUtil.formatExecError)(err, config, globalConfig) + ) + .join('\n'); + } + + await (0, _state.dispatch)({ + name: 'teardown' + }); + return { + ...(0, _testResult.createEmptyTestResult)(), + console: undefined, + displayName: config.displayName, + failureMessage, + numFailingTests, + numPassingTests, + numPendingTests, + numTodoTests, + testExecError, + testFilePath: testPath, + testResults: assertionResults + }; +}; + +exports.runAndTransformResultsToJestFormat = runAndTransformResultsToJestFormat; + +const handleSnapshotStateAfterRetry = snapshotState => event => { + switch (event.name) { + case 'test_retry': { + // Clear any snapshot data that occurred in previous test run + snapshotState.clear(); + } + } +}; + +const eventHandler = async event => { + switch (event.name) { + case 'test_start': { + (0, _expect.setState)({ + currentTestName: (0, _utils.getTestID)(event.test) + }); + break; + } + + case 'test_done': { + _addSuppressedErrors(event.test); + + _addExpectedAssertionErrors(event.test); + + break; + } + } +}; + +const _addExpectedAssertionErrors = test => { + const failures = (0, _expect.extractExpectedAssertionsErrors)(); + const errors = failures.map(failure => failure.error); + test.errors = test.errors.concat(errors); +}; // Get suppressed errors from ``jest-matchers`` that weren't throw during +// test execution and add them to the test result, potentially failing +// a passing test. + +const _addSuppressedErrors = test => { + const {suppressedErrors} = (0, _expect.getState)(); + (0, _expect.setState)({ + suppressedErrors: [] + }); + + if (suppressedErrors.length) { + test.errors = test.errors.concat(suppressedErrors); + } +}; diff --git a/packages/jest-circus/build/legacy-code-todo-rewrite/jestExpect.d.ts b/packages/jest-circus/build/legacy-code-todo-rewrite/jestExpect.d.ts new file mode 100644 index 000000000000..4bdcb51f2701 --- /dev/null +++ b/packages/jest-circus/build/legacy-code-todo-rewrite/jestExpect.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import expect = require('expect'); +export declare type Expect = typeof expect; +declare const _default: (config: Pick) => Expect; +export default _default; diff --git a/packages/jest-circus/build/legacy-code-todo-rewrite/jestExpect.js b/packages/jest-circus/build/legacy-code-todo-rewrite/jestExpect.js new file mode 100644 index 000000000000..a4cd100db3f5 --- /dev/null +++ b/packages/jest-circus/build/legacy-code-todo-rewrite/jestExpect.js @@ -0,0 +1,39 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _expect = _interopRequireDefault(require('expect')); + +var _jestSnapshot = require('jest-snapshot'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +var _default = config => { + _expect.default.setState({ + expand: config.expand + }); + + _expect.default.extend({ + toMatchInlineSnapshot: _jestSnapshot.toMatchInlineSnapshot, + toMatchSnapshot: _jestSnapshot.toMatchSnapshot, + toThrowErrorMatchingInlineSnapshot: + _jestSnapshot.toThrowErrorMatchingInlineSnapshot, + toThrowErrorMatchingSnapshot: _jestSnapshot.toThrowErrorMatchingSnapshot + }); + + _expect.default.addSnapshotSerializer = _jestSnapshot.addSerializer; + return _expect.default; +}; + +exports.default = _default; diff --git a/packages/jest-circus/build/run.d.ts b/packages/jest-circus/build/run.d.ts new file mode 100644 index 000000000000..6e8313633b8f --- /dev/null +++ b/packages/jest-circus/build/run.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Circus } from '@jest/types'; +declare const run: () => Promise; +export default run; diff --git a/packages/jest-circus/build/run.js b/packages/jest-circus/build/run.js new file mode 100644 index 000000000000..e268383b4774 --- /dev/null +++ b/packages/jest-circus/build/run.js @@ -0,0 +1,236 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _state = require('./state'); + +var _types = require('./types'); + +var _utils = require('./utils'); + +/** + * 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 run = async () => { + const {rootDescribeBlock} = (0, _state.getState)(); + await (0, _state.dispatch)({ + name: 'run_start' + }); + await _runTestsForDescribeBlock(rootDescribeBlock); + await (0, _state.dispatch)({ + name: 'run_finish' + }); + return (0, _utils.makeRunResult)( + (0, _state.getState)().rootDescribeBlock, + (0, _state.getState)().unhandledErrors + ); +}; + +const _runTestsForDescribeBlock = async describeBlock => { + await (0, _state.dispatch)({ + describeBlock, + name: 'run_describe_start' + }); + const {beforeAll, afterAll} = (0, _utils.getAllHooksForDescribe)( + describeBlock + ); + const isSkipped = describeBlock.mode === 'skip'; + + if (!isSkipped) { + for (const hook of beforeAll) { + await _callCircusHook({ + describeBlock, + hook + }); + } + } // Tests that fail and are retried we run after other tests + + const retryTimes = parseInt(global[_types.RETRY_TIMES], 10) || 0; + const deferredRetryTests = []; + + for (const child of describeBlock.children) { + switch (child.type) { + case 'describeBlock': { + await _runTestsForDescribeBlock(child); + break; + } + + case 'test': { + const hasErrorsBeforeTestRun = child.errors.length > 0; + await _runTest(child, isSkipped); + + if ( + hasErrorsBeforeTestRun === false && + retryTimes > 0 && + child.errors.length > 0 + ) { + deferredRetryTests.push(child); + } + + break; + } + } + } // Re-run failed tests n-times if configured + + for (const test of deferredRetryTests) { + let numRetriesAvailable = retryTimes; + + while (numRetriesAvailable > 0 && test.errors.length > 0) { + // Clear errors so retries occur + await (0, _state.dispatch)({ + name: 'test_retry', + test + }); + await _runTest(test, isSkipped); + numRetriesAvailable--; + } + } + + if (!isSkipped) { + for (const hook of afterAll) { + await _callCircusHook({ + describeBlock, + hook + }); + } + } + + await (0, _state.dispatch)({ + describeBlock, + name: 'run_describe_finish' + }); +}; + +const _runTest = async (test, parentSkipped) => { + await (0, _state.dispatch)({ + name: 'test_start', + test + }); + const testContext = Object.create(null); + const {hasFocusedTests, testNamePattern} = (0, _state.getState)(); + const isSkipped = + parentSkipped || + test.mode === 'skip' || + (hasFocusedTests && test.mode !== 'only') || + (testNamePattern && !testNamePattern.test((0, _utils.getTestID)(test))); + + if (isSkipped) { + await (0, _state.dispatch)({ + name: 'test_skip', + test + }); + return; + } + + if (test.mode === 'todo') { + await (0, _state.dispatch)({ + name: 'test_todo', + test + }); + return; + } + + const {afterEach, beforeEach} = (0, _utils.getEachHooksForTest)(test); + + for (const hook of beforeEach) { + if (test.errors.length) { + // If any of the before hooks failed already, we don't run any + // hooks after that. + break; + } + + await _callCircusHook({ + hook, + test, + testContext + }); + } + + await _callCircusTest(test, testContext); + + for (const hook of afterEach) { + await _callCircusHook({ + hook, + test, + testContext + }); + } // `afterAll` hooks should not affect test status (pass or fail), because if + // we had a global `afterAll` hook it would block all existing tests until + // this hook is executed. So we dispatch `test_done` right away. + + await (0, _state.dispatch)({ + name: 'test_done', + test + }); +}; + +const _callCircusHook = async ({hook, test, describeBlock, testContext}) => { + await (0, _state.dispatch)({ + hook, + name: 'hook_start' + }); + const timeout = hook.timeout || (0, _state.getState)().testTimeout; + + try { + await (0, _utils.callAsyncCircusFn)(hook, testContext, { + isHook: true, + timeout + }); + await (0, _state.dispatch)({ + describeBlock, + hook, + name: 'hook_success', + test + }); + } catch (error) { + await (0, _state.dispatch)({ + describeBlock, + error, + hook, + name: 'hook_failure', + test + }); + } +}; + +const _callCircusTest = async (test, testContext) => { + await (0, _state.dispatch)({ + name: 'test_fn_start', + test + }); + const timeout = test.timeout || (0, _state.getState)().testTimeout; + (0, _utils.invariant)( + test.fn, + `Tests with no 'fn' should have 'mode' set to 'skipped'` + ); + + if (test.errors.length) { + return; // We don't run the test if there's already an error in before hooks. + } + + try { + await (0, _utils.callAsyncCircusFn)(test, testContext, { + isHook: false, + timeout + }); + await (0, _state.dispatch)({ + name: 'test_fn_success', + test + }); + } catch (error) { + await (0, _state.dispatch)({ + error, + name: 'test_fn_failure', + test + }); + } +}; + +var _default = run; +exports.default = _default; diff --git a/packages/jest-circus/build/state.d.ts b/packages/jest-circus/build/state.d.ts new file mode 100644 index 000000000000..da0c34e4cbd4 --- /dev/null +++ b/packages/jest-circus/build/state.d.ts @@ -0,0 +1,13 @@ +/** + * 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. + */ +import type { Circus } from '@jest/types'; +export declare const ROOT_DESCRIBE_BLOCK_NAME = "ROOT_DESCRIBE_BLOCK"; +export declare const getState: () => Circus.State; +export declare const setState: (state: Circus.State) => Circus.State; +export declare const dispatch: (event: Circus.AsyncEvent) => Promise; +export declare const dispatchSync: (event: Circus.SyncEvent) => void; +export declare const addEventHandler: (handler: Circus.EventHandler) => void; diff --git a/packages/jest-circus/build/state.js b/packages/jest-circus/build/state.js new file mode 100644 index 000000000000..80ec74251e70 --- /dev/null +++ b/packages/jest-circus/build/state.js @@ -0,0 +1,75 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.addEventHandler = exports.dispatchSync = exports.dispatch = exports.setState = exports.getState = exports.ROOT_DESCRIBE_BLOCK_NAME = void 0; + +var _eventHandler = _interopRequireDefault(require('./eventHandler')); + +var _formatNodeAssertErrors = _interopRequireDefault( + require('./formatNodeAssertErrors') +); + +var _types = require('./types'); + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 eventHandlers = [_eventHandler.default, _formatNodeAssertErrors.default]; +const ROOT_DESCRIBE_BLOCK_NAME = 'ROOT_DESCRIBE_BLOCK'; +exports.ROOT_DESCRIBE_BLOCK_NAME = ROOT_DESCRIBE_BLOCK_NAME; +const ROOT_DESCRIBE_BLOCK = (0, _utils.makeDescribe)(ROOT_DESCRIBE_BLOCK_NAME); +const INITIAL_STATE = { + currentDescribeBlock: ROOT_DESCRIBE_BLOCK, + currentlyRunningTest: null, + expand: undefined, + hasFocusedTests: false, + hasStarted: false, + includeTestLocationInResult: false, + parentProcess: null, + rootDescribeBlock: ROOT_DESCRIBE_BLOCK, + testNamePattern: null, + testTimeout: 5000, + unhandledErrors: [] +}; +global[_types.STATE_SYM] = INITIAL_STATE; + +const getState = () => global[_types.STATE_SYM]; + +exports.getState = getState; + +const setState = state => (global[_types.STATE_SYM] = state); + +exports.setState = setState; + +const dispatch = async event => { + for (const handler of eventHandlers) { + await handler(event, getState()); + } +}; + +exports.dispatch = dispatch; + +const dispatchSync = event => { + for (const handler of eventHandlers) { + handler(event, getState()); + } +}; + +exports.dispatchSync = dispatchSync; + +const addEventHandler = handler => { + eventHandlers.push(handler); +}; + +exports.addEventHandler = addEventHandler; diff --git a/packages/jest-circus/build/testCaseReportHandler.d.ts b/packages/jest-circus/build/testCaseReportHandler.d.ts new file mode 100644 index 000000000000..b5a5462f100b --- /dev/null +++ b/packages/jest-circus/build/testCaseReportHandler.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import type { Circus } from '@jest/types'; +import type { TestFileEvent } from 'jest-runner'; +declare const testCaseReportHandler: (testPath: string, sendMessageToJest: TestFileEvent) => (event: Circus.Event) => void; +export default testCaseReportHandler; diff --git a/packages/jest-circus/build/testCaseReportHandler.js b/packages/jest-circus/build/testCaseReportHandler.js new file mode 100644 index 000000000000..4c8beb1d42aa --- /dev/null +++ b/packages/jest-circus/build/testCaseReportHandler.js @@ -0,0 +1,28 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _utils = require('./utils'); + +/** + * 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 testCaseReportHandler = (testPath, sendMessageToJest) => event => { + switch (event.name) { + case 'test_done': { + const testResult = (0, _utils.makeSingleTestResult)(event.test); + const testCaseResult = (0, _utils.parseSingleTestResult)(testResult); + sendMessageToJest('test-case-result', [testPath, testCaseResult]); + break; + } + } +}; + +var _default = testCaseReportHandler; +exports.default = _default; diff --git a/packages/jest-circus/build/types.d.ts b/packages/jest-circus/build/types.d.ts new file mode 100644 index 000000000000..3dc6d8884f3b --- /dev/null +++ b/packages/jest-circus/build/types.d.ts @@ -0,0 +1,21 @@ +/** + * 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. + */ +import type { Circus } from '@jest/types'; +import expect = require('expect'); +export declare const STATE_SYM: "STATE_SYM_SYMBOL"; +export declare const RETRY_TIMES: "RETRY_TIMES_SYMBOL"; +export declare const TEST_TIMEOUT_SYMBOL: "TEST_TIMEOUT_SYMBOL"; +declare global { + module NodeJS { + interface Global { + STATE_SYM_SYMBOL: Circus.State; + RETRY_TIMES_SYMBOL: string; + TEST_TIMEOUT_SYMBOL: number; + expect: typeof expect; + } + } +} diff --git a/packages/jest-circus/build/types.js b/packages/jest-circus/build/types.js new file mode 100644 index 000000000000..74def7211463 --- /dev/null +++ b/packages/jest-circus/build/types.js @@ -0,0 +1,21 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.TEST_TIMEOUT_SYMBOL = exports.RETRY_TIMES = exports.STATE_SYM = void 0; + +var _expect = _interopRequireDefault(require('expect')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +const STATE_SYM = Symbol('JEST_STATE_SYMBOL'); +exports.STATE_SYM = STATE_SYM; +const RETRY_TIMES = Symbol.for('RETRY_TIMES'); // To pass this value from Runtime object to state we need to use global[sym] + +exports.RETRY_TIMES = RETRY_TIMES; +const TEST_TIMEOUT_SYMBOL = Symbol.for('TEST_TIMEOUT_SYMBOL'); +exports.TEST_TIMEOUT_SYMBOL = TEST_TIMEOUT_SYMBOL; diff --git a/packages/jest-circus/build/utils.d.ts b/packages/jest-circus/build/utils.d.ts new file mode 100644 index 000000000000..f22b4fca0332 --- /dev/null +++ b/packages/jest-circus/build/utils.d.ts @@ -0,0 +1,33 @@ +/** + * 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. + */ +import type { AssertionResult } from '@jest/test-result'; +import type { Circus } from '@jest/types'; +export declare const makeDescribe: (name: Circus.BlockName, parent?: Circus.DescribeBlock | undefined, mode?: void | "skip" | "only" | "todo" | undefined) => Circus.DescribeBlock; +export declare const makeTest: (fn: Circus.TestFn, mode: Circus.TestMode, name: Circus.TestName, parent: Circus.DescribeBlock, timeout: number | undefined, asyncError: Circus.Exception) => Circus.TestEntry; +declare type DescribeHooks = { + beforeAll: Array; + afterAll: Array; +}; +export declare const getAllHooksForDescribe: (describe: Circus.DescribeBlock) => DescribeHooks; +declare type TestHooks = { + beforeEach: Array; + afterEach: Array; +}; +export declare const getEachHooksForTest: (test: Circus.TestEntry) => TestHooks; +export declare const describeBlockHasTests: (describe: Circus.DescribeBlock) => boolean; +export declare const callAsyncCircusFn: (testOrHook: Circus.TestEntry | Circus.Hook, testContext: Circus.TestContext | undefined, { isHook, timeout }: { + isHook: boolean; + timeout: number; +}) => Promise; +export declare const getTestDuration: (test: Circus.TestEntry) => number | null; +export declare const makeRunResult: (describeBlock: Circus.DescribeBlock, unhandledErrors: Array) => Circus.RunResult; +export declare const makeSingleTestResult: (test: Circus.TestEntry) => Circus.TestResult; +export declare const getTestID: (test: Circus.TestEntry) => string; +export declare const addErrorToEachTestUnderDescribe: (describeBlock: Circus.DescribeBlock, error: Circus.Exception, asyncError: Circus.Exception) => void; +export declare function invariant(condition: unknown, message?: string): asserts condition; +export declare const parseSingleTestResult: (testResult: Circus.TestResult) => AssertionResult; +export {}; diff --git a/packages/jest-circus/build/utils.js b/packages/jest-circus/build/utils.js new file mode 100644 index 000000000000..8a3458473210 --- /dev/null +++ b/packages/jest-circus/build/utils.js @@ -0,0 +1,553 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.invariant = invariant; +exports.parseSingleTestResult = exports.addErrorToEachTestUnderDescribe = exports.getTestID = exports.makeSingleTestResult = exports.makeRunResult = exports.getTestDuration = exports.callAsyncCircusFn = exports.describeBlockHasTests = exports.getEachHooksForTest = exports.getAllHooksForDescribe = exports.makeTest = exports.makeDescribe = void 0; + +var path = _interopRequireWildcard(require('path')); + +var _co = _interopRequireDefault(require('co')); + +var _dedent = _interopRequireDefault(require('dedent')); + +var _isGeneratorFn = _interopRequireDefault(require('is-generator-fn')); + +var _slash = _interopRequireDefault(require('slash')); + +var _stackUtils = _interopRequireDefault(require('stack-utils')); + +var _jestUtil = require('jest-util'); + +var _prettyFormat = _interopRequireDefault(require('pretty-format')); + +var _state = require('./state'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var jestNow = global[Symbol.for('jest-native-now')] || global.Date.now; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Promise = global[Symbol.for('jest-native-promise')] || global.Promise; +const stackUtils = new _stackUtils.default({ + cwd: 'A path that does not exist' +}); +const jestEachBuildDir = (0, _slash.default)( + path.dirname(require.resolve('jest-each')) +); + +function takesDoneCallback(fn) { + return fn.length > 0; +} + +function isGeneratorFunction(fn) { + return (0, _isGeneratorFn.default)(fn); +} + +const makeDescribe = (name, parent, mode) => { + let _mode = mode; + + if (parent && !mode) { + // If not set explicitly, inherit from the parent describe. + _mode = parent.mode; + } + + return { + type: 'describeBlock', + // eslint-disable-next-line sort-keys + children: [], + hooks: [], + mode: _mode, + name: (0, _jestUtil.convertDescriptorToString)(name), + parent, + tests: [] + }; +}; + +exports.makeDescribe = makeDescribe; + +const makeTest = (fn, mode, name, parent, timeout, asyncError) => ({ + type: 'test', + // eslint-disable-next-line sort-keys + asyncError, + duration: null, + errors: [], + fn, + invocations: 0, + mode, + name: (0, _jestUtil.convertDescriptorToString)(name), + parent, + seenDone: false, + startedAt: null, + status: null, + timeout +}); // Traverse the tree of describe blocks and return true if at least one describe +// block has an enabled test. + +exports.makeTest = makeTest; + +const hasEnabledTest = describeBlock => { + const {hasFocusedTests, testNamePattern} = (0, _state.getState)(); + return describeBlock.children.some(child => + child.type === 'describeBlock' + ? hasEnabledTest(child) + : !( + child.mode === 'skip' || + (hasFocusedTests && child.mode !== 'only') || + (testNamePattern && !testNamePattern.test(getTestID(child))) + ) + ); +}; + +const getAllHooksForDescribe = describe => { + const result = { + afterAll: [], + beforeAll: [] + }; + + if (hasEnabledTest(describe)) { + for (const hook of describe.hooks) { + switch (hook.type) { + case 'beforeAll': + result.beforeAll.push(hook); + break; + + case 'afterAll': + result.afterAll.push(hook); + break; + } + } + } + + return result; +}; + +exports.getAllHooksForDescribe = getAllHooksForDescribe; + +const getEachHooksForTest = test => { + const result = { + afterEach: [], + beforeEach: [] + }; + let block = test.parent; + + do { + const beforeEachForCurrentBlock = []; // TODO: inline after https://github.com/microsoft/TypeScript/pull/34840 is released + + let hook; + + for (hook of block.hooks) { + switch (hook.type) { + case 'beforeEach': + beforeEachForCurrentBlock.push(hook); + break; + + case 'afterEach': + result.afterEach.push(hook); + break; + } + } // 'beforeEach' hooks are executed from top to bottom, the opposite of the + // way we traversed it. + + result.beforeEach = [...beforeEachForCurrentBlock, ...result.beforeEach]; + } while ((block = block.parent)); + + return result; +}; + +exports.getEachHooksForTest = getEachHooksForTest; + +const describeBlockHasTests = describe => + describe.children.some( + child => child.type === 'test' || describeBlockHasTests(child) + ); + +exports.describeBlockHasTests = describeBlockHasTests; + +const _makeTimeoutMessage = (timeout, isHook) => + `Exceeded timeout of ${(0, _jestUtil.formatTime)(timeout)} for a ${ + isHook ? 'hook' : 'test' + }.\nUse jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test.`; // Global values can be overwritten by mocks or tests. We'll capture +// the original values in the variables before we require any files. + +const {setTimeout, clearTimeout} = global; + +function checkIsError(error) { + return !!(error && error.message && error.stack); +} + +const callAsyncCircusFn = (testOrHook, testContext, {isHook, timeout}) => { + let timeoutID; + let completed = false; + const {fn, asyncError} = testOrHook; + return new Promise((resolve, reject) => { + timeoutID = setTimeout( + () => reject(_makeTimeoutMessage(timeout, isHook)), + timeout + ); // If this fn accepts `done` callback we return a promise that fulfills as + // soon as `done` called. + + if (takesDoneCallback(fn)) { + let returnedValue = undefined; + + const done = reason => { + // We need to keep a stack here before the promise tick + const errorAtDone = new _jestUtil.ErrorWithStack(undefined, done); + + if (!completed && testOrHook.seenDone) { + errorAtDone.message = + 'Expected done to be called once, but it was called multiple times.'; + + if (reason) { + errorAtDone.message += + ' Reason: ' + + (0, _prettyFormat.default)(reason, { + maxDepth: 3 + }); + } + + reject(errorAtDone); + throw errorAtDone; + } else { + testOrHook.seenDone = true; + } // Use `Promise.resolve` to allow the event loop to go a single tick in case `done` is called synchronously + + Promise.resolve().then(() => { + if (returnedValue !== undefined) { + asyncError.message = (0, _dedent.default)` + Test functions cannot both take a 'done' callback and return something. Either use a 'done' callback, or return a promise. + Returned value: ${(0, _prettyFormat.default)(returnedValue, { + maxDepth: 3 + })} + `; + return reject(asyncError); + } + + let errorAsErrorObject; + + if (checkIsError(reason)) { + errorAsErrorObject = reason; + } else { + errorAsErrorObject = errorAtDone; + errorAtDone.message = `Failed: ${(0, _prettyFormat.default)( + reason, + { + maxDepth: 3 + } + )}`; + } // Consider always throwing, regardless if `reason` is set or not + + if (completed && reason) { + errorAsErrorObject.message = + 'Caught error after test environment was torn down\n\n' + + errorAsErrorObject.message; + throw errorAsErrorObject; + } + + return reason ? reject(errorAsErrorObject) : resolve(); + }); + }; + + returnedValue = fn.call(testContext, done); + return; + } + + let returnedValue; + + if (isGeneratorFunction(fn)) { + returnedValue = _co.default.wrap(fn).call({}); + } else { + try { + returnedValue = fn.call(testContext); + } catch (error) { + reject(error); + return; + } + } // If it's a Promise, return it. Test for an object with a `then` function + // to support custom Promise implementations. + + if ( + typeof returnedValue === 'object' && + returnedValue !== null && + typeof returnedValue.then === 'function' + ) { + returnedValue.then(resolve, reject); + return; + } + + if (!isHook && returnedValue !== undefined) { + reject( + new Error((0, _dedent.default)` + test functions can only return Promise or undefined. + Returned value: ${(0, _prettyFormat.default)(returnedValue, { + maxDepth: 3 + })} + `) + ); + return; + } // Otherwise this test is synchronous, and if it didn't throw it means + // it passed. + + resolve(); + }) + .then(() => { + var _timeoutID$unref, _timeoutID; + + completed = true; // If timeout is not cleared/unrefed the node process won't exit until + // it's resolved. + + (_timeoutID$unref = (_timeoutID = timeoutID).unref) === null || + _timeoutID$unref === void 0 + ? void 0 + : _timeoutID$unref.call(_timeoutID); + clearTimeout(timeoutID); + }) + .catch(error => { + var _timeoutID$unref2, _timeoutID2; + + completed = true; + (_timeoutID$unref2 = (_timeoutID2 = timeoutID).unref) === null || + _timeoutID$unref2 === void 0 + ? void 0 + : _timeoutID$unref2.call(_timeoutID2); + clearTimeout(timeoutID); + throw error; + }); +}; + +exports.callAsyncCircusFn = callAsyncCircusFn; + +const getTestDuration = test => { + const {startedAt} = test; + return typeof startedAt === 'number' ? jestNow() - startedAt : null; +}; + +exports.getTestDuration = getTestDuration; + +const makeRunResult = (describeBlock, unhandledErrors) => ({ + testResults: makeTestResults(describeBlock), + unhandledErrors: unhandledErrors.map(_getError).map(getErrorStack) +}); + +exports.makeRunResult = makeRunResult; + +const makeSingleTestResult = test => { + const {includeTestLocationInResult} = (0, _state.getState)(); + const testPath = []; + let parent = test; + const {status} = test; + invariant(status, 'Status should be present after tests are run.'); + + do { + testPath.unshift(parent.name); + } while ((parent = parent.parent)); + + let location = null; + + if (includeTestLocationInResult) { + var _parsedLine, _parsedLine$file; + + const stackLines = test.asyncError.stack.split('\n'); + const stackLine = stackLines[1]; + let parsedLine = stackUtils.parseLine(stackLine); + + if ( + (_parsedLine = parsedLine) !== null && + _parsedLine !== void 0 && + (_parsedLine$file = _parsedLine.file) !== null && + _parsedLine$file !== void 0 && + _parsedLine$file.startsWith(jestEachBuildDir) + ) { + const stackLine = stackLines[4]; + parsedLine = stackUtils.parseLine(stackLine); + } + + if ( + parsedLine && + typeof parsedLine.column === 'number' && + typeof parsedLine.line === 'number' + ) { + location = { + column: parsedLine.column, + line: parsedLine.line + }; + } + } + + const errorsDetailed = test.errors.map(_getError); + return { + duration: test.duration, + errors: errorsDetailed.map(getErrorStack), + errorsDetailed, + invocations: test.invocations, + location, + status, + testPath: Array.from(testPath) + }; +}; + +exports.makeSingleTestResult = makeSingleTestResult; + +const makeTestResults = describeBlock => { + const testResults = []; + + for (const child of describeBlock.children) { + switch (child.type) { + case 'describeBlock': { + testResults.push(...makeTestResults(child)); + break; + } + + case 'test': { + testResults.push(makeSingleTestResult(child)); + break; + } + } + } + + return testResults; +}; // Return a string that identifies the test (concat of parent describe block +// names + test title) + +const getTestID = test => { + const titles = []; + let parent = test; + + do { + titles.unshift(parent.name); + } while ((parent = parent.parent)); + + titles.shift(); // remove TOP_DESCRIBE_BLOCK_NAME + + return titles.join(' '); +}; + +exports.getTestID = getTestID; + +const _getError = errors => { + let error; + let asyncError; + + if (Array.isArray(errors)) { + error = errors[0]; + asyncError = errors[1]; + } else { + error = errors; + asyncError = new Error(); + } + + if (error && (typeof error.stack === 'string' || error.message)) { + return error; + } + + asyncError.message = `thrown: ${(0, _prettyFormat.default)(error, { + maxDepth: 3 + })}`; + return asyncError; +}; + +const getErrorStack = error => + typeof error.stack === 'string' ? error.stack : error.message; + +const addErrorToEachTestUnderDescribe = (describeBlock, error, asyncError) => { + for (const child of describeBlock.children) { + switch (child.type) { + case 'describeBlock': + addErrorToEachTestUnderDescribe(child, error, asyncError); + break; + + case 'test': + child.errors.push([error, asyncError]); + break; + } + } +}; + +exports.addErrorToEachTestUnderDescribe = addErrorToEachTestUnderDescribe; + +function invariant(condition, message) { + if (!condition) { + throw new Error(message); + } +} + +const parseSingleTestResult = testResult => { + let status; + + if (testResult.status === 'skip') { + status = 'pending'; + } else if (testResult.status === 'todo') { + status = 'todo'; + } else if (testResult.errors.length > 0) { + status = 'failed'; + } else { + status = 'passed'; + } + + const ancestorTitles = testResult.testPath.filter( + name => name !== _state.ROOT_DESCRIBE_BLOCK_NAME + ); + const title = ancestorTitles.pop(); + return { + ancestorTitles, + duration: testResult.duration, + failureDetails: testResult.errorsDetailed, + failureMessages: Array.from(testResult.errors), + fullName: title + ? ancestorTitles.concat(title).join(' ') + : ancestorTitles.join(' '), + invocations: testResult.invocations, + location: testResult.location, + numPassingAsserts: 0, + status, + title: testResult.testPath[testResult.testPath.length - 1] + }; +}; + +exports.parseSingleTestResult = parseSingleTestResult; diff --git a/packages/jest-cli/bin/jest.js b/packages/jest-cli/bin/jest.js index fc6308c8d2cf..82a1357a2a0e 100755 --- a/packages/jest-cli/bin/jest.js +++ b/packages/jest-cli/bin/jest.js @@ -6,6 +6,8 @@ * LICENSE file in the root directory of this source tree. */ +require('module-alias/register'); + const importLocal = require('import-local'); if (!importLocal(__filename)) { diff --git a/packages/jest-cli/build/cli/args.d.ts b/packages/jest-cli/build/cli/args.d.ts new file mode 100644 index 000000000000..908ae789355c --- /dev/null +++ b/packages/jest-cli/build/cli/args.d.ts @@ -0,0 +1,501 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export declare function check(argv: Config.Argv): true; +export declare const usage = "Usage: $0 [--config=] [TestPathPattern]"; +export declare const docs = "Documentation: https://jestjs.io/"; +export declare const options: { + readonly all: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly automock: { + readonly default: undefined; + readonly description: "Automock all files by default."; + readonly type: "boolean"; + }; + readonly bail: { + readonly alias: "b"; + readonly default: undefined; + readonly description: "Exit the test suite immediately after `n` number of failing tests."; + readonly type: "boolean"; + }; + readonly browser: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly cache: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly cacheDirectory: { + readonly description: string; + readonly type: "string"; + }; + readonly changedFilesWithAncestor: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly changedSince: { + readonly description: string; + readonly nargs: 1; + readonly type: "string"; + }; + readonly ci: { + readonly default: boolean; + readonly description: string; + readonly type: "boolean"; + }; + readonly clearCache: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly clearMocks: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly collectCoverage: { + readonly default: undefined; + readonly description: "Alias for --coverage."; + readonly type: "boolean"; + }; + readonly collectCoverageFrom: { + readonly description: string; + readonly type: "string"; + }; + readonly collectCoverageOnlyFrom: { + readonly description: "Explicit list of paths coverage will be restricted to."; + readonly string: true; + readonly type: "array"; + }; + readonly color: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly colors: { + readonly default: undefined; + readonly description: "Alias for `--color`."; + readonly type: "boolean"; + }; + readonly config: { + readonly alias: "c"; + readonly description: string; + readonly type: "string"; + }; + readonly coverage: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly coverageDirectory: { + readonly description: "The directory where Jest should output its coverage files."; + readonly type: "string"; + }; + readonly coveragePathIgnorePatterns: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly coverageProvider: { + readonly choices: readonly ["babel", "v8"]; + readonly description: "Select between Babel and V8 to collect coverage"; + }; + readonly coverageReporters: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly coverageThreshold: { + readonly description: string; + readonly type: "string"; + }; + readonly debug: { + readonly default: undefined; + readonly description: "Print debugging info about your jest config."; + readonly type: "boolean"; + }; + readonly detectLeaks: { + readonly default: false; + readonly description: string; + readonly type: "boolean"; + }; + readonly detectOpenHandles: { + readonly default: false; + readonly description: string; + readonly type: "boolean"; + }; + readonly env: { + readonly description: string; + readonly type: "string"; + }; + readonly errorOnDeprecated: { + readonly default: false; + readonly description: "Make calling deprecated APIs throw helpful error messages."; + readonly type: "boolean"; + }; + readonly expand: { + readonly alias: "e"; + readonly default: undefined; + readonly description: "Use this flag to show full diffs instead of a patch."; + readonly type: "boolean"; + }; + readonly filter: { + readonly default: undefined; + readonly description: string; + readonly type: "string"; + }; + readonly findRelatedTests: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly forceExit: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly globalSetup: { + readonly description: "The path to a module that runs before All Tests."; + readonly type: "string"; + }; + readonly globalTeardown: { + readonly description: "The path to a module that runs after All Tests."; + readonly type: "string"; + }; + readonly globals: { + readonly description: string; + readonly type: "string"; + }; + readonly haste: { + readonly description: "A JSON string with map of variables for the haste module system"; + readonly type: "string"; + }; + readonly init: { + readonly description: "Generate a basic configuration file"; + readonly type: "boolean"; + }; + readonly injectGlobals: { + readonly description: "Should Jest inject global variables or not"; + readonly type: "boolean"; + }; + readonly json: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly lastCommit: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly listTests: { + readonly default: false; + readonly description: string; + readonly type: "boolean"; + }; + readonly logHeapUsage: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly maxConcurrency: { + readonly default: 5; + readonly description: string; + readonly type: "number"; + }; + readonly maxWorkers: { + readonly alias: "w"; + readonly description: string; + readonly type: "string"; + }; + readonly moduleDirectories: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly moduleFileExtensions: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly moduleNameMapper: { + readonly description: string; + readonly type: "string"; + }; + readonly modulePathIgnorePatterns: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly modulePaths: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly noStackTrace: { + readonly default: undefined; + readonly description: "Disables stack trace in test results output"; + readonly type: "boolean"; + }; + readonly notify: { + readonly default: undefined; + readonly description: "Activates notifications for test results."; + readonly type: "boolean"; + }; + readonly notifyMode: { + readonly default: "failure-change"; + readonly description: "Specifies when notifications will appear for test results."; + readonly type: "string"; + }; + readonly onlyChanged: { + readonly alias: "o"; + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly onlyFailures: { + readonly alias: "f"; + readonly default: undefined; + readonly description: "Run tests that failed in the previous execution."; + readonly type: "boolean"; + }; + readonly outputFile: { + readonly description: string; + readonly type: "string"; + }; + readonly passWithNoTests: { + readonly default: false; + readonly description: "Will not fail if no tests are found (for example while using `--testPathPattern`.)"; + readonly type: "boolean"; + }; + readonly preset: { + readonly description: "A preset that is used as a base for Jest's configuration."; + readonly type: "string"; + }; + readonly prettierPath: { + readonly default: undefined; + readonly description: "The path to the \"prettier\" module used for inline snapshots."; + readonly type: "string"; + }; + readonly projects: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly reporters: { + readonly description: "A list of custom reporters for the test suite."; + readonly string: true; + readonly type: "array"; + }; + readonly resetMocks: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly resetModules: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly resolver: { + readonly description: "A JSON string which allows the use of a custom resolver."; + readonly type: "string"; + }; + readonly restoreMocks: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly rootDir: { + readonly description: string; + readonly type: "string"; + }; + readonly roots: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly runInBand: { + readonly alias: "i"; + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly runTestsByPath: { + readonly default: false; + readonly description: string; + readonly type: "boolean"; + }; + readonly runner: { + readonly description: "Allows to use a custom runner instead of Jest's default test runner."; + readonly type: "string"; + }; + readonly selectProjects: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly setupFiles: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly setupFilesAfterEnv: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly showConfig: { + readonly default: undefined; + readonly description: "Print your jest config and then exits."; + readonly type: "boolean"; + }; + readonly silent: { + readonly default: undefined; + readonly description: "Prevent tests from printing messages through the console."; + readonly type: "boolean"; + }; + readonly skipFilter: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly snapshotSerializers: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly testEnvironment: { + readonly description: "Alias for --env"; + readonly type: "string"; + }; + readonly testEnvironmentOptions: { + readonly description: string; + readonly type: "string"; + }; + readonly testFailureExitCode: { + readonly description: "Exit code of `jest` command if the test run failed"; + readonly type: "string"; + }; + readonly testLocationInResults: { + readonly default: false; + readonly description: "Add `location` information to the test results"; + readonly type: "boolean"; + }; + readonly testMatch: { + readonly description: "The glob patterns Jest uses to detect test files."; + readonly string: true; + readonly type: "array"; + }; + readonly testNamePattern: { + readonly alias: "t"; + readonly description: "Run only tests with a name that matches the regex pattern."; + readonly type: "string"; + }; + readonly testPathIgnorePatterns: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly testPathPattern: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly testRegex: { + readonly description: "A string or array of string regexp patterns that Jest uses to detect test files."; + readonly string: true; + readonly type: "array"; + }; + readonly testResultsProcessor: { + readonly description: string; + readonly type: "string"; + }; + readonly testRunner: { + readonly description: string; + readonly type: "string"; + }; + readonly testSequencer: { + readonly description: string; + readonly type: "string"; + }; + readonly testTimeout: { + readonly description: "This option sets the default timeouts of test cases."; + readonly type: "number"; + }; + readonly testURL: { + readonly description: "This option sets the URL for the jsdom environment."; + readonly type: "string"; + }; + readonly timers: { + readonly description: string; + readonly type: "string"; + }; + readonly transform: { + readonly description: string; + readonly type: "string"; + }; + readonly transformIgnorePatterns: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly unmockedModulePathPatterns: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly updateSnapshot: { + readonly alias: "u"; + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly useStderr: { + readonly default: undefined; + readonly description: "Divert all output to stderr."; + readonly type: "boolean"; + }; + readonly verbose: { + readonly default: undefined; + readonly description: "Display individual test results with the test suite hierarchy."; + readonly type: "boolean"; + }; + readonly version: { + readonly alias: "v"; + readonly default: undefined; + readonly description: "Print the version and exit"; + readonly type: "boolean"; + }; + readonly watch: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly watchAll: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; + readonly watchPathIgnorePatterns: { + readonly description: string; + readonly string: true; + readonly type: "array"; + }; + readonly watchman: { + readonly default: undefined; + readonly description: string; + readonly type: "boolean"; + }; +}; diff --git a/packages/jest-cli/build/cli/args.js b/packages/jest-cli/build/cli/args.js new file mode 100644 index 000000000000..c87bd05c2975 --- /dev/null +++ b/packages/jest-cli/build/cli/args.js @@ -0,0 +1,779 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.check = check; +exports.options = exports.docs = exports.usage = void 0; + +function _isCi() { + const data = _interopRequireDefault(require('is-ci')); + + _isCi = function () { + return data; + }; + + return data; +} + +function _jestConfig() { + const data = require('jest-config'); + + _jestConfig = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +function check(argv) { + if (argv.runInBand && argv.hasOwnProperty('maxWorkers')) { + throw new Error( + 'Both --runInBand and --maxWorkers were specified, but these two ' + + 'options do not make sense together. Which is it?' + ); + } + + for (const key of [ + 'onlyChanged', + 'lastCommit', + 'changedFilesWithAncestor', + 'changedSince' + ]) { + if (argv[key] && argv.watchAll) { + throw new Error( + `Both --${key} and --watchAll were specified, but these two ` + + 'options do not make sense together. Try the --watch option which ' + + 'reruns only tests related to changed files.' + ); + } + } + + if (argv.onlyFailures && argv.watchAll) { + throw new Error( + `Both --onlyFailures and --watchAll were specified, but these two ` + + 'options do not make sense together.' + ); + } + + if (argv.findRelatedTests && argv._.length === 0) { + throw new Error( + 'The --findRelatedTests option requires file paths to be specified.\n' + + 'Example usage: jest --findRelatedTests ./src/source.js ' + + './src/index.js.' + ); + } + + if (argv.hasOwnProperty('maxWorkers') && argv.maxWorkers === undefined) { + throw new Error( + 'The --maxWorkers (-w) option requires a number or string to be specified.\n' + + 'Example usage: jest --maxWorkers 2\n' + + 'Example usage: jest --maxWorkers 50%\n' + + 'Or did you mean --watch?' + ); + } + + if (argv.selectProjects && argv.selectProjects.length === 0) { + throw new Error( + 'The --selectProjects option requires the name of at least one project to be specified.\n' + + 'Example usage: jest --selectProjects my-first-project my-second-project' + ); + } + + if ( + argv.config && + !(0, _jestConfig().isJSONString)(argv.config) && + !argv.config.match( + new RegExp( + `\\.(${_jestConfig() + .constants.JEST_CONFIG_EXT_ORDER.map(e => e.substring(1)) + .join('|')})$`, + 'i' + ) + ) + ) { + throw new Error( + `The --config option requires a JSON string literal, or a file path with one of these extensions: ${_jestConfig().constants.JEST_CONFIG_EXT_ORDER.join( + ', ' + )}.\nExample usage: jest --config ./jest.config.js` + ); + } + + return true; +} + +const usage = 'Usage: $0 [--config=] [TestPathPattern]'; +exports.usage = usage; +const docs = 'Documentation: https://jestjs.io/'; +exports.docs = docs; +const options = { + all: { + default: undefined, + description: + 'The opposite of `onlyChanged`. If `onlyChanged` is set by ' + + 'default, running jest with `--all` will force Jest to run all tests ' + + 'instead of running only tests related to changed files.', + type: 'boolean' + }, + automock: { + default: undefined, + description: 'Automock all files by default.', + type: 'boolean' + }, + bail: { + alias: 'b', + default: undefined, + description: + 'Exit the test suite immediately after `n` number of failing tests.', + type: 'boolean' + }, + browser: { + default: undefined, + description: + 'Respect the "browser" field in package.json ' + + 'when resolving modules. Some packages export different versions ' + + 'based on whether they are operating in node.js or a browser.', + type: 'boolean' + }, + cache: { + default: undefined, + description: + 'Whether to use the transform cache. Disable the cache ' + + 'using --no-cache.', + type: 'boolean' + }, + cacheDirectory: { + description: + 'The directory where Jest should store its cached ' + + ' dependency information.', + type: 'string' + }, + changedFilesWithAncestor: { + default: undefined, + description: + 'Runs tests related to the current changes and the changes made in the ' + + 'last commit. Behaves similarly to `--onlyChanged`.', + type: 'boolean' + }, + changedSince: { + description: + 'Runs tests related to the changes since the provided branch. If the ' + + 'current branch has diverged from the given branch, then only changes ' + + 'made locally will be tested. Behaves similarly to `--onlyChanged`.', + nargs: 1, + type: 'string' + }, + ci: { + default: _isCi().default, + description: + 'Whether to run Jest in continuous integration (CI) mode. ' + + 'This option is on by default in most popular CI environments. It will ' + + 'prevent snapshots from being written unless explicitly requested.', + type: 'boolean' + }, + clearCache: { + default: undefined, + description: + 'Clears the configured Jest cache directory and then exits. ' + + 'Default directory can be found by calling jest --showConfig', + type: 'boolean' + }, + clearMocks: { + default: undefined, + description: + 'Automatically clear mock calls and instances between every ' + + 'test. Equivalent to calling jest.clearAllMocks() between each test.', + type: 'boolean' + }, + collectCoverage: { + default: undefined, + description: 'Alias for --coverage.', + type: 'boolean' + }, + collectCoverageFrom: { + description: + 'A glob pattern relative to matching the files that coverage ' + + 'info needs to be collected from.', + type: 'string' + }, + collectCoverageOnlyFrom: { + description: 'Explicit list of paths coverage will be restricted to.', + string: true, + type: 'array' + }, + color: { + default: undefined, + description: + 'Forces test results output color highlighting (even if ' + + 'stdout is not a TTY). Set to false if you would like to have no colors.', + type: 'boolean' + }, + colors: { + default: undefined, + description: 'Alias for `--color`.', + type: 'boolean' + }, + config: { + alias: 'c', + description: + 'The path to a jest config file specifying how to find ' + + 'and execute tests. If no rootDir is set in the config, the directory ' + + 'containing the config file is assumed to be the rootDir for the project.' + + 'This can also be a JSON encoded value which Jest will use as configuration.', + type: 'string' + }, + coverage: { + default: undefined, + description: + 'Indicates that test coverage information should be ' + + 'collected and reported in the output.', + type: 'boolean' + }, + coverageDirectory: { + description: 'The directory where Jest should output its coverage files.', + type: 'string' + }, + coveragePathIgnorePatterns: { + description: + 'An array of regexp pattern strings that are matched ' + + 'against all file paths before executing the test. If the file path' + + 'matches any of the patterns, coverage information will be skipped.', + string: true, + type: 'array' + }, + coverageProvider: { + choices: ['babel', 'v8'], + description: 'Select between Babel and V8 to collect coverage' + }, + coverageReporters: { + description: + 'A list of reporter names that Jest uses when writing ' + + 'coverage reports. Any istanbul reporter can be used.', + string: true, + type: 'array' + }, + coverageThreshold: { + description: + 'A JSON string with which will be used to configure ' + + 'minimum threshold enforcement for coverage results', + type: 'string' + }, + debug: { + default: undefined, + description: 'Print debugging info about your jest config.', + type: 'boolean' + }, + detectLeaks: { + default: false, + description: + '**EXPERIMENTAL**: Detect memory leaks in tests. After executing a ' + + 'test, it will try to garbage collect the global object used, and fail ' + + 'if it was leaked', + type: 'boolean' + }, + detectOpenHandles: { + default: false, + description: + 'Print out remaining open handles preventing Jest from exiting at the ' + + 'end of a test run. Implies `runInBand`.', + type: 'boolean' + }, + env: { + description: + 'The test environment used for all tests. This can point to ' + + 'any file or node module. Examples: `jsdom`, `node` or ' + + '`path/to/my-environment.js`', + type: 'string' + }, + errorOnDeprecated: { + default: false, + description: 'Make calling deprecated APIs throw helpful error messages.', + type: 'boolean' + }, + expand: { + alias: 'e', + default: undefined, + description: 'Use this flag to show full diffs instead of a patch.', + type: 'boolean' + }, + filter: { + default: undefined, + description: + 'Path to a module exporting a filtering function. This method receives ' + + 'a list of tests which can be manipulated to exclude tests from ' + + 'running. Especially useful when used in conjunction with a testing ' + + 'infrastructure to filter known broken tests.', + type: 'string' + }, + findRelatedTests: { + default: undefined, + description: + 'Find related tests for a list of source files that were ' + + 'passed in as arguments. Useful for pre-commit hook integration to run ' + + 'the minimal amount of tests necessary.', + type: 'boolean' + }, + forceExit: { + default: undefined, + description: + 'Force Jest to exit after all tests have completed running. ' + + 'This is useful when resources set up by test code cannot be ' + + 'adequately cleaned up.', + type: 'boolean' + }, + globalSetup: { + description: 'The path to a module that runs before All Tests.', + type: 'string' + }, + globalTeardown: { + description: 'The path to a module that runs after All Tests.', + type: 'string' + }, + globals: { + description: + 'A JSON string with map of global variables that need ' + + 'to be available in all test environments.', + type: 'string' + }, + haste: { + description: + 'A JSON string with map of variables for the haste module system', + type: 'string' + }, + init: { + description: 'Generate a basic configuration file', + type: 'boolean' + }, + injectGlobals: { + description: 'Should Jest inject global variables or not', + type: 'boolean' + }, + json: { + default: undefined, + description: + 'Prints the test results in JSON. This mode will send all ' + + 'other test output and user messages to stderr.', + type: 'boolean' + }, + lastCommit: { + default: undefined, + description: + 'Run all tests affected by file changes in the last commit made. ' + + 'Behaves similarly to `--onlyChanged`.', + type: 'boolean' + }, + listTests: { + default: false, + description: + 'Lists all tests Jest will run given the arguments and ' + + 'exits. Most useful in a CI system together with `--findRelatedTests` ' + + 'to determine the tests Jest will run based on specific files', + type: 'boolean' + }, + logHeapUsage: { + default: undefined, + description: + 'Logs the heap usage after every test. Useful to debug ' + + 'memory leaks. Use together with `--runInBand` and `--expose-gc` in ' + + 'node.', + type: 'boolean' + }, + maxConcurrency: { + default: 5, + description: + 'Specifies the maximum number of tests that are allowed to run' + + 'concurrently. This only affects tests using `test.concurrent`.', + type: 'number' + }, + maxWorkers: { + alias: 'w', + description: + 'Specifies the maximum number of workers the worker-pool ' + + 'will spawn for running tests. This defaults to the number of the ' + + 'cores available on your machine. (its usually best not to override ' + + 'this default)', + type: 'string' + }, + moduleDirectories: { + description: + 'An array of directory names to be searched recursively ' + + "up from the requiring module's location.", + string: true, + type: 'array' + }, + moduleFileExtensions: { + description: + 'An array of file extensions your modules use. If you ' + + 'require modules without specifying a file extension, these are the ' + + 'extensions Jest will look for. ', + string: true, + type: 'array' + }, + moduleNameMapper: { + description: + 'A JSON string with a map from regular expressions to ' + + 'module names or to arrays of module names that allow to stub ' + + 'out resources, like images or styles with a single module', + type: 'string' + }, + modulePathIgnorePatterns: { + description: + 'An array of regexp pattern strings that are matched ' + + 'against all module paths before those paths are to be considered ' + + '"visible" to the module loader.', + string: true, + type: 'array' + }, + modulePaths: { + description: + 'An alternative API to setting the NODE_PATH env variable, ' + + 'modulePaths is an array of absolute paths to additional locations to ' + + 'search when resolving modules.', + string: true, + type: 'array' + }, + noStackTrace: { + default: undefined, + description: 'Disables stack trace in test results output', + type: 'boolean' + }, + notify: { + default: undefined, + description: 'Activates notifications for test results.', + type: 'boolean' + }, + notifyMode: { + default: 'failure-change', + description: 'Specifies when notifications will appear for test results.', + type: 'string' + }, + onlyChanged: { + alias: 'o', + default: undefined, + description: + 'Attempts to identify which tests to run based on which ' + + "files have changed in the current repository. Only works if you're " + + 'running tests in a git or hg repository at the moment.', + type: 'boolean' + }, + onlyFailures: { + alias: 'f', + default: undefined, + description: 'Run tests that failed in the previous execution.', + type: 'boolean' + }, + outputFile: { + description: + 'Write test results to a file when the --json option is ' + + 'also specified.', + type: 'string' + }, + passWithNoTests: { + default: false, + description: + 'Will not fail if no tests are found (for example while using `--testPathPattern`.)', + type: 'boolean' + }, + preset: { + description: "A preset that is used as a base for Jest's configuration.", + type: 'string' + }, + prettierPath: { + default: undefined, + description: 'The path to the "prettier" module used for inline snapshots.', + type: 'string' + }, + projects: { + description: + 'A list of projects that use Jest to run all tests of all ' + + 'projects in a single instance of Jest.', + string: true, + type: 'array' + }, + reporters: { + description: 'A list of custom reporters for the test suite.', + string: true, + type: 'array' + }, + resetMocks: { + default: undefined, + description: + 'Automatically reset mock state between every test. ' + + 'Equivalent to calling jest.resetAllMocks() between each test.', + type: 'boolean' + }, + resetModules: { + default: undefined, + description: + 'If enabled, the module registry for every test file will ' + + 'be reset before running each individual test.', + type: 'boolean' + }, + resolver: { + description: 'A JSON string which allows the use of a custom resolver.', + type: 'string' + }, + restoreMocks: { + default: undefined, + description: + 'Automatically restore mock state and implementation between every test. ' + + 'Equivalent to calling jest.restoreAllMocks() between each test.', + type: 'boolean' + }, + rootDir: { + description: + 'The root directory that Jest should scan for tests and ' + + 'modules within.', + type: 'string' + }, + roots: { + description: + 'A list of paths to directories that Jest should use to ' + + 'search for files in.', + string: true, + type: 'array' + }, + runInBand: { + alias: 'i', + default: undefined, + description: + 'Run all tests serially in the current process (rather than ' + + 'creating a worker pool of child processes that run tests). This ' + + 'is sometimes useful for debugging, but such use cases are pretty ' + + 'rare.', + type: 'boolean' + }, + runTestsByPath: { + default: false, + description: + 'Used when provided patterns are exact file paths. This avoids ' + + 'converting them into a regular expression and matching it against ' + + 'every single file.', + type: 'boolean' + }, + runner: { + description: + "Allows to use a custom runner instead of Jest's default test runner.", + type: 'string' + }, + selectProjects: { + description: + 'Run only the tests of the specified projects.' + + 'Jest uses the attribute `displayName` in the configuration to identify each project.', + string: true, + type: 'array' + }, + setupFiles: { + description: + 'A list of paths to modules that run some code to configure or ' + + 'set up the testing environment before each test. ', + string: true, + type: 'array' + }, + setupFilesAfterEnv: { + description: + 'A list of paths to modules that run some code to configure or ' + + 'set up the testing framework before each test ', + string: true, + type: 'array' + }, + showConfig: { + default: undefined, + description: 'Print your jest config and then exits.', + type: 'boolean' + }, + silent: { + default: undefined, + description: 'Prevent tests from printing messages through the console.', + type: 'boolean' + }, + skipFilter: { + default: undefined, + description: + 'Disables the filter provided by --filter. Useful for CI jobs, or ' + + 'local enforcement when fixing tests.', + type: 'boolean' + }, + snapshotSerializers: { + description: + 'A list of paths to snapshot serializer modules Jest should ' + + 'use for snapshot testing.', + string: true, + type: 'array' + }, + testEnvironment: { + description: 'Alias for --env', + type: 'string' + }, + testEnvironmentOptions: { + description: + 'Test environment options that will be passed to the testEnvironment. ' + + 'The relevant options depend on the environment.', + type: 'string' // Object + }, + testFailureExitCode: { + description: 'Exit code of `jest` command if the test run failed', + type: 'string' // number + }, + testLocationInResults: { + default: false, + description: 'Add `location` information to the test results', + type: 'boolean' + }, + testMatch: { + description: 'The glob patterns Jest uses to detect test files.', + string: true, + type: 'array' + }, + testNamePattern: { + alias: 't', + description: 'Run only tests with a name that matches the regex pattern.', + type: 'string' + }, + testPathIgnorePatterns: { + description: + 'An array of regexp pattern strings that are matched ' + + 'against all test paths before executing the test. If the test path ' + + 'matches any of the patterns, it will be skipped.', + string: true, + type: 'array' + }, + testPathPattern: { + description: + 'A regexp pattern string that is matched against all tests ' + + 'paths before executing the test.', + string: true, + type: 'array' + }, + testRegex: { + description: + 'A string or array of string regexp patterns that Jest uses to detect test files.', + string: true, + type: 'array' + }, + testResultsProcessor: { + description: + 'Allows the use of a custom results processor. ' + + 'This processor must be a node module that exports ' + + 'a function expecting as the first argument the result object.', + type: 'string' + }, + testRunner: { + description: + 'Allows to specify a custom test runner. The default is' + + ' `jest-circus/runner`. A path to a custom test runner can be provided:' + + ' `/path/to/testRunner.js`.', + type: 'string' + }, + testSequencer: { + description: + 'Allows to specify a custom test sequencer. The default is ' + + '`@jest/test-sequencer`. A path to a custom test sequencer can be ' + + 'provided: `/path/to/testSequencer.js`', + type: 'string' + }, + testTimeout: { + description: 'This option sets the default timeouts of test cases.', + type: 'number' + }, + testURL: { + description: 'This option sets the URL for the jsdom environment.', + type: 'string' + }, + timers: { + description: + 'Setting this value to fake allows the use of fake timers ' + + 'for functions such as setTimeout.', + type: 'string' + }, + transform: { + description: + 'A JSON string which maps from regular expressions to paths ' + + 'to transformers.', + type: 'string' + }, + transformIgnorePatterns: { + description: + 'An array of regexp pattern strings that are matched ' + + 'against all source file paths before transformation.', + string: true, + type: 'array' + }, + unmockedModulePathPatterns: { + description: + 'An array of regexp pattern strings that are matched ' + + 'against all modules before the module loader will automatically ' + + 'return a mock for them.', + string: true, + type: 'array' + }, + updateSnapshot: { + alias: 'u', + default: undefined, + description: + 'Use this flag to re-record snapshots. ' + + 'Can be used together with a test suite pattern or with ' + + '`--testNamePattern` to re-record snapshot for test matching ' + + 'the pattern', + type: 'boolean' + }, + useStderr: { + default: undefined, + description: 'Divert all output to stderr.', + type: 'boolean' + }, + verbose: { + default: undefined, + description: + 'Display individual test results with the test suite hierarchy.', + type: 'boolean' + }, + version: { + alias: 'v', + default: undefined, + description: 'Print the version and exit', + type: 'boolean' + }, + watch: { + default: undefined, + description: + 'Watch files for changes and rerun tests related to ' + + 'changed files. If you want to re-run all tests when a file has ' + + 'changed, use the `--watchAll` option.', + type: 'boolean' + }, + watchAll: { + default: undefined, + description: + 'Watch files for changes and rerun all tests. If you want ' + + 'to re-run only the tests related to the changed files, use the ' + + '`--watch` option.', + type: 'boolean' + }, + watchPathIgnorePatterns: { + description: + 'An array of regexp pattern strings that are matched ' + + 'against all paths before trigger test re-run in watch mode. ' + + 'If the test path matches any of the patterns, it will be skipped.', + string: true, + type: 'array' + }, + watchman: { + default: undefined, + description: + 'Whether to use watchman for file crawling. Disable using ' + + '--no-watchman.', + type: 'boolean' + } +}; +exports.options = options; diff --git a/packages/jest-cli/build/cli/index.d.ts b/packages/jest-cli/build/cli/index.d.ts new file mode 100644 index 000000000000..f93e0bfaa2df --- /dev/null +++ b/packages/jest-cli/build/cli/index.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export declare function run(maybeArgv?: Array, project?: Config.Path): Promise; +export declare const buildArgv: (maybeArgv?: string[] | undefined) => Config.Argv; diff --git a/packages/jest-cli/build/cli/index.js b/packages/jest-cli/build/cli/index.js new file mode 100644 index 000000000000..733fe3a81c65 --- /dev/null +++ b/packages/jest-cli/build/cli/index.js @@ -0,0 +1,273 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.run = run; +exports.buildArgv = void 0; + +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 _exit() { + const data = _interopRequireDefault(require('exit')); + + _exit = function () { + return data; + }; + + return data; +} + +function _yargs() { + const data = _interopRequireDefault(require('yargs')); + + _yargs = function () { + return data; + }; + + return data; +} + +function _core() { + const data = require('@jest/core'); + + _core = function () { + return data; + }; + + return data; +} + +function _jestConfig() { + const data = require('jest-config'); + + _jestConfig = 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; +} + +function _init() { + const data = _interopRequireDefault(require('../init')); + + _init = function () { + return data; + }; + + return data; +} + +var args = _interopRequireWildcard(require('./args')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +async function run(maybeArgv, project) { + try { + const argv = buildArgv(maybeArgv); + + if (argv.init) { + await (0, _init().default)(); + return; + } + + const projects = getProjectListFromCLIArgs(argv, project); + const {results, globalConfig} = await (0, _core().runCLI)(argv, projects); + readResultsAndExit(results, globalConfig); + } catch (error) { + (0, _jestUtil().clearLine)(process.stderr); + (0, _jestUtil().clearLine)(process.stdout); + + if (error !== null && error !== void 0 && error.stack) { + console.error(_chalk().default.red(error.stack)); + } else { + console.error(_chalk().default.red(error)); + } + + (0, _exit().default)(1); + throw error; + } +} + +const buildArgv = maybeArgv => { + const version = + (0, _core().getVersion)() + + (__dirname.includes(`packages${path().sep}jest-cli`) ? '-dev' : ''); + const rawArgv = maybeArgv || process.argv.slice(2); + const argv = (0, _yargs().default)(rawArgv) + .usage(args.usage) + .version(version) + .alias('help', 'h') + .options(args.options) + .epilogue(args.docs) + .check(args.check).argv; + (0, _jestValidate().validateCLIOptions)( + argv, + {...args.options, deprecationEntries: _jestConfig().deprecationEntries}, // strip leading dashes + Array.isArray(rawArgv) + ? rawArgv.map(rawArgv => rawArgv.replace(/^--?/, '')) + : Object.keys(rawArgv) + ); // strip dashed args + + return Object.keys(argv).reduce( + (result, key) => { + if (!key.includes('-')) { + result[key] = argv[key]; + } + + return result; + }, + { + $0: argv.$0, + _: argv._ + } + ); +}; + +exports.buildArgv = buildArgv; + +const getProjectListFromCLIArgs = (argv, project) => { + const projects = argv.projects ? argv.projects : []; + + if (project) { + projects.push(project); + } + + if (!projects.length && process.platform === 'win32') { + try { + projects.push((0, _jestUtil().tryRealpath)(process.cwd())); + } catch { + // do nothing, just catch error + // process.binding('fs').realpath can throw, e.g. on mapped drives + } + } + + if (!projects.length) { + projects.push(process.cwd()); + } + + return projects; +}; + +const readResultsAndExit = (result, globalConfig) => { + const code = !result || result.success ? 0 : globalConfig.testFailureExitCode; // Only exit if needed + + process.on('exit', () => { + if (typeof code === 'number' && code !== 0) { + process.exitCode = code; + } + }); + + if (globalConfig.forceExit) { + if (!globalConfig.detectOpenHandles) { + console.warn( + _chalk().default.bold('Force exiting Jest: ') + + 'Have you considered using `--detectOpenHandles` to detect ' + + 'async operations that kept running after all tests finished?' + ); + } + + (0, _exit().default)(code); + } else if (!globalConfig.detectOpenHandles) { + setTimeout(() => { + console.warn( + _chalk().default.yellow.bold( + 'Jest did not exit one second after the test run has completed.\n\n' + ) + + _chalk().default.yellow( + 'This usually means that there are asynchronous operations that ' + + "weren't stopped in your tests. Consider running Jest with " + + '`--detectOpenHandles` to troubleshoot this issue.' + ) + ); + }, 1000).unref(); + } +}; diff --git a/packages/jest-cli/build/index.d.ts b/packages/jest-cli/build/index.d.ts new file mode 100644 index 000000000000..00cb5feef2d0 --- /dev/null +++ b/packages/jest-cli/build/index.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export { run } from './cli'; diff --git a/packages/jest-cli/build/index.js b/packages/jest-cli/build/index.js new file mode 100644 index 000000000000..fdfb92e9b401 --- /dev/null +++ b/packages/jest-cli/build/index.js @@ -0,0 +1,13 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'run', { + enumerable: true, + get: function () { + return _cli.run; + } +}); + +var _cli = require('./cli'); diff --git a/packages/jest-cli/build/init/errors.d.ts b/packages/jest-cli/build/init/errors.d.ts new file mode 100644 index 000000000000..426fdc0f1d21 --- /dev/null +++ b/packages/jest-cli/build/init/errors.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ +export declare class NotFoundPackageJsonError extends Error { + constructor(rootDir: string); +} +export declare class MalformedPackageJsonError extends Error { + constructor(packageJsonPath: string); +} diff --git a/packages/jest-cli/build/init/errors.js b/packages/jest-cli/build/init/errors.js new file mode 100644 index 000000000000..eed7c8a660e3 --- /dev/null +++ b/packages/jest-cli/build/init/errors.js @@ -0,0 +1,35 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.MalformedPackageJsonError = exports.NotFoundPackageJsonError = void 0; + +/** + * 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. + */ +class NotFoundPackageJsonError extends Error { + constructor(rootDir) { + super(`Could not find a "package.json" file in ${rootDir}`); + this.name = ''; + Error.captureStackTrace(this, () => {}); + } +} + +exports.NotFoundPackageJsonError = NotFoundPackageJsonError; + +class MalformedPackageJsonError extends Error { + constructor(packageJsonPath) { + super( + `There is malformed json in ${packageJsonPath}\n` + + 'Fix it, and then run "jest --init"' + ); + this.name = ''; + Error.captureStackTrace(this, () => {}); + } +} + +exports.MalformedPackageJsonError = MalformedPackageJsonError; diff --git a/packages/jest-cli/build/init/generateConfigFile.d.ts b/packages/jest-cli/build/init/generateConfigFile.d.ts new file mode 100644 index 000000000000..c5afd8d3f3ff --- /dev/null +++ b/packages/jest-cli/build/init/generateConfigFile.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +declare const generateConfigFile: (results: Record, generateEsm?: boolean) => string; +export default generateConfigFile; diff --git a/packages/jest-cli/build/init/generateConfigFile.js b/packages/jest-cli/build/init/generateConfigFile.js new file mode 100644 index 000000000000..a3afb53f9457 --- /dev/null +++ b/packages/jest-cli/build/init/generateConfigFile.js @@ -0,0 +1,108 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestConfig() { + const data = require('jest-config'); + + _jestConfig = function () { + return data; + }; + + return data; +} + +/** + * 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 stringifyOption = (option, map, linePrefix = '') => { + const optionDescription = ` // ${_jestConfig().descriptions[option]}`; + const stringifiedObject = `${option}: ${JSON.stringify( + map[option], + null, + 2 + )}`; + return ( + optionDescription + + '\n' + + stringifiedObject + .split('\n') + .map(line => ' ' + linePrefix + line) + .join('\n') + + ',\n' + ); +}; + +const generateConfigFile = (results, generateEsm = false) => { + const { + useTypescript, + coverage, + coverageProvider, + clearMocks, + environment + } = results; + const overrides = {}; + + if (coverage) { + Object.assign(overrides, { + coverageDirectory: 'coverage' + }); + } + + if (coverageProvider === 'v8') { + Object.assign(overrides, { + coverageProvider: 'v8' + }); + } + + if (environment === 'jsdom') { + Object.assign(overrides, { + testEnvironment: 'jsdom' + }); + } + + if (clearMocks) { + Object.assign(overrides, { + clearMocks: true + }); + } + + const overrideKeys = Object.keys(overrides); + const properties = []; + + for (const option in _jestConfig().descriptions) { + const opt = option; + + if (overrideKeys.includes(opt)) { + properties.push(stringifyOption(opt, overrides)); + } else { + properties.push(stringifyOption(opt, _jestConfig().defaults, '// ')); + } + } + + const configHeaderMessage = `/* + * For a detailed explanation regarding each configuration property${ + useTypescript ? ' and type check' : '' + }, visit: + * https://jestjs.io/docs/en/configuration.html + */ + +`; + return ( + configHeaderMessage + + (useTypescript || generateEsm + ? 'export default {\n' + : 'module.exports = {\n') + + properties.join('\n') + + '};\n' + ); +}; + +var _default = generateConfigFile; +exports.default = _default; diff --git a/packages/jest-cli/build/init/index.d.ts b/packages/jest-cli/build/init/index.d.ts new file mode 100644 index 000000000000..b11e02189cc7 --- /dev/null +++ b/packages/jest-cli/build/init/index.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +declare const _default: (rootDir?: string) => Promise; +export default _default; diff --git a/packages/jest-cli/build/init/index.js b/packages/jest-cli/build/init/index.js new file mode 100644 index 000000000000..df0bac20f10b --- /dev/null +++ b/packages/jest-cli/build/init/index.js @@ -0,0 +1,250 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +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 fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _prompts() { + const data = _interopRequireDefault(require('prompts')); + + _prompts = function () { + return data; + }; + + return data; +} + +function _jestConfig() { + const data = require('jest-config'); + + _jestConfig = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _errors = require('./errors'); + +var _generateConfigFile = _interopRequireDefault( + require('./generateConfigFile') +); + +var _modifyPackageJson = _interopRequireDefault(require('./modifyPackageJson')); + +var _questions = _interopRequireWildcard(require('./questions')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 { + JEST_CONFIG_BASE_NAME, + JEST_CONFIG_EXT_MJS, + JEST_CONFIG_EXT_JS, + JEST_CONFIG_EXT_TS, + JEST_CONFIG_EXT_ORDER, + PACKAGE_JSON +} = _jestConfig().constants; + +const getConfigFilename = ext => JEST_CONFIG_BASE_NAME + ext; + +var _default = async ( + rootDir = (0, _jestUtil().tryRealpath)(process.cwd()) +) => { + // prerequisite checks + const projectPackageJsonPath = path().join(rootDir, PACKAGE_JSON); + + if (!fs().existsSync(projectPackageJsonPath)) { + throw new _errors.NotFoundPackageJsonError(rootDir); + } + + const questions = _questions.default.slice(0); + + let hasJestProperty = false; + let projectPackageJson; + + try { + projectPackageJson = JSON.parse( + fs().readFileSync(projectPackageJsonPath, 'utf-8') + ); + } catch { + throw new _errors.MalformedPackageJsonError(projectPackageJsonPath); + } + + if (projectPackageJson.jest) { + hasJestProperty = true; + } + + const existingJestConfigExt = JEST_CONFIG_EXT_ORDER.find(ext => + fs().existsSync(path().join(rootDir, getConfigFilename(ext))) + ); + + if (hasJestProperty || existingJestConfigExt) { + const result = await (0, _prompts().default)({ + initial: true, + message: + 'It seems that you already have a jest configuration, do you want to override it?', + name: 'continue', + type: 'confirm' + }); + + if (!result.continue) { + console.log(); + console.log('Aborting...'); + return; + } + } // Add test script installation only if needed + + if ( + !projectPackageJson.scripts || + projectPackageJson.scripts.test !== 'jest' + ) { + questions.unshift(_questions.testScriptQuestion); + } // Start the init process + + console.log(); + console.log( + _chalk().default.underline( + `The following questions will help Jest to create a suitable configuration for your project\n` + ) + ); + let promptAborted = false; // @ts-expect-error: Return type cannot be object - faulty typings + + const results = await (0, _prompts().default)(questions, { + onCancel: () => { + promptAborted = true; + } + }); + + if (promptAborted) { + console.log(); + console.log('Aborting...'); + return; + } // Determine if Jest should use JS or TS for the config file + + const jestConfigFileExt = results.useTypescript + ? JEST_CONFIG_EXT_TS + : projectPackageJson.type === 'module' + ? JEST_CONFIG_EXT_MJS + : JEST_CONFIG_EXT_JS; // Determine Jest config path + + const jestConfigPath = existingJestConfigExt + ? getConfigFilename(existingJestConfigExt) + : path().join(rootDir, getConfigFilename(jestConfigFileExt)); + const shouldModifyScripts = results.scripts; + + if (shouldModifyScripts || hasJestProperty) { + const modifiedPackageJson = (0, _modifyPackageJson.default)({ + projectPackageJson, + shouldModifyScripts + }); + fs().writeFileSync(projectPackageJsonPath, modifiedPackageJson); + console.log(''); + console.log( + `✏️ Modified ${_chalk().default.cyan(projectPackageJsonPath)}` + ); + } + + const generatedConfig = (0, _generateConfigFile.default)( + results, + projectPackageJson.type === 'module' || + jestConfigPath.endsWith(JEST_CONFIG_EXT_MJS) + ); + fs().writeFileSync(jestConfigPath, generatedConfig); + console.log(''); + console.log( + `📝 Configuration file created at ${_chalk().default.cyan(jestConfigPath)}` + ); +}; + +exports.default = _default; diff --git a/packages/jest-cli/build/init/modifyPackageJson.d.ts b/packages/jest-cli/build/init/modifyPackageJson.d.ts new file mode 100644 index 000000000000..30e92fe92916 --- /dev/null +++ b/packages/jest-cli/build/init/modifyPackageJson.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ +import type { ProjectPackageJson } from './types'; +declare const modifyPackageJson: ({ projectPackageJson, shouldModifyScripts, }: { + projectPackageJson: ProjectPackageJson; + shouldModifyScripts: boolean; +}) => string; +export default modifyPackageJson; diff --git a/packages/jest-cli/build/init/modifyPackageJson.js b/packages/jest-cli/build/init/modifyPackageJson.js new file mode 100644 index 000000000000..526b485b6e80 --- /dev/null +++ b/packages/jest-cli/build/init/modifyPackageJson.js @@ -0,0 +1,28 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +/** + * 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 modifyPackageJson = ({projectPackageJson, shouldModifyScripts}) => { + if (shouldModifyScripts) { + projectPackageJson.scripts + ? (projectPackageJson.scripts.test = 'jest') + : (projectPackageJson.scripts = { + test: 'jest' + }); + } + + delete projectPackageJson.jest; + return JSON.stringify(projectPackageJson, null, 2) + '\n'; +}; + +var _default = modifyPackageJson; +exports.default = _default; diff --git a/packages/jest-cli/build/init/questions.d.ts b/packages/jest-cli/build/init/questions.d.ts new file mode 100644 index 000000000000..4d261ad1cd52 --- /dev/null +++ b/packages/jest-cli/build/init/questions.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import type { PromptObject } from 'prompts'; +declare const defaultQuestions: Array; +export default defaultQuestions; +export declare const testScriptQuestion: PromptObject; diff --git a/packages/jest-cli/build/init/questions.js b/packages/jest-cli/build/init/questions.js new file mode 100644 index 000000000000..23631c1fd538 --- /dev/null +++ b/packages/jest-cli/build/init/questions.js @@ -0,0 +1,75 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.testScriptQuestion = exports.default = void 0; + +/** + * 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 defaultQuestions = [ + { + initial: false, + message: 'Would you like to use Typescript for the configuration file?', + name: 'useTypescript', + type: 'confirm' + }, + { + choices: [ + { + title: 'node', + value: 'node' + }, + { + title: 'jsdom (browser-like)', + value: 'jsdom' + } + ], + initial: 0, + message: 'Choose the test environment that will be used for testing', + name: 'environment', + type: 'select' + }, + { + initial: false, + message: 'Do you want Jest to add coverage reports?', + name: 'coverage', + type: 'confirm' + }, + { + choices: [ + { + title: 'v8', + value: 'v8' + }, + { + title: 'babel', + value: 'babel' + } + ], + initial: 0, + message: 'Which provider should be used to instrument code for coverage?', + name: 'coverageProvider', + type: 'select' + }, + { + initial: false, + message: 'Automatically clear mock calls and instances between every test?', + name: 'clearMocks', + type: 'confirm' + } +]; +var _default = defaultQuestions; +exports.default = _default; +const testScriptQuestion = { + initial: true, + message: + 'Would you like to use Jest when running "test" script in "package.json"?', + name: 'scripts', + type: 'confirm' +}; +exports.testScriptQuestion = testScriptQuestion; diff --git a/packages/jest-cli/build/init/types.d.ts b/packages/jest-cli/build/init/types.d.ts new file mode 100644 index 000000000000..5b5a340e9169 --- /dev/null +++ b/packages/jest-cli/build/init/types.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export declare type ProjectPackageJson = { + jest?: Partial; + scripts?: Record; + type?: 'commonjs' | 'module'; +}; diff --git a/packages/jest-cli/build/init/types.js b/packages/jest-cli/build/init/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-cli/build/init/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-config/build/Defaults.d.ts b/packages/jest-config/build/Defaults.d.ts new file mode 100644 index 000000000000..8a26ee6ec7b4 --- /dev/null +++ b/packages/jest-config/build/Defaults.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +declare const defaultOptions: Config.DefaultOptions; +export default defaultOptions; diff --git a/packages/jest-config/build/Defaults.js b/packages/jest-config/build/Defaults.js new file mode 100644 index 000000000000..5b68cf4d7066 --- /dev/null +++ b/packages/jest-config/build/Defaults.js @@ -0,0 +1,108 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _path() { + const data = require('path'); + + _path = function () { + return data; + }; + + return data; +} + +function _jestRegexUtil() { + const data = require('jest-regex-util'); + + _jestRegexUtil = function () { + return data; + }; + + return data; +} + +var _constants = require('./constants'); + +var _getCacheDirectory = _interopRequireDefault(require('./getCacheDirectory')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 NODE_MODULES_REGEXP = (0, _jestRegexUtil().replacePathSepForRegex)( + _constants.NODE_MODULES +); +const defaultOptions = { + automock: false, + bail: 0, + cache: true, + cacheDirectory: (0, _getCacheDirectory.default)(), + changedFilesWithAncestor: false, + clearMocks: false, + collectCoverage: false, + coveragePathIgnorePatterns: [NODE_MODULES_REGEXP], + coverageProvider: 'babel', + coverageReporters: ['json', 'text', 'lcov', 'clover'], + errorOnDeprecated: false, + expand: false, + extensionsToTreatAsEsm: [], + forceCoverageMatch: [], + globals: {}, + haste: { + computeSha1: false, + throwOnModuleCollision: false + }, + injectGlobals: true, + maxConcurrency: 5, + maxWorkers: '50%', + moduleDirectories: ['node_modules'], + moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'node'], + moduleNameMapper: {}, + modulePathIgnorePatterns: [], + noStackTrace: false, + notify: false, + notifyMode: 'failure-change', + prettierPath: 'prettier', + resetMocks: false, + resetModules: false, + restoreMocks: false, + roots: [''], + runTestsByPath: false, + runner: 'jest-runner', + setupFiles: [], + setupFilesAfterEnv: [], + skipFilter: false, + slowTestThreshold: 5, + snapshotSerializers: [], + testEnvironment: 'jest-environment-node', + testEnvironmentOptions: {}, + testFailureExitCode: 1, + testLocationInResults: false, + testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'], + testPathIgnorePatterns: [NODE_MODULES_REGEXP], + testRegex: [], + testRunner: 'jest-circus/runner', + testSequencer: '@jest/test-sequencer', + testURL: 'http://localhost', + timers: 'real', + transformIgnorePatterns: [ + NODE_MODULES_REGEXP, + `\\.pnp\\.[^\\${_path().sep}]+$` + ], + useStderr: false, + watch: false, + watchPathIgnorePatterns: [], + watchman: true +}; +var _default = defaultOptions; +exports.default = _default; diff --git a/packages/jest-config/build/Deprecated.d.ts b/packages/jest-config/build/Deprecated.d.ts new file mode 100644 index 000000000000..4c05472dd428 --- /dev/null +++ b/packages/jest-config/build/Deprecated.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { DeprecatedOptions } from 'jest-validate'; +declare const deprecatedOptions: DeprecatedOptions; +export default deprecatedOptions; diff --git a/packages/jest-config/build/Deprecated.js b/packages/jest-config/build/Deprecated.js new file mode 100644 index 000000000000..a9b8ef80ef09 --- /dev/null +++ b/packages/jest-config/build/Deprecated.js @@ -0,0 +1,103 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _prettyFormat() { + const data = _interopRequireDefault(require('pretty-format')); + + _prettyFormat = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 format = value => + (0, _prettyFormat().default)(value, { + min: true + }); + +const deprecatedOptions = { + browser: () => ` Option ${_chalk().default.bold( + '"browser"' + )} has been deprecated. Please install "browser-resolve" and use the "resolver" option in Jest configuration as follows: + { + ${_chalk().default.bold('"resolver"')}: ${_chalk().default.bold( + '"browser-resolve"' + )} + } + `, + preprocessorIgnorePatterns: options => ` Option ${_chalk().default.bold( + '"preprocessorIgnorePatterns"' + )} was replaced by ${_chalk().default.bold( + '"transformIgnorePatterns"' + )}, which support multiple preprocessors. + + Jest now treats your current configuration as: + { + ${_chalk().default.bold( + '"transformIgnorePatterns"' + )}: ${_chalk().default.bold(format(options.preprocessorIgnorePatterns))} + } + + Please update your configuration.`, + scriptPreprocessor: options => ` Option ${_chalk().default.bold( + '"scriptPreprocessor"' + )} was replaced by ${_chalk().default.bold( + '"transform"' + )}, which support multiple preprocessors. + + Jest now treats your current configuration as: + { + ${_chalk().default.bold('"transform"')}: ${_chalk().default.bold( + `{".*": ${format(options.scriptPreprocessor)}}` + )} + } + + Please update your configuration.`, + setupTestFrameworkScriptFile: _options => ` Option ${_chalk().default.bold( + '"setupTestFrameworkScriptFile"' + )} was replaced by configuration ${_chalk().default.bold( + '"setupFilesAfterEnv"' + )}, which supports multiple paths. + + Please update your configuration.`, + testPathDirs: options => ` Option ${_chalk().default.bold( + '"testPathDirs"' + )} was replaced by ${_chalk().default.bold('"roots"')}. + + Jest now treats your current configuration as: + { + ${_chalk().default.bold('"roots"')}: ${_chalk().default.bold( + format(options.testPathDirs) + )} + } + + Please update your configuration. + ` +}; +var _default = deprecatedOptions; +exports.default = _default; diff --git a/packages/jest-config/build/Descriptions.d.ts b/packages/jest-config/build/Descriptions.d.ts new file mode 100644 index 000000000000..acf6e3b67012 --- /dev/null +++ b/packages/jest-config/build/Descriptions.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +declare const descriptions: { + [key in keyof Config.InitialOptions]: string; +}; +export default descriptions; diff --git a/packages/jest-config/build/Descriptions.js b/packages/jest-config/build/Descriptions.js new file mode 100644 index 000000000000..bb1d54109b8c --- /dev/null +++ b/packages/jest-config/build/Descriptions.js @@ -0,0 +1,105 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +/** + * 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 descriptions = { + automock: 'All imported modules in your tests should be mocked automatically', + bail: 'Stop running tests after `n` failures', + cacheDirectory: + 'The directory where Jest should store its cached dependency information', + clearMocks: 'Automatically clear mock calls and instances between every test', + collectCoverage: + 'Indicates whether the coverage information should be collected while executing the test', + collectCoverageFrom: + 'An array of glob patterns indicating a set of files for which coverage information should be collected', + coverageDirectory: + 'The directory where Jest should output its coverage files', + coveragePathIgnorePatterns: + 'An array of regexp pattern strings used to skip coverage collection', + coverageProvider: + 'Indicates which provider should be used to instrument code for coverage', + coverageReporters: + 'A list of reporter names that Jest uses when writing coverage reports', + coverageThreshold: + 'An object that configures minimum threshold enforcement for coverage results', + dependencyExtractor: 'A path to a custom dependency extractor', + errorOnDeprecated: + 'Make calling deprecated APIs throw helpful error messages', + forceCoverageMatch: + 'Force coverage collection from ignored files using an array of glob patterns', + globalSetup: + 'A path to a module which exports an async function that is triggered once before all test suites', + globalTeardown: + 'A path to a module which exports an async function that is triggered once after all test suites', + globals: + 'A set of global variables that need to be available in all test environments', + maxWorkers: + 'The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.', + moduleDirectories: + "An array of directory names to be searched recursively up from the requiring module's location", + moduleFileExtensions: 'An array of file extensions your modules use', + moduleNameMapper: + 'A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module', + modulePathIgnorePatterns: + "An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader", + notify: 'Activates notifications for test results', + notifyMode: + 'An enum that specifies notification mode. Requires { notify: true }', + preset: "A preset that is used as a base for Jest's configuration", + projects: 'Run tests from one or more projects', + reporters: 'Use this configuration option to add custom reporters to Jest', + resetMocks: 'Automatically reset mock state between every test', + resetModules: 'Reset the module registry before running each individual test', + resolver: 'A path to a custom resolver', + restoreMocks: 'Automatically restore mock state between every test', + rootDir: + 'The root directory that Jest should scan for tests and modules within', + roots: + 'A list of paths to directories that Jest should use to search for files in', + runner: + "Allows you to use a custom runner instead of Jest's default test runner", + setupFiles: + 'The paths to modules that run some code to configure or set up the testing environment before each test', + setupFilesAfterEnv: + 'A list of paths to modules that run some code to configure or set up the testing framework before each test', + slowTestThreshold: + 'The number of seconds after which a test is considered as slow and reported as such in the results.', + snapshotSerializers: + 'A list of paths to snapshot serializer modules Jest should use for snapshot testing', + testEnvironment: 'The test environment that will be used for testing', + testEnvironmentOptions: 'Options that will be passed to the testEnvironment', + testLocationInResults: 'Adds a location field to test results', + 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 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', + testURL: + 'This option sets the URL for the jsdom environment. It is reflected in properties such as location.href', + timers: + 'Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"', + transform: 'A map from regular expressions to paths to transformers', + transformIgnorePatterns: + 'An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation', + unmockedModulePathPatterns: + 'An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them', + verbose: + 'Indicates whether each individual test should be reported during the run', + watchPathIgnorePatterns: + 'An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode', + watchman: 'Whether to use watchman for file crawling' +}; +var _default = descriptions; +exports.default = _default; diff --git a/packages/jest-config/build/ReporterValidationErrors.d.ts b/packages/jest-config/build/ReporterValidationErrors.d.ts new file mode 100644 index 000000000000..642fdae48c90 --- /dev/null +++ b/packages/jest-config/build/ReporterValidationErrors.d.ts @@ -0,0 +1,19 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import { ValidationError } from 'jest-validate'; +/** + * Reporter Validation Error is thrown if the given arguments + * within the reporter are not valid. + * + * This is a highly specific reporter error and in the future will be + * merged with jest-validate. Till then, we can make use of it. It works + * and that's what counts most at this time. + */ +export declare function createReporterError(reporterIndex: number, reporterValue: Array | string): ValidationError; +export declare function createArrayReporterError(arrayReporter: Config.ReporterConfig, reporterIndex: number, valueIndex: number, value: string | Record, expectedType: string, valueName: string): ValidationError; +export declare function validateReporters(reporterConfig: Array): boolean; diff --git a/packages/jest-config/build/ReporterValidationErrors.js b/packages/jest-config/build/ReporterValidationErrors.js new file mode 100644 index 000000000000..84055d38f63f --- /dev/null +++ b/packages/jest-config/build/ReporterValidationErrors.js @@ -0,0 +1,138 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.createReporterError = createReporterError; +exports.createArrayReporterError = createArrayReporterError; +exports.validateReporters = validateReporters; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestGetType() { + const data = _interopRequireDefault(require('jest-get-type')); + + _jestGetType = function () { + return data; + }; + + return data; +} + +function _jestValidate() { + const data = require('jest-validate'); + + _jestValidate = function () { + return data; + }; + + return data; +} + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 validReporterTypes = ['array', 'string']; +const ERROR = `${_utils.BULLET}Reporter Validation Error`; +/** + * Reporter Validation Error is thrown if the given arguments + * within the reporter are not valid. + * + * This is a highly specific reporter error and in the future will be + * merged with jest-validate. Till then, we can make use of it. It works + * and that's what counts most at this time. + */ + +function createReporterError(reporterIndex, reporterValue) { + const errorMessage = + ` Reporter at index ${reporterIndex} must be of type:\n` + + ` ${_chalk().default.bold.green(validReporterTypes.join(' or '))}\n` + + ` but instead received:\n` + + ` ${_chalk().default.bold.red( + (0, _jestGetType().default)(reporterValue) + )}`; + return new (_jestValidate().ValidationError)( + ERROR, + errorMessage, + _utils.DOCUMENTATION_NOTE + ); +} + +function createArrayReporterError( + arrayReporter, + reporterIndex, + valueIndex, + value, + expectedType, + valueName +) { + const errorMessage = + ` Unexpected value for ${valueName} ` + + `at index ${valueIndex} of reporter at index ${reporterIndex}\n` + + ' Expected:\n' + + ` ${_chalk().default.bold.red(expectedType)}\n` + + ' Got:\n' + + ` ${_chalk().default.bold.green((0, _jestGetType().default)(value))}\n` + + ` Reporter configuration:\n` + + ` ${_chalk().default.bold.green( + JSON.stringify(arrayReporter, null, 2).split('\n').join('\n ') + )}`; + return new (_jestValidate().ValidationError)( + ERROR, + errorMessage, + _utils.DOCUMENTATION_NOTE + ); +} + +function validateReporters(reporterConfig) { + return reporterConfig.every((reporter, index) => { + if (Array.isArray(reporter)) { + validateArrayReporter(reporter, index); + } else if (typeof reporter !== 'string') { + throw createReporterError(index, reporter); + } + + return true; + }); +} + +function validateArrayReporter(arrayReporter, reporterIndex) { + const [path, options] = arrayReporter; + + if (typeof path !== 'string') { + throw createArrayReporterError( + arrayReporter, + reporterIndex, + 0, + path, + 'string', + 'Path' + ); + } else if (typeof options !== 'object') { + throw createArrayReporterError( + arrayReporter, + reporterIndex, + 1, + options, + 'object', + 'Reporter Configuration' + ); + } +} diff --git a/packages/jest-config/build/ValidConfig.d.ts b/packages/jest-config/build/ValidConfig.d.ts new file mode 100644 index 000000000000..a58484fa0d2f --- /dev/null +++ b/packages/jest-config/build/ValidConfig.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +declare const initialOptions: Config.InitialOptions; +export default initialOptions; diff --git a/packages/jest-config/build/ValidConfig.js b/packages/jest-config/build/ValidConfig.js new file mode 100644 index 000000000000..70cd05392dbb --- /dev/null +++ b/packages/jest-config/build/ValidConfig.js @@ -0,0 +1,183 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestRegexUtil() { + const data = require('jest-regex-util'); + + _jestRegexUtil = function () { + return data; + }; + + return data; +} + +function _jestValidate() { + const data = require('jest-validate'); + + _jestValidate = function () { + return data; + }; + + return data; +} + +var _constants = require('./constants'); + +/** + * 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 NODE_MODULES_REGEXP = (0, _jestRegexUtil().replacePathSepForRegex)( + _constants.NODE_MODULES +); +const initialOptions = { + automock: false, + bail: (0, _jestValidate().multipleValidOptions)(false, 0), + cache: true, + cacheDirectory: '/tmp/user/jest', + changedFilesWithAncestor: false, + changedSince: 'master', + clearMocks: false, + collectCoverage: true, + collectCoverageFrom: ['src', '!public'], + collectCoverageOnlyFrom: { + '/this-directory-is-covered/Covered.js': true + }, + coverageDirectory: 'coverage', + coveragePathIgnorePatterns: [NODE_MODULES_REGEXP], + coverageProvider: 'v8', + coverageReporters: ['json', 'text', 'lcov', 'clover'], + coverageThreshold: { + global: { + branches: 50, + functions: 100, + lines: 100, + statements: 100 + } + }, + dependencyExtractor: '/dependencyExtractor.js', + displayName: (0, _jestValidate().multipleValidOptions)('test-config', { + color: 'blue', + name: 'test-config' + }), + errorOnDeprecated: false, + expand: false, + extensionsToTreatAsEsm: [], + extraGlobals: [], + filter: '/filter.js', + forceCoverageMatch: ['**/*.t.js'], + forceExit: false, + globalSetup: 'setup.js', + globalTeardown: 'teardown.js', + globals: { + __DEV__: true + }, + haste: { + computeSha1: true, + defaultPlatform: 'ios', + hasteImplModulePath: '/haste_impl.js', + platforms: ['ios', 'android'], + retainAllFiles: false, + throwOnModuleCollision: false + }, + injectGlobals: true, + json: false, + lastCommit: false, + logHeapUsage: true, + maxConcurrency: 5, + maxWorkers: '50%', + moduleDirectories: ['node_modules'], + moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node'], + moduleLoader: '', + moduleNameMapper: { + '^React$': '/node_modules/react' + }, + modulePathIgnorePatterns: ['/build/'], + modulePaths: ['/shared/vendor/modules'], + name: 'string', + noStackTrace: false, + notify: false, + notifyMode: 'failure-change', + onlyChanged: false, + onlyFailures: false, + preset: 'react-native', + prettierPath: '/node_modules/prettier', + projects: ['project-a', 'project-b/'], + reporters: [ + 'default', + 'custom-reporter-1', + [ + 'custom-reporter-2', + { + configValue: true + } + ] + ], + resetMocks: false, + resetModules: false, + resolver: '/resolver.js', + restoreMocks: false, + rootDir: '/', + roots: [''], + runTestsByPath: false, + runner: 'jest-runner', + setupFiles: ['/setup.js'], + setupFilesAfterEnv: ['/testSetupFile.js'], + silent: true, + skipFilter: false, + skipNodeResolution: false, + slowTestThreshold: 5, + snapshotResolver: '/snapshotResolver.js', + snapshotSerializers: ['my-serializer-module'], + testEnvironment: 'jest-environment-jsdom', + testEnvironmentOptions: { + userAgent: 'Agent/007' + }, + testFailureExitCode: 1, + testLocationInResults: false, + testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], + testNamePattern: 'test signature', + testPathIgnorePatterns: [NODE_MODULES_REGEXP], + testRegex: (0, + _jestValidate() + .multipleValidOptions)('(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$', [ + '/__tests__/\\.test\\.[jt]sx?$', + '/__tests__/\\.spec\\.[jt]sx?$' + ]), + testResultsProcessor: 'processor-node-module', + testRunner: 'circus', + testSequencer: '@jest/test-sequencer', + testTimeout: 5000, + testURL: 'http://localhost', + timers: 'real', + transform: { + '\\.js$': '/preprocessor.js' + }, + transformIgnorePatterns: [NODE_MODULES_REGEXP], + unmockedModulePathPatterns: ['mock'], + updateSnapshot: true, + useStderr: false, + verbose: false, + watch: false, + watchAll: false, + watchPathIgnorePatterns: ['/e2e/'], + watchPlugins: [ + 'path/to/yourWatchPlugin', + [ + 'jest-watch-typeahead/filename', + { + key: 'k', + prompt: 'do something with my custom prompt' + } + ] + ], + watchman: true +}; +var _default = initialOptions; +exports.default = _default; diff --git a/packages/jest-config/build/color.d.ts b/packages/jest-config/build/color.d.ts new file mode 100644 index 000000000000..e6822a57fe88 --- /dev/null +++ b/packages/jest-config/build/color.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import type { ForegroundColor } from 'chalk'; +declare type Color = typeof ForegroundColor; +export declare const getDisplayNameColor: (seed?: string | undefined) => Color; +export {}; diff --git a/packages/jest-config/build/color.js b/packages/jest-config/build/color.js new file mode 100644 index 000000000000..4955faf3cbf2 --- /dev/null +++ b/packages/jest-config/build/color.js @@ -0,0 +1,37 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.getDisplayNameColor = void 0; + +function _crypto() { + const data = require('crypto'); + + _crypto = function () { + return data; + }; + + return data; +} + +/** + * 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 colors = ['red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']; + +const getDisplayNameColor = seed => { + if (seed === undefined) { + return 'white'; + } + + const hash = (0, _crypto().createHash)('sha256'); + hash.update(seed); + const num = hash.digest().readUInt32LE(0); + return colors[num % colors.length]; +}; + +exports.getDisplayNameColor = getDisplayNameColor; diff --git a/packages/jest-config/build/constants.d.ts b/packages/jest-config/build/constants.d.ts new file mode 100644 index 000000000000..90de36b82fd7 --- /dev/null +++ b/packages/jest-config/build/constants.d.ts @@ -0,0 +1,17 @@ +/** + * 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. + */ +export declare const NODE_MODULES: string; +export declare const DEFAULT_JS_PATTERN = "\\.[jt]sx?$"; +export declare const DEFAULT_REPORTER_LABEL = "default"; +export declare const PACKAGE_JSON = "package.json"; +export declare const JEST_CONFIG_BASE_NAME = "jest.config"; +export declare const JEST_CONFIG_EXT_CJS = ".cjs"; +export declare const JEST_CONFIG_EXT_MJS = ".mjs"; +export declare const JEST_CONFIG_EXT_JS = ".js"; +export declare const JEST_CONFIG_EXT_TS = ".ts"; +export declare const JEST_CONFIG_EXT_JSON = ".json"; +export declare const JEST_CONFIG_EXT_ORDER: readonly string[]; diff --git a/packages/jest-config/build/constants.js b/packages/jest-config/build/constants.js new file mode 100644 index 000000000000..d49c4ccd0948 --- /dev/null +++ b/packages/jest-config/build/constants.js @@ -0,0 +1,93 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.JEST_CONFIG_EXT_ORDER = exports.JEST_CONFIG_EXT_JSON = exports.JEST_CONFIG_EXT_TS = exports.JEST_CONFIG_EXT_JS = exports.JEST_CONFIG_EXT_MJS = exports.JEST_CONFIG_EXT_CJS = exports.JEST_CONFIG_BASE_NAME = exports.PACKAGE_JSON = exports.DEFAULT_REPORTER_LABEL = exports.DEFAULT_JS_PATTERN = exports.NODE_MODULES = void 0; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 NODE_MODULES = path().sep + 'node_modules' + path().sep; +exports.NODE_MODULES = NODE_MODULES; +const DEFAULT_JS_PATTERN = '\\.[jt]sx?$'; +exports.DEFAULT_JS_PATTERN = DEFAULT_JS_PATTERN; +const DEFAULT_REPORTER_LABEL = 'default'; +exports.DEFAULT_REPORTER_LABEL = DEFAULT_REPORTER_LABEL; +const PACKAGE_JSON = 'package.json'; +exports.PACKAGE_JSON = PACKAGE_JSON; +const JEST_CONFIG_BASE_NAME = 'jest.config'; +exports.JEST_CONFIG_BASE_NAME = JEST_CONFIG_BASE_NAME; +const JEST_CONFIG_EXT_CJS = '.cjs'; +exports.JEST_CONFIG_EXT_CJS = JEST_CONFIG_EXT_CJS; +const JEST_CONFIG_EXT_MJS = '.mjs'; +exports.JEST_CONFIG_EXT_MJS = JEST_CONFIG_EXT_MJS; +const JEST_CONFIG_EXT_JS = '.js'; +exports.JEST_CONFIG_EXT_JS = JEST_CONFIG_EXT_JS; +const JEST_CONFIG_EXT_TS = '.ts'; +exports.JEST_CONFIG_EXT_TS = JEST_CONFIG_EXT_TS; +const JEST_CONFIG_EXT_JSON = '.json'; +exports.JEST_CONFIG_EXT_JSON = JEST_CONFIG_EXT_JSON; +const JEST_CONFIG_EXT_ORDER = Object.freeze([ + JEST_CONFIG_EXT_JS, + JEST_CONFIG_EXT_TS, + JEST_CONFIG_EXT_MJS, + JEST_CONFIG_EXT_CJS, + JEST_CONFIG_EXT_JSON +]); +exports.JEST_CONFIG_EXT_ORDER = JEST_CONFIG_EXT_ORDER; diff --git a/packages/jest-config/build/getCacheDirectory.d.ts b/packages/jest-config/build/getCacheDirectory.d.ts new file mode 100644 index 000000000000..c642e9376890 --- /dev/null +++ b/packages/jest-config/build/getCacheDirectory.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +declare const getCacheDirectory: () => Config.Path; +export default getCacheDirectory; diff --git a/packages/jest-config/build/getCacheDirectory.js b/packages/jest-config/build/getCacheDirectory.js new file mode 100644 index 000000000000..b228ebeea59a --- /dev/null +++ b/packages/jest-config/build/getCacheDirectory.js @@ -0,0 +1,104 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _os() { + const data = require('os'); + + _os = function () { + return data; + }; + + return data; +} + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 getCacheDirectory = () => { + const {getuid} = process; + const tmpdirPath = path().join( + (0, _jestUtil().tryRealpath)((0, _os().tmpdir)()), + 'jest' + ); + + if (getuid == null) { + return tmpdirPath; + } else { + // On some platforms tmpdir() is `/tmp`, causing conflicts between different + // users and permission issues. Adding an additional subdivision by UID can + // help. + return `${tmpdirPath}_${getuid.call(process).toString(36)}`; + } +}; + +var _default = getCacheDirectory; +exports.default = _default; diff --git a/packages/jest-config/build/getMaxWorkers.d.ts b/packages/jest-config/build/getMaxWorkers.d.ts new file mode 100644 index 000000000000..81dfedc367bc --- /dev/null +++ b/packages/jest-config/build/getMaxWorkers.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function getMaxWorkers(argv: Partial>, defaultOptions?: Partial>): number; diff --git a/packages/jest-config/build/getMaxWorkers.js b/packages/jest-config/build/getMaxWorkers.js new file mode 100644 index 000000000000..7f97165cc241 --- /dev/null +++ b/packages/jest-config/build/getMaxWorkers.js @@ -0,0 +1,57 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getMaxWorkers; + +function _os() { + const data = require('os'); + + _os = function () { + return data; + }; + + return data; +} + +/** + * 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. + */ +function getMaxWorkers(argv, defaultOptions) { + if (argv.runInBand) { + return 1; + } else if (argv.maxWorkers) { + return parseWorkers(argv.maxWorkers); + } else if (defaultOptions && defaultOptions.maxWorkers) { + return parseWorkers(defaultOptions.maxWorkers); + } else { + // In watch mode, Jest should be unobtrusive and not use all available CPUs. + const numCpus = (0, _os().cpus)() ? (0, _os().cpus)().length : 1; + const isWatchModeEnabled = argv.watch || argv.watchAll; + return Math.max( + isWatchModeEnabled ? Math.floor(numCpus / 2) : numCpus - 1, + 1 + ); + } +} + +const parseWorkers = maxWorkers => { + const parsed = parseInt(maxWorkers.toString(), 10); + + if ( + typeof maxWorkers === 'string' && + maxWorkers.trim().endsWith('%') && + parsed > 0 && + parsed <= 100 + ) { + const numCpus = (0, _os().cpus)().length; + const workers = Math.floor((parsed / 100) * numCpus); + return workers >= 1 ? workers : 1; + } + + return parsed > 0 ? parsed : 1; +}; diff --git a/packages/jest-config/build/index.d.ts b/packages/jest-config/build/index.d.ts new file mode 100644 index 000000000000..86a152e29a43 --- /dev/null +++ b/packages/jest-config/build/index.d.ts @@ -0,0 +1,27 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import * as constants from './constants'; +export { getTestEnvironment, isJSONString } from './utils'; +export { default as normalize } from './normalize'; +export { default as deprecationEntries } from './Deprecated'; +export { replaceRootDirInPath } from './utils'; +export { default as defaults } from './Defaults'; +export { default as descriptions } from './Descriptions'; +export { constants }; +declare type ReadConfig = { + configPath: Config.Path | null | undefined; + globalConfig: Config.GlobalConfig; + hasDeprecationWarnings: boolean; + projectConfig: Config.ProjectConfig; +}; +export declare function readConfig(argv: Config.Argv, packageRootOrConfig: Config.Path | Config.InitialOptions, skipArgvConfigOption?: boolean, parentConfigPath?: Config.Path | null, projectIndex?: number): Promise; +export declare function readConfigs(argv: Config.Argv, projectPaths: Array): Promise<{ + globalConfig: Config.GlobalConfig; + configs: Array; + hasDeprecationWarnings: boolean; +}>; diff --git a/packages/jest-config/build/index.js b/packages/jest-config/build/index.js new file mode 100644 index 000000000000..0de0ad6bfd07 --- /dev/null +++ b/packages/jest-config/build/index.js @@ -0,0 +1,479 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.readConfig = readConfig; +exports.readConfigs = readConfigs; +Object.defineProperty(exports, 'normalize', { + enumerable: true, + get: function () { + return _normalize.default; + } +}); +Object.defineProperty(exports, 'getTestEnvironment', { + enumerable: true, + get: function () { + return _utils.getTestEnvironment; + } +}); +Object.defineProperty(exports, 'isJSONString', { + enumerable: true, + get: function () { + return _utils.isJSONString; + } +}); +Object.defineProperty(exports, 'replaceRootDirInPath', { + enumerable: true, + get: function () { + return _utils.replaceRootDirInPath; + } +}); +Object.defineProperty(exports, 'deprecationEntries', { + enumerable: true, + get: function () { + return _Deprecated.default; + } +}); +Object.defineProperty(exports, 'defaults', { + enumerable: true, + get: function () { + return _Defaults.default; + } +}); +Object.defineProperty(exports, 'descriptions', { + enumerable: true, + get: function () { + return _Descriptions.default; + } +}); +exports.constants = void 0; + +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 fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var constants = _interopRequireWildcard(require('./constants')); + +exports.constants = constants; + +var _normalize = _interopRequireDefault(require('./normalize')); + +var _readConfigFileAndSetRootDir = _interopRequireDefault( + require('./readConfigFileAndSetRootDir') +); + +var _resolveConfigPath = _interopRequireDefault(require('./resolveConfigPath')); + +var _utils = require('./utils'); + +var _Deprecated = _interopRequireDefault(require('./Deprecated')); + +var _Defaults = _interopRequireDefault(require('./Defaults')); + +var _Descriptions = _interopRequireDefault(require('./Descriptions')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +async function readConfig( + argv, + packageRootOrConfig, // Whether it needs to look into `--config` arg passed to CLI. + // It only used to read initial config. If the initial config contains + // `project` property, we don't want to read `--config` value and rather + // read individual configs for every project. + skipArgvConfigOption, + parentConfigPath, + projectIndex = Infinity +) { + let rawOptions; + let configPath = null; + + if (typeof packageRootOrConfig !== 'string') { + if (parentConfigPath) { + const parentConfigDirname = path().dirname(parentConfigPath); + rawOptions = packageRootOrConfig; + rawOptions.rootDir = rawOptions.rootDir + ? (0, _utils.replaceRootDirInPath)( + parentConfigDirname, + rawOptions.rootDir + ) + : parentConfigDirname; + } else { + throw new Error( + 'Jest: Cannot use configuration as an object without a file path.' + ); + } + } else if ((0, _utils.isJSONString)(argv.config)) { + // A JSON string was passed to `--config` argument and we can parse it + // and use as is. + let config; + + try { + config = JSON.parse(argv.config); + } catch { + throw new Error( + 'There was an error while parsing the `--config` argument as a JSON string.' + ); + } // NOTE: we might need to resolve this dir to an absolute path in the future + + config.rootDir = config.rootDir || packageRootOrConfig; + rawOptions = config; // A string passed to `--config`, which is either a direct path to the config + // or a path to directory containing `package.json`, `jest.config.js` or `jest.config.ts` + } else if (!skipArgvConfigOption && typeof argv.config == 'string') { + configPath = (0, _resolveConfigPath.default)(argv.config, process.cwd()); + rawOptions = await (0, _readConfigFileAndSetRootDir.default)(configPath); + } else { + // Otherwise just try to find config in the current rootDir. + configPath = (0, _resolveConfigPath.default)( + packageRootOrConfig, + process.cwd() + ); + rawOptions = await (0, _readConfigFileAndSetRootDir.default)(configPath); + } + + if (typeof rawOptions === 'function') { + rawOptions = await rawOptions(); + } + + const {options, hasDeprecationWarnings} = (0, _normalize.default)( + rawOptions, + argv, + configPath, + projectIndex + ); + const {globalConfig, projectConfig} = groupOptions(options); + return { + configPath, + globalConfig, + hasDeprecationWarnings, + projectConfig + }; +} + +const groupOptions = options => ({ + globalConfig: Object.freeze({ + bail: options.bail, + changedFilesWithAncestor: options.changedFilesWithAncestor, + changedSince: options.changedSince, + collectCoverage: options.collectCoverage, + collectCoverageFrom: options.collectCoverageFrom, + collectCoverageOnlyFrom: options.collectCoverageOnlyFrom, + coverageDirectory: options.coverageDirectory, + coverageProvider: options.coverageProvider, + coverageReporters: options.coverageReporters, + coverageThreshold: options.coverageThreshold, + detectLeaks: options.detectLeaks, + detectOpenHandles: options.detectOpenHandles, + errorOnDeprecated: options.errorOnDeprecated, + expand: options.expand, + filter: options.filter, + findRelatedTests: options.findRelatedTests, + forceExit: options.forceExit, + globalSetup: options.globalSetup, + globalTeardown: options.globalTeardown, + json: options.json, + lastCommit: options.lastCommit, + listTests: options.listTests, + logHeapUsage: options.logHeapUsage, + maxConcurrency: options.maxConcurrency, + maxWorkers: options.maxWorkers, + noSCM: undefined, + noStackTrace: options.noStackTrace, + nonFlagArgs: options.nonFlagArgs, + notify: options.notify, + notifyMode: options.notifyMode, + onlyChanged: options.onlyChanged, + onlyFailures: options.onlyFailures, + outputFile: options.outputFile, + passWithNoTests: options.passWithNoTests, + projects: options.projects, + replname: options.replname, + reporters: options.reporters, + rootDir: options.rootDir, + runTestsByPath: options.runTestsByPath, + silent: options.silent, + skipFilter: options.skipFilter, + testFailureExitCode: options.testFailureExitCode, + testNamePattern: options.testNamePattern, + testPathPattern: options.testPathPattern, + testResultsProcessor: options.testResultsProcessor, + testSequencer: options.testSequencer, + testTimeout: options.testTimeout, + updateSnapshot: options.updateSnapshot, + useStderr: options.useStderr, + verbose: options.verbose, + watch: options.watch, + watchAll: options.watchAll, + watchPlugins: options.watchPlugins, + watchman: options.watchman + }), + projectConfig: Object.freeze({ + automock: options.automock, + cache: options.cache, + cacheDirectory: options.cacheDirectory, + clearMocks: options.clearMocks, + coveragePathIgnorePatterns: options.coveragePathIgnorePatterns, + cwd: options.cwd, + dependencyExtractor: options.dependencyExtractor, + detectLeaks: options.detectLeaks, + detectOpenHandles: options.detectOpenHandles, + displayName: options.displayName, + errorOnDeprecated: options.errorOnDeprecated, + extensionsToTreatAsEsm: options.extensionsToTreatAsEsm, + extraGlobals: options.extraGlobals, + filter: options.filter, + forceCoverageMatch: options.forceCoverageMatch, + globalSetup: options.globalSetup, + globalTeardown: options.globalTeardown, + globals: options.globals, + haste: options.haste, + injectGlobals: options.injectGlobals, + moduleDirectories: options.moduleDirectories, + moduleFileExtensions: options.moduleFileExtensions, + moduleLoader: options.moduleLoader, + moduleNameMapper: options.moduleNameMapper, + modulePathIgnorePatterns: options.modulePathIgnorePatterns, + modulePaths: options.modulePaths, + name: options.name, + prettierPath: options.prettierPath, + resetMocks: options.resetMocks, + resetModules: options.resetModules, + resolver: options.resolver, + restoreMocks: options.restoreMocks, + rootDir: options.rootDir, + roots: options.roots, + runner: options.runner, + setupFiles: options.setupFiles, + setupFilesAfterEnv: options.setupFilesAfterEnv, + skipFilter: options.skipFilter, + skipNodeResolution: options.skipNodeResolution, + slowTestThreshold: options.slowTestThreshold, + snapshotResolver: options.snapshotResolver, + snapshotSerializers: options.snapshotSerializers, + testEnvironment: options.testEnvironment, + testEnvironmentOptions: options.testEnvironmentOptions, + testLocationInResults: options.testLocationInResults, + testMatch: options.testMatch, + testPathIgnorePatterns: options.testPathIgnorePatterns, + testRegex: options.testRegex, + testRunner: options.testRunner, + testURL: options.testURL, + timers: options.timers, + transform: options.transform, + transformIgnorePatterns: options.transformIgnorePatterns, + unmockedModulePathPatterns: options.unmockedModulePathPatterns, + watchPathIgnorePatterns: options.watchPathIgnorePatterns + }) +}); + +const ensureNoDuplicateConfigs = (parsedConfigs, projects) => { + if (projects.length <= 1) { + return; + } + + const configPathMap = new Map(); + + for (const config of parsedConfigs) { + const {configPath} = config; + + if (configPathMap.has(configPath)) { + const message = `Whoops! Two projects resolved to the same config path: ${_chalk().default.bold( + String(configPath) + )}: + + Project 1: ${_chalk().default.bold( + projects[parsedConfigs.findIndex(x => x === config)] + )} + Project 2: ${_chalk().default.bold( + projects[parsedConfigs.findIndex(x => x === configPathMap.get(configPath))] + )} + +This usually means that your ${_chalk().default.bold( + '"projects"' + )} config includes a directory that doesn't have any configuration recognizable by Jest. Please fix it. +`; + throw new Error(message); + } + + if (configPath !== null) { + configPathMap.set(configPath, config); + } + } +}; // Possible scenarios: +// 1. jest --config config.json +// 2. jest --projects p1 p2 +// 3. jest --projects p1 p2 --config config.json +// 4. jest --projects p1 +// 5. jest +// +// If no projects are specified, process.cwd() will be used as the default +// (and only) project. + +async function readConfigs(argv, projectPaths) { + let globalConfig; + let hasDeprecationWarnings; + let configs = []; + let projects = projectPaths; + let configPath; + + if (projectPaths.length === 1) { + const parsedConfig = await readConfig(argv, projects[0]); + configPath = parsedConfig.configPath; + hasDeprecationWarnings = parsedConfig.hasDeprecationWarnings; + globalConfig = parsedConfig.globalConfig; + configs = [parsedConfig.projectConfig]; + + if (globalConfig.projects && globalConfig.projects.length) { + // Even though we had one project in CLI args, there might be more + // projects defined in the config. + // In other words, if this was a single project, + // and its config has `projects` settings, use that value instead. + projects = globalConfig.projects; + } + } + + if (projects.length > 0) { + const projectIsCwd = + process.platform === 'win32' + ? projects[0] === (0, _jestUtil().tryRealpath)(process.cwd()) + : projects[0] === process.cwd(); + const parsedConfigs = await Promise.all( + projects + .filter(root => { + // Ignore globbed files that cannot be `require`d. + if ( + typeof root === 'string' && + fs().existsSync(root) && + !fs().lstatSync(root).isDirectory() && + !constants.JEST_CONFIG_EXT_ORDER.some(ext => root.endsWith(ext)) + ) { + return false; + } + + return true; + }) + .map((root, projectIndex) => { + const projectIsTheOnlyProject = + projectIndex === 0 && projects.length === 1; + const skipArgvConfigOption = !( + projectIsTheOnlyProject && projectIsCwd + ); + return readConfig( + argv, + root, + skipArgvConfigOption, + configPath, + projectIndex + ); + }) + ); + ensureNoDuplicateConfigs(parsedConfigs, projects); + configs = parsedConfigs.map(({projectConfig}) => projectConfig); + + if (!hasDeprecationWarnings) { + hasDeprecationWarnings = parsedConfigs.some( + ({hasDeprecationWarnings}) => !!hasDeprecationWarnings + ); + } // If no config was passed initially, use the one from the first project + + if (!globalConfig) { + globalConfig = parsedConfigs[0].globalConfig; + } + } + + if (!globalConfig || !configs.length) { + throw new Error('jest: No configuration found for any project.'); + } + + return { + configs, + globalConfig, + hasDeprecationWarnings: !!hasDeprecationWarnings + }; +} diff --git a/packages/jest-config/build/normalize.d.ts b/packages/jest-config/build/normalize.d.ts new file mode 100644 index 000000000000..fc939e63d758 --- /dev/null +++ b/packages/jest-config/build/normalize.d.ts @@ -0,0 +1,13 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +declare type AllOptions = Config.ProjectConfig & Config.GlobalConfig; +export default function normalize(initialOptions: Config.InitialOptions, argv: Config.Argv, configPath?: Config.Path | null, projectIndex?: number): { + hasDeprecationWarnings: boolean; + options: AllOptions; +}; +export {}; diff --git a/packages/jest-config/build/normalize.js b/packages/jest-config/build/normalize.js new file mode 100644 index 000000000000..3596461f2286 --- /dev/null +++ b/packages/jest-config/build/normalize.js @@ -0,0 +1,1325 @@ +'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 = _interopRequireDefault(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() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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']; +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 = (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 = require(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(/^(!?)(\/)(.*)/, '$1$3') + ); + } + + return value; +}; + +const normalizeUnmockedModulePathPatterns = ( + options, + key // _replaceRootDirTags is specifically well-suited for substituting +) => + // 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(//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.` + ); + } +} + +function normalize(initialOptions, argv, configPath, projectIndex = Infinity) { + var _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 = 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, _utils.getTestEnvironment)({ + rootDir: options.rootDir, + testEnvironment: + options.testEnvironment || _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); + 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, _utils.getRunner)(newOptions.resolver, { + filePath: option, + 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 '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, _utils.getWatchPlugin)(newOptions.resolver, { + filePath: watchPlugin, + rootDir: options.rootDir + }) + }; + } else { + return { + config: watchPlugin[1] || {}, + path: (0, _utils.getWatchPlugin)(newOptions.resolver, { + filePath: watchPlugin[0], + rootDir: options.rootDir + }) + }; + } + }); + break; + } // @ts-expect-error: automock is missing in GlobalConfig, so what + + newOptions[key] = value; + return newOptions; + }, newOptions); + 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, _utils.getSequencer)(newOptions.resolver, { + filePath: options.testSequencer || _Defaults.default.testSequencer, + rootDir: options.rootDir + }); + 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 + }; +} diff --git a/packages/jest-config/build/readConfigFileAndSetRootDir.d.ts b/packages/jest-config/build/readConfigFileAndSetRootDir.d.ts new file mode 100644 index 000000000000..0b5a48fb65c9 --- /dev/null +++ b/packages/jest-config/build/readConfigFileAndSetRootDir.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function readConfigFileAndSetRootDir(configPath: Config.Path): Promise; diff --git a/packages/jest-config/build/readConfigFileAndSetRootDir.js b/packages/jest-config/build/readConfigFileAndSetRootDir.js new file mode 100644 index 000000000000..8d58f0b04975 --- /dev/null +++ b/packages/jest-config/build/readConfigFileAndSetRootDir.js @@ -0,0 +1,211 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = readConfigFileAndSetRootDir; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _url() { + const data = require('url'); + + _url = function () { + return data; + }; + + return data; +} + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _constants = require('./constants'); + +var _jsonlint = _interopRequireDefault(require('./vendor/jsonlint')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +// @ts-expect-error: vendored +// Read the configuration and set its `rootDir` +// 1. If it's a `package.json` file, we look into its "jest" property +// 2. If it's a `jest.config.ts` file, we use `ts-node` to transpile & require it +// 3. For any other file, we just require it. If we receive an 'ERR_REQUIRE_ESM' +// from node, perform a dynamic import instead. +async function readConfigFileAndSetRootDir(configPath) { + const isTS = configPath.endsWith(_constants.JEST_CONFIG_EXT_TS); + const isJSON = configPath.endsWith(_constants.JEST_CONFIG_EXT_JSON); + let configObject; + + try { + if (isTS) { + configObject = await loadTSConfigFile(configPath); + } else { + configObject = require(configPath); + } + } catch (error) { + if (error.code === 'ERR_REQUIRE_ESM') { + try { + const configUrl = (0, _url().pathToFileURL)(configPath); // node `import()` supports URL, but TypeScript doesn't know that + + const importedConfig = await import(configUrl.href); + + if (!importedConfig.default) { + throw new Error( + `Jest: Failed to load mjs config file ${configPath} - did you use a default export?` + ); + } + + configObject = importedConfig.default; + } catch (innerError) { + if (innerError.message === 'Not supported') { + throw new Error( + `Jest: Your version of Node does not support dynamic import - please enable it or use a .cjs file extension for file ${configPath}` + ); + } + + throw innerError; + } + } else if (isJSON) { + throw new Error( + `Jest: Failed to parse config file ${configPath}\n` + + ` ${_jsonlint.default.errors(fs().readFileSync(configPath, 'utf8'))}` + ); + } else if (isTS) { + throw new Error( + `Jest: Failed to parse the TypeScript config file ${configPath}\n` + + ` ${error}` + ); + } else { + throw error; + } + } + + if (configPath.endsWith(_constants.PACKAGE_JSON)) { + // Event if there's no "jest" property in package.json we will still use + // an empty object. + configObject = configObject.jest || {}; + } + + if (configObject.rootDir) { + // We don't touch it if it has an absolute path specified + if (!path().isAbsolute(configObject.rootDir)) { + // otherwise, we'll resolve it relative to the file's __dirname + configObject.rootDir = path().resolve( + path().dirname(configPath), + configObject.rootDir + ); + } + } else { + // If rootDir is not there, we'll set it to this file's __dirname + configObject.rootDir = path().dirname(configPath); + } + + return configObject; +} // Load the TypeScript configuration + +const loadTSConfigFile = async configPath => { + let registerer; // Register TypeScript compiler instance + + try { + registerer = require('ts-node').register({ + compilerOptions: { + module: 'CommonJS' + } + }); + } catch (e) { + if (e.code === 'MODULE_NOT_FOUND') { + throw new Error( + `Jest: 'ts-node' is required for the TypeScript configuration files. Make sure it is installed\nError: ${e.message}` + ); + } + + throw e; + } + + registerer.enabled(true); + let configObject = (0, _jestUtil().interopRequireDefault)(require(configPath)) + .default; // In case the config is a function which imports more Typescript code + + if (typeof configObject === 'function') { + configObject = await configObject(); + } + + registerer.enabled(false); + return configObject; +}; diff --git a/packages/jest-config/build/resolveConfigPath.d.ts b/packages/jest-config/build/resolveConfigPath.d.ts new file mode 100644 index 000000000000..3cac54f5a051 --- /dev/null +++ b/packages/jest-config/build/resolveConfigPath.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +declare const _default: (pathToResolve: Config.Path, cwd: Config.Path) => Config.Path; +export default _default; diff --git a/packages/jest-config/build/resolveConfigPath.js b/packages/jest-config/build/resolveConfigPath.js new file mode 100644 index 000000000000..9896ee9b9a40 --- /dev/null +++ b/packages/jest-config/build/resolveConfigPath.js @@ -0,0 +1,153 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +var _constants = require('./constants'); + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 isFile = filePath => + fs().existsSync(filePath) && !fs().lstatSync(filePath).isDirectory(); + +const getConfigFilename = ext => _constants.JEST_CONFIG_BASE_NAME + ext; + +var _default = (pathToResolve, cwd) => { + if (!path().isAbsolute(cwd)) { + throw new Error(`"cwd" must be an absolute path. cwd: ${cwd}`); + } + + const absolutePath = path().isAbsolute(pathToResolve) + ? pathToResolve + : path().resolve(cwd, pathToResolve); + + if (isFile(absolutePath)) { + return absolutePath; + } // This is a guard against passing non existing path as a project/config, + // that will otherwise result in a very confusing situation. + // e.g. + // With a directory structure like this: + // my_project/ + // packcage.json + // + // Passing a `my_project/some_directory_that_doesnt_exist` as a project + // name will resolve into a (possibly empty) `my_project/package.json` and + // try to run all tests it finds under `my_project` directory. + + if (!fs().existsSync(absolutePath)) { + throw new Error( + `Can't find a root directory while resolving a config file path.\n` + + `Provided path to resolve: ${pathToResolve}\n` + + `cwd: ${cwd}` + ); + } + + return resolveConfigPathByTraversing(absolutePath, pathToResolve, cwd); +}; + +exports.default = _default; + +const resolveConfigPathByTraversing = (pathToResolve, initialPath, cwd) => { + const jestConfig = _constants.JEST_CONFIG_EXT_ORDER.map(ext => + path().resolve(pathToResolve, getConfigFilename(ext)) + ).find(isFile); + + if (jestConfig) { + return jestConfig; + } + + const packageJson = path().resolve(pathToResolve, _constants.PACKAGE_JSON); + + if (isFile(packageJson)) { + return packageJson; + } // This is the system root. + // We tried everything, config is nowhere to be found ¯\_(ツ)_/¯ + + if (pathToResolve === path().dirname(pathToResolve)) { + throw new Error(makeResolutionErrorMessage(initialPath, cwd)); + } // go up a level and try it again + + return resolveConfigPathByTraversing( + path().dirname(pathToResolve), + initialPath, + cwd + ); +}; + +const makeResolutionErrorMessage = (initialPath, cwd) => + 'Could not find a config file based on provided values:\n' + + `path: "${initialPath}"\n` + + `cwd: "${cwd}"\n` + + 'Config paths must be specified by either a direct path to a config\n' + + 'file, or a path to a directory. If directory is given, Jest will try to\n' + + `traverse directory tree up, until it finds one of those files in exact order: ${_constants.JEST_CONFIG_EXT_ORDER.map( + ext => `"${getConfigFilename(ext)}"` + ).join(' or ')}.`; diff --git a/packages/jest-config/build/setFromArgv.d.ts b/packages/jest-config/build/setFromArgv.d.ts new file mode 100644 index 000000000000..1e45a955a8fe --- /dev/null +++ b/packages/jest-config/build/setFromArgv.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function setFromArgv(options: Config.InitialOptions, argv: Config.Argv): Config.InitialOptions; diff --git a/packages/jest-config/build/setFromArgv.js b/packages/jest-config/build/setFromArgv.js new file mode 100644 index 000000000000..6990440c389e --- /dev/null +++ b/packages/jest-config/build/setFromArgv.js @@ -0,0 +1,67 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = setFromArgv; + +var _utils = require('./utils'); + +/** + * 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 specialArgs = ['_', '$0', 'h', 'help', 'config']; + +function setFromArgv(options, argv) { + const argvToOptions = Object.keys(argv) + .filter(key => argv[key] !== undefined && specialArgs.indexOf(key) === -1) + .reduce((options, key) => { + switch (key) { + case 'coverage': + options.collectCoverage = argv[key]; + break; + + case 'json': + options.useStderr = argv[key]; + break; + + case 'watchAll': + options.watch = false; + options.watchAll = argv[key]; + break; + + case 'env': + options.testEnvironment = argv[key]; + break; + + case 'config': + break; + + case 'coverageThreshold': + case 'globals': + case 'moduleNameMapper': + case 'transform': + case 'haste': + const str = argv[key]; + + if ((0, _utils.isJSONString)(str)) { + options[key] = JSON.parse(str); + } + + break; + + default: + options[key] = argv[key]; + } + + return options; + }, {}); + return { + ...options, + ...((0, _utils.isJSONString)(argv.config) ? JSON.parse(argv.config) : null), + ...argvToOptions + }; +} diff --git a/packages/jest-config/build/utils.d.ts b/packages/jest-config/build/utils.d.ts new file mode 100644 index 000000000000..1faef7b44847 --- /dev/null +++ b/packages/jest-config/build/utils.d.ts @@ -0,0 +1,74 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +declare type ResolveOptions = { + rootDir: Config.Path; + key: string; + filePath: Config.Path; + optional?: boolean; +}; +export declare const BULLET: string; +export declare const DOCUMENTATION_NOTE: string; +export declare const resolve: (resolver: string | null | undefined, { key, filePath, rootDir, optional }: ResolveOptions) => string; +export declare const escapeGlobCharacters: (path: Config.Path) => Config.Glob; +export declare const replaceRootDirInPath: (rootDir: Config.Path, filePath: Config.Path) => string; +declare type OrArray = T | Array; +declare type ReplaceRootDirConfigObj = Record; +declare type ReplaceRootDirConfigValues = OrArray | OrArray | OrArray; +export declare const _replaceRootDirTags: (rootDir: Config.Path, config: T) => T; +export declare const resolveWithPrefix: (resolver: string | undefined | null, { filePath, humanOptionName, optionName, prefix, rootDir, }: { + filePath: string; + humanOptionName: string; + optionName: string; + prefix: string; + rootDir: Config.Path; +}) => string; +/** + * Finds the test environment to use: + * + * 1. looks for jest-environment- relative to project. + * 1. looks for jest-environment- relative to Jest. + * 1. looks for relative to project. + * 1. looks for relative to Jest. + */ +export declare const getTestEnvironment: ({ rootDir, testEnvironment: filePath, }: { + rootDir: Config.Path; + testEnvironment: string; +}) => string; +/** + * Finds the watch plugins to use: + * + * 1. looks for jest-watch- relative to project. + * 1. looks for jest-watch- relative to Jest. + * 1. looks for relative to project. + * 1. looks for relative to Jest. + */ +export declare const getWatchPlugin: (resolver: string | undefined | null, { filePath, rootDir }: { + filePath: string; + rootDir: Config.Path; +}) => string; +/** + * Finds the runner to use: + * + * 1. looks for jest-runner- relative to project. + * 1. looks for jest-runner- relative to Jest. + * 1. looks for relative to project. + * 1. looks for relative to Jest. + */ +export declare const getRunner: (resolver: string | undefined | null, { filePath, rootDir }: { + filePath: string; + rootDir: Config.Path; +}) => string; +declare type JSONString = string & { + readonly $$type: never; +}; +export declare const isJSONString: (text?: string | JSONString | undefined) => text is JSONString; +export declare const getSequencer: (resolver: string | undefined | null, { filePath, rootDir }: { + filePath: string; + rootDir: Config.Path; +}) => string; +export {}; diff --git a/packages/jest-config/build/utils.js b/packages/jest-config/build/utils.js new file mode 100644 index 000000000000..ce23c4c25e2e --- /dev/null +++ b/packages/jest-config/build/utils.js @@ -0,0 +1,313 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.getSequencer = exports.isJSONString = exports.getRunner = exports.getWatchPlugin = exports.getTestEnvironment = exports.resolveWithPrefix = exports._replaceRootDirTags = exports.replaceRootDirInPath = exports.escapeGlobCharacters = exports.resolve = exports.DOCUMENTATION_NOTE = exports.BULLET = void 0; + +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 _jestResolve() { + const data = _interopRequireDefault(require('jest-resolve')); + + _jestResolve = function () { + return data; + }; + + return data; +} + +function _jestValidate() { + const data = require('jest-validate'); + + _jestValidate = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 BULLET = _chalk().default.bold('\u25cf '); + +exports.BULLET = BULLET; +const DOCUMENTATION_NOTE = ` ${_chalk().default.bold( + 'Configuration Documentation:' +)} + https://jestjs.io/docs/configuration.html +`; +exports.DOCUMENTATION_NOTE = DOCUMENTATION_NOTE; + +const createValidationError = message => + new (_jestValidate().ValidationError)( + `${BULLET}Validation Error`, + message, + DOCUMENTATION_NOTE + ); + +const resolve = (resolver, {key, filePath, rootDir, optional}) => { + const module = _jestResolve().default.findNodeModule( + replaceRootDirInPath(rootDir, filePath), + { + basedir: rootDir, + resolver: resolver || undefined + } + ); + + if (!module && !optional) { + throw createValidationError(` Module ${_chalk().default.bold( + filePath + )} in the ${_chalk().default.bold(key)} option was not found. + ${_chalk().default.bold('')} is: ${rootDir}`); + } /// can cast as string since nulls will be thrown + + return module; +}; + +exports.resolve = resolve; + +const escapeGlobCharacters = path => path.replace(/([()*{}\[\]!?\\])/g, '\\$1'); + +exports.escapeGlobCharacters = escapeGlobCharacters; + +const replaceRootDirInPath = (rootDir, filePath) => { + if (!/^/.test(filePath)) { + return filePath; + } + + return path().resolve( + rootDir, + path().normalize('./' + filePath.substr(''.length)) + ); +}; + +exports.replaceRootDirInPath = replaceRootDirInPath; + +const _replaceRootDirInObject = (rootDir, config) => { + const newConfig = {}; + + for (const configKey in config) { + newConfig[configKey] = + configKey === 'rootDir' + ? config[configKey] + : _replaceRootDirTags(rootDir, config[configKey]); + } + + return newConfig; +}; + +const _replaceRootDirTags = (rootDir, config) => { + if (config == null) { + return config; + } + + switch (typeof config) { + case 'object': + if (Array.isArray(config)) { + /// can be string[] or {}[] + return config.map(item => _replaceRootDirTags(rootDir, item)); + } + + if (config instanceof RegExp) { + return config; + } + + return _replaceRootDirInObject(rootDir, config); + + case 'string': + return replaceRootDirInPath(rootDir, config); + } + + return config; +}; + +exports._replaceRootDirTags = _replaceRootDirTags; + +const resolveWithPrefix = ( + resolver, + {filePath, humanOptionName, optionName, prefix, rootDir} +) => { + const fileName = replaceRootDirInPath(rootDir, filePath); + + let module = _jestResolve().default.findNodeModule(`${prefix}${fileName}`, { + basedir: rootDir, + resolver: resolver || undefined + }); + + if (module) { + return module; + } + + try { + return require.resolve(`${prefix}${fileName}`); + } catch {} + + module = _jestResolve().default.findNodeModule(fileName, { + basedir: rootDir, + resolver: resolver || undefined + }); + + if (module) { + return module; + } + + try { + return require.resolve(fileName); + } catch {} + + throw createValidationError( + ` ${humanOptionName} ${_chalk().default.bold( + fileName + )} cannot be found. Make sure the ${_chalk().default.bold( + optionName + )} configuration option points to an existing node module.` + ); +}; +/** + * Finds the test environment to use: + * + * 1. looks for jest-environment- relative to project. + * 1. looks for jest-environment- relative to Jest. + * 1. looks for relative to project. + * 1. looks for relative to Jest. + */ + +exports.resolveWithPrefix = resolveWithPrefix; + +const getTestEnvironment = ({rootDir, testEnvironment: filePath}) => + resolveWithPrefix(undefined, { + filePath, + humanOptionName: 'Test environment', + optionName: 'testEnvironment', + prefix: 'jest-environment-', + rootDir + }); +/** + * Finds the watch plugins to use: + * + * 1. looks for jest-watch- relative to project. + * 1. looks for jest-watch- relative to Jest. + * 1. looks for relative to project. + * 1. looks for relative to Jest. + */ + +exports.getTestEnvironment = getTestEnvironment; + +const getWatchPlugin = (resolver, {filePath, rootDir}) => + resolveWithPrefix(resolver, { + filePath, + humanOptionName: 'Watch plugin', + optionName: 'watchPlugins', + prefix: 'jest-watch-', + rootDir + }); +/** + * Finds the runner to use: + * + * 1. looks for jest-runner- relative to project. + * 1. looks for jest-runner- relative to Jest. + * 1. looks for relative to project. + * 1. looks for relative to Jest. + */ + +exports.getWatchPlugin = getWatchPlugin; + +const getRunner = (resolver, {filePath, rootDir}) => + resolveWithPrefix(resolver, { + filePath, + humanOptionName: 'Jest Runner', + optionName: 'runner', + prefix: 'jest-runner-', + rootDir + }); + +exports.getRunner = getRunner; + +// newtype +const isJSONString = text => + text != null && + typeof text === 'string' && + text.startsWith('{') && + text.endsWith('}'); + +exports.isJSONString = isJSONString; + +const getSequencer = (resolver, {filePath, rootDir}) => + resolveWithPrefix(resolver, { + filePath, + humanOptionName: 'Jest Sequencer', + optionName: 'testSequencer', + prefix: 'jest-sequencer-', + rootDir + }); + +exports.getSequencer = getSequencer; diff --git a/packages/jest-config/build/validatePattern.d.ts b/packages/jest-config/build/validatePattern.d.ts new file mode 100644 index 000000000000..8f15207c5011 --- /dev/null +++ b/packages/jest-config/build/validatePattern.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function validatePattern(pattern?: string): boolean; diff --git a/packages/jest-config/build/validatePattern.js b/packages/jest-config/build/validatePattern.js new file mode 100644 index 000000000000..d18752e5e335 --- /dev/null +++ b/packages/jest-config/build/validatePattern.js @@ -0,0 +1,25 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = validatePattern; + +/** + * 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. + */ +function validatePattern(pattern) { + if (pattern) { + try { + // eslint-disable-next-line no-new + new RegExp(pattern, 'i'); + } catch { + return false; + } + } + + return true; +} diff --git a/packages/jest-config/build/vendor/jsonlint.js b/packages/jest-config/build/vendor/jsonlint.js new file mode 100644 index 000000000000..93399c5615d1 --- /dev/null +++ b/packages/jest-config/build/vendor/jsonlint.js @@ -0,0 +1,917 @@ +'use strict'; + +// From: https://github.com/zaach/jsonlint +// Vendored in Jest to avoid jsonlint's transitive dependencies. + +/* eslint-disable */ +var jsonlint = (function () { + var parser = { + trace: function trace() {}, + yy: {}, + symbols_: { + error: 2, + JSONString: 3, + STRING: 4, + JSONNumber: 5, + NUMBER: 6, + JSONNullLiteral: 7, + NULL: 8, + JSONBooleanLiteral: 9, + TRUE: 10, + FALSE: 11, + JSONText: 12, + JSONValue: 13, + EOF: 14, + JSONObject: 15, + JSONArray: 16, + '{': 17, + '}': 18, + JSONMemberList: 19, + JSONMember: 20, + ':': 21, + ',': 22, + '[': 23, + ']': 24, + JSONElementList: 25, + $accept: 0, + $end: 1 + }, + terminals_: { + 2: 'error', + 4: 'STRING', + 6: 'NUMBER', + 8: 'NULL', + 10: 'TRUE', + 11: 'FALSE', + 14: 'EOF', + 17: '{', + 18: '}', + 21: ':', + 22: ',', + 23: '[', + 24: ']' + }, + productions_: [ + 0, + [3, 1], + [5, 1], + [7, 1], + [9, 1], + [9, 1], + [12, 2], + [13, 1], + [13, 1], + [13, 1], + [13, 1], + [13, 1], + [13, 1], + [15, 2], + [15, 3], + [20, 3], + [19, 1], + [19, 3], + [16, 2], + [16, 3], + [25, 1], + [25, 3] + ], + performAction: function anonymous( + yytext, + yyleng, + yylineno, + yy, + yystate, + $$, + _$ + ) { + var $0 = $$.length - 1; + + switch (yystate) { + case 1: + // replace escaped characters with actual character + this.$ = yytext + .replace(/\\(\\|")/g, '$' + '1') + .replace(/\\n/g, '\n') + .replace(/\\r/g, '\r') + .replace(/\\t/g, '\t') + .replace(/\\v/g, '\v') + .replace(/\\f/g, '\f') + .replace(/\\b/g, '\b'); + break; + + case 2: + this.$ = Number(yytext); + break; + + case 3: + this.$ = null; + break; + + case 4: + this.$ = true; + break; + + case 5: + this.$ = false; + break; + + case 6: + return (this.$ = $$[$0 - 1]); + break; + + case 13: + this.$ = {}; + break; + + case 14: + this.$ = $$[$0 - 1]; + break; + + case 15: + this.$ = [$$[$0 - 2], $$[$0]]; + break; + + case 16: + this.$ = {}; + this.$[$$[$0][0]] = $$[$0][1]; + break; + + case 17: + this.$ = $$[$0 - 2]; + $$[$0 - 2][$$[$0][0]] = $$[$0][1]; + break; + + case 18: + this.$ = []; + break; + + case 19: + this.$ = $$[$0 - 1]; + break; + + case 20: + this.$ = [$$[$0]]; + break; + + case 21: + this.$ = $$[$0 - 2]; + $$[$0 - 2].push($$[$0]); + break; + } + }, + table: [ + { + 3: 5, + 4: [1, 12], + 5: 6, + 6: [1, 13], + 7: 3, + 8: [1, 9], + 9: 4, + 10: [1, 10], + 11: [1, 11], + 12: 1, + 13: 2, + 15: 7, + 16: 8, + 17: [1, 14], + 23: [1, 15] + }, + { + 1: [3] + }, + { + 14: [1, 16] + }, + { + 14: [2, 7], + 18: [2, 7], + 22: [2, 7], + 24: [2, 7] + }, + { + 14: [2, 8], + 18: [2, 8], + 22: [2, 8], + 24: [2, 8] + }, + { + 14: [2, 9], + 18: [2, 9], + 22: [2, 9], + 24: [2, 9] + }, + { + 14: [2, 10], + 18: [2, 10], + 22: [2, 10], + 24: [2, 10] + }, + { + 14: [2, 11], + 18: [2, 11], + 22: [2, 11], + 24: [2, 11] + }, + { + 14: [2, 12], + 18: [2, 12], + 22: [2, 12], + 24: [2, 12] + }, + { + 14: [2, 3], + 18: [2, 3], + 22: [2, 3], + 24: [2, 3] + }, + { + 14: [2, 4], + 18: [2, 4], + 22: [2, 4], + 24: [2, 4] + }, + { + 14: [2, 5], + 18: [2, 5], + 22: [2, 5], + 24: [2, 5] + }, + { + 14: [2, 1], + 18: [2, 1], + 21: [2, 1], + 22: [2, 1], + 24: [2, 1] + }, + { + 14: [2, 2], + 18: [2, 2], + 22: [2, 2], + 24: [2, 2] + }, + { + 3: 20, + 4: [1, 12], + 18: [1, 17], + 19: 18, + 20: 19 + }, + { + 3: 5, + 4: [1, 12], + 5: 6, + 6: [1, 13], + 7: 3, + 8: [1, 9], + 9: 4, + 10: [1, 10], + 11: [1, 11], + 13: 23, + 15: 7, + 16: 8, + 17: [1, 14], + 23: [1, 15], + 24: [1, 21], + 25: 22 + }, + { + 1: [2, 6] + }, + { + 14: [2, 13], + 18: [2, 13], + 22: [2, 13], + 24: [2, 13] + }, + { + 18: [1, 24], + 22: [1, 25] + }, + { + 18: [2, 16], + 22: [2, 16] + }, + { + 21: [1, 26] + }, + { + 14: [2, 18], + 18: [2, 18], + 22: [2, 18], + 24: [2, 18] + }, + { + 22: [1, 28], + 24: [1, 27] + }, + { + 22: [2, 20], + 24: [2, 20] + }, + { + 14: [2, 14], + 18: [2, 14], + 22: [2, 14], + 24: [2, 14] + }, + { + 3: 20, + 4: [1, 12], + 20: 29 + }, + { + 3: 5, + 4: [1, 12], + 5: 6, + 6: [1, 13], + 7: 3, + 8: [1, 9], + 9: 4, + 10: [1, 10], + 11: [1, 11], + 13: 30, + 15: 7, + 16: 8, + 17: [1, 14], + 23: [1, 15] + }, + { + 14: [2, 19], + 18: [2, 19], + 22: [2, 19], + 24: [2, 19] + }, + { + 3: 5, + 4: [1, 12], + 5: 6, + 6: [1, 13], + 7: 3, + 8: [1, 9], + 9: 4, + 10: [1, 10], + 11: [1, 11], + 13: 31, + 15: 7, + 16: 8, + 17: [1, 14], + 23: [1, 15] + }, + { + 18: [2, 17], + 22: [2, 17] + }, + { + 18: [2, 15], + 22: [2, 15] + }, + { + 22: [2, 21], + 24: [2, 21] + } + ], + defaultActions: { + 16: [2, 6] + }, + parseError: function parseError(str, hash) { + throw new Error(str); + }, + parse: function parse(input) { + var self = this, + stack = [0], + vstack = [null], + // semantic value stack + lstack = [], + // location stack + table = this.table, + yytext = '', + yylineno = 0, + yyleng = 0, + recovering = 0, + TERROR = 2, + EOF = 1; //this.reductionCount = this.shiftCount = 0; + + this.lexer.setInput(input); + this.lexer.yy = this.yy; + this.yy.lexer = this.lexer; + if (typeof this.lexer.yylloc == 'undefined') this.lexer.yylloc = {}; + var yyloc = this.lexer.yylloc; + lstack.push(yyloc); + if (typeof this.yy.parseError === 'function') + this.parseError = this.yy.parseError; + + function popStack(n) { + stack.length = stack.length - 2 * n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + + function lex() { + var token; + token = self.lexer.lex() || 1; // $end = 1 + // if token isn't its numeric value, convert + + if (typeof token !== 'number') { + token = self.symbols_[token] || token; + } + + return token; + } + + var symbol, + preErrorSymbol, + state, + action, + a, + r, + yyval = {}, + p, + len, + newState, + expected; + + while (true) { + // retrieve state number from top of stack + state = stack[stack.length - 1]; // use default actions if available + + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol == null) symbol = lex(); // read action for current state and first input + + action = table[state] && table[state][symbol]; + } // handle parse error + + _handle_error: if ( + typeof action === 'undefined' || + !action.length || + !action[0] + ) { + if (!recovering) { + // Report error + expected = []; + + for (p in table[state]) + if (this.terminals_[p] && p > 2) { + expected.push("'" + this.terminals_[p] + "'"); + } + + var errStr = ''; + + if (this.lexer.showPosition) { + errStr = + 'Parse error on line ' + + (yylineno + 1) + + ':\n' + + this.lexer.showPosition() + + '\nExpecting ' + + expected.join(', ') + + ", got '" + + this.terminals_[symbol] + + "'"; + } else { + errStr = + 'Parse error on line ' + + (yylineno + 1) + + ': Unexpected ' + + (symbol == 1 + ? /*EOF*/ + 'end of input' + : "'" + (this.terminals_[symbol] || symbol) + "'"); + } + + this.parseError(errStr, { + text: this.lexer.match, + token: this.terminals_[symbol] || symbol, + line: this.lexer.yylineno, + loc: yyloc, + expected: expected + }); + } // just recovered from another error + + if (recovering == 3) { + if (symbol == EOF) { + throw new Error(errStr || 'Parsing halted.'); + } // discard current lookahead and grab another + + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + symbol = lex(); + } // try to recover from error + + while (1) { + // check for error recovery rule in this state + if (TERROR.toString() in table[state]) { + break; + } + + if (state == 0) { + throw new Error(errStr || 'Parsing halted.'); + } + + popStack(1); + state = stack[stack.length - 1]; + } + + preErrorSymbol = symbol; // save the lookahead token + + symbol = TERROR; // insert generic error symbol as new lookahead + + state = stack[stack.length - 1]; + action = table[state] && table[state][TERROR]; + recovering = 3; // allow 3 real symbols to be shifted before reporting a new error + } // this shouldn't happen, unless resolve defaults are off + + if (action[0] instanceof Array && action.length > 1) { + throw new Error( + 'Parse Error: multiple actions possible at state: ' + + state + + ', token: ' + + symbol + ); + } + + switch (action[0]) { + case 1: + // shift + //this.shiftCount++; + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); // push state + + symbol = null; + + if (!preErrorSymbol) { + // normal execution/no error + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) recovering--; + } else { + // error just occurred, resume old lookahead f/ before error + symbol = preErrorSymbol; + preErrorSymbol = null; + } + + break; + + case 2: + // reduce + //this.reductionCount++; + len = this.productions_[action[1]][1]; // perform semantic action + + yyval.$ = vstack[vstack.length - len]; // default to $$ = $1 + // default location, uses first token for firsts, last for lasts + + yyval._$ = { + first_line: lstack[lstack.length - (len || 1)].first_line, + last_line: lstack[lstack.length - 1].last_line, + first_column: lstack[lstack.length - (len || 1)].first_column, + last_column: lstack[lstack.length - 1].last_column + }; + r = this.performAction.call( + yyval, + yytext, + yyleng, + yylineno, + this.yy, + action[1], + vstack, + lstack + ); + + if (typeof r !== 'undefined') { + return r; + } // pop off stack + + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + + stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) + + vstack.push(yyval.$); + lstack.push(yyval._$); // goto new state = table[STATE][NONTERMINAL] + + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + + case 3: + // accept + return true; + } + } + + return true; + } + }; + /* Jison generated lexer */ + + var lexer = (function () { + var lexer = { + EOF: 1, + parseError: function parseError(str, hash) { + if (this.yy.parseError) { + this.yy.parseError(str, hash); + } else { + throw new Error(str); + } + }, + setInput: function (input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = { + first_line: 1, + first_column: 0, + last_line: 1, + last_column: 0 + }; + return this; + }, + input: function () { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/\n/); + if (lines) this.yylineno++; + this._input = this._input.slice(1); + return ch; + }, + unput: function (ch) { + this._input = ch + this._input; + return this; + }, + more: function () { + this._more = true; + return this; + }, + less: function (n) { + this._input = this.match.slice(n) + this._input; + }, + pastInput: function () { + var past = this.matched.substr( + 0, + this.matched.length - this.match.length + ); + return ( + (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, '') + ); + }, + upcomingInput: function () { + var next = this.match; + + if (next.length < 20) { + next += this._input.substr(0, 20 - next.length); + } + + return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace( + /\n/g, + '' + ); + }, + showPosition: function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join('-'); + return pre + this.upcomingInput() + '\n' + c + '^'; + }, + next: function () { + if (this.done) { + return this.EOF; + } + + if (!this._input) this.done = true; + var token, match, tempMatch, index, col, lines; + + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + + var rules = this._currentRules(); + + for (var i = 0; i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (!this.options.flex) break; + } + } + + if (match) { + lines = match[0].match(/\n.*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = { + first_line: this.yylloc.last_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.last_column, + last_column: lines + ? lines[lines.length - 1].length - 1 + : this.yylloc.last_column + match[0].length + }; + this.yytext += match[0]; + this.match += match[0]; + this.yyleng = this.yytext.length; + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call( + this, + this.yy, + this, + rules[index], + this.conditionStack[this.conditionStack.length - 1] + ); + if (this.done && this._input) this.done = false; + if (token) return token; + else return; + } + + if (this._input === '') { + return this.EOF; + } else { + this.parseError( + 'Lexical error on line ' + + (this.yylineno + 1) + + '. Unrecognized text.\n' + + this.showPosition(), + { + text: '', + token: null, + line: this.yylineno + } + ); + } + }, + lex: function lex() { + var r = this.next(); + + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, + begin: function begin(condition) { + this.conditionStack.push(condition); + }, + popState: function popState() { + return this.conditionStack.pop(); + }, + _currentRules: function _currentRules() { + return this.conditions[ + this.conditionStack[this.conditionStack.length - 1] + ].rules; + }, + topState: function () { + return this.conditionStack[this.conditionStack.length - 2]; + }, + pushState: function begin(condition) { + this.begin(condition); + } + }; + lexer.options = {}; + + lexer.performAction = function anonymous( + yy, + yy_, + $avoiding_name_collisions, + YY_START + ) { + var YYSTATE = YY_START; + + switch ($avoiding_name_collisions) { + case 0: + /* skip whitespace */ + break; + + case 1: + return 6; + break; + + case 2: + yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2); + return 4; + break; + + case 3: + return 17; + break; + + case 4: + return 18; + break; + + case 5: + return 23; + break; + + case 6: + return 24; + break; + + case 7: + return 22; + break; + + case 8: + return 21; + break; + + case 9: + return 10; + break; + + case 10: + return 11; + break; + + case 11: + return 8; + break; + + case 12: + return 14; + break; + + case 13: + return 'INVALID'; + break; + } + }; + + lexer.rules = [ + /^(?:\s+)/, + /^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/, + /^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/, + /^(?:\{)/, + /^(?:\})/, + /^(?:\[)/, + /^(?:\])/, + /^(?:,)/, + /^(?::)/, + /^(?:true\b)/, + /^(?:false\b)/, + /^(?:null\b)/, + /^(?:$)/, + /^(?:.)/ + ]; + lexer.conditions = { + INITIAL: { + rules: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], + inclusive: true + } + }; + return lexer; + })(); + + parser.lexer = lexer; + return parser; +})(); + +exports.parser = jsonlint; + +exports.errors = function (input) { + try { + this.parse(input); + } catch (e) { + return e.stack; + } +}; + +exports.parse = function () { + return jsonlint.parse.apply(jsonlint, arguments); +}; + +exports.main = function commonjsMain(args) { + if (!args[1]) throw new Error('Usage: ' + args[0] + ' FILE'); + + if (typeof process !== 'undefined') { + var source = require('fs').readFileSync( + require('path').join(process.cwd(), args[1]), + 'utf8' + ); + } else { + var cwd = require('file').path(require('file').cwd()); + + var source = cwd.join(args[1]).read({ + charset: 'utf-8' + }); + } + + return exports.parser.parse(source); +}; diff --git a/packages/jest-console/build/BufferedConsole.d.ts b/packages/jest-console/build/BufferedConsole.d.ts new file mode 100644 index 000000000000..61db55c65008 --- /dev/null +++ b/packages/jest-console/build/BufferedConsole.d.ts @@ -0,0 +1,36 @@ +/** + * 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. + */ +/// +import { Console } from 'console'; +import type { ConsoleBuffer, LogMessage, LogType } from './types'; +export default class BufferedConsole extends Console { + private _buffer; + private _counters; + private _timers; + private _groupDepth; + Console: NodeJS.ConsoleConstructor; + constructor(); + static write(buffer: ConsoleBuffer, type: LogType, message: LogMessage, level?: number | null): ConsoleBuffer; + private _log; + assert(value: unknown, message?: string | Error): void; + count(label?: string): void; + countReset(label?: string): void; + debug(firstArg: unknown, ...rest: Array): void; + dir(firstArg: unknown, options?: NodeJS.InspectOptions): void; + dirxml(firstArg: unknown, ...rest: Array): void; + error(firstArg: unknown, ...rest: Array): void; + group(title?: string, ...rest: Array): void; + groupCollapsed(title?: string, ...rest: Array): void; + groupEnd(): void; + info(firstArg: unknown, ...rest: Array): void; + log(firstArg: unknown, ...rest: Array): void; + time(label?: string): void; + timeEnd(label?: string): void; + timeLog(label?: string, ...data: Array): void; + warn(firstArg: unknown, ...rest: Array): void; + getBuffer(): ConsoleBuffer | undefined; +} diff --git a/packages/jest-console/build/BufferedConsole.js b/packages/jest-console/build/BufferedConsole.js new file mode 100644 index 000000000000..defe65941f61 --- /dev/null +++ b/packages/jest-console/build/BufferedConsole.js @@ -0,0 +1,258 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _assert() { + const data = _interopRequireDefault(require('assert')); + + _assert = function () { + return data; + }; + + return data; +} + +function _console() { + const data = require('console'); + + _console = function () { + return data; + }; + + return data; +} + +function _util() { + const data = require('util'); + + _util = function () { + return data; + }; + + return data; +} + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class BufferedConsole extends _console().Console { + constructor() { + super({ + write: message => { + BufferedConsole.write(this._buffer, 'log', message, null); + return true; + } + }); + + _defineProperty(this, '_buffer', []); + + _defineProperty(this, '_counters', {}); + + _defineProperty(this, '_timers', {}); + + _defineProperty(this, '_groupDepth', 0); + + _defineProperty(this, 'Console', _console().Console); + } + + static write(buffer, type, message, level) { + const stackLevel = level != null ? level : 2; + const rawStack = new (_jestUtil().ErrorWithStack)( + undefined, + BufferedConsole.write + ).stack; + invariant(rawStack, 'always have a stack trace'); + const origin = rawStack + .split('\n') + .slice(stackLevel) + .filter(Boolean) + .join('\n'); + buffer.push({ + message, + origin, + type + }); + return buffer; + } + + _log(type, message) { + BufferedConsole.write( + this._buffer, + type, + ' '.repeat(this._groupDepth) + message, + 3 + ); + } + + assert(value, message) { + try { + (0, _assert().default)(value, message); + } catch (error) { + this._log('assert', error.toString()); + } + } + + count(label = 'default') { + if (!this._counters[label]) { + this._counters[label] = 0; + } + + this._log( + 'count', + (0, _util().format)(`${label}: ${++this._counters[label]}`) + ); + } + + countReset(label = 'default') { + this._counters[label] = 0; + } + + debug(firstArg, ...rest) { + this._log('debug', (0, _util().format)(firstArg, ...rest)); + } + + dir(firstArg, options = {}) { + const representation = (0, _util().inspect)(firstArg, options); + + this._log('dir', (0, _util().formatWithOptions)(options, representation)); + } + + dirxml(firstArg, ...rest) { + this._log('dirxml', (0, _util().format)(firstArg, ...rest)); + } + + error(firstArg, ...rest) { + this._log('error', (0, _util().format)(firstArg, ...rest)); + } + + group(title, ...rest) { + this._groupDepth++; + + if (title || rest.length > 0) { + this._log( + 'group', + _chalk().default.bold((0, _util().format)(title, ...rest)) + ); + } + } + + groupCollapsed(title, ...rest) { + this._groupDepth++; + + if (title || rest.length > 0) { + this._log( + 'groupCollapsed', + _chalk().default.bold((0, _util().format)(title, ...rest)) + ); + } + } + + groupEnd() { + if (this._groupDepth > 0) { + this._groupDepth--; + } + } + + info(firstArg, ...rest) { + this._log('info', (0, _util().format)(firstArg, ...rest)); + } + + log(firstArg, ...rest) { + this._log('log', (0, _util().format)(firstArg, ...rest)); + } + + time(label = 'default') { + if (this._timers[label]) { + return; + } + + this._timers[label] = new Date(); + } + + timeEnd(label = 'default') { + const startTime = this._timers[label]; + + if (startTime) { + const endTime = new Date(); + const time = endTime.getTime() - startTime.getTime(); + + this._log( + 'time', + (0, _util().format)(`${label}: ${(0, _jestUtil().formatTime)(time)}`) + ); + + delete this._timers[label]; + } + } + + timeLog(label = 'default', ...data) { + const startTime = this._timers[label]; + + if (startTime) { + const endTime = new Date(); + const time = endTime.getTime() - startTime.getTime(); + + this._log( + 'time', + (0, _util().format)( + `${label}: ${(0, _jestUtil().formatTime)(time)}`, + ...data + ) + ); + } + } + + warn(firstArg, ...rest) { + this._log('warn', (0, _util().format)(firstArg, ...rest)); + } + + getBuffer() { + return this._buffer.length ? this._buffer : undefined; + } +} + +exports.default = BufferedConsole; + +function invariant(condition, message) { + if (!condition) { + throw new Error(message); + } +} diff --git a/packages/jest-console/build/CustomConsole.d.ts b/packages/jest-console/build/CustomConsole.d.ts new file mode 100644 index 000000000000..8cf06993bf38 --- /dev/null +++ b/packages/jest-console/build/CustomConsole.d.ts @@ -0,0 +1,40 @@ +/** + * 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. + */ +/// +import { Console } from 'console'; +import type { LogMessage, LogType } from './types'; +declare type Formatter = (type: LogType, message: LogMessage) => string; +export default class CustomConsole extends Console { + private _stdout; + private _stderr; + private _formatBuffer; + private _counters; + private _timers; + private _groupDepth; + Console: NodeJS.ConsoleConstructor; + constructor(stdout: NodeJS.WriteStream, stderr: NodeJS.WriteStream, formatBuffer?: Formatter); + private _log; + private _logError; + assert(value: unknown, message?: string | Error): asserts value; + count(label?: string): void; + countReset(label?: string): void; + debug(firstArg: unknown, ...args: Array): void; + dir(firstArg: unknown, options?: NodeJS.InspectOptions): void; + dirxml(firstArg: unknown, ...args: Array): void; + error(firstArg: unknown, ...args: Array): void; + group(title?: string, ...args: Array): void; + groupCollapsed(title?: string, ...args: Array): void; + groupEnd(): void; + info(firstArg: unknown, ...args: Array): void; + log(firstArg: unknown, ...args: Array): void; + time(label?: string): void; + timeEnd(label?: string): void; + timeLog(label?: string, ...data: Array): void; + warn(firstArg: unknown, ...args: Array): void; + getBuffer(): undefined; +} +export {}; diff --git a/packages/jest-console/build/CustomConsole.js b/packages/jest-console/build/CustomConsole.js new file mode 100644 index 000000000000..25ab7bedeb8a --- /dev/null +++ b/packages/jest-console/build/CustomConsole.js @@ -0,0 +1,240 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _assert() { + const data = _interopRequireDefault(require('assert')); + + _assert = function () { + return data; + }; + + return data; +} + +function _console() { + const data = require('console'); + + _console = function () { + return data; + }; + + return data; +} + +function _util() { + const data = require('util'); + + _util = function () { + return data; + }; + + return data; +} + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class CustomConsole extends _console().Console { + constructor(stdout, stderr, formatBuffer = (_type, message) => message) { + super(stdout, stderr); + + _defineProperty(this, '_stdout', void 0); + + _defineProperty(this, '_stderr', void 0); + + _defineProperty(this, '_formatBuffer', void 0); + + _defineProperty(this, '_counters', {}); + + _defineProperty(this, '_timers', {}); + + _defineProperty(this, '_groupDepth', 0); + + _defineProperty(this, 'Console', _console().Console); + + this._stdout = stdout; + this._stderr = stderr; + this._formatBuffer = formatBuffer; + } + + _log(type, message) { + (0, _jestUtil().clearLine)(this._stdout); + super.log( + this._formatBuffer(type, ' '.repeat(this._groupDepth) + message) + ); + } + + _logError(type, message) { + (0, _jestUtil().clearLine)(this._stderr); + super.error( + this._formatBuffer(type, ' '.repeat(this._groupDepth) + message) + ); + } + + assert(value, message) { + try { + (0, _assert().default)(value, message); + } catch (error) { + this._logError('assert', error.toString()); + } + } + + count(label = 'default') { + if (!this._counters[label]) { + this._counters[label] = 0; + } + + this._log( + 'count', + (0, _util().format)(`${label}: ${++this._counters[label]}`) + ); + } + + countReset(label = 'default') { + this._counters[label] = 0; + } + + debug(firstArg, ...args) { + this._log('debug', (0, _util().format)(firstArg, ...args)); + } + + dir(firstArg, options = {}) { + const representation = (0, _util().inspect)(firstArg, options); + + this._log('dir', (0, _util().formatWithOptions)(options, representation)); + } + + dirxml(firstArg, ...args) { + this._log('dirxml', (0, _util().format)(firstArg, ...args)); + } + + error(firstArg, ...args) { + this._logError('error', (0, _util().format)(firstArg, ...args)); + } + + group(title, ...args) { + this._groupDepth++; + + if (title || args.length > 0) { + this._log( + 'group', + _chalk().default.bold((0, _util().format)(title, ...args)) + ); + } + } + + groupCollapsed(title, ...args) { + this._groupDepth++; + + if (title || args.length > 0) { + this._log( + 'groupCollapsed', + _chalk().default.bold((0, _util().format)(title, ...args)) + ); + } + } + + groupEnd() { + if (this._groupDepth > 0) { + this._groupDepth--; + } + } + + info(firstArg, ...args) { + this._log('info', (0, _util().format)(firstArg, ...args)); + } + + log(firstArg, ...args) { + this._log('log', (0, _util().format)(firstArg, ...args)); + } + + time(label = 'default') { + if (this._timers[label]) { + return; + } + + this._timers[label] = new Date(); + } + + timeEnd(label = 'default') { + const startTime = this._timers[label]; + + if (startTime) { + const endTime = new Date().getTime(); + const time = endTime - startTime.getTime(); + + this._log( + 'time', + (0, _util().format)(`${label}: ${(0, _jestUtil().formatTime)(time)}`) + ); + + delete this._timers[label]; + } + } + + timeLog(label = 'default', ...data) { + const startTime = this._timers[label]; + + if (startTime) { + const endTime = new Date(); + const time = endTime.getTime() - startTime.getTime(); + + this._log( + 'time', + (0, _util().format)( + `${label}: ${(0, _jestUtil().formatTime)(time)}`, + ...data + ) + ); + } + } + + warn(firstArg, ...args) { + this._logError('warn', (0, _util().format)(firstArg, ...args)); + } + + getBuffer() { + return undefined; + } +} + +exports.default = CustomConsole; diff --git a/packages/jest-console/build/NullConsole.d.ts b/packages/jest-console/build/NullConsole.d.ts new file mode 100644 index 000000000000..d39fc1c226b7 --- /dev/null +++ b/packages/jest-console/build/NullConsole.d.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ +import CustomConsole from './CustomConsole'; +export default class NullConsole extends CustomConsole { + assert(): void; + debug(): void; + dir(): void; + error(): void; + info(): void; + log(): void; + time(): void; + timeEnd(): void; + timeLog(): void; + trace(): void; + warn(): void; + group(): void; + groupCollapsed(): void; + groupEnd(): void; +} diff --git a/packages/jest-console/build/NullConsole.js b/packages/jest-console/build/NullConsole.js new file mode 100644 index 000000000000..745e35fb37bf --- /dev/null +++ b/packages/jest-console/build/NullConsole.js @@ -0,0 +1,50 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _CustomConsole = _interopRequireDefault(require('./CustomConsole')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +class NullConsole extends _CustomConsole.default { + assert() {} + + debug() {} + + dir() {} + + error() {} + + info() {} + + log() {} + + time() {} + + timeEnd() {} + + timeLog() {} + + trace() {} + + warn() {} + + group() {} + + groupCollapsed() {} + + groupEnd() {} +} + +exports.default = NullConsole; diff --git a/packages/jest-console/build/getConsoleOutput.d.ts b/packages/jest-console/build/getConsoleOutput.d.ts new file mode 100644 index 000000000000..0752502963f1 --- /dev/null +++ b/packages/jest-console/build/getConsoleOutput.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import { StackTraceConfig } from 'jest-message-util'; +import type { ConsoleBuffer } from './types'; +declare const _default: (buffer: ConsoleBuffer, config: StackTraceConfig, globalConfig: Config.GlobalConfig) => string; +export default _default; diff --git a/packages/jest-console/build/getConsoleOutput.js b/packages/jest-console/build/getConsoleOutput.js new file mode 100644 index 000000000000..503fd190ba52 --- /dev/null +++ b/packages/jest-console/build/getConsoleOutput.js @@ -0,0 +1,103 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestMessageUtil() { + const data = require('jest-message-util'); + + _jestMessageUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +var _default = (buffer, config, globalConfig) => { + const TITLE_INDENT = globalConfig.verbose ? ' ' : ' '; + const CONSOLE_INDENT = TITLE_INDENT + ' '; + const logEntries = buffer.reduce((output, {type, message, origin}) => { + message = message + .split(/\n/) + .map(line => CONSOLE_INDENT + line) + .join('\n'); + let typeMessage = 'console.' + type; + let noStackTrace = true; + let noCodeFrame = true; + + if (type === 'warn') { + var _globalConfig$noStack; + + message = _chalk().default.yellow(message); + typeMessage = _chalk().default.yellow(typeMessage); + noStackTrace = + (_globalConfig$noStack = + globalConfig === null || globalConfig === void 0 + ? void 0 + : globalConfig.noStackTrace) !== null && + _globalConfig$noStack !== void 0 + ? _globalConfig$noStack + : false; + noCodeFrame = false; + } else if (type === 'error') { + var _globalConfig$noStack2; + + message = _chalk().default.red(message); + typeMessage = _chalk().default.red(typeMessage); + noStackTrace = + (_globalConfig$noStack2 = + globalConfig === null || globalConfig === void 0 + ? void 0 + : globalConfig.noStackTrace) !== null && + _globalConfig$noStack2 !== void 0 + ? _globalConfig$noStack2 + : false; + noCodeFrame = false; + } + + const options = { + noCodeFrame, + noStackTrace + }; + const formattedStackTrace = (0, _jestMessageUtil().formatStackTrace)( + origin, + config, + options + ); + return ( + output + + TITLE_INDENT + + _chalk().default.dim(typeMessage) + + '\n' + + message.trimRight() + + '\n' + + _chalk().default.dim(formattedStackTrace.trimRight()) + + '\n\n' + ); + }, ''); + return logEntries.trimRight() + '\n'; +}; + +exports.default = _default; diff --git a/packages/jest-console/build/index.d.ts b/packages/jest-console/build/index.d.ts new file mode 100644 index 000000000000..e5e568c05a4e --- /dev/null +++ b/packages/jest-console/build/index.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +export { default as BufferedConsole } from './BufferedConsole'; +export { default as CustomConsole } from './CustomConsole'; +export { default as NullConsole } from './NullConsole'; +export { default as getConsoleOutput } from './getConsoleOutput'; +export type { ConsoleBuffer, LogMessage, LogType, LogEntry } from './types'; diff --git a/packages/jest-console/build/index.js b/packages/jest-console/build/index.js new file mode 100644 index 000000000000..88f6cb4ddc2c --- /dev/null +++ b/packages/jest-console/build/index.js @@ -0,0 +1,41 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'BufferedConsole', { + enumerable: true, + get: function () { + return _BufferedConsole.default; + } +}); +Object.defineProperty(exports, 'CustomConsole', { + enumerable: true, + get: function () { + return _CustomConsole.default; + } +}); +Object.defineProperty(exports, 'NullConsole', { + enumerable: true, + get: function () { + return _NullConsole.default; + } +}); +Object.defineProperty(exports, 'getConsoleOutput', { + enumerable: true, + get: function () { + return _getConsoleOutput.default; + } +}); + +var _BufferedConsole = _interopRequireDefault(require('./BufferedConsole')); + +var _CustomConsole = _interopRequireDefault(require('./CustomConsole')); + +var _NullConsole = _interopRequireDefault(require('./NullConsole')); + +var _getConsoleOutput = _interopRequireDefault(require('./getConsoleOutput')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} diff --git a/packages/jest-console/build/types.d.ts b/packages/jest-console/build/types.d.ts new file mode 100644 index 000000000000..8490c6764658 --- /dev/null +++ b/packages/jest-console/build/types.d.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ +export declare type LogMessage = string; +export declare type LogEntry = { + message: LogMessage; + origin: string; + type: LogType; +}; +export declare type LogCounters = { + [label: string]: number; +}; +export declare type LogTimers = { + [label: string]: Date; +}; +export declare type LogType = 'assert' | 'count' | 'debug' | 'dir' | 'dirxml' | 'error' | 'group' | 'groupCollapsed' | 'info' | 'log' | 'time' | 'warn'; +export declare type ConsoleBuffer = Array; diff --git a/packages/jest-console/build/types.js b/packages/jest-console/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-console/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-core/build/FailedTestsCache.d.ts b/packages/jest-core/build/FailedTestsCache.d.ts new file mode 100644 index 000000000000..b85a912991fe --- /dev/null +++ b/packages/jest-core/build/FailedTestsCache.d.ts @@ -0,0 +1,13 @@ +/** + * 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. + */ +import type { TestResult } from '@jest/test-result'; +import type { Test } from 'jest-runner'; +export default class FailedTestsCache { + private _enabledTestsMap?; + filterTests(tests: Array): Array; + setTestResults(testResults: Array): void; +} diff --git a/packages/jest-core/build/FailedTestsCache.js b/packages/jest-core/build/FailedTestsCache.js new file mode 100644 index 000000000000..389e8b8efb18 --- /dev/null +++ b/packages/jest-core/build/FailedTestsCache.js @@ -0,0 +1,59 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * 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. + */ +class FailedTestsCache { + constructor() { + _defineProperty(this, '_enabledTestsMap', void 0); + } + + filterTests(tests) { + const enabledTestsMap = this._enabledTestsMap; + + if (!enabledTestsMap) { + return tests; + } + + return tests.filter(testResult => enabledTestsMap[testResult.path]); + } + + setTestResults(testResults) { + this._enabledTestsMap = (testResults || []) + .filter(testResult => testResult.numFailingTests) + .reduce((suiteMap, testResult) => { + suiteMap[testResult.testFilePath] = testResult.testResults + .filter(test => test.status === 'failed') + .reduce((testMap, test) => { + testMap[test.fullName] = true; + return testMap; + }, {}); + return suiteMap; + }, {}); + this._enabledTestsMap = Object.freeze(this._enabledTestsMap); + } +} + +exports.default = FailedTestsCache; diff --git a/packages/jest-core/build/ReporterDispatcher.d.ts b/packages/jest-core/build/ReporterDispatcher.d.ts new file mode 100644 index 000000000000..f483bfffbb07 --- /dev/null +++ b/packages/jest-core/build/ReporterDispatcher.d.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ +import type { Reporter, ReporterOnStartOptions } from '@jest/reporters'; +import type { AggregatedResult, TestCaseResult, TestResult } from '@jest/test-result'; +import type { Test } from 'jest-runner'; +import type { Context } from 'jest-runtime'; +export default class ReporterDispatcher { + private _reporters; + constructor(); + register(reporter: Reporter): void; + unregister(ReporterClass: Function): void; + onTestFileResult(test: Test, testResult: TestResult, results: AggregatedResult): Promise; + onTestFileStart(test: Test): Promise; + onRunStart(results: AggregatedResult, options: ReporterOnStartOptions): Promise; + onTestCaseResult(test: Test, testCaseResult: TestCaseResult): Promise; + onRunComplete(contexts: Set, results: AggregatedResult): Promise; + getErrors(): Array; + hasErrors(): boolean; +} diff --git a/packages/jest-core/build/ReporterDispatcher.js b/packages/jest-core/build/ReporterDispatcher.js new file mode 100644 index 000000000000..efd6af0e4340 --- /dev/null +++ b/packages/jest-core/build/ReporterDispatcher.js @@ -0,0 +1,104 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * 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. + */ + +/* eslint-disable local/ban-types-eventually */ +class ReporterDispatcher { + constructor() { + _defineProperty(this, '_reporters', void 0); + + this._reporters = []; + } + + register(reporter) { + this._reporters.push(reporter); + } + + unregister(ReporterClass) { + this._reporters = this._reporters.filter( + reporter => !(reporter instanceof ReporterClass) + ); + } + + async onTestFileResult(test, testResult, results) { + for (const reporter of this._reporters) { + if (reporter.onTestFileResult) { + await reporter.onTestFileResult(test, testResult, results); + } else if (reporter.onTestResult) { + await reporter.onTestResult(test, testResult, results); + } + } // Release memory if unused later. + + testResult.coverage = undefined; + testResult.console = undefined; + } + + async onTestFileStart(test) { + for (const reporter of this._reporters) { + if (reporter.onTestFileStart) { + await reporter.onTestFileStart(test); + } else if (reporter.onTestStart) { + await reporter.onTestStart(test); + } + } + } + + async onRunStart(results, options) { + for (const reporter of this._reporters) { + reporter.onRunStart && (await reporter.onRunStart(results, options)); + } + } + + async onTestCaseResult(test, testCaseResult) { + for (const reporter of this._reporters) { + if (reporter.onTestCaseResult) { + await reporter.onTestCaseResult(test, testCaseResult); + } + } + } + + async onRunComplete(contexts, results) { + for (const reporter of this._reporters) { + if (reporter.onRunComplete) { + await reporter.onRunComplete(contexts, results); + } + } + } // Return a list of last errors for every reporter + + getErrors() { + return this._reporters.reduce((list, reporter) => { + const error = reporter.getLastError && reporter.getLastError(); + return error ? list.concat(error) : list; + }, []); + } + + hasErrors() { + return this.getErrors().length !== 0; + } +} + +exports.default = ReporterDispatcher; diff --git a/packages/jest-core/build/SearchSource.d.ts b/packages/jest-core/build/SearchSource.d.ts new file mode 100644 index 000000000000..c6fc41604ae7 --- /dev/null +++ b/packages/jest-core/build/SearchSource.d.ts @@ -0,0 +1,45 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { ChangedFiles } from 'jest-changed-files'; +import type { Test } from 'jest-runner'; +import type { Context } from 'jest-runtime'; +import type { Filter, Stats } from './types'; +export declare type SearchResult = { + noSCM?: boolean; + stats?: Stats; + collectCoverageFrom?: Set; + tests: Array; + total?: number; +}; +export declare type TestSelectionConfig = { + input?: string; + findRelatedTests?: boolean; + onlyChanged?: boolean; + paths?: Array; + shouldTreatInputAsPattern?: boolean; + testPathPattern?: string; + watch?: boolean; +}; +export default class SearchSource { + private _context; + private _dependencyResolver; + private _testPathCases; + constructor(context: Context); + private _getOrBuildDependencyResolver; + private _filterTestPathsWithStats; + private _getAllTestPaths; + isTestFilePath(path: Config.Path): boolean; + findMatchingTests(testPathPattern?: string): SearchResult; + findRelatedTests(allPaths: Set, collectCoverage: boolean): SearchResult; + findTestsByPaths(paths: Array): SearchResult; + findRelatedTestsFromPattern(paths: Array, collectCoverage: boolean): SearchResult; + findTestRelatedToChangedFiles(changedFilesInfo: ChangedFiles, collectCoverage: boolean): SearchResult; + private _getTestPaths; + getTestPaths(globalConfig: Config.GlobalConfig, changedFiles: ChangedFiles | undefined, filter?: Filter): Promise; + findRelatedSourcesFromTestsInChangedFiles(changedFilesInfo: ChangedFiles): Array; +} diff --git a/packages/jest-core/build/SearchSource.js b/packages/jest-core/build/SearchSource.js new file mode 100644 index 000000000000..74b87571227a --- /dev/null +++ b/packages/jest-core/build/SearchSource.js @@ -0,0 +1,480 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function os() { + const data = _interopRequireWildcard(require('os')); + + os = function () { + return data; + }; + + return data; +} + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _micromatch() { + const data = _interopRequireDefault(require('micromatch')); + + _micromatch = function () { + return data; + }; + + return data; +} + +function _jestConfig() { + const data = require('jest-config'); + + _jestConfig = function () { + return data; + }; + + return data; +} + +function _jestRegexUtil() { + const data = require('jest-regex-util'); + + _jestRegexUtil = function () { + return data; + }; + + return data; +} + +function _jestResolveDependencies() { + const data = require('jest-resolve-dependencies'); + + _jestResolveDependencies = function () { + return data; + }; + + return data; +} + +function _jestSnapshot() { + const data = require('jest-snapshot'); + + _jestSnapshot = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const regexToMatcher = testRegex => { + const regexes = testRegex.map(testRegex => new RegExp(testRegex)); + return path => + regexes.some(regex => { + const result = regex.test(path); // prevent stateful regexes from breaking, just in case + + regex.lastIndex = 0; + return result; + }); +}; + +const toTests = (context, tests) => + tests.map(path => ({ + context, + duration: undefined, + path + })); + +const hasSCM = changedFilesInfo => { + const {repos} = changedFilesInfo; // no SCM (git/hg/...) is found in any of the roots. + + const noSCM = Object.values(repos).every(scm => scm.size === 0); + return !noSCM; +}; + +class SearchSource { + constructor(context) { + _defineProperty(this, '_context', void 0); + + _defineProperty(this, '_dependencyResolver', void 0); + + _defineProperty(this, '_testPathCases', []); + + const {config} = context; + this._context = context; + this._dependencyResolver = null; + const rootPattern = new RegExp( + config.roots + .map(dir => (0, _jestRegexUtil().escapePathForRegex)(dir + path().sep)) + .join('|') + ); + + this._testPathCases.push({ + isMatch: path => rootPattern.test(path), + stat: 'roots' + }); + + if (config.testMatch.length) { + this._testPathCases.push({ + isMatch: (0, _jestUtil().globsToMatcher)(config.testMatch), + stat: 'testMatch' + }); + } + + if (config.testPathIgnorePatterns.length) { + const testIgnorePatternsRegex = new RegExp( + config.testPathIgnorePatterns.join('|') + ); + + this._testPathCases.push({ + isMatch: path => !testIgnorePatternsRegex.test(path), + stat: 'testPathIgnorePatterns' + }); + } + + if (config.testRegex.length) { + this._testPathCases.push({ + isMatch: regexToMatcher(config.testRegex), + stat: 'testRegex' + }); + } + } + + _getOrBuildDependencyResolver() { + if (!this._dependencyResolver) { + this._dependencyResolver = new (_jestResolveDependencies().DependencyResolver)( + this._context.resolver, + this._context.hasteFS, + (0, _jestSnapshot().buildSnapshotResolver)(this._context.config) + ); + } + + return this._dependencyResolver; + } + + _filterTestPathsWithStats(allPaths, testPathPattern) { + const data = { + stats: { + roots: 0, + testMatch: 0, + testPathIgnorePatterns: 0, + testRegex: 0 + }, + tests: [], + total: allPaths.length + }; + const testCases = Array.from(this._testPathCases); // clone + + if (testPathPattern) { + const regex = (0, _jestUtil().testPathPatternToRegExp)(testPathPattern); + testCases.push({ + isMatch: path => regex.test(path), + stat: 'testPathPattern' + }); + data.stats.testPathPattern = 0; + } + + data.tests = allPaths.filter(test => { + let filterResult = true; + + for (const {isMatch, stat} of testCases) { + if (isMatch(test.path)) { + data.stats[stat]++; + } else { + filterResult = false; + } + } + + return filterResult; + }); + return data; + } + + _getAllTestPaths(testPathPattern) { + return this._filterTestPathsWithStats( + toTests(this._context, this._context.hasteFS.getAllFiles()), + testPathPattern + ); + } + + isTestFilePath(path) { + return this._testPathCases.every(testCase => testCase.isMatch(path)); + } + + findMatchingTests(testPathPattern) { + return this._getAllTestPaths(testPathPattern); + } + + findRelatedTests(allPaths, collectCoverage) { + const dependencyResolver = this._getOrBuildDependencyResolver(); + + if (!collectCoverage) { + return { + tests: toTests( + this._context, + dependencyResolver.resolveInverse( + allPaths, + this.isTestFilePath.bind(this), + { + skipNodeResolution: this._context.config.skipNodeResolution + } + ) + ) + }; + } + + const testModulesMap = dependencyResolver.resolveInverseModuleMap( + allPaths, + this.isTestFilePath.bind(this), + { + skipNodeResolution: this._context.config.skipNodeResolution + } + ); + const allPathsAbsolute = Array.from(allPaths).map(p => path().resolve(p)); + const collectCoverageFrom = new Set(); + testModulesMap.forEach(testModule => { + if (!testModule.dependencies) { + return; + } + + testModule.dependencies.forEach(p => { + if (!allPathsAbsolute.includes(p)) { + return; + } + + const filename = (0, _jestConfig().replaceRootDirInPath)( + this._context.config.rootDir, + p + ); + collectCoverageFrom.add( + path().isAbsolute(filename) + ? path().relative(this._context.config.rootDir, filename) + : filename + ); + }); + }); + return { + collectCoverageFrom, + tests: toTests( + this._context, + testModulesMap.map(testModule => testModule.file) + ) + }; + } + + findTestsByPaths(paths) { + return { + tests: toTests( + this._context, + paths + .map(p => path().resolve(this._context.config.cwd, p)) + .filter(this.isTestFilePath.bind(this)) + ) + }; + } + + findRelatedTestsFromPattern(paths, collectCoverage) { + if (Array.isArray(paths) && paths.length) { + const resolvedPaths = paths.map(p => + path().resolve(this._context.config.cwd, p) + ); + return this.findRelatedTests(new Set(resolvedPaths), collectCoverage); + } + + return { + tests: [] + }; + } + + findTestRelatedToChangedFiles(changedFilesInfo, collectCoverage) { + if (!hasSCM(changedFilesInfo)) { + return { + noSCM: true, + tests: [] + }; + } + + const {changedFiles} = changedFilesInfo; + return this.findRelatedTests(changedFiles, collectCoverage); + } + + _getTestPaths(globalConfig, changedFiles) { + if (globalConfig.onlyChanged) { + if (!changedFiles) { + throw new Error('Changed files must be set when running with -o.'); + } + + return this.findTestRelatedToChangedFiles( + changedFiles, + globalConfig.collectCoverage + ); + } + + let paths = globalConfig.nonFlagArgs; + + if (globalConfig.findRelatedTests && 'win32' === os().platform()) { + const allFiles = this._context.hasteFS.getAllFiles(); + + const options = { + nocase: true, + windows: false + }; + paths = paths + .map(p => { + const relativePath = path() + .resolve(this._context.config.cwd, p) + .replace(/\\/g, '\\\\'); + const match = (0, _micromatch().default)( + allFiles, + relativePath, + options + ); + return match[0]; + }) + .filter(Boolean); + } + + if (globalConfig.runTestsByPath && paths && paths.length) { + return this.findTestsByPaths(paths); + } else if (globalConfig.findRelatedTests && paths && paths.length) { + return this.findRelatedTestsFromPattern( + paths, + globalConfig.collectCoverage + ); + } else if (globalConfig.testPathPattern != null) { + return this.findMatchingTests(globalConfig.testPathPattern); + } else { + return { + tests: [] + }; + } + } + + async getTestPaths(globalConfig, changedFiles, filter) { + const searchResult = this._getTestPaths(globalConfig, changedFiles); + + const filterPath = globalConfig.filter; + + if (filter) { + const tests = searchResult.tests; + const filterResult = await filter(tests.map(test => test.path)); + + if (!Array.isArray(filterResult.filtered)) { + throw new Error( + `Filter ${filterPath} did not return a valid test list` + ); + } + + const filteredSet = new Set( + filterResult.filtered.map(result => result.test) + ); + return { + ...searchResult, + tests: tests.filter(test => filteredSet.has(test.path)) + }; + } + + return searchResult; + } + + findRelatedSourcesFromTestsInChangedFiles(changedFilesInfo) { + if (!hasSCM(changedFilesInfo)) { + return []; + } + + const {changedFiles} = changedFilesInfo; + + const dependencyResolver = this._getOrBuildDependencyResolver(); + + const relatedSourcesSet = new Set(); + changedFiles.forEach(filePath => { + if (this.isTestFilePath(filePath)) { + const sourcePaths = dependencyResolver.resolve(filePath, { + skipNodeResolution: this._context.config.skipNodeResolution + }); + sourcePaths.forEach(sourcePath => relatedSourcesSet.add(sourcePath)); + } + }); + return Array.from(relatedSourcesSet); + } +} + +exports.default = SearchSource; diff --git a/packages/jest-core/build/SnapshotInteractiveMode.d.ts b/packages/jest-core/build/SnapshotInteractiveMode.d.ts new file mode 100644 index 000000000000..1198a50456d5 --- /dev/null +++ b/packages/jest-core/build/SnapshotInteractiveMode.d.ts @@ -0,0 +1,30 @@ +/** + * 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. + */ +/// +import type { AggregatedResult, AssertionLocation } from '@jest/test-result'; +export default class SnapshotInteractiveMode { + private _pipe; + private _isActive; + private _updateTestRunnerConfig; + private _testAssertions; + private _countPaths; + private _skippedNum; + constructor(pipe: NodeJS.WritableStream); + isActive(): boolean; + getSkippedNum(): number; + private _clearTestSummary; + private _drawUIProgress; + private _drawUIDoneWithSkipped; + private _drawUIDone; + private _drawUIOverlay; + put(key: string): void; + abort(): void; + restart(): void; + updateWithResults(results: AggregatedResult): void; + private _run; + run(failedSnapshotTestAssertions: Array, onConfigChange: (assertion: AssertionLocation | null, shouldUpdateSnapshot: boolean) => unknown): void; +} diff --git a/packages/jest-core/build/SnapshotInteractiveMode.js b/packages/jest-core/build/SnapshotInteractiveMode.js new file mode 100644 index 000000000000..fac34db6ac0b --- /dev/null +++ b/packages/jest-core/build/SnapshotInteractiveMode.js @@ -0,0 +1,327 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _ansiEscapes() { + const data = _interopRequireDefault(require('ansi-escapes')); + + _ansiEscapes = function () { + return data; + }; + + return data; +} + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _jestWatcher() { + const data = require('jest-watcher'); + + _jestWatcher = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const {ARROW, CLEAR} = _jestUtil().specialChars; + +class SnapshotInteractiveMode { + constructor(pipe) { + _defineProperty(this, '_pipe', void 0); + + _defineProperty(this, '_isActive', void 0); + + _defineProperty(this, '_updateTestRunnerConfig', void 0); + + _defineProperty(this, '_testAssertions', void 0); + + _defineProperty(this, '_countPaths', void 0); + + _defineProperty(this, '_skippedNum', void 0); + + this._pipe = pipe; + this._isActive = false; + this._skippedNum = 0; + } + + isActive() { + return this._isActive; + } + + getSkippedNum() { + return this._skippedNum; + } + + _clearTestSummary() { + this._pipe.write(_ansiEscapes().default.cursorUp(6)); + + this._pipe.write(_ansiEscapes().default.eraseDown); + } + + _drawUIProgress() { + this._clearTestSummary(); + + const numPass = this._countPaths - this._testAssertions.length; + const numRemaining = this._countPaths - numPass - this._skippedNum; + + let stats = _chalk().default.bold.dim( + (0, _jestUtil().pluralize)('snapshot', numRemaining) + ' remaining' + ); + + if (numPass) { + stats += + ', ' + + _chalk().default.bold.green( + (0, _jestUtil().pluralize)('snapshot', numPass) + ' updated' + ); + } + + if (this._skippedNum) { + stats += + ', ' + + _chalk().default.bold.yellow( + (0, _jestUtil().pluralize)('snapshot', this._skippedNum) + ' skipped' + ); + } + + const messages = [ + '\n' + _chalk().default.bold('Interactive Snapshot Progress'), + ARROW + stats, + '\n' + _chalk().default.bold('Watch Usage'), + _chalk().default.dim(ARROW + 'Press ') + + 'u' + + _chalk().default.dim(' to update failing snapshots for this test.'), + _chalk().default.dim(ARROW + 'Press ') + + 's' + + _chalk().default.dim(' to skip the current test.'), + _chalk().default.dim(ARROW + 'Press ') + + 'q' + + _chalk().default.dim(' to quit Interactive Snapshot Mode.'), + _chalk().default.dim(ARROW + 'Press ') + + 'Enter' + + _chalk().default.dim(' to trigger a test run.') + ]; + + this._pipe.write(messages.filter(Boolean).join('\n') + '\n'); + } + + _drawUIDoneWithSkipped() { + this._pipe.write(CLEAR); + + const numPass = this._countPaths - this._testAssertions.length; + + let stats = _chalk().default.bold.dim( + (0, _jestUtil().pluralize)('snapshot', this._countPaths) + ' reviewed' + ); + + if (numPass) { + stats += + ', ' + + _chalk().default.bold.green( + (0, _jestUtil().pluralize)('snapshot', numPass) + ' updated' + ); + } + + if (this._skippedNum) { + stats += + ', ' + + _chalk().default.bold.yellow( + (0, _jestUtil().pluralize)('snapshot', this._skippedNum) + ' skipped' + ); + } + + const messages = [ + '\n' + _chalk().default.bold('Interactive Snapshot Result'), + ARROW + stats, + '\n' + _chalk().default.bold('Watch Usage'), + _chalk().default.dim(ARROW + 'Press ') + + 'r' + + _chalk().default.dim(' to restart Interactive Snapshot Mode.'), + _chalk().default.dim(ARROW + 'Press ') + + 'q' + + _chalk().default.dim(' to quit Interactive Snapshot Mode.') + ]; + + this._pipe.write(messages.filter(Boolean).join('\n') + '\n'); + } + + _drawUIDone() { + this._pipe.write(CLEAR); + + const numPass = this._countPaths - this._testAssertions.length; + + let stats = _chalk().default.bold.dim( + (0, _jestUtil().pluralize)('snapshot', this._countPaths) + ' reviewed' + ); + + if (numPass) { + stats += + ', ' + + _chalk().default.bold.green( + (0, _jestUtil().pluralize)('snapshot', numPass) + ' updated' + ); + } + + const messages = [ + '\n' + _chalk().default.bold('Interactive Snapshot Result'), + ARROW + stats, + '\n' + _chalk().default.bold('Watch Usage'), + _chalk().default.dim(ARROW + 'Press ') + + 'Enter' + + _chalk().default.dim(' to return to watch mode.') + ]; + + this._pipe.write(messages.filter(Boolean).join('\n') + '\n'); + } + + _drawUIOverlay() { + if (this._testAssertions.length === 0) { + return this._drawUIDone(); + } + + if (this._testAssertions.length - this._skippedNum === 0) { + return this._drawUIDoneWithSkipped(); + } + + return this._drawUIProgress(); + } + + put(key) { + switch (key) { + case 's': + if (this._skippedNum === this._testAssertions.length) break; + this._skippedNum += 1; // move skipped test to the end + + this._testAssertions.push(this._testAssertions.shift()); + + if (this._testAssertions.length - this._skippedNum > 0) { + this._run(false); + } else { + this._drawUIDoneWithSkipped(); + } + + break; + + case 'u': + this._run(true); + + break; + + case 'q': + case _jestWatcher().KEYS.ESCAPE: + this.abort(); + break; + + case 'r': + this.restart(); + break; + + case _jestWatcher().KEYS.ENTER: + if (this._testAssertions.length === 0) { + this.abort(); + } else { + this._run(false); + } + + break; + + default: + break; + } + } + + abort() { + this._isActive = false; + this._skippedNum = 0; + + this._updateTestRunnerConfig(null, false); + } + + restart() { + this._skippedNum = 0; + this._countPaths = this._testAssertions.length; + + this._run(false); + } + + updateWithResults(results) { + const hasSnapshotFailure = !!results.snapshot.failure; + + if (hasSnapshotFailure) { + this._drawUIOverlay(); + + return; + } + + this._testAssertions.shift(); + + if (this._testAssertions.length - this._skippedNum === 0) { + this._drawUIOverlay(); + + return; + } // Go to the next test + + this._run(false); + } + + _run(shouldUpdateSnapshot) { + const testAssertion = this._testAssertions[0]; + + this._updateTestRunnerConfig(testAssertion, shouldUpdateSnapshot); + } + + run(failedSnapshotTestAssertions, onConfigChange) { + if (!failedSnapshotTestAssertions.length) { + return; + } + + this._testAssertions = [...failedSnapshotTestAssertions]; + this._countPaths = this._testAssertions.length; + this._updateTestRunnerConfig = onConfigChange; + this._isActive = true; + + this._run(false); + } +} + +exports.default = SnapshotInteractiveMode; diff --git a/packages/jest-core/build/TestNamePatternPrompt.d.ts b/packages/jest-core/build/TestNamePatternPrompt.d.ts new file mode 100644 index 000000000000..c3a187cdab8d --- /dev/null +++ b/packages/jest-core/build/TestNamePatternPrompt.d.ts @@ -0,0 +1,17 @@ +/** + * 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. + */ +/// +import type { TestResult } from '@jest/test-result'; +import { PatternPrompt, Prompt, ScrollOptions } from 'jest-watcher'; +export default class TestNamePatternPrompt extends PatternPrompt { + _cachedTestResults: Array; + constructor(pipe: NodeJS.WritableStream, prompt: Prompt); + _onChange(pattern: string, options: ScrollOptions): void; + _printPrompt(pattern: string): void; + _getMatchedTests(pattern: string): Array; + updateCachedTestResults(testResults?: Array): void; +} diff --git a/packages/jest-core/build/TestNamePatternPrompt.js b/packages/jest-core/build/TestNamePatternPrompt.js new file mode 100644 index 000000000000..4d9bb2bf28ca --- /dev/null +++ b/packages/jest-core/build/TestNamePatternPrompt.js @@ -0,0 +1,86 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestWatcher() { + const data = require('jest-watcher'); + + _jestWatcher = function () { + return data; + }; + + return data; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +// TODO: Make underscored props `private` +class TestNamePatternPrompt extends _jestWatcher().PatternPrompt { + constructor(pipe, prompt) { + super(pipe, prompt); + + _defineProperty(this, '_cachedTestResults', void 0); + + this._entityName = 'tests'; + this._cachedTestResults = []; + } + + _onChange(pattern, options) { + super._onChange(pattern, options); + + this._printPrompt(pattern); + } + + _printPrompt(pattern) { + const pipe = this._pipe; + (0, _jestWatcher().printPatternCaret)(pattern, pipe); + (0, _jestWatcher().printRestoredPatternCaret)( + pattern, + this._currentUsageRows, + pipe + ); + } + + _getMatchedTests(pattern) { + let regex; + + try { + regex = new RegExp(pattern, 'i'); + } catch { + return []; + } + + const matchedTests = []; + + this._cachedTestResults.forEach(({testResults}) => + testResults.forEach(({title}) => { + if (regex.test(title)) { + matchedTests.push(title); + } + }) + ); + + return matchedTests; + } + + updateCachedTestResults(testResults = []) { + this._cachedTestResults = testResults; + } +} + +exports.default = TestNamePatternPrompt; diff --git a/packages/jest-core/build/TestPathPatternPrompt.d.ts b/packages/jest-core/build/TestPathPatternPrompt.d.ts new file mode 100644 index 000000000000..420aa25712c9 --- /dev/null +++ b/packages/jest-core/build/TestPathPatternPrompt.d.ts @@ -0,0 +1,24 @@ +/** + * 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. + */ +/// +import type { Test } from 'jest-runner'; +import type { Context } from 'jest-runtime'; +import { PatternPrompt, Prompt, ScrollOptions } from 'jest-watcher'; +import type SearchSource from './SearchSource'; +declare type SearchSources = Array<{ + context: Context; + searchSource: SearchSource; +}>; +export default class TestPathPatternPrompt extends PatternPrompt { + _searchSources?: SearchSources; + constructor(pipe: NodeJS.WritableStream, prompt: Prompt); + _onChange(pattern: string, options: ScrollOptions): void; + _printPrompt(pattern: string): void; + _getMatchedTests(pattern: string): Array; + updateSearchSources(searchSources: SearchSources): void; +} +export {}; diff --git a/packages/jest-core/build/TestPathPatternPrompt.js b/packages/jest-core/build/TestPathPatternPrompt.js new file mode 100644 index 000000000000..fe0a2bf7c9da --- /dev/null +++ b/packages/jest-core/build/TestPathPatternPrompt.js @@ -0,0 +1,81 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestWatcher() { + const data = require('jest-watcher'); + + _jestWatcher = function () { + return data; + }; + + return data; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +// TODO: Make underscored props `private` +class TestPathPatternPrompt extends _jestWatcher().PatternPrompt { + constructor(pipe, prompt) { + super(pipe, prompt); + + _defineProperty(this, '_searchSources', void 0); + + this._entityName = 'filenames'; + } + + _onChange(pattern, options) { + super._onChange(pattern, options); + + this._printPrompt(pattern); + } + + _printPrompt(pattern) { + const pipe = this._pipe; + (0, _jestWatcher().printPatternCaret)(pattern, pipe); + (0, _jestWatcher().printRestoredPatternCaret)( + pattern, + this._currentUsageRows, + pipe + ); + } + + _getMatchedTests(pattern) { + let regex; + + try { + regex = new RegExp(pattern, 'i'); + } catch {} + + let tests = []; + + if (regex && this._searchSources) { + this._searchSources.forEach(({searchSource}) => { + tests = tests.concat(searchSource.findMatchingTests(pattern).tests); + }); + } + + return tests; + } + + updateSearchSources(searchSources) { + this._searchSources = searchSources; + } +} + +exports.default = TestPathPatternPrompt; diff --git a/packages/jest-core/build/TestScheduler.d.ts b/packages/jest-core/build/TestScheduler.d.ts new file mode 100644 index 000000000000..7bc3a783bf77 --- /dev/null +++ b/packages/jest-core/build/TestScheduler.d.ts @@ -0,0 +1,41 @@ +/** + * 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. + */ +import { Reporter } from '@jest/reporters'; +import { AggregatedResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import { Test } from 'jest-runner'; +import type TestWatcher from './TestWatcher'; +export declare type TestSchedulerOptions = { + startRun: (globalConfig: Config.GlobalConfig) => void; +}; +export declare type TestSchedulerContext = { + firstRun: boolean; + previousSuccess: boolean; + changedFiles?: Set; + sourcesRelatedToTestsInChangedFiles?: Set; +}; +export default class TestScheduler { + private readonly _dispatcher; + private readonly _globalConfig; + private readonly _options; + private readonly _context; + constructor(globalConfig: Config.GlobalConfig, options: TestSchedulerOptions, context: TestSchedulerContext); + addReporter(reporter: Reporter): void; + removeReporter(ReporterClass: Function): void; + scheduleTests(tests: Array, watcher: TestWatcher): Promise; + private _partitionTests; + private _shouldAddDefaultReporters; + private _setupReporters; + private _setupDefaultReporters; + private _addCustomReporters; + /** + * Get properties of a reporter in an object + * to make dealing with them less painful. + */ + private _getReporterProps; + private _bailIfNeeded; +} diff --git a/packages/jest-core/build/TestScheduler.js b/packages/jest-core/build/TestScheduler.js new file mode 100644 index 000000000000..40cb176be61b --- /dev/null +++ b/packages/jest-core/build/TestScheduler.js @@ -0,0 +1,571 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _exit() { + const data = _interopRequireDefault(require('exit')); + + _exit = function () { + return data; + }; + + return data; +} + +function _reporters() { + const data = require('@jest/reporters'); + + _reporters = function () { + return data; + }; + + return data; +} + +function _testResult() { + const data = require('@jest/test-result'); + + _testResult = function () { + return data; + }; + + return data; +} + +function _transform() { + const data = require('@jest/transform'); + + _transform = function () { + return data; + }; + + return data; +} + +function _jestMessageUtil() { + const data = require('jest-message-util'); + + _jestMessageUtil = function () { + return data; + }; + + return data; +} + +function _jestRunner() { + const data = _interopRequireDefault(require('jest-runner')); + + _jestRunner = function () { + return data; + }; + + return data; +} + +function _jestSnapshot() { + const data = _interopRequireDefault(require('jest-snapshot')); + + _jestSnapshot = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _ReporterDispatcher = _interopRequireDefault( + require('./ReporterDispatcher') +); + +var _testSchedulerHelper = require('./testSchedulerHelper'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +// The default jest-runner is required because it is the default test runner +// and required implicitly through the `runner` ProjectConfig option. +_jestRunner().default; + +class TestScheduler { + constructor(globalConfig, options, context) { + _defineProperty(this, '_dispatcher', void 0); + + _defineProperty(this, '_globalConfig', void 0); + + _defineProperty(this, '_options', void 0); + + _defineProperty(this, '_context', void 0); + + this._dispatcher = new _ReporterDispatcher.default(); + this._globalConfig = globalConfig; + this._options = options; + this._context = context; + + this._setupReporters(); + } + + addReporter(reporter) { + this._dispatcher.register(reporter); + } + + removeReporter(ReporterClass) { + this._dispatcher.unregister(ReporterClass); + } + + async scheduleTests(tests, watcher) { + const onTestFileStart = this._dispatcher.onTestFileStart.bind( + this._dispatcher + ); + + const timings = []; + const contexts = new Set(); + tests.forEach(test => { + contexts.add(test.context); + + if (test.duration) { + timings.push(test.duration); + } + }); + const aggregatedResults = createAggregatedResults(tests.length); + const estimatedTime = Math.ceil( + getEstimatedTime(timings, this._globalConfig.maxWorkers) / 1000 + ); + const runInBand = (0, _testSchedulerHelper.shouldRunInBand)( + tests, + timings, + this._globalConfig + ); + + const onResult = async (test, testResult) => { + if (watcher.isInterrupted()) { + return Promise.resolve(); + } + + if (testResult.testResults.length === 0) { + const message = 'Your test suite must contain at least one test.'; + return onFailure(test, { + message, + stack: new Error(message).stack + }); + } // Throws when the context is leaked after executing a test. + + if (testResult.leaks) { + const message = + _chalk().default.red.bold('EXPERIMENTAL FEATURE!\n') + + 'Your test suite is leaking memory. Please ensure all references are cleaned.\n' + + '\n' + + 'There is a number of things that can leak memory:\n' + + ' - Async operations that have not finished (e.g. fs.readFile).\n' + + ' - Timers not properly mocked (e.g. setInterval, setTimeout).\n' + + ' - Keeping references to the global scope.'; + return onFailure(test, { + message, + stack: new Error(message).stack + }); + } + + (0, _testResult().addResult)(aggregatedResults, testResult); + await this._dispatcher.onTestFileResult( + test, + testResult, + aggregatedResults + ); + return this._bailIfNeeded(contexts, aggregatedResults, watcher); + }; + + const onFailure = async (test, error) => { + if (watcher.isInterrupted()) { + return; + } + + const testResult = (0, _testResult().buildFailureTestResult)( + test.path, + error + ); + testResult.failureMessage = (0, _jestMessageUtil().formatExecError)( + testResult.testExecError, + test.context.config, + this._globalConfig, + test.path + ); + (0, _testResult().addResult)(aggregatedResults, testResult); + await this._dispatcher.onTestFileResult( + test, + testResult, + aggregatedResults + ); + }; + + const updateSnapshotState = () => { + contexts.forEach(context => { + const status = _jestSnapshot().default.cleanup( + context.hasteFS, + this._globalConfig.updateSnapshot, + _jestSnapshot().default.buildSnapshotResolver(context.config), + context.config.testPathIgnorePatterns + ); + + aggregatedResults.snapshot.filesRemoved += status.filesRemoved; + aggregatedResults.snapshot.filesRemovedList = ( + aggregatedResults.snapshot.filesRemovedList || [] + ).concat(status.filesRemovedList); + }); + const updateAll = this._globalConfig.updateSnapshot === 'all'; + aggregatedResults.snapshot.didUpdate = updateAll; + aggregatedResults.snapshot.failure = !!( + !updateAll && + (aggregatedResults.snapshot.unchecked || + aggregatedResults.snapshot.unmatched || + aggregatedResults.snapshot.filesRemoved) + ); + }; + + await this._dispatcher.onRunStart(aggregatedResults, { + estimatedTime, + showStatus: !runInBand + }); + const testRunners = Object.create(null); + const contextsByTestRunner = new WeakMap(); + contexts.forEach(context => { + const {config} = context; + + if (!testRunners[config.runner]) { + var _this$_context, _this$_context2; + + const transformer = new (_transform().ScriptTransformer)(config); + const Runner = (0, _jestUtil().interopRequireDefault)( + transformer.requireAndTranspileModule(config.runner) + ).default; + const runner = new Runner(this._globalConfig, { + changedFiles: + (_this$_context = this._context) === null || + _this$_context === void 0 + ? void 0 + : _this$_context.changedFiles, + sourcesRelatedToTestsInChangedFiles: + (_this$_context2 = this._context) === null || + _this$_context2 === void 0 + ? void 0 + : _this$_context2.sourcesRelatedToTestsInChangedFiles + }); + testRunners[config.runner] = runner; + contextsByTestRunner.set(runner, context); + } + }); + + const testsByRunner = this._partitionTests(testRunners, tests); + + if (testsByRunner) { + try { + for (const runner of Object.keys(testRunners)) { + const testRunner = testRunners[runner]; + const context = contextsByTestRunner.get(testRunner); + invariant(context); + const tests = testsByRunner[runner]; + const testRunnerOptions = { + serial: runInBand || Boolean(testRunner.isSerial) + }; + /** + * Test runners with event emitters are still not supported + * for third party test runners. + */ + + if (testRunner.__PRIVATE_UNSTABLE_API_supportsEventEmitters__) { + const unsubscribes = [ + testRunner.on('test-file-start', ([test]) => + onTestFileStart(test) + ), + testRunner.on('test-file-success', ([test, testResult]) => + onResult(test, testResult) + ), + testRunner.on('test-file-failure', ([test, error]) => + onFailure(test, error) + ), + testRunner.on( + 'test-case-result', + ([testPath, testCaseResult]) => { + const test = { + context, + path: testPath + }; + + this._dispatcher.onTestCaseResult(test, testCaseResult); + } + ) + ]; + await testRunner.runTests( + tests, + watcher, + undefined, + undefined, + undefined, + testRunnerOptions + ); + unsubscribes.forEach(sub => sub()); + } else { + await testRunner.runTests( + tests, + watcher, + onTestFileStart, + onResult, + onFailure, + testRunnerOptions + ); + } + } + } catch (error) { + if (!watcher.isInterrupted()) { + throw error; + } + } + } + + updateSnapshotState(); + aggregatedResults.wasInterrupted = watcher.isInterrupted(); + await this._dispatcher.onRunComplete(contexts, aggregatedResults); + const anyTestFailures = !( + aggregatedResults.numFailedTests === 0 && + aggregatedResults.numRuntimeErrorTestSuites === 0 + ); + + const anyReporterErrors = this._dispatcher.hasErrors(); + + aggregatedResults.success = !( + anyTestFailures || + aggregatedResults.snapshot.failure || + anyReporterErrors + ); + return aggregatedResults; + } + + _partitionTests(testRunners, tests) { + if (Object.keys(testRunners).length > 1) { + return tests.reduce((testRuns, test) => { + const runner = test.context.config.runner; + + if (!testRuns[runner]) { + testRuns[runner] = []; + } + + testRuns[runner].push(test); + return testRuns; + }, Object.create(null)); + } else if (tests.length > 0 && tests[0] != null) { + // If there is only one runner, don't partition the tests. + return Object.assign(Object.create(null), { + [tests[0].context.config.runner]: tests + }); + } else { + return null; + } + } + + _shouldAddDefaultReporters(reporters) { + return ( + !reporters || + !!reporters.find( + reporter => this._getReporterProps(reporter).path === 'default' + ) + ); + } + + _setupReporters() { + const {collectCoverage, notify, reporters} = this._globalConfig; + + const isDefault = this._shouldAddDefaultReporters(reporters); + + if (isDefault) { + this._setupDefaultReporters(collectCoverage); + } + + if (!isDefault && collectCoverage) { + var _this$_context3, _this$_context4; + + this.addReporter( + new (_reporters().CoverageReporter)(this._globalConfig, { + changedFiles: + (_this$_context3 = this._context) === null || + _this$_context3 === void 0 + ? void 0 + : _this$_context3.changedFiles, + sourcesRelatedToTestsInChangedFiles: + (_this$_context4 = this._context) === null || + _this$_context4 === void 0 + ? void 0 + : _this$_context4.sourcesRelatedToTestsInChangedFiles + }) + ); + } + + if (notify) { + this.addReporter( + new (_reporters().NotifyReporter)( + this._globalConfig, + this._options.startRun, + this._context + ) + ); + } + + if (reporters && Array.isArray(reporters)) { + this._addCustomReporters(reporters); + } + } + + _setupDefaultReporters(collectCoverage) { + this.addReporter( + this._globalConfig.verbose + ? new (_reporters().VerboseReporter)(this._globalConfig) + : new (_reporters().DefaultReporter)(this._globalConfig) + ); + + if (collectCoverage) { + var _this$_context5, _this$_context6; + + this.addReporter( + new (_reporters().CoverageReporter)(this._globalConfig, { + changedFiles: + (_this$_context5 = this._context) === null || + _this$_context5 === void 0 + ? void 0 + : _this$_context5.changedFiles, + sourcesRelatedToTestsInChangedFiles: + (_this$_context6 = this._context) === null || + _this$_context6 === void 0 + ? void 0 + : _this$_context6.sourcesRelatedToTestsInChangedFiles + }) + ); + } + + this.addReporter(new (_reporters().SummaryReporter)(this._globalConfig)); + } + + _addCustomReporters(reporters) { + reporters.forEach(reporter => { + const {options, path} = this._getReporterProps(reporter); + + if (path === 'default') return; + + try { + // TODO: Use `requireAndTranspileModule` for Jest 26 + const Reporter = (0, _jestUtil().interopRequireDefault)(require(path)) + .default; + this.addReporter(new Reporter(this._globalConfig, options)); + } catch (error) { + error.message = + 'An error occurred while adding the reporter at path "' + + _chalk().default.bold(path) + + '".' + + error.message; + throw error; + } + }); + } + /** + * Get properties of a reporter in an object + * to make dealing with them less painful. + */ + + _getReporterProps(reporter) { + if (typeof reporter === 'string') { + return { + options: this._options, + path: reporter + }; + } else if (Array.isArray(reporter)) { + const [path, options] = reporter; + return { + options, + path + }; + } + + throw new Error('Reporter should be either a string or an array'); + } + + async _bailIfNeeded(contexts, aggregatedResults, watcher) { + if ( + this._globalConfig.bail !== 0 && + aggregatedResults.numFailedTests >= this._globalConfig.bail + ) { + if (watcher.isWatchMode()) { + await watcher.setState({ + interrupted: true + }); + return; + } + + try { + await this._dispatcher.onRunComplete(contexts, aggregatedResults); + } finally { + const exitCode = this._globalConfig.testFailureExitCode; + (0, _exit().default)(exitCode); + } + } + } +} + +exports.default = TestScheduler; + +function invariant(condition, message) { + if (!condition) { + throw new Error(message); + } +} + +const createAggregatedResults = numTotalTestSuites => { + const result = (0, _testResult().makeEmptyAggregatedTestResult)(); + result.numTotalTestSuites = numTotalTestSuites; + result.startTime = Date.now(); + result.success = false; + return result; +}; + +const getEstimatedTime = (timings, workers) => { + if (timings.length === 0) { + return 0; + } + + const max = Math.max(...timings); + return timings.length <= workers + ? max + : Math.max(timings.reduce((sum, time) => sum + time) / workers, max); +}; diff --git a/packages/jest-core/build/TestWatcher.d.ts b/packages/jest-core/build/TestWatcher.d.ts new file mode 100644 index 000000000000..baceb5e15362 --- /dev/null +++ b/packages/jest-core/build/TestWatcher.d.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ +import emittery = require('emittery'); +declare type State = { + interrupted: boolean; +}; +export default class TestWatcher extends emittery<{ + change: State; +}> { + state: State; + private _isWatchMode; + constructor({ isWatchMode }: { + isWatchMode: boolean; + }); + setState(state: State): Promise; + isInterrupted(): boolean; + isWatchMode(): boolean; +} +export {}; diff --git a/packages/jest-core/build/TestWatcher.js b/packages/jest-core/build/TestWatcher.js new file mode 100644 index 000000000000..491279b83747 --- /dev/null +++ b/packages/jest-core/build/TestWatcher.js @@ -0,0 +1,64 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _emittery() { + const data = _interopRequireDefault(require('emittery')); + + _emittery = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class TestWatcher extends _emittery().default { + constructor({isWatchMode}) { + super(); + + _defineProperty(this, 'state', void 0); + + _defineProperty(this, '_isWatchMode', void 0); + + this.state = { + interrupted: false + }; + this._isWatchMode = isWatchMode; + } + + async setState(state) { + Object.assign(this.state, state); + await this.emit('change', this.state); + } + + isInterrupted() { + return this.state.interrupted; + } + + isWatchMode() { + return this._isWatchMode; + } +} + +exports.default = TestWatcher; diff --git a/packages/jest-core/build/assets/jest_logo.png b/packages/jest-core/build/assets/jest_logo.png new file mode 100644 index 000000000000..079356bc1611 Binary files /dev/null and b/packages/jest-core/build/assets/jest_logo.png differ diff --git a/packages/jest-core/build/cli/index.d.ts b/packages/jest-core/build/cli/index.d.ts new file mode 100644 index 000000000000..bfa970e45d9d --- /dev/null +++ b/packages/jest-core/build/cli/index.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ +import type { AggregatedResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +export declare function runCLI(argv: Config.Argv, projects: Array): Promise<{ + results: AggregatedResult; + globalConfig: Config.GlobalConfig; +}>; diff --git a/packages/jest-core/build/cli/index.js b/packages/jest-core/build/cli/index.js new file mode 100644 index 000000000000..8b9d651eecdb --- /dev/null +++ b/packages/jest-core/build/cli/index.js @@ -0,0 +1,485 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.runCLI = runCLI; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _exit() { + const data = _interopRequireDefault(require('exit')); + + _exit = function () { + return data; + }; + + return data; +} + +function _rimraf() { + const data = _interopRequireDefault(require('rimraf')); + + _rimraf = function () { + return data; + }; + + return data; +} + +function _console() { + const data = require('@jest/console'); + + _console = function () { + return data; + }; + + return data; +} + +function _jestConfig() { + const data = require('jest-config'); + + _jestConfig = function () { + return data; + }; + + return data; +} + +function _jestRuntime() { + const data = _interopRequireDefault(require('jest-runtime')); + + _jestRuntime = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _TestWatcher() { + const data = _interopRequireDefault(require('../TestWatcher')); + + _TestWatcher = function () { + return data; + }; + + return data; +} + +function _collectHandles() { + const data = require('../collectHandles'); + + _collectHandles = function () { + return data; + }; + + return data; +} + +function _getChangedFilesPromise() { + const data = _interopRequireDefault(require('../getChangedFilesPromise')); + + _getChangedFilesPromise = function () { + return data; + }; + + return data; +} + +function _getConfigsOfProjectsToRun() { + const data = _interopRequireDefault(require('../getConfigsOfProjectsToRun')); + + _getConfigsOfProjectsToRun = function () { + return data; + }; + + return data; +} + +function _getProjectNamesMissingWarning() { + const data = _interopRequireDefault( + require('../getProjectNamesMissingWarning') + ); + + _getProjectNamesMissingWarning = function () { + return data; + }; + + return data; +} + +function _getSelectProjectsMessage() { + const data = _interopRequireDefault(require('../getSelectProjectsMessage')); + + _getSelectProjectsMessage = function () { + return data; + }; + + return data; +} + +function _createContext() { + const data = _interopRequireDefault(require('../lib/createContext')); + + _createContext = function () { + return data; + }; + + return data; +} + +function _handleDeprecationWarnings() { + const data = _interopRequireDefault( + require('../lib/handleDeprecationWarnings') + ); + + _handleDeprecationWarnings = function () { + return data; + }; + + return data; +} + +function _logDebugMessages() { + const data = _interopRequireDefault(require('../lib/logDebugMessages')); + + _logDebugMessages = function () { + return data; + }; + + return data; +} + +function _pluralize() { + const data = _interopRequireDefault(require('../pluralize')); + + _pluralize = function () { + return data; + }; + + return data; +} + +function _runJest() { + const data = _interopRequireDefault(require('../runJest')); + + _runJest = function () { + return data; + }; + + return data; +} + +function _watch() { + const data = _interopRequireDefault(require('../watch')); + + _watch = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 {print: preRunMessagePrint} = _jestUtil().preRunMessage; + +async function runCLI(argv, projects) { + let results; // If we output a JSON object, we can't write anything to stdout, since + // it'll break the JSON structure and it won't be valid. + + const outputStream = + argv.json || argv.useStderr ? process.stderr : process.stdout; + const {globalConfig, configs, hasDeprecationWarnings} = await (0, + _jestConfig().readConfigs)(argv, projects); + + if (argv.debug) { + (0, _logDebugMessages().default)(globalConfig, configs, outputStream); + } + + if (argv.showConfig) { + (0, _logDebugMessages().default)(globalConfig, configs, process.stdout); + (0, _exit().default)(0); + } + + if (argv.clearCache) { + configs.forEach(config => { + _rimraf().default.sync(config.cacheDirectory); + + process.stdout.write(`Cleared ${config.cacheDirectory}\n`); + }); + (0, _exit().default)(0); + } + + let configsOfProjectsToRun = configs; + + if (argv.selectProjects) { + const namesMissingWarning = (0, _getProjectNamesMissingWarning().default)( + configs + ); + + if (namesMissingWarning) { + outputStream.write(namesMissingWarning); + } + + configsOfProjectsToRun = (0, _getConfigsOfProjectsToRun().default)( + argv.selectProjects, + configs + ); + outputStream.write( + (0, _getSelectProjectsMessage().default)(configsOfProjectsToRun) + ); + } + + await _run10000( + globalConfig, + configsOfProjectsToRun, + hasDeprecationWarnings, + outputStream, + r => (results = r) + ); + + if (argv.watch || argv.watchAll) { + // If in watch mode, return the promise that will never resolve. + // If the watch mode is interrupted, watch should handle the process + // shutdown. + return new Promise(() => {}); + } + + if (!results) { + throw new Error( + 'AggregatedResult must be present after test run is complete' + ); + } + + const {openHandles} = results; + + if (openHandles && openHandles.length) { + const formatted = (0, _collectHandles().formatHandleErrors)( + openHandles, + configs[0] + ); + const openHandlesString = (0, _pluralize().default)( + 'open handle', + formatted.length, + 's' + ); + const message = + _chalk().default.red( + `\nJest has detected the following ${openHandlesString} potentially keeping Jest from exiting:\n\n` + ) + formatted.join('\n\n'); + console.error(message); + } + + return { + globalConfig, + results + }; +} + +const buildContextsAndHasteMaps = async ( + configs, + globalConfig, + outputStream +) => { + const hasteMapInstances = Array(configs.length); + const contexts = await Promise.all( + configs.map(async (config, index) => { + (0, _jestUtil().createDirectory)(config.cacheDirectory); + + const hasteMapInstance = _jestRuntime().default.createHasteMap(config, { + console: new (_console().CustomConsole)(outputStream, outputStream), + maxWorkers: Math.max( + 1, + Math.floor(globalConfig.maxWorkers / configs.length) + ), + resetCache: !config.cache, + watch: globalConfig.watch || globalConfig.watchAll, + watchman: globalConfig.watchman + }); + + hasteMapInstances[index] = hasteMapInstance; + return (0, _createContext().default)( + config, + await hasteMapInstance.build() + ); + }) + ); + return { + contexts, + hasteMapInstances + }; +}; + +const _run10000 = async ( + globalConfig, + configs, + hasDeprecationWarnings, + outputStream, + onComplete +) => { + // Queries to hg/git can take a while, so we need to start the process + // as soon as possible, so by the time we need the result it's already there. + const changedFilesPromise = (0, _getChangedFilesPromise().default)( + globalConfig, + configs + ); // Filter may need to do an HTTP call or something similar to setup. + // We will wait on an async response from this before using the filter. + + let filter; + + if (globalConfig.filter && !globalConfig.skipFilter) { + const rawFilter = require(globalConfig.filter); + + let filterSetupPromise; + + if (rawFilter.setup) { + // Wrap filter setup Promise to avoid "uncaught Promise" error. + // If an error is returned, we surface it in the return value. + filterSetupPromise = (async () => { + try { + await rawFilter.setup(); + } catch (err) { + return err; + } + + return undefined; + })(); + } + + filter = async testPaths => { + if (filterSetupPromise) { + // Expect an undefined return value unless there was an error. + const err = await filterSetupPromise; + + if (err) { + throw err; + } + } + + return rawFilter(testPaths); + }; + } + + const {contexts, hasteMapInstances} = await buildContextsAndHasteMaps( + configs, + globalConfig, + outputStream + ); + globalConfig.watch || globalConfig.watchAll + ? await runWatch( + contexts, + configs, + hasDeprecationWarnings, + globalConfig, + outputStream, + hasteMapInstances, + filter + ) + : await runWithoutWatch( + globalConfig, + contexts, + outputStream, + onComplete, + changedFilesPromise, + filter + ); +}; + +const runWatch = async ( + contexts, + _configs, + hasDeprecationWarnings, + globalConfig, + outputStream, + hasteMapInstances, + filter +) => { + if (hasDeprecationWarnings) { + try { + await (0, _handleDeprecationWarnings().default)( + outputStream, + process.stdin + ); + return (0, _watch().default)( + globalConfig, + contexts, + outputStream, + hasteMapInstances, + undefined, + undefined, + filter + ); + } catch { + (0, _exit().default)(0); + } + } + + return (0, _watch().default)( + globalConfig, + contexts, + outputStream, + hasteMapInstances, + undefined, + undefined, + filter + ); +}; + +const runWithoutWatch = async ( + globalConfig, + contexts, + outputStream, + onComplete, + changedFilesPromise, + filter +) => { + const startRun = async () => { + if (!globalConfig.listTests) { + preRunMessagePrint(outputStream); + } + + return (0, _runJest().default)({ + changedFilesPromise, + contexts, + failedTestsCache: undefined, + filter, + globalConfig, + onComplete, + outputStream, + startRun, + testWatcher: new (_TestWatcher().default)({ + isWatchMode: false + }) + }); + }; + + return startRun(); +}; diff --git a/packages/jest-core/build/collectHandles.d.ts b/packages/jest-core/build/collectHandles.d.ts new file mode 100644 index 000000000000..a331a0a829b5 --- /dev/null +++ b/packages/jest-core/build/collectHandles.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function collectHandles(): () => Array; +export declare function formatHandleErrors(errors: Array, config: Config.ProjectConfig): Array; diff --git a/packages/jest-core/build/collectHandles.js b/packages/jest-core/build/collectHandles.js new file mode 100644 index 000000000000..be5611734cf2 --- /dev/null +++ b/packages/jest-core/build/collectHandles.js @@ -0,0 +1,214 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = collectHandles; +exports.formatHandleErrors = formatHandleErrors; + +function asyncHooks() { + const data = _interopRequireWildcard(require('async_hooks')); + + asyncHooks = function () { + return data; + }; + + return data; +} + +function _stripAnsi() { + const data = _interopRequireDefault(require('strip-ansi')); + + _stripAnsi = function () { + return data; + }; + + return data; +} + +function _jestMessageUtil() { + const data = require('jest-message-util'); + + _jestMessageUtil = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ + +/* eslint-disable local/ban-types-eventually */ +function stackIsFromUser(stack) { + // Either the test file, or something required by it + if (stack.includes('Runtime.requireModule')) { + return true; + } // jest-jasmine it or describe call + + if (stack.includes('asyncJestTest') || stack.includes('asyncJestLifecycle')) { + return true; + } // An async function call from within circus + + if (stack.includes('callAsyncCircusFn')) { + // jest-circus it or describe call + return ( + stack.includes('_callCircusTest') || stack.includes('_callCircusHook') + ); + } + + return false; +} + +const alwaysActive = () => true; // Inspired by https://github.com/mafintosh/why-is-node-running/blob/master/index.js +// Extracted as we want to format the result ourselves + +function collectHandles() { + const activeHandles = new Map(); + const hook = asyncHooks().createHook({ + destroy(asyncId) { + activeHandles.delete(asyncId); + }, + + init: function initHook(asyncId, type, _triggerAsyncId, resource) { + if ( + type === 'PROMISE' || + type === 'TIMERWRAP' || + type === 'ELDHISTOGRAM' + ) { + return; + } + + const error = new (_jestUtil().ErrorWithStack)(type, initHook); + + if (stackIsFromUser(error.stack || '')) { + let isActive; + + if (type === 'Timeout' || type === 'Immediate') { + if ('hasRef' in resource) { + // Timer that supports hasRef (Node v11+) + // @ts-expect-error: doesn't exist in v10 typings + isActive = resource.hasRef.bind(resource); + } else { + // Timer that doesn't support hasRef + isActive = alwaysActive; + } + } else { + // Any other async resource + isActive = alwaysActive; + } + + activeHandles.set(asyncId, { + error, + isActive + }); + } + } + }); + hook.enable(); + return () => { + hook.disable(); // Get errors for every async resource still referenced at this moment + + const result = Array.from(activeHandles.values()) + .filter(({isActive}) => isActive()) + .map(({error}) => error); + activeHandles.clear(); + return result; + }; +} + +function formatHandleErrors(errors, config) { + const stacks = new Set(); + return ( + errors + .map(err => + (0, _jestMessageUtil().formatExecError)( + err, + config, + { + noStackTrace: false + }, + undefined, + true + ) + ) // E.g. timeouts might give multiple traces to the same line of code + // This hairy filtering tries to remove entries with duplicate stack traces + .filter(handle => { + const ansiFree = (0, _stripAnsi().default)(handle); + const match = ansiFree.match(/\s+at(.*)/); + + if (!match || match.length < 2) { + return true; + } + + const stack = ansiFree.substr(ansiFree.indexOf(match[1])).trim(); + + if (stacks.has(stack)) { + return false; + } + + stacks.add(stack); + return true; + }) + ); +} diff --git a/packages/jest-core/build/getChangedFilesPromise.d.ts b/packages/jest-core/build/getChangedFilesPromise.d.ts new file mode 100644 index 000000000000..8e734d3da5e3 --- /dev/null +++ b/packages/jest-core/build/getChangedFilesPromise.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import { ChangedFilesPromise } from 'jest-changed-files'; +declare const _default: (globalConfig: Config.GlobalConfig, configs: Array) => ChangedFilesPromise | undefined; +export default _default; diff --git a/packages/jest-core/build/getChangedFilesPromise.js b/packages/jest-core/build/getChangedFilesPromise.js new file mode 100644 index 000000000000..8e5d408db1cc --- /dev/null +++ b/packages/jest-core/build/getChangedFilesPromise.js @@ -0,0 +1,79 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestChangedFiles() { + const data = require('jest-changed-files'); + + _jestChangedFiles = function () { + return data; + }; + + return data; +} + +function _jestMessageUtil() { + const data = require('jest-message-util'); + + _jestMessageUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +var _default = (globalConfig, configs) => { + if (globalConfig.onlyChanged) { + const allRootsForAllProjects = configs.reduce((roots, config) => { + if (config.roots) { + roots.push(...config.roots); + } + + return roots; + }, []); + return (0, _jestChangedFiles().getChangedFilesForRoots)( + allRootsForAllProjects, + { + changedSince: globalConfig.changedSince, + lastCommit: globalConfig.lastCommit, + withAncestor: globalConfig.changedFilesWithAncestor + } + ).catch(e => { + const message = (0, _jestMessageUtil().formatExecError)(e, configs[0], { + noStackTrace: true + }) + .split('\n') + .filter(line => !line.includes('Command failed:')) + .join('\n'); + console.error(_chalk().default.red(`\n\n${message}`)); + process.exit(1); + }); + } + + return undefined; +}; + +exports.default = _default; diff --git a/packages/jest-core/build/getConfigsOfProjectsToRun.d.ts b/packages/jest-core/build/getConfigsOfProjectsToRun.d.ts new file mode 100644 index 000000000000..0a780d44fa4b --- /dev/null +++ b/packages/jest-core/build/getConfigsOfProjectsToRun.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function getConfigsOfProjectsToRun(namesOfProjectsToRun: Array, projectConfigs: Array): Array; diff --git a/packages/jest-core/build/getConfigsOfProjectsToRun.js b/packages/jest-core/build/getConfigsOfProjectsToRun.js new file mode 100644 index 000000000000..9326e303261a --- /dev/null +++ b/packages/jest-core/build/getConfigsOfProjectsToRun.js @@ -0,0 +1,28 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getConfigsOfProjectsToRun; + +var _getProjectDisplayName = _interopRequireDefault( + require('./getProjectDisplayName') +); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +function getConfigsOfProjectsToRun(namesOfProjectsToRun, projectConfigs) { + const setOfProjectsToRun = new Set(namesOfProjectsToRun); + return projectConfigs.filter(config => { + const name = (0, _getProjectDisplayName.default)(config); + return name && setOfProjectsToRun.has(name); + }); +} diff --git a/packages/jest-core/build/getNoTestFound.d.ts b/packages/jest-core/build/getNoTestFound.d.ts new file mode 100644 index 000000000000..4e30581d807b --- /dev/null +++ b/packages/jest-core/build/getNoTestFound.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { TestRunData } from './types'; +export default function getNoTestFound(testRunData: TestRunData, globalConfig: Config.GlobalConfig): string; diff --git a/packages/jest-core/build/getNoTestFound.js b/packages/jest-core/build/getNoTestFound.js new file mode 100644 index 000000000000..bd33d584737e --- /dev/null +++ b/packages/jest-core/build/getNoTestFound.js @@ -0,0 +1,63 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getNoTestFound; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +var _pluralize = _interopRequireDefault(require('./pluralize')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +function getNoTestFound(testRunData, globalConfig) { + const testFiles = testRunData.reduce( + (current, testRun) => current + (testRun.matches.total || 0), + 0 + ); + let dataMessage; + + if (globalConfig.runTestsByPath) { + dataMessage = `Files: ${globalConfig.nonFlagArgs + .map(p => `"${p}"`) + .join(', ')}`; + } else { + dataMessage = `Pattern: ${_chalk().default.yellow( + globalConfig.testPathPattern + )} - 0 matches`; + } + + return ( + _chalk().default.bold('No tests found, exiting with code 1') + + '\n' + + 'Run with `--passWithNoTests` to exit with code 0' + + '\n' + + `In ${_chalk().default.bold(globalConfig.rootDir)}` + + '\n' + + ` ${(0, _pluralize.default)('file', testFiles, 's')} checked across ${(0, + _pluralize.default)( + 'project', + testRunData.length, + 's' + )}. Run with \`--verbose\` for more details.` + + '\n' + + dataMessage + ); +} diff --git a/packages/jest-core/build/getNoTestFoundFailed.d.ts b/packages/jest-core/build/getNoTestFoundFailed.d.ts new file mode 100644 index 000000000000..16c5fab42d8d --- /dev/null +++ b/packages/jest-core/build/getNoTestFoundFailed.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function getNoTestFoundFailed(globalConfig: Config.GlobalConfig): string; diff --git a/packages/jest-core/build/getNoTestFoundFailed.js b/packages/jest-core/build/getNoTestFoundFailed.js new file mode 100644 index 000000000000..a7d404c36390 --- /dev/null +++ b/packages/jest-core/build/getNoTestFoundFailed.js @@ -0,0 +1,51 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getNoTestFoundFailed; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +function getNoTestFoundFailed(globalConfig) { + let msg = _chalk().default.bold('No failed test found.'); + + if (_jestUtil().isInteractive) { + msg += _chalk().default.dim( + '\n' + + (globalConfig.watch + ? 'Press `f` to quit "only failed tests" mode.' + : 'Run Jest without `--onlyFailures` or with `--all` to run all tests.') + ); + } + + return msg; +} diff --git a/packages/jest-core/build/getNoTestFoundPassWithNoTests.d.ts b/packages/jest-core/build/getNoTestFoundPassWithNoTests.d.ts new file mode 100644 index 000000000000..a594ba41cd2b --- /dev/null +++ b/packages/jest-core/build/getNoTestFoundPassWithNoTests.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function getNoTestFoundPassWithNoTests(): string; diff --git a/packages/jest-core/build/getNoTestFoundPassWithNoTests.js b/packages/jest-core/build/getNoTestFoundPassWithNoTests.js new file mode 100644 index 000000000000..eb279c3b9bb2 --- /dev/null +++ b/packages/jest-core/build/getNoTestFoundPassWithNoTests.js @@ -0,0 +1,30 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getNoTestFoundPassWithNoTests; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +function getNoTestFoundPassWithNoTests() { + return _chalk().default.bold('No tests found, exiting with code 0'); +} diff --git a/packages/jest-core/build/getNoTestFoundRelatedToChangedFiles.d.ts b/packages/jest-core/build/getNoTestFoundRelatedToChangedFiles.d.ts new file mode 100644 index 000000000000..53cdefcca473 --- /dev/null +++ b/packages/jest-core/build/getNoTestFoundRelatedToChangedFiles.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function getNoTestFoundRelatedToChangedFiles(globalConfig: Config.GlobalConfig): string; diff --git a/packages/jest-core/build/getNoTestFoundRelatedToChangedFiles.js b/packages/jest-core/build/getNoTestFoundRelatedToChangedFiles.js new file mode 100644 index 000000000000..9571d189a7c0 --- /dev/null +++ b/packages/jest-core/build/getNoTestFoundRelatedToChangedFiles.js @@ -0,0 +1,57 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getNoTestFoundRelatedToChangedFiles; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +function getNoTestFoundRelatedToChangedFiles(globalConfig) { + const ref = globalConfig.changedSince + ? `"${globalConfig.changedSince}"` + : 'last commit'; + + let msg = _chalk().default.bold( + `No tests found related to files changed since ${ref}.` + ); + + if (_jestUtil().isInteractive) { + msg += _chalk().default.dim( + '\n' + + (globalConfig.watch + ? 'Press `a` to run all tests, or run Jest with `--watchAll`.' + : 'Run Jest without `-o` or with `--all` to run all tests.') + ); + } + + return msg; +} diff --git a/packages/jest-core/build/getNoTestFoundVerbose.d.ts b/packages/jest-core/build/getNoTestFoundVerbose.d.ts new file mode 100644 index 000000000000..a84572a8b5fb --- /dev/null +++ b/packages/jest-core/build/getNoTestFoundVerbose.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { TestRunData } from './types'; +export default function getNoTestFoundVerbose(testRunData: TestRunData, globalConfig: Config.GlobalConfig): string; diff --git a/packages/jest-core/build/getNoTestFoundVerbose.js b/packages/jest-core/build/getNoTestFoundVerbose.js new file mode 100644 index 000000000000..fb53f55d257c --- /dev/null +++ b/packages/jest-core/build/getNoTestFoundVerbose.js @@ -0,0 +1,95 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getNoTestFoundVerbose; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +var _pluralize = _interopRequireDefault(require('./pluralize')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +function getNoTestFoundVerbose(testRunData, globalConfig) { + const individualResults = testRunData.map(testRun => { + const stats = testRun.matches.stats || {}; + const config = testRun.context.config; + const statsMessage = Object.keys(stats) + .map(key => { + if (key === 'roots' && config.roots.length === 1) { + return null; + } + + const value = config[key]; + + if (value) { + const valueAsString = Array.isArray(value) + ? value.join(', ') + : String(value); + const matches = (0, _pluralize.default)( + 'match', + stats[key] || 0, + 'es' + ); + return ` ${key}: ${_chalk().default.yellow( + valueAsString + )} - ${matches}`; + } + + return null; + }) + .filter(line => line) + .join('\n'); + return testRun.matches.total + ? `In ${_chalk().default.bold(config.rootDir)}\n` + + ` ${(0, _pluralize.default)( + 'file', + testRun.matches.total || 0, + 's' + )} checked.\n` + + statsMessage + : `No files found in ${config.rootDir}.\n` + + `Make sure Jest's configuration does not exclude this directory.` + + `\nTo set up Jest, make sure a package.json file exists.\n` + + `Jest Documentation: ` + + `facebook.github.io/jest/docs/configuration.html`; + }); + let dataMessage; + + if (globalConfig.runTestsByPath) { + dataMessage = `Files: ${globalConfig.nonFlagArgs + .map(p => `"${p}"`) + .join(', ')}`; + } else { + dataMessage = `Pattern: ${_chalk().default.yellow( + globalConfig.testPathPattern + )} - 0 matches`; + } + + return ( + _chalk().default.bold('No tests found, exiting with code 1') + + '\n' + + 'Run with `--passWithNoTests` to exit with code 0' + + '\n' + + individualResults.join('\n') + + '\n' + + dataMessage + ); +} diff --git a/packages/jest-core/build/getNoTestsFoundMessage.d.ts b/packages/jest-core/build/getNoTestsFoundMessage.d.ts new file mode 100644 index 000000000000..910467ee90f4 --- /dev/null +++ b/packages/jest-core/build/getNoTestsFoundMessage.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { TestRunData } from './types'; +export default function getNoTestsFoundMessage(testRunData: TestRunData, globalConfig: Config.GlobalConfig): string; diff --git a/packages/jest-core/build/getNoTestsFoundMessage.js b/packages/jest-core/build/getNoTestsFoundMessage.js new file mode 100644 index 000000000000..293d0928d037 --- /dev/null +++ b/packages/jest-core/build/getNoTestsFoundMessage.js @@ -0,0 +1,52 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getNoTestsFoundMessage; + +var _getNoTestFound = _interopRequireDefault(require('./getNoTestFound')); + +var _getNoTestFoundFailed = _interopRequireDefault( + require('./getNoTestFoundFailed') +); + +var _getNoTestFoundPassWithNoTests = _interopRequireDefault( + require('./getNoTestFoundPassWithNoTests') +); + +var _getNoTestFoundRelatedToChangedFiles = _interopRequireDefault( + require('./getNoTestFoundRelatedToChangedFiles') +); + +var _getNoTestFoundVerbose = _interopRequireDefault( + require('./getNoTestFoundVerbose') +); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +function getNoTestsFoundMessage(testRunData, globalConfig) { + if (globalConfig.onlyFailures) { + return (0, _getNoTestFoundFailed.default)(globalConfig); + } + + if (globalConfig.onlyChanged) { + return (0, _getNoTestFoundRelatedToChangedFiles.default)(globalConfig); + } + + if (globalConfig.passWithNoTests) { + return (0, _getNoTestFoundPassWithNoTests.default)(); + } + + return testRunData.length === 1 || globalConfig.verbose + ? (0, _getNoTestFoundVerbose.default)(testRunData, globalConfig) + : (0, _getNoTestFound.default)(testRunData, globalConfig); +} diff --git a/packages/jest-core/build/getProjectDisplayName.d.ts b/packages/jest-core/build/getProjectDisplayName.d.ts new file mode 100644 index 000000000000..192e9feed876 --- /dev/null +++ b/packages/jest-core/build/getProjectDisplayName.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function getProjectDisplayName(projectConfig: Config.ProjectConfig): string | undefined; diff --git a/packages/jest-core/build/getProjectDisplayName.js b/packages/jest-core/build/getProjectDisplayName.js new file mode 100644 index 000000000000..55e869ef28e7 --- /dev/null +++ b/packages/jest-core/build/getProjectDisplayName.js @@ -0,0 +1,22 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getProjectDisplayName; + +/** + * 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. + */ +function getProjectDisplayName(projectConfig) { + const {displayName} = projectConfig; + + if (!displayName) { + return undefined; + } + + return displayName.name; +} diff --git a/packages/jest-core/build/getProjectNamesMissingWarning.d.ts b/packages/jest-core/build/getProjectNamesMissingWarning.d.ts new file mode 100644 index 000000000000..b2e17cf0ca7d --- /dev/null +++ b/packages/jest-core/build/getProjectNamesMissingWarning.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function getProjectNamesMissingWarning(projectConfigs: Array): string | undefined; diff --git a/packages/jest-core/build/getProjectNamesMissingWarning.js b/packages/jest-core/build/getProjectNamesMissingWarning.js new file mode 100644 index 000000000000..32ac1e19a5b5 --- /dev/null +++ b/packages/jest-core/build/getProjectNamesMissingWarning.js @@ -0,0 +1,49 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getProjectNamesMissingWarning; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +var _getProjectDisplayName = _interopRequireDefault( + require('./getProjectDisplayName') +); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +function getProjectNamesMissingWarning(projectConfigs) { + const numberOfProjectsWithoutAName = projectConfigs.filter( + config => !(0, _getProjectDisplayName.default)(config) + ).length; + + if (numberOfProjectsWithoutAName === 0) { + return undefined; + } + + return _chalk().default.yellow( + `You provided values for --selectProjects but ${ + numberOfProjectsWithoutAName === 1 + ? 'a project does not have a name' + : `${numberOfProjectsWithoutAName} projects do not have a name` + }.\n` + + 'Set displayName in the config of all projects in order to disable this warning.\n' + ); +} diff --git a/packages/jest-core/build/getSelectProjectsMessage.d.ts b/packages/jest-core/build/getSelectProjectsMessage.d.ts new file mode 100644 index 000000000000..21a4cd290679 --- /dev/null +++ b/packages/jest-core/build/getSelectProjectsMessage.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function getSelectProjectsMessage(projectConfigs: Array): string; diff --git a/packages/jest-core/build/getSelectProjectsMessage.js b/packages/jest-core/build/getSelectProjectsMessage.js new file mode 100644 index 000000000000..5607f69ef9df --- /dev/null +++ b/packages/jest-core/build/getSelectProjectsMessage.js @@ -0,0 +1,65 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getSelectProjectsMessage; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +var _getProjectDisplayName = _interopRequireDefault( + require('./getProjectDisplayName') +); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +function getSelectProjectsMessage(projectConfigs) { + if (projectConfigs.length === 0) { + return getNoSelectionWarning(); + } + + return getProjectsRunningMessage(projectConfigs); +} + +function getNoSelectionWarning() { + return _chalk().default.yellow( + 'You provided values for --selectProjects but no projects were found matching the selection.\n' + ); +} + +function getProjectsRunningMessage(projectConfigs) { + if (projectConfigs.length === 1) { + const name = (0, _getProjectDisplayName.default)(projectConfigs[0]); + return `Running one project: ${_chalk().default.bold(name)}\n`; + } + + const projectsList = projectConfigs + .map(getProjectNameListElement) + .sort() + .join('\n'); + return `Running ${projectConfigs.length} projects:\n${projectsList}\n`; +} + +function getProjectNameListElement(projectConfig) { + const name = (0, _getProjectDisplayName.default)(projectConfig); + const elementContent = name + ? _chalk().default.bold(name) + : ''; + return `- ${elementContent}`; +} diff --git a/packages/jest-core/build/jest.d.ts b/packages/jest-core/build/jest.d.ts new file mode 100644 index 000000000000..af85122841ea --- /dev/null +++ b/packages/jest-core/build/jest.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +export { default as SearchSource } from './SearchSource'; +export { default as TestScheduler } from './TestScheduler'; +export { default as TestWatcher } from './TestWatcher'; +export { runCLI } from './cli'; +export { default as getVersion } from './version'; diff --git a/packages/jest-core/build/jest.js b/packages/jest-core/build/jest.js new file mode 100644 index 000000000000..ab9911a73431 --- /dev/null +++ b/packages/jest-core/build/jest.js @@ -0,0 +1,49 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'SearchSource', { + enumerable: true, + get: function () { + return _SearchSource.default; + } +}); +Object.defineProperty(exports, 'TestScheduler', { + enumerable: true, + get: function () { + return _TestScheduler.default; + } +}); +Object.defineProperty(exports, 'TestWatcher', { + enumerable: true, + get: function () { + return _TestWatcher.default; + } +}); +Object.defineProperty(exports, 'runCLI', { + enumerable: true, + get: function () { + return _cli.runCLI; + } +}); +Object.defineProperty(exports, 'getVersion', { + enumerable: true, + get: function () { + return _version.default; + } +}); + +var _SearchSource = _interopRequireDefault(require('./SearchSource')); + +var _TestScheduler = _interopRequireDefault(require('./TestScheduler')); + +var _TestWatcher = _interopRequireDefault(require('./TestWatcher')); + +var _cli = require('./cli'); + +var _version = _interopRequireDefault(require('./version')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} diff --git a/packages/jest-core/build/lib/activeFiltersMessage.d.ts b/packages/jest-core/build/lib/activeFiltersMessage.d.ts new file mode 100644 index 000000000000..15f595ae0f18 --- /dev/null +++ b/packages/jest-core/build/lib/activeFiltersMessage.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +declare const activeFilters: (globalConfig: Config.GlobalConfig, delimiter?: string) => string; +export default activeFilters; diff --git a/packages/jest-core/build/lib/activeFiltersMessage.js b/packages/jest-core/build/lib/activeFiltersMessage.js new file mode 100644 index 000000000000..ff633327584e --- /dev/null +++ b/packages/jest-core/build/lib/activeFiltersMessage.js @@ -0,0 +1,54 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 activeFilters = (globalConfig, delimiter = '\n') => { + const {testNamePattern, testPathPattern} = globalConfig; + + if (testNamePattern || testPathPattern) { + const filters = [ + testPathPattern + ? _chalk().default.dim('filename ') + + _chalk().default.yellow('/' + testPathPattern + '/') + : null, + testNamePattern + ? _chalk().default.dim('test name ') + + _chalk().default.yellow('/' + testNamePattern + '/') + : null + ] + .filter(f => f) + .join(', '); + const messages = [ + '\n' + _chalk().default.bold('Active Filters: ') + filters + ]; + return messages.filter(message => !!message).join(delimiter); + } + + return ''; +}; + +var _default = activeFilters; +exports.default = _default; diff --git a/packages/jest-core/build/lib/createContext.d.ts b/packages/jest-core/build/lib/createContext.d.ts new file mode 100644 index 000000000000..823cf62c183d --- /dev/null +++ b/packages/jest-core/build/lib/createContext.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { HasteMapObject } from 'jest-haste-map'; +import { Context } from 'jest-runtime'; +declare const _default: (config: Config.ProjectConfig, { hasteFS, moduleMap }: HasteMapObject) => Context; +export default _default; diff --git a/packages/jest-core/build/lib/createContext.js b/packages/jest-core/build/lib/createContext.js new file mode 100644 index 000000000000..312d4e778ef2 --- /dev/null +++ b/packages/jest-core/build/lib/createContext.js @@ -0,0 +1,35 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestRuntime() { + const data = _interopRequireDefault(require('jest-runtime')); + + _jestRuntime = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +var _default = (config, {hasteFS, moduleMap}) => ({ + config, + hasteFS, + moduleMap, + resolver: _jestRuntime().default.createResolver(config, moduleMap) +}); + +exports.default = _default; diff --git a/packages/jest-core/build/lib/handleDeprecationWarnings.d.ts b/packages/jest-core/build/lib/handleDeprecationWarnings.d.ts new file mode 100644 index 000000000000..d43d83df5f34 --- /dev/null +++ b/packages/jest-core/build/lib/handleDeprecationWarnings.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +/// +declare const _default: (pipe: NodeJS.WriteStream, stdin?: NodeJS.ReadStream) => Promise; +export default _default; diff --git a/packages/jest-core/build/lib/handleDeprecationWarnings.js b/packages/jest-core/build/lib/handleDeprecationWarnings.js new file mode 100644 index 000000000000..da393952c962 --- /dev/null +++ b/packages/jest-core/build/lib/handleDeprecationWarnings.js @@ -0,0 +1,73 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestWatcher() { + const data = require('jest-watcher'); + + _jestWatcher = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +var _default = (pipe, stdin = process.stdin) => + new Promise((resolve, reject) => { + if (typeof stdin.setRawMode === 'function') { + const messages = [ + _chalk().default.red('There are deprecation warnings.\n'), + _chalk().default.dim(' \u203A Press ') + + 'Enter' + + _chalk().default.dim(' to continue.'), + _chalk().default.dim(' \u203A Press ') + + 'Esc' + + _chalk().default.dim(' to exit.') + ]; + pipe.write(messages.join('\n')); + stdin.setRawMode(true); + stdin.resume(); + stdin.setEncoding('utf8'); // this is a string since we set encoding above + + stdin.on('data', key => { + if (key === _jestWatcher().KEYS.ENTER) { + resolve(); + } else if ( + [ + _jestWatcher().KEYS.ESCAPE, + _jestWatcher().KEYS.CONTROL_C, + _jestWatcher().KEYS.CONTROL_D + ].indexOf(key) !== -1 + ) { + reject(); + } + }); + } else { + resolve(); + } + }); + +exports.default = _default; diff --git a/packages/jest-core/build/lib/isValidPath.d.ts b/packages/jest-core/build/lib/isValidPath.d.ts new file mode 100644 index 000000000000..d81880adc173 --- /dev/null +++ b/packages/jest-core/build/lib/isValidPath.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function isValidPath(globalConfig: Config.GlobalConfig, filePath: Config.Path): boolean; diff --git a/packages/jest-core/build/lib/isValidPath.js b/packages/jest-core/build/lib/isValidPath.js new file mode 100644 index 000000000000..10971d38b784 --- /dev/null +++ b/packages/jest-core/build/lib/isValidPath.js @@ -0,0 +1,29 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = isValidPath; + +function _jestSnapshot() { + const data = require('jest-snapshot'); + + _jestSnapshot = function () { + return data; + }; + + return data; +} + +/** + * 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. + */ +function isValidPath(globalConfig, filePath) { + return ( + !filePath.includes(globalConfig.coverageDirectory) && + !(0, _jestSnapshot().isSnapshotPath)(filePath) + ); +} diff --git a/packages/jest-core/build/lib/logDebugMessages.d.ts b/packages/jest-core/build/lib/logDebugMessages.d.ts new file mode 100644 index 000000000000..b312cd9e1b5c --- /dev/null +++ b/packages/jest-core/build/lib/logDebugMessages.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +/// +import type { Config } from '@jest/types'; +export default function logDebugMessages(globalConfig: Config.GlobalConfig, configs: Array | Config.ProjectConfig, outputStream: NodeJS.WriteStream): void; diff --git a/packages/jest-core/build/lib/logDebugMessages.js b/packages/jest-core/build/lib/logDebugMessages.js new file mode 100644 index 000000000000..9c1f2f5fc99a --- /dev/null +++ b/packages/jest-core/build/lib/logDebugMessages.js @@ -0,0 +1,23 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = logDebugMessages; + +/** + * 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 VERSION = require('../../package.json').version; // if the output here changes, update `getConfig` in e2e/runJest.ts + +function logDebugMessages(globalConfig, configs, outputStream) { + const output = { + configs, + globalConfig, + version: VERSION + }; + outputStream.write(JSON.stringify(output, null, ' ') + '\n'); +} diff --git a/packages/jest-core/build/lib/updateGlobalConfig.d.ts b/packages/jest-core/build/lib/updateGlobalConfig.d.ts new file mode 100644 index 000000000000..be0f5513eb63 --- /dev/null +++ b/packages/jest-core/build/lib/updateGlobalConfig.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { AllowedConfigOptions } from 'jest-watcher'; +declare type ExtraConfigOptions = Partial>; +declare const _default: (globalConfig: Config.GlobalConfig, options?: AllowedConfigOptions & ExtraConfigOptions) => Config.GlobalConfig; +export default _default; diff --git a/packages/jest-core/build/lib/updateGlobalConfig.js b/packages/jest-core/build/lib/updateGlobalConfig.js new file mode 100644 index 000000000000..9c47f7c8bb63 --- /dev/null +++ b/packages/jest-core/build/lib/updateGlobalConfig.js @@ -0,0 +1,124 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestRegexUtil() { + const data = require('jest-regex-util'); + + _jestRegexUtil = function () { + return data; + }; + + return data; +} + +/** + * 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. + */ +var _default = (globalConfig, options = {}) => { + const newConfig = {...globalConfig}; + + if (options.mode === 'watch') { + newConfig.watch = true; + newConfig.watchAll = false; + } else if (options.mode === 'watchAll') { + newConfig.watch = false; + newConfig.watchAll = true; + } + + if (options.testNamePattern !== undefined) { + newConfig.testNamePattern = options.testNamePattern || ''; + } + + if (options.testPathPattern !== undefined) { + newConfig.testPathPattern = + (0, _jestRegexUtil().replacePathSepForRegex)(options.testPathPattern) || + ''; + } + + newConfig.onlyChanged = false; + newConfig.onlyChanged = + !newConfig.watchAll && + !newConfig.testNamePattern && + !newConfig.testPathPattern; + + if (typeof options.bail === 'boolean') { + newConfig.bail = options.bail ? 1 : 0; + } else if (options.bail !== undefined) { + newConfig.bail = options.bail; + } + + if (options.changedSince !== undefined) { + newConfig.changedSince = options.changedSince; + } + + if (options.collectCoverage !== undefined) { + newConfig.collectCoverage = options.collectCoverage || false; + } + + if (options.collectCoverageFrom !== undefined) { + newConfig.collectCoverageFrom = options.collectCoverageFrom; + } + + if (options.collectCoverageOnlyFrom !== undefined) { + newConfig.collectCoverageOnlyFrom = options.collectCoverageOnlyFrom; + } + + if (options.coverageDirectory !== undefined) { + newConfig.coverageDirectory = options.coverageDirectory; + } + + if (options.coverageReporters !== undefined) { + newConfig.coverageReporters = options.coverageReporters; + } + + if (options.findRelatedTests !== undefined) { + newConfig.findRelatedTests = options.findRelatedTests; + } + + if (options.nonFlagArgs !== undefined) { + newConfig.nonFlagArgs = options.nonFlagArgs; + } + + if (options.noSCM) { + newConfig.noSCM = true; + } + + if (options.notify !== undefined) { + newConfig.notify = options.notify || false; + } + + if (options.notifyMode !== undefined) { + newConfig.notifyMode = options.notifyMode; + } + + if (options.onlyFailures !== undefined) { + newConfig.onlyFailures = options.onlyFailures || false; + } + + if (options.passWithNoTests !== undefined) { + newConfig.passWithNoTests = true; + } + + if (options.reporters !== undefined) { + newConfig.reporters = options.reporters; + } + + if (options.updateSnapshot !== undefined) { + newConfig.updateSnapshot = options.updateSnapshot; + } + + if (options.verbose !== undefined) { + newConfig.verbose = options.verbose || false; + } + + return Object.freeze(newConfig); +}; + +exports.default = _default; diff --git a/packages/jest-core/build/lib/watchPluginsHelpers.d.ts b/packages/jest-core/build/lib/watchPluginsHelpers.d.ts new file mode 100644 index 000000000000..99e8d7f72885 --- /dev/null +++ b/packages/jest-core/build/lib/watchPluginsHelpers.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { UsageData, WatchPlugin } from 'jest-watcher'; +export declare const filterInteractivePlugins: (watchPlugins: Array, globalConfig: Config.GlobalConfig) => Array; +export declare const getSortedUsageRows: (watchPlugins: Array, globalConfig: Config.GlobalConfig) => Array; diff --git a/packages/jest-core/build/lib/watchPluginsHelpers.js b/packages/jest-core/build/lib/watchPluginsHelpers.js new file mode 100644 index 000000000000..9f595ce2a219 --- /dev/null +++ b/packages/jest-core/build/lib/watchPluginsHelpers.js @@ -0,0 +1,62 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.getSortedUsageRows = exports.filterInteractivePlugins = void 0; + +/** + * 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 filterInteractivePlugins = (watchPlugins, globalConfig) => { + const usageInfos = watchPlugins.map( + p => p.getUsageInfo && p.getUsageInfo(globalConfig) + ); + return watchPlugins.filter((_plugin, i) => { + const usageInfo = usageInfos[i]; + + if (usageInfo) { + const {key} = usageInfo; + return !usageInfos.slice(i + 1).some(u => !!u && key === u.key); + } + + return false; + }); +}; + +exports.filterInteractivePlugins = filterInteractivePlugins; + +function notEmpty(value) { + return value != null; +} + +const getSortedUsageRows = (watchPlugins, globalConfig) => + filterInteractivePlugins(watchPlugins, globalConfig) + .sort((a, b) => { + if (a.isInternal && b.isInternal) { + // internal plugins in the order we specify them + return 0; + } + + if (a.isInternal !== b.isInternal) { + // external plugins afterwards + return a.isInternal ? -1 : 1; + } + + const usageInfoA = a.getUsageInfo && a.getUsageInfo(globalConfig); + const usageInfoB = b.getUsageInfo && b.getUsageInfo(globalConfig); + + if (usageInfoA && usageInfoB) { + // external plugins in alphabetical order + return usageInfoA.key.localeCompare(usageInfoB.key); + } + + return 0; + }) + .map(p => p.getUsageInfo && p.getUsageInfo(globalConfig)) + .filter(notEmpty); + +exports.getSortedUsageRows = getSortedUsageRows; diff --git a/packages/jest-core/build/plugins/Quit.d.ts b/packages/jest-core/build/plugins/Quit.d.ts new file mode 100644 index 000000000000..e86561247f66 --- /dev/null +++ b/packages/jest-core/build/plugins/Quit.d.ts @@ -0,0 +1,18 @@ +/** + * 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. + */ +/// +import { BaseWatchPlugin, UsageData } from 'jest-watcher'; +declare class QuitPlugin extends BaseWatchPlugin { + isInternal: true; + constructor(options: { + stdin: NodeJS.ReadStream; + stdout: NodeJS.WriteStream; + }); + run(): Promise; + getUsageInfo(): UsageData; +} +export default QuitPlugin; diff --git a/packages/jest-core/build/plugins/Quit.js b/packages/jest-core/build/plugins/Quit.js new file mode 100644 index 000000000000..e643a2b6ea72 --- /dev/null +++ b/packages/jest-core/build/plugins/Quit.js @@ -0,0 +1,60 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestWatcher() { + const data = require('jest-watcher'); + + _jestWatcher = function () { + return data; + }; + + return data; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class QuitPlugin extends _jestWatcher().BaseWatchPlugin { + constructor(options) { + super(options); + + _defineProperty(this, 'isInternal', void 0); + + this.isInternal = true; + } + + async run() { + if (typeof this._stdin.setRawMode === 'function') { + this._stdin.setRawMode(false); + } + + this._stdout.write('\n'); + + process.exit(0); + } + + getUsageInfo() { + return { + key: 'q', + prompt: 'quit watch mode' + }; + } +} + +var _default = QuitPlugin; +exports.default = _default; diff --git a/packages/jest-core/build/plugins/TestNamePattern.d.ts b/packages/jest-core/build/plugins/TestNamePattern.d.ts new file mode 100644 index 000000000000..dc0409af1265 --- /dev/null +++ b/packages/jest-core/build/plugins/TestNamePattern.d.ts @@ -0,0 +1,21 @@ +/** + * 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. + */ +/// +import type { Config } from '@jest/types'; +import { BaseWatchPlugin, Prompt, UpdateConfigCallback, UsageData } from 'jest-watcher'; +declare class TestNamePatternPlugin extends BaseWatchPlugin { + _prompt: Prompt; + isInternal: true; + constructor(options: { + stdin: NodeJS.ReadStream; + stdout: NodeJS.WriteStream; + }); + getUsageInfo(): UsageData; + onKey(key: string): void; + run(globalConfig: Config.GlobalConfig, updateConfigAndRun: UpdateConfigCallback): Promise; +} +export default TestNamePatternPlugin; diff --git a/packages/jest-core/build/plugins/TestNamePattern.js b/packages/jest-core/build/plugins/TestNamePattern.js new file mode 100644 index 000000000000..da33cb2fbb8d --- /dev/null +++ b/packages/jest-core/build/plugins/TestNamePattern.js @@ -0,0 +1,103 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestWatcher() { + const data = require('jest-watcher'); + + _jestWatcher = function () { + return data; + }; + + return data; +} + +function _TestNamePatternPrompt() { + const data = _interopRequireDefault(require('../TestNamePatternPrompt')); + + _TestNamePatternPrompt = function () { + return data; + }; + + return data; +} + +function _activeFiltersMessage() { + const data = _interopRequireDefault(require('../lib/activeFiltersMessage')); + + _activeFiltersMessage = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class TestNamePatternPlugin extends _jestWatcher().BaseWatchPlugin { + constructor(options) { + super(options); + + _defineProperty(this, '_prompt', void 0); + + _defineProperty(this, 'isInternal', void 0); + + this._prompt = new (_jestWatcher().Prompt)(); + this.isInternal = true; + } + + getUsageInfo() { + return { + key: 't', + prompt: 'filter by a test name regex pattern' + }; + } + + onKey(key) { + this._prompt.put(key); + } + + run(globalConfig, updateConfigAndRun) { + return new Promise((res, rej) => { + const testNamePatternPrompt = new (_TestNamePatternPrompt().default)( + this._stdout, + this._prompt + ); + testNamePatternPrompt.run( + value => { + updateConfigAndRun({ + mode: 'watch', + testNamePattern: value + }); + res(); + }, + rej, + { + header: (0, _activeFiltersMessage().default)(globalConfig) + } + ); + }); + } +} + +var _default = TestNamePatternPlugin; +exports.default = _default; diff --git a/packages/jest-core/build/plugins/TestPathPattern.d.ts b/packages/jest-core/build/plugins/TestPathPattern.d.ts new file mode 100644 index 000000000000..9eefab97f5e2 --- /dev/null +++ b/packages/jest-core/build/plugins/TestPathPattern.d.ts @@ -0,0 +1,21 @@ +/** + * 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. + */ +/// +import type { Config } from '@jest/types'; +import { BaseWatchPlugin, UpdateConfigCallback, UsageData } from 'jest-watcher'; +declare class TestPathPatternPlugin extends BaseWatchPlugin { + private _prompt; + isInternal: true; + constructor(options: { + stdin: NodeJS.ReadStream; + stdout: NodeJS.WriteStream; + }); + getUsageInfo(): UsageData; + onKey(key: string): void; + run(globalConfig: Config.GlobalConfig, updateConfigAndRun: UpdateConfigCallback): Promise; +} +export default TestPathPatternPlugin; diff --git a/packages/jest-core/build/plugins/TestPathPattern.js b/packages/jest-core/build/plugins/TestPathPattern.js new file mode 100644 index 000000000000..bc46560dea6a --- /dev/null +++ b/packages/jest-core/build/plugins/TestPathPattern.js @@ -0,0 +1,103 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestWatcher() { + const data = require('jest-watcher'); + + _jestWatcher = function () { + return data; + }; + + return data; +} + +function _TestPathPatternPrompt() { + const data = _interopRequireDefault(require('../TestPathPatternPrompt')); + + _TestPathPatternPrompt = function () { + return data; + }; + + return data; +} + +function _activeFiltersMessage() { + const data = _interopRequireDefault(require('../lib/activeFiltersMessage')); + + _activeFiltersMessage = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class TestPathPatternPlugin extends _jestWatcher().BaseWatchPlugin { + constructor(options) { + super(options); + + _defineProperty(this, '_prompt', void 0); + + _defineProperty(this, 'isInternal', void 0); + + this._prompt = new (_jestWatcher().Prompt)(); + this.isInternal = true; + } + + getUsageInfo() { + return { + key: 'p', + prompt: 'filter by a filename regex pattern' + }; + } + + onKey(key) { + this._prompt.put(key); + } + + run(globalConfig, updateConfigAndRun) { + return new Promise((res, rej) => { + const testPathPatternPrompt = new (_TestPathPatternPrompt().default)( + this._stdout, + this._prompt + ); + testPathPatternPrompt.run( + value => { + updateConfigAndRun({ + mode: 'watch', + testPathPattern: value + }); + res(); + }, + rej, + { + header: (0, _activeFiltersMessage().default)(globalConfig) + } + ); + }); + } +} + +var _default = TestPathPatternPlugin; +exports.default = _default; diff --git a/packages/jest-core/build/plugins/UpdateSnapshots.d.ts b/packages/jest-core/build/plugins/UpdateSnapshots.d.ts new file mode 100644 index 000000000000..3eff4c2dfe07 --- /dev/null +++ b/packages/jest-core/build/plugins/UpdateSnapshots.d.ts @@ -0,0 +1,21 @@ +/** + * 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. + */ +/// +import type { Config } from '@jest/types'; +import { BaseWatchPlugin, JestHookSubscriber, UpdateConfigCallback, UsageData } from 'jest-watcher'; +declare class UpdateSnapshotsPlugin extends BaseWatchPlugin { + private _hasSnapshotFailure; + isInternal: true; + constructor(options: { + stdin: NodeJS.ReadStream; + stdout: NodeJS.WriteStream; + }); + run(_globalConfig: Config.GlobalConfig, updateConfigAndRun: UpdateConfigCallback): Promise; + apply(hooks: JestHookSubscriber): void; + getUsageInfo(): UsageData | null; +} +export default UpdateSnapshotsPlugin; diff --git a/packages/jest-core/build/plugins/UpdateSnapshots.js b/packages/jest-core/build/plugins/UpdateSnapshots.js new file mode 100644 index 000000000000..c2a9de75d73a --- /dev/null +++ b/packages/jest-core/build/plugins/UpdateSnapshots.js @@ -0,0 +1,70 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestWatcher() { + const data = require('jest-watcher'); + + _jestWatcher = function () { + return data; + }; + + return data; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class UpdateSnapshotsPlugin extends _jestWatcher().BaseWatchPlugin { + constructor(options) { + super(options); + + _defineProperty(this, '_hasSnapshotFailure', void 0); + + _defineProperty(this, 'isInternal', void 0); + + this.isInternal = true; + this._hasSnapshotFailure = false; + } + + run(_globalConfig, updateConfigAndRun) { + updateConfigAndRun({ + updateSnapshot: 'all' + }); + return Promise.resolve(false); + } + + apply(hooks) { + hooks.onTestRunComplete(results => { + this._hasSnapshotFailure = results.snapshot.failure; + }); + } + + getUsageInfo() { + if (this._hasSnapshotFailure) { + return { + key: 'u', + prompt: 'update failing snapshots' + }; + } + + return null; + } +} + +var _default = UpdateSnapshotsPlugin; +exports.default = _default; diff --git a/packages/jest-core/build/plugins/UpdateSnapshotsInteractive.d.ts b/packages/jest-core/build/plugins/UpdateSnapshotsInteractive.d.ts new file mode 100644 index 000000000000..c6a60e657aba --- /dev/null +++ b/packages/jest-core/build/plugins/UpdateSnapshotsInteractive.d.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ +import type { AggregatedResult, AssertionLocation } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import { BaseWatchPlugin, JestHookSubscriber, UsageData } from 'jest-watcher'; +declare class UpdateSnapshotInteractivePlugin extends BaseWatchPlugin { + private _snapshotInteractiveMode; + private _failedSnapshotTestAssertions; + isInternal: true; + getFailedSnapshotTestAssertions(testResults: AggregatedResult): Array; + apply(hooks: JestHookSubscriber): void; + onKey(key: string): void; + run(_globalConfig: Config.GlobalConfig, updateConfigAndRun: Function): Promise; + getUsageInfo(): UsageData | null; +} +export default UpdateSnapshotInteractivePlugin; diff --git a/packages/jest-core/build/plugins/UpdateSnapshotsInteractive.js b/packages/jest-core/build/plugins/UpdateSnapshotsInteractive.js new file mode 100644 index 000000000000..ac0382d8ee9f --- /dev/null +++ b/packages/jest-core/build/plugins/UpdateSnapshotsInteractive.js @@ -0,0 +1,145 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestWatcher() { + const data = require('jest-watcher'); + + _jestWatcher = function () { + return data; + }; + + return data; +} + +function _SnapshotInteractiveMode() { + const data = _interopRequireDefault(require('../SnapshotInteractiveMode')); + + _SnapshotInteractiveMode = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class UpdateSnapshotInteractivePlugin extends _jestWatcher().BaseWatchPlugin { + constructor(...args) { + super(...args); + + _defineProperty( + this, + '_snapshotInteractiveMode', + new (_SnapshotInteractiveMode().default)(this._stdout) + ); + + _defineProperty(this, '_failedSnapshotTestAssertions', []); + + _defineProperty(this, 'isInternal', true); + } + + getFailedSnapshotTestAssertions(testResults) { + const failedTestPaths = []; + + if (testResults.numFailedTests === 0 || !testResults.testResults) { + return failedTestPaths; + } + + testResults.testResults.forEach(testResult => { + if (testResult.snapshot && testResult.snapshot.unmatched) { + testResult.testResults.forEach(result => { + if (result.status === 'failed') { + failedTestPaths.push({ + fullName: result.fullName, + path: testResult.testFilePath + }); + } + }); + } + }); + return failedTestPaths; + } + + apply(hooks) { + hooks.onTestRunComplete(results => { + this._failedSnapshotTestAssertions = this.getFailedSnapshotTestAssertions( + results + ); + + if (this._snapshotInteractiveMode.isActive()) { + this._snapshotInteractiveMode.updateWithResults(results); + } + }); + } + + onKey(key) { + if (this._snapshotInteractiveMode.isActive()) { + this._snapshotInteractiveMode.put(key); + } + } + + run(_globalConfig, updateConfigAndRun) { + if (this._failedSnapshotTestAssertions.length) { + return new Promise(res => { + this._snapshotInteractiveMode.run( + this._failedSnapshotTestAssertions, + (assertion, shouldUpdateSnapshot) => { + updateConfigAndRun({ + mode: 'watch', + testNamePattern: assertion ? `^${assertion.fullName}$` : '', + testPathPattern: assertion ? assertion.path : '', + updateSnapshot: shouldUpdateSnapshot ? 'all' : 'none' + }); + + if (!this._snapshotInteractiveMode.isActive()) { + res(); + } + } + ); + }); + } else { + return Promise.resolve(); + } + } + + getUsageInfo() { + var _this$_failedSnapshot; + + if ( + ((_this$_failedSnapshot = this._failedSnapshotTestAssertions) === null || + _this$_failedSnapshot === void 0 + ? void 0 + : _this$_failedSnapshot.length) > 0 + ) { + return { + key: 'i', + prompt: 'update failing snapshots interactively' + }; + } + + return null; + } +} + +var _default = UpdateSnapshotInteractivePlugin; +exports.default = _default; diff --git a/packages/jest-core/build/pluralize.d.ts b/packages/jest-core/build/pluralize.d.ts new file mode 100644 index 000000000000..a4e01ce9665c --- /dev/null +++ b/packages/jest-core/build/pluralize.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function pluralize(word: string, count: number, ending: string): string; diff --git a/packages/jest-core/build/pluralize.js b/packages/jest-core/build/pluralize.js new file mode 100644 index 000000000000..08695e5dce90 --- /dev/null +++ b/packages/jest-core/build/pluralize.js @@ -0,0 +1,16 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = pluralize; + +/** + * 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. + */ +function pluralize(word, count, ending) { + return `${count} ${word}${count === 1 ? '' : ending}`; +} diff --git a/packages/jest-core/build/runGlobalHook.d.ts b/packages/jest-core/build/runGlobalHook.d.ts new file mode 100644 index 000000000000..1505051538cc --- /dev/null +++ b/packages/jest-core/build/runGlobalHook.d.ts @@ -0,0 +1,14 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { Test } from 'jest-runner'; +declare const _default: ({ allTests, globalConfig, moduleName, }: { + allTests: Array; + globalConfig: Config.GlobalConfig; + moduleName: 'globalSetup' | 'globalTeardown'; +}) => Promise; +export default _default; diff --git a/packages/jest-core/build/runGlobalHook.js b/packages/jest-core/build/runGlobalHook.js new file mode 100644 index 000000000000..66ea5f964dcc --- /dev/null +++ b/packages/jest-core/build/runGlobalHook.js @@ -0,0 +1,165 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function util() { + const data = _interopRequireWildcard(require('util')); + + util = function () { + return data; + }; + + return data; +} + +function _pEachSeries() { + const data = _interopRequireDefault(require('p-each-series')); + + _pEachSeries = function () { + return data; + }; + + return data; +} + +function _transform() { + const data = require('@jest/transform'); + + _transform = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _prettyFormat() { + const data = _interopRequireDefault(require('pretty-format')); + + _prettyFormat = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +var _default = async ({allTests, globalConfig, moduleName}) => { + const globalModulePaths = new Set( + allTests.map(test => test.context.config[moduleName]) + ); + + if (globalConfig[moduleName]) { + globalModulePaths.add(globalConfig[moduleName]); + } + + if (globalModulePaths.size > 0) { + await (0, _pEachSeries().default)(globalModulePaths, async modulePath => { + if (!modulePath) { + return; + } + + const correctConfig = allTests.find( + t => t.context.config[moduleName] === modulePath + ); + const projectConfig = correctConfig + ? correctConfig.context.config // Fallback to first config + : allTests[0].context.config; + const transformer = new (_transform().ScriptTransformer)(projectConfig); + + try { + await transformer.requireAndTranspileModule(modulePath, async m => { + const globalModule = (0, _jestUtil().interopRequireDefault)(m) + .default; + + if (typeof globalModule !== 'function') { + throw new TypeError( + `${moduleName} file must export a function at ${modulePath}` + ); + } + + await globalModule(globalConfig); + }); + } catch (error) { + if (util().types.isNativeError(error)) { + error.message = `Jest: Got error running ${moduleName} - ${modulePath}, reason: ${error.message}`; + throw error; + } + + throw new Error( + `Jest: Got error running ${moduleName} - ${modulePath}, reason: ${(0, + _prettyFormat().default)(error, { + maxDepth: 3 + })}` + ); + } + }); + } + + return Promise.resolve(); +}; + +exports.default = _default; diff --git a/packages/jest-core/build/runJest.d.ts b/packages/jest-core/build/runJest.d.ts new file mode 100644 index 000000000000..9a8240b99017 --- /dev/null +++ b/packages/jest-core/build/runJest.d.ts @@ -0,0 +1,27 @@ +/** + * 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. + */ +/// +import { AggregatedResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import type { ChangedFilesPromise } from 'jest-changed-files'; +import type { Context } from 'jest-runtime'; +import { JestHookEmitter } from 'jest-watcher'; +import type FailedTestsCache from './FailedTestsCache'; +import type TestWatcher from './TestWatcher'; +import type { Filter } from './types'; +export default function runJest({ contexts, globalConfig, outputStream, testWatcher, jestHooks, startRun, changedFilesPromise, onComplete, failedTestsCache, filter, }: { + globalConfig: Config.GlobalConfig; + contexts: Array; + outputStream: NodeJS.WriteStream; + testWatcher: TestWatcher; + jestHooks?: JestHookEmitter; + startRun: (globalConfig: Config.GlobalConfig) => void; + changedFilesPromise?: ChangedFilesPromise; + onComplete: (testResults: AggregatedResult) => void; + failedTestsCache?: FailedTestsCache; + filter?: Filter; +}): Promise; diff --git a/packages/jest-core/build/runJest.js b/packages/jest-core/build/runJest.js new file mode 100644 index 000000000000..2aca4f7564f0 --- /dev/null +++ b/packages/jest-core/build/runJest.js @@ -0,0 +1,402 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = runJest; + +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 _exit() { + const data = _interopRequireDefault(require('exit')); + + _exit = function () { + return data; + }; + + return data; +} + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _console() { + const data = require('@jest/console'); + + _console = function () { + return data; + }; + + return data; +} + +function _testResult() { + const data = require('@jest/test-result'); + + _testResult = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _jestWatcher() { + const data = require('jest-watcher'); + + _jestWatcher = function () { + return data; + }; + + return data; +} + +var _SearchSource = _interopRequireDefault(require('./SearchSource')); + +var _TestScheduler = _interopRequireDefault(require('./TestScheduler')); + +var _collectHandles = _interopRequireDefault(require('./collectHandles')); + +var _getNoTestsFoundMessage = _interopRequireDefault( + require('./getNoTestsFoundMessage') +); + +var _runGlobalHook = _interopRequireDefault(require('./runGlobalHook')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 getTestPaths = async ( + globalConfig, + source, + outputStream, + changedFiles, + jestHooks, + filter +) => { + const data = await source.getTestPaths(globalConfig, changedFiles, filter); + + if (!data.tests.length && globalConfig.onlyChanged && data.noSCM) { + new (_console().CustomConsole)(outputStream, outputStream).log( + 'Jest can only find uncommitted changed files in a git or hg ' + + 'repository. If you make your project a git or hg ' + + 'repository (`git init` or `hg init`), Jest will be able ' + + 'to only run tests related to files changed since the last ' + + 'commit.' + ); + } + + const shouldTestArray = await Promise.all( + data.tests.map(test => + jestHooks.shouldRunTestSuite({ + config: test.context.config, + duration: test.duration, + testPath: test.path + }) + ) + ); + const filteredTests = data.tests.filter((_test, i) => shouldTestArray[i]); + return {...data, allTests: filteredTests.length, tests: filteredTests}; +}; + +const processResults = (runResults, options) => { + const { + outputFile, + json: isJSON, + onComplete, + outputStream, + testResultsProcessor, + collectHandles + } = options; + + if (collectHandles) { + runResults.openHandles = collectHandles(); + } else { + runResults.openHandles = []; + } + + if (testResultsProcessor) { + runResults = require(testResultsProcessor)(runResults); + } + + if (isJSON) { + if (outputFile) { + const cwd = (0, _jestUtil().tryRealpath)(process.cwd()); + const filePath = path().resolve(cwd, outputFile); + fs().writeFileSync( + filePath, + JSON.stringify((0, _testResult().formatTestResults)(runResults)) + ); + outputStream.write( + `Test results written to: ${path().relative(cwd, filePath)}\n` + ); + } else { + process.stdout.write( + JSON.stringify((0, _testResult().formatTestResults)(runResults)) + ); + } + } + + return onComplete && onComplete(runResults); +}; + +const testSchedulerContext = { + firstRun: true, + previousSuccess: true +}; + +async function runJest({ + contexts, + globalConfig, + outputStream, + testWatcher, + jestHooks = new (_jestWatcher().JestHook)().getEmitter(), + startRun, + changedFilesPromise, + onComplete, + failedTestsCache, + filter +}) { + const Sequencer = (0, _jestUtil().interopRequireDefault)( + require(globalConfig.testSequencer) + ).default; + const sequencer = new Sequencer(); + let allTests = []; + + if (changedFilesPromise && globalConfig.watch) { + const {repos} = await changedFilesPromise; + const noSCM = Object.keys(repos).every(scm => repos[scm].size === 0); + + if (noSCM) { + process.stderr.write( + '\n' + + _chalk().default.bold('--watch') + + ' is not supported without git/hg, please use --watchAll ' + + '\n' + ); + (0, _exit().default)(1); + } + } + + const searchSources = contexts.map( + context => new _SearchSource.default(context) + ); + const testRunData = await Promise.all( + contexts.map(async (context, index) => { + const searchSource = searchSources[index]; + const matches = await getTestPaths( + globalConfig, + searchSource, + outputStream, + changedFilesPromise && (await changedFilesPromise), + jestHooks, + filter + ); + allTests = allTests.concat(matches.tests); + return { + context, + matches + }; + }) + ); + allTests = await sequencer.sort(allTests); + + if (globalConfig.listTests) { + const testsPaths = Array.from(new Set(allTests.map(test => test.path))); + /* eslint-disable no-console */ + + if (globalConfig.json) { + console.log(JSON.stringify(testsPaths)); + } else { + console.log(testsPaths.join('\n')); + } + /* eslint-enable */ + + onComplete && + onComplete((0, _testResult().makeEmptyAggregatedTestResult)()); + return; + } + + if (globalConfig.onlyFailures) { + if (failedTestsCache) { + allTests = failedTestsCache.filterTests(allTests); + } else { + allTests = await sequencer.allFailedTests(allTests); + } + } + + const hasTests = allTests.length > 0; + + if (!hasTests) { + const noTestsFoundMessage = (0, _getNoTestsFoundMessage.default)( + testRunData, + globalConfig + ); + + if ( + globalConfig.passWithNoTests || + globalConfig.findRelatedTests || + globalConfig.lastCommit || + globalConfig.onlyChanged + ) { + new (_console().CustomConsole)(outputStream, outputStream).log( + noTestsFoundMessage + ); + } else { + new (_console().CustomConsole)(outputStream, outputStream).error( + noTestsFoundMessage + ); + (0, _exit().default)(1); + } + } else if ( + allTests.length === 1 && + globalConfig.silent !== true && + globalConfig.verbose !== false + ) { + const newConfig = {...globalConfig, verbose: true}; + globalConfig = Object.freeze(newConfig); + } + + let collectHandles; + + if (globalConfig.detectOpenHandles) { + collectHandles = (0, _collectHandles.default)(); + } + + if (hasTests) { + await (0, _runGlobalHook.default)({ + allTests, + globalConfig, + moduleName: 'globalSetup' + }); + } + + if (changedFilesPromise) { + const changedFilesInfo = await changedFilesPromise; + + if (changedFilesInfo.changedFiles) { + testSchedulerContext.changedFiles = changedFilesInfo.changedFiles; + const sourcesRelatedToTestsInChangedFilesArray = contexts + .map((_, index) => { + const searchSource = searchSources[index]; + const relatedSourceFromTestsInChangedFiles = searchSource.findRelatedSourcesFromTestsInChangedFiles( + changedFilesInfo + ); + return relatedSourceFromTestsInChangedFiles; + }) + .reduce((total, paths) => total.concat(paths), []); + testSchedulerContext.sourcesRelatedToTestsInChangedFiles = new Set( + sourcesRelatedToTestsInChangedFilesArray + ); + } + } + + const results = await new _TestScheduler.default( + globalConfig, + { + startRun + }, + testSchedulerContext + ).scheduleTests(allTests, testWatcher); + await sequencer.cacheResults(allTests, results); + + if (hasTests) { + await (0, _runGlobalHook.default)({ + allTests, + globalConfig, + moduleName: 'globalTeardown' + }); + } + + await processResults(results, { + collectHandles, + json: globalConfig.json, + onComplete, + outputFile: globalConfig.outputFile, + outputStream, + testResultsProcessor: globalConfig.testResultsProcessor + }); +} diff --git a/packages/jest-core/build/testSchedulerHelper.d.ts b/packages/jest-core/build/testSchedulerHelper.d.ts new file mode 100644 index 000000000000..b0ca3d7e082b --- /dev/null +++ b/packages/jest-core/build/testSchedulerHelper.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { Test } from 'jest-runner'; +export declare function shouldRunInBand(tests: Array, timings: Array, { detectOpenHandles, maxWorkers, watch, watchAll }: Config.GlobalConfig): boolean; diff --git a/packages/jest-core/build/testSchedulerHelper.js b/packages/jest-core/build/testSchedulerHelper.js new file mode 100644 index 000000000000..78cf48c23cab --- /dev/null +++ b/packages/jest-core/build/testSchedulerHelper.js @@ -0,0 +1,51 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.shouldRunInBand = shouldRunInBand; + +/** + * 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 SLOW_TEST_TIME = 1000; + +function shouldRunInBand( + tests, + timings, + {detectOpenHandles, maxWorkers, watch, watchAll} +) { + // detectOpenHandles makes no sense without runInBand, because it cannot detect leaks in workers + if (detectOpenHandles) { + return true; + } + /* + * Run in band if we only have one test or one worker available, unless we + * are using the watch mode, in which case the TTY has to be responsive and + * we cannot schedule anything in the main thread. Same logic applies to + * watchAll. + * Also, if we are confident from previous runs that the tests will finish + * quickly we also run in band to reduce the overhead of spawning workers. + * Finally, the user can provide the runInBand argument in the CLI to + * force running in band. + * https://github.com/facebook/jest/blob/700e0dadb85f5dc8ff5dac6c7e98956690049734/packages/jest-config/src/getMaxWorkers.js#L14-L17 + */ + + const isWatchMode = watch || watchAll; + const areFastTests = timings.every(timing => timing < SLOW_TEST_TIME); + const oneWorkerOrLess = maxWorkers <= 1; + const oneTestOrLess = tests.length <= 1; + + if (isWatchMode) { + return oneWorkerOrLess || (oneTestOrLess && areFastTests); + } + + return ( + oneWorkerOrLess || + oneTestOrLess || + (tests.length <= 20 && timings.length > 0 && areFastTests) + ); +} diff --git a/packages/jest-core/build/types.d.ts b/packages/jest-core/build/types.d.ts new file mode 100644 index 000000000000..3128e1c771ba --- /dev/null +++ b/packages/jest-core/build/types.d.ts @@ -0,0 +1,39 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { Test } from 'jest-runner'; +import type { Context } from 'jest-runtime'; +export declare type Stats = { + roots: number; + testMatch: number; + testPathIgnorePatterns: number; + testRegex: number; + testPathPattern?: number; +}; +export declare type TestRunData = Array<{ + context: Context; + matches: { + allTests: number; + tests: Array; + total?: number; + stats?: Stats; + }; +}>; +export declare type TestPathCases = Array<{ + stat: keyof Stats; + isMatch: (path: Config.Path) => boolean; +}>; +export declare type TestPathCasesWithPathPattern = TestPathCases & { + testPathPattern: (path: Config.Path) => boolean; +}; +export declare type FilterResult = { + test: string; + message: string; +}; +export declare type Filter = (testPaths: Array) => Promise<{ + filtered: Array; +}>; diff --git a/packages/jest-core/build/types.js b/packages/jest-core/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-core/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-core/build/version.d.ts b/packages/jest-core/build/version.d.ts new file mode 100644 index 000000000000..86467315e14e --- /dev/null +++ b/packages/jest-core/build/version.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function getVersion(): string; diff --git a/packages/jest-core/build/version.js b/packages/jest-core/build/version.js new file mode 100644 index 000000000000..1bcd800b346f --- /dev/null +++ b/packages/jest-core/build/version.js @@ -0,0 +1,19 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getVersion; + +/** + * 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. + */ +// Cannot be `import` as it's not under TS root dir +const {version: VERSION} = require('../package.json'); + +function getVersion() { + return VERSION; +} diff --git a/packages/jest-core/build/watch.d.ts b/packages/jest-core/build/watch.d.ts new file mode 100644 index 000000000000..d8df91064a8e --- /dev/null +++ b/packages/jest-core/build/watch.d.ts @@ -0,0 +1,13 @@ +/** + * 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. + */ +/// +import type { Config } from '@jest/types'; +import type { default as HasteMap } from 'jest-haste-map'; +import type { Context } from 'jest-runtime'; +import { JestHook } from 'jest-watcher'; +import type { Filter } from './types'; +export default function watch(initialGlobalConfig: Config.GlobalConfig, contexts: Array, outputStream: NodeJS.WriteStream, hasteMapInstances: Array, stdin?: NodeJS.ReadStream, hooks?: JestHook, filter?: Filter): Promise; diff --git a/packages/jest-core/build/watch.js b/packages/jest-core/build/watch.js new file mode 100644 index 000000000000..b30e32ad31f1 --- /dev/null +++ b/packages/jest-core/build/watch.js @@ -0,0 +1,775 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = watch; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _ansiEscapes() { + const data = _interopRequireDefault(require('ansi-escapes')); + + _ansiEscapes = function () { + return data; + }; + + return data; +} + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _exit() { + const data = _interopRequireDefault(require('exit')); + + _exit = function () { + return data; + }; + + return data; +} + +function _slash() { + const data = _interopRequireDefault(require('slash')); + + _slash = function () { + return data; + }; + + return data; +} + +function _jestMessageUtil() { + const data = require('jest-message-util'); + + _jestMessageUtil = function () { + return data; + }; + + return data; +} + +function _jestResolve() { + const data = _interopRequireDefault(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; +} + +function _jestWatcher() { + const data = require('jest-watcher'); + + _jestWatcher = function () { + return data; + }; + + return data; +} + +var _FailedTestsCache = _interopRequireDefault(require('./FailedTestsCache')); + +var _SearchSource = _interopRequireDefault(require('./SearchSource')); + +var _TestWatcher = _interopRequireDefault(require('./TestWatcher')); + +var _getChangedFilesPromise = _interopRequireDefault( + require('./getChangedFilesPromise') +); + +var _activeFiltersMessage = _interopRequireDefault( + require('./lib/activeFiltersMessage') +); + +var _createContext = _interopRequireDefault(require('./lib/createContext')); + +var _isValidPath = _interopRequireDefault(require('./lib/isValidPath')); + +var _updateGlobalConfig = _interopRequireDefault( + require('./lib/updateGlobalConfig') +); + +var _watchPluginsHelpers = require('./lib/watchPluginsHelpers'); + +var _Quit = _interopRequireDefault(require('./plugins/Quit')); + +var _TestNamePattern = _interopRequireDefault( + require('./plugins/TestNamePattern') +); + +var _TestPathPattern = _interopRequireDefault( + require('./plugins/TestPathPattern') +); + +var _UpdateSnapshots = _interopRequireDefault( + require('./plugins/UpdateSnapshots') +); + +var _UpdateSnapshotsInteractive = _interopRequireDefault( + require('./plugins/UpdateSnapshotsInteractive') +); + +var _runJest = _interopRequireDefault(require('./runJest')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 {print: preRunMessagePrint} = _jestUtil().preRunMessage; + +let hasExitListener = false; +const INTERNAL_PLUGINS = [ + _TestPathPattern.default, + _TestNamePattern.default, + _UpdateSnapshots.default, + _UpdateSnapshotsInteractive.default, + _Quit.default +]; +const RESERVED_KEY_PLUGINS = new Map([ + [ + _UpdateSnapshots.default, + { + forbiddenOverwriteMessage: 'updating snapshots', + key: 'u' + } + ], + [ + _UpdateSnapshotsInteractive.default, + { + forbiddenOverwriteMessage: 'updating snapshots interactively', + key: 'i' + } + ], + [ + _Quit.default, + { + forbiddenOverwriteMessage: 'quitting watch mode' + } + ] +]); + +function watch( + initialGlobalConfig, + contexts, + outputStream, + hasteMapInstances, + stdin = process.stdin, + hooks = new (_jestWatcher().JestHook)(), + filter +) { + // `globalConfig` will be constantly updated and reassigned as a result of + // watch mode interactions. + let globalConfig = initialGlobalConfig; + let activePlugin; + globalConfig = (0, _updateGlobalConfig.default)(globalConfig, { + mode: globalConfig.watch ? 'watch' : 'watchAll', + passWithNoTests: true + }); + + const updateConfigAndRun = ({ + bail, + changedSince, + collectCoverage, + collectCoverageFrom, + collectCoverageOnlyFrom, + coverageDirectory, + coverageReporters, + findRelatedTests, + mode, + nonFlagArgs, + notify, + notifyMode, + onlyFailures, + reporters, + testNamePattern, + testPathPattern, + updateSnapshot, + verbose + } = {}) => { + const previousUpdateSnapshot = globalConfig.updateSnapshot; + globalConfig = (0, _updateGlobalConfig.default)(globalConfig, { + bail, + changedSince, + collectCoverage, + collectCoverageFrom, + collectCoverageOnlyFrom, + coverageDirectory, + coverageReporters, + findRelatedTests, + mode, + nonFlagArgs, + notify, + notifyMode, + onlyFailures, + reporters, + testNamePattern, + testPathPattern, + updateSnapshot, + verbose + }); + startRun(globalConfig); + globalConfig = (0, _updateGlobalConfig.default)(globalConfig, { + // updateSnapshot is not sticky after a run. + updateSnapshot: + previousUpdateSnapshot === 'all' ? 'none' : previousUpdateSnapshot + }); + }; + + const watchPlugins = INTERNAL_PLUGINS.map( + InternalPlugin => + new InternalPlugin({ + stdin, + stdout: outputStream + }) + ); + watchPlugins.forEach(plugin => { + const hookSubscriber = hooks.getSubscriber(); + + if (plugin.apply) { + plugin.apply(hookSubscriber); + } + }); + + if (globalConfig.watchPlugins != null) { + const watchPluginKeys = new Map(); + + for (const plugin of watchPlugins) { + const reservedInfo = RESERVED_KEY_PLUGINS.get(plugin.constructor) || {}; + const key = reservedInfo.key || getPluginKey(plugin, globalConfig); + + if (!key) { + continue; + } + + const {forbiddenOverwriteMessage} = reservedInfo; + watchPluginKeys.set(key, { + forbiddenOverwriteMessage, + overwritable: forbiddenOverwriteMessage == null, + plugin + }); + } + + for (const pluginWithConfig of globalConfig.watchPlugins) { + let plugin; + + try { + const ThirdPartyPlugin = require(pluginWithConfig.path); + + plugin = new ThirdPartyPlugin({ + config: pluginWithConfig.config, + stdin, + stdout: outputStream + }); + } catch (error) { + const errorWithContext = new Error( + `Failed to initialize watch plugin "${_chalk().default.bold( + (0, _slash().default)( + path().relative(process.cwd(), pluginWithConfig.path) + ) + )}":\n\n${(0, _jestMessageUtil().formatExecError)( + error, + contexts[0].config, + { + noStackTrace: false + } + )}` + ); + delete errorWithContext.stack; + return Promise.reject(errorWithContext); + } + + checkForConflicts(watchPluginKeys, plugin, globalConfig); + const hookSubscriber = hooks.getSubscriber(); + + if (plugin.apply) { + plugin.apply(hookSubscriber); + } + + watchPlugins.push(plugin); + } + } + + const failedTestsCache = new _FailedTestsCache.default(); + let searchSources = contexts.map(context => ({ + context, + searchSource: new _SearchSource.default(context) + })); + let isRunning = false; + let testWatcher; + let shouldDisplayWatchUsage = true; + let isWatchUsageDisplayed = false; + + const emitFileChange = () => { + if (hooks.isUsed('onFileChange')) { + const projects = searchSources.map(({context, searchSource}) => ({ + config: context.config, + testPaths: searchSource.findMatchingTests('').tests.map(t => t.path) + })); + hooks.getEmitter().onFileChange({ + projects + }); + } + }; + + emitFileChange(); + hasteMapInstances.forEach((hasteMapInstance, index) => { + hasteMapInstance.on('change', ({eventsQueue, hasteFS, moduleMap}) => { + const validPaths = eventsQueue.filter(({filePath}) => + (0, _isValidPath.default)(globalConfig, filePath) + ); + + if (validPaths.length) { + const context = (contexts[index] = (0, _createContext.default)( + contexts[index].config, + { + hasteFS, + moduleMap + } + )); + activePlugin = null; + searchSources = searchSources.slice(); + searchSources[index] = { + context, + searchSource: new _SearchSource.default(context) + }; + emitFileChange(); + startRun(globalConfig); + } + }); + }); + + if (!hasExitListener) { + hasExitListener = true; + process.on('exit', () => { + if (activePlugin) { + outputStream.write(_ansiEscapes().default.cursorDown()); + outputStream.write(_ansiEscapes().default.eraseDown); + } + }); + } + + const startRun = globalConfig => { + if (isRunning) { + return Promise.resolve(null); + } + + testWatcher = new _TestWatcher.default({ + isWatchMode: true + }); + _jestUtil().isInteractive && + outputStream.write(_jestUtil().specialChars.CLEAR); + preRunMessagePrint(outputStream); + isRunning = true; + const configs = contexts.map(context => context.config); + const changedFilesPromise = (0, _getChangedFilesPromise.default)( + globalConfig, + configs + ); // Clear cache for required modules + + _jestResolve().default.clearDefaultResolverCache(); + + return (0, _runJest.default)({ + changedFilesPromise, + contexts, + failedTestsCache, + filter, + globalConfig, + jestHooks: hooks.getEmitter(), + onComplete: results => { + isRunning = false; + hooks.getEmitter().onTestRunComplete(results); // Create a new testWatcher instance so that re-runs won't be blocked. + // The old instance that was passed to Jest will still be interrupted + // and prevent test runs from the previous run. + + testWatcher = new _TestWatcher.default({ + isWatchMode: true + }); // Do not show any Watch Usage related stuff when running in a + // non-interactive environment + + if (_jestUtil().isInteractive) { + if (shouldDisplayWatchUsage) { + outputStream.write(usage(globalConfig, watchPlugins)); + shouldDisplayWatchUsage = false; // hide Watch Usage after first run + + isWatchUsageDisplayed = true; + } else { + outputStream.write(showToggleUsagePrompt()); + shouldDisplayWatchUsage = false; + isWatchUsageDisplayed = false; + } + } else { + outputStream.write('\n'); + } + + failedTestsCache.setTestResults(results.testResults); + }, + outputStream, + startRun, + testWatcher + }).catch(( + error // Errors thrown inside `runJest`, e.g. by resolvers, are caught here for + ) => + // continuous watch mode execution. We need to reprint them to the + // terminal and give just a little bit of extra space so they fit below + // `preRunMessagePrint` message nicely. + console.error( + '\n\n' + + (0, _jestMessageUtil().formatExecError)(error, contexts[0].config, { + noStackTrace: false + }) + ) + ); + }; + + const onKeypress = key => { + if ( + key === _jestWatcher().KEYS.CONTROL_C || + key === _jestWatcher().KEYS.CONTROL_D + ) { + if (typeof stdin.setRawMode === 'function') { + stdin.setRawMode(false); + } + + outputStream.write('\n'); + (0, _exit().default)(0); + return; + } + + if (activePlugin != null && activePlugin.onKey) { + // if a plugin is activate, Jest should let it handle keystrokes, so ignore + // them here + activePlugin.onKey(key); + return; + } // Abort test run + + const pluginKeys = (0, _watchPluginsHelpers.getSortedUsageRows)( + watchPlugins, + globalConfig + ).map(usage => Number(usage.key).toString(16)); + + if ( + isRunning && + testWatcher && + ['q', _jestWatcher().KEYS.ENTER, 'a', 'o', 'f'] + .concat(pluginKeys) + .includes(key) + ) { + testWatcher.setState({ + interrupted: true + }); + return; + } + + const matchingWatchPlugin = (0, + _watchPluginsHelpers.filterInteractivePlugins)( + watchPlugins, + globalConfig + ).find(plugin => getPluginKey(plugin, globalConfig) === key); + + if (matchingWatchPlugin != null) { + if (isRunning) { + testWatcher.setState({ + interrupted: true + }); + return; + } // "activate" the plugin, which has jest ignore keystrokes so the plugin + // can handle them + + activePlugin = matchingWatchPlugin; + + if (activePlugin.run) { + activePlugin.run(globalConfig, updateConfigAndRun).then( + shouldRerun => { + activePlugin = null; + + if (shouldRerun) { + updateConfigAndRun(); + } + }, + () => { + activePlugin = null; + onCancelPatternPrompt(); + } + ); + } else { + activePlugin = null; + } + } + + switch (key) { + case _jestWatcher().KEYS.ENTER: + startRun(globalConfig); + break; + + case 'a': + globalConfig = (0, _updateGlobalConfig.default)(globalConfig, { + mode: 'watchAll', + testNamePattern: '', + testPathPattern: '' + }); + startRun(globalConfig); + break; + + case 'c': + updateConfigAndRun({ + mode: 'watch', + testNamePattern: '', + testPathPattern: '' + }); + break; + + case 'f': + globalConfig = (0, _updateGlobalConfig.default)(globalConfig, { + onlyFailures: !globalConfig.onlyFailures + }); + startRun(globalConfig); + break; + + case 'o': + globalConfig = (0, _updateGlobalConfig.default)(globalConfig, { + mode: 'watch', + testNamePattern: '', + testPathPattern: '' + }); + startRun(globalConfig); + break; + + case '?': + break; + + case 'w': + if (!shouldDisplayWatchUsage && !isWatchUsageDisplayed) { + outputStream.write(_ansiEscapes().default.cursorUp()); + outputStream.write(_ansiEscapes().default.eraseDown); + outputStream.write(usage(globalConfig, watchPlugins)); + isWatchUsageDisplayed = true; + shouldDisplayWatchUsage = false; + } + + break; + } + }; + + const onCancelPatternPrompt = () => { + outputStream.write(_ansiEscapes().default.cursorHide); + outputStream.write(_jestUtil().specialChars.CLEAR); + outputStream.write(usage(globalConfig, watchPlugins)); + outputStream.write(_ansiEscapes().default.cursorShow); + }; + + if (typeof stdin.setRawMode === 'function') { + stdin.setRawMode(true); + stdin.resume(); + stdin.setEncoding('utf8'); + stdin.on('data', onKeypress); + } + + startRun(globalConfig); + return Promise.resolve(); +} + +const checkForConflicts = (watchPluginKeys, plugin, globalConfig) => { + const key = getPluginKey(plugin, globalConfig); + + if (!key) { + return; + } + + const conflictor = watchPluginKeys.get(key); + + if (!conflictor || conflictor.overwritable) { + watchPluginKeys.set(key, { + overwritable: false, + plugin + }); + return; + } + + let error; + + if (conflictor.forbiddenOverwriteMessage) { + error = ` + Watch plugin ${_chalk().default.bold.red( + getPluginIdentifier(plugin) + )} attempted to register key ${_chalk().default.bold.red(`<${key}>`)}, + that is reserved internally for ${_chalk().default.bold.red( + conflictor.forbiddenOverwriteMessage + )}. + Please change the configuration key for this plugin.`.trim(); + } else { + const plugins = [conflictor.plugin, plugin] + .map(p => _chalk().default.bold.red(getPluginIdentifier(p))) + .join(' and '); + error = ` + Watch plugins ${plugins} both attempted to register key ${_chalk().default.bold.red( + `<${key}>` + )}. + Please change the key configuration for one of the conflicting plugins to avoid overlap.`.trim(); + } + + throw new (_jestValidate().ValidationError)( + 'Watch plugin configuration error', + error + ); +}; + +const getPluginIdentifier = ( + plugin // This breaks as `displayName` is not defined as a static, but since +) => + // WatchPlugin is an interface, and it is my understanding interface + // static fields are not definable anymore, no idea how to circumvent + // this :-( + // @ts-expect-error: leave `displayName` be. + plugin.constructor.displayName || plugin.constructor.name; + +const getPluginKey = (plugin, globalConfig) => { + if (typeof plugin.getUsageInfo === 'function') { + return ( + plugin.getUsageInfo(globalConfig) || { + key: null + } + ).key; + } + + return null; +}; + +const usage = (globalConfig, watchPlugins, delimiter = '\n') => { + const messages = [ + (0, _activeFiltersMessage.default)(globalConfig), + globalConfig.testPathPattern || globalConfig.testNamePattern + ? _chalk().default.dim(' \u203A Press ') + + 'c' + + _chalk().default.dim(' to clear filters.') + : null, + '\n' + _chalk().default.bold('Watch Usage'), + globalConfig.watch + ? _chalk().default.dim(' \u203A Press ') + + 'a' + + _chalk().default.dim(' to run all tests.') + : null, + globalConfig.onlyFailures + ? _chalk().default.dim(' \u203A Press ') + + 'f' + + _chalk().default.dim(' to quit "only failed tests" mode.') + : _chalk().default.dim(' \u203A Press ') + + 'f' + + _chalk().default.dim(' to run only failed tests.'), + (globalConfig.watchAll || + globalConfig.testPathPattern || + globalConfig.testNamePattern) && + !globalConfig.noSCM + ? _chalk().default.dim(' \u203A Press ') + + 'o' + + _chalk().default.dim(' to only run tests related to changed files.') + : null, + ...(0, _watchPluginsHelpers.getSortedUsageRows)( + watchPlugins, + globalConfig + ).map( + plugin => + _chalk().default.dim(' \u203A Press') + + ' ' + + plugin.key + + ' ' + + _chalk().default.dim(`to ${plugin.prompt}.`) + ), + _chalk().default.dim(' \u203A Press ') + + 'Enter' + + _chalk().default.dim(' to trigger a test run.') + ]; + return messages.filter(message => !!message).join(delimiter) + '\n'; +}; + +const showToggleUsagePrompt = () => + '\n' + + _chalk().default.bold('Watch Usage: ') + + _chalk().default.dim('Press ') + + 'w' + + _chalk().default.dim(' to show more.'); diff --git a/packages/jest-create-cache-key-function/build/index.d.ts b/packages/jest-create-cache-key-function/build/index.d.ts new file mode 100644 index 000000000000..2014e240c099 --- /dev/null +++ b/packages/jest-create-cache-key-function/build/index.d.ts @@ -0,0 +1,22 @@ +/** + * 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. + * + */ +import type { Config } from '@jest/types'; +declare type OldCacheKeyOptions = { + config: Config.ProjectConfig; + instrument: boolean; +}; +declare type NewCacheKeyOptions = { + config: Config.ProjectConfig; + configString: string; + instrument: boolean; +}; +declare type OldGetCacheKeyFunction = (fileData: string, filePath: Config.Path, configStr: string, options: OldCacheKeyOptions) => string; +declare type NewGetCacheKeyFunction = (sourceText: string, sourcePath: Config.Path, options: NewCacheKeyOptions) => string; +declare type GetCacheKeyFunction = OldGetCacheKeyFunction | NewGetCacheKeyFunction; +declare const _default: (files?: Array, values?: Array) => GetCacheKeyFunction; +export default _default; diff --git a/packages/jest-create-cache-key-function/build/index.js b/packages/jest-create-cache-key-function/build/index.js new file mode 100644 index 000000000000..cbdd8c6c3998 --- /dev/null +++ b/packages/jest-create-cache-key-function/build/index.js @@ -0,0 +1,83 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _crypto() { + const data = require('crypto'); + + _crypto = function () { + return data; + }; + + return data; +} + +function _fs() { + const data = require('fs'); + + _fs = function () { + return data; + }; + + return data; +} + +function _path() { + const data = require('path'); + + _path = function () { + return data; + }; + + return data; +} + +/** + * 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. + * + */ +// eslint-disable-next-line no-restricted-imports +function getGlobalCacheKey(files, values) { + return [ + process.env.NODE_ENV, + process.env.BABEL_ENV, + ...values, + ...files.map(file => (0, _fs().readFileSync)(file)) + ] + .reduce( + (hash, chunk) => hash.update('\0', 'utf8').update(chunk || ''), + (0, _crypto().createHash)('md5') + ) + .digest('hex'); +} + +function getCacheKeyFunction(globalCacheKey) { + return (sourceText, sourcePath, configString, options) => { + // Jest 27 passes a single options bag which contains `configString` rather than as a separate argument. + // We can hide that API difference, though, so this module is usable for both jest@<27 and jest@>=27 + const inferredOptions = options || configString; + const {config, instrument} = inferredOptions; + return (0, _crypto().createHash)('md5') + .update(globalCacheKey) + .update('\0', 'utf8') + .update(sourceText) + .update('\0', 'utf8') + .update( + config.rootDir ? (0, _path().relative)(config.rootDir, sourcePath) : '' + ) + .update('\0', 'utf8') + .update(instrument ? 'instrument' : '') + .digest('hex'); + }; +} + +var _default = (files = [], values = []) => + getCacheKeyFunction(getGlobalCacheKey(files, values)); + +exports.default = _default; diff --git a/packages/jest-diff/build/cleanupSemantic.d.ts b/packages/jest-diff/build/cleanupSemantic.d.ts new file mode 100644 index 000000000000..fe662afb0748 --- /dev/null +++ b/packages/jest-diff/build/cleanupSemantic.d.ts @@ -0,0 +1,57 @@ +/** + * Diff Match and Patch + * Copyright 2018 The diff-match-patch Authors. + * https://github.com/google/diff-match-patch + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @fileoverview Computes the difference between two texts to create a patch. + * Applies the patch onto another text, allowing for errors. + * @author fraser@google.com (Neil Fraser) + */ +/** + * CHANGES by pedrottimark to diff_match_patch_uncompressed.ts file: + * + * 1. Delete anything not needed to use diff_cleanupSemantic method + * 2. Convert from prototype properties to var declarations + * 3. Convert Diff to class from constructor and prototype + * 4. Add type annotations for arguments and return values + * 5. Add exports + */ +/** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ +declare var DIFF_DELETE: number; +declare var DIFF_INSERT: number; +declare var DIFF_EQUAL: number; +/** + * Class representing one diff tuple. + * Attempts to look like a two-element array (which is what this used to be). + * @param {number} op Operation, one of: DIFF_DELETE, DIFF_INSERT, DIFF_EQUAL. + * @param {string} text Text to be deleted, inserted, or retained. + * @constructor + */ +declare class Diff { + 0: number; + 1: string; + constructor(op: number, text: string); +} +/** + * Reduce the number of edits by eliminating semantically trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ +declare var diff_cleanupSemantic: (diffs: Array) => void; +export { Diff, DIFF_EQUAL, DIFF_DELETE, DIFF_INSERT, diff_cleanupSemantic as cleanupSemantic, }; diff --git a/packages/jest-diff/build/cleanupSemantic.js b/packages/jest-diff/build/cleanupSemantic.js new file mode 100644 index 000000000000..408558d3bd41 --- /dev/null +++ b/packages/jest-diff/build/cleanupSemantic.js @@ -0,0 +1,651 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.cleanupSemantic = exports.DIFF_INSERT = exports.DIFF_DELETE = exports.DIFF_EQUAL = exports.Diff = void 0; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * Diff Match and Patch + * Copyright 2018 The diff-match-patch Authors. + * https://github.com/google/diff-match-patch + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Computes the difference between two texts to create a patch. + * Applies the patch onto another text, allowing for errors. + * @author fraser@google.com (Neil Fraser) + */ + +/** + * CHANGES by pedrottimark to diff_match_patch_uncompressed.ts file: + * + * 1. Delete anything not needed to use diff_cleanupSemantic method + * 2. Convert from prototype properties to var declarations + * 3. Convert Diff to class from constructor and prototype + * 4. Add type annotations for arguments and return values + * 5. Add exports + */ + +/** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ +var DIFF_DELETE = -1; +exports.DIFF_DELETE = DIFF_DELETE; +var DIFF_INSERT = 1; +exports.DIFF_INSERT = DIFF_INSERT; +var DIFF_EQUAL = 0; +/** + * Class representing one diff tuple. + * Attempts to look like a two-element array (which is what this used to be). + * @param {number} op Operation, one of: DIFF_DELETE, DIFF_INSERT, DIFF_EQUAL. + * @param {string} text Text to be deleted, inserted, or retained. + * @constructor + */ + +exports.DIFF_EQUAL = DIFF_EQUAL; + +class Diff { + constructor(op, text) { + _defineProperty(this, 0, void 0); + + _defineProperty(this, 1, void 0); + + this[0] = op; + this[1] = text; + } +} +/** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ + +exports.Diff = Diff; + +var diff_commonPrefix = function (text1, text2) { + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) { + return 0; + } // Binary search. + // Performance analysis: https://neil.fraser.name/news/2007/10/09/ + + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + var pointerstart = 0; + + while (pointermin < pointermid) { + if ( + text1.substring(pointerstart, pointermid) == + text2.substring(pointerstart, pointermid) + ) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + + return pointermid; +}; +/** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ + +var diff_commonSuffix = function (text1, text2) { + // Quick check for common null cases. + if ( + !text1 || + !text2 || + text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1) + ) { + return 0; + } // Binary search. + // Performance analysis: https://neil.fraser.name/news/2007/10/09/ + + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + var pointerend = 0; + + while (pointermin < pointermid) { + if ( + text1.substring(text1.length - pointermid, text1.length - pointerend) == + text2.substring(text2.length - pointermid, text2.length - pointerend) + ) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + + return pointermid; +}; +/** + * Determine if the suffix of one string is the prefix of another. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of the first + * string and the start of the second string. + * @private + */ + +var diff_commonOverlap_ = function (text1, text2) { + // Cache the text lengths to prevent multiple calls. + var text1_length = text1.length; + var text2_length = text2.length; // Eliminate the null case. + + if (text1_length == 0 || text2_length == 0) { + return 0; + } // Truncate the longer string. + + if (text1_length > text2_length) { + text1 = text1.substring(text1_length - text2_length); + } else if (text1_length < text2_length) { + text2 = text2.substring(0, text1_length); + } + + var text_length = Math.min(text1_length, text2_length); // Quick check for the worst case. + + if (text1 == text2) { + return text_length; + } // Start by looking for a single character match + // and increase length until no match is found. + // Performance analysis: https://neil.fraser.name/news/2010/11/04/ + + var best = 0; + var length = 1; + + while (true) { + var pattern = text1.substring(text_length - length); + var found = text2.indexOf(pattern); + + if (found == -1) { + return best; + } + + length += found; + + if ( + found == 0 || + text1.substring(text_length - length) == text2.substring(0, length) + ) { + best = length; + length++; + } + } +}; +/** + * Reduce the number of edits by eliminating semantically trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ + +var diff_cleanupSemantic = function (diffs) { + var changes = false; + var equalities = []; // Stack of indices where equalities are found. + + var equalitiesLength = 0; // Keeping our own length var is faster in JS. + + /** @type {?string} */ + + var lastEquality = null; // Always equal to diffs[equalities[equalitiesLength - 1]][1] + + var pointer = 0; // Index of current position. + // Number of characters that changed prior to the equality. + + var length_insertions1 = 0; + var length_deletions1 = 0; // Number of characters that changed after the equality. + + var length_insertions2 = 0; + var length_deletions2 = 0; + + while (pointer < diffs.length) { + if (diffs[pointer][0] == DIFF_EQUAL) { + // Equality found. + equalities[equalitiesLength++] = pointer; + length_insertions1 = length_insertions2; + length_deletions1 = length_deletions2; + length_insertions2 = 0; + length_deletions2 = 0; + lastEquality = diffs[pointer][1]; + } else { + // An insertion or deletion. + if (diffs[pointer][0] == DIFF_INSERT) { + length_insertions2 += diffs[pointer][1].length; + } else { + length_deletions2 += diffs[pointer][1].length; + } // Eliminate an equality that is smaller or equal to the edits on both + // sides of it. + + if ( + lastEquality && + lastEquality.length <= + Math.max(length_insertions1, length_deletions1) && + lastEquality.length <= Math.max(length_insertions2, length_deletions2) + ) { + // Duplicate record. + diffs.splice( + equalities[equalitiesLength - 1], + 0, + new Diff(DIFF_DELETE, lastEquality) + ); // Change second copy to insert. + + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; // Throw away the equality we just deleted. + + equalitiesLength--; // Throw away the previous equality (it needs to be reevaluated). + + equalitiesLength--; + pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; + length_insertions1 = 0; // Reset the counters. + + length_deletions1 = 0; + length_insertions2 = 0; + length_deletions2 = 0; + lastEquality = null; + changes = true; + } + } + + pointer++; + } // Normalize the diff. + + if (changes) { + diff_cleanupMerge(diffs); + } + + diff_cleanupSemanticLossless(diffs); // Find any overlaps between deletions and insertions. + // e.g: abcxxxxxxdef + // -> abcxxxdef + // e.g: xxxabcdefxxx + // -> defxxxabc + // Only extract an overlap if it is as big as the edit ahead or behind it. + + pointer = 1; + + while (pointer < diffs.length) { + if ( + diffs[pointer - 1][0] == DIFF_DELETE && + diffs[pointer][0] == DIFF_INSERT + ) { + var deletion = diffs[pointer - 1][1]; + var insertion = diffs[pointer][1]; + var overlap_length1 = diff_commonOverlap_(deletion, insertion); + var overlap_length2 = diff_commonOverlap_(insertion, deletion); + + if (overlap_length1 >= overlap_length2) { + if ( + overlap_length1 >= deletion.length / 2 || + overlap_length1 >= insertion.length / 2 + ) { + // Overlap found. Insert an equality and trim the surrounding edits. + diffs.splice( + pointer, + 0, + new Diff(DIFF_EQUAL, insertion.substring(0, overlap_length1)) + ); + diffs[pointer - 1][1] = deletion.substring( + 0, + deletion.length - overlap_length1 + ); + diffs[pointer + 1][1] = insertion.substring(overlap_length1); + pointer++; + } + } else { + if ( + overlap_length2 >= deletion.length / 2 || + overlap_length2 >= insertion.length / 2 + ) { + // Reverse overlap found. + // Insert an equality and swap and trim the surrounding edits. + diffs.splice( + pointer, + 0, + new Diff(DIFF_EQUAL, deletion.substring(0, overlap_length2)) + ); + diffs[pointer - 1][0] = DIFF_INSERT; + diffs[pointer - 1][1] = insertion.substring( + 0, + insertion.length - overlap_length2 + ); + diffs[pointer + 1][0] = DIFF_DELETE; + diffs[pointer + 1][1] = deletion.substring(overlap_length2); + pointer++; + } + } + + pointer++; + } + + pointer++; + } +}; +/** + * Look for single edits surrounded on both sides by equalities + * which can be shifted sideways to align the edit to a word boundary. + * e.g: The cat came. -> The cat came. + * @param {!Array.} diffs Array of diff tuples. + */ + +exports.cleanupSemantic = diff_cleanupSemantic; + +var diff_cleanupSemanticLossless = function (diffs) { + /** + * Given two strings, compute a score representing whether the internal + * boundary falls on logical boundaries. + * Scores range from 6 (best) to 0 (worst). + * Closure, but does not reference any external variables. + * @param {string} one First string. + * @param {string} two Second string. + * @return {number} The score. + * @private + */ + function diff_cleanupSemanticScore_(one, two) { + if (!one || !two) { + // Edges are the best. + return 6; + } // Each port of this function behaves slightly differently due to + // subtle differences in each language's definition of things like + // 'whitespace'. Since this function's purpose is largely cosmetic, + // the choice has been made to use each language's native features + // rather than force total conformity. + + var char1 = one.charAt(one.length - 1); + var char2 = two.charAt(0); + var nonAlphaNumeric1 = char1.match(nonAlphaNumericRegex_); + var nonAlphaNumeric2 = char2.match(nonAlphaNumericRegex_); + var whitespace1 = nonAlphaNumeric1 && char1.match(whitespaceRegex_); + var whitespace2 = nonAlphaNumeric2 && char2.match(whitespaceRegex_); + var lineBreak1 = whitespace1 && char1.match(linebreakRegex_); + var lineBreak2 = whitespace2 && char2.match(linebreakRegex_); + var blankLine1 = lineBreak1 && one.match(blanklineEndRegex_); + var blankLine2 = lineBreak2 && two.match(blanklineStartRegex_); + + if (blankLine1 || blankLine2) { + // Five points for blank lines. + return 5; + } else if (lineBreak1 || lineBreak2) { + // Four points for line breaks. + return 4; + } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) { + // Three points for end of sentences. + return 3; + } else if (whitespace1 || whitespace2) { + // Two points for whitespace. + return 2; + } else if (nonAlphaNumeric1 || nonAlphaNumeric2) { + // One point for non-alphanumeric. + return 1; + } + + return 0; + } + + var pointer = 1; // Intentionally ignore the first and last element (don't need checking). + + while (pointer < diffs.length - 1) { + if ( + diffs[pointer - 1][0] == DIFF_EQUAL && + diffs[pointer + 1][0] == DIFF_EQUAL + ) { + // This is a single edit surrounded by equalities. + var equality1 = diffs[pointer - 1][1]; + var edit = diffs[pointer][1]; + var equality2 = diffs[pointer + 1][1]; // First, shift the edit as far left as possible. + + var commonOffset = diff_commonSuffix(equality1, edit); + + if (commonOffset) { + var commonString = edit.substring(edit.length - commonOffset); + equality1 = equality1.substring(0, equality1.length - commonOffset); + edit = commonString + edit.substring(0, edit.length - commonOffset); + equality2 = commonString + equality2; + } // Second, step character by character right, looking for the best fit. + + var bestEquality1 = equality1; + var bestEdit = edit; + var bestEquality2 = equality2; + var bestScore = + diff_cleanupSemanticScore_(equality1, edit) + + diff_cleanupSemanticScore_(edit, equality2); + + while (edit.charAt(0) === equality2.charAt(0)) { + equality1 += edit.charAt(0); + edit = edit.substring(1) + equality2.charAt(0); + equality2 = equality2.substring(1); + var score = + diff_cleanupSemanticScore_(equality1, edit) + + diff_cleanupSemanticScore_(edit, equality2); // The >= encourages trailing rather than leading whitespace on edits. + + if (score >= bestScore) { + bestScore = score; + bestEquality1 = equality1; + bestEdit = edit; + bestEquality2 = equality2; + } + } + + if (diffs[pointer - 1][1] != bestEquality1) { + // We have an improvement, save it back to the diff. + if (bestEquality1) { + diffs[pointer - 1][1] = bestEquality1; + } else { + diffs.splice(pointer - 1, 1); + pointer--; + } + + diffs[pointer][1] = bestEdit; + + if (bestEquality2) { + diffs[pointer + 1][1] = bestEquality2; + } else { + diffs.splice(pointer + 1, 1); + pointer--; + } + } + } + + pointer++; + } +}; // Define some regex patterns for matching boundaries. + +var nonAlphaNumericRegex_ = /[^a-zA-Z0-9]/; +var whitespaceRegex_ = /\s/; +var linebreakRegex_ = /[\r\n]/; +var blanklineEndRegex_ = /\n\r?\n$/; +var blanklineStartRegex_ = /^\r?\n\r?\n/; +/** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {!Array.} diffs Array of diff tuples. + */ + +var diff_cleanupMerge = function (diffs) { + // Add a dummy entry at the end. + diffs.push(new Diff(DIFF_EQUAL, '')); + var pointer = 0; + var count_delete = 0; + var count_insert = 0; + var text_delete = ''; + var text_insert = ''; + var commonlength; + + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + count_insert++; + text_insert += diffs[pointer][1]; + pointer++; + break; + + case DIFF_DELETE: + count_delete++; + text_delete += diffs[pointer][1]; + pointer++; + break; + + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (count_delete + count_insert > 1) { + if (count_delete !== 0 && count_insert !== 0) { + // Factor out any common prefixies. + commonlength = diff_commonPrefix(text_insert, text_delete); + + if (commonlength !== 0) { + if ( + pointer - count_delete - count_insert > 0 && + diffs[pointer - count_delete - count_insert - 1][0] == + DIFF_EQUAL + ) { + diffs[ + pointer - count_delete - count_insert - 1 + ][1] += text_insert.substring(0, commonlength); + } else { + diffs.splice( + 0, + 0, + new Diff(DIFF_EQUAL, text_insert.substring(0, commonlength)) + ); + pointer++; + } + + text_insert = text_insert.substring(commonlength); + text_delete = text_delete.substring(commonlength); + } // Factor out any common suffixies. + + commonlength = diff_commonSuffix(text_insert, text_delete); + + if (commonlength !== 0) { + diffs[pointer][1] = + text_insert.substring(text_insert.length - commonlength) + + diffs[pointer][1]; + text_insert = text_insert.substring( + 0, + text_insert.length - commonlength + ); + text_delete = text_delete.substring( + 0, + text_delete.length - commonlength + ); + } + } // Delete the offending records and add the merged ones. + + pointer -= count_delete + count_insert; + diffs.splice(pointer, count_delete + count_insert); + + if (text_delete.length) { + diffs.splice(pointer, 0, new Diff(DIFF_DELETE, text_delete)); + pointer++; + } + + if (text_insert.length) { + diffs.splice(pointer, 0, new Diff(DIFF_INSERT, text_insert)); + pointer++; + } + + pointer++; + } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) { + // Merge this equality with the previous one. + diffs[pointer - 1][1] += diffs[pointer][1]; + diffs.splice(pointer, 1); + } else { + pointer++; + } + + count_insert = 0; + count_delete = 0; + text_delete = ''; + text_insert = ''; + break; + } + } + + if (diffs[diffs.length - 1][1] === '') { + diffs.pop(); // Remove the dummy entry at the end. + } // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: ABAC -> ABAC + + var changes = false; + pointer = 1; // Intentionally ignore the first and last element (don't need checking). + + while (pointer < diffs.length - 1) { + if ( + diffs[pointer - 1][0] == DIFF_EQUAL && + diffs[pointer + 1][0] == DIFF_EQUAL + ) { + // This is a single edit surrounded by equalities. + if ( + diffs[pointer][1].substring( + diffs[pointer][1].length - diffs[pointer - 1][1].length + ) == diffs[pointer - 1][1] + ) { + // Shift the edit over the previous equality. + diffs[pointer][1] = + diffs[pointer - 1][1] + + diffs[pointer][1].substring( + 0, + diffs[pointer][1].length - diffs[pointer - 1][1].length + ); + diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; + diffs.splice(pointer - 1, 1); + changes = true; + } else if ( + diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == + diffs[pointer + 1][1] + ) { + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1]; + diffs[pointer][1] = + diffs[pointer][1].substring(diffs[pointer + 1][1].length) + + diffs[pointer + 1][1]; + diffs.splice(pointer + 1, 1); + changes = true; + } + } + + pointer++; + } // If shifts were made, the diff needs reordering and another shift sweep. + + if (changes) { + diff_cleanupMerge(diffs); + } +}; diff --git a/packages/jest-diff/build/constants.d.ts b/packages/jest-diff/build/constants.d.ts new file mode 100644 index 000000000000..a8f7e8a227f1 --- /dev/null +++ b/packages/jest-diff/build/constants.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +export declare const NO_DIFF_MESSAGE = "Compared values have no visual difference."; +export declare const SIMILAR_MESSAGE: string; diff --git a/packages/jest-diff/build/constants.js b/packages/jest-diff/build/constants.js new file mode 100644 index 000000000000..ccf73e512e4f --- /dev/null +++ b/packages/jest-diff/build/constants.js @@ -0,0 +1,19 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.SIMILAR_MESSAGE = exports.NO_DIFF_MESSAGE = void 0; + +/** + * 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 NO_DIFF_MESSAGE = 'Compared values have no visual difference.'; +exports.NO_DIFF_MESSAGE = NO_DIFF_MESSAGE; +const SIMILAR_MESSAGE = + 'Compared values serialize to the same structure.\n' + + 'Printing internal object structure without calling `toJSON` instead.'; +exports.SIMILAR_MESSAGE = SIMILAR_MESSAGE; diff --git a/packages/jest-diff/build/diffLines.d.ts b/packages/jest-diff/build/diffLines.d.ts new file mode 100644 index 000000000000..d311dcf8ed60 --- /dev/null +++ b/packages/jest-diff/build/diffLines.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +import { Diff } from './cleanupSemantic'; +import type { DiffOptions } from './types'; +export declare const diffLinesUnified: (aLines: Array, bLines: Array, options?: DiffOptions | undefined) => string; +export declare const diffLinesUnified2: (aLinesDisplay: Array, bLinesDisplay: Array, aLinesCompare: Array, bLinesCompare: Array, options?: DiffOptions | undefined) => string; +export declare const diffLinesRaw: (aLines: Array, bLines: Array) => Array; diff --git a/packages/jest-diff/build/diffLines.js b/packages/jest-diff/build/diffLines.js new file mode 100644 index 000000000000..f783e17d10f9 --- /dev/null +++ b/packages/jest-diff/build/diffLines.js @@ -0,0 +1,143 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.diffLinesRaw = exports.diffLinesUnified2 = exports.diffLinesUnified = void 0; + +var _diffSequences = _interopRequireDefault(require('diff-sequences')); + +var _cleanupSemantic = require('./cleanupSemantic'); + +var _normalizeDiffOptions = require('./normalizeDiffOptions'); + +var _printDiffs = require('./printDiffs'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 isEmptyString = lines => lines.length === 1 && lines[0].length === 0; // Compare two arrays of strings line-by-line. Format as comparison lines. + +const diffLinesUnified = (aLines, bLines, options) => + (0, _printDiffs.printDiffLines)( + diffLinesRaw( + isEmptyString(aLines) ? [] : aLines, + isEmptyString(bLines) ? [] : bLines + ), + (0, _normalizeDiffOptions.normalizeDiffOptions)(options) + ); // Given two pairs of arrays of strings: +// Compare the pair of comparison arrays line-by-line. +// Format the corresponding lines in the pair of displayable arrays. + +exports.diffLinesUnified = diffLinesUnified; + +const diffLinesUnified2 = ( + aLinesDisplay, + bLinesDisplay, + aLinesCompare, + bLinesCompare, + options +) => { + if (isEmptyString(aLinesDisplay) && isEmptyString(aLinesCompare)) { + aLinesDisplay = []; + aLinesCompare = []; + } + + if (isEmptyString(bLinesDisplay) && isEmptyString(bLinesCompare)) { + bLinesDisplay = []; + bLinesCompare = []; + } + + if ( + aLinesDisplay.length !== aLinesCompare.length || + bLinesDisplay.length !== bLinesCompare.length + ) { + // Fall back to diff of display lines. + return diffLinesUnified(aLinesDisplay, bLinesDisplay, options); + } + + const diffs = diffLinesRaw(aLinesCompare, bLinesCompare); // Replace comparison lines with displayable lines. + + let aIndex = 0; + let bIndex = 0; + diffs.forEach(diff => { + switch (diff[0]) { + case _cleanupSemantic.DIFF_DELETE: + diff[1] = aLinesDisplay[aIndex]; + aIndex += 1; + break; + + case _cleanupSemantic.DIFF_INSERT: + diff[1] = bLinesDisplay[bIndex]; + bIndex += 1; + break; + + default: + diff[1] = bLinesDisplay[bIndex]; + aIndex += 1; + bIndex += 1; + } + }); + return (0, _printDiffs.printDiffLines)( + diffs, + (0, _normalizeDiffOptions.normalizeDiffOptions)(options) + ); +}; // Compare two arrays of strings line-by-line. + +exports.diffLinesUnified2 = diffLinesUnified2; + +const diffLinesRaw = (aLines, bLines) => { + const aLength = aLines.length; + const bLength = bLines.length; + + const isCommon = (aIndex, bIndex) => aLines[aIndex] === bLines[bIndex]; + + const diffs = []; + let aIndex = 0; + let bIndex = 0; + + const foundSubsequence = (nCommon, aCommon, bCommon) => { + for (; aIndex !== aCommon; aIndex += 1) { + diffs.push( + new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_DELETE, aLines[aIndex]) + ); + } + + for (; bIndex !== bCommon; bIndex += 1) { + diffs.push( + new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_INSERT, bLines[bIndex]) + ); + } + + for (; nCommon !== 0; nCommon -= 1, aIndex += 1, bIndex += 1) { + diffs.push( + new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_EQUAL, bLines[bIndex]) + ); + } + }; + + (0, _diffSequences.default)(aLength, bLength, isCommon, foundSubsequence); // After the last common subsequence, push remaining change items. + + for (; aIndex !== aLength; aIndex += 1) { + diffs.push( + new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_DELETE, aLines[aIndex]) + ); + } + + for (; bIndex !== bLength; bIndex += 1) { + diffs.push( + new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_INSERT, bLines[bIndex]) + ); + } + + return diffs; +}; + +exports.diffLinesRaw = diffLinesRaw; diff --git a/packages/jest-diff/build/diffStrings.d.ts b/packages/jest-diff/build/diffStrings.d.ts new file mode 100644 index 000000000000..5146c55c81b1 --- /dev/null +++ b/packages/jest-diff/build/diffStrings.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import { Diff } from './cleanupSemantic'; +declare const diffStrings: (a: string, b: string) => Array; +export default diffStrings; diff --git a/packages/jest-diff/build/diffStrings.js b/packages/jest-diff/build/diffStrings.js new file mode 100644 index 000000000000..3ad4f1a5b7f6 --- /dev/null +++ b/packages/jest-diff/build/diffStrings.js @@ -0,0 +1,78 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _diffSequences = _interopRequireDefault(require('diff-sequences')); + +var _cleanupSemantic = require('./cleanupSemantic'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 diffStrings = (a, b) => { + const isCommon = (aIndex, bIndex) => a[aIndex] === b[bIndex]; + + let aIndex = 0; + let bIndex = 0; + const diffs = []; + + const foundSubsequence = (nCommon, aCommon, bCommon) => { + if (aIndex !== aCommon) { + diffs.push( + new _cleanupSemantic.Diff( + _cleanupSemantic.DIFF_DELETE, + a.slice(aIndex, aCommon) + ) + ); + } + + if (bIndex !== bCommon) { + diffs.push( + new _cleanupSemantic.Diff( + _cleanupSemantic.DIFF_INSERT, + b.slice(bIndex, bCommon) + ) + ); + } + + aIndex = aCommon + nCommon; // number of characters compared in a + + bIndex = bCommon + nCommon; // number of characters compared in b + + diffs.push( + new _cleanupSemantic.Diff( + _cleanupSemantic.DIFF_EQUAL, + b.slice(bCommon, bIndex) + ) + ); + }; + + (0, _diffSequences.default)(a.length, b.length, isCommon, foundSubsequence); // After the last common subsequence, push remaining change items. + + if (aIndex !== a.length) { + diffs.push( + new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_DELETE, a.slice(aIndex)) + ); + } + + if (bIndex !== b.length) { + diffs.push( + new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_INSERT, b.slice(bIndex)) + ); + } + + return diffs; +}; + +var _default = diffStrings; +exports.default = _default; diff --git a/packages/jest-diff/build/getAlignedDiffs.d.ts b/packages/jest-diff/build/getAlignedDiffs.d.ts new file mode 100644 index 000000000000..7838cface8fe --- /dev/null +++ b/packages/jest-diff/build/getAlignedDiffs.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import { Diff } from './cleanupSemantic'; +import type { DiffOptionsColor } from './types'; +declare const getAlignedDiffs: (diffs: Array, changeColor: DiffOptionsColor) => Array; +export default getAlignedDiffs; diff --git a/packages/jest-diff/build/getAlignedDiffs.js b/packages/jest-diff/build/getAlignedDiffs.js new file mode 100644 index 000000000000..2110a349884c --- /dev/null +++ b/packages/jest-diff/build/getAlignedDiffs.js @@ -0,0 +1,244 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _cleanupSemantic = require('./cleanupSemantic'); + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +// Given change op and array of diffs, return concatenated string: +// * include common strings +// * include change strings which have argument op with changeColor +// * exclude change strings which have opposite op +const concatenateRelevantDiffs = (op, diffs, changeColor) => + diffs.reduce( + (reduced, diff) => + reduced + + (diff[0] === _cleanupSemantic.DIFF_EQUAL + ? diff[1] + : diff[0] === op && diff[1].length !== 0 // empty if change is newline + ? changeColor(diff[1]) + : ''), + '' + ); // Encapsulate change lines until either a common newline or the end. + +class ChangeBuffer { + // incomplete line + // complete lines + constructor(op, changeColor) { + _defineProperty(this, 'op', void 0); + + _defineProperty(this, 'line', void 0); + + _defineProperty(this, 'lines', void 0); + + _defineProperty(this, 'changeColor', void 0); + + this.op = op; + this.line = []; + this.lines = []; + this.changeColor = changeColor; + } + + pushSubstring(substring) { + this.pushDiff(new _cleanupSemantic.Diff(this.op, substring)); + } + + pushLine() { + // Assume call only if line has at least one diff, + // therefore an empty line must have a diff which has an empty string. + // If line has multiple diffs, then assume it has a common diff, + // therefore change diffs have change color; + // otherwise then it has line color only. + this.lines.push( + this.line.length !== 1 + ? new _cleanupSemantic.Diff( + this.op, + concatenateRelevantDiffs(this.op, this.line, this.changeColor) + ) + : this.line[0][0] === this.op + ? this.line[0] // can use instance + : new _cleanupSemantic.Diff(this.op, this.line[0][1]) // was common diff + ); + this.line.length = 0; + } + + isLineEmpty() { + return this.line.length === 0; + } // Minor input to buffer. + + pushDiff(diff) { + this.line.push(diff); + } // Main input to buffer. + + align(diff) { + const string = diff[1]; + + if (string.includes('\n')) { + const substrings = string.split('\n'); + const iLast = substrings.length - 1; + substrings.forEach((substring, i) => { + if (i < iLast) { + // The first substring completes the current change line. + // A middle substring is a change line. + this.pushSubstring(substring); + this.pushLine(); + } else if (substring.length !== 0) { + // The last substring starts a change line, if it is not empty. + // Important: This non-empty condition also automatically omits + // the newline appended to the end of expected and received strings. + this.pushSubstring(substring); + } + }); + } else { + // Append non-multiline string to current change line. + this.pushDiff(diff); + } + } // Output from buffer. + + moveLinesTo(lines) { + if (!this.isLineEmpty()) { + this.pushLine(); + } + + lines.push(...this.lines); + this.lines.length = 0; + } +} // Encapsulate common and change lines. + +class CommonBuffer { + constructor(deleteBuffer, insertBuffer) { + _defineProperty(this, 'deleteBuffer', void 0); + + _defineProperty(this, 'insertBuffer', void 0); + + _defineProperty(this, 'lines', void 0); + + this.deleteBuffer = deleteBuffer; + this.insertBuffer = insertBuffer; + this.lines = []; + } + + pushDiffCommonLine(diff) { + this.lines.push(diff); + } + + pushDiffChangeLines(diff) { + const isDiffEmpty = diff[1].length === 0; // An empty diff string is redundant, unless a change line is empty. + + if (!isDiffEmpty || this.deleteBuffer.isLineEmpty()) { + this.deleteBuffer.pushDiff(diff); + } + + if (!isDiffEmpty || this.insertBuffer.isLineEmpty()) { + this.insertBuffer.pushDiff(diff); + } + } + + flushChangeLines() { + this.deleteBuffer.moveLinesTo(this.lines); + this.insertBuffer.moveLinesTo(this.lines); + } // Input to buffer. + + align(diff) { + const op = diff[0]; + const string = diff[1]; + + if (string.includes('\n')) { + const substrings = string.split('\n'); + const iLast = substrings.length - 1; + substrings.forEach((substring, i) => { + if (i === 0) { + const subdiff = new _cleanupSemantic.Diff(op, substring); + + if ( + this.deleteBuffer.isLineEmpty() && + this.insertBuffer.isLineEmpty() + ) { + // If both current change lines are empty, + // then the first substring is a common line. + this.flushChangeLines(); + this.pushDiffCommonLine(subdiff); + } else { + // If either current change line is non-empty, + // then the first substring completes the change lines. + this.pushDiffChangeLines(subdiff); + this.flushChangeLines(); + } + } else if (i < iLast) { + // A middle substring is a common line. + this.pushDiffCommonLine(new _cleanupSemantic.Diff(op, substring)); + } else if (substring.length !== 0) { + // The last substring starts a change line, if it is not empty. + // Important: This non-empty condition also automatically omits + // the newline appended to the end of expected and received strings. + this.pushDiffChangeLines(new _cleanupSemantic.Diff(op, substring)); + } + }); + } else { + // Append non-multiline string to current change lines. + // Important: It cannot be at the end following empty change lines, + // because newline appended to the end of expected and received strings. + this.pushDiffChangeLines(diff); + } + } // Output from buffer. + + getLines() { + this.flushChangeLines(); + return this.lines; + } +} // Given diffs from expected and received strings, +// return new array of diffs split or joined into lines. +// +// To correctly align a change line at the end, the algorithm: +// * assumes that a newline was appended to the strings +// * omits the last newline from the output array +// +// Assume the function is not called: +// * if either expected or received is empty string +// * if neither expected nor received is multiline string + +const getAlignedDiffs = (diffs, changeColor) => { + const deleteBuffer = new ChangeBuffer( + _cleanupSemantic.DIFF_DELETE, + changeColor + ); + const insertBuffer = new ChangeBuffer( + _cleanupSemantic.DIFF_INSERT, + changeColor + ); + const commonBuffer = new CommonBuffer(deleteBuffer, insertBuffer); + diffs.forEach(diff => { + switch (diff[0]) { + case _cleanupSemantic.DIFF_DELETE: + deleteBuffer.align(diff); + break; + + case _cleanupSemantic.DIFF_INSERT: + insertBuffer.align(diff); + break; + + default: + commonBuffer.align(diff); + } + }); + return commonBuffer.getLines(); +}; + +var _default = getAlignedDiffs; +exports.default = _default; diff --git a/packages/jest-diff/build/index.d.ts b/packages/jest-diff/build/index.d.ts new file mode 100644 index 000000000000..c86ed4c9b05c --- /dev/null +++ b/packages/jest-diff/build/index.d.ts @@ -0,0 +1,16 @@ +/** + * 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. + */ +import { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, Diff } from './cleanupSemantic'; +import { diffLinesRaw, diffLinesUnified, diffLinesUnified2 } from './diffLines'; +import { diffStringsRaw, diffStringsUnified } from './printDiffs'; +import type { DiffOptions } from './types'; +export type { DiffOptions, DiffOptionsColor } from './types'; +export { diffLinesRaw, diffLinesUnified, diffLinesUnified2 }; +export { diffStringsRaw, diffStringsUnified }; +export { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, Diff }; +declare function diff(a: any, b: any, options?: DiffOptions): string | null; +export default diff; diff --git a/packages/jest-diff/build/index.js b/packages/jest-diff/build/index.js new file mode 100644 index 000000000000..f97489e40887 --- /dev/null +++ b/packages/jest-diff/build/index.js @@ -0,0 +1,300 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'DIFF_DELETE', { + enumerable: true, + get: function () { + return _cleanupSemantic.DIFF_DELETE; + } +}); +Object.defineProperty(exports, 'DIFF_EQUAL', { + enumerable: true, + get: function () { + return _cleanupSemantic.DIFF_EQUAL; + } +}); +Object.defineProperty(exports, 'DIFF_INSERT', { + enumerable: true, + get: function () { + return _cleanupSemantic.DIFF_INSERT; + } +}); +Object.defineProperty(exports, 'Diff', { + enumerable: true, + get: function () { + return _cleanupSemantic.Diff; + } +}); +Object.defineProperty(exports, 'diffLinesRaw', { + enumerable: true, + get: function () { + return _diffLines.diffLinesRaw; + } +}); +Object.defineProperty(exports, 'diffLinesUnified', { + enumerable: true, + get: function () { + return _diffLines.diffLinesUnified; + } +}); +Object.defineProperty(exports, 'diffLinesUnified2', { + enumerable: true, + get: function () { + return _diffLines.diffLinesUnified2; + } +}); +Object.defineProperty(exports, 'diffStringsRaw', { + enumerable: true, + get: function () { + return _printDiffs.diffStringsRaw; + } +}); +Object.defineProperty(exports, 'diffStringsUnified', { + enumerable: true, + get: function () { + return _printDiffs.diffStringsUnified; + } +}); +exports.default = void 0; + +var _chalk = _interopRequireDefault(require('chalk')); + +var _jestGetType = _interopRequireDefault(require('jest-get-type')); + +var _prettyFormat = _interopRequireWildcard(require('pretty-format')); + +var _cleanupSemantic = require('./cleanupSemantic'); + +var _constants = require('./constants'); + +var _diffLines = require('./diffLines'); + +var _normalizeDiffOptions = require('./normalizeDiffOptions'); + +var _printDiffs = require('./printDiffs'); + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; + +const getCommonMessage = (message, options) => { + const {commonColor} = (0, _normalizeDiffOptions.normalizeDiffOptions)( + options + ); + return commonColor(message); +}; + +const { + AsymmetricMatcher, + DOMCollection, + DOMElement, + Immutable, + ReactElement, + ReactTestComponent +} = _prettyFormat.plugins; +const PLUGINS = [ + ReactTestComponent, + ReactElement, + DOMElement, + DOMCollection, + Immutable, + AsymmetricMatcher +]; +const FORMAT_OPTIONS = { + plugins: PLUGINS +}; +const FORMAT_OPTIONS_0 = {...FORMAT_OPTIONS, indent: 0}; +const FALLBACK_FORMAT_OPTIONS = { + callToJSON: false, + maxDepth: 10, + plugins: PLUGINS +}; +const FALLBACK_FORMAT_OPTIONS_0 = {...FALLBACK_FORMAT_OPTIONS, indent: 0}; // Generate a string that will highlight the difference between two values +// with green and red. (similar to how github does code diffing) +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + +function diff(a, b, options) { + if (Object.is(a, b)) { + return getCommonMessage(_constants.NO_DIFF_MESSAGE, options); + } + + const aType = (0, _jestGetType.default)(a); + let expectedType = aType; + let omitDifference = false; + + if (aType === 'object' && typeof a.asymmetricMatch === 'function') { + if (a.$$typeof !== Symbol.for('jest.asymmetricMatcher')) { + // Do not know expected type of user-defined asymmetric matcher. + return null; + } + + if (typeof a.getExpectedType !== 'function') { + // For example, expect.anything() matches either null or undefined + return null; + } + + expectedType = a.getExpectedType(); // Primitive types boolean and number omit difference below. + // For example, omit difference for expect.stringMatching(regexp) + + omitDifference = expectedType === 'string'; + } + + if (expectedType !== (0, _jestGetType.default)(b)) { + return ( + ' Comparing two different types of values.' + + ` Expected ${_chalk.default.green(expectedType)} but ` + + `received ${_chalk.default.red((0, _jestGetType.default)(b))}.` + ); + } + + if (omitDifference) { + return null; + } + + switch (aType) { + case 'string': + return (0, _diffLines.diffLinesUnified)( + a.split('\n'), + b.split('\n'), + options + ); + + case 'boolean': + case 'number': + return comparePrimitive(a, b, options); + + case 'map': + return compareObjects(sortMap(a), sortMap(b), options); + + case 'set': + return compareObjects(sortSet(a), sortSet(b), options); + + default: + return compareObjects(a, b, options); + } +} + +function comparePrimitive(a, b, options) { + const aFormat = (0, _prettyFormat.default)(a, FORMAT_OPTIONS); + const bFormat = (0, _prettyFormat.default)(b, FORMAT_OPTIONS); + return aFormat === bFormat + ? getCommonMessage(_constants.NO_DIFF_MESSAGE, options) + : (0, _diffLines.diffLinesUnified)( + aFormat.split('\n'), + bFormat.split('\n'), + options + ); +} + +function sortMap(map) { + return new Map(Array.from(map.entries()).sort()); +} + +function sortSet(set) { + return new Set(Array.from(set.values()).sort()); +} + +function compareObjects(a, b, options) { + let difference; + let hasThrown = false; + const noDiffMessage = getCommonMessage(_constants.NO_DIFF_MESSAGE, options); + + try { + const aCompare = (0, _prettyFormat.default)(a, FORMAT_OPTIONS_0); + const bCompare = (0, _prettyFormat.default)(b, FORMAT_OPTIONS_0); + + if (aCompare === bCompare) { + difference = noDiffMessage; + } else { + const aDisplay = (0, _prettyFormat.default)(a, FORMAT_OPTIONS); + const bDisplay = (0, _prettyFormat.default)(b, FORMAT_OPTIONS); + difference = (0, _diffLines.diffLinesUnified2)( + aDisplay.split('\n'), + bDisplay.split('\n'), + aCompare.split('\n'), + bCompare.split('\n'), + options + ); + } + } catch { + hasThrown = true; + } // If the comparison yields no results, compare again but this time + // without calling `toJSON`. It's also possible that toJSON might throw. + + if (difference === undefined || difference === noDiffMessage) { + const aCompare = (0, _prettyFormat.default)(a, FALLBACK_FORMAT_OPTIONS_0); + const bCompare = (0, _prettyFormat.default)(b, FALLBACK_FORMAT_OPTIONS_0); + + if (aCompare === bCompare) { + difference = noDiffMessage; + } else { + const aDisplay = (0, _prettyFormat.default)(a, FALLBACK_FORMAT_OPTIONS); + const bDisplay = (0, _prettyFormat.default)(b, FALLBACK_FORMAT_OPTIONS); + difference = (0, _diffLines.diffLinesUnified2)( + aDisplay.split('\n'), + bDisplay.split('\n'), + aCompare.split('\n'), + bCompare.split('\n'), + options + ); + } + + if (difference !== noDiffMessage && !hasThrown) { + difference = + getCommonMessage(_constants.SIMILAR_MESSAGE, options) + + '\n\n' + + difference; + } + } + + return difference; +} + +var _default = diff; +exports.default = _default; diff --git a/packages/jest-diff/build/joinAlignedDiffs.d.ts b/packages/jest-diff/build/joinAlignedDiffs.d.ts new file mode 100644 index 000000000000..80cdf6d96cc8 --- /dev/null +++ b/packages/jest-diff/build/joinAlignedDiffs.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import { Diff } from './cleanupSemantic'; +import type { DiffOptionsNormalized } from './types'; +export declare const joinAlignedDiffsNoExpand: (diffs: Array, options: DiffOptionsNormalized) => string; +export declare const joinAlignedDiffsExpand: (diffs: Array, options: DiffOptionsNormalized) => string; diff --git a/packages/jest-diff/build/joinAlignedDiffs.js b/packages/jest-diff/build/joinAlignedDiffs.js new file mode 100644 index 000000000000..647f7145d2c1 --- /dev/null +++ b/packages/jest-diff/build/joinAlignedDiffs.js @@ -0,0 +1,235 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.joinAlignedDiffsExpand = exports.joinAlignedDiffsNoExpand = void 0; + +var _cleanupSemantic = require('./cleanupSemantic'); + +var _printDiffs = require('./printDiffs'); + +/** + * 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. + */ +// jest --no-expand +// +// Given array of aligned strings with inverse highlight formatting, +// return joined lines with diff formatting (and patch marks, if needed). +const joinAlignedDiffsNoExpand = (diffs, options) => { + const iLength = diffs.length; + const nContextLines = options.contextLines; + const nContextLines2 = nContextLines + nContextLines; // First pass: count output lines and see if it has patches. + + let jLength = iLength; + let hasExcessAtStartOrEnd = false; + let nExcessesBetweenChanges = 0; + let i = 0; + + while (i !== iLength) { + const iStart = i; + + while (i !== iLength && diffs[i][0] === _cleanupSemantic.DIFF_EQUAL) { + i += 1; + } + + if (iStart !== i) { + if (iStart === 0) { + // at start + if (i > nContextLines) { + jLength -= i - nContextLines; // subtract excess common lines + + hasExcessAtStartOrEnd = true; + } + } else if (i === iLength) { + // at end + const n = i - iStart; + + if (n > nContextLines) { + jLength -= n - nContextLines; // subtract excess common lines + + hasExcessAtStartOrEnd = true; + } + } else { + // between changes + const n = i - iStart; + + if (n > nContextLines2) { + jLength -= n - nContextLines2; // subtract excess common lines + + nExcessesBetweenChanges += 1; + } + } + } + + while (i !== iLength && diffs[i][0] !== _cleanupSemantic.DIFF_EQUAL) { + i += 1; + } + } + + const hasPatch = nExcessesBetweenChanges !== 0 || hasExcessAtStartOrEnd; + + if (nExcessesBetweenChanges !== 0) { + jLength += nExcessesBetweenChanges + 1; // add patch lines + } else if (hasExcessAtStartOrEnd) { + jLength += 1; // add patch line + } + + const jLast = jLength - 1; + const lines = []; + let jPatchMark = 0; // index of placeholder line for current patch mark + + if (hasPatch) { + lines.push(''); // placeholder line for first patch mark + } // Indexes of expected or received lines in current patch: + + let aStart = 0; + let bStart = 0; + let aEnd = 0; + let bEnd = 0; + + const pushCommonLine = line => { + const j = lines.length; + lines.push( + (0, _printDiffs.printCommonLine)(line, j === 0 || j === jLast, options) + ); + aEnd += 1; + bEnd += 1; + }; + + const pushDeleteLine = line => { + const j = lines.length; + lines.push( + (0, _printDiffs.printDeleteLine)(line, j === 0 || j === jLast, options) + ); + aEnd += 1; + }; + + const pushInsertLine = line => { + const j = lines.length; + lines.push( + (0, _printDiffs.printInsertLine)(line, j === 0 || j === jLast, options) + ); + bEnd += 1; + }; // Second pass: push lines with diff formatting (and patch marks, if needed). + + i = 0; + + while (i !== iLength) { + let iStart = i; + + while (i !== iLength && diffs[i][0] === _cleanupSemantic.DIFF_EQUAL) { + i += 1; + } + + if (iStart !== i) { + if (iStart === 0) { + // at beginning + if (i > nContextLines) { + iStart = i - nContextLines; + aStart = iStart; + bStart = iStart; + aEnd = aStart; + bEnd = bStart; + } + + for (let iCommon = iStart; iCommon !== i; iCommon += 1) { + pushCommonLine(diffs[iCommon][1]); + } + } else if (i === iLength) { + // at end + const iEnd = i - iStart > nContextLines ? iStart + nContextLines : i; + + for (let iCommon = iStart; iCommon !== iEnd; iCommon += 1) { + pushCommonLine(diffs[iCommon][1]); + } + } else { + // between changes + const nCommon = i - iStart; + + if (nCommon > nContextLines2) { + const iEnd = iStart + nContextLines; + + for (let iCommon = iStart; iCommon !== iEnd; iCommon += 1) { + pushCommonLine(diffs[iCommon][1]); + } + + lines[jPatchMark] = (0, _printDiffs.createPatchMark)( + aStart, + aEnd, + bStart, + bEnd, + options + ); + jPatchMark = lines.length; + lines.push(''); // placeholder line for next patch mark + + const nOmit = nCommon - nContextLines2; + aStart = aEnd + nOmit; + bStart = bEnd + nOmit; + aEnd = aStart; + bEnd = bStart; + + for (let iCommon = i - nContextLines; iCommon !== i; iCommon += 1) { + pushCommonLine(diffs[iCommon][1]); + } + } else { + for (let iCommon = iStart; iCommon !== i; iCommon += 1) { + pushCommonLine(diffs[iCommon][1]); + } + } + } + } + + while (i !== iLength && diffs[i][0] === _cleanupSemantic.DIFF_DELETE) { + pushDeleteLine(diffs[i][1]); + i += 1; + } + + while (i !== iLength && diffs[i][0] === _cleanupSemantic.DIFF_INSERT) { + pushInsertLine(diffs[i][1]); + i += 1; + } + } + + if (hasPatch) { + lines[jPatchMark] = (0, _printDiffs.createPatchMark)( + aStart, + aEnd, + bStart, + bEnd, + options + ); + } + + return lines.join('\n'); +}; // jest --expand +// +// Given array of aligned strings with inverse highlight formatting, +// return joined lines with diff formatting. + +exports.joinAlignedDiffsNoExpand = joinAlignedDiffsNoExpand; + +const joinAlignedDiffsExpand = (diffs, options) => + diffs + .map((diff, i, diffs) => { + const line = diff[1]; + const isFirstOrLast = i === 0 || i === diffs.length - 1; + + switch (diff[0]) { + case _cleanupSemantic.DIFF_DELETE: + return (0, _printDiffs.printDeleteLine)(line, isFirstOrLast, options); + + case _cleanupSemantic.DIFF_INSERT: + return (0, _printDiffs.printInsertLine)(line, isFirstOrLast, options); + + default: + return (0, _printDiffs.printCommonLine)(line, isFirstOrLast, options); + } + }) + .join('\n'); + +exports.joinAlignedDiffsExpand = joinAlignedDiffsExpand; diff --git a/packages/jest-diff/build/normalizeDiffOptions.d.ts b/packages/jest-diff/build/normalizeDiffOptions.d.ts new file mode 100644 index 000000000000..00db0a6100a9 --- /dev/null +++ b/packages/jest-diff/build/normalizeDiffOptions.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { DiffOptions, DiffOptionsNormalized } from './types'; +export declare const noColor: (string: string) => string; +export declare const normalizeDiffOptions: (options?: DiffOptions) => DiffOptionsNormalized; diff --git a/packages/jest-diff/build/normalizeDiffOptions.js b/packages/jest-diff/build/normalizeDiffOptions.js new file mode 100644 index 000000000000..a2c10774dec3 --- /dev/null +++ b/packages/jest-diff/build/normalizeDiffOptions.js @@ -0,0 +1,57 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.normalizeDiffOptions = exports.noColor = void 0; + +var _chalk = _interopRequireDefault(require('chalk')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 noColor = string => string; + +exports.noColor = noColor; +const DIFF_CONTEXT_DEFAULT = 5; +const OPTIONS_DEFAULT = { + aAnnotation: 'Expected', + aColor: _chalk.default.green, + aIndicator: '-', + bAnnotation: 'Received', + bColor: _chalk.default.red, + bIndicator: '+', + changeColor: _chalk.default.inverse, + changeLineTrailingSpaceColor: noColor, + commonColor: _chalk.default.dim, + commonIndicator: ' ', + commonLineTrailingSpaceColor: noColor, + contextLines: DIFF_CONTEXT_DEFAULT, + emptyFirstOrLastLinePlaceholder: '', + expand: true, + includeChangeCounts: false, + omitAnnotationLines: false, + patchColor: _chalk.default.yellow +}; + +const getContextLines = contextLines => + typeof contextLines === 'number' && + Number.isSafeInteger(contextLines) && + contextLines >= 0 + ? contextLines + : DIFF_CONTEXT_DEFAULT; // Pure function returns options with all properties. + +const normalizeDiffOptions = (options = {}) => ({ + ...OPTIONS_DEFAULT, + ...options, + contextLines: getContextLines(options.contextLines) +}); + +exports.normalizeDiffOptions = normalizeDiffOptions; diff --git a/packages/jest-diff/build/printDiffs.d.ts b/packages/jest-diff/build/printDiffs.d.ts new file mode 100644 index 000000000000..5a9c7cb61f5a --- /dev/null +++ b/packages/jest-diff/build/printDiffs.d.ts @@ -0,0 +1,22 @@ +/** + * 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. + */ +import { Diff } from './cleanupSemantic'; +import type { DiffOptions, DiffOptionsNormalized } from './types'; +export declare const printDeleteLine: (line: string, isFirstOrLast: boolean, { aColor, aIndicator, changeLineTrailingSpaceColor, emptyFirstOrLastLinePlaceholder, }: DiffOptionsNormalized) => string; +export declare const printInsertLine: (line: string, isFirstOrLast: boolean, { bColor, bIndicator, changeLineTrailingSpaceColor, emptyFirstOrLastLinePlaceholder, }: DiffOptionsNormalized) => string; +export declare const printCommonLine: (line: string, isFirstOrLast: boolean, { commonColor, commonIndicator, commonLineTrailingSpaceColor, emptyFirstOrLastLinePlaceholder, }: DiffOptionsNormalized) => string; +export declare const hasCommonDiff: (diffs: Array, isMultiline: boolean) => boolean; +export declare type ChangeCounts = { + a: number; + b: number; +}; +export declare const countChanges: (diffs: Array) => ChangeCounts; +export declare const printAnnotation: ({ aAnnotation, aColor, aIndicator, bAnnotation, bColor, bIndicator, includeChangeCounts, omitAnnotationLines, }: DiffOptionsNormalized, changeCounts: ChangeCounts) => string; +export declare const printDiffLines: (diffs: Array, options: DiffOptionsNormalized) => string; +export declare const createPatchMark: (aStart: number, aEnd: number, bStart: number, bEnd: number, { patchColor }: DiffOptionsNormalized) => string; +export declare const diffStringsUnified: (a: string, b: string, options?: DiffOptions | undefined) => string; +export declare const diffStringsRaw: (a: string, b: string, cleanup: boolean) => Array; diff --git a/packages/jest-diff/build/printDiffs.js b/packages/jest-diff/build/printDiffs.js new file mode 100644 index 000000000000..0141ba0a4f39 --- /dev/null +++ b/packages/jest-diff/build/printDiffs.js @@ -0,0 +1,257 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.diffStringsRaw = exports.diffStringsUnified = exports.createPatchMark = exports.printDiffLines = exports.printAnnotation = exports.countChanges = exports.hasCommonDiff = exports.printCommonLine = exports.printInsertLine = exports.printDeleteLine = void 0; + +var _cleanupSemantic = require('./cleanupSemantic'); + +var _diffLines = require('./diffLines'); + +var _diffStrings = _interopRequireDefault(require('./diffStrings')); + +var _getAlignedDiffs = _interopRequireDefault(require('./getAlignedDiffs')); + +var _joinAlignedDiffs = require('./joinAlignedDiffs'); + +var _normalizeDiffOptions = require('./normalizeDiffOptions'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 formatTrailingSpaces = (line, trailingSpaceFormatter) => + line.replace(/\s+$/, match => trailingSpaceFormatter(match)); + +const printDiffLine = ( + line, + isFirstOrLast, + color, + indicator, + trailingSpaceFormatter, + emptyFirstOrLastLinePlaceholder +) => + line.length !== 0 + ? color( + indicator + ' ' + formatTrailingSpaces(line, trailingSpaceFormatter) + ) + : indicator !== ' ' + ? color(indicator) + : isFirstOrLast && emptyFirstOrLastLinePlaceholder.length !== 0 + ? color(indicator + ' ' + emptyFirstOrLastLinePlaceholder) + : ''; + +const printDeleteLine = ( + line, + isFirstOrLast, + { + aColor, + aIndicator, + changeLineTrailingSpaceColor, + emptyFirstOrLastLinePlaceholder + } +) => + printDiffLine( + line, + isFirstOrLast, + aColor, + aIndicator, + changeLineTrailingSpaceColor, + emptyFirstOrLastLinePlaceholder + ); + +exports.printDeleteLine = printDeleteLine; + +const printInsertLine = ( + line, + isFirstOrLast, + { + bColor, + bIndicator, + changeLineTrailingSpaceColor, + emptyFirstOrLastLinePlaceholder + } +) => + printDiffLine( + line, + isFirstOrLast, + bColor, + bIndicator, + changeLineTrailingSpaceColor, + emptyFirstOrLastLinePlaceholder + ); + +exports.printInsertLine = printInsertLine; + +const printCommonLine = ( + line, + isFirstOrLast, + { + commonColor, + commonIndicator, + commonLineTrailingSpaceColor, + emptyFirstOrLastLinePlaceholder + } +) => + printDiffLine( + line, + isFirstOrLast, + commonColor, + commonIndicator, + commonLineTrailingSpaceColor, + emptyFirstOrLastLinePlaceholder + ); + +exports.printCommonLine = printCommonLine; + +const hasCommonDiff = (diffs, isMultiline) => { + if (isMultiline) { + // Important: Ignore common newline that was appended to multiline strings! + const iLast = diffs.length - 1; + return diffs.some( + (diff, i) => + diff[0] === _cleanupSemantic.DIFF_EQUAL && + (i !== iLast || diff[1] !== '\n') + ); + } + + return diffs.some(diff => diff[0] === _cleanupSemantic.DIFF_EQUAL); +}; + +exports.hasCommonDiff = hasCommonDiff; + +const countChanges = diffs => { + let a = 0; + let b = 0; + diffs.forEach(diff => { + switch (diff[0]) { + case _cleanupSemantic.DIFF_DELETE: + a += 1; + break; + + case _cleanupSemantic.DIFF_INSERT: + b += 1; + break; + } + }); + return { + a, + b + }; +}; + +exports.countChanges = countChanges; + +const printAnnotation = ( + { + aAnnotation, + aColor, + aIndicator, + bAnnotation, + bColor, + bIndicator, + includeChangeCounts, + omitAnnotationLines + }, + changeCounts +) => { + if (omitAnnotationLines) { + return ''; + } + + let aRest = ''; + let bRest = ''; + + if (includeChangeCounts) { + const aCount = String(changeCounts.a); + const bCount = String(changeCounts.b); // Padding right aligns the ends of the annotations. + + const baAnnotationLengthDiff = bAnnotation.length - aAnnotation.length; + const aAnnotationPadding = ' '.repeat(Math.max(0, baAnnotationLengthDiff)); + const bAnnotationPadding = ' '.repeat(Math.max(0, -baAnnotationLengthDiff)); // Padding left aligns the ends of the counts. + + const baCountLengthDiff = bCount.length - aCount.length; + const aCountPadding = ' '.repeat(Math.max(0, baCountLengthDiff)); + const bCountPadding = ' '.repeat(Math.max(0, -baCountLengthDiff)); + aRest = + aAnnotationPadding + ' ' + aIndicator + ' ' + aCountPadding + aCount; + bRest = + bAnnotationPadding + ' ' + bIndicator + ' ' + bCountPadding + bCount; + } + + return ( + aColor(aIndicator + ' ' + aAnnotation + aRest) + + '\n' + + bColor(bIndicator + ' ' + bAnnotation + bRest) + + '\n\n' + ); +}; + +exports.printAnnotation = printAnnotation; + +const printDiffLines = (diffs, options) => + printAnnotation(options, countChanges(diffs)) + + (options.expand + ? (0, _joinAlignedDiffs.joinAlignedDiffsExpand)(diffs, options) + : (0, _joinAlignedDiffs.joinAlignedDiffsNoExpand)(diffs, options)); // In GNU diff format, indexes are one-based instead of zero-based. + +exports.printDiffLines = printDiffLines; + +const createPatchMark = (aStart, aEnd, bStart, bEnd, {patchColor}) => + patchColor( + `@@ -${aStart + 1},${aEnd - aStart} +${bStart + 1},${bEnd - bStart} @@` + ); // Compare two strings character-by-character. +// Format as comparison lines in which changed substrings have inverse colors. + +exports.createPatchMark = createPatchMark; + +const diffStringsUnified = (a, b, options) => { + if (a !== b && a.length !== 0 && b.length !== 0) { + const isMultiline = a.includes('\n') || b.includes('\n'); // getAlignedDiffs assumes that a newline was appended to the strings. + + const diffs = diffStringsRaw( + isMultiline ? a + '\n' : a, + isMultiline ? b + '\n' : b, + true // cleanupSemantic + ); + + if (hasCommonDiff(diffs, isMultiline)) { + const optionsNormalized = (0, _normalizeDiffOptions.normalizeDiffOptions)( + options + ); + const lines = (0, _getAlignedDiffs.default)( + diffs, + optionsNormalized.changeColor + ); + return printDiffLines(lines, optionsNormalized); + } + } // Fall back to line-by-line diff. + + return (0, _diffLines.diffLinesUnified)( + a.split('\n'), + b.split('\n'), + options + ); +}; // Compare two strings character-by-character. +// Optionally clean up small common substrings, also known as chaff. + +exports.diffStringsUnified = diffStringsUnified; + +const diffStringsRaw = (a, b, cleanup) => { + const diffs = (0, _diffStrings.default)(a, b); + + if (cleanup) { + (0, _cleanupSemantic.cleanupSemantic)(diffs); // impure function + } + + return diffs; +}; + +exports.diffStringsRaw = diffStringsRaw; diff --git a/packages/jest-diff/build/types.d.ts b/packages/jest-diff/build/types.d.ts new file mode 100644 index 000000000000..03e476d31e17 --- /dev/null +++ b/packages/jest-diff/build/types.d.ts @@ -0,0 +1,45 @@ +/** + * 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. + */ +export declare type DiffOptionsColor = (arg: string) => string; +export declare type DiffOptions = { + aAnnotation?: string; + aColor?: DiffOptionsColor; + aIndicator?: string; + bAnnotation?: string; + bColor?: DiffOptionsColor; + bIndicator?: string; + changeColor?: DiffOptionsColor; + changeLineTrailingSpaceColor?: DiffOptionsColor; + commonColor?: DiffOptionsColor; + commonIndicator?: string; + commonLineTrailingSpaceColor?: DiffOptionsColor; + contextLines?: number; + emptyFirstOrLastLinePlaceholder?: string; + expand?: boolean; + includeChangeCounts?: boolean; + omitAnnotationLines?: boolean; + patchColor?: DiffOptionsColor; +}; +export declare type DiffOptionsNormalized = { + aAnnotation: string; + aColor: DiffOptionsColor; + aIndicator: string; + bAnnotation: string; + bColor: DiffOptionsColor; + bIndicator: string; + changeColor: DiffOptionsColor; + changeLineTrailingSpaceColor: DiffOptionsColor; + commonColor: DiffOptionsColor; + commonIndicator: string; + commonLineTrailingSpaceColor: DiffOptionsColor; + contextLines: number; + emptyFirstOrLastLinePlaceholder: string; + expand: boolean; + includeChangeCounts: boolean; + omitAnnotationLines: boolean; + patchColor: DiffOptionsColor; +}; diff --git a/packages/jest-diff/build/types.js b/packages/jest-diff/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-diff/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-docblock/build/index.d.ts b/packages/jest-docblock/build/index.d.ts new file mode 100644 index 000000000000..c45712128f25 --- /dev/null +++ b/packages/jest-docblock/build/index.d.ts @@ -0,0 +1,19 @@ +/** + * 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. + */ +declare type Pragmas = Record>; +export declare function extract(contents: string): string; +export declare function strip(contents: string): string; +export declare function parse(docblock: string): Pragmas; +export declare function parseWithComments(docblock: string): { + comments: string; + pragmas: Pragmas; +}; +export declare function print({ comments, pragmas, }: { + comments?: string; + pragmas?: Pragmas; +}): string; +export {}; diff --git a/packages/jest-docblock/build/index.js b/packages/jest-docblock/build/index.js new file mode 100644 index 000000000000..a2b89cf753f2 --- /dev/null +++ b/packages/jest-docblock/build/index.js @@ -0,0 +1,152 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.extract = extract; +exports.strip = strip; +exports.parse = parse; +exports.parseWithComments = parseWithComments; +exports.print = print; + +function _os() { + const data = require('os'); + + _os = function () { + return data; + }; + + return data; +} + +function _detectNewline() { + const data = _interopRequireDefault(require('detect-newline')); + + _detectNewline = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 commentEndRe = /\*\/$/; +const commentStartRe = /^\/\*\*/; +const docblockRe = /^\s*(\/\*\*?(.|\r?\n)*?\*\/)/; +const lineCommentRe = /(^|\s+)\/\/([^\r\n]*)/g; +const ltrimNewlineRe = /^(\r?\n)+/; +const multilineRe = /(?:^|\r?\n) *(@[^\r\n]*?) *\r?\n *(?![^@\r\n]*\/\/[^]*)([^@\r\n\s][^@\r\n]+?) *\r?\n/g; +const propertyRe = /(?:^|\r?\n) *@(\S+) *([^\r\n]*)/g; +const stringStartRe = /(\r?\n|^) *\* ?/g; +const STRING_ARRAY = []; + +function extract(contents) { + const match = contents.match(docblockRe); + return match ? match[0].trimLeft() : ''; +} + +function strip(contents) { + const match = contents.match(docblockRe); + return match && match[0] ? contents.substring(match[0].length) : contents; +} + +function parse(docblock) { + return parseWithComments(docblock).pragmas; +} + +function parseWithComments(docblock) { + const line = (0, _detectNewline().default)(docblock) || _os().EOL; + + docblock = docblock + .replace(commentStartRe, '') + .replace(commentEndRe, '') + .replace(stringStartRe, '$1'); // Normalize multi-line directives + + let prev = ''; + + while (prev !== docblock) { + prev = docblock; + docblock = docblock.replace(multilineRe, `${line}$1 $2${line}`); + } + + docblock = docblock.replace(ltrimNewlineRe, '').trimRight(); + const result = Object.create(null); + const comments = docblock + .replace(propertyRe, '') + .replace(ltrimNewlineRe, '') + .trimRight(); + let match; + + while ((match = propertyRe.exec(docblock))) { + // strip linecomments from pragmas + const nextPragma = match[2].replace(lineCommentRe, ''); + + if ( + typeof result[match[1]] === 'string' || + Array.isArray(result[match[1]]) + ) { + result[match[1]] = STRING_ARRAY.concat(result[match[1]], nextPragma); + } else { + result[match[1]] = nextPragma; + } + } + + return { + comments, + pragmas: result + }; +} + +function print({comments = '', pragmas = {}}) { + const line = (0, _detectNewline().default)(comments) || _os().EOL; + + const head = '/**'; + const start = ' *'; + const tail = ' */'; + const keys = Object.keys(pragmas); + const printedObject = keys + .map(key => printKeyValues(key, pragmas[key])) + .reduce((arr, next) => arr.concat(next), []) + .map(keyValue => start + ' ' + keyValue + line) + .join(''); + + if (!comments) { + if (keys.length === 0) { + return ''; + } + + if (keys.length === 1 && !Array.isArray(pragmas[keys[0]])) { + const value = pragmas[keys[0]]; + return `${head} ${printKeyValues(keys[0], value)[0]}${tail}`; + } + } + + const printedComments = + comments + .split(line) + .map(textLine => `${start} ${textLine}`) + .join(line) + line; + return ( + head + + line + + (comments ? printedComments : '') + + (comments && keys.length ? start + line : '') + + printedObject + + tail + ); +} + +function printKeyValues(key, valueOrArray) { + return STRING_ARRAY.concat(valueOrArray).map(value => + `@${key} ${value}`.trim() + ); +} diff --git a/packages/jest-each/build/bind.d.ts b/packages/jest-each/build/bind.d.ts new file mode 100644 index 000000000000..c3a982cc158d --- /dev/null +++ b/packages/jest-each/build/bind.d.ts @@ -0,0 +1,15 @@ +/** + * 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. + * + */ +import type { Global } from '@jest/types'; +export declare type EachTests = Array<{ + title: string; + arguments: Array; +}>; +declare type GlobalCallback = (testName: string, fn: Global.ConcurrentTestFn, timeout?: number) => void; +declare const _default: (cb: GlobalCallback, supportsDone?: boolean) => (table: Global.EachTable, ...taggedTemplateData: Global.TemplateData) => (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; +export default _default; diff --git a/packages/jest-each/build/bind.js b/packages/jest-each/build/bind.js new file mode 100644 index 000000000000..8e782d6d1ffd --- /dev/null +++ b/packages/jest-each/build/bind.js @@ -0,0 +1,79 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _array = _interopRequireDefault(require('./table/array')); + +var _template = _interopRequireDefault(require('./table/template')); + +var _validation = require('./validation'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + * + */ +var _default = (cb, supportsDone = true) => (table, ...taggedTemplateData) => + function eachBind(title, test, timeout) { + try { + const tests = isArrayTable(taggedTemplateData) + ? buildArrayTests(title, table) + : buildTemplateTests(title, table, taggedTemplateData); + return tests.forEach(row => + cb( + row.title, + applyArguments(supportsDone, row.arguments, test), + timeout + ) + ); + } catch (e) { + const error = new (_jestUtil().ErrorWithStack)(e.message, eachBind); + return cb(title, () => { + throw error; + }); + } + }; + +exports.default = _default; + +const isArrayTable = data => data.length === 0; + +const buildArrayTests = (title, table) => { + (0, _validation.validateArrayTable)(table); + return (0, _array.default)(title, table); +}; + +const buildTemplateTests = (title, table, taggedTemplateData) => { + const headings = getHeadingKeys(table[0]); + (0, _validation.validateTemplateTableArguments)(headings, taggedTemplateData); + return (0, _template.default)(title, headings, taggedTemplateData); +}; + +const getHeadingKeys = headings => + (0, _validation.extractValidTemplateHeadings)(headings) + .replace(/\s/g, '') + .split('|'); + +const applyArguments = (supportsDone, params, test) => + supportsDone && params.length < test.length + ? done => test(...params, done) + : () => test(...params); diff --git a/packages/jest-each/build/index.d.ts b/packages/jest-each/build/index.d.ts new file mode 100644 index 000000000000..31fa8d29c12d --- /dev/null +++ b/packages/jest-each/build/index.d.ts @@ -0,0 +1,79 @@ +/** + * 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. + * + */ +import type { Global } from '@jest/types'; +import bind from './bind'; +declare type Global = Global.Global; +declare const install: (g: Global, table: Global.EachTable, ...data: Global.TemplateData) => { + describe: { + (title: string, suite: Global.EachTestFn, timeout?: number | undefined): void; + skip: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + only: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + }; + fdescribe: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + fit: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + it: { + (title: string, test: Global.EachTestFn, timeout?: number | undefined): void; + skip: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + only: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + concurrent: { + (title: string, test: Global.EachTestFn, timeout?: number | undefined): void; + only: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + skip: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + }; + }; + test: { + (title: string, test: Global.EachTestFn, timeout?: number | undefined): void; + skip: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + only: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + concurrent: { + (title: string, test: Global.EachTestFn, timeout?: number | undefined): void; + only: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + skip: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + }; + }; + xdescribe: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + xit: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + xtest: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; +}; +declare const each: { + (table: Global.EachTable, ...data: Global.TemplateData): ReturnType; + withGlobal(g: Global): (table: Global.EachTable, ...data: Global.TemplateData) => { + describe: { + (title: string, suite: Global.EachTestFn, timeout?: number | undefined): void; + skip: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + only: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + }; + fdescribe: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + fit: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + it: { + (title: string, test: Global.EachTestFn, timeout?: number | undefined): void; + skip: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + only: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + concurrent: { + (title: string, test: Global.EachTestFn, timeout?: number | undefined): void; + only: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + skip: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + }; + }; + test: { + (title: string, test: Global.EachTestFn, timeout?: number | undefined): void; + skip: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + only: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + concurrent: { + (title: string, test: Global.EachTestFn, timeout?: number | undefined): void; + only: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + skip: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + }; + }; + xdescribe: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + xit: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + xtest: (title: string, test: Global.EachTestFn, timeout?: number | undefined) => void; + }; +}; +export { bind }; +export default each; diff --git a/packages/jest-each/build/index.js b/packages/jest-each/build/index.js new file mode 100644 index 000000000000..b5cca0f55c42 --- /dev/null +++ b/packages/jest-each/build/index.js @@ -0,0 +1,94 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'bind', { + enumerable: true, + get: function () { + return _bind.default; + } +}); +exports.default = void 0; + +var _bind = _interopRequireDefault(require('./bind')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 install = (g, table, ...data) => { + const bindingWithArray = data.length === 0; + const bindingWithTemplate = Array.isArray(table) && !!table.raw; + + if (!bindingWithArray && !bindingWithTemplate) { + throw new Error( + '`.each` must only be called with an Array or Tagged Template Literal.' + ); + } + + const test = (title, test, timeout) => + (0, _bind.default)(g.test)(table, ...data)(title, test, timeout); + + test.skip = (0, _bind.default)(g.test.skip)(table, ...data); + test.only = (0, _bind.default)(g.test.only)(table, ...data); + + const testConcurrent = (title, test, timeout) => + (0, _bind.default)(g.test.concurrent)(table, ...data)(title, test, timeout); + + test.concurrent = testConcurrent; + testConcurrent.only = (0, _bind.default)(g.test.concurrent.only)( + table, + ...data + ); + testConcurrent.skip = (0, _bind.default)(g.test.concurrent.skip)( + table, + ...data + ); + + const it = (title, test, timeout) => + (0, _bind.default)(g.it)(table, ...data)(title, test, timeout); + + it.skip = (0, _bind.default)(g.it.skip)(table, ...data); + it.only = (0, _bind.default)(g.it.only)(table, ...data); + it.concurrent = testConcurrent; + const xit = (0, _bind.default)(g.xit)(table, ...data); + const fit = (0, _bind.default)(g.fit)(table, ...data); + const xtest = (0, _bind.default)(g.xtest)(table, ...data); + + const describe = (title, suite, timeout) => + (0, _bind.default)(g.describe, false)(table, ...data)( + title, + suite, + timeout + ); + + describe.skip = (0, _bind.default)(g.describe.skip, false)(table, ...data); + describe.only = (0, _bind.default)(g.describe.only, false)(table, ...data); + const fdescribe = (0, _bind.default)(g.fdescribe, false)(table, ...data); + const xdescribe = (0, _bind.default)(g.xdescribe, false)(table, ...data); + return { + describe, + fdescribe, + fit, + it, + test, + xdescribe, + xit, + xtest + }; +}; + +const each = (table, ...data) => install(global, table, ...data); + +each.withGlobal = g => (table, ...data) => install(g, table, ...data); + +var _default = each; +exports.default = _default; diff --git a/packages/jest-each/build/table/array.d.ts b/packages/jest-each/build/table/array.d.ts new file mode 100644 index 000000000000..5fad1d6ab58a --- /dev/null +++ b/packages/jest-each/build/table/array.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + * + */ +import type { Global } from '@jest/types'; +import type { EachTests } from '../bind'; +declare const _default: (title: string, arrayTable: Global.ArrayTable) => EachTests; +export default _default; diff --git a/packages/jest-each/build/table/array.js b/packages/jest-each/build/table/array.js new file mode 100644 index 000000000000..10f150a0458e --- /dev/null +++ b/packages/jest-each/build/table/array.js @@ -0,0 +1,131 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function util() { + const data = _interopRequireWildcard(require('util')); + + util = function () { + return data; + }; + + return data; +} + +function _prettyFormat() { + const data = _interopRequireDefault(require('pretty-format')); + + _prettyFormat = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 SUPPORTED_PLACEHOLDERS = /%[sdifjoOp%]/g; +const PRETTY_PLACEHOLDER = '%p'; +const INDEX_PLACEHOLDER = '%#'; +const PLACEHOLDER_PREFIX = '%'; +const JEST_EACH_PLACEHOLDER_ESCAPE = '@@__JEST_EACH_PLACEHOLDER_ESCAPE__@@'; + +var _default = (title, arrayTable) => + normaliseTable(arrayTable).map((row, index) => ({ + arguments: row, + title: formatTitle(title, row, index) + })); + +exports.default = _default; + +const normaliseTable = table => (isTable(table) ? table : table.map(colToRow)); + +const isTable = table => table.every(Array.isArray); + +const colToRow = col => [col]; + +const formatTitle = (title, row, rowIndex) => + row + .reduce((formattedTitle, value) => { + const [placeholder] = getMatchingPlaceholders(formattedTitle); + const normalisedValue = normalisePlaceholderValue(value); + if (!placeholder) return formattedTitle; + if (placeholder === PRETTY_PLACEHOLDER) + return interpolatePrettyPlaceholder(formattedTitle, normalisedValue); + return util().format(formattedTitle, normalisedValue); + }, interpolateTitleIndex(title, rowIndex)) + .replace(new RegExp(JEST_EACH_PLACEHOLDER_ESCAPE, 'g'), PLACEHOLDER_PREFIX); + +const normalisePlaceholderValue = value => + typeof value === 'string' && SUPPORTED_PLACEHOLDERS.test(value) + ? value.replace(PLACEHOLDER_PREFIX, JEST_EACH_PLACEHOLDER_ESCAPE) + : value; + +const getMatchingPlaceholders = title => + title.match(SUPPORTED_PLACEHOLDERS) || []; + +const interpolateTitleIndex = (title, index) => + title.replace(INDEX_PLACEHOLDER, index.toString()); + +const interpolatePrettyPlaceholder = (title, value) => + title.replace( + PRETTY_PLACEHOLDER, + (0, _prettyFormat().default)(value, { + maxDepth: 1, + min: true + }) + ); diff --git a/packages/jest-each/build/table/template.d.ts b/packages/jest-each/build/table/template.d.ts new file mode 100644 index 000000000000..69ed4855122b --- /dev/null +++ b/packages/jest-each/build/table/template.d.ts @@ -0,0 +1,19 @@ +/** + * 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. + * + */ +import type { Global } from '@jest/types'; +import type { EachTests } from '../bind'; +declare type Template = Record; +declare type Headings = Array; +declare const _default: (title: string, headings: Headings, row: Global.Row) => EachTests; +export default _default; +export declare function getPath(obj: Obj, path: [A, B, C, D, E]): Obj[A][B][C][D][E]; +export declare function getPath(obj: Obj, path: [A, B, C, D]): Obj[A][B][C][D]; +export declare function getPath(obj: Obj, path: [A, B, C]): Obj[A][B][C]; +export declare function getPath(obj: Obj, path: [A, B]): Obj[A][B]; +export declare function getPath(obj: Obj, path: [A]): Obj[A]; +export declare function getPath(obj: Obj, path: Array): unknown; diff --git a/packages/jest-each/build/table/template.js b/packages/jest-each/build/table/template.js new file mode 100644 index 000000000000..1fb903c30230 --- /dev/null +++ b/packages/jest-each/build/table/template.js @@ -0,0 +1,102 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.getPath = getPath; +exports.default = void 0; + +function _jestGetType() { + const data = require('jest-get-type'); + + _jestGetType = function () { + return data; + }; + + return data; +} + +function _prettyFormat() { + const data = _interopRequireDefault(require('pretty-format')); + + _prettyFormat = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + * + */ +var _default = (title, headings, row) => { + const table = convertRowToTable(row, headings); + const templates = convertTableToTemplates(table, headings); + return templates.map(template => ({ + arguments: [template], + title: interpolate(title, template) + })); +}; + +exports.default = _default; + +const convertRowToTable = (row, headings) => + Array.from({ + length: row.length / headings.length + }).map((_, index) => + row.slice( + index * headings.length, + index * headings.length + headings.length + ) + ); + +const convertTableToTemplates = (table, headings) => + table.map(row => + row.reduce( + (acc, value, index) => + Object.assign(acc, { + [headings[index]]: value + }), + {} + ) + ); + +const interpolate = (title, template) => + Object.keys(template) + .reduce(getMatchingKeyPaths(title), []) // aka flatMap + .reduce(replaceKeyPathWithValue(template), title); + +const getMatchingKeyPaths = title => (matches, key) => + matches.concat(title.match(new RegExp(`\\$${key}[\\.\\w]*`, 'g')) || []); + +const replaceKeyPathWithValue = template => (title, match) => { + const keyPath = match.replace('$', '').split('.'); + const value = getPath(template, keyPath); + + if ((0, _jestGetType().isPrimitive)(value)) { + return title.replace(match, String(value)); + } + + return title.replace( + match, + (0, _prettyFormat().default)(value, { + maxDepth: 1, + min: true + }) + ); +}; +/* eslint import/export: 0*/ + +function getPath(template, [head, ...tail]) { + if (!head || !template.hasOwnProperty || !template.hasOwnProperty(head)) + return template; + return getPath(template[head], tail); +} diff --git a/packages/jest-each/build/validation.d.ts b/packages/jest-each/build/validation.d.ts new file mode 100644 index 000000000000..8abfaaafcc46 --- /dev/null +++ b/packages/jest-each/build/validation.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + * + */ +import type { Global } from '@jest/types'; +export declare const validateArrayTable: (table: unknown) => void; +export declare const validateTemplateTableArguments: (headings: Array, data: Global.TemplateData) => void; +export declare const extractValidTemplateHeadings: (headings: string) => string; diff --git a/packages/jest-each/build/validation.js b/packages/jest-each/build/validation.js new file mode 100644 index 000000000000..9d45f29c7af3 --- /dev/null +++ b/packages/jest-each/build/validation.js @@ -0,0 +1,130 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.extractValidTemplateHeadings = exports.validateTemplateTableArguments = exports.validateArrayTable = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _prettyFormat() { + const data = _interopRequireDefault(require('pretty-format')); + + _prettyFormat = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 EXPECTED_COLOR = _chalk().default.green; + +const RECEIVED_COLOR = _chalk().default.red; + +const validateArrayTable = table => { + if (!Array.isArray(table)) { + throw new Error( + '`.each` must be called with an Array or Tagged Template Literal.\n\n' + + `Instead was called with: ${(0, _prettyFormat().default)(table, { + maxDepth: 1, + min: true + })}\n` + ); + } + + if (isTaggedTemplateLiteral(table)) { + if (isEmptyString(table[0])) { + throw new Error( + 'Error: `.each` called with an empty Tagged Template Literal of table data.\n' + ); + } + + throw new Error( + 'Error: `.each` called with a Tagged Template Literal with no data, remember to interpolate with ${expression} syntax.\n' + ); + } + + if (isEmptyTable(table)) { + throw new Error( + 'Error: `.each` called with an empty Array of table data.\n' + ); + } +}; + +exports.validateArrayTable = validateArrayTable; + +const isTaggedTemplateLiteral = array => array.raw !== undefined; + +const isEmptyTable = table => table.length === 0; + +const isEmptyString = str => typeof str === 'string' && str.trim() === ''; + +const validateTemplateTableArguments = (headings, data) => { + const missingData = data.length % headings.length; + + if (missingData > 0) { + throw new Error( + 'Not enough arguments supplied for given headings:\n' + + EXPECTED_COLOR(headings.join(' | ')) + + '\n\n' + + 'Received:\n' + + RECEIVED_COLOR((0, _prettyFormat().default)(data)) + + '\n\n' + + `Missing ${RECEIVED_COLOR(missingData.toString())} ${pluralize( + 'argument', + missingData + )}` + ); + } +}; + +exports.validateTemplateTableArguments = validateTemplateTableArguments; + +const pluralize = (word, count) => word + (count === 1 ? '' : 's'); + +const START_OF_LINE = '^'; +const NEWLINE = '\\n'; +const HEADING = '\\s*\\w+\\s*'; +const PIPE = '\\|'; +const REPEATABLE_HEADING = `(${PIPE}${HEADING})*`; +const HEADINGS_FORMAT = new RegExp( + START_OF_LINE + NEWLINE + HEADING + REPEATABLE_HEADING + NEWLINE, + 'g' +); + +const extractValidTemplateHeadings = headings => { + const matches = headings.match(HEADINGS_FORMAT); + + if (matches === null) { + throw new Error( + 'Table headings do not conform to expected format:\n\n' + + EXPECTED_COLOR('heading1 | headingN') + + '\n\n' + + 'Received:\n\n' + + RECEIVED_COLOR((0, _prettyFormat().default)(headings)) + ); + } + + return matches[0]; +}; + +exports.extractValidTemplateHeadings = extractValidTemplateHeadings; diff --git a/packages/jest-environment-jsdom/build/index.d.ts b/packages/jest-environment-jsdom/build/index.d.ts new file mode 100644 index 000000000000..ff7031517716 --- /dev/null +++ b/packages/jest-environment-jsdom/build/index.d.ts @@ -0,0 +1,34 @@ +/** + * 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. + */ +/// +import type { Context, Script } from 'vm'; +import { JSDOM } from 'jsdom'; +import type { EnvironmentContext, JestEnvironment } from '@jest/environment'; +import { LegacyFakeTimers, ModernFakeTimers } from '@jest/fake-timers'; +import type { Config, Global } from '@jest/types'; +import { ModuleMocker } from 'jest-mock'; +declare type Win = Window & Global.Global & { + Error: { + stackTraceLimit: number; + }; +}; +declare class JSDOMEnvironment implements JestEnvironment { + dom: JSDOM | null; + fakeTimers: LegacyFakeTimers | null; + fakeTimersModern: ModernFakeTimers | null; + global: Win; + errorEventListener: ((event: Event & { + error: Error; + }) => void) | null; + moduleMocker: ModuleMocker | null; + constructor(config: Config.ProjectConfig, options?: EnvironmentContext); + setup(): Promise; + teardown(): Promise; + runScript(script: Script): T | null; + getVmContext(): Context | null; +} +export = JSDOMEnvironment; diff --git a/packages/jest-environment-jsdom/build/index.js b/packages/jest-environment-jsdom/build/index.js new file mode 100644 index 000000000000..dad40d8b0ac9 --- /dev/null +++ b/packages/jest-environment-jsdom/build/index.js @@ -0,0 +1,185 @@ +'use strict'; + +function _jsdom() { + const data = require('jsdom'); + + _jsdom = function () { + return data; + }; + + return data; +} + +function _fakeTimers() { + const data = require('@jest/fake-timers'); + + _fakeTimers = function () { + return data; + }; + + return data; +} + +function _jestMock() { + const data = require('jest-mock'); + + _jestMock = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class JSDOMEnvironment { + constructor(config, options = {}) { + _defineProperty(this, 'dom', void 0); + + _defineProperty(this, 'fakeTimers', void 0); + + _defineProperty(this, 'fakeTimersModern', void 0); + + _defineProperty(this, 'global', void 0); + + _defineProperty(this, 'errorEventListener', void 0); + + _defineProperty(this, 'moduleMocker', void 0); + + this.dom = new (_jsdom().JSDOM)('', { + pretendToBeVisual: true, + runScripts: 'dangerously', + url: config.testURL, + virtualConsole: new (_jsdom().VirtualConsole)().sendTo( + options.console || console + ), + ...config.testEnvironmentOptions + }); + const global = (this.global = this.dom.window.document.defaultView); + + if (!global) { + throw new Error('JSDOM did not return a Window object'); + } // for "universal" code (code should use `globalThis`) + + global.global = global; // Node's error-message stack size is limited at 10, but it's pretty useful + // to see more than that when a test fails. + + this.global.Error.stackTraceLimit = 100; + (0, _jestUtil().installCommonGlobals)(global, config.globals); // Report uncaught errors. + + this.errorEventListener = event => { + if (userErrorListenerCount === 0 && event.error) { + process.emit('uncaughtException', event.error); + } + }; + + global.addEventListener('error', this.errorEventListener); // However, don't report them as uncaught if the user listens to 'error' event. + // In that case, we assume the might have custom error handling logic. + + const originalAddListener = global.addEventListener; + const originalRemoveListener = global.removeEventListener; + let userErrorListenerCount = 0; + + global.addEventListener = function (...args) { + if (args[0] === 'error') { + userErrorListenerCount++; + } + + return originalAddListener.apply(this, args); + }; + + global.removeEventListener = function (...args) { + if (args[0] === 'error') { + userErrorListenerCount--; + } + + return originalRemoveListener.apply(this, args); + }; + + this.moduleMocker = new (_jestMock().ModuleMocker)(global); + const timerConfig = { + idToRef: id => id, + refToId: ref => ref + }; + this.fakeTimers = new (_fakeTimers().LegacyFakeTimers)({ + config, + global, + moduleMocker: this.moduleMocker, + timerConfig + }); + this.fakeTimersModern = new (_fakeTimers().ModernFakeTimers)({ + config, + global + }); + } + + async setup() {} + + async teardown() { + if (this.fakeTimers) { + this.fakeTimers.dispose(); + } + + if (this.fakeTimersModern) { + this.fakeTimersModern.dispose(); + } + + if (this.global) { + if (this.errorEventListener) { + this.global.removeEventListener('error', this.errorEventListener); + } // Dispose "document" to prevent "load" event from triggering. + + Object.defineProperty(this.global, 'document', { + value: null + }); + this.global.close(); + } + + this.errorEventListener = null; // @ts-expect-error + + this.global = null; + this.dom = null; + this.fakeTimers = null; + this.fakeTimersModern = null; + } + + runScript(script) { + if (this.dom) { + return script.runInContext(this.dom.getInternalVMContext()); + } + + return null; + } + + getVmContext() { + if (this.dom) { + return this.dom.getInternalVMContext(); + } + + return null; + } +} + +module.exports = JSDOMEnvironment; diff --git a/packages/jest-environment-node/build/index.d.ts b/packages/jest-environment-node/build/index.d.ts new file mode 100644 index 000000000000..3722e509418a --- /dev/null +++ b/packages/jest-environment-node/build/index.d.ts @@ -0,0 +1,30 @@ +/** + * 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. + */ +/// +import { Context, Script } from 'vm'; +import type { JestEnvironment } from '@jest/environment'; +import { LegacyFakeTimers, ModernFakeTimers } from '@jest/fake-timers'; +import type { Config, Global } from '@jest/types'; +import { ModuleMocker } from 'jest-mock'; +declare type Timer = { + id: number; + ref: () => Timer; + unref: () => Timer; +}; +declare class NodeEnvironment implements JestEnvironment { + context: Context | null; + fakeTimers: LegacyFakeTimers | null; + fakeTimersModern: ModernFakeTimers | null; + global: Global.Global; + moduleMocker: ModuleMocker | null; + constructor(config: Config.ProjectConfig); + setup(): Promise; + teardown(): Promise; + runScript(script: Script): T | null; + getVmContext(): Context | null; +} +export = NodeEnvironment; diff --git a/packages/jest-environment-node/build/index.js b/packages/jest-environment-node/build/index.js new file mode 100644 index 000000000000..da7f37bc8ee7 --- /dev/null +++ b/packages/jest-environment-node/build/index.js @@ -0,0 +1,165 @@ +'use strict'; + +function _vm() { + const data = require('vm'); + + _vm = function () { + return data; + }; + + return data; +} + +function _fakeTimers() { + const data = require('@jest/fake-timers'); + + _fakeTimers = function () { + return data; + }; + + return data; +} + +function _jestMock() { + const data = require('jest-mock'); + + _jestMock = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class NodeEnvironment { + constructor(config) { + _defineProperty(this, 'context', void 0); + + _defineProperty(this, 'fakeTimers', void 0); + + _defineProperty(this, 'fakeTimersModern', void 0); + + _defineProperty(this, 'global', void 0); + + _defineProperty(this, 'moduleMocker', void 0); + + this.context = (0, _vm().createContext)(); + const global = (this.global = (0, _vm().runInContext)( + 'this', + Object.assign(this.context, config.testEnvironmentOptions) + )); + global.global = global; + global.clearInterval = clearInterval; + global.clearTimeout = clearTimeout; + global.setInterval = setInterval; + global.setTimeout = setTimeout; + global.ArrayBuffer = ArrayBuffer; // TextEncoder (global or via 'util') references a Uint8Array constructor + // different than the global one used by users in tests. This makes sure the + // same constructor is referenced by both. + + global.Uint8Array = Uint8Array; // URL and URLSearchParams are global in Node >= 10 + + if (typeof URL !== 'undefined' && typeof URLSearchParams !== 'undefined') { + global.URL = URL; + global.URLSearchParams = URLSearchParams; + } // TextDecoder and TextDecoder are global in Node >= 11 + + if ( + typeof TextEncoder !== 'undefined' && + typeof TextDecoder !== 'undefined' + ) { + global.TextEncoder = TextEncoder; + global.TextDecoder = TextDecoder; + } // queueMicrotask is global in Node >= 11 + + if (typeof queueMicrotask !== 'undefined') { + global.queueMicrotask = queueMicrotask; + } + + (0, _jestUtil().installCommonGlobals)(global, config.globals); + this.moduleMocker = new (_jestMock().ModuleMocker)(global); + + const timerIdToRef = id => ({ + id, + + ref() { + return this; + }, + + unref() { + return this; + } + }); + + const timerRefToId = timer => (timer && timer.id) || undefined; + + const timerConfig = { + idToRef: timerIdToRef, + refToId: timerRefToId + }; + this.fakeTimers = new (_fakeTimers().LegacyFakeTimers)({ + config, + global, + moduleMocker: this.moduleMocker, + timerConfig + }); + this.fakeTimersModern = new (_fakeTimers().ModernFakeTimers)({ + config, + global + }); + } + + async setup() {} + + async teardown() { + if (this.fakeTimers) { + this.fakeTimers.dispose(); + } + + if (this.fakeTimersModern) { + this.fakeTimersModern.dispose(); + } + + this.context = null; + this.fakeTimers = null; + this.fakeTimersModern = null; + } // TS infers the return type to be `any`, since that's what `runInContext` + // returns. + + runScript(script) { + if (this.context) { + return script.runInContext(this.context); + } + + return null; + } + + getVmContext() { + return this.context; + } +} + +module.exports = NodeEnvironment; diff --git a/packages/jest-environment/build/index.d.ts b/packages/jest-environment/build/index.d.ts new file mode 100644 index 000000000000..c7acdf70c342 --- /dev/null +++ b/packages/jest-environment/build/index.d.ts @@ -0,0 +1,261 @@ +/** + * 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. + */ +/// +import type { Context, Script } from 'vm'; +import type { LegacyFakeTimers, ModernFakeTimers } from '@jest/fake-timers'; +import type { Circus, Config, Global } from '@jest/types'; +import type { fn as JestMockFn, spyOn as JestMockSpyOn, ModuleMocker } from 'jest-mock'; +export declare type EnvironmentContext = Partial<{ + console: Console; + docblockPragmas: Record>; + testPath: Config.Path; +}>; +export declare type ModuleWrapper = (this: Module['exports'], module: Module, exports: Module['exports'], require: Module['require'], __dirname: string, __filename: Module['filename'], jest?: Jest, ...extraGlobals: Array) => unknown; +export declare class JestEnvironment { + constructor(config: Config.ProjectConfig, context?: EnvironmentContext); + global: Global.Global; + fakeTimers: LegacyFakeTimers | null; + fakeTimersModern: ModernFakeTimers | null; + moduleMocker: ModuleMocker | null; + /** + * @deprecated implement getVmContext instead + */ + runScript(script: Script): T | null; + getVmContext?(): Context | null; + setup(): Promise; + teardown(): Promise; + handleTestEvent?(event: Circus.Event, state: Circus.State): void | Promise; +} +export declare type Module = NodeModule; +export interface Jest { + /** + * Advances all timers by the needed milliseconds so that only the next timeouts/intervals will run. + * Optionally, you can provide steps, so it will run steps amount of next timeouts/intervals. + */ + advanceTimersToNextTimer(steps?: number): void; + /** + * Disables automatic mocking in the module loader. + */ + autoMockOff(): Jest; + /** + * Enables automatic mocking in the module loader. + */ + autoMockOn(): Jest; + /** + * Clears the mock.calls and mock.instances properties of all mocks. + * Equivalent to calling .mockClear() on every mocked function. + */ + clearAllMocks(): Jest; + /** + * Removes any pending timers from the timer system. If any timers have been + * scheduled, they will be cleared and will never have the opportunity to + * execute in the future. + */ + clearAllTimers(): void; + /** + * Indicates that the module system should never return a mocked version + * of the specified module, including all of the specified module's + * dependencies. + */ + deepUnmock(moduleName: string): Jest; + /** + * Disables automatic mocking in the module loader. + * + * After this method is called, all `require()`s will return the real + * versions of each module (rather than a mocked version). + */ + disableAutomock(): Jest; + /** + * When using `babel-jest`, calls to mock will automatically be hoisted to + * the top of the code block. Use this method if you want to explicitly avoid + * this behavior. + */ + doMock(moduleName: string, moduleFactory?: () => unknown): Jest; + /** + * Indicates that the module system should never return a mocked version + * of the specified module from require() (e.g. that it should always return + * the real module). + */ + dontMock(moduleName: string): Jest; + /** + * Enables automatic mocking in the module loader. + */ + enableAutomock(): Jest; + /** + * Creates a mock function. Optionally takes a mock implementation. + */ + fn: typeof JestMockFn; + /** + * Given the name of a module, use the automatic mocking system to generate a + * mocked version of the module for you. + * + * This is useful when you want to create a manual mock that extends the + * automatic mock's behavior. + * + * @deprecated Use `jest.createMockFromModule()` instead + */ + genMockFromModule(moduleName: string): unknown; + /** + * Given the name of a module, use the automatic mocking system to generate a + * mocked version of the module for you. + * + * This is useful when you want to create a manual mock that extends the + * automatic mock's behavior. + */ + createMockFromModule(moduleName: string): unknown; + /** + * Determines if the given function is a mocked function. + */ + isMockFunction(fn: (...args: Array) => unknown): fn is ReturnType; + /** + * Mocks a module with an auto-mocked version when it is being required. + */ + mock(moduleName: string, moduleFactory?: () => unknown, options?: { + virtual?: boolean; + }): Jest; + /** + * Returns the actual module instead of a mock, bypassing all checks on + * whether the module should receive a mock implementation or not. + * + * @example + ``` + jest.mock('../myModule', () => { + // Require the original module to not be mocked... + const originalModule = jest.requireActual(moduleName); + return { + __esModule: true, // Use it when dealing with esModules + ...originalModule, + getRandom: jest.fn().mockReturnValue(10), + }; + }); + + const getRandom = require('../myModule').getRandom; + + getRandom(); // Always returns 10 + ``` + */ + requireActual: (moduleName: string) => unknown; + /** + * Returns a mock module instead of the actual module, bypassing all checks + * on whether the module should be required normally or not. + */ + requireMock: (moduleName: string) => unknown; + /** + * Resets the state of all mocks. + * Equivalent to calling .mockReset() on every mocked function. + */ + resetAllMocks(): Jest; + /** + * Resets the module registry - the cache of all required modules. This is + * useful to isolate modules where local state might conflict between tests. + */ + resetModules(): Jest; + /** + * Restores all mocks back to their original value. Equivalent to calling + * `.mockRestore` on every mocked function. + * + * Beware that jest.restoreAllMocks() only works when the mock was created with + * jest.spyOn; other mocks will require you to manually restore them. + */ + restoreAllMocks(): Jest; + /** + * Runs failed tests n-times until they pass or until the max number of + * retries is exhausted. This only works with `jest-circus`! + */ + retryTimes(numRetries: number): Jest; + /** + * Exhausts tasks queued by setImmediate(). + * + * > Note: This function is not available when using Lolex as fake timers implementation + */ + runAllImmediates(): void; + /** + * Exhausts the micro-task queue (usually interfaced in node via + * process.nextTick). + */ + runAllTicks(): void; + /** + * Exhausts the macro-task queue (i.e., all tasks queued by setTimeout() + * and setInterval()). + */ + runAllTimers(): void; + /** + * Executes only the macro-tasks that are currently pending (i.e., only the + * tasks that have been queued by setTimeout() or setInterval() up to this + * point). If any of the currently pending macro-tasks schedule new + * macro-tasks, those new tasks will not be executed by this call. + */ + runOnlyPendingTimers(): void; + /** + * Advances all timers by msToRun milliseconds. All pending "macro-tasks" + * that have been queued via setTimeout() or setInterval(), and would be + * executed within this timeframe will be executed. + */ + advanceTimersByTime(msToRun: number): void; + /** + * Returns the number of fake timers still left to run. + */ + getTimerCount(): number; + /** + * Explicitly supplies the mock object that the module system should return + * for the specified module. + * + * Note It is recommended to use `jest.mock()` instead. The `jest.mock` + * API's second argument is a module factory instead of the expected + * exported module object. + */ + setMock(moduleName: string, moduleExports: unknown): Jest; + /** + * Set the default timeout interval for tests and before/after hooks in + * milliseconds. + * + * Note: The default timeout interval is 5 seconds if this method is not + * called. + */ + setTimeout(timeout: number): Jest; + /** + * Creates a mock function similar to `jest.fn` but also tracks calls to + * `object[methodName]`. + * + * Note: By default, jest.spyOn also calls the spied method. This is + * different behavior from most other test libraries. + */ + spyOn: typeof JestMockSpyOn; + /** + * Indicates that the module system should never return a mocked version of + * the specified module from require() (e.g. that it should always return the + * real module). + */ + unmock(moduleName: string): Jest; + /** + * Instructs Jest to use fake versions of the standard timer functions. + */ + useFakeTimers(implementation?: 'modern' | 'legacy'): Jest; + /** + * Instructs Jest to use the real versions of the standard timer functions. + */ + useRealTimers(): Jest; + /** + * `jest.isolateModules(fn)` goes a step further than `jest.resetModules()` + * and creates a sandbox registry for the modules that are loaded inside + * the callback function. This is useful to isolate specific modules for + * every test so that local module state doesn't conflict between tests. + */ + isolateModules(fn: () => void): Jest; + /** + * When mocking time, `Date.now()` will also be mocked. If you for some reason need access to the real current time, you can invoke this function. + * + * > Note: This function is only available when using Lolex as fake timers implementation + */ + getRealSystemTime(): number; + /** + * Set the current system time used by fake timers. Simulates a user changing the system clock while your program is running. It affects the current time but it does not in itself cause e.g. timers to fire; they will fire exactly as they would have done without the call to `jest.setSystemTime()`. + * + * > Note: This function is only available when using Lolex as fake timers implementation + */ + setSystemTime(now?: number | Date): void; +} diff --git a/packages/jest-environment/build/index.js b/packages/jest-environment/build/index.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-environment/build/index.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-fake-timers/build/index.d.ts b/packages/jest-fake-timers/build/index.d.ts new file mode 100644 index 000000000000..5a6e64ca7f04 --- /dev/null +++ b/packages/jest-fake-timers/build/index.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +export { default as LegacyFakeTimers } from './legacyFakeTimers'; +export { default as ModernFakeTimers } from './modernFakeTimers'; diff --git a/packages/jest-fake-timers/build/index.js b/packages/jest-fake-timers/build/index.js new file mode 100644 index 000000000000..5fe1fbe91173 --- /dev/null +++ b/packages/jest-fake-timers/build/index.js @@ -0,0 +1,25 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'LegacyFakeTimers', { + enumerable: true, + get: function () { + return _legacyFakeTimers.default; + } +}); +Object.defineProperty(exports, 'ModernFakeTimers', { + enumerable: true, + get: function () { + return _modernFakeTimers.default; + } +}); + +var _legacyFakeTimers = _interopRequireDefault(require('./legacyFakeTimers')); + +var _modernFakeTimers = _interopRequireDefault(require('./modernFakeTimers')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} diff --git a/packages/jest-fake-timers/build/legacyFakeTimers.d.ts b/packages/jest-fake-timers/build/legacyFakeTimers.d.ts new file mode 100644 index 000000000000..edbc556903bc --- /dev/null +++ b/packages/jest-fake-timers/build/legacyFakeTimers.d.ts @@ -0,0 +1,62 @@ +/** + * 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. + */ +/// +import { StackTraceConfig } from 'jest-message-util'; +import type { ModuleMocker } from 'jest-mock'; +declare type Callback = (...args: Array) => void; +declare type TimerConfig = { + idToRef: (id: number) => Ref; + refToId: (ref: Ref) => number | void; +}; +export default class FakeTimers { + private _cancelledTicks; + private _config; + private _disposed?; + private _fakeTimerAPIs; + private _global; + private _immediates; + private _maxLoops; + private _moduleMocker; + private _now; + private _ticks; + private _timerAPIs; + private _timers; + private _uuidCounter; + private _timerConfig; + constructor({ global, moduleMocker, timerConfig, config, maxLoops, }: { + global: NodeJS.Global; + moduleMocker: ModuleMocker; + timerConfig: TimerConfig; + config: StackTraceConfig; + maxLoops?: number; + }); + clearAllTimers(): void; + dispose(): void; + reset(): void; + runAllTicks(): void; + runAllImmediates(): void; + private _runImmediate; + runAllTimers(): void; + runOnlyPendingTimers(): void; + advanceTimersToNextTimer(steps?: number): void; + advanceTimersByTime(msToRun: number): void; + runWithRealTimers(cb: Callback): void; + useRealTimers(): void; + useFakeTimers(): void; + getTimerCount(): number; + private _checkFakeTimers; + private _createMocks; + private _fakeClearTimer; + private _fakeClearImmediate; + private _fakeNextTick; + private _fakeSetImmediate; + private _fakeSetInterval; + private _fakeSetTimeout; + private _getNextTimerHandle; + private _runTimerHandle; +} +export {}; diff --git a/packages/jest-fake-timers/build/legacyFakeTimers.js b/packages/jest-fake-timers/build/legacyFakeTimers.js new file mode 100644 index 000000000000..cc7fc16ebd66 --- /dev/null +++ b/packages/jest-fake-timers/build/legacyFakeTimers.js @@ -0,0 +1,615 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _util() { + const data = _interopRequireDefault(require('util')); + + _util = function () { + return data; + }; + + return data; +} + +function _jestMessageUtil() { + const data = require('jest-message-util'); + + _jestMessageUtil = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const MS_IN_A_YEAR = 31536000000; + +class FakeTimers { + constructor({global, moduleMocker, timerConfig, config, maxLoops}) { + _defineProperty(this, '_cancelledTicks', void 0); + + _defineProperty(this, '_config', void 0); + + _defineProperty(this, '_disposed', void 0); + + _defineProperty(this, '_fakeTimerAPIs', void 0); + + _defineProperty(this, '_global', void 0); + + _defineProperty(this, '_immediates', void 0); + + _defineProperty(this, '_maxLoops', void 0); + + _defineProperty(this, '_moduleMocker', void 0); + + _defineProperty(this, '_now', void 0); + + _defineProperty(this, '_ticks', void 0); + + _defineProperty(this, '_timerAPIs', void 0); + + _defineProperty(this, '_timers', void 0); + + _defineProperty(this, '_uuidCounter', void 0); + + _defineProperty(this, '_timerConfig', void 0); + + this._global = global; + this._timerConfig = timerConfig; + this._config = config; + this._maxLoops = maxLoops || 100000; + this._uuidCounter = 1; + this._moduleMocker = moduleMocker; // Store original timer APIs for future reference + + this._timerAPIs = { + clearImmediate: global.clearImmediate, + clearInterval: global.clearInterval, + clearTimeout: global.clearTimeout, + nextTick: global.process && global.process.nextTick, + setImmediate: global.setImmediate, + setInterval: global.setInterval, + setTimeout: global.setTimeout + }; + this.reset(); + } + + clearAllTimers() { + this._immediates = []; + + this._timers.clear(); + } + + dispose() { + this._disposed = true; + this.clearAllTimers(); + } + + reset() { + this._cancelledTicks = {}; + this._now = 0; + this._ticks = []; + this._immediates = []; + this._timers = new Map(); + } + + runAllTicks() { + this._checkFakeTimers(); // Only run a generous number of ticks and then bail. + // This is just to help avoid recursive loops + + let i; + + for (i = 0; i < this._maxLoops; i++) { + const tick = this._ticks.shift(); + + if (tick === undefined) { + break; + } + + if (!this._cancelledTicks.hasOwnProperty(tick.uuid)) { + // Callback may throw, so update the map prior calling. + this._cancelledTicks[tick.uuid] = true; + tick.callback(); + } + } + + if (i === this._maxLoops) { + throw new Error( + 'Ran ' + + this._maxLoops + + ' ticks, and there are still more! ' + + "Assuming we've hit an infinite recursion and bailing out..." + ); + } + } + + runAllImmediates() { + this._checkFakeTimers(); // Only run a generous number of immediates and then bail. + + let i; + + for (i = 0; i < this._maxLoops; i++) { + const immediate = this._immediates.shift(); + + if (immediate === undefined) { + break; + } + + this._runImmediate(immediate); + } + + if (i === this._maxLoops) { + throw new Error( + 'Ran ' + + this._maxLoops + + ' immediates, and there are still more! Assuming ' + + "we've hit an infinite recursion and bailing out..." + ); + } + } + + _runImmediate(immediate) { + try { + immediate.callback(); + } finally { + this._fakeClearImmediate(immediate.uuid); + } + } + + runAllTimers() { + this._checkFakeTimers(); + + this.runAllTicks(); + this.runAllImmediates(); // Only run a generous number of timers and then bail. + // This is just to help avoid recursive loops + + let i; + + for (i = 0; i < this._maxLoops; i++) { + const nextTimerHandle = this._getNextTimerHandle(); // If there are no more timer handles, stop! + + if (nextTimerHandle === null) { + break; + } + + this._runTimerHandle(nextTimerHandle); // Some of the immediate calls could be enqueued + // during the previous handling of the timers, we should + // run them as well. + + if (this._immediates.length) { + this.runAllImmediates(); + } + + if (this._ticks.length) { + this.runAllTicks(); + } + } + + if (i === this._maxLoops) { + throw new Error( + 'Ran ' + + this._maxLoops + + ' timers, and there are still more! ' + + "Assuming we've hit an infinite recursion and bailing out..." + ); + } + } + + runOnlyPendingTimers() { + // We need to hold the current shape of `this._timers` because existing + // timers can add new ones to the map and hence would run more than necessary. + // See https://github.com/facebook/jest/pull/4608 for details + const timerEntries = Array.from(this._timers.entries()); + + this._checkFakeTimers(); + + this._immediates.forEach(this._runImmediate, this); + + timerEntries + .sort(([, left], [, right]) => left.expiry - right.expiry) + .forEach(([timerHandle]) => this._runTimerHandle(timerHandle)); + } + + advanceTimersToNextTimer(steps = 1) { + if (steps < 1) { + return; + } + + const nextExpiry = Array.from(this._timers.values()).reduce( + (minExpiry, timer) => { + if (minExpiry === null || timer.expiry < minExpiry) return timer.expiry; + return minExpiry; + }, + null + ); + + if (nextExpiry !== null) { + this.advanceTimersByTime(nextExpiry - this._now); + this.advanceTimersToNextTimer(steps - 1); + } + } + + advanceTimersByTime(msToRun) { + this._checkFakeTimers(); // Only run a generous number of timers and then bail. + // This is just to help avoid recursive loops + + let i; + + for (i = 0; i < this._maxLoops; i++) { + const timerHandle = this._getNextTimerHandle(); // If there are no more timer handles, stop! + + if (timerHandle === null) { + break; + } + + const timerValue = this._timers.get(timerHandle); + + if (timerValue === undefined) { + break; + } + + const nextTimerExpiry = timerValue.expiry; + + if (this._now + msToRun < nextTimerExpiry) { + // There are no timers between now and the target we're running to, so + // adjust our time cursor and quit + this._now += msToRun; + break; + } else { + msToRun -= nextTimerExpiry - this._now; + this._now = nextTimerExpiry; + + this._runTimerHandle(timerHandle); + } + } + + if (i === this._maxLoops) { + throw new Error( + 'Ran ' + + this._maxLoops + + ' timers, and there are still more! ' + + "Assuming we've hit an infinite recursion and bailing out..." + ); + } + } + + runWithRealTimers(cb) { + const prevClearImmediate = this._global.clearImmediate; + const prevClearInterval = this._global.clearInterval; + const prevClearTimeout = this._global.clearTimeout; + const prevNextTick = this._global.process.nextTick; + const prevSetImmediate = this._global.setImmediate; + const prevSetInterval = this._global.setInterval; + const prevSetTimeout = this._global.setTimeout; + this.useRealTimers(); + let cbErr = null; + let errThrown = false; + + try { + cb(); + } catch (e) { + errThrown = true; + cbErr = e; + } + + this._global.clearImmediate = prevClearImmediate; + this._global.clearInterval = prevClearInterval; + this._global.clearTimeout = prevClearTimeout; + this._global.process.nextTick = prevNextTick; + this._global.setImmediate = prevSetImmediate; + this._global.setInterval = prevSetInterval; + this._global.setTimeout = prevSetTimeout; + + if (errThrown) { + throw cbErr; + } + } + + useRealTimers() { + const global = this._global; + (0, _jestUtil().setGlobal)( + global, + 'clearImmediate', + this._timerAPIs.clearImmediate + ); + (0, _jestUtil().setGlobal)( + global, + 'clearInterval', + this._timerAPIs.clearInterval + ); + (0, _jestUtil().setGlobal)( + global, + 'clearTimeout', + this._timerAPIs.clearTimeout + ); + (0, _jestUtil().setGlobal)( + global, + 'setImmediate', + this._timerAPIs.setImmediate + ); + (0, _jestUtil().setGlobal)( + global, + 'setInterval', + this._timerAPIs.setInterval + ); + (0, _jestUtil().setGlobal)( + global, + 'setTimeout', + this._timerAPIs.setTimeout + ); + global.process.nextTick = this._timerAPIs.nextTick; + } + + useFakeTimers() { + this._createMocks(); + + const global = this._global; + (0, _jestUtil().setGlobal)( + global, + 'clearImmediate', + this._fakeTimerAPIs.clearImmediate + ); + (0, _jestUtil().setGlobal)( + global, + 'clearInterval', + this._fakeTimerAPIs.clearInterval + ); + (0, _jestUtil().setGlobal)( + global, + 'clearTimeout', + this._fakeTimerAPIs.clearTimeout + ); + (0, _jestUtil().setGlobal)( + global, + 'setImmediate', + this._fakeTimerAPIs.setImmediate + ); + (0, _jestUtil().setGlobal)( + global, + 'setInterval', + this._fakeTimerAPIs.setInterval + ); + (0, _jestUtil().setGlobal)( + global, + 'setTimeout', + this._fakeTimerAPIs.setTimeout + ); + global.process.nextTick = this._fakeTimerAPIs.nextTick; + } + + getTimerCount() { + this._checkFakeTimers(); + + return this._timers.size + this._immediates.length + this._ticks.length; + } + + _checkFakeTimers() { + var _this$_fakeTimerAPIs; + + if ( + this._global.setTimeout !== + ((_this$_fakeTimerAPIs = this._fakeTimerAPIs) === null || + _this$_fakeTimerAPIs === void 0 + ? void 0 + : _this$_fakeTimerAPIs.setTimeout) + ) { + this._global.console.warn( + `A function to advance timers was called but the timers API is not ` + + `mocked with fake timers. Call \`jest.useFakeTimers()\` in this ` + + `test or enable fake timers globally by setting ` + + `\`"timers": "fake"\` in ` + + `the configuration file. This warning is likely a result of a ` + + `default configuration change in Jest 15.\n\n` + + `Release Blog Post: https://jestjs.io/blog/2016/09/01/jest-15.html\n` + + `Stack Trace:\n` + + (0, _jestMessageUtil().formatStackTrace)( + new Error().stack, + this._config, + { + noStackTrace: false + } + ) + ); + } + } + + _createMocks() { + const fn = ( + impl // @ts-expect-error TODO: figure out better typings here + ) => this._moduleMocker.fn().mockImplementation(impl); + + const promisifiableFakeSetTimeout = fn(this._fakeSetTimeout.bind(this)); // @ts-expect-error TODO: figure out better typings here + + promisifiableFakeSetTimeout[_util().default.promisify.custom] = ( + delay, + arg + ) => + new Promise(resolve => promisifiableFakeSetTimeout(resolve, delay, arg)); // TODO: add better typings; these are mocks, but typed as regular timers + + this._fakeTimerAPIs = { + clearImmediate: fn(this._fakeClearImmediate.bind(this)), + clearInterval: fn(this._fakeClearTimer.bind(this)), + clearTimeout: fn(this._fakeClearTimer.bind(this)), + nextTick: fn(this._fakeNextTick.bind(this)), + // @ts-expect-error TODO: figure out better typings here + setImmediate: fn(this._fakeSetImmediate.bind(this)), + // @ts-expect-error TODO: figure out better typings here + setInterval: fn(this._fakeSetInterval.bind(this)), + // @ts-expect-error TODO: figure out better typings here + setTimeout: promisifiableFakeSetTimeout + }; + } + + _fakeClearTimer(timerRef) { + const uuid = this._timerConfig.refToId(timerRef); + + if (uuid) { + this._timers.delete(String(uuid)); + } + } + + _fakeClearImmediate(uuid) { + this._immediates = this._immediates.filter( + immediate => immediate.uuid !== uuid + ); + } + + _fakeNextTick(callback, ...args) { + if (this._disposed) { + return; + } + + const uuid = String(this._uuidCounter++); + + this._ticks.push({ + callback: () => callback.apply(null, args), + uuid + }); + + const cancelledTicks = this._cancelledTicks; + + this._timerAPIs.nextTick(() => { + if (!cancelledTicks.hasOwnProperty(uuid)) { + // Callback may throw, so update the map prior calling. + cancelledTicks[uuid] = true; + callback.apply(null, args); + } + }); + } + + _fakeSetImmediate(callback, ...args) { + if (this._disposed) { + return null; + } + + const uuid = String(this._uuidCounter++); + + this._immediates.push({ + callback: () => callback.apply(null, args), + uuid + }); + + this._timerAPIs.setImmediate(() => { + if (this._immediates.find(x => x.uuid === uuid)) { + try { + callback.apply(null, args); + } finally { + this._fakeClearImmediate(uuid); + } + } + }); + + return uuid; + } + + _fakeSetInterval(callback, intervalDelay, ...args) { + if (this._disposed) { + return null; + } + + if (intervalDelay == null) { + intervalDelay = 0; + } + + const uuid = this._uuidCounter++; + + this._timers.set(String(uuid), { + callback: () => callback.apply(null, args), + expiry: this._now + intervalDelay, + interval: intervalDelay, + type: 'interval' + }); + + return this._timerConfig.idToRef(uuid); + } + + _fakeSetTimeout(callback, delay, ...args) { + if (this._disposed) { + return null; + } // eslint-disable-next-line no-bitwise + + delay = Number(delay) | 0; + const uuid = this._uuidCounter++; + + this._timers.set(String(uuid), { + callback: () => callback.apply(null, args), + expiry: this._now + delay, + interval: undefined, + type: 'timeout' + }); + + return this._timerConfig.idToRef(uuid); + } + + _getNextTimerHandle() { + let nextTimerHandle = null; + let soonestTime = MS_IN_A_YEAR; + + this._timers.forEach((timer, uuid) => { + if (timer.expiry < soonestTime) { + soonestTime = timer.expiry; + nextTimerHandle = uuid; + } + }); + + return nextTimerHandle; + } + + _runTimerHandle(timerHandle) { + const timer = this._timers.get(timerHandle); + + if (!timer) { + return; + } + + switch (timer.type) { + case 'timeout': + const callback = timer.callback; + + this._timers.delete(timerHandle); + + callback(); + break; + + case 'interval': + timer.expiry = this._now + (timer.interval || 0); + timer.callback(); + break; + + default: + throw new Error('Unexpected timer type: ' + timer.type); + } + } +} + +exports.default = FakeTimers; diff --git a/packages/jest-fake-timers/build/modernFakeTimers.d.ts b/packages/jest-fake-timers/build/modernFakeTimers.d.ts new file mode 100644 index 000000000000..21703a74294b --- /dev/null +++ b/packages/jest-fake-timers/build/modernFakeTimers.d.ts @@ -0,0 +1,35 @@ +/** + * 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. + */ +/// +import { StackTraceConfig } from 'jest-message-util'; +export default class FakeTimers { + private _clock; + private _config; + private _fakingTime; + private _global; + private _fakeTimers; + private _maxLoops; + constructor({ global, config, maxLoops, }: { + global: NodeJS.Global; + config: StackTraceConfig; + maxLoops?: number; + }); + clearAllTimers(): void; + dispose(): void; + runAllTimers(): void; + runOnlyPendingTimers(): void; + advanceTimersToNextTimer(steps?: number): void; + advanceTimersByTime(msToRun: number): void; + runAllTicks(): void; + useRealTimers(): void; + useFakeTimers(): void; + reset(): void; + setSystemTime(now?: number | Date): void; + getRealSystemTime(): number; + getTimerCount(): number; + private _checkFakeTimers; +} diff --git a/packages/jest-fake-timers/build/modernFakeTimers.js b/packages/jest-fake-timers/build/modernFakeTimers.js new file mode 100644 index 000000000000..976ed53a36d3 --- /dev/null +++ b/packages/jest-fake-timers/build/modernFakeTimers.js @@ -0,0 +1,182 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _fakeTimers() { + const data = require('@sinonjs/fake-timers'); + + _fakeTimers = function () { + return data; + }; + + return data; +} + +function _jestMessageUtil() { + const data = require('jest-message-util'); + + _jestMessageUtil = function () { + return data; + }; + + return data; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class FakeTimers { + constructor({global, config, maxLoops}) { + _defineProperty(this, '_clock', void 0); + + _defineProperty(this, '_config', void 0); + + _defineProperty(this, '_fakingTime', void 0); + + _defineProperty(this, '_global', void 0); + + _defineProperty(this, '_fakeTimers', void 0); + + _defineProperty(this, '_maxLoops', void 0); + + this._global = global; + this._config = config; + this._maxLoops = maxLoops || 100000; + this._fakingTime = false; + this._fakeTimers = (0, _fakeTimers().withGlobal)(global); + } + + clearAllTimers() { + if (this._fakingTime) { + this._clock.reset(); + } + } + + dispose() { + this.useRealTimers(); + } + + runAllTimers() { + if (this._checkFakeTimers()) { + this._clock.runAll(); + } + } + + runOnlyPendingTimers() { + if (this._checkFakeTimers()) { + this._clock.runToLast(); + } + } + + advanceTimersToNextTimer(steps = 1) { + if (this._checkFakeTimers()) { + for (let i = steps; i > 0; i--) { + this._clock.next(); // Fire all timers at this point: https://github.com/sinonjs/fake-timers/issues/250 + + this._clock.tick(0); + + if (this._clock.countTimers() === 0) { + break; + } + } + } + } + + advanceTimersByTime(msToRun) { + if (this._checkFakeTimers()) { + this._clock.tick(msToRun); + } + } + + runAllTicks() { + if (this._checkFakeTimers()) { + // @ts-expect-error + this._clock.runMicrotasks(); + } + } + + useRealTimers() { + if (this._fakingTime) { + this._clock.uninstall(); + + this._fakingTime = false; + } + } + + useFakeTimers() { + if (!this._fakingTime) { + const toFake = Object.keys(this._fakeTimers.timers); + this._clock = this._fakeTimers.install({ + loopLimit: this._maxLoops, + now: Date.now(), + target: this._global, + toFake + }); + this._fakingTime = true; + } + } + + reset() { + if (this._checkFakeTimers()) { + const {now} = this._clock; + + this._clock.reset(); + + this._clock.setSystemTime(now); + } + } + + setSystemTime(now) { + if (this._checkFakeTimers()) { + this._clock.setSystemTime(now); + } + } + + getRealSystemTime() { + return Date.now(); + } + + getTimerCount() { + if (this._checkFakeTimers()) { + return this._clock.countTimers(); + } + + return 0; + } + + _checkFakeTimers() { + if (!this._fakingTime) { + this._global.console.warn( + 'A function to advance timers was called but the timers API is not ' + + 'mocked with fake timers. Call `jest.useFakeTimers()` in this test or ' + + 'enable fake timers globally by setting `"timers": "fake"` in the ' + + 'configuration file\nStack Trace:\n' + + (0, _jestMessageUtil().formatStackTrace)( + new Error().stack, + this._config, + { + noStackTrace: false + } + ) + ); + } + + return this._fakingTime; + } +} + +exports.default = FakeTimers; diff --git a/packages/jest-get-type/build/index.d.ts b/packages/jest-get-type/build/index.d.ts new file mode 100644 index 000000000000..83a8be02c74e --- /dev/null +++ b/packages/jest-get-type/build/index.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ +declare type ValueType = 'array' | 'bigint' | 'boolean' | 'function' | 'null' | 'number' | 'object' | 'regexp' | 'map' | 'set' | 'date' | 'string' | 'symbol' | 'undefined'; +declare function getType(value: unknown): ValueType; +declare namespace getType { + var isPrimitive: (value: unknown) => boolean; +} +export = getType; diff --git a/packages/jest-get-type/build/index.js b/packages/jest-get-type/build/index.js new file mode 100644 index 000000000000..1dba77a78613 --- /dev/null +++ b/packages/jest-get-type/build/index.js @@ -0,0 +1,51 @@ +'use strict'; + +/** + * 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. + */ +// get the type of a value with handling the edge cases like `typeof []` +// and `typeof null` +function getType(value) { + if (value === undefined) { + return 'undefined'; + } else if (value === null) { + return 'null'; + } else if (Array.isArray(value)) { + return 'array'; + } else if (typeof value === 'boolean') { + return 'boolean'; + } else if (typeof value === 'function') { + return 'function'; + } else if (typeof value === 'number') { + return 'number'; + } else if (typeof value === 'string') { + return 'string'; + } else if (typeof value === 'bigint') { + return 'bigint'; + } else if (typeof value === 'object') { + if (value != null) { + if (value.constructor === RegExp) { + return 'regexp'; + } else if (value.constructor === Map) { + return 'map'; + } else if (value.constructor === Set) { + return 'set'; + } else if (value.constructor === Date) { + return 'date'; + } + } + + return 'object'; + } else if (typeof value === 'symbol') { + return 'symbol'; + } + + throw new Error(`value of unknown type: ${value}`); +} + +getType.isPrimitive = value => Object(value) !== value; + +module.exports = getType; diff --git a/packages/jest-globals/build/index.d.ts b/packages/jest-globals/build/index.d.ts new file mode 100644 index 000000000000..1cc83b8a4391 --- /dev/null +++ b/packages/jest-globals/build/index.d.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ +import type { Jest } from '@jest/environment'; +import type { Global } from '@jest/types'; +import importedExpect = require('expect'); +export declare const jest: Jest; +export declare const expect: typeof importedExpect; +export declare const it: Global.GlobalAdditions['it']; +export declare const test: Global.GlobalAdditions['test']; +export declare const fit: Global.GlobalAdditions['fit']; +export declare const xit: Global.GlobalAdditions['xit']; +export declare const xtest: Global.GlobalAdditions['xtest']; +export declare const describe: Global.GlobalAdditions['describe']; +export declare const xdescribe: Global.GlobalAdditions['xdescribe']; +export declare const fdescribe: Global.GlobalAdditions['fdescribe']; +export declare const beforeAll: Global.GlobalAdditions['beforeAll']; +export declare const beforeEach: Global.GlobalAdditions['beforeEach']; +export declare const afterEach: Global.GlobalAdditions['afterEach']; +export declare const afterAll: Global.GlobalAdditions['afterAll']; diff --git a/packages/jest-globals/build/index.js b/packages/jest-globals/build/index.js new file mode 100644 index 000000000000..06f90c10cdcf --- /dev/null +++ b/packages/jest-globals/build/index.js @@ -0,0 +1,25 @@ +'use strict'; + +function _expect() { + const data = _interopRequireDefault(require('expect')); + + _expect = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +throw new Error( + 'Do not import `@jest/globals` outside of the Jest test environment' +); diff --git a/packages/jest-haste-map/build/HasteFS.d.ts b/packages/jest-haste-map/build/HasteFS.d.ts new file mode 100644 index 000000000000..93cc4046c1a7 --- /dev/null +++ b/packages/jest-haste-map/build/HasteFS.d.ts @@ -0,0 +1,27 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { FileData } from './types'; +export default class HasteFS { + private readonly _rootDir; + private readonly _files; + constructor({ rootDir, files }: { + rootDir: Config.Path; + files: FileData; + }); + getModuleName(file: Config.Path): string | null; + getSize(file: Config.Path): number | null; + getDependencies(file: Config.Path): Array | null; + getSha1(file: Config.Path): string | null; + exists(file: Config.Path): boolean; + getAllFiles(): Array; + getFileIterator(): Iterable; + getAbsoluteFileIterator(): Iterable; + matchFiles(pattern: RegExp | string): Array; + matchFilesWithGlob(globs: Array, root: Config.Path | null): Set; + private _getFileData; +} diff --git a/packages/jest-haste-map/build/HasteFS.js b/packages/jest-haste-map/build/HasteFS.js new file mode 100644 index 000000000000..5e8670dcaa5b --- /dev/null +++ b/packages/jest-haste-map/build/HasteFS.js @@ -0,0 +1,179 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _constants = _interopRequireDefault(require('./constants')); + +var fastPath = _interopRequireWildcard(require('./lib/fast_path')); + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class HasteFS { + constructor({rootDir, files}) { + _defineProperty(this, '_rootDir', void 0); + + _defineProperty(this, '_files', void 0); + + this._rootDir = rootDir; + this._files = files; + } + + getModuleName(file) { + const fileMetadata = this._getFileData(file); + + return (fileMetadata && fileMetadata[_constants.default.ID]) || null; + } + + getSize(file) { + const fileMetadata = this._getFileData(file); + + return (fileMetadata && fileMetadata[_constants.default.SIZE]) || null; + } + + getDependencies(file) { + const fileMetadata = this._getFileData(file); + + if (fileMetadata) { + return fileMetadata[_constants.default.DEPENDENCIES] + ? fileMetadata[_constants.default.DEPENDENCIES].split( + _constants.default.DEPENDENCY_DELIM + ) + : []; + } else { + return null; + } + } + + getSha1(file) { + const fileMetadata = this._getFileData(file); + + return (fileMetadata && fileMetadata[_constants.default.SHA1]) || null; + } + + exists(file) { + return this._getFileData(file) != null; + } + + getAllFiles() { + return Array.from(this.getAbsoluteFileIterator()); + } + + getFileIterator() { + return this._files.keys(); + } + + *getAbsoluteFileIterator() { + for (const file of this.getFileIterator()) { + yield fastPath.resolve(this._rootDir, file); + } + } + + matchFiles(pattern) { + if (!(pattern instanceof RegExp)) { + pattern = new RegExp(pattern); + } + + const files = []; + + for (const file of this.getAbsoluteFileIterator()) { + if (pattern.test(file)) { + files.push(file); + } + } + + return files; + } + + matchFilesWithGlob(globs, root) { + const files = new Set(); + const matcher = (0, _jestUtil().globsToMatcher)(globs); + + for (const file of this.getAbsoluteFileIterator()) { + const filePath = root ? fastPath.relative(root, file) : file; + + if (matcher((0, _jestUtil().replacePathSepForGlob)(filePath))) { + files.add(file); + } + } + + return files; + } + + _getFileData(file) { + const relativePath = fastPath.relative(this._rootDir, file); + return this._files.get(relativePath); + } +} + +exports.default = HasteFS; diff --git a/packages/jest-haste-map/build/ModuleMap.d.ts b/packages/jest-haste-map/build/ModuleMap.d.ts new file mode 100644 index 000000000000..b9f682dbf524 --- /dev/null +++ b/packages/jest-haste-map/build/ModuleMap.d.ts @@ -0,0 +1,48 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { DuplicatesSet, HTypeValue, MockData, ModuleMapData, RawModuleMap } from './types'; +declare type ValueType = T extends Map ? V : never; +export declare type SerializableModuleMap = { + duplicates: ReadonlyArray<[string, [string, [string, [string, number]]]]>; + map: ReadonlyArray<[string, ValueType]>; + mocks: ReadonlyArray<[string, ValueType]>; + rootDir: Config.Path; +}; +export default class ModuleMap { + static DuplicateHasteCandidatesError: typeof DuplicateHasteCandidatesError; + private readonly _raw; + private json; + private static mapToArrayRecursive; + private static mapFromArrayRecursive; + constructor(raw: RawModuleMap); + getModule(name: string, platform?: string | null, supportsNativePlatform?: boolean | null, type?: HTypeValue | null): Config.Path | null; + getPackage(name: string, platform: string | null | undefined, _supportsNativePlatform: boolean | null): Config.Path | null; + getMockModule(name: string): Config.Path | undefined; + getRawModuleMap(): RawModuleMap; + toJSON(): SerializableModuleMap; + static fromJSON(serializableModuleMap: SerializableModuleMap): ModuleMap; + /** + * When looking up a module's data, we walk through each eligible platform for + * the query. For each platform, we want to check if there are known + * duplicates for that name+platform pair. The duplication logic normally + * removes elements from the `map` object, but we want to check upfront to be + * extra sure. If metadata exists both in the `duplicates` object and the + * `map`, this would be a bug. + */ + private _getModuleMetadata; + private _assertNoDuplicates; + static create(rootDir: Config.Path): ModuleMap; +} +declare class DuplicateHasteCandidatesError extends Error { + hasteName: string; + platform: string | null; + supportsNativePlatform: boolean; + duplicatesSet: DuplicatesSet; + constructor(name: string, platform: string, supportsNativePlatform: boolean, duplicatesSet: DuplicatesSet); +} +export {}; diff --git a/packages/jest-haste-map/build/ModuleMap.js b/packages/jest-haste-map/build/ModuleMap.js new file mode 100644 index 000000000000..1858c5bd98ef --- /dev/null +++ b/packages/jest-haste-map/build/ModuleMap.js @@ -0,0 +1,305 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _constants = _interopRequireDefault(require('./constants')); + +var fastPath = _interopRequireWildcard(require('./lib/fast_path')); + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const EMPTY_OBJ = {}; +const EMPTY_MAP = new Map(); + +class ModuleMap { + static mapToArrayRecursive(map) { + let arr = Array.from(map); + + if (arr[0] && arr[0][1] instanceof Map) { + arr = arr.map(el => [el[0], this.mapToArrayRecursive(el[1])]); + } + + return arr; + } + + static mapFromArrayRecursive(arr) { + if (arr[0] && Array.isArray(arr[1])) { + arr = arr.map(el => [el[0], this.mapFromArrayRecursive(el[1])]); + } + + return new Map(arr); + } + + constructor(raw) { + _defineProperty(this, '_raw', void 0); + + _defineProperty(this, 'json', void 0); + + this._raw = raw; + } + + getModule(name, platform, supportsNativePlatform, type) { + if (type == null) { + type = _constants.default.MODULE; + } + + const module = this._getModuleMetadata( + name, + platform, + !!supportsNativePlatform + ); + + if (module && module[_constants.default.TYPE] === type) { + const modulePath = module[_constants.default.PATH]; + return modulePath && fastPath.resolve(this._raw.rootDir, modulePath); + } + + return null; + } + + getPackage(name, platform, _supportsNativePlatform) { + return this.getModule(name, platform, null, _constants.default.PACKAGE); + } + + getMockModule(name) { + const mockPath = + this._raw.mocks.get(name) || this._raw.mocks.get(name + '/index'); + + return mockPath && fastPath.resolve(this._raw.rootDir, mockPath); + } + + getRawModuleMap() { + return { + duplicates: this._raw.duplicates, + map: this._raw.map, + mocks: this._raw.mocks, + rootDir: this._raw.rootDir + }; + } + + toJSON() { + if (!this.json) { + this.json = { + duplicates: ModuleMap.mapToArrayRecursive(this._raw.duplicates), + map: Array.from(this._raw.map), + mocks: Array.from(this._raw.mocks), + rootDir: this._raw.rootDir + }; + } + + return this.json; + } + + static fromJSON(serializableModuleMap) { + return new ModuleMap({ + duplicates: ModuleMap.mapFromArrayRecursive( + serializableModuleMap.duplicates + ), + map: new Map(serializableModuleMap.map), + mocks: new Map(serializableModuleMap.mocks), + rootDir: serializableModuleMap.rootDir + }); + } + /** + * When looking up a module's data, we walk through each eligible platform for + * the query. For each platform, we want to check if there are known + * duplicates for that name+platform pair. The duplication logic normally + * removes elements from the `map` object, but we want to check upfront to be + * extra sure. If metadata exists both in the `duplicates` object and the + * `map`, this would be a bug. + */ + + _getModuleMetadata(name, platform, supportsNativePlatform) { + const map = this._raw.map.get(name) || EMPTY_OBJ; + const dupMap = this._raw.duplicates.get(name) || EMPTY_MAP; + + if (platform != null) { + this._assertNoDuplicates( + name, + platform, + supportsNativePlatform, + dupMap.get(platform) + ); + + if (map[platform] != null) { + return map[platform]; + } + } + + if (supportsNativePlatform) { + this._assertNoDuplicates( + name, + _constants.default.NATIVE_PLATFORM, + supportsNativePlatform, + dupMap.get(_constants.default.NATIVE_PLATFORM) + ); + + if (map[_constants.default.NATIVE_PLATFORM]) { + return map[_constants.default.NATIVE_PLATFORM]; + } + } + + this._assertNoDuplicates( + name, + _constants.default.GENERIC_PLATFORM, + supportsNativePlatform, + dupMap.get(_constants.default.GENERIC_PLATFORM) + ); + + if (map[_constants.default.GENERIC_PLATFORM]) { + return map[_constants.default.GENERIC_PLATFORM]; + } + + return null; + } + + _assertNoDuplicates(name, platform, supportsNativePlatform, relativePathSet) { + if (relativePathSet == null) { + return; + } // Force flow refinement + + const previousSet = relativePathSet; + const duplicates = new Map(); + + for (const [relativePath, type] of previousSet) { + const duplicatePath = fastPath.resolve(this._raw.rootDir, relativePath); + duplicates.set(duplicatePath, type); + } + + throw new DuplicateHasteCandidatesError( + name, + platform, + supportsNativePlatform, + duplicates + ); + } + + static create(rootDir) { + return new ModuleMap({ + duplicates: new Map(), + map: new Map(), + mocks: new Map(), + rootDir + }); + } +} + +exports.default = ModuleMap; + +_defineProperty(ModuleMap, 'DuplicateHasteCandidatesError', void 0); + +class DuplicateHasteCandidatesError extends Error { + constructor(name, platform, supportsNativePlatform, duplicatesSet) { + const platformMessage = getPlatformMessage(platform); + super( + `The name \`${name}\` was looked up in the Haste module map. It ` + + `cannot be resolved, because there exists several different ` + + `files, or packages, that provide a module for ` + + `that particular name and platform. ${platformMessage} You must ` + + `delete or exclude files until there remains only one of these:\n\n` + + Array.from(duplicatesSet) + .map( + ([dupFilePath, dupFileType]) => + ` * \`${dupFilePath}\` (${getTypeMessage(dupFileType)})\n` + ) + .sort() + .join('') + ); + + _defineProperty(this, 'hasteName', void 0); + + _defineProperty(this, 'platform', void 0); + + _defineProperty(this, 'supportsNativePlatform', void 0); + + _defineProperty(this, 'duplicatesSet', void 0); + + this.hasteName = name; + this.platform = platform; + this.supportsNativePlatform = supportsNativePlatform; + this.duplicatesSet = duplicatesSet; + } +} + +function getPlatformMessage(platform) { + if (platform === _constants.default.GENERIC_PLATFORM) { + return 'The platform is generic (no extension).'; + } + + return `The platform extension is \`${platform}\`.`; +} + +function getTypeMessage(type) { + switch (type) { + case _constants.default.MODULE: + return 'module'; + + case _constants.default.PACKAGE: + return 'package'; + } + + return 'unknown'; +} + +ModuleMap.DuplicateHasteCandidatesError = DuplicateHasteCandidatesError; diff --git a/packages/jest-haste-map/build/blacklist.d.ts b/packages/jest-haste-map/build/blacklist.d.ts new file mode 100644 index 000000000000..c29223113ea4 --- /dev/null +++ b/packages/jest-haste-map/build/blacklist.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +declare const extensions: Set; +export default extensions; diff --git a/packages/jest-haste-map/build/blacklist.js b/packages/jest-haste-map/build/blacklist.js new file mode 100644 index 000000000000..190a3988cfa4 --- /dev/null +++ b/packages/jest-haste-map/build/blacklist.js @@ -0,0 +1,59 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +/** + * 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. + */ +// This list is compiled after the MDN list of the most common MIME types (see +// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/ +// Complete_list_of_MIME_types). +// +// Only MIME types starting with "image/", "video/", "audio/" and "font/" are +// reflected in the list. Adding "application/" is too risky since some text +// file formats (like ".js" and ".json") have an "application/" MIME type. +// +// Feel free to add any extensions that cannot be a Haste module. +const extensions = new Set([ + // JSONs are never haste modules, except for "package.json", which is handled. + '.json', // Image extensions. + '.bmp', + '.gif', + '.ico', + '.jpeg', + '.jpg', + '.png', + '.svg', + '.tiff', + '.tif', + '.webp', // Video extensions. + '.avi', + '.mp4', + '.mpeg', + '.mpg', + '.ogv', + '.webm', + '.3gp', + '.3g2', // Audio extensions. + '.aac', + '.midi', + '.mid', + '.mp3', + '.oga', + '.wav', + '.3gp', + '.3g2', // Font extensions. + '.eot', + '.otf', + '.ttf', + '.woff', + '.woff2' +]); +var _default = extensions; +exports.default = _default; diff --git a/packages/jest-haste-map/build/constants.d.ts b/packages/jest-haste-map/build/constants.d.ts new file mode 100644 index 000000000000..b418969008af --- /dev/null +++ b/packages/jest-haste-map/build/constants.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { HType } from './types'; +declare const constants: HType; +export default constants; diff --git a/packages/jest-haste-map/build/constants.js b/packages/jest-haste-map/build/constants.js new file mode 100644 index 000000000000..2269de4fc3ab --- /dev/null +++ b/packages/jest-haste-map/build/constants.js @@ -0,0 +1,52 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +/** + * 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. + */ + +/* + * This file exports a set of constants that are used for Jest's haste map + * serialization. On very large repositories, the haste map cache becomes very + * large to the point where it is the largest overhead in starting up Jest. + * + * This constant key map allows to keep the map smaller without having to build + * a custom serialization library. + */ + +/* eslint-disable sort-keys */ +const constants = { + /* dependency serialization */ + DEPENDENCY_DELIM: '\0', + + /* file map attributes */ + ID: 0, + MTIME: 1, + SIZE: 2, + VISITED: 3, + DEPENDENCIES: 4, + SHA1: 5, + + /* module map attributes */ + PATH: 0, + TYPE: 1, + + /* module types */ + MODULE: 0, + PACKAGE: 1, + + /* platforms */ + GENERIC_PLATFORM: 'g', + NATIVE_PLATFORM: 'native' +}; +/* eslint-enable */ + +var _default = constants; +exports.default = _default; diff --git a/packages/jest-haste-map/build/crawlers/node.d.ts b/packages/jest-haste-map/build/crawlers/node.d.ts new file mode 100644 index 000000000000..ae0f21d98410 --- /dev/null +++ b/packages/jest-haste-map/build/crawlers/node.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ +import type { CrawlerOptions, FileData, InternalHasteMap } from '../types'; +declare const _default: (options: CrawlerOptions) => Promise<{ + removedFiles: FileData; + hasteMap: InternalHasteMap; +}>; +export = _default; diff --git a/packages/jest-haste-map/build/crawlers/node.js b/packages/jest-haste-map/build/crawlers/node.js new file mode 100644 index 000000000000..1e7372c3d830 --- /dev/null +++ b/packages/jest-haste-map/build/crawlers/node.js @@ -0,0 +1,320 @@ +'use strict'; + +function _child_process() { + const data = require('child_process'); + + _child_process = function () { + return data; + }; + + return data; +} + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = _interopRequireDefault(require('../constants')); + + _constants = function () { + return data; + }; + + return data; +} + +function fastPath() { + const data = _interopRequireWildcard(require('../lib/fast_path')); + + fastPath = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +async function hasNativeFindSupport(forceNodeFilesystemAPI) { + if (forceNodeFilesystemAPI) { + return false; + } + + try { + return await new Promise(resolve => { + // Check the find binary supports the non-POSIX -iname parameter wrapped in parens. + const args = [ + '.', + '-type', + 'f', + '(', + '-iname', + '*.ts', + '-o', + '-iname', + '*.js', + ')' + ]; + const child = (0, _child_process().spawn)('find', args, { + cwd: __dirname + }); + child.on('error', () => { + resolve(false); + }); + child.on('exit', code => { + resolve(code === 0); + }); + }); + } catch { + return false; + } +} + +function find(roots, extensions, ignore, callback) { + const result = []; + let activeCalls = 0; + + function search(directory) { + activeCalls++; + fs().readdir( + directory, + { + withFileTypes: true + }, + (err, entries) => { + activeCalls--; + + if (err) { + callback(result); + return; + } // node < v10.10 does not support the withFileTypes option, and + // entry will be a string. + + entries.forEach(entry => { + const file = path().join( + directory, + typeof entry === 'string' ? entry : entry.name + ); + + if (ignore(file)) { + return; + } + + if (typeof entry !== 'string') { + if (entry.isSymbolicLink()) { + return; + } + + if (entry.isDirectory()) { + search(file); + return; + } + } + + activeCalls++; + fs().lstat(file, (err, stat) => { + activeCalls--; // This logic is unnecessary for node > v10.10, but leaving it in + // since we need it for backwards-compatibility still. + + if (!err && stat && !stat.isSymbolicLink()) { + if (stat.isDirectory()) { + search(file); + } else { + const ext = path().extname(file).substr(1); + + if (extensions.indexOf(ext) !== -1) { + result.push([file, stat.mtime.getTime(), stat.size]); + } + } + } + + if (activeCalls === 0) { + callback(result); + } + }); + }); + + if (activeCalls === 0) { + callback(result); + } + } + ); + } + + if (roots.length > 0) { + roots.forEach(search); + } else { + callback(result); + } +} + +function findNative(roots, extensions, ignore, callback) { + const args = Array.from(roots); + args.push('-type', 'f'); + + if (extensions.length) { + args.push('('); + } + + extensions.forEach((ext, index) => { + if (index) { + args.push('-o'); + } + + args.push('-iname'); + args.push('*.' + ext); + }); + + if (extensions.length) { + args.push(')'); + } + + const child = (0, _child_process().spawn)('find', args); + let stdout = ''; + + if (child.stdout === null) { + throw new Error( + 'stdout is null - this should never happen. Please open up an issue at https://github.com/facebook/jest' + ); + } + + child.stdout.setEncoding('utf-8'); + child.stdout.on('data', data => (stdout += data)); + child.stdout.on('close', () => { + const lines = stdout + .trim() + .split('\n') + .filter(x => !ignore(x)); + const result = []; + let count = lines.length; + + if (!count) { + callback([]); + } else { + lines.forEach(path => { + fs().stat(path, (err, stat) => { + if (!err && stat) { + result.push([path, stat.mtime.getTime(), stat.size]); + } + + if (--count === 0) { + callback(result); + } + }); + }); + } + }); +} + +module.exports = async function nodeCrawl(options) { + const { + data, + extensions, + forceNodeFilesystemAPI, + ignore, + rootDir, + roots + } = options; + const useNativeFind = await hasNativeFindSupport(forceNodeFilesystemAPI); + return new Promise(resolve => { + const callback = list => { + const files = new Map(); + const removedFiles = new Map(data.files); + list.forEach(fileData => { + const [filePath, mtime, size] = fileData; + const relativeFilePath = fastPath().relative(rootDir, filePath); + const existingFile = data.files.get(relativeFilePath); + + if ( + existingFile && + existingFile[_constants().default.MTIME] === mtime + ) { + files.set(relativeFilePath, existingFile); + } else { + // See ../constants.js; SHA-1 will always be null and fulfilled later. + files.set(relativeFilePath, ['', mtime, size, 0, '', null]); + } + + removedFiles.delete(relativeFilePath); + }); + data.files = files; + resolve({ + hasteMap: data, + removedFiles + }); + }; + + if (useNativeFind) { + findNative(roots, extensions, ignore, callback); + } else { + find(roots, extensions, ignore, callback); + } + }); +}; diff --git a/packages/jest-haste-map/build/crawlers/watchman.d.ts b/packages/jest-haste-map/build/crawlers/watchman.d.ts new file mode 100644 index 000000000000..bf261da8ab15 --- /dev/null +++ b/packages/jest-haste-map/build/crawlers/watchman.d.ts @@ -0,0 +1,13 @@ +/** + * 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. + */ +import type { CrawlerOptions, FileData, InternalHasteMap } from '../types'; +declare const _default: (options: CrawlerOptions) => Promise<{ + changedFiles?: FileData | undefined; + removedFiles: FileData; + hasteMap: InternalHasteMap; +}>; +export = _default; diff --git a/packages/jest-haste-map/build/crawlers/watchman.js b/packages/jest-haste-map/build/crawlers/watchman.js new file mode 100644 index 000000000000..7ea275024629 --- /dev/null +++ b/packages/jest-haste-map/build/crawlers/watchman.js @@ -0,0 +1,364 @@ +'use strict'; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _fbWatchman() { + const data = _interopRequireDefault(require('fb-watchman')); + + _fbWatchman = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = _interopRequireDefault(require('../constants')); + + _constants = function () { + return data; + }; + + return data; +} + +function fastPath() { + const data = _interopRequireWildcard(require('../lib/fast_path')); + + fastPath = function () { + return data; + }; + + return data; +} + +function _normalizePathSep() { + const data = _interopRequireDefault(require('../lib/normalizePathSep')); + + _normalizePathSep = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 watchmanURL = 'https://facebook.github.io/watchman/docs/troubleshooting'; + +function WatchmanError(error) { + error.message = + `Watchman error: ${error.message.trim()}. Make sure watchman ` + + `is running for this project. See ${watchmanURL}.`; + return error; +} + +module.exports = async function watchmanCrawl(options) { + const fields = ['name', 'exists', 'mtime_ms', 'size']; + const {data, extensions, ignore, rootDir, roots} = options; + const defaultWatchExpression = [ + 'allof', + ['type', 'f'], + ['anyof', ...extensions.map(extension => ['suffix', extension])] + ]; + const clocks = data.clocks; + const client = new (_fbWatchman().default.Client)(); + let clientError; + client.on('error', error => (clientError = WatchmanError(error))); + + const cmd = (...args) => + new Promise((resolve, reject) => + client.command(args, (error, result) => + error ? reject(WatchmanError(error)) : resolve(result) + ) + ); + + if (options.computeSha1) { + const {capabilities} = await cmd('list-capabilities'); + + if (capabilities.indexOf('field-content.sha1hex') !== -1) { + fields.push('content.sha1hex'); + } + } + + async function getWatchmanRoots(roots) { + const watchmanRoots = new Map(); + await Promise.all( + roots.map(async root => { + const response = await cmd('watch-project', root); + const existing = watchmanRoots.get(response.watch); // A root can only be filtered if it was never seen with a + // relative_path before. + + const canBeFiltered = !existing || existing.length > 0; + + if (canBeFiltered) { + if (response.relative_path) { + watchmanRoots.set( + response.watch, + (existing || []).concat(response.relative_path) + ); + } else { + // Make the filter directories an empty array to signal that this + // root was already seen and needs to be watched for all files or + // directories. + watchmanRoots.set(response.watch, []); + } + } + }) + ); + return watchmanRoots; + } + + async function queryWatchmanForDirs(rootProjectDirMappings) { + const results = new Map(); + let isFresh = false; + await Promise.all( + Array.from(rootProjectDirMappings).map( + async ([root, directoryFilters]) => { + var _since$scm; + + const expression = Array.from(defaultWatchExpression); + const glob = []; + + if (directoryFilters.length > 0) { + expression.push([ + 'anyof', + ...directoryFilters.map(dir => ['dirname', dir]) + ]); + + for (const directory of directoryFilters) { + for (const extension of extensions) { + glob.push(`${directory}/**/*.${extension}`); + } + } + } else { + for (const extension of extensions) { + glob.push(`**/*.${extension}`); + } + } // Jest is only going to store one type of clock; a string that + // represents a local clock. However, the Watchman crawler supports + // a second type of clock that can be written by automation outside of + // Jest, called an "scm query", which fetches changed files based on + // source control mergebases. The reason this is necessary is because + // local clocks are not portable across systems, but scm queries are. + // By using scm queries, we can create the haste map on a different + // system and import it, transforming the clock into a local clock. + + const since = clocks.get(fastPath().relative(rootDir, root)); + const query = + since !== undefined // Use the `since` generator if we have a clock available + ? { + expression, + fields, + since + } // Otherwise use the `glob` filter + : { + expression, + fields, + glob, + glob_includedotfiles: true + }; + const response = await cmd('query', root, query); + + if ('warning' in response) { + console.warn('watchman warning: ', response.warning); + } // When a source-control query is used, we ignore the "is fresh" + // response from Watchman because it will be true despite the query + // being incremental. + + const isSourceControlQuery = + typeof since !== 'string' && + (since === null || since === void 0 + ? void 0 + : (_since$scm = since.scm) === null || _since$scm === void 0 + ? void 0 + : _since$scm['mergebase-with']) !== undefined; + + if (!isSourceControlQuery) { + isFresh = isFresh || response.is_fresh_instance; + } + + results.set(root, response); + } + ) + ); + return { + isFresh, + results + }; + } + + let files = data.files; + let removedFiles = new Map(); + const changedFiles = new Map(); + let results; + let isFresh = false; + + try { + const watchmanRoots = await getWatchmanRoots(roots); + const watchmanFileResults = await queryWatchmanForDirs(watchmanRoots); // Reset the file map if watchman was restarted and sends us a list of + // files. + + if (watchmanFileResults.isFresh) { + files = new Map(); + removedFiles = new Map(data.files); + isFresh = true; + } + + results = watchmanFileResults.results; + } finally { + client.end(); + } + + if (clientError) { + throw clientError; + } + + for (const [watchRoot, response] of results) { + const fsRoot = (0, _normalizePathSep().default)(watchRoot); + const relativeFsRoot = fastPath().relative(rootDir, fsRoot); + clocks.set( + relativeFsRoot, // Ensure we persist only the local clock. + typeof response.clock === 'string' ? response.clock : response.clock.clock + ); + + for (const fileData of response.files) { + const filePath = + fsRoot + path().sep + (0, _normalizePathSep().default)(fileData.name); + const relativeFilePath = fastPath().relative(rootDir, filePath); + const existingFileData = data.files.get(relativeFilePath); // If watchman is fresh, the removed files map starts with all files + // and we remove them as we verify they still exist. + + if (isFresh && existingFileData && fileData.exists) { + removedFiles.delete(relativeFilePath); + } + + if (!fileData.exists) { + // No need to act on files that do not exist and were not tracked. + if (existingFileData) { + files.delete(relativeFilePath); // If watchman is not fresh, we will know what specific files were + // deleted since we last ran and can track only those files. + + if (!isFresh) { + removedFiles.set(relativeFilePath, existingFileData); + } + } + } else if (!ignore(filePath)) { + const mtime = + typeof fileData.mtime_ms === 'number' + ? fileData.mtime_ms + : fileData.mtime_ms.toNumber(); + const size = fileData.size; + let sha1hex = fileData['content.sha1hex']; + + if (typeof sha1hex !== 'string' || sha1hex.length !== 40) { + sha1hex = undefined; + } + + let nextData; + + if ( + existingFileData && + existingFileData[_constants().default.MTIME] === mtime + ) { + nextData = existingFileData; + } else if ( + existingFileData && + sha1hex && + existingFileData[_constants().default.SHA1] === sha1hex + ) { + nextData = [ + existingFileData[0], + mtime, + existingFileData[2], + existingFileData[3], + existingFileData[4], + existingFileData[5] + ]; + } else { + var _sha1hex; + + // See ../constants.ts + nextData = [ + '', + mtime, + size, + 0, + '', + (_sha1hex = sha1hex) !== null && _sha1hex !== void 0 + ? _sha1hex + : null + ]; + } + + files.set(relativeFilePath, nextData); + changedFiles.set(relativeFilePath, nextData); + } + } + } + + data.files = files; + return { + changedFiles: isFresh ? undefined : changedFiles, + hasteMap: data, + removedFiles + }; +}; diff --git a/packages/jest-haste-map/build/getMockName.d.ts b/packages/jest-haste-map/build/getMockName.d.ts new file mode 100644 index 000000000000..dbd08cd16c6b --- /dev/null +++ b/packages/jest-haste-map/build/getMockName.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +declare const getMockName: (filePath: string) => string; +export default getMockName; diff --git a/packages/jest-haste-map/build/getMockName.js b/packages/jest-haste-map/build/getMockName.js new file mode 100644 index 000000000000..f8b6ad15036f --- /dev/null +++ b/packages/jest-haste-map/build/getMockName.js @@ -0,0 +1,76 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 MOCKS_PATTERN = path().sep + '__mocks__' + path().sep; + +const getMockName = filePath => { + const mockPath = filePath.split(MOCKS_PATTERN)[1]; + return mockPath + .substring(0, mockPath.lastIndexOf(path().extname(mockPath))) + .replace(/\\/g, '/'); +}; + +var _default = getMockName; +exports.default = _default; diff --git a/packages/jest-haste-map/build/index.d.ts b/packages/jest-haste-map/build/index.d.ts new file mode 100644 index 000000000000..42c2c09ff6c5 --- /dev/null +++ b/packages/jest-haste-map/build/index.d.ts @@ -0,0 +1,178 @@ +/** + * 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. + */ +/// +import { EventEmitter } from 'events'; +import type { Config } from '@jest/types'; +import HasteModuleMap from './ModuleMap'; +import type { HasteRegExp, InternalHasteMap, HasteMap as InternalHasteMapObject } from './types'; +declare type Options = { + cacheDirectory?: string; + computeDependencies?: boolean; + computeSha1?: boolean; + console?: Console; + dependencyExtractor?: string | null; + extensions: Array; + forceNodeFilesystemAPI?: boolean; + hasteImplModulePath?: string; + ignorePattern?: HasteRegExp; + maxWorkers: number; + mocksPattern?: string; + name: string; + platforms: Array; + resetCache?: boolean; + retainAllFiles: boolean; + rootDir: string; + roots: Array; + skipPackageJson?: boolean; + throwOnModuleCollision?: boolean; + useWatchman?: boolean; + watch?: boolean; +}; +export { default as ModuleMap } from './ModuleMap'; +export type { SerializableModuleMap } from './ModuleMap'; +export type { default as FS } from './HasteFS'; +export type { ChangeEvent, HasteMap as HasteMapObject } from './types'; +/** + * HasteMap is a JavaScript implementation of Facebook's haste module system. + * + * This implementation is inspired by https://github.com/facebook/node-haste + * and was built with for high-performance in large code repositories with + * hundreds of thousands of files. This implementation is scalable and provides + * predictable performance. + * + * Because the haste map creation and synchronization is critical to startup + * performance and most tasks are blocked by I/O this class makes heavy use of + * synchronous operations. It uses worker processes for parallelizing file + * access and metadata extraction. + * + * The data structures created by `jest-haste-map` can be used directly from the + * cache without further processing. The metadata objects in the `files` and + * `map` objects contain cross-references: a metadata object from one can look + * up the corresponding metadata object in the other map. Note that in most + * projects, the number of files will be greater than the number of haste + * modules one module can refer to many files based on platform extensions. + * + * type HasteMap = { + * clocks: WatchmanClocks, + * files: {[filepath: string]: FileMetaData}, + * map: {[id: string]: ModuleMapItem}, + * mocks: {[id: string]: string}, + * } + * + * // Watchman clocks are used for query synchronization and file system deltas. + * type WatchmanClocks = {[filepath: string]: string}; + * + * type FileMetaData = { + * id: ?string, // used to look up module metadata objects in `map`. + * mtime: number, // check for outdated files. + * size: number, // size of the file in bytes. + * visited: boolean, // whether the file has been parsed or not. + * dependencies: Array, // all relative dependencies of this file. + * sha1: ?string, // SHA-1 of the file, if requested via options. + * }; + * + * // Modules can be targeted to a specific platform based on the file name. + * // Example: platform.ios.js and Platform.android.js will both map to the same + * // `Platform` module. The platform should be specified during resolution. + * type ModuleMapItem = {[platform: string]: ModuleMetaData}; + * + * // + * type ModuleMetaData = { + * path: string, // the path to look up the file object in `files`. + * type: string, // the module type (either `package` or `module`). + * }; + * + * Note that the data structures described above are conceptual only. The actual + * implementation uses arrays and constant keys for metadata storage. Instead of + * `{id: 'flatMap', mtime: 3421, size: 42, visited: true, dependencies: []}` the real + * representation is similar to `['flatMap', 3421, 42, 1, []]` to save storage space + * and reduce parse and write time of a big JSON blob. + * + * The HasteMap is created as follows: + * 1. read data from the cache or create an empty structure. + * + * 2. crawl the file system. + * * empty cache: crawl the entire file system. + * * cache available: + * * if watchman is available: get file system delta changes. + * * if watchman is unavailable: crawl the entire file system. + * * build metadata objects for every file. This builds the `files` part of + * the `HasteMap`. + * + * 3. parse and extract metadata from changed files. + * * this is done in parallel over worker processes to improve performance. + * * the worst case is to parse all files. + * * the best case is no file system access and retrieving all data from + * the cache. + * * the average case is a small number of changed files. + * + * 4. serialize the new `HasteMap` in a cache file. + * Worker processes can directly access the cache through `HasteMap.read()`. + * + */ +export default class HasteMap extends EventEmitter { + private _buildPromise; + private _cachePath; + private _changeInterval?; + private _console; + private _options; + private _watchers; + private _worker; + constructor(options: Options); + static getCacheFilePath(tmpdir: Config.Path, name: string, ...extra: Array): string; + getCacheFilePath(): string; + build(): Promise; + /** + * 1. read data from the cache or create an empty structure. + */ + read(): InternalHasteMap; + readModuleMap(): HasteModuleMap; + /** + * 2. crawl the file system. + */ + private _buildFileMap; + /** + * 3. parse and extract metadata from changed files. + */ + private _processFile; + private _buildHasteMap; + private _cleanup; + /** + * 4. serialize the new `HasteMap` in a cache file. + */ + private _persist; + /** + * Creates workers or parses files and extracts metadata in-process. + */ + private _getWorker; + private _crawl; + /** + * Watch mode + */ + private _watch; + /** + * This function should be called when the file under `filePath` is removed + * or changed. When that happens, we want to figure out if that file was + * part of a group of files that had the same ID. If it was, we want to + * remove it from the group. Furthermore, if there is only one file + * remaining in the group, then we want to restore that single file as the + * correct resolution for its ID, and cleanup the duplicates index. + */ + private _recoverDuplicates; + end(): Promise; + /** + * Helpers + */ + private _ignore; + private _createEmptyMap; + static H: import("./types").HType; +} +export declare class DuplicateError extends Error { + mockPath1: string; + mockPath2: string; + constructor(mockPath1: string, mockPath2: string); +} diff --git a/packages/jest-haste-map/build/index.js b/packages/jest-haste-map/build/index.js new file mode 100644 index 000000000000..06f4d4f80543 --- /dev/null +++ b/packages/jest-haste-map/build/index.js @@ -0,0 +1,1232 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'ModuleMap', { + enumerable: true, + get: function () { + return _ModuleMap.default; + } +}); +exports.DuplicateError = exports.default = void 0; + +function _child_process() { + const data = require('child_process'); + + _child_process = function () { + return data; + }; + + return data; +} + +function _crypto() { + const data = require('crypto'); + + _crypto = function () { + return data; + }; + + return data; +} + +function _events() { + const data = require('events'); + + _events = function () { + return data; + }; + + return data; +} + +function _os() { + const data = require('os'); + + _os = function () { + return data; + }; + + return data; +} + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _jestRegexUtil() { + const data = require('jest-regex-util'); + + _jestRegexUtil = function () { + return data; + }; + + return data; +} + +function _jestSerializer() { + const data = _interopRequireDefault(require('jest-serializer')); + + _jestSerializer = function () { + return data; + }; + + return data; +} + +function _jestWorker() { + const data = require('jest-worker'); + + _jestWorker = function () { + return data; + }; + + return data; +} + +var _HasteFS = _interopRequireDefault(require('./HasteFS')); + +var _ModuleMap = _interopRequireDefault(require('./ModuleMap')); + +var _constants = _interopRequireDefault(require('./constants')); + +var _node = _interopRequireDefault(require('./crawlers/node')); + +var _watchman = _interopRequireDefault(require('./crawlers/watchman')); + +var _getMockName = _interopRequireDefault(require('./getMockName')); + +var fastPath = _interopRequireWildcard(require('./lib/fast_path')); + +var _getPlatformExtension = _interopRequireDefault( + require('./lib/getPlatformExtension') +); + +var _normalizePathSep = _interopRequireDefault( + require('./lib/normalizePathSep') +); + +var _FSEventsWatcher = _interopRequireDefault( + require('./watchers/FSEventsWatcher') +); + +var _NodeWatcher = _interopRequireDefault(require('./watchers/NodeWatcher')); + +var _WatchmanWatcher = _interopRequireDefault( + require('./watchers/WatchmanWatcher') +); + +var _worker = require('./worker'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +// TypeScript doesn't like us importing from outside `rootDir`, but it doesn't +// understand `require`. +const {version: VERSION} = require('../package.json'); + +const CHANGE_INTERVAL = 30; +const MAX_WAIT_TIME = 240000; +const NODE_MODULES = path().sep + 'node_modules' + path().sep; +const PACKAGE_JSON = path().sep + 'package.json'; +const VCS_DIRECTORIES = ['.git', '.hg'] + .map(vcs => + (0, _jestRegexUtil().escapePathForRegex)(path().sep + vcs + path().sep) + ) + .join('|'); + +const canUseWatchman = (() => { + try { + (0, _child_process().execSync)('watchman --version', { + stdio: ['ignore'] + }); + return true; + } catch {} + + return false; +})(); + +function invariant(condition, message) { + if (!condition) { + throw new Error(message); + } +} +/** + * HasteMap is a JavaScript implementation of Facebook's haste module system. + * + * This implementation is inspired by https://github.com/facebook/node-haste + * and was built with for high-performance in large code repositories with + * hundreds of thousands of files. This implementation is scalable and provides + * predictable performance. + * + * Because the haste map creation and synchronization is critical to startup + * performance and most tasks are blocked by I/O this class makes heavy use of + * synchronous operations. It uses worker processes for parallelizing file + * access and metadata extraction. + * + * The data structures created by `jest-haste-map` can be used directly from the + * cache without further processing. The metadata objects in the `files` and + * `map` objects contain cross-references: a metadata object from one can look + * up the corresponding metadata object in the other map. Note that in most + * projects, the number of files will be greater than the number of haste + * modules one module can refer to many files based on platform extensions. + * + * type HasteMap = { + * clocks: WatchmanClocks, + * files: {[filepath: string]: FileMetaData}, + * map: {[id: string]: ModuleMapItem}, + * mocks: {[id: string]: string}, + * } + * + * // Watchman clocks are used for query synchronization and file system deltas. + * type WatchmanClocks = {[filepath: string]: string}; + * + * type FileMetaData = { + * id: ?string, // used to look up module metadata objects in `map`. + * mtime: number, // check for outdated files. + * size: number, // size of the file in bytes. + * visited: boolean, // whether the file has been parsed or not. + * dependencies: Array, // all relative dependencies of this file. + * sha1: ?string, // SHA-1 of the file, if requested via options. + * }; + * + * // Modules can be targeted to a specific platform based on the file name. + * // Example: platform.ios.js and Platform.android.js will both map to the same + * // `Platform` module. The platform should be specified during resolution. + * type ModuleMapItem = {[platform: string]: ModuleMetaData}; + * + * // + * type ModuleMetaData = { + * path: string, // the path to look up the file object in `files`. + * type: string, // the module type (either `package` or `module`). + * }; + * + * Note that the data structures described above are conceptual only. The actual + * implementation uses arrays and constant keys for metadata storage. Instead of + * `{id: 'flatMap', mtime: 3421, size: 42, visited: true, dependencies: []}` the real + * representation is similar to `['flatMap', 3421, 42, 1, []]` to save storage space + * and reduce parse and write time of a big JSON blob. + * + * The HasteMap is created as follows: + * 1. read data from the cache or create an empty structure. + * + * 2. crawl the file system. + * * empty cache: crawl the entire file system. + * * cache available: + * * if watchman is available: get file system delta changes. + * * if watchman is unavailable: crawl the entire file system. + * * build metadata objects for every file. This builds the `files` part of + * the `HasteMap`. + * + * 3. parse and extract metadata from changed files. + * * this is done in parallel over worker processes to improve performance. + * * the worst case is to parse all files. + * * the best case is no file system access and retrieving all data from + * the cache. + * * the average case is a small number of changed files. + * + * 4. serialize the new `HasteMap` in a cache file. + * Worker processes can directly access the cache through `HasteMap.read()`. + * + */ + +class HasteMap extends _events().EventEmitter { + constructor(options) { + super(); + + _defineProperty(this, '_buildPromise', void 0); + + _defineProperty(this, '_cachePath', void 0); + + _defineProperty(this, '_changeInterval', void 0); + + _defineProperty(this, '_console', void 0); + + _defineProperty(this, '_options', void 0); + + _defineProperty(this, '_watchers', void 0); + + _defineProperty(this, '_worker', void 0); + + this._options = { + cacheDirectory: options.cacheDirectory || (0, _os().tmpdir)(), + computeDependencies: + options.computeDependencies === undefined + ? true + : options.computeDependencies, + computeSha1: options.computeSha1 || false, + dependencyExtractor: options.dependencyExtractor || null, + extensions: options.extensions, + forceNodeFilesystemAPI: !!options.forceNodeFilesystemAPI, + hasteImplModulePath: options.hasteImplModulePath, + maxWorkers: options.maxWorkers, + mocksPattern: options.mocksPattern + ? new RegExp(options.mocksPattern) + : null, + name: options.name, + platforms: options.platforms, + resetCache: options.resetCache, + retainAllFiles: options.retainAllFiles, + rootDir: options.rootDir, + roots: Array.from(new Set(options.roots)), + skipPackageJson: !!options.skipPackageJson, + throwOnModuleCollision: !!options.throwOnModuleCollision, + useWatchman: options.useWatchman == null ? true : options.useWatchman, + watch: !!options.watch + }; + this._console = options.console || global.console; + + if (options.ignorePattern) { + if (options.ignorePattern instanceof RegExp) { + this._options.ignorePattern = new RegExp( + options.ignorePattern.source.concat('|' + VCS_DIRECTORIES), + options.ignorePattern.flags + ); + } else { + throw new Error( + 'jest-haste-map: the `ignorePattern` option must be a RegExp' + ); + } + } else { + this._options.ignorePattern = new RegExp(VCS_DIRECTORIES); + } + + const rootDirHash = (0, _crypto().createHash)('md5') + .update(options.rootDir) + .digest('hex'); + let hasteImplHash = ''; + let dependencyExtractorHash = ''; + + if (options.hasteImplModulePath) { + const hasteImpl = require(options.hasteImplModulePath); + + if (hasteImpl.getCacheKey) { + hasteImplHash = String(hasteImpl.getCacheKey()); + } + } + + if (options.dependencyExtractor) { + const dependencyExtractor = require(options.dependencyExtractor); + + if (dependencyExtractor.getCacheKey) { + dependencyExtractorHash = String(dependencyExtractor.getCacheKey()); + } + } + + this._cachePath = HasteMap.getCacheFilePath( + this._options.cacheDirectory, + `haste-map-${this._options.name}-${rootDirHash}`, + VERSION, + this._options.name, + this._options.roots + .map(root => fastPath.relative(options.rootDir, root)) + .join(':'), + this._options.extensions.join(':'), + this._options.platforms.join(':'), + this._options.computeSha1.toString(), + options.mocksPattern || '', + (options.ignorePattern || '').toString(), + hasteImplHash, + dependencyExtractorHash + ); + this._buildPromise = null; + this._watchers = []; + this._worker = null; + } + + static getCacheFilePath(tmpdir, name, ...extra) { + const hash = (0, _crypto().createHash)('md5').update(extra.join('')); + return path().join( + tmpdir, + name.replace(/\W/g, '-') + '-' + hash.digest('hex') + ); + } + + getCacheFilePath() { + return this._cachePath; + } + + build() { + if (!this._buildPromise) { + this._buildPromise = (async () => { + const data = await this._buildFileMap(); // Persist when we don't know if files changed (changedFiles undefined) + // or when we know a file was changed or deleted. + + let hasteMap; + + if ( + data.changedFiles === undefined || + data.changedFiles.size > 0 || + data.removedFiles.size > 0 + ) { + hasteMap = await this._buildHasteMap(data); + + this._persist(hasteMap); + } else { + hasteMap = data.hasteMap; + } + + const rootDir = this._options.rootDir; + const hasteFS = new _HasteFS.default({ + files: hasteMap.files, + rootDir + }); + const moduleMap = new _ModuleMap.default({ + duplicates: hasteMap.duplicates, + map: hasteMap.map, + mocks: hasteMap.mocks, + rootDir + }); + + const __hasteMapForTest = + (process.env.NODE_ENV === 'test' && hasteMap) || null; + + await this._watch(hasteMap); + return { + __hasteMapForTest, + hasteFS, + moduleMap + }; + })(); + } + + return this._buildPromise; + } + /** + * 1. read data from the cache or create an empty structure. + */ + + read() { + let hasteMap; + + try { + hasteMap = _jestSerializer().default.readFileSync(this._cachePath); + } catch { + hasteMap = this._createEmptyMap(); + } + + return hasteMap; + } + + readModuleMap() { + const data = this.read(); + return new _ModuleMap.default({ + duplicates: data.duplicates, + map: data.map, + mocks: data.mocks, + rootDir: this._options.rootDir + }); + } + /** + * 2. crawl the file system. + */ + + async _buildFileMap() { + let hasteMap; + + try { + const read = this._options.resetCache ? this._createEmptyMap : this.read; + hasteMap = await read.call(this); + } catch { + hasteMap = this._createEmptyMap(); + } + + return this._crawl(hasteMap); + } + /** + * 3. parse and extract metadata from changed files. + */ + + _processFile(hasteMap, map, mocks, filePath, workerOptions) { + const rootDir = this._options.rootDir; + + const setModule = (id, module) => { + let moduleMap = map.get(id); + + if (!moduleMap) { + moduleMap = Object.create(null); + map.set(id, moduleMap); + } + + const platform = + (0, _getPlatformExtension.default)( + module[_constants.default.PATH], + this._options.platforms + ) || _constants.default.GENERIC_PLATFORM; + + const existingModule = moduleMap[platform]; + + if ( + existingModule && + existingModule[_constants.default.PATH] !== + module[_constants.default.PATH] + ) { + const method = this._options.throwOnModuleCollision ? 'error' : 'warn'; + + this._console[method]( + [ + 'jest-haste-map: Haste module naming collision: ' + id, + ' The following files share their name; please adjust your hasteImpl:', + ' * ' + + path().sep + + existingModule[_constants.default.PATH], + ' * ' + path().sep + module[_constants.default.PATH], + '' + ].join('\n') + ); + + if (this._options.throwOnModuleCollision) { + throw new DuplicateError( + existingModule[_constants.default.PATH], + module[_constants.default.PATH] + ); + } // We do NOT want consumers to use a module that is ambiguous. + + delete moduleMap[platform]; + + if (Object.keys(moduleMap).length === 1) { + map.delete(id); + } + + let dupsByPlatform = hasteMap.duplicates.get(id); + + if (dupsByPlatform == null) { + dupsByPlatform = new Map(); + hasteMap.duplicates.set(id, dupsByPlatform); + } + + const dups = new Map([ + [module[_constants.default.PATH], module[_constants.default.TYPE]], + [ + existingModule[_constants.default.PATH], + existingModule[_constants.default.TYPE] + ] + ]); + dupsByPlatform.set(platform, dups); + return; + } + + const dupsByPlatform = hasteMap.duplicates.get(id); + + if (dupsByPlatform != null) { + const dups = dupsByPlatform.get(platform); + + if (dups != null) { + dups.set( + module[_constants.default.PATH], + module[_constants.default.TYPE] + ); + } + + return; + } + + moduleMap[platform] = module; + }; + + const relativeFilePath = fastPath.relative(rootDir, filePath); + const fileMetadata = hasteMap.files.get(relativeFilePath); + + if (!fileMetadata) { + throw new Error( + 'jest-haste-map: File to process was not found in the haste map.' + ); + } + + const moduleMetadata = hasteMap.map.get( + fileMetadata[_constants.default.ID] + ); + const computeSha1 = + this._options.computeSha1 && !fileMetadata[_constants.default.SHA1]; // Callback called when the response from the worker is successful. + + const workerReply = metadata => { + // `1` for truthy values instead of `true` to save cache space. + fileMetadata[_constants.default.VISITED] = 1; + const metadataId = metadata.id; + const metadataModule = metadata.module; + + if (metadataId && metadataModule) { + fileMetadata[_constants.default.ID] = metadataId; + setModule(metadataId, metadataModule); + } + + fileMetadata[_constants.default.DEPENDENCIES] = metadata.dependencies + ? metadata.dependencies.join(_constants.default.DEPENDENCY_DELIM) + : ''; + + if (computeSha1) { + fileMetadata[_constants.default.SHA1] = metadata.sha1; + } + }; // Callback called when the response from the worker is an error. + + const workerError = error => { + if (typeof error !== 'object' || !error.message || !error.stack) { + error = new Error(error); + error.stack = ''; // Remove stack for stack-less errors. + } + + if (!['ENOENT', 'EACCES'].includes(error.code)) { + throw error; + } // If a file cannot be read we remove it from the file list and + // ignore the failure silently. + + hasteMap.files.delete(relativeFilePath); + }; // If we retain all files in the virtual HasteFS representation, we avoid + // reading them if they aren't important (node_modules). + + if (this._options.retainAllFiles && filePath.includes(NODE_MODULES)) { + if (computeSha1) { + return this._getWorker(workerOptions) + .getSha1({ + computeDependencies: this._options.computeDependencies, + computeSha1, + dependencyExtractor: this._options.dependencyExtractor, + filePath, + hasteImplModulePath: this._options.hasteImplModulePath, + rootDir + }) + .then(workerReply, workerError); + } + + return null; + } + + if ( + this._options.mocksPattern && + this._options.mocksPattern.test(filePath) + ) { + const mockPath = (0, _getMockName.default)(filePath); + const existingMockPath = mocks.get(mockPath); + + if (existingMockPath) { + const secondMockPath = fastPath.relative(rootDir, filePath); + + if (existingMockPath !== secondMockPath) { + const method = this._options.throwOnModuleCollision + ? 'error' + : 'warn'; + + this._console[method]( + [ + 'jest-haste-map: duplicate manual mock found: ' + mockPath, + ' The following files share their name; please delete one of them:', + ' * ' + path().sep + existingMockPath, + ' * ' + path().sep + secondMockPath, + '' + ].join('\n') + ); + + if (this._options.throwOnModuleCollision) { + throw new DuplicateError(existingMockPath, secondMockPath); + } + } + } + + mocks.set(mockPath, relativeFilePath); + } + + if (fileMetadata[_constants.default.VISITED]) { + if (!fileMetadata[_constants.default.ID]) { + return null; + } + + if (moduleMetadata != null) { + const platform = + (0, _getPlatformExtension.default)( + filePath, + this._options.platforms + ) || _constants.default.GENERIC_PLATFORM; + + const module = moduleMetadata[platform]; + + if (module == null) { + return null; + } + + const moduleId = fileMetadata[_constants.default.ID]; + let modulesByPlatform = map.get(moduleId); + + if (!modulesByPlatform) { + modulesByPlatform = Object.create(null); + map.set(moduleId, modulesByPlatform); + } + + modulesByPlatform[platform] = module; + return null; + } + } + + return this._getWorker(workerOptions) + .worker({ + computeDependencies: this._options.computeDependencies, + computeSha1, + dependencyExtractor: this._options.dependencyExtractor, + filePath, + hasteImplModulePath: this._options.hasteImplModulePath, + rootDir + }) + .then(workerReply, workerError); + } + + _buildHasteMap(data) { + const {removedFiles, changedFiles, hasteMap} = data; // If any files were removed or we did not track what files changed, process + // every file looking for changes. Otherwise, process only changed files. + + let map; + let mocks; + let filesToProcess; + + if (changedFiles === undefined || removedFiles.size) { + map = new Map(); + mocks = new Map(); + filesToProcess = hasteMap.files; + } else { + map = hasteMap.map; + mocks = hasteMap.mocks; + filesToProcess = changedFiles; + } + + for (const [relativeFilePath, fileMetadata] of removedFiles) { + this._recoverDuplicates( + hasteMap, + relativeFilePath, + fileMetadata[_constants.default.ID] + ); + } + + const promises = []; + + for (const relativeFilePath of filesToProcess.keys()) { + if ( + this._options.skipPackageJson && + relativeFilePath.endsWith(PACKAGE_JSON) + ) { + continue; + } // SHA-1, if requested, should already be present thanks to the crawler. + + const filePath = fastPath.resolve( + this._options.rootDir, + relativeFilePath + ); + + const promise = this._processFile(hasteMap, map, mocks, filePath); + + if (promise) { + promises.push(promise); + } + } + + return Promise.all(promises).then( + () => { + this._cleanup(); + + hasteMap.map = map; + hasteMap.mocks = mocks; + return hasteMap; + }, + error => { + this._cleanup(); + + throw error; + } + ); + } + + _cleanup() { + const worker = this._worker; // @ts-expect-error + + if (worker && typeof worker.end === 'function') { + // @ts-expect-error + worker.end(); + } + + this._worker = null; + } + /** + * 4. serialize the new `HasteMap` in a cache file. + */ + + _persist(hasteMap) { + _jestSerializer().default.writeFileSync(this._cachePath, hasteMap); + } + /** + * Creates workers or parses files and extracts metadata in-process. + */ + + _getWorker(options) { + if (!this._worker) { + if ((options && options.forceInBand) || this._options.maxWorkers <= 1) { + this._worker = { + getSha1: _worker.getSha1, + worker: _worker.worker + }; + } else { + // @ts-expect-error: assignment of a worker with custom properties. + this._worker = new (_jestWorker().Worker)(require.resolve('./worker'), { + exposedMethods: ['getSha1', 'worker'], + maxRetries: 3, + numWorkers: this._options.maxWorkers + }); + } + } + + return this._worker; + } + + _crawl(hasteMap) { + const options = this._options; + + const ignore = this._ignore.bind(this); + + const crawl = + canUseWatchman && this._options.useWatchman + ? _watchman.default + : _node.default; + const crawlerOptions = { + computeSha1: options.computeSha1, + data: hasteMap, + extensions: options.extensions, + forceNodeFilesystemAPI: options.forceNodeFilesystemAPI, + ignore, + rootDir: options.rootDir, + roots: options.roots + }; + + const retry = error => { + if (crawl === _watchman.default) { + this._console.warn( + `jest-haste-map: Watchman crawl failed. Retrying once with node ` + + `crawler.\n` + + ` Usually this happens when watchman isn't running. Create an ` + + `empty \`.watchmanconfig\` file in your project's root folder or ` + + `initialize a git or hg repository in your project.\n` + + ` ` + + error + ); + + return (0, _node.default)(crawlerOptions).catch(e => { + throw new Error( + `Crawler retry failed:\n` + + ` Original error: ${error.message}\n` + + ` Retry error: ${e.message}\n` + ); + }); + } + + throw error; + }; + + try { + return crawl(crawlerOptions).catch(retry); + } catch (error) { + return retry(error); + } + } + /** + * Watch mode + */ + + _watch(hasteMap) { + if (!this._options.watch) { + return Promise.resolve(); + } // In watch mode, we'll only warn about module collisions and we'll retain + // all files, even changes to node_modules. + + this._options.throwOnModuleCollision = false; + this._options.retainAllFiles = true; // WatchmanWatcher > FSEventsWatcher > sane.NodeWatcher + + const Watcher = + canUseWatchman && this._options.useWatchman + ? _WatchmanWatcher.default + : _FSEventsWatcher.default.isSupported() + ? _FSEventsWatcher.default + : _NodeWatcher.default; + const extensions = this._options.extensions; + const ignorePattern = this._options.ignorePattern; + const rootDir = this._options.rootDir; + let changeQueue = Promise.resolve(); + let eventsQueue = []; // We only need to copy the entire haste map once on every "frame". + + let mustCopy = true; + + const createWatcher = root => { + const watcher = new Watcher(root, { + dot: true, + glob: extensions.map(extension => '**/*.' + extension), + ignored: ignorePattern + }); + return new Promise((resolve, reject) => { + const rejectTimeout = setTimeout( + () => reject(new Error('Failed to start watch mode.')), + MAX_WAIT_TIME + ); + watcher.once('ready', () => { + clearTimeout(rejectTimeout); + watcher.on('all', onChange); + resolve(watcher); + }); + }); + }; + + const emitChange = () => { + if (eventsQueue.length) { + mustCopy = true; + const changeEvent = { + eventsQueue, + hasteFS: new _HasteFS.default({ + files: hasteMap.files, + rootDir + }), + moduleMap: new _ModuleMap.default({ + duplicates: hasteMap.duplicates, + map: hasteMap.map, + mocks: hasteMap.mocks, + rootDir + }) + }; + this.emit('change', changeEvent); + eventsQueue = []; + } + }; + + const onChange = (type, filePath, root, stat) => { + filePath = path().join(root, (0, _normalizePathSep.default)(filePath)); + + if ( + (stat && stat.isDirectory()) || + this._ignore(filePath) || + !extensions.some(extension => filePath.endsWith(extension)) + ) { + return; + } + + const relativeFilePath = fastPath.relative(rootDir, filePath); + const fileMetadata = hasteMap.files.get(relativeFilePath); // The file has been accessed, not modified + + if ( + type === 'change' && + fileMetadata && + stat && + fileMetadata[_constants.default.MTIME] === stat.mtime.getTime() + ) { + return; + } + + changeQueue = changeQueue + .then(() => { + // If we get duplicate events for the same file, ignore them. + if ( + eventsQueue.find( + event => + event.type === type && + event.filePath === filePath && + ((!event.stat && !stat) || + (!!event.stat && + !!stat && + event.stat.mtime.getTime() === stat.mtime.getTime())) + ) + ) { + return null; + } + + if (mustCopy) { + mustCopy = false; + hasteMap = { + clocks: new Map(hasteMap.clocks), + duplicates: new Map(hasteMap.duplicates), + files: new Map(hasteMap.files), + map: new Map(hasteMap.map), + mocks: new Map(hasteMap.mocks) + }; + } + + const add = () => { + eventsQueue.push({ + filePath, + stat, + type + }); + return null; + }; + + const fileMetadata = hasteMap.files.get(relativeFilePath); // If it's not an addition, delete the file and all its metadata + + if (fileMetadata != null) { + const moduleName = fileMetadata[_constants.default.ID]; + + const platform = + (0, _getPlatformExtension.default)( + filePath, + this._options.platforms + ) || _constants.default.GENERIC_PLATFORM; + + hasteMap.files.delete(relativeFilePath); + let moduleMap = hasteMap.map.get(moduleName); + + if (moduleMap != null) { + // We are forced to copy the object because jest-haste-map exposes + // the map as an immutable entity. + moduleMap = copy(moduleMap); + delete moduleMap[platform]; + + if (Object.keys(moduleMap).length === 0) { + hasteMap.map.delete(moduleName); + } else { + hasteMap.map.set(moduleName, moduleMap); + } + } + + if ( + this._options.mocksPattern && + this._options.mocksPattern.test(filePath) + ) { + const mockName = (0, _getMockName.default)(filePath); + hasteMap.mocks.delete(mockName); + } + + this._recoverDuplicates(hasteMap, relativeFilePath, moduleName); + } // If the file was added or changed, + // parse it and update the haste map. + + if (type === 'add' || type === 'change') { + invariant( + stat, + 'since the file exists or changed, it should have stats' + ); + const fileMetadata = [ + '', + stat.mtime.getTime(), + stat.size, + 0, + '', + null + ]; + hasteMap.files.set(relativeFilePath, fileMetadata); + + const promise = this._processFile( + hasteMap, + hasteMap.map, + hasteMap.mocks, + filePath, + { + forceInBand: true + } + ); // Cleanup + + this._cleanup(); + + if (promise) { + return promise.then(add); + } else { + // If a file in node_modules has changed, + // emit an event regardless. + add(); + } + } else { + add(); + } + + return null; + }) + .catch(error => { + this._console.error( + `jest-haste-map: watch error:\n ${error.stack}\n` + ); + }); + }; + + this._changeInterval = setInterval(emitChange, CHANGE_INTERVAL); + return Promise.all(this._options.roots.map(createWatcher)).then( + watchers => { + this._watchers = watchers; + } + ); + } + /** + * This function should be called when the file under `filePath` is removed + * or changed. When that happens, we want to figure out if that file was + * part of a group of files that had the same ID. If it was, we want to + * remove it from the group. Furthermore, if there is only one file + * remaining in the group, then we want to restore that single file as the + * correct resolution for its ID, and cleanup the duplicates index. + */ + + _recoverDuplicates(hasteMap, relativeFilePath, moduleName) { + let dupsByPlatform = hasteMap.duplicates.get(moduleName); + + if (dupsByPlatform == null) { + return; + } + + const platform = + (0, _getPlatformExtension.default)( + relativeFilePath, + this._options.platforms + ) || _constants.default.GENERIC_PLATFORM; + + let dups = dupsByPlatform.get(platform); + + if (dups == null) { + return; + } + + dupsByPlatform = copyMap(dupsByPlatform); + hasteMap.duplicates.set(moduleName, dupsByPlatform); + dups = copyMap(dups); + dupsByPlatform.set(platform, dups); + dups.delete(relativeFilePath); + + if (dups.size !== 1) { + return; + } + + const uniqueModule = dups.entries().next().value; + + if (!uniqueModule) { + return; + } + + let dedupMap = hasteMap.map.get(moduleName); + + if (dedupMap == null) { + dedupMap = Object.create(null); + hasteMap.map.set(moduleName, dedupMap); + } + + dedupMap[platform] = uniqueModule; + dupsByPlatform.delete(platform); + + if (dupsByPlatform.size === 0) { + hasteMap.duplicates.delete(moduleName); + } + } + + async end() { + if (this._changeInterval) { + clearInterval(this._changeInterval); + } + + if (!this._watchers.length) { + return; + } + + await Promise.all(this._watchers.map(watcher => watcher.close())); + this._watchers = []; + } + /** + * Helpers + */ + + _ignore(filePath) { + const ignorePattern = this._options.ignorePattern; + const ignoreMatched = + ignorePattern instanceof RegExp + ? ignorePattern.test(filePath) + : ignorePattern && ignorePattern(filePath); + return ( + ignoreMatched || + (!this._options.retainAllFiles && filePath.includes(NODE_MODULES)) + ); + } + + _createEmptyMap() { + return { + clocks: new Map(), + duplicates: new Map(), + files: new Map(), + map: new Map(), + mocks: new Map() + }; + } +} + +exports.default = HasteMap; + +_defineProperty(HasteMap, 'H', _constants.default); + +class DuplicateError extends Error { + constructor(mockPath1, mockPath2) { + super('Duplicated files or mocks. Please check the console for more info'); + + _defineProperty(this, 'mockPath1', void 0); + + _defineProperty(this, 'mockPath2', void 0); + + this.mockPath1 = mockPath1; + this.mockPath2 = mockPath2; + } +} + +exports.DuplicateError = DuplicateError; + +function copy(object) { + return Object.assign(Object.create(null), object); +} + +function copyMap(input) { + return new Map(input); +} diff --git a/packages/jest-haste-map/build/lib/dependencyExtractor.d.ts b/packages/jest-haste-map/build/lib/dependencyExtractor.d.ts new file mode 100644 index 000000000000..cb927b5bac51 --- /dev/null +++ b/packages/jest-haste-map/build/lib/dependencyExtractor.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export declare function extract(code: string): Set; diff --git a/packages/jest-haste-map/build/lib/dependencyExtractor.js b/packages/jest-haste-map/build/lib/dependencyExtractor.js new file mode 100644 index 000000000000..6d34b23847c5 --- /dev/null +++ b/packages/jest-haste-map/build/lib/dependencyExtractor.js @@ -0,0 +1,99 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.extract = extract; + +var _isRegExpSupported = _interopRequireDefault(require('./isRegExpSupported')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +// Negative look behind is only supported in Node 9+ +const NOT_A_DOT = (0, _isRegExpSupported.default)('(? `([\`'"])([^'"\`]*?)(?:\\${pos})`; + +const WORD_SEPARATOR = '\\b'; +const LEFT_PARENTHESIS = '\\('; +const RIGHT_PARENTHESIS = '\\)'; +const WHITESPACE = '\\s*'; +const OPTIONAL_COMMA = '(:?,\\s*)?'; + +function createRegExp(parts, flags) { + return new RegExp(parts.join(''), flags); +} + +function alternatives(...parts) { + return `(?:${parts.join('|')})`; +} + +function functionCallStart(...names) { + return [ + NOT_A_DOT, + WORD_SEPARATOR, + alternatives(...names), + WHITESPACE, + LEFT_PARENTHESIS, + WHITESPACE + ]; +} + +const BLOCK_COMMENT_RE = /\/\*[^]*?\*\//g; +const LINE_COMMENT_RE = /\/\/.*/g; +const REQUIRE_OR_DYNAMIC_IMPORT_RE = createRegExp( + [ + ...functionCallStart('require', 'import'), + CAPTURE_STRING_LITERAL(1), + WHITESPACE, + OPTIONAL_COMMA, + RIGHT_PARENTHESIS + ], + 'g' +); +const IMPORT_OR_EXPORT_RE = createRegExp( + [ + '\\b(?:import|export)\\s+(?!type(?:of)?\\s+)(?:[^\'"]+\\s+from\\s+)?', + CAPTURE_STRING_LITERAL(1) + ], + 'g' +); +const JEST_EXTENSIONS_RE = createRegExp( + [ + ...functionCallStart( + 'jest\\s*\\.\\s*(?:requireActual|requireMock|genMockFromModule|createMockFromModule)' + ), + CAPTURE_STRING_LITERAL(1), + WHITESPACE, + OPTIONAL_COMMA, + RIGHT_PARENTHESIS + ], + 'g' +); + +function extract(code) { + const dependencies = new Set(); + + const addDependency = (match, _, dep) => { + dependencies.add(dep); + return match; + }; + + code + .replace(BLOCK_COMMENT_RE, '') + .replace(LINE_COMMENT_RE, '') + .replace(IMPORT_OR_EXPORT_RE, addDependency) + .replace(REQUIRE_OR_DYNAMIC_IMPORT_RE, addDependency) + .replace(JEST_EXTENSIONS_RE, addDependency); + return dependencies; +} diff --git a/packages/jest-haste-map/build/lib/fast_path.d.ts b/packages/jest-haste-map/build/lib/fast_path.d.ts new file mode 100644 index 000000000000..eaf1c634745c --- /dev/null +++ b/packages/jest-haste-map/build/lib/fast_path.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +export declare function relative(rootDir: string, filename: string): string; +export declare function resolve(rootDir: string, relativeFilename: string): string; diff --git a/packages/jest-haste-map/build/lib/fast_path.js b/packages/jest-haste-map/build/lib/fast_path.js new file mode 100644 index 000000000000..3236c3bbaab8 --- /dev/null +++ b/packages/jest-haste-map/build/lib/fast_path.js @@ -0,0 +1,81 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.relative = relative; +exports.resolve = resolve; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +// rootDir and filename must be absolute paths (resolved) +function relative(rootDir, filename) { + return filename.indexOf(rootDir + path().sep) === 0 + ? filename.substr(rootDir.length + 1) + : path().relative(rootDir, filename); +} + +const INDIRECTION_FRAGMENT = '..' + path().sep; // rootDir must be an absolute path and relativeFilename must be simple +// (e.g.: foo/bar or ../foo/bar, but never ./foo or foo/../bar) + +function resolve(rootDir, relativeFilename) { + return relativeFilename.indexOf(INDIRECTION_FRAGMENT) === 0 + ? path().resolve(rootDir, relativeFilename) + : rootDir + path().sep + relativeFilename; +} diff --git a/packages/jest-haste-map/build/lib/getPlatformExtension.d.ts b/packages/jest-haste-map/build/lib/getPlatformExtension.d.ts new file mode 100644 index 000000000000..62f88defedd3 --- /dev/null +++ b/packages/jest-haste-map/build/lib/getPlatformExtension.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function getPlatformExtension(file: string, platforms?: Array): string | null; diff --git a/packages/jest-haste-map/build/lib/getPlatformExtension.js b/packages/jest-haste-map/build/lib/getPlatformExtension.js new file mode 100644 index 000000000000..6b9e564f9b84 --- /dev/null +++ b/packages/jest-haste-map/build/lib/getPlatformExtension.js @@ -0,0 +1,31 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getPlatformExtension; + +/** + * 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 SUPPORTED_PLATFORM_EXTS = new Set(['android', 'ios', 'native', 'web']); // Extract platform extension: index.ios.js -> ios + +function getPlatformExtension(file, platforms) { + const last = file.lastIndexOf('.'); + const secondToLast = file.lastIndexOf('.', last - 1); + + if (secondToLast === -1) { + return null; + } + + const platform = file.substring(secondToLast + 1, last); // If an overriding platform array is passed, check that first + + if (platforms && platforms.indexOf(platform) !== -1) { + return platform; + } + + return SUPPORTED_PLATFORM_EXTS.has(platform) ? platform : null; +} diff --git a/packages/jest-haste-map/build/lib/isRegExpSupported.d.ts b/packages/jest-haste-map/build/lib/isRegExpSupported.d.ts new file mode 100644 index 000000000000..ba40ca3982ec --- /dev/null +++ b/packages/jest-haste-map/build/lib/isRegExpSupported.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function isRegExpSupported(value: string): boolean; diff --git a/packages/jest-haste-map/build/lib/isRegExpSupported.js b/packages/jest-haste-map/build/lib/isRegExpSupported.js new file mode 100644 index 000000000000..b6224a770ba4 --- /dev/null +++ b/packages/jest-haste-map/build/lib/isRegExpSupported.js @@ -0,0 +1,22 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = isRegExpSupported; + +/** + * 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. + */ +function isRegExpSupported(value) { + try { + // eslint-disable-next-line no-new + new RegExp(value); + return true; + } catch { + return false; + } +} diff --git a/packages/jest-haste-map/build/lib/normalizePathSep.d.ts b/packages/jest-haste-map/build/lib/normalizePathSep.d.ts new file mode 100644 index 000000000000..1490ab7f4d5a --- /dev/null +++ b/packages/jest-haste-map/build/lib/normalizePathSep.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +declare let normalizePathSep: (string: string) => string; +export default normalizePathSep; diff --git a/packages/jest-haste-map/build/lib/normalizePathSep.js b/packages/jest-haste-map/build/lib/normalizePathSep.js new file mode 100644 index 000000000000..df76a24f3ecc --- /dev/null +++ b/packages/jest-haste-map/build/lib/normalizePathSep.js @@ -0,0 +1,75 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +let normalizePathSep; + +if (path().sep === '/') { + normalizePathSep = filePath => filePath; +} else { + normalizePathSep = filePath => filePath.replace(/\//g, path().sep); +} + +var _default = normalizePathSep; +exports.default = _default; diff --git a/packages/jest-haste-map/build/types.d.ts b/packages/jest-haste-map/build/types.d.ts new file mode 100644 index 000000000000..ff7c06a66ff8 --- /dev/null +++ b/packages/jest-haste-map/build/types.d.ts @@ -0,0 +1,108 @@ +/** + * 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. + */ +/// +import type { Stats } from 'graceful-fs'; +import type { Config } from '@jest/types'; +import type HasteFS from './HasteFS'; +import type ModuleMap from './ModuleMap'; +export declare type IgnoreMatcher = (item: string) => boolean; +export declare type WorkerMessage = { + computeDependencies: boolean; + computeSha1: boolean; + dependencyExtractor?: string | null; + rootDir: string; + filePath: string; + hasteImplModulePath?: string; + retainAllFiles?: boolean; +}; +export declare type WorkerMetadata = { + dependencies: Array | undefined | null; + id: string | undefined | null; + module: ModuleMetaData | undefined | null; + sha1: string | undefined | null; +}; +export declare type CrawlerOptions = { + computeSha1: boolean; + data: InternalHasteMap; + extensions: Array; + forceNodeFilesystemAPI: boolean; + ignore: IgnoreMatcher; + rootDir: string; + roots: Array; +}; +export declare type HasteImpl = { + getHasteName(filePath: Config.Path): string | undefined; +}; +export declare type FileData = Map; +export declare type FileMetaData = [ + string, + number, + number, + 0 | 1, + string, + string | null | undefined +]; +export declare type MockData = Map; +export declare type ModuleMapData = Map; +export declare type WatchmanClockSpec = string | { + scm: { + 'mergebase-with': string; + }; +}; +export declare type WatchmanClocks = Map; +export declare type HasteRegExp = RegExp | ((str: string) => boolean); +export declare type DuplicatesSet = Map; +export declare type DuplicatesIndex = Map>; +export declare type InternalHasteMap = { + clocks: WatchmanClocks; + duplicates: DuplicatesIndex; + files: FileData; + map: ModuleMapData; + mocks: MockData; +}; +export declare type HasteMap = { + hasteFS: HasteFS; + moduleMap: ModuleMap; + __hasteMapForTest?: InternalHasteMap | null; +}; +export declare type RawModuleMap = { + rootDir: Config.Path; + duplicates: DuplicatesIndex; + map: ModuleMapData; + mocks: MockData; +}; +declare type ModuleMapItem = { + [platform: string]: ModuleMetaData; +}; +export declare type ModuleMetaData = [Config.Path, /* type */ number]; +export declare type HType = { + ID: 0; + MTIME: 1; + SIZE: 2; + VISITED: 3; + DEPENDENCIES: 4; + SHA1: 5; + PATH: 0; + TYPE: 1; + MODULE: 0; + PACKAGE: 1; + GENERIC_PLATFORM: 'g'; + NATIVE_PLATFORM: 'native'; + DEPENDENCY_DELIM: '\0'; +}; +export declare type HTypeValue = HType[keyof HType]; +export declare type EventsQueue = Array<{ + filePath: Config.Path; + stat: Stats | undefined; + type: string; +}>; +export declare type ChangeEvent = { + eventsQueue: EventsQueue; + hasteFS: HasteFS; + moduleMap: ModuleMap; +}; +export {}; diff --git a/packages/jest-haste-map/build/types.js b/packages/jest-haste-map/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-haste-map/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-haste-map/build/watchers/FSEventsWatcher.d.ts b/packages/jest-haste-map/build/watchers/FSEventsWatcher.d.ts new file mode 100644 index 000000000000..2c63ee9be5c1 --- /dev/null +++ b/packages/jest-haste-map/build/watchers/FSEventsWatcher.d.ts @@ -0,0 +1,44 @@ +/** + * 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. + * + */ +/// +import { EventEmitter } from 'events'; +import { Matcher } from 'anymatch'; +/** + * Export `FSEventsWatcher` class. + * Watches `dir`. + */ +declare class FSEventsWatcher extends EventEmitter { + readonly root: string; + readonly ignored?: Matcher; + readonly glob: Array; + readonly dot: boolean; + readonly hasIgnore: boolean; + readonly doIgnore: (path: string) => boolean; + readonly fsEventsWatchStopper: () => Promise; + private _tracked; + static isSupported(): boolean; + private static normalizeProxy; + private static recReaddir; + constructor(dir: string, opts: { + root: string; + ignored?: Matcher; + glob: string | Array; + dot: boolean; + }); + /** + * End watching. + */ + close(callback?: () => void): Promise; + private isFileIncluded; + private handleEvent; + /** + * Emit events. + */ + private _emit; +} +export = FSEventsWatcher; diff --git a/packages/jest-haste-map/build/watchers/FSEventsWatcher.js b/packages/jest-haste-map/build/watchers/FSEventsWatcher.js new file mode 100644 index 000000000000..1df2fb091d10 --- /dev/null +++ b/packages/jest-haste-map/build/watchers/FSEventsWatcher.js @@ -0,0 +1,294 @@ +'use strict'; + +function _events() { + const data = require('events'); + + _events = function () { + return data; + }; + + return data; +} + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _anymatch() { + const data = _interopRequireDefault(require('anymatch')); + + _anymatch = function () { + return data; + }; + + return data; +} + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _micromatch() { + const data = _interopRequireDefault(require('micromatch')); + + _micromatch = function () { + return data; + }; + + return data; +} + +function _walker() { + const data = _interopRequireDefault(require('walker')); + + _walker = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error +// @ts-ignore: this is for CI which runs linux and might not have this +let fsevents = null; + +try { + fsevents = require('fsevents'); +} catch { + // Optional dependency, only supported on Darwin. +} + +const CHANGE_EVENT = 'change'; +const DELETE_EVENT = 'delete'; +const ADD_EVENT = 'add'; +const ALL_EVENT = 'all'; + +/** + * Export `FSEventsWatcher` class. + * Watches `dir`. + */ +class FSEventsWatcher extends _events().EventEmitter { + static isSupported() { + return fsevents !== null; + } + + static normalizeProxy(callback) { + return (filepath, stats) => callback(path().normalize(filepath), stats); + } + + static recReaddir( + dir, + dirCallback, + fileCallback, + endCallback, + errorCallback, + ignored + ) { + (0, _walker().default)(dir) + .filterDir( + currentDir => !ignored || !(0, _anymatch().default)(ignored, currentDir) + ) + .on('dir', FSEventsWatcher.normalizeProxy(dirCallback)) + .on('file', FSEventsWatcher.normalizeProxy(fileCallback)) + .on('error', errorCallback) + .on('end', () => { + endCallback(); + }); + } + + constructor(dir, opts) { + if (!fsevents) { + throw new Error( + '`fsevents` unavailable (this watcher can only be used on Darwin)' + ); + } + + super(); + + _defineProperty(this, 'root', void 0); + + _defineProperty(this, 'ignored', void 0); + + _defineProperty(this, 'glob', void 0); + + _defineProperty(this, 'dot', void 0); + + _defineProperty(this, 'hasIgnore', void 0); + + _defineProperty(this, 'doIgnore', void 0); + + _defineProperty(this, 'fsEventsWatchStopper', void 0); + + _defineProperty(this, '_tracked', void 0); + + this.dot = opts.dot || false; + this.ignored = opts.ignored; + this.glob = Array.isArray(opts.glob) ? opts.glob : [opts.glob]; + this.hasIgnore = + Boolean(opts.ignored) && !(Array.isArray(opts) && opts.length > 0); + this.doIgnore = opts.ignored + ? (0, _anymatch().default)(opts.ignored) + : () => false; + this.root = path().resolve(dir); + this.fsEventsWatchStopper = fsevents.watch( + this.root, + this.handleEvent.bind(this) + ); + this._tracked = new Set(); + FSEventsWatcher.recReaddir( + this.root, + filepath => { + this._tracked.add(filepath); + }, + filepath => { + this._tracked.add(filepath); + }, + this.emit.bind(this, 'ready'), + this.emit.bind(this, 'error'), + this.ignored + ); + } + /** + * End watching. + */ + + async close(callback) { + await this.fsEventsWatchStopper(); + this.removeAllListeners(); + + if (typeof callback === 'function') { + process.nextTick(callback.bind(null, null, true)); + } + } + + isFileIncluded(relativePath) { + if (this.doIgnore(relativePath)) { + return false; + } + + return this.glob.length + ? (0, _micromatch().default)([relativePath], this.glob, { + dot: this.dot + }).length > 0 + : this.dot || + (0, _micromatch().default)([relativePath], '**/*').length > 0; + } + + handleEvent(filepath) { + const relativePath = path().relative(this.root, filepath); + + if (!this.isFileIncluded(relativePath)) { + return; + } + + fs().lstat(filepath, (error, stat) => { + if (error && error.code !== 'ENOENT') { + this.emit('error', error); + return; + } + + if (error) { + // Ignore files that aren't tracked and don't exist. + if (!this._tracked.has(filepath)) { + return; + } + + this._emit(DELETE_EVENT, relativePath); + + this._tracked.delete(filepath); + + return; + } + + if (this._tracked.has(filepath)) { + this._emit(CHANGE_EVENT, relativePath, stat); + } else { + this._tracked.add(filepath); + + this._emit(ADD_EVENT, relativePath, stat); + } + }); + } + /** + * Emit events. + */ + + _emit(type, file, stat) { + this.emit(type, file, this.root, stat); + this.emit(ALL_EVENT, type, file, this.root, stat); + } +} + +module.exports = FSEventsWatcher; diff --git a/packages/jest-haste-map/build/watchers/NodeWatcher.js b/packages/jest-haste-map/build/watchers/NodeWatcher.js new file mode 100644 index 000000000000..8ae1bc4f0be0 --- /dev/null +++ b/packages/jest-haste-map/build/watchers/NodeWatcher.js @@ -0,0 +1,375 @@ +// vendored from https://github.com/amasad/sane/blob/64ff3a870c42e84f744086884bf55a4f9c22d376/src/node_watcher.js +'use strict'; + +const EventEmitter = require('events').EventEmitter; + +const fs = require('fs'); + +const platform = require('os').platform(); + +const path = require('path'); + +const common = require('./common'); +/** + * Constants + */ + +const DEFAULT_DELAY = common.DEFAULT_DELAY; +const CHANGE_EVENT = common.CHANGE_EVENT; +const DELETE_EVENT = common.DELETE_EVENT; +const ADD_EVENT = common.ADD_EVENT; +const ALL_EVENT = common.ALL_EVENT; +/** + * Export `NodeWatcher` class. + * Watches `dir`. + * + * @class NodeWatcher + * @param {String} dir + * @param {Object} opts + * @public + */ + +module.exports = class NodeWatcher extends EventEmitter { + constructor(dir, opts) { + super(); + common.assignOptions(this, opts); + this.watched = Object.create(null); + this.changeTimers = Object.create(null); + this.dirRegistery = Object.create(null); + this.root = path.resolve(dir); + this.watchdir = this.watchdir.bind(this); + this.register = this.register.bind(this); + this.checkedEmitError = this.checkedEmitError.bind(this); + this.watchdir(this.root); + common.recReaddir( + this.root, + this.watchdir, + this.register, + this.emit.bind(this, 'ready'), + this.checkedEmitError, + this.ignored + ); + } + /** + * Register files that matches our globs to know what to type of event to + * emit in the future. + * + * Registery looks like the following: + * + * dirRegister => Map { + * dirpath => Map { + * filename => true + * } + * } + * + * @param {string} filepath + * @return {boolean} whether or not we have registered the file. + * @private + */ + + register(filepath) { + const relativePath = path.relative(this.root, filepath); + + if ( + !common.isFileIncluded(this.globs, this.dot, this.doIgnore, relativePath) + ) { + return false; + } + + const dir = path.dirname(filepath); + + if (!this.dirRegistery[dir]) { + this.dirRegistery[dir] = Object.create(null); + } + + const filename = path.basename(filepath); + this.dirRegistery[dir][filename] = true; + return true; + } + /** + * Removes a file from the registery. + * + * @param {string} filepath + * @private + */ + + unregister(filepath) { + const dir = path.dirname(filepath); + + if (this.dirRegistery[dir]) { + const filename = path.basename(filepath); + delete this.dirRegistery[dir][filename]; + } + } + /** + * Removes a dir from the registery. + * + * @param {string} dirpath + * @private + */ + + unregisterDir(dirpath) { + if (this.dirRegistery[dirpath]) { + delete this.dirRegistery[dirpath]; + } + } + /** + * Checks if a file or directory exists in the registery. + * + * @param {string} fullpath + * @return {boolean} + * @private + */ + + registered(fullpath) { + const dir = path.dirname(fullpath); + return ( + this.dirRegistery[fullpath] || + (this.dirRegistery[dir] && + this.dirRegistery[dir][path.basename(fullpath)]) + ); + } + /** + * Emit "error" event if it's not an ignorable event + * + * @param error + * @private + */ + + checkedEmitError(error) { + if (!isIgnorableFileError(error)) { + this.emit('error', error); + } + } + /** + * Watch a directory. + * + * @param {string} dir + * @private + */ + + watchdir(dir) { + if (this.watched[dir]) { + return; + } + + const watcher = fs.watch( + dir, + { + persistent: true + }, + this.normalizeChange.bind(this, dir) + ); + this.watched[dir] = watcher; + watcher.on('error', this.checkedEmitError); + + if (this.root !== dir) { + this.register(dir); + } + } + /** + * Stop watching a directory. + * + * @param {string} dir + * @private + */ + + stopWatching(dir) { + if (this.watched[dir]) { + this.watched[dir].close(); + delete this.watched[dir]; + } + } + /** + * End watching. + * + * @public + */ + + close() { + Object.keys(this.watched).forEach(this.stopWatching, this); + this.removeAllListeners(); + return Promise.resolve(); + } + /** + * On some platforms, as pointed out on the fs docs (most likely just win32) + * the file argument might be missing from the fs event. Try to detect what + * change by detecting if something was deleted or the most recent file change. + * + * @param {string} dir + * @param {string} event + * @param {string} file + * @public + */ + + detectChangedFile(dir, event, callback) { + if (!this.dirRegistery[dir]) { + return; + } + + let found = false; + let closest = { + mtime: 0 + }; + let c = 0; + Object.keys(this.dirRegistery[dir]).forEach(function (file, i, arr) { + fs.lstat(path.join(dir, file), (error, stat) => { + if (found) { + return; + } + + if (error) { + if (isIgnorableFileError(error)) { + found = true; + callback(file); + } else { + this.emit('error', error); + } + } else { + if (stat.mtime > closest.mtime) { + stat.file = file; + closest = stat; + } + + if (arr.length === ++c) { + callback(closest.file); + } + } + }); + }, this); + } + /** + * Normalize fs events and pass it on to be processed. + * + * @param {string} dir + * @param {string} event + * @param {string} file + * @public + */ + + normalizeChange(dir, event, file) { + if (!file) { + this.detectChangedFile(dir, event, actualFile => { + if (actualFile) { + this.processChange(dir, event, actualFile); + } + }); + } else { + this.processChange(dir, event, path.normalize(file)); + } + } + /** + * Process changes. + * + * @param {string} dir + * @param {string} event + * @param {string} file + * @public + */ + + processChange(dir, event, file) { + const fullPath = path.join(dir, file); + const relativePath = path.join(path.relative(this.root, dir), file); + fs.lstat(fullPath, (error, stat) => { + if (error && error.code !== 'ENOENT') { + this.emit('error', error); + } else if (!error && stat.isDirectory()) { + // win32 emits usless change events on dirs. + if (event !== 'change') { + this.watchdir(fullPath); + + if ( + common.isFileIncluded( + this.globs, + this.dot, + this.doIgnore, + relativePath + ) + ) { + this.emitEvent(ADD_EVENT, relativePath, stat); + } + } + } else { + const registered = this.registered(fullPath); + + if (error && error.code === 'ENOENT') { + this.unregister(fullPath); + this.stopWatching(fullPath); + this.unregisterDir(fullPath); + + if (registered) { + this.emitEvent(DELETE_EVENT, relativePath); + } + } else if (registered) { + this.emitEvent(CHANGE_EVENT, relativePath, stat); + } else { + if (this.register(fullPath)) { + this.emitEvent(ADD_EVENT, relativePath, stat); + } + } + } + }); + } + /** + * Triggers a 'change' event after debounding it to take care of duplicate + * events on os x. + * + * @private + */ + + emitEvent(type, file, stat) { + const key = type + '-' + file; + const addKey = ADD_EVENT + '-' + file; + + if (type === CHANGE_EVENT && this.changeTimers[addKey]) { + // Ignore the change event that is immediately fired after an add event. + // (This happens on Linux). + return; + } + + clearTimeout(this.changeTimers[key]); + this.changeTimers[key] = setTimeout(() => { + delete this.changeTimers[key]; + + if (type === ADD_EVENT && stat.isDirectory()) { + // Recursively emit add events and watch for sub-files/folders + common.recReaddir( + path.resolve(this.root, file), + function emitAddDir(dir, stats) { + this.watchdir(dir); + this.rawEmitEvent(ADD_EVENT, path.relative(this.root, dir), stats); + }.bind(this), + function emitAddFile(file, stats) { + this.register(file); + this.rawEmitEvent(ADD_EVENT, path.relative(this.root, file), stats); + }.bind(this), + function endCallback() {}, + this.checkedEmitError, + this.ignored + ); + } else { + this.rawEmitEvent(type, file, stat); + } + }, DEFAULT_DELAY); + } + /** + * Actually emit the events + */ + + rawEmitEvent(type, file, stat) { + this.emit(type, file, this.root, stat); + this.emit(ALL_EVENT, type, file, this.root, stat); + } +}; +/** + * Determine if a given FS error can be ignored + * + * @private + */ + +function isIgnorableFileError(error) { + return ( + error.code === 'ENOENT' || // Workaround Windows node issue #4337. + (error.code === 'EPERM' && platform === 'win32') + ); +} diff --git a/packages/jest-haste-map/build/watchers/RecrawlWarning.js b/packages/jest-haste-map/build/watchers/RecrawlWarning.js new file mode 100644 index 000000000000..de82691c848e --- /dev/null +++ b/packages/jest-haste-map/build/watchers/RecrawlWarning.js @@ -0,0 +1,56 @@ +// vendored from https://github.com/amasad/sane/blob/64ff3a870c42e84f744086884bf55a4f9c22d376/src/utils/recrawl-warning-dedupe.js +'use strict'; + +class RecrawlWarning { + constructor(root, count) { + this.root = root; + this.count = count; + } + + static findByRoot(root) { + for (let i = 0; i < this.RECRAWL_WARNINGS.length; i++) { + const warning = this.RECRAWL_WARNINGS[i]; + + if (warning.root === root) { + return warning; + } + } + + return undefined; + } + + static isRecrawlWarningDupe(warningMessage) { + if (typeof warningMessage !== 'string') { + return false; + } + + const match = warningMessage.match(this.REGEXP); + + if (!match) { + return false; + } + + const count = Number(match[1]); + const root = match[2]; + const warning = this.findByRoot(root); + + if (warning) { + // only keep the highest count, assume count to either stay the same or + // increase. + if (warning.count >= count) { + return true; + } else { + // update the existing warning to the latest (highest) count + warning.count = count; + return false; + } + } else { + this.RECRAWL_WARNINGS.push(new RecrawlWarning(root, count)); + return false; + } + } +} + +RecrawlWarning.RECRAWL_WARNINGS = []; +RecrawlWarning.REGEXP = /Recrawled this watch (\d+) times, most recently because:\n([^:]+)/; +module.exports = RecrawlWarning; diff --git a/packages/jest-haste-map/build/watchers/WatchmanWatcher.js b/packages/jest-haste-map/build/watchers/WatchmanWatcher.js new file mode 100644 index 000000000000..54d05b29ed33 --- /dev/null +++ b/packages/jest-haste-map/build/watchers/WatchmanWatcher.js @@ -0,0 +1,418 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = WatchmanWatcher; + +function _assert() { + const data = _interopRequireDefault(require('assert')); + + _assert = function () { + return data; + }; + + return data; +} + +function _events() { + const data = require('events'); + + _events = function () { + return data; + }; + + return data; +} + +function _path() { + const data = _interopRequireDefault(require('path')); + + _path = function () { + return data; + }; + + return data; +} + +function _fbWatchman() { + const data = _interopRequireDefault(require('fb-watchman')); + + _fbWatchman = function () { + return data; + }; + + return data; +} + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +var _RecrawlWarning = _interopRequireDefault(require('./RecrawlWarning')); + +var _common = _interopRequireDefault(require('./common')); + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 CHANGE_EVENT = _common.default.CHANGE_EVENT; +const DELETE_EVENT = _common.default.DELETE_EVENT; +const ADD_EVENT = _common.default.ADD_EVENT; +const ALL_EVENT = _common.default.ALL_EVENT; +const SUB_NAME = 'sane-sub'; +/** + * Watches `dir`. + * + * @class PollWatcher + * @param String dir + * @param {Object} opts + * @public + */ + +function WatchmanWatcher(dir, opts) { + _common.default.assignOptions(this, opts); + + this.root = _path().default.resolve(dir); + this.init(); +} // eslint-disable-next-line no-proto + +WatchmanWatcher.prototype.__proto__ = _events().EventEmitter.prototype; +/** + * Run the watchman `watch` command on the root and subscribe to changes. + * + * @private + */ + +WatchmanWatcher.prototype.init = function () { + if (this.client) { + this.client.removeAllListeners(); + } + + const self = this; + this.client = new (_fbWatchman().default.Client)(); + this.client.on('error', error => { + self.emit('error', error); + }); + this.client.on('subscription', this.handleChangeEvent.bind(this)); + this.client.on('end', () => { + console.warn('[sane] Warning: Lost connection to watchman, reconnecting..'); + self.init(); + }); + this.watchProjectInfo = null; + + function getWatchRoot() { + return self.watchProjectInfo ? self.watchProjectInfo.root : self.root; + } + + function onCapability(error, resp) { + if (handleError(self, error)) { + // The Watchman watcher is unusable on this system, we cannot continue + return; + } + + handleWarning(resp); + self.capabilities = resp.capabilities; + + if (self.capabilities.relative_root) { + self.client.command(['watch-project', getWatchRoot()], onWatchProject); + } else { + self.client.command(['watch', getWatchRoot()], onWatch); + } + } + + function onWatchProject(error, resp) { + if (handleError(self, error)) { + return; + } + + handleWarning(resp); + self.watchProjectInfo = { + relativePath: resp.relative_path ? resp.relative_path : '', + root: resp.watch + }; + self.client.command(['clock', getWatchRoot()], onClock); + } + + function onWatch(error, resp) { + if (handleError(self, error)) { + return; + } + + handleWarning(resp); + self.client.command(['clock', getWatchRoot()], onClock); + } + + function onClock(error, resp) { + if (handleError(self, error)) { + return; + } + + handleWarning(resp); + const options = { + fields: ['name', 'exists', 'new'], + since: resp.clock + }; // If the server has the wildmatch capability available it supports + // the recursive **/*.foo style match and we can offload our globs + // to the watchman server. This saves both on data size to be + // communicated back to us and compute for evaluating the globs + // in our node process. + + if (self.capabilities.wildmatch) { + if (self.globs.length === 0) { + if (!self.dot) { + // Make sure we honor the dot option if even we're not using globs. + options.expression = [ + 'match', + '**', + 'wholename', + { + includedotfiles: false + } + ]; + } + } else { + options.expression = ['anyof']; + + for (const i in self.globs) { + options.expression.push([ + 'match', + self.globs[i], + 'wholename', + { + includedotfiles: self.dot + } + ]); + } + } + } + + if (self.capabilities.relative_root) { + options.relative_root = self.watchProjectInfo.relativePath; + } + + self.client.command( + ['subscribe', getWatchRoot(), SUB_NAME, options], + onSubscribe + ); + } + + function onSubscribe(error, resp) { + if (handleError(self, error)) { + return; + } + + handleWarning(resp); + self.emit('ready'); + } + + self.client.capabilityCheck( + { + optional: ['wildmatch', 'relative_root'] + }, + onCapability + ); +}; +/** + * Handles a change event coming from the subscription. + * + * @param {Object} resp + * @private + */ + +WatchmanWatcher.prototype.handleChangeEvent = function (resp) { + _assert().default.equal( + resp.subscription, + SUB_NAME, + 'Invalid subscription event.' + ); + + if (resp.is_fresh_instance) { + this.emit('fresh_instance'); + } + + if (resp.is_fresh_instance) { + this.emit('fresh_instance'); + } + + if (Array.isArray(resp.files)) { + resp.files.forEach(this.handleFileChange, this); + } +}; +/** + * Handles a single change event record. + * + * @param {Object} changeDescriptor + * @private + */ + +WatchmanWatcher.prototype.handleFileChange = function (changeDescriptor) { + const self = this; + let absPath; + let relativePath; + + if (this.capabilities.relative_root) { + relativePath = changeDescriptor.name; + absPath = _path().default.join( + this.watchProjectInfo.root, + this.watchProjectInfo.relativePath, + relativePath + ); + } else { + absPath = _path().default.join(this.root, changeDescriptor.name); + relativePath = changeDescriptor.name; + } + + if ( + !(self.capabilities.wildmatch && !this.hasIgnore) && + !_common.default.isFileIncluded( + this.globs, + this.dot, + this.doIgnore, + relativePath + ) + ) { + return; + } + + if (!changeDescriptor.exists) { + self.emitEvent(DELETE_EVENT, relativePath, self.root); + } else { + fs().lstat(absPath, (error, stat) => { + // Files can be deleted between the event and the lstat call + // the most reliable thing to do here is to ignore the event. + if (error && error.code === 'ENOENT') { + return; + } + + if (handleError(self, error)) { + return; + } + + const eventType = changeDescriptor.new ? ADD_EVENT : CHANGE_EVENT; // Change event on dirs are mostly useless. + + if (!(eventType === CHANGE_EVENT && stat.isDirectory())) { + self.emitEvent(eventType, relativePath, self.root, stat); + } + }); + } +}; +/** + * Dispatches the event. + * + * @param {string} eventType + * @param {string} filepath + * @param {string} root + * @param {fs.Stat} stat + * @private + */ + +WatchmanWatcher.prototype.emitEvent = function ( + eventType, + filepath, + root, + stat +) { + this.emit(eventType, filepath, root, stat); + this.emit(ALL_EVENT, eventType, filepath, root, stat); +}; +/** + * Closes the watcher. + * + */ + +WatchmanWatcher.prototype.close = function () { + this.client.removeAllListeners(); + this.client.end(); + return Promise.resolve(); +}; +/** + * Handles an error and returns true if exists. + * + * @param {WatchmanWatcher} self + * @param {Error} error + * @private + */ + +function handleError(self, error) { + if (error != null) { + self.emit('error', error); + return true; + } else { + return false; + } +} +/** + * Handles a warning in the watchman resp object. + * + * @param {object} resp + * @private + */ + +function handleWarning(resp) { + if ('warning' in resp) { + if (_RecrawlWarning.default.isRecrawlWarningDupe(resp.warning)) { + return true; + } + + console.warn(resp.warning); + return true; + } else { + return false; + } +} diff --git a/packages/jest-haste-map/build/watchers/common.js b/packages/jest-haste-map/build/watchers/common.js new file mode 100644 index 000000000000..6372744715b6 --- /dev/null +++ b/packages/jest-haste-map/build/watchers/common.js @@ -0,0 +1,114 @@ +// vendored from https://github.com/amasad/sane/blob/64ff3a870c42e84f744086884bf55a4f9c22d376/src/common.js +'use strict'; + +const platform = require('os').platform(); + +const path = require('path'); + +const anymatch = require('anymatch'); + +const micromatch = require('micromatch'); + +const walker = require('walker'); +/** + * Constants + */ + +exports.DEFAULT_DELAY = 100; +exports.CHANGE_EVENT = 'change'; +exports.DELETE_EVENT = 'delete'; +exports.ADD_EVENT = 'add'; +exports.ALL_EVENT = 'all'; +/** + * Assigns options to the watcher. + * + * @param {NodeWatcher|PollWatcher|WatchmanWatcher} watcher + * @param {?object} opts + * @return {boolean} + * @public + */ + +exports.assignOptions = function (watcher, opts) { + opts = opts || {}; + watcher.globs = opts.glob || []; + watcher.dot = opts.dot || false; + watcher.ignored = opts.ignored || false; + + if (!Array.isArray(watcher.globs)) { + watcher.globs = [watcher.globs]; + } + + watcher.hasIgnore = + Boolean(opts.ignored) && !(Array.isArray(opts) && opts.length > 0); + watcher.doIgnore = opts.ignored ? anymatch(opts.ignored) : () => false; + + if (opts.watchman && opts.watchmanPath) { + watcher.watchmanPath = opts.watchmanPath; + } + + return opts; +}; +/** + * Checks a file relative path against the globs array. + * + * @param {array} globs + * @param {string} relativePath + * @return {boolean} + * @public + */ + +exports.isFileIncluded = function (globs, dot, doIgnore, relativePath) { + if (doIgnore(relativePath)) { + return false; + } + + return globs.length + ? micromatch.some(relativePath, globs, { + dot + }) + : dot || micromatch.some(relativePath, '**/*'); +}; +/** + * Traverse a directory recursively calling `callback` on every directory. + * + * @param {string} dir + * @param {function} dirCallback + * @param {function} fileCallback + * @param {function} endCallback + * @param {*} ignored + * @public + */ + +exports.recReaddir = function ( + dir, + dirCallback, + fileCallback, + endCallback, + errorCallback, + ignored +) { + walker(dir) + .filterDir(currentDir => !anymatch(ignored, currentDir)) + .on('dir', normalizeProxy(dirCallback)) + .on('file', normalizeProxy(fileCallback)) + .on('error', errorCallback) + .on('end', () => { + if (platform === 'win32') { + setTimeout(endCallback, 1000); + } else { + endCallback(); + } + }); +}; +/** + * Returns a callback that when called will normalize a path and call the + * original callback + * + * @param {function} callback + * @return {function} + * @private + */ + +function normalizeProxy(callback) { + return (filepath, stats) => callback(path.normalize(filepath), stats); +} diff --git a/packages/jest-haste-map/build/worker.d.ts b/packages/jest-haste-map/build/worker.d.ts new file mode 100644 index 000000000000..d17e714c82ff --- /dev/null +++ b/packages/jest-haste-map/build/worker.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { WorkerMessage, WorkerMetadata } from './types'; +export declare function worker(data: WorkerMessage): Promise; +export declare function getSha1(data: WorkerMessage): Promise; diff --git a/packages/jest-haste-map/build/worker.js b/packages/jest-haste-map/build/worker.js new file mode 100644 index 000000000000..a11e1af03c67 --- /dev/null +++ b/packages/jest-haste-map/build/worker.js @@ -0,0 +1,197 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.worker = worker; +exports.getSha1 = getSha1; + +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 fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +var _blacklist = _interopRequireDefault(require('./blacklist')); + +var _constants = _interopRequireDefault(require('./constants')); + +var dependencyExtractor = _interopRequireWildcard( + require('./lib/dependencyExtractor') +); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 PACKAGE_JSON = path().sep + 'package.json'; +let hasteImpl = null; +let hasteImplModulePath = null; + +function sha1hex(content) { + return (0, _crypto().createHash)('sha1').update(content).digest('hex'); +} + +async function worker(data) { + if ( + data.hasteImplModulePath && + data.hasteImplModulePath !== hasteImplModulePath + ) { + if (hasteImpl) { + throw new Error('jest-haste-map: hasteImplModulePath changed'); + } + + hasteImplModulePath = data.hasteImplModulePath; + hasteImpl = require(hasteImplModulePath); + } + + let content; + let dependencies; + let id; + let module; + let sha1; + const {computeDependencies, computeSha1, rootDir, filePath} = data; + + const getContent = () => { + if (content === undefined) { + content = fs().readFileSync(filePath, 'utf8'); + } + + return content; + }; + + if (filePath.endsWith(PACKAGE_JSON)) { + // Process a package.json that is returned as a PACKAGE type with its name. + try { + const fileData = JSON.parse(getContent()); + + if (fileData.name) { + const relativeFilePath = path().relative(rootDir, filePath); + id = fileData.name; + module = [relativeFilePath, _constants.default.PACKAGE]; + } + } catch (err) { + throw new Error(`Cannot parse ${filePath} as JSON: ${err.message}`); + } + } else if ( + !_blacklist.default.has(filePath.substr(filePath.lastIndexOf('.'))) + ) { + // Process a random file that is returned as a MODULE. + if (hasteImpl) { + id = hasteImpl.getHasteName(filePath); + } + + if (computeDependencies) { + const content = getContent(); + dependencies = Array.from( + data.dependencyExtractor + ? require(data.dependencyExtractor).extract( + content, + filePath, + dependencyExtractor.extract + ) + : dependencyExtractor.extract(content) + ); + } + + if (id) { + const relativeFilePath = path().relative(rootDir, filePath); + module = [relativeFilePath, _constants.default.MODULE]; + } + } // If a SHA-1 is requested on update, compute it. + + if (computeSha1) { + sha1 = sha1hex(getContent() || fs().readFileSync(filePath)); + } + + return { + dependencies, + id, + module, + sha1 + }; +} + +async function getSha1(data) { + const sha1 = data.computeSha1 + ? sha1hex(fs().readFileSync(data.filePath)) + : null; + return { + dependencies: undefined, + id: undefined, + module: undefined, + sha1 + }; +} diff --git a/packages/jest-jasmine2/build/ExpectationFailed.d.ts b/packages/jest-jasmine2/build/ExpectationFailed.d.ts new file mode 100644 index 000000000000..657f1ccf21b7 --- /dev/null +++ b/packages/jest-jasmine2/build/ExpectationFailed.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +export default class ExpectationFailed extends Error { +} diff --git a/packages/jest-jasmine2/build/ExpectationFailed.js b/packages/jest-jasmine2/build/ExpectationFailed.js new file mode 100644 index 000000000000..cca6c96f4e62 --- /dev/null +++ b/packages/jest-jasmine2/build/ExpectationFailed.js @@ -0,0 +1,16 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +/** + * 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. + */ +class ExpectationFailed extends Error {} + +exports.default = ExpectationFailed; diff --git a/packages/jest-jasmine2/build/PCancelable.d.ts b/packages/jest-jasmine2/build/PCancelable.d.ts new file mode 100644 index 000000000000..f245a6d6a645 --- /dev/null +++ b/packages/jest-jasmine2/build/PCancelable.d.ts @@ -0,0 +1,17 @@ +/** + * 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. + */ +export default class PCancelable extends Promise { + private _pending; + private _canceled; + private _promise; + private _cancel?; + private _reject; + constructor(executor: (onCancel: (cancelHandler: () => void) => void, resolve: (value?: T | PromiseLike) => void, reject: (reason?: unknown) => void) => void); + then(onFulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onRejected?: ((reason: unknown) => TResult2 | PromiseLike) | undefined | null): Promise; + catch(onRejected?: ((reason: unknown) => TResult | PromiseLike) | undefined | null): Promise; + cancel(): void; +} diff --git a/packages/jest-jasmine2/build/PCancelable.js b/packages/jest-jasmine2/build/PCancelable.js new file mode 100644 index 000000000000..65360994b187 --- /dev/null +++ b/packages/jest-jasmine2/build/PCancelable.js @@ -0,0 +1,97 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Promise = global[Symbol.for('jest-native-promise')] || global.Promise; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * 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. + */ +class CancelError extends Error { + constructor() { + super('Promise was canceled'); + this.name = 'CancelError'; + } +} + +class PCancelable extends Promise { + constructor(executor) { + super(resolve => resolve()); + + _defineProperty(this, '_pending', true); + + _defineProperty(this, '_canceled', false); + + _defineProperty(this, '_promise', void 0); + + _defineProperty(this, '_cancel', void 0); + + _defineProperty(this, '_reject', () => {}); + + this._promise = new Promise((resolve, reject) => { + this._reject = reject; + return executor( + fn => { + this._cancel = fn; + }, + val => { + this._pending = false; + resolve(val); + }, + err => { + this._pending = false; + reject(err); + } + ); + }); + } + + then(onFulfilled, onRejected) { + return this._promise.then(onFulfilled, onRejected); + } + + catch(onRejected) { + return this._promise.catch(onRejected); + } + + cancel() { + if (!this._pending || this._canceled) { + return; + } + + if (typeof this._cancel === 'function') { + try { + this._cancel(); + } catch (err) { + this._reject(err); + } + } + + this._canceled = true; + + this._reject(new CancelError()); + } +} + +exports.default = PCancelable; diff --git a/packages/jest-jasmine2/build/assertionErrorMessage.d.ts b/packages/jest-jasmine2/build/assertionErrorMessage.d.ts new file mode 100644 index 000000000000..8c00fb5740bc --- /dev/null +++ b/packages/jest-jasmine2/build/assertionErrorMessage.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import { DiffOptions } from 'jest-matcher-utils'; +import type { AssertionErrorWithStack } from './types'; +declare function assertionErrorMessage(error: AssertionErrorWithStack, options: DiffOptions): string; +export default assertionErrorMessage; diff --git a/packages/jest-jasmine2/build/assertionErrorMessage.js b/packages/jest-jasmine2/build/assertionErrorMessage.js new file mode 100644 index 000000000000..c5ea9f2c6c8e --- /dev/null +++ b/packages/jest-jasmine2/build/assertionErrorMessage.js @@ -0,0 +1,156 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _chalk = _interopRequireDefault(require('chalk')); + +var _jestMatcherUtils = require('jest-matcher-utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 assertOperatorsMap = { + '!=': 'notEqual', + '!==': 'notStrictEqual', + '==': 'equal', + '===': 'strictEqual' +}; +const humanReadableOperators = { + deepEqual: 'to deeply equal', + deepStrictEqual: 'to deeply and strictly equal', + equal: 'to be equal', + notDeepEqual: 'not to deeply equal', + notDeepStrictEqual: 'not to deeply and strictly equal', + notEqual: 'to not be equal', + notStrictEqual: 'not be strictly equal', + strictEqual: 'to strictly be equal' +}; + +const getOperatorName = (operator, stack) => { + if (typeof operator === 'string') { + return assertOperatorsMap[operator] || operator; + } + + if (stack.match('.doesNotThrow')) { + return 'doesNotThrow'; + } + + if (stack.match('.throws')) { + return 'throws'; + } // this fallback is only needed for versions older than node 10 + + if (stack.match('.fail')) { + return 'fail'; + } + + return ''; +}; + +const operatorMessage = operator => { + const niceOperatorName = getOperatorName(operator, ''); + const humanReadableOperator = humanReadableOperators[niceOperatorName]; + return typeof operator === 'string' + ? `${humanReadableOperator || niceOperatorName} to:\n` + : ''; +}; + +const assertThrowingMatcherHint = operatorName => + operatorName + ? _chalk.default.dim('assert') + + _chalk.default.dim('.' + operatorName + '(') + + _chalk.default.red('function') + + _chalk.default.dim(')') + : ''; + +const assertMatcherHint = (operator, operatorName, expected) => { + let message = ''; + + if (operator === '==' && expected === true) { + message = + _chalk.default.dim('assert') + + _chalk.default.dim('(') + + _chalk.default.red('received') + + _chalk.default.dim(')'); + } else if (operatorName) { + message = + _chalk.default.dim('assert') + + _chalk.default.dim('.' + operatorName + '(') + + _chalk.default.red('received') + + _chalk.default.dim(', ') + + _chalk.default.green('expected') + + _chalk.default.dim(')'); + } + + return message; +}; + +function assertionErrorMessage(error, options) { + const {expected, actual, generatedMessage, message, operator, stack} = error; + const diffString = (0, _jestMatcherUtils.diff)(expected, actual, options); + const hasCustomMessage = !generatedMessage; + const operatorName = getOperatorName(operator, stack); + const trimmedStack = stack + .replace(message, '') + .replace(/AssertionError(.*)/g, ''); + + if (operatorName === 'doesNotThrow') { + return ( + buildHintString(assertThrowingMatcherHint(operatorName)) + + _chalk.default.reset(`Expected the function not to throw an error.\n`) + + _chalk.default.reset(`Instead, it threw:\n`) + + ` ${(0, _jestMatcherUtils.printReceived)(actual)}` + + _chalk.default.reset( + hasCustomMessage ? '\n\nMessage:\n ' + message : '' + ) + + trimmedStack + ); + } + + if (operatorName === 'throws') { + return ( + buildHintString(assertThrowingMatcherHint(operatorName)) + + _chalk.default.reset(`Expected the function to throw an error.\n`) + + _chalk.default.reset(`But it didn't throw anything.`) + + _chalk.default.reset( + hasCustomMessage ? '\n\nMessage:\n ' + message : '' + ) + + trimmedStack + ); + } + + if (operatorName === 'fail') { + return ( + buildHintString(assertMatcherHint(operator, operatorName, expected)) + + _chalk.default.reset(hasCustomMessage ? 'Message:\n ' + message : '') + + trimmedStack + ); + } + + return ( + buildHintString(assertMatcherHint(operator, operatorName, expected)) + + _chalk.default.reset(`Expected value ${operatorMessage(operator)}`) + + ` ${(0, _jestMatcherUtils.printExpected)(expected)}\n` + + _chalk.default.reset(`Received:\n`) + + ` ${(0, _jestMatcherUtils.printReceived)(actual)}` + + _chalk.default.reset(hasCustomMessage ? '\n\nMessage:\n ' + message : '') + + (diffString ? `\n\nDifference:\n\n${diffString}` : '') + + trimmedStack + ); +} + +function buildHintString(hint) { + return hint ? hint + '\n\n' : ''; +} + +var _default = assertionErrorMessage; +exports.default = _default; diff --git a/packages/jest-jasmine2/build/each.d.ts b/packages/jest-jasmine2/build/each.d.ts new file mode 100644 index 000000000000..c49770bdf72b --- /dev/null +++ b/packages/jest-jasmine2/build/each.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { JestEnvironment } from '@jest/environment'; +declare const _default: (environment: JestEnvironment) => void; +export default _default; diff --git a/packages/jest-jasmine2/build/each.js b/packages/jest-jasmine2/build/each.js new file mode 100644 index 000000000000..953ab7dd6d2c --- /dev/null +++ b/packages/jest-jasmine2/build/each.js @@ -0,0 +1,46 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _jestEach = require('jest-each'); + +/** + * 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. + */ +var _default = environment => { + environment.global.it.each = (0, _jestEach.bind)(environment.global.it); + environment.global.fit.each = (0, _jestEach.bind)(environment.global.fit); + environment.global.xit.each = (0, _jestEach.bind)(environment.global.xit); + environment.global.describe.each = (0, _jestEach.bind)( + environment.global.describe, + false + ); + environment.global.xdescribe.each = (0, _jestEach.bind)( + environment.global.xdescribe, + false + ); + environment.global.fdescribe.each = (0, _jestEach.bind)( + environment.global.fdescribe, + false + ); + environment.global.it.concurrent.each = (0, _jestEach.bind)( + environment.global.it.concurrent, + false + ); + environment.global.it.concurrent.only.each = (0, _jestEach.bind)( + environment.global.it.concurrent.only, + false + ); + environment.global.it.concurrent.skip.each = (0, _jestEach.bind)( + environment.global.it.concurrent.skip, + false + ); +}; + +exports.default = _default; diff --git a/packages/jest-jasmine2/build/errorOnPrivate.d.ts b/packages/jest-jasmine2/build/errorOnPrivate.d.ts new file mode 100644 index 000000000000..081dd14a7d91 --- /dev/null +++ b/packages/jest-jasmine2/build/errorOnPrivate.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Global } from '@jest/types'; +export declare function installErrorOnPrivate(global: Global.Global): void; diff --git a/packages/jest-jasmine2/build/errorOnPrivate.js b/packages/jest-jasmine2/build/errorOnPrivate.js new file mode 100644 index 000000000000..cf8d87ffcf4e --- /dev/null +++ b/packages/jest-jasmine2/build/errorOnPrivate.js @@ -0,0 +1,66 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.installErrorOnPrivate = installErrorOnPrivate; + +var _jestUtil = require('jest-util'); + +/** + * 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. + */ +// prettier-ignore +const disabledGlobals = { + fail: 'Illegal usage of global `fail`, prefer throwing an error, or the `done.fail` callback.', + pending: 'Illegal usage of global `pending`, prefer explicitly skipping a test using `test.skip`', + spyOn: 'Illegal usage of global `spyOn`, prefer `jest.spyOn`.', + spyOnProperty: 'Illegal usage of global `spyOnProperty`, prefer `jest.spyOn`.' +}; +// prettier-ignore +const disabledJasmineMethods = { + addMatchers: 'Illegal usage of `jasmine.addMatchers`, prefer `expect.extends`.', + any: 'Illegal usage of `jasmine.any`, prefer `expect.any`.', + anything: 'Illegal usage of `jasmine.anything`, prefer `expect.anything`.', + arrayContaining: 'Illegal usage of `jasmine.arrayContaining`, prefer `expect.arrayContaining`.', + createSpy: 'Illegal usage of `jasmine.createSpy`, prefer `jest.fn`.', + objectContaining: 'Illegal usage of `jasmine.objectContaining`, prefer `expect.objectContaining`.', + stringMatching: 'Illegal usage of `jasmine.stringMatching`, prefer `expect.stringMatching`.' +}; + +function installErrorOnPrivate(global) { + const jasmine = global.jasmine; + Object.keys(disabledGlobals).forEach(functionName => { + global[functionName] = () => { + throwAtFunction(disabledGlobals[functionName], global[functionName]); + }; + }); + Object.keys(disabledJasmineMethods).forEach(methodName => { + // @ts-expect-error + jasmine[methodName] = () => { + throwAtFunction(disabledJasmineMethods[methodName], jasmine[methodName]); + }; + }); + + function set() { + throwAtFunction( + 'Illegal usage of `jasmine.DEFAULT_TIMEOUT_INTERVAL`, prefer `jest.setTimeout`.', + set + ); + } + + const original = jasmine.DEFAULT_TIMEOUT_INTERVAL; + Object.defineProperty(jasmine, 'DEFAULT_TIMEOUT_INTERVAL', { + configurable: true, + enumerable: true, + get: () => original, + set + }); +} + +function throwAtFunction(message, fn) { + throw new _jestUtil.ErrorWithStack(message, fn); +} diff --git a/packages/jest-jasmine2/build/expectationResultFactory.d.ts b/packages/jest-jasmine2/build/expectationResultFactory.d.ts new file mode 100644 index 000000000000..f3101fadf06a --- /dev/null +++ b/packages/jest-jasmine2/build/expectationResultFactory.d.ts @@ -0,0 +1,16 @@ +/** + * 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. + */ +import type { FailedAssertion } from '@jest/test-result'; +export declare type Options = { + matcherName: string; + passed: boolean; + actual?: any; + error?: any; + expected?: any; + message?: string | null; +}; +export default function expectationResultFactory(options: Options, initError?: Error): FailedAssertion; diff --git a/packages/jest-jasmine2/build/expectationResultFactory.js b/packages/jest-jasmine2/build/expectationResultFactory.js new file mode 100644 index 000000000000..bc64749cf4e8 --- /dev/null +++ b/packages/jest-jasmine2/build/expectationResultFactory.js @@ -0,0 +1,97 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = expectationResultFactory; + +var _prettyFormat = _interopRequireDefault(require('pretty-format')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +function messageFormatter({error, message, passed}) { + if (passed) { + return 'Passed.'; + } + + if (message) { + return message; + } + + if (typeof error === 'string') { + return error; + } + + if ( + // duck-type Error, see #2549 + error && + typeof error === 'object' && + typeof error.message === 'string' && + typeof error.name === 'string' + ) { + if (error.message === '') { + return error.name; + } + + return `${error.name}: ${error.message}`; + } + + return `thrown: ${(0, _prettyFormat.default)(error, { + maxDepth: 3 + })}`; +} + +function stackFormatter(options, initError, errorMessage) { + if (options.passed) { + return ''; + } + + if (options.error) { + if (typeof options.error.stack === 'string') { + return options.error.stack; + } + + if (options.error === errorMessage) { + return errorMessage; + } + } + + if (initError) { + return errorMessage.trimRight() + '\n\n' + initError.stack; + } + + return new Error(errorMessage).stack; +} + +function expectationResultFactory(options, initError) { + const message = messageFormatter(options); + const stack = stackFormatter(options, initError, message); + + if (options.passed) { + return { + error: options.error, + matcherName: options.matcherName, + message, + passed: options.passed, + stack + }; + } + + return { + actual: options.actual, + error: options.error, + expected: options.expected, + matcherName: options.matcherName, + message, + passed: options.passed, + stack + }; +} diff --git a/packages/jest-jasmine2/build/index.d.ts b/packages/jest-jasmine2/build/index.d.ts new file mode 100644 index 000000000000..e0e56a850b02 --- /dev/null +++ b/packages/jest-jasmine2/build/index.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ +import type { JestEnvironment } from '@jest/environment'; +import type { TestResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import type Runtime from 'jest-runtime'; +export type { Jasmine } from './types'; +export default function jasmine2(globalConfig: Config.GlobalConfig, config: Config.ProjectConfig, environment: JestEnvironment, runtime: Runtime, testPath: string): Promise; diff --git a/packages/jest-jasmine2/build/index.js b/packages/jest-jasmine2/build/index.js new file mode 100644 index 000000000000..0c8a77966c1a --- /dev/null +++ b/packages/jest-jasmine2/build/index.js @@ -0,0 +1,247 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = jasmine2; + +var path = _interopRequireWildcard(require('path')); + +var _sourceMap = require('@jest/source-map'); + +var _each = _interopRequireDefault(require('./each')); + +var _errorOnPrivate = require('./errorOnPrivate'); + +var _jasmineAsyncInstall = _interopRequireDefault( + require('./jasmineAsyncInstall') +); + +var _reporter = _interopRequireDefault(require('./reporter')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 JASMINE = require.resolve('./jasmine/jasmineLight'); + +const jestEachBuildDir = path.dirname(require.resolve('jest-each')); + +async function jasmine2(globalConfig, config, environment, runtime, testPath) { + const reporter = new _reporter.default(globalConfig, config, testPath); + const jasmineFactory = runtime.requireInternalModule(JASMINE); + const jasmine = jasmineFactory.create({ + process, + testPath, + testTimeout: globalConfig.testTimeout + }); + const env = jasmine.getEnv(); + + const jasmineInterface = jasmineFactory._interface(jasmine, env); + + Object.assign(environment.global, jasmineInterface); + env.addReporter(jasmineInterface.jsApiReporter); // TODO: Remove config option if V8 exposes some way of getting location of caller + // in a future version + + if (config.testLocationInResults === true) { + function wrapIt(original) { + const wrapped = (testName, fn, timeout) => { + var _stack$getFileName; + + const sourcemaps = runtime.getSourceMaps(); + let stack = (0, _sourceMap.getCallsite)(1, sourcemaps); + const it = original(testName, fn, timeout); + + if ( + (_stack$getFileName = stack.getFileName()) !== null && + _stack$getFileName !== void 0 && + _stack$getFileName.startsWith(jestEachBuildDir) + ) { + stack = (0, _sourceMap.getCallsite)(4, sourcemaps); + } // @ts-expect-error + + it.result.__callsite = stack; + return it; + }; + + return wrapped; + } + + environment.global.it = wrapIt(environment.global.it); + environment.global.xit = wrapIt(environment.global.xit); + environment.global.fit = wrapIt(environment.global.fit); + } + + (0, _jasmineAsyncInstall.default)(globalConfig, environment.global); + (0, _each.default)(environment); + environment.global.test = environment.global.it; + environment.global.it.only = environment.global.fit; + environment.global.it.todo = env.todo; + environment.global.it.skip = environment.global.xit; + environment.global.xtest = environment.global.xit; + environment.global.describe.skip = environment.global.xdescribe; + environment.global.describe.only = environment.global.fdescribe; + + if (config.timers === 'fake' || config.timers === 'modern') { + environment.fakeTimersModern.useFakeTimers(); + } else if (config.timers === 'legacy') { + environment.fakeTimers.useFakeTimers(); + } + + env.beforeEach(() => { + if (config.resetModules) { + runtime.resetModules(); + } + + if (config.clearMocks) { + runtime.clearAllMocks(); + } + + if (config.resetMocks) { + runtime.resetAllMocks(); + + if (config.timers === 'legacy') { + environment.fakeTimers.useFakeTimers(); + } + } + + if (config.restoreMocks) { + runtime.restoreAllMocks(); + } + }); + env.addReporter(reporter); + runtime + .requireInternalModule(path.resolve(__dirname, './jestExpect.js')) + .default({ + expand: globalConfig.expand + }); + + if (globalConfig.errorOnDeprecated) { + (0, _errorOnPrivate.installErrorOnPrivate)(environment.global); + } else { + Object.defineProperty(jasmine, 'DEFAULT_TIMEOUT_INTERVAL', { + configurable: true, + enumerable: true, + + get() { + return this._DEFAULT_TIMEOUT_INTERVAL; + }, + + set(value) { + this._DEFAULT_TIMEOUT_INTERVAL = value; + } + }); + } + + const snapshotState = runtime + .requireInternalModule(path.resolve(__dirname, './setup_jest_globals.js')) + .default({ + config, + globalConfig, + localRequire: runtime.requireModule.bind(runtime), + testPath + }); + + for (const path of config.setupFilesAfterEnv) { + const esm = runtime.unstable_shouldLoadAsEsm(path); + + if (esm) { + await runtime.unstable_importModule(path); + } else { + runtime.requireModule(path); + } + } + + if (globalConfig.testNamePattern) { + const testNameRegex = new RegExp(globalConfig.testNamePattern, 'i'); + + env.specFilter = spec => testNameRegex.test(spec.getFullName()); + } + + const esm = runtime.unstable_shouldLoadAsEsm(testPath); + + if (esm) { + await runtime.unstable_importModule(testPath); + } else { + runtime.requireModule(testPath); + } + + await env.execute(); + const results = await reporter.getResults(); + return addSnapshotData(results, snapshotState); +} + +const addSnapshotData = (results, snapshotState) => { + results.testResults.forEach(({fullName, status}) => { + if (status === 'pending' || status === 'failed') { + // if test is skipped or failed, we don't want to mark + // its snapshots as obsolete. + snapshotState.markSnapshotsAsCheckedForTest(fullName); + } + }); + const uncheckedCount = snapshotState.getUncheckedCount(); + const uncheckedKeys = snapshotState.getUncheckedKeys(); + + if (uncheckedCount) { + snapshotState.removeUncheckedKeys(); + } + + const status = snapshotState.save(); + results.snapshot.fileDeleted = status.deleted; + results.snapshot.added = snapshotState.added; + results.snapshot.matched = snapshotState.matched; + results.snapshot.unmatched = snapshotState.unmatched; + results.snapshot.updated = snapshotState.updated; + results.snapshot.unchecked = !status.deleted ? uncheckedCount : 0; // Copy the array to prevent memory leaks + + results.snapshot.uncheckedKeys = Array.from(uncheckedKeys); + return results; +}; diff --git a/packages/jest-jasmine2/build/isError.d.ts b/packages/jest-jasmine2/build/isError.d.ts new file mode 100644 index 000000000000..b21d235992af --- /dev/null +++ b/packages/jest-jasmine2/build/isError.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +export default function isError(potentialError: any): { + isError: boolean; + message: string | null; +}; diff --git a/packages/jest-jasmine2/build/isError.js b/packages/jest-jasmine2/build/isError.js new file mode 100644 index 000000000000..d2a5487e2e81 --- /dev/null +++ b/packages/jest-jasmine2/build/isError.js @@ -0,0 +1,36 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = isError; + +var _prettyFormat = _interopRequireDefault(require('pretty-format')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +function isError(potentialError) { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + // duck-type Error, see #2549 + const isError = + potentialError !== null && + typeof potentialError === 'object' && + typeof potentialError.message === 'string' && + typeof potentialError.name === 'string'; + const message = isError + ? null + : `Failed: ${(0, _prettyFormat.default)(potentialError, { + maxDepth: 3 + })}`; + return { + isError, + message + }; +} diff --git a/packages/jest-jasmine2/build/jasmine/CallTracker.d.ts b/packages/jest-jasmine2/build/jasmine/CallTracker.d.ts new file mode 100644 index 000000000000..1692e9c917c7 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/CallTracker.d.ts @@ -0,0 +1,25 @@ +/** + * 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. + * + */ +export declare type Context = { + object: unknown; + args: Array; + returnValue?: unknown; +}; +declare class CallTracker { + track: (context: Context) => void; + any: () => boolean; + count: () => number; + argsFor: (index: number) => Array; + all: () => Array; + allArgs: () => Array; + first: () => Context; + mostRecent: () => Context; + reset: () => void; + constructor(); +} +export default CallTracker; diff --git a/packages/jest-jasmine2/build/jasmine/CallTracker.js b/packages/jest-jasmine2/build/jasmine/CallTracker.js new file mode 100644 index 000000000000..c0ce7505c1d8 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/CallTracker.js @@ -0,0 +1,121 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * 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. + * + */ +// This file is a heavily modified fork of Jasmine. Original license: + +/* +Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +class CallTracker { + constructor() { + _defineProperty(this, 'track', void 0); + + _defineProperty(this, 'any', void 0); + + _defineProperty(this, 'count', void 0); + + _defineProperty(this, 'argsFor', void 0); + + _defineProperty(this, 'all', void 0); + + _defineProperty(this, 'allArgs', void 0); + + _defineProperty(this, 'first', void 0); + + _defineProperty(this, 'mostRecent', void 0); + + _defineProperty(this, 'reset', void 0); + + let calls = []; + + this.track = function (context) { + calls.push(context); + }; + + this.any = function () { + return !!calls.length; + }; + + this.count = function () { + return calls.length; + }; + + this.argsFor = function (index) { + const call = calls[index]; + return call ? call.args : []; + }; + + this.all = function () { + return calls; + }; + + this.allArgs = function () { + const callArgs = []; + + for (let i = 0; i < calls.length; i++) { + callArgs.push(calls[i].args); + } + + return callArgs; + }; + + this.first = function () { + return calls[0]; + }; + + this.mostRecent = function () { + return calls[calls.length - 1]; + }; + + this.reset = function () { + calls = []; + }; + } +} + +var _default = CallTracker; +exports.default = _default; diff --git a/packages/jest-jasmine2/build/jasmine/Env.d.ts b/packages/jest-jasmine2/build/jasmine/Env.d.ts new file mode 100644 index 000000000000..2e808cfb4cd0 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/Env.d.ts @@ -0,0 +1,43 @@ +/** + * 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. + * + */ +import { QueueableFn } from '../queueRunner'; +import type { AssertionErrorWithStack, Jasmine, Reporter, SpecDefinitionsFn, Spy } from '../types'; +import type { default as Spec } from './Spec'; +import type Suite from './Suite'; +export default function (j$: Jasmine): { + new (_options?: Record | undefined): { + specFilter: (spec: Spec) => boolean; + catchExceptions: (value: unknown) => boolean; + throwOnExpectationFailure: (value: unknown) => void; + catchingExceptions: () => boolean; + topSuite: () => Suite; + fail: (error: Error | AssertionErrorWithStack) => void; + pending: (message: string) => void; + afterAll: (afterAllFunction: QueueableFn['fn'], timeout?: number | undefined) => void; + fit: (description: string, fn: QueueableFn['fn'], timeout?: number | undefined) => Spec; + throwingExpectationFailures: () => boolean; + randomizeTests: (value: unknown) => void; + randomTests: () => boolean; + seed: (value: unknown) => unknown; + execute: (runnablesToRun?: string[] | undefined, suiteTree?: Suite | undefined) => Promise; + fdescribe: (description: string, specDefinitions: SpecDefinitionsFn) => Suite; + spyOn: (obj: Record, methodName: string, accessType?: "configurable" | "enumerable" | "value" | "writable" | "get" | "set" | undefined) => Spy; + beforeEach: (beforeEachFunction: QueueableFn['fn'], timeout?: number | undefined) => void; + afterEach: (afterEachFunction: QueueableFn['fn'], timeout?: number | undefined) => void; + clearReporters: () => void; + addReporter: (reporterToAdd: Reporter) => void; + it: (description: string, fn: QueueableFn['fn'], timeout?: number | undefined) => Spec; + xdescribe: (description: string, specDefinitions: SpecDefinitionsFn) => Suite; + xit: (description: string, fn: QueueableFn['fn'], timeout?: number | undefined) => Spec; + beforeAll: (beforeAllFunction: QueueableFn['fn'], timeout?: number | undefined) => void; + todo: () => Spec; + provideFallbackReporter: (reporterToAdd: Reporter) => void; + allowRespy: (allow: boolean) => void; + describe: (description: string, specDefinitions: SpecDefinitionsFn) => Suite; + }; +}; diff --git a/packages/jest-jasmine2/build/jasmine/Env.js b/packages/jest-jasmine2/build/jasmine/Env.js new file mode 100644 index 000000000000..06a39ea7f58c --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/Env.js @@ -0,0 +1,727 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = _default; + +var _assert = require('assert'); + +var _jestUtil = require('jest-util'); + +var _assertionErrorMessage = _interopRequireDefault( + require('../assertionErrorMessage') +); + +var _isError = _interopRequireDefault(require('../isError')); + +var _queueRunner = _interopRequireDefault(require('../queueRunner')); + +var _treeProcessor = _interopRequireDefault(require('../treeProcessor')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +function _default(j$) { + var _temp; + + // https://github.com/typescript-eslint/typescript-eslint/pull/2833 + // eslint-disable-next-line @typescript-eslint/no-unused-vars + return ( + (_temp = class Env { + constructor(_options) { + _defineProperty(this, 'specFilter', void 0); + + _defineProperty(this, 'catchExceptions', void 0); + + _defineProperty(this, 'throwOnExpectationFailure', void 0); + + _defineProperty(this, 'catchingExceptions', void 0); + + _defineProperty(this, 'topSuite', void 0); + + _defineProperty(this, 'fail', void 0); + + _defineProperty(this, 'pending', void 0); + + _defineProperty(this, 'afterAll', void 0); + + _defineProperty(this, 'fit', void 0); + + _defineProperty(this, 'throwingExpectationFailures', void 0); + + _defineProperty(this, 'randomizeTests', void 0); + + _defineProperty(this, 'randomTests', void 0); + + _defineProperty(this, 'seed', void 0); + + _defineProperty(this, 'execute', void 0); + + _defineProperty(this, 'fdescribe', void 0); + + _defineProperty(this, 'spyOn', void 0); + + _defineProperty(this, 'beforeEach', void 0); + + _defineProperty(this, 'afterEach', void 0); + + _defineProperty(this, 'clearReporters', void 0); + + _defineProperty(this, 'addReporter', void 0); + + _defineProperty(this, 'it', void 0); + + _defineProperty(this, 'xdescribe', void 0); + + _defineProperty(this, 'xit', void 0); + + _defineProperty(this, 'beforeAll', void 0); + + _defineProperty(this, 'todo', void 0); + + _defineProperty(this, 'provideFallbackReporter', void 0); + + _defineProperty(this, 'allowRespy', void 0); + + _defineProperty(this, 'describe', void 0); + + let totalSpecsDefined = 0; + let catchExceptions = true; + const realSetTimeout = global.setTimeout; + const realClearTimeout = global.clearTimeout; + const runnableResources = {}; + const currentlyExecutingSuites = []; + let currentSpec = null; + let throwOnExpectationFailure = false; + let random = false; + let seed = null; + let nextSpecId = 0; + let nextSuiteId = 0; + + const getNextSpecId = function () { + return 'spec' + nextSpecId++; + }; + + const getNextSuiteId = function () { + return 'suite' + nextSuiteId++; + }; + + const topSuite = new j$.Suite({ + id: getNextSuiteId(), + description: '', + + getTestPath() { + return j$.testPath; + } + }); + let currentDeclarationSuite = topSuite; + + const currentSuite = function () { + return currentlyExecutingSuites[currentlyExecutingSuites.length - 1]; + }; + + const currentRunnable = function () { + return currentSpec || currentSuite(); + }; + + const reporter = new j$.ReportDispatcher([ + 'jasmineStarted', + 'jasmineDone', + 'suiteStarted', + 'suiteDone', + 'specStarted', + 'specDone' + ]); + + this.specFilter = function () { + return true; + }; + + const defaultResourcesForRunnable = function (id, _parentRunnableId) { + const resources = { + spies: [] + }; + runnableResources[id] = resources; + }; + + const clearResourcesForRunnable = function (id) { + spyRegistry.clearSpies(); + delete runnableResources[id]; + }; + + const beforeAndAfterFns = function (suite) { + return function () { + let afters = []; + let befores = []; + + while (suite) { + befores = befores.concat(suite.beforeFns); + afters = afters.concat(suite.afterFns); + suite = suite.parentSuite; + } + + return { + befores: befores.reverse(), + afters + }; + }; + }; + + const getSpecName = function (spec, suite) { + const fullName = [spec.description]; + const suiteFullName = suite.getFullName(); + + if (suiteFullName !== '') { + fullName.unshift(suiteFullName); + } + + return fullName.join(' '); + }; + + this.catchExceptions = function (value) { + catchExceptions = !!value; + return catchExceptions; + }; + + this.catchingExceptions = function () { + return catchExceptions; + }; + + this.throwOnExpectationFailure = function (value) { + throwOnExpectationFailure = !!value; + }; + + this.throwingExpectationFailures = function () { + return throwOnExpectationFailure; + }; + + this.randomizeTests = function (value) { + random = !!value; + }; + + this.randomTests = function () { + return random; + }; + + this.seed = function (value) { + if (value) { + seed = value; + } + + return seed; + }; + + const queueRunnerFactory = options => { + options.clearTimeout = realClearTimeout; + options.fail = this.fail; + options.setTimeout = realSetTimeout; + return (0, _queueRunner.default)(options); + }; + + this.topSuite = function () { + return topSuite; + }; + + const uncaught = err => { + if (currentSpec) { + currentSpec.onException(err); + currentSpec.cancel(); + } else { + console.error('Unhandled error'); + console.error(err.stack); + } + }; + + let oldListenersException; + let oldListenersRejection; + + const executionSetup = function () { + // Need to ensure we are the only ones handling these exceptions. + oldListenersException = process + .listeners('uncaughtException') + .slice(); + oldListenersRejection = process + .listeners('unhandledRejection') + .slice(); + j$.process.removeAllListeners('uncaughtException'); + j$.process.removeAllListeners('unhandledRejection'); + j$.process.on('uncaughtException', uncaught); + j$.process.on('unhandledRejection', uncaught); + }; + + const executionTeardown = function () { + j$.process.removeListener('uncaughtException', uncaught); + j$.process.removeListener('unhandledRejection', uncaught); // restore previous exception handlers + + oldListenersException.forEach(listener => { + j$.process.on('uncaughtException', listener); + }); + oldListenersRejection.forEach(listener => { + j$.process.on('unhandledRejection', listener); + }); + }; + + this.execute = async function (runnablesToRun, suiteTree = topSuite) { + if (!runnablesToRun) { + if (focusedRunnables.length) { + runnablesToRun = focusedRunnables; + } else { + runnablesToRun = [suiteTree.id]; + } + } + + if (currentlyExecutingSuites.length === 0) { + executionSetup(); + } + + const lastDeclarationSuite = currentDeclarationSuite; + await (0, _treeProcessor.default)({ + nodeComplete(suite) { + if (!suite.disabled) { + clearResourcesForRunnable(suite.id); + } + + currentlyExecutingSuites.pop(); + + if (suite === topSuite) { + reporter.jasmineDone({ + failedExpectations: topSuite.result.failedExpectations + }); + } else { + reporter.suiteDone(suite.getResult()); + } + }, + + nodeStart(suite) { + currentlyExecutingSuites.push(suite); + defaultResourcesForRunnable( + suite.id, + suite.parentSuite && suite.parentSuite.id + ); + + if (suite === topSuite) { + reporter.jasmineStarted({ + totalSpecsDefined + }); + } else { + reporter.suiteStarted(suite.result); + } + }, + + queueRunnerFactory, + runnableIds: runnablesToRun, + tree: suiteTree + }); + currentDeclarationSuite = lastDeclarationSuite; + + if (currentlyExecutingSuites.length === 0) { + executionTeardown(); + } + }; + + this.addReporter = function (reporterToAdd) { + reporter.addReporter(reporterToAdd); + }; + + this.provideFallbackReporter = function (reporterToAdd) { + reporter.provideFallbackReporter(reporterToAdd); + }; + + this.clearReporters = function () { + reporter.clearReporters(); + }; + + const spyRegistry = new j$.SpyRegistry({ + currentSpies() { + if (!currentRunnable()) { + throw new Error( + 'Spies must be created in a before function or a spec' + ); + } + + return runnableResources[currentRunnable().id].spies; + } + }); + + this.allowRespy = function (allow) { + spyRegistry.allowRespy(allow); + }; + + this.spyOn = function (...args) { + return spyRegistry.spyOn.apply(spyRegistry, args); + }; + + const suiteFactory = function (description) { + const suite = new j$.Suite({ + id: getNextSuiteId(), + description, + parentSuite: currentDeclarationSuite, + throwOnExpectationFailure, + + getTestPath() { + return j$.testPath; + } + }); + return suite; + }; + + this.describe = function (description, specDefinitions) { + const suite = suiteFactory(description); + + if (specDefinitions === undefined) { + throw new Error( + `Missing second argument. It must be a callback function.` + ); + } + + if (typeof specDefinitions !== 'function') { + throw new Error( + `Invalid second argument, ${specDefinitions}. It must be a callback function.` + ); + } + + if (specDefinitions.length > 0) { + throw new Error('describe does not expect any arguments'); + } + + if (currentDeclarationSuite.markedPending) { + suite.pend(); + } + + if (currentDeclarationSuite.markedTodo) { + // @ts-expect-error TODO Possible error: Suite does not have todo method + suite.todo(); + } + + addSpecsToSuite(suite, specDefinitions); + return suite; + }; + + this.xdescribe = function (description, specDefinitions) { + const suite = suiteFactory(description); + suite.pend(); + addSpecsToSuite(suite, specDefinitions); + return suite; + }; + + const focusedRunnables = []; + + this.fdescribe = function (description, specDefinitions) { + const suite = suiteFactory(description); + suite.isFocused = true; + focusedRunnables.push(suite.id); + unfocusAncestor(); + addSpecsToSuite(suite, specDefinitions); + return suite; + }; + + const addSpecsToSuite = (suite, specDefinitions) => { + const parentSuite = currentDeclarationSuite; + parentSuite.addChild(suite); + currentDeclarationSuite = suite; + let declarationError = undefined; + let describeReturnValue; + + try { + describeReturnValue = specDefinitions.call(suite); + } catch (e) { + declarationError = e; + } + + if ((0, _jestUtil.isPromise)(describeReturnValue)) { + declarationError = new Error( + 'Returning a Promise from "describe" is not supported. Tests must be defined synchronously.' + ); + } else if (describeReturnValue !== undefined) { + declarationError = new Error( + 'A "describe" callback must not return a value.' + ); + } + + if (declarationError) { + this.it('encountered a declaration exception', () => { + throw declarationError; + }); + } + + currentDeclarationSuite = parentSuite; + }; + + function findFocusedAncestor(suite) { + while (suite) { + if (suite.isFocused) { + return suite.id; + } + + suite = suite.parentSuite; + } + + return null; + } + + function unfocusAncestor() { + const focusedAncestor = findFocusedAncestor(currentDeclarationSuite); + + if (focusedAncestor) { + for (let i = 0; i < focusedRunnables.length; i++) { + if (focusedRunnables[i] === focusedAncestor) { + focusedRunnables.splice(i, 1); + break; + } + } + } + } + + const specFactory = (description, fn, suite, timeout) => { + totalSpecsDefined++; + const spec = new j$.Spec({ + id: getNextSpecId(), + beforeAndAfterFns: beforeAndAfterFns(suite), + resultCallback: specResultCallback, + + getSpecName(spec) { + return getSpecName(spec, suite); + }, + + getTestPath() { + return j$.testPath; + }, + + onStart: specStarted, + description, + queueRunnerFactory, + + userContext() { + return suite.clonedSharedUserContext(); + }, + + queueableFn: { + fn, + + timeout() { + return timeout || j$._DEFAULT_TIMEOUT_INTERVAL; + } + }, + throwOnExpectationFailure + }); + + if (!this.specFilter(spec)) { + spec.disable(); + } + + return spec; + + function specResultCallback(result) { + clearResourcesForRunnable(spec.id); + currentSpec = null; + reporter.specDone(result); + } + + function specStarted(spec) { + currentSpec = spec; + defaultResourcesForRunnable(spec.id, suite.id); + reporter.specStarted(spec.result); + } + }; + + this.it = function (description, fn, timeout) { + if (typeof description !== 'string') { + throw new Error( + `Invalid first argument, ${description}. It must be a string.` + ); + } + + if (fn === undefined) { + throw new Error( + 'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.' + ); + } + + if (typeof fn !== 'function') { + throw new Error( + `Invalid second argument, ${fn}. It must be a callback function.` + ); + } + + const spec = specFactory( + description, + fn, + currentDeclarationSuite, + timeout + ); + + if (currentDeclarationSuite.markedPending) { + spec.pend(); + } // When a test is defined inside another, jasmine will not run it. + // This check throws an error to warn the user about the edge-case. + + if (currentSpec !== null) { + throw new Error( + `Tests cannot be nested. Test "${spec.description}" cannot run because it is nested within "${currentSpec.description}".` + ); + } + + currentDeclarationSuite.addChild(spec); + return spec; + }; + + this.xit = function (...args) { + const spec = this.it.apply(this, args); + spec.pend('Temporarily disabled with xit'); + return spec; + }; + + this.todo = function () { + const description = arguments[0]; + + if (arguments.length !== 1 || typeof description !== 'string') { + throw new _jestUtil.ErrorWithStack( + 'Todo must be called with only a description.', + this.todo + ); + } + + const spec = specFactory( + description, + () => {}, + currentDeclarationSuite + ); + + if (currentDeclarationSuite.markedPending) { + spec.pend(); + } else { + spec.todo(); + } + + currentDeclarationSuite.addChild(spec); + return spec; + }; + + this.fit = function (description, fn, timeout) { + const spec = specFactory( + description, + fn, + currentDeclarationSuite, + timeout + ); + currentDeclarationSuite.addChild(spec); + + if (currentDeclarationSuite.markedPending) { + spec.pend(); + } else { + focusedRunnables.push(spec.id); + } + + unfocusAncestor(); + return spec; + }; + + this.beforeEach = function (beforeEachFunction, timeout) { + currentDeclarationSuite.beforeEach({ + fn: beforeEachFunction, + + timeout() { + return timeout || j$._DEFAULT_TIMEOUT_INTERVAL; + } + }); + }; + + this.beforeAll = function (beforeAllFunction, timeout) { + currentDeclarationSuite.beforeAll({ + fn: beforeAllFunction, + + timeout() { + return timeout || j$._DEFAULT_TIMEOUT_INTERVAL; + } + }); + }; + + this.afterEach = function (afterEachFunction, timeout) { + currentDeclarationSuite.afterEach({ + fn: afterEachFunction, + + timeout() { + return timeout || j$._DEFAULT_TIMEOUT_INTERVAL; + } + }); + }; + + this.afterAll = function (afterAllFunction, timeout) { + currentDeclarationSuite.afterAll({ + fn: afterAllFunction, + + timeout() { + return timeout || j$._DEFAULT_TIMEOUT_INTERVAL; + } + }); + }; + + this.pending = function (message) { + let fullMessage = j$.Spec.pendingSpecExceptionMessage; + + if (message) { + fullMessage += message; + } + + throw fullMessage; + }; + + this.fail = function (error) { + let checkIsError; + let message; + + if ( + error instanceof _assert.AssertionError || + (error && error.name === _assert.AssertionError.name) + ) { + checkIsError = false; // @ts-expect-error TODO Possible error: j$.Spec does not have expand property + + message = (0, _assertionErrorMessage.default)(error, { + expand: j$.Spec.expand + }); + } else { + const check = (0, _isError.default)(error); + checkIsError = check.isError; + message = check.message; + } + + const errorAsErrorObject = checkIsError ? error : new Error(message); + const runnable = currentRunnable(); + + if (!runnable) { + errorAsErrorObject.message = + 'Caught error after test environment was torn down\n\n' + + errorAsErrorObject.message; + throw errorAsErrorObject; + } + + runnable.addExpectationResult(false, { + matcherName: '', + passed: false, + expected: '', + actual: '', + message, + error: errorAsErrorObject + }); + }; + } + }), + _temp + ); +} diff --git a/packages/jest-jasmine2/build/jasmine/JsApiReporter.d.ts b/packages/jest-jasmine2/build/jasmine/JsApiReporter.d.ts new file mode 100644 index 000000000000..ece0b732df40 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/JsApiReporter.d.ts @@ -0,0 +1,31 @@ +/** + * 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. + * + */ +import type { Reporter, RunDetails } from '../types'; +import type { SpecResult } from './Spec'; +import type { SuiteResult } from './Suite'; +import type Timer from './Timer'; +export default class JsApiReporter implements Reporter { + started: boolean; + finished: boolean; + runDetails: RunDetails; + jasmineStarted: (runDetails: RunDetails) => void; + jasmineDone: (runDetails: RunDetails) => void; + status: () => unknown; + executionTime: () => unknown; + suiteStarted: (result: SuiteResult) => void; + suiteDone: (result: SuiteResult) => void; + suiteResults: (index: number, length: number) => Array; + suites: () => Record; + specResults: (index: number, length: number) => Array; + specDone: (result: SpecResult) => void; + specs: () => Array; + specStarted: (spec: SpecResult) => void; + constructor(options: { + timer?: Timer; + }); +} diff --git a/packages/jest-jasmine2/build/jasmine/JsApiReporter.js b/packages/jest-jasmine2/build/jasmine/JsApiReporter.js new file mode 100644 index 000000000000..982f4469d630 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/JsApiReporter.js @@ -0,0 +1,173 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * 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. + * + */ +// This file is a heavily modified fork of Jasmine. Original license: + +/* +Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* eslint-disable sort-keys */ +const noopTimer = { + start() {}, + + elapsed() { + return 0; + } +}; + +class JsApiReporter { + constructor(options) { + _defineProperty(this, 'started', void 0); + + _defineProperty(this, 'finished', void 0); + + _defineProperty(this, 'runDetails', void 0); + + _defineProperty(this, 'jasmineStarted', void 0); + + _defineProperty(this, 'jasmineDone', void 0); + + _defineProperty(this, 'status', void 0); + + _defineProperty(this, 'executionTime', void 0); + + _defineProperty(this, 'suiteStarted', void 0); + + _defineProperty(this, 'suiteDone', void 0); + + _defineProperty(this, 'suiteResults', void 0); + + _defineProperty(this, 'suites', void 0); + + _defineProperty(this, 'specResults', void 0); + + _defineProperty(this, 'specDone', void 0); + + _defineProperty(this, 'specs', void 0); + + _defineProperty(this, 'specStarted', void 0); + + const timer = options.timer || noopTimer; + let status = 'loaded'; + this.started = false; + this.finished = false; + this.runDetails = {}; + + this.jasmineStarted = () => { + this.started = true; + status = 'started'; + timer.start(); + }; + + let executionTime; + + function validateAfterAllExceptions({failedExpectations}) { + if (failedExpectations && failedExpectations.length > 0) { + throw failedExpectations[0]; + } + } + + this.jasmineDone = function (runDetails) { + validateAfterAllExceptions(runDetails); + this.finished = true; + this.runDetails = runDetails; + executionTime = timer.elapsed(); + status = 'done'; + }; + + this.status = function () { + return status; + }; + + const suites = []; + const suites_hash = {}; + + this.specStarted = function () {}; + + this.suiteStarted = function (result) { + suites_hash[result.id] = result; + }; + + this.suiteDone = function (result) { + storeSuite(result); + }; + + this.suiteResults = function (index, length) { + return suites.slice(index, index + length); + }; + + function storeSuite(result) { + suites.push(result); + suites_hash[result.id] = result; + } + + this.suites = function () { + return suites_hash; + }; + + const specs = []; + + this.specDone = function (result) { + specs.push(result); + }; + + this.specResults = function (index, length) { + return specs.slice(index, index + length); + }; + + this.specs = function () { + return specs; + }; + + this.executionTime = function () { + return executionTime; + }; + } +} + +exports.default = JsApiReporter; diff --git a/packages/jest-jasmine2/build/jasmine/ReportDispatcher.d.ts b/packages/jest-jasmine2/build/jasmine/ReportDispatcher.d.ts new file mode 100644 index 000000000000..9f89d05c77d9 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/ReportDispatcher.d.ts @@ -0,0 +1,22 @@ +/** + * 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. + * + */ +import type { Reporter, RunDetails } from '../types'; +import type { SpecResult } from './Spec'; +import type { SuiteResult } from './Suite'; +export default class ReportDispatcher implements Reporter { + addReporter: (reporter: Reporter) => void; + provideFallbackReporter: (reporter: Reporter) => void; + clearReporters: () => void; + jasmineDone: (runDetails: RunDetails) => void; + jasmineStarted: (runDetails: RunDetails) => void; + specDone: (result: SpecResult) => void; + specStarted: (spec: SpecResult) => void; + suiteDone: (result: SuiteResult) => void; + suiteStarted: (result: SuiteResult) => void; + constructor(methods: Array); +} diff --git a/packages/jest-jasmine2/build/jasmine/ReportDispatcher.js b/packages/jest-jasmine2/build/jasmine/ReportDispatcher.js new file mode 100644 index 000000000000..3acead9f1d79 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/ReportDispatcher.js @@ -0,0 +1,127 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * 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. + * + */ +// This file is a heavily modified fork of Jasmine. Original license: + +/* +Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* eslint-disable local/prefer-spread-eventually, local/prefer-rest-params-eventually */ +class ReportDispatcher { + // @ts-expect-error + // @ts-expect-error + // @ts-expect-error + // @ts-expect-error + // @ts-expect-error + // @ts-expect-error + constructor(methods) { + _defineProperty(this, 'addReporter', void 0); + + _defineProperty(this, 'provideFallbackReporter', void 0); + + _defineProperty(this, 'clearReporters', void 0); + + _defineProperty(this, 'jasmineDone', void 0); + + _defineProperty(this, 'jasmineStarted', void 0); + + _defineProperty(this, 'specDone', void 0); + + _defineProperty(this, 'specStarted', void 0); + + _defineProperty(this, 'suiteDone', void 0); + + _defineProperty(this, 'suiteStarted', void 0); + + const dispatchedMethods = methods || []; + + for (let i = 0; i < dispatchedMethods.length; i++) { + const method = dispatchedMethods[i]; + + this[method] = (function (m) { + return function () { + dispatch(m, arguments); + }; + })(method); + } + + let reporters = []; + let fallbackReporter = null; + + this.addReporter = function (reporter) { + reporters.push(reporter); + }; + + this.provideFallbackReporter = function (reporter) { + fallbackReporter = reporter; + }; + + this.clearReporters = function () { + reporters = []; + }; + + return this; + + function dispatch(method, args) { + if (reporters.length === 0 && fallbackReporter !== null) { + reporters.push(fallbackReporter); + } + + for (let i = 0; i < reporters.length; i++) { + const reporter = reporters[i]; + + if (reporter[method]) { + // @ts-expect-error + reporter[method].apply(reporter, args); + } + } + } + } +} + +exports.default = ReportDispatcher; diff --git a/packages/jest-jasmine2/build/jasmine/Spec.d.ts b/packages/jest-jasmine2/build/jasmine/Spec.d.ts new file mode 100644 index 000000000000..e6ba7ceff1af --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/Spec.d.ts @@ -0,0 +1,81 @@ +/** + * 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. + * + */ +import type { FailedAssertion, Milliseconds, Status } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import ExpectationFailed from '../ExpectationFailed'; +import expectationResultFactory, { Options as ExpectationResultFactoryOptions } from '../expectationResultFactory'; +import type { QueueableFn, default as queueRunner } from '../queueRunner'; +import type { AssertionErrorWithStack } from '../types'; +export declare type Attributes = { + id: string; + resultCallback: (result: Spec['result']) => void; + description: string; + throwOnExpectationFailure: unknown; + getTestPath: () => Config.Path; + queueableFn: QueueableFn; + beforeAndAfterFns: () => { + befores: Array; + afters: Array; + }; + userContext: () => unknown; + onStart: (context: Spec) => void; + getSpecName: (spec: Spec) => string; + queueRunnerFactory: typeof queueRunner; +}; +export declare type SpecResult = { + id: string; + description: string; + fullName: string; + duration?: Milliseconds; + failedExpectations: Array; + testPath: Config.Path; + passedExpectations: Array>; + pendingReason: string; + status: Status; + __callsite?: { + getColumnNumber: () => number; + getLineNumber: () => number; + }; +}; +export default class Spec { + id: string; + description: string; + resultCallback: (result: SpecResult) => void; + queueableFn: QueueableFn; + beforeAndAfterFns: () => { + befores: Array; + afters: Array; + }; + userContext: () => unknown; + onStart: (spec: Spec) => void; + getSpecName: (spec: Spec) => string; + queueRunnerFactory: typeof queueRunner; + throwOnExpectationFailure: boolean; + initError: Error; + result: SpecResult; + disabled?: boolean; + currentRun?: ReturnType; + markedTodo?: boolean; + markedPending?: boolean; + expand?: boolean; + static pendingSpecExceptionMessage: string; + static isPendingSpecException(e: Error): boolean; + constructor(attrs: Attributes); + addExpectationResult(passed: boolean, data: ExpectationResultFactoryOptions, isError?: boolean): void; + execute(onComplete?: () => void, enabled?: boolean): void; + cancel(): void; + onException(error: ExpectationFailed | AssertionErrorWithStack): void; + disable(): void; + pend(message?: string): void; + todo(): void; + getResult(): SpecResult; + status(enabled?: boolean): "todo" | "passed" | "failed" | "pending" | "disabled"; + isExecutable(): boolean; + getFullName(): string; + isAssertionError(error: Error): boolean; +} diff --git a/packages/jest-jasmine2/build/jasmine/Spec.js b/packages/jest-jasmine2/build/jasmine/Spec.js new file mode 100644 index 000000000000..32fbc4962d37 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/Spec.js @@ -0,0 +1,298 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _assert = require('assert'); + +var _ExpectationFailed = _interopRequireDefault( + require('../ExpectationFailed') +); + +var _assertionErrorMessage = _interopRequireDefault( + require('../assertionErrorMessage') +); + +var _expectationResultFactory = _interopRequireDefault( + require('../expectationResultFactory') +); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class Spec { + static isPendingSpecException(e) { + return !!( + e && + e.toString && + e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1 + ); + } + + constructor(attrs) { + _defineProperty(this, 'id', void 0); + + _defineProperty(this, 'description', void 0); + + _defineProperty(this, 'resultCallback', void 0); + + _defineProperty(this, 'queueableFn', void 0); + + _defineProperty(this, 'beforeAndAfterFns', void 0); + + _defineProperty(this, 'userContext', void 0); + + _defineProperty(this, 'onStart', void 0); + + _defineProperty(this, 'getSpecName', void 0); + + _defineProperty(this, 'queueRunnerFactory', void 0); + + _defineProperty(this, 'throwOnExpectationFailure', void 0); + + _defineProperty(this, 'initError', void 0); + + _defineProperty(this, 'result', void 0); + + _defineProperty(this, 'disabled', void 0); + + _defineProperty(this, 'currentRun', void 0); + + _defineProperty(this, 'markedTodo', void 0); + + _defineProperty(this, 'markedPending', void 0); + + _defineProperty(this, 'expand', void 0); + + this.resultCallback = attrs.resultCallback || function () {}; + + this.id = attrs.id; + this.description = attrs.description || ''; + this.queueableFn = attrs.queueableFn; + + this.beforeAndAfterFns = + attrs.beforeAndAfterFns || + function () { + return { + befores: [], + afters: [] + }; + }; + + this.userContext = + attrs.userContext || + function () { + return {}; + }; + + this.onStart = attrs.onStart || function () {}; + + this.getSpecName = + attrs.getSpecName || + function () { + return ''; + }; + + this.queueRunnerFactory = attrs.queueRunnerFactory || function () {}; + + this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; + this.initError = new Error(); + this.initError.name = ''; // Without this line v8 stores references to all closures + // in the stack in the Error object. This line stringifies the stack + // property to allow garbage-collecting objects on the stack + // https://crbug.com/v8/7142 + + this.initError.stack = this.initError.stack; + this.queueableFn.initError = this.initError; // @ts-expect-error + + this.result = { + id: this.id, + description: this.description, + fullName: this.getFullName(), + failedExpectations: [], + passedExpectations: [], + pendingReason: '', + testPath: attrs.getTestPath() + }; + } + + addExpectationResult(passed, data, isError) { + const expectationResult = (0, _expectationResultFactory.default)( + data, + this.initError + ); + + if (passed) { + this.result.passedExpectations.push(expectationResult); + } else { + this.result.failedExpectations.push(expectationResult); + + if (this.throwOnExpectationFailure && !isError) { + throw new _ExpectationFailed.default(); + } + } + } + + execute(onComplete, enabled) { + const self = this; + this.onStart(this); + + if ( + !this.isExecutable() || + this.markedPending || + this.markedTodo || + enabled === false + ) { + complete(enabled); + return; + } + + const fns = this.beforeAndAfterFns(); + const allFns = fns.befores.concat(this.queueableFn).concat(fns.afters); + this.currentRun = this.queueRunnerFactory({ + queueableFns: allFns, + + onException() { + // @ts-expect-error + self.onException.apply(self, arguments); + }, + + userContext: this.userContext(), + setTimeout, + clearTimeout, + fail: () => {} + }); + this.currentRun.then(() => complete(true)); + + function complete(enabledAgain) { + self.result.status = self.status(enabledAgain); + self.resultCallback(self.result); + + if (onComplete) { + onComplete(); + } + } + } + + cancel() { + if (this.currentRun) { + this.currentRun.cancel(); + } + } + + onException(error) { + if (Spec.isPendingSpecException(error)) { + this.pend(extractCustomPendingMessage(error)); + return; + } + + if (error instanceof _ExpectationFailed.default) { + return; + } + + this.addExpectationResult( + false, + { + matcherName: '', + passed: false, + expected: '', + actual: '', + error: this.isAssertionError(error) + ? (0, _assertionErrorMessage.default)(error, { + expand: this.expand + }) + : error + }, + true + ); + } + + disable() { + this.disabled = true; + } + + pend(message) { + this.markedPending = true; + + if (message) { + this.result.pendingReason = message; + } + } + + todo() { + this.markedTodo = true; + } + + getResult() { + this.result.status = this.status(); + return this.result; + } + + status(enabled) { + if (this.disabled || enabled === false) { + return 'disabled'; + } + + if (this.markedTodo) { + return 'todo'; + } + + if (this.markedPending) { + return 'pending'; + } + + if (this.result.failedExpectations.length > 0) { + return 'failed'; + } else { + return 'passed'; + } + } + + isExecutable() { + return !this.disabled; + } + + getFullName() { + return this.getSpecName(this); + } + + isAssertionError(error) { + return ( + error instanceof _assert.AssertionError || + (error && error.name === _assert.AssertionError.name) + ); + } +} + +exports.default = Spec; + +_defineProperty(Spec, 'pendingSpecExceptionMessage', void 0); + +Spec.pendingSpecExceptionMessage = '=> marked Pending'; + +const extractCustomPendingMessage = function (e) { + const fullMessage = e.toString(); + const boilerplateStart = fullMessage.indexOf( + Spec.pendingSpecExceptionMessage + ); + const boilerplateEnd = + boilerplateStart + Spec.pendingSpecExceptionMessage.length; + return fullMessage.substr(boilerplateEnd); +}; diff --git a/packages/jest-jasmine2/build/jasmine/SpyStrategy.d.ts b/packages/jest-jasmine2/build/jasmine/SpyStrategy.d.ts new file mode 100644 index 000000000000..a54754d42f6d --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/SpyStrategy.d.ts @@ -0,0 +1,22 @@ +/** + * 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. + * + */ +export default class SpyStrategy { + identity: () => string; + exec: (...args: Array) => unknown; + callThrough: () => unknown; + returnValue: (value: unknown) => unknown; + returnValues: () => unknown; + throwError: (something: string | Error) => unknown; + callFake: (fn: Function) => unknown; + stub: (fn: Function) => unknown; + constructor({ name, fn, getSpy, }?: { + name?: string; + fn?: Function; + getSpy?: () => unknown; + }); +} diff --git a/packages/jest-jasmine2/build/jasmine/SpyStrategy.js b/packages/jest-jasmine2/build/jasmine/SpyStrategy.js new file mode 100644 index 000000000000..fc7de390bc55 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/SpyStrategy.js @@ -0,0 +1,143 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * 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. + * + */ +// This file is a heavily modified fork of Jasmine. Original license: + +/* +Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* eslint-disable local/ban-types-eventually, local/prefer-rest-params-eventually */ +class SpyStrategy { + constructor({ + name = 'unknown', + fn = function () {}, + getSpy = function () {} + } = {}) { + _defineProperty(this, 'identity', void 0); + + _defineProperty(this, 'exec', void 0); + + _defineProperty(this, 'callThrough', void 0); + + _defineProperty(this, 'returnValue', void 0); + + _defineProperty(this, 'returnValues', void 0); + + _defineProperty(this, 'throwError', void 0); + + _defineProperty(this, 'callFake', void 0); + + _defineProperty(this, 'stub', void 0); + + const identity = name; + const originalFn = fn; + + let plan = function () {}; + + this.identity = function () { + return identity; + }; + + this.exec = function () { + return plan.apply(this, arguments); + }; + + this.callThrough = function () { + plan = originalFn; + return getSpy(); + }; + + this.returnValue = function (value) { + plan = function () { + return value; + }; + + return getSpy(); + }; + + this.returnValues = function () { + const values = Array.prototype.slice.call(arguments); + + plan = function () { + return values.shift(); + }; + + return getSpy(); + }; + + this.throwError = function (something) { + const error = + something instanceof Error ? something : new Error(something); + + plan = function () { + throw error; + }; + + return getSpy(); + }; + + this.callFake = function (fn) { + if (typeof fn !== 'function') { + throw new Error( + 'Argument passed to callFake should be a function, got ' + fn + ); + } + + plan = fn; + return getSpy(); + }; + + this.stub = function (_fn) { + plan = function () {}; + + return getSpy(); + }; + } +} + +exports.default = SpyStrategy; diff --git a/packages/jest-jasmine2/build/jasmine/Suite.d.ts b/packages/jest-jasmine2/build/jasmine/Suite.d.ts new file mode 100644 index 000000000000..7bfd374e06ca --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/Suite.d.ts @@ -0,0 +1,61 @@ +/** + * 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. + * + */ +import type { Config } from '@jest/types'; +import expectationResultFactory from '../expectationResultFactory'; +import type { QueueableFn } from '../queueRunner'; +import type Spec from './Spec'; +export declare type SuiteResult = { + id: string; + description: string; + fullName: string; + failedExpectations: Array>; + testPath: Config.Path; + status?: string; +}; +export declare type Attributes = { + id: string; + parentSuite?: Suite; + description: string; + throwOnExpectationFailure?: boolean; + getTestPath: () => Config.Path; +}; +export default class Suite { + id: string; + parentSuite?: Suite; + description: string; + throwOnExpectationFailure: boolean; + beforeFns: Array; + afterFns: Array; + beforeAllFns: Array; + afterAllFns: Array; + disabled: boolean; + children: Array; + result: SuiteResult; + sharedContext?: object; + markedPending: boolean; + markedTodo: boolean; + isFocused: boolean; + constructor(attrs: Attributes); + getFullName(): string; + disable(): void; + pend(_message?: string): void; + beforeEach(fn: QueueableFn): void; + beforeAll(fn: QueueableFn): void; + afterEach(fn: QueueableFn): void; + afterAll(fn: QueueableFn): void; + addChild(child: Suite | Spec): void; + status(): "failed" | "pending" | "disabled" | "finished"; + isExecutable(): boolean; + canBeReentered(): boolean; + getResult(): SuiteResult; + sharedUserContext(): object; + clonedSharedUserContext(): object; + onException(...args: Parameters): void; + addExpectationResult(...args: Parameters): void; + execute(..._args: Array): void; +} diff --git a/packages/jest-jasmine2/build/jasmine/Suite.js b/packages/jest-jasmine2/build/jasmine/Suite.js new file mode 100644 index 000000000000..79f9f15e0b85 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/Suite.js @@ -0,0 +1,235 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _jestUtil = require('jest-util'); + +var _ExpectationFailed = _interopRequireDefault( + require('../ExpectationFailed') +); + +var _expectationResultFactory = _interopRequireDefault( + require('../expectationResultFactory') +); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class Suite { + constructor(attrs) { + _defineProperty(this, 'id', void 0); + + _defineProperty(this, 'parentSuite', void 0); + + _defineProperty(this, 'description', void 0); + + _defineProperty(this, 'throwOnExpectationFailure', void 0); + + _defineProperty(this, 'beforeFns', void 0); + + _defineProperty(this, 'afterFns', void 0); + + _defineProperty(this, 'beforeAllFns', void 0); + + _defineProperty(this, 'afterAllFns', void 0); + + _defineProperty(this, 'disabled', void 0); + + _defineProperty(this, 'children', void 0); + + _defineProperty(this, 'result', void 0); + + _defineProperty(this, 'sharedContext', void 0); + + _defineProperty(this, 'markedPending', void 0); + + _defineProperty(this, 'markedTodo', void 0); + + _defineProperty(this, 'isFocused', void 0); + + this.markedPending = false; + this.markedTodo = false; + this.isFocused = false; + this.id = attrs.id; + this.parentSuite = attrs.parentSuite; + this.description = (0, _jestUtil.convertDescriptorToString)( + attrs.description + ); + this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; + this.beforeFns = []; + this.afterFns = []; + this.beforeAllFns = []; + this.afterAllFns = []; + this.disabled = false; + this.children = []; + this.result = { + id: this.id, + description: this.description, + fullName: this.getFullName(), + failedExpectations: [], + testPath: attrs.getTestPath() + }; + } + + getFullName() { + const fullName = []; + + for ( + let parentSuite = this; + parentSuite; + parentSuite = parentSuite.parentSuite + ) { + if (parentSuite.parentSuite) { + fullName.unshift(parentSuite.description); + } + } + + return fullName.join(' '); + } + + disable() { + this.disabled = true; + } + + pend(_message) { + this.markedPending = true; + } + + beforeEach(fn) { + this.beforeFns.unshift(fn); + } + + beforeAll(fn) { + this.beforeAllFns.push(fn); + } + + afterEach(fn) { + this.afterFns.unshift(fn); + } + + afterAll(fn) { + this.afterAllFns.unshift(fn); + } + + addChild(child) { + this.children.push(child); + } + + status() { + if (this.disabled) { + return 'disabled'; + } + + if (this.markedPending) { + return 'pending'; + } + + if (this.result.failedExpectations.length > 0) { + return 'failed'; + } else { + return 'finished'; + } + } + + isExecutable() { + return !this.disabled; + } + + canBeReentered() { + return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0; + } + + getResult() { + this.result.status = this.status(); + return this.result; + } + + sharedUserContext() { + if (!this.sharedContext) { + this.sharedContext = {}; + } + + return this.sharedContext; + } + + clonedSharedUserContext() { + return this.sharedUserContext(); + } + + onException(...args) { + if (args[0] instanceof _ExpectationFailed.default) { + return; + } + + if (isAfterAll(this.children)) { + const data = { + matcherName: '', + passed: false, + expected: '', + actual: '', + error: arguments[0] + }; + this.result.failedExpectations.push( + (0, _expectationResultFactory.default)(data) + ); + } else { + for (let i = 0; i < this.children.length; i++) { + const child = this.children[i]; + child.onException.apply(child, args); + } + } + } + + addExpectationResult(...args) { + if (isAfterAll(this.children) && isFailure(args)) { + const data = args[1]; + this.result.failedExpectations.push( + (0, _expectationResultFactory.default)(data) + ); + + if (this.throwOnExpectationFailure) { + throw new _ExpectationFailed.default(); + } + } else { + for (let i = 0; i < this.children.length; i++) { + const child = this.children[i]; + + try { + child.addExpectationResult.apply(child, args); + } catch { + // keep going + } + } + } + } + + execute(..._args) {} +} + +exports.default = Suite; + +function isAfterAll(children) { + return children && children[0] && children[0].result.status; +} + +function isFailure(args) { + return !args[0]; +} diff --git a/packages/jest-jasmine2/build/jasmine/Timer.d.ts b/packages/jest-jasmine2/build/jasmine/Timer.d.ts new file mode 100644 index 000000000000..17c97e615e0d --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/Timer.d.ts @@ -0,0 +1,14 @@ +/** + * 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. + * + */ +export default class Timer { + start: () => void; + elapsed: () => number; + constructor(options?: { + now?: () => number; + }); +} diff --git a/packages/jest-jasmine2/build/jasmine/Timer.js b/packages/jest-jasmine2/build/jasmine/Timer.js new file mode 100644 index 000000000000..61839408e3e4 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/Timer.js @@ -0,0 +1,79 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * 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. + * + */ +// This file is a heavily modified fork of Jasmine. Original license: + +/* +Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +const defaultNow = (function (Date) { + return function () { + return new Date().getTime(); + }; +})(Date); + +class Timer { + constructor(options) { + _defineProperty(this, 'start', void 0); + + _defineProperty(this, 'elapsed', void 0); + + options = options || {}; + const now = options.now || defaultNow; + let startTime; + + this.start = function () { + startTime = now(); + }; + + this.elapsed = function () { + return now() - startTime; + }; + } +} + +exports.default = Timer; diff --git a/packages/jest-jasmine2/build/jasmine/createSpy.d.ts b/packages/jest-jasmine2/build/jasmine/createSpy.d.ts new file mode 100644 index 000000000000..f1d017352fb7 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/createSpy.d.ts @@ -0,0 +1,13 @@ +/** + * 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. + * + */ +import type { Spy } from '../types'; +interface Fn extends Record { + (): unknown; +} +declare function createSpy(name: string, originalFn: Fn): Spy; +export default createSpy; diff --git a/packages/jest-jasmine2/build/jasmine/createSpy.js b/packages/jest-jasmine2/build/jasmine/createSpy.js new file mode 100644 index 000000000000..17a6728ad3da --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/createSpy.js @@ -0,0 +1,88 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _CallTracker = _interopRequireDefault(require('./CallTracker')); + +var _SpyStrategy = _interopRequireDefault(require('./SpyStrategy')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + * + */ +// This file is a heavily modified fork of Jasmine. Original license: + +/* +Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* eslint-disable sort-keys, local/prefer-rest-params-eventually */ +function createSpy(name, originalFn) { + const spyStrategy = new _SpyStrategy.default({ + name, + fn: originalFn, + + getSpy() { + return spy; + } + }); + const callTracker = new _CallTracker.default(); + + const spy = function (...args) { + const callData = { + object: this, + args: Array.prototype.slice.apply(arguments) + }; + callTracker.track(callData); + const returnValue = spyStrategy.exec.apply(this, args); + callData.returnValue = returnValue; + return returnValue; + }; + + for (const prop in originalFn) { + if (prop === 'and' || prop === 'calls') { + throw new Error( + "Jasmine spies would overwrite the 'and' and 'calls' properties " + + 'on the object being spied upon' + ); + } + + spy[prop] = originalFn[prop]; + } + + spy.and = spyStrategy; + spy.calls = callTracker; + return spy; +} + +var _default = createSpy; +exports.default = _default; diff --git a/packages/jest-jasmine2/build/jasmine/jasmineLight.d.ts b/packages/jest-jasmine2/build/jasmine/jasmineLight.d.ts new file mode 100644 index 000000000000..ad4fe02f4da7 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/jasmineLight.d.ts @@ -0,0 +1,27 @@ +/** + * 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. + * + */ +import type { Jasmine, SpecDefinitionsFn } from '../types'; +import JsApiReporter from './JsApiReporter'; +export declare const create: (createOptions: Record) => Jasmine; +export declare const _interface: (jasmine: Jasmine, env: any) => { + describe(description: string, specDefinitions: SpecDefinitionsFn): any; + xdescribe(description: string, specDefinitions: SpecDefinitionsFn): any; + fdescribe(description: string, specDefinitions: SpecDefinitionsFn): any; + it(): any; + xit(): any; + fit(): any; + beforeEach(): any; + afterEach(): any; + beforeAll(): any; + afterAll(): any; + pending(): any; + fail(): any; + spyOn(obj: Record, methodName: string, accessType?: string | undefined): any; + jsApiReporter: JsApiReporter; + jasmine: Jasmine; +}; diff --git a/packages/jest-jasmine2/build/jasmine/jasmineLight.js b/packages/jest-jasmine2/build/jasmine/jasmineLight.js new file mode 100644 index 000000000000..04655d34a788 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/jasmineLight.js @@ -0,0 +1,171 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports._interface = exports.create = void 0; + +var _Env = _interopRequireDefault(require('./Env')); + +var _JsApiReporter = _interopRequireDefault(require('./JsApiReporter')); + +var _ReportDispatcher = _interopRequireDefault(require('./ReportDispatcher')); + +var _Spec = _interopRequireDefault(require('./Spec')); + +var _Suite = _interopRequireDefault(require('./Suite')); + +var _Timer = _interopRequireDefault(require('./Timer')); + +var _createSpy = _interopRequireDefault(require('./createSpy')); + +var _spyRegistry = _interopRequireDefault(require('./spyRegistry')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + * + */ +// This file is a heavily modified fork of Jasmine. Original license: + +/* +Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* eslint-disable sort-keys, local/prefer-spread-eventually, local/prefer-rest-params-eventually */ +const create = function (createOptions) { + const j$ = {...createOptions}; + j$._DEFAULT_TIMEOUT_INTERVAL = createOptions.testTimeout || 5000; + + j$.getEnv = function (options) { + const env = (j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options)); //jasmine. singletons in here (setTimeout blah blah). + + return env; + }; + + j$.createSpy = _createSpy.default; + j$.Env = (0, _Env.default)(j$); + j$.JsApiReporter = _JsApiReporter.default; + j$.ReportDispatcher = _ReportDispatcher.default; + j$.Spec = _Spec.default; + j$.SpyRegistry = _spyRegistry.default; + j$.Suite = _Suite.default; + j$.Timer = _Timer.default; + j$.version = '2.5.2-light'; + return j$; +}; // Interface is a reserved word in strict mode, so can't export it as ESM + +exports.create = create; + +const _interface = function (jasmine, env) { + const jasmineInterface = { + describe(description, specDefinitions) { + return env.describe(description, specDefinitions); + }, + + xdescribe(description, specDefinitions) { + return env.xdescribe(description, specDefinitions); + }, + + fdescribe(description, specDefinitions) { + return env.fdescribe(description, specDefinitions); + }, + + it() { + return env.it.apply(env, arguments); + }, + + xit() { + return env.xit.apply(env, arguments); + }, + + fit() { + return env.fit.apply(env, arguments); + }, + + beforeEach() { + if (typeof arguments[0] !== 'function') { + throw new Error( + 'Invalid first argument. It must be a callback function.' + ); + } + + return env.beforeEach.apply(env, arguments); + }, + + afterEach() { + if (typeof arguments[0] !== 'function') { + throw new Error( + 'Invalid first argument. It must be a callback function.' + ); + } + + return env.afterEach.apply(env, arguments); + }, + + beforeAll() { + if (typeof arguments[0] !== 'function') { + throw new Error( + 'Invalid first argument. It must be a callback function.' + ); + } + + return env.beforeAll.apply(env, arguments); + }, + + afterAll() { + if (typeof arguments[0] !== 'function') { + throw new Error( + 'Invalid first argument. It must be a callback function.' + ); + } + + return env.afterAll.apply(env, arguments); + }, + + pending() { + return env.pending.apply(env, arguments); + }, + + fail() { + return env.fail.apply(env, arguments); + }, + + spyOn(obj, methodName, accessType) { + return env.spyOn(obj, methodName, accessType); + }, + + jsApiReporter: new jasmine.JsApiReporter({ + timer: new jasmine.Timer() + }), + jasmine + }; + return jasmineInterface; +}; + +exports._interface = _interface; diff --git a/packages/jest-jasmine2/build/jasmine/spyRegistry.d.ts b/packages/jest-jasmine2/build/jasmine/spyRegistry.d.ts new file mode 100644 index 000000000000..c5f215c311c8 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/spyRegistry.d.ts @@ -0,0 +1,18 @@ +/** + * 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. + * + */ +import type { Spy } from '../types'; +export default class SpyRegistry { + allowRespy: (allow: unknown) => void; + spyOn: (obj: Record, methodName: string, accessType?: keyof PropertyDescriptor) => Spy; + clearSpies: () => void; + respy: unknown; + private _spyOnProperty; + constructor({ currentSpies, }?: { + currentSpies?: () => Array; + }); +} diff --git a/packages/jest-jasmine2/build/jasmine/spyRegistry.js b/packages/jest-jasmine2/build/jasmine/spyRegistry.js new file mode 100644 index 000000000000..ce200ff0b220 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmine/spyRegistry.js @@ -0,0 +1,222 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _CallTracker = _interopRequireDefault(require('./CallTracker')); + +var _SpyStrategy = _interopRequireDefault(require('./SpyStrategy')); + +var _createSpy = _interopRequireDefault(require('./createSpy')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const formatErrorMsg = (domain, usage) => { + const usageDefinition = usage ? '\nUsage: ' + usage : ''; + return msg => domain + ' : ' + msg + usageDefinition; +}; + +function isSpy(putativeSpy) { + if (!putativeSpy) { + return false; + } + + return ( + putativeSpy.and instanceof _SpyStrategy.default && + putativeSpy.calls instanceof _CallTracker.default + ); +} + +const getErrorMsg = formatErrorMsg('', 'spyOn(, )'); + +class SpyRegistry { + constructor({currentSpies = () => []} = {}) { + _defineProperty(this, 'allowRespy', void 0); + + _defineProperty(this, 'spyOn', void 0); + + _defineProperty(this, 'clearSpies', void 0); + + _defineProperty(this, 'respy', void 0); + + _defineProperty(this, '_spyOnProperty', void 0); + + this.allowRespy = function (allow) { + this.respy = allow; + }; + + this.spyOn = (obj, methodName, accessType) => { + if (accessType) { + return this._spyOnProperty(obj, methodName, accessType); + } + + if (obj === void 0) { + throw new Error( + getErrorMsg( + 'could not find an object to spy upon for ' + methodName + '()' + ) + ); + } + + if (methodName === void 0) { + throw new Error(getErrorMsg('No method name supplied')); + } + + if (obj[methodName] === void 0) { + throw new Error(getErrorMsg(methodName + '() method does not exist')); + } + + if (obj[methodName] && isSpy(obj[methodName])) { + if (this.respy) { + return obj[methodName]; + } else { + throw new Error( + getErrorMsg(methodName + ' has already been spied upon') + ); + } + } + + let descriptor; + + try { + descriptor = Object.getOwnPropertyDescriptor(obj, methodName); + } catch { + // IE 8 doesn't support `definePropery` on non-DOM nodes + } + + if (descriptor && !(descriptor.writable || descriptor.set)) { + throw new Error( + getErrorMsg(methodName + ' is not declared writable or has no setter') + ); + } + + const originalMethod = obj[methodName]; + const spiedMethod = (0, _createSpy.default)(methodName, originalMethod); + let restoreStrategy; + + if (Object.prototype.hasOwnProperty.call(obj, methodName)) { + restoreStrategy = function () { + obj[methodName] = originalMethod; + }; + } else { + restoreStrategy = function () { + if (!delete obj[methodName]) { + obj[methodName] = originalMethod; + } + }; + } + + currentSpies().push({ + restoreObjectToOriginalState: restoreStrategy + }); + obj[methodName] = spiedMethod; + return spiedMethod; + }; + + this._spyOnProperty = function (obj, propertyName, accessType = 'get') { + if (!obj) { + throw new Error( + getErrorMsg( + 'could not find an object to spy upon for ' + propertyName + ) + ); + } + + if (!propertyName) { + throw new Error(getErrorMsg('No property name supplied')); + } + + let descriptor; + + try { + descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); + } catch { + // IE 8 doesn't support `definePropery` on non-DOM nodes + } + + if (!descriptor) { + throw new Error(getErrorMsg(propertyName + ' property does not exist')); + } + + if (!descriptor.configurable) { + throw new Error( + getErrorMsg(propertyName + ' is not declared configurable') + ); + } + + if (!descriptor[accessType]) { + throw new Error( + getErrorMsg( + 'Property ' + + propertyName + + ' does not have access type ' + + accessType + ) + ); + } + + if (obj[propertyName] && isSpy(obj[propertyName])) { + if (this.respy) { + return obj[propertyName]; + } else { + throw new Error( + getErrorMsg(propertyName + ' has already been spied upon') + ); + } + } + + const originalDescriptor = descriptor; + const spiedProperty = (0, _createSpy.default)( + propertyName, + descriptor[accessType] + ); + let restoreStrategy; + + if (Object.prototype.hasOwnProperty.call(obj, propertyName)) { + restoreStrategy = function () { + Object.defineProperty(obj, propertyName, originalDescriptor); + }; + } else { + restoreStrategy = function () { + delete obj[propertyName]; + }; + } + + currentSpies().push({ + restoreObjectToOriginalState: restoreStrategy + }); + const spiedDescriptor = {...descriptor, [accessType]: spiedProperty}; + Object.defineProperty(obj, propertyName, spiedDescriptor); + return spiedProperty; + }; + + this.clearSpies = function () { + const spies = currentSpies(); + + for (let i = spies.length - 1; i >= 0; i--) { + const spyEntry = spies[i]; + spyEntry.restoreObjectToOriginalState(); + } + }; + } +} + +exports.default = SpyRegistry; diff --git a/packages/jest-jasmine2/build/jasmineAsyncInstall.d.ts b/packages/jest-jasmine2/build/jasmineAsyncInstall.d.ts new file mode 100644 index 000000000000..34382fd54cd2 --- /dev/null +++ b/packages/jest-jasmine2/build/jasmineAsyncInstall.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config, Global } from '@jest/types'; +export default function jasmineAsyncInstall(globalConfig: Config.GlobalConfig, global: Global.Global): void; diff --git a/packages/jest-jasmine2/build/jasmineAsyncInstall.js b/packages/jest-jasmine2/build/jasmineAsyncInstall.js new file mode 100644 index 000000000000..c8c79d111cff --- /dev/null +++ b/packages/jest-jasmine2/build/jasmineAsyncInstall.js @@ -0,0 +1,190 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = jasmineAsyncInstall; + +var _co = _interopRequireDefault(require('co')); + +var _isGeneratorFn = _interopRequireDefault(require('is-generator-fn')); + +var _throat = _interopRequireDefault(require('throat')); + +var _isError = _interopRequireDefault(require('./isError')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Promise = global[Symbol.for('jest-native-promise')] || global.Promise; + +function isPromise(obj) { + return obj && typeof obj.then === 'function'; +} + +const doneFnNoop = () => {}; + +doneFnNoop.fail = () => {}; + +function promisifyLifeCycleFunction(originalFn, env) { + return function (fn, timeout) { + if (!fn) { + // @ts-expect-error: missing fn arg is handled by originalFn + return originalFn.call(env); + } + + const hasDoneCallback = typeof fn === 'function' && fn.length > 0; + + if (hasDoneCallback) { + // Jasmine will handle it + return originalFn.call(env, fn, timeout); + } + + const extraError = new Error(); // Without this line v8 stores references to all closures + // in the stack in the Error object. This line stringifies the stack + // property to allow garbage-collecting objects on the stack + // https://crbug.com/v8/7142 + + extraError.stack = extraError.stack; // We make *all* functions async and run `done` right away if they + // didn't return a promise. + + const asyncJestLifecycle = function (done) { + const wrappedFn = (0, _isGeneratorFn.default)(fn) + ? _co.default.wrap(fn) + : fn; + const returnValue = wrappedFn.call({}, doneFnNoop); + + if (isPromise(returnValue)) { + returnValue.then(done.bind(null, null), error => { + const {isError: checkIsError, message} = (0, _isError.default)(error); + + if (message) { + extraError.message = message; + } + + done.fail(checkIsError ? error : extraError); + }); + } else { + done(); + } + }; + + return originalFn.call(env, asyncJestLifecycle, timeout); + }; +} // Similar to promisifyLifeCycleFunction but throws an error +// when the return value is neither a Promise nor `undefined` + +function promisifyIt(originalFn, env, jasmine) { + return function (specName, fn, timeout) { + if (!fn) { + // @ts-expect-error: missing fn arg is handled by originalFn + const spec = originalFn.call(env, specName); + spec.pend('not implemented'); + return spec; + } + + const hasDoneCallback = fn.length > 0; + + if (hasDoneCallback) { + return originalFn.call(env, specName, fn, timeout); + } + + const extraError = new Error(); // Without this line v8 stores references to all closures + // in the stack in the Error object. This line stringifies the stack + // property to allow garbage-collecting objects on the stack + // https://crbug.com/v8/7142 + + extraError.stack = extraError.stack; + + const asyncJestTest = function (done) { + const wrappedFn = (0, _isGeneratorFn.default)(fn) + ? _co.default.wrap(fn) + : fn; + const returnValue = wrappedFn.call({}, doneFnNoop); + + if (isPromise(returnValue)) { + returnValue.then(done.bind(null, null), error => { + const {isError: checkIsError, message} = (0, _isError.default)(error); + + if (message) { + extraError.message = message; + } + + if (jasmine.Spec.isPendingSpecException(error)) { + env.pending(message); + done(); + } else { + done.fail(checkIsError ? error : extraError); + } + }); + } else if (returnValue === undefined) { + done(); + } else { + done.fail( + new Error( + 'Jest: `it` and `test` must return either a Promise or undefined.' + ) + ); + } + }; + + return originalFn.call(env, specName, asyncJestTest, timeout); + }; +} + +function makeConcurrent(originalFn, env, mutex) { + const concurrentFn = function (specName, fn, timeout) { + let promise = Promise.resolve(); + const spec = originalFn.call(env, specName, () => promise, timeout); + + if (env != null && !env.specFilter(spec)) { + return spec; + } + + try { + promise = mutex(() => { + const promise = fn(); + + if (isPromise(promise)) { + return promise; + } + + throw new Error( + `Jest: concurrent test "${spec.getFullName()}" must return a Promise.` + ); + }); + } catch (error) { + promise = Promise.reject(error); + } + + return spec; + }; // each is binded after the function is made concurrent, so for now it is made noop + + concurrentFn.each = () => () => {}; + + return concurrentFn; +} + +function jasmineAsyncInstall(globalConfig, global) { + const jasmine = global.jasmine; + const mutex = (0, _throat.default)(globalConfig.maxConcurrency); + const env = jasmine.getEnv(); + env.it = promisifyIt(env.it, env, jasmine); + env.fit = promisifyIt(env.fit, env, jasmine); + + global.it.concurrent = (env => { + const concurrent = makeConcurrent(env.it, env, mutex); + concurrent.only = makeConcurrent(env.fit, env, mutex); + concurrent.skip = makeConcurrent(env.xit, env, mutex); + return concurrent; + })(env); + + global.fit.concurrent = makeConcurrent(env.fit, env, mutex); + env.afterAll = promisifyLifeCycleFunction(env.afterAll, env); + env.afterEach = promisifyLifeCycleFunction(env.afterEach, env); + env.beforeAll = promisifyLifeCycleFunction(env.beforeAll, env); + env.beforeEach = promisifyLifeCycleFunction(env.beforeEach, env); +} diff --git a/packages/jest-jasmine2/build/jestExpect.d.ts b/packages/jest-jasmine2/build/jestExpect.d.ts new file mode 100644 index 000000000000..0557578a6c9c --- /dev/null +++ b/packages/jest-jasmine2/build/jestExpect.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +declare const _default: (config: { + expand: boolean; +}) => void; +export default _default; diff --git a/packages/jest-jasmine2/build/jestExpect.js b/packages/jest-jasmine2/build/jestExpect.js new file mode 100644 index 000000000000..97e59ecf64e4 --- /dev/null +++ b/packages/jest-jasmine2/build/jestExpect.js @@ -0,0 +1,71 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _expect = _interopRequireDefault(require('expect')); + +var _jestSnapshot = require('jest-snapshot'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ + +/* eslint-disable local/prefer-spread-eventually */ +var _default = config => { + global.expect = _expect.default; + + _expect.default.setState({ + expand: config.expand + }); + + _expect.default.extend({ + toMatchInlineSnapshot: _jestSnapshot.toMatchInlineSnapshot, + toMatchSnapshot: _jestSnapshot.toMatchSnapshot, + toThrowErrorMatchingInlineSnapshot: + _jestSnapshot.toThrowErrorMatchingInlineSnapshot, + toThrowErrorMatchingSnapshot: _jestSnapshot.toThrowErrorMatchingSnapshot + }); + + _expect.default.addSnapshotSerializer = _jestSnapshot.addSerializer; + const jasmine = global.jasmine; + jasmine.anything = _expect.default.anything; + jasmine.any = _expect.default.any; + jasmine.objectContaining = _expect.default.objectContaining; + jasmine.arrayContaining = _expect.default.arrayContaining; + jasmine.stringMatching = _expect.default.stringMatching; + + jasmine.addMatchers = jasmineMatchersObject => { + const jestMatchersObject = Object.create(null); + Object.keys(jasmineMatchersObject).forEach(name => { + jestMatchersObject[name] = function (...args) { + // use "expect.extend" if you need to use equality testers (via this.equal) + const result = jasmineMatchersObject[name](null, null); // if there is no 'negativeCompare', both should be handled by `compare` + + const negativeCompare = result.negativeCompare || result.compare; + return this.isNot + ? negativeCompare.apply( + null, // @ts-expect-error + args + ) + : result.compare.apply( + null, // @ts-expect-error + args + ); + }; + }); + const expect = global.expect; + expect.extend(jestMatchersObject); + }; +}; + +exports.default = _default; diff --git a/packages/jest-jasmine2/build/pTimeout.d.ts b/packages/jest-jasmine2/build/pTimeout.d.ts new file mode 100644 index 000000000000..261b3e9de927 --- /dev/null +++ b/packages/jest-jasmine2/build/pTimeout.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function pTimeout(promise: Promise, ms: number, clearTimeout: NodeJS.Global['clearTimeout'], setTimeout: NodeJS.Global['setTimeout'], onTimeout: () => void): Promise; diff --git a/packages/jest-jasmine2/build/pTimeout.js b/packages/jest-jasmine2/build/pTimeout.js new file mode 100644 index 000000000000..4f878955a2e6 --- /dev/null +++ b/packages/jest-jasmine2/build/pTimeout.js @@ -0,0 +1,33 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = pTimeout; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Promise = global[Symbol.for('jest-native-promise')] || global.Promise; + +/** + * 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. + */ +// A specialized version of `p-timeout` that does not touch globals. +// It does not throw on timeout. +function pTimeout(promise, ms, clearTimeout, setTimeout, onTimeout) { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => resolve(onTimeout()), ms); + promise.then( + val => { + clearTimeout(timer); + resolve(val); + }, + err => { + clearTimeout(timer); + reject(err); + } + ); + }); +} diff --git a/packages/jest-jasmine2/build/queueRunner.d.ts b/packages/jest-jasmine2/build/queueRunner.d.ts new file mode 100644 index 000000000000..50426146fcd1 --- /dev/null +++ b/packages/jest-jasmine2/build/queueRunner.d.ts @@ -0,0 +1,30 @@ +/** + * 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. + */ +declare type Global = NodeJS.Global; +export declare type Options = { + clearTimeout: Global['clearTimeout']; + fail: (error: Error) => void; + onException: (error: Error) => void; + queueableFns: Array; + setTimeout: Global['setTimeout']; + userContext: unknown; +}; +export interface DoneFn { + (error?: any): void; + fail: (error: Error) => void; +} +export declare type QueueableFn = { + fn: (done: DoneFn) => void; + timeout?: () => number; + initError?: Error; +}; +declare type PromiseCallback = (() => void | PromiseLike) | undefined | null; +export default function queueRunner(options: Options): PromiseLike & { + cancel: () => void; + catch: (onRejected?: PromiseCallback) => Promise; +}; +export {}; diff --git a/packages/jest-jasmine2/build/queueRunner.js b/packages/jest-jasmine2/build/queueRunner.js new file mode 100644 index 000000000000..1c1f3e06c719 --- /dev/null +++ b/packages/jest-jasmine2/build/queueRunner.js @@ -0,0 +1,83 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = queueRunner; + +var _jestUtil = require('jest-util'); + +var _PCancelable = _interopRequireDefault(require('./PCancelable')); + +var _pTimeout = _interopRequireDefault(require('./pTimeout')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Promise = global[Symbol.for('jest-native-promise')] || global.Promise; + +function queueRunner(options) { + const token = new _PCancelable.default((onCancel, resolve) => { + onCancel(resolve); + }); + + const mapper = ({fn, timeout, initError = new Error()}) => { + let promise = new Promise(resolve => { + const next = function (...args) { + const err = args[0]; + + if (err) { + options.fail.apply(null, args); + } + + resolve(); + }; + + next.fail = function (...args) { + options.fail.apply(null, args); + resolve(); + }; + + try { + fn.call(options.userContext, next); + } catch (e) { + options.onException(e); + resolve(); + } + }); + promise = Promise.race([promise, token]); + + if (!timeout) { + return promise; + } + + const timeoutMs = timeout(); + return (0, _pTimeout.default)( + promise, + timeoutMs, + options.clearTimeout, + options.setTimeout, + () => { + initError.message = + 'Timeout - Async callback was not invoked within the ' + + (0, _jestUtil.formatTime)(timeoutMs) + + ' timeout specified by jest.setTimeout.'; + initError.stack = initError.message + initError.stack; + options.onException(initError); + } + ); + }; + + const result = options.queueableFns.reduce( + (promise, fn) => promise.then(() => mapper(fn)), + Promise.resolve() + ); + return { + cancel: token.cancel.bind(token), + catch: result.catch.bind(result), + then: result.then.bind(result) + }; +} diff --git a/packages/jest-jasmine2/build/reporter.d.ts b/packages/jest-jasmine2/build/reporter.d.ts new file mode 100644 index 000000000000..3d6ee8c5e540 --- /dev/null +++ b/packages/jest-jasmine2/build/reporter.d.ts @@ -0,0 +1,31 @@ +/** + * 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. + */ +import { TestResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import type { SpecResult } from './jasmine/Spec'; +import type { SuiteResult } from './jasmine/Suite'; +import type { Reporter, RunDetails } from './types'; +export default class Jasmine2Reporter implements Reporter { + private _testResults; + private _globalConfig; + private _config; + private _currentSuites; + private _resolve; + private _resultsPromise; + private _startTimes; + private _testPath; + constructor(globalConfig: Config.GlobalConfig, config: Config.ProjectConfig, testPath: Config.Path); + jasmineStarted(_runDetails: RunDetails): void; + specStarted(spec: SpecResult): void; + specDone(result: SpecResult): void; + suiteStarted(suite: SuiteResult): void; + suiteDone(_result: SuiteResult): void; + jasmineDone(_runDetails: RunDetails): void; + getResults(): Promise; + private _addMissingMessageToStack; + private _extractSpecResults; +} diff --git a/packages/jest-jasmine2/build/reporter.js b/packages/jest-jasmine2/build/reporter.js new file mode 100644 index 000000000000..084446253cd5 --- /dev/null +++ b/packages/jest-jasmine2/build/reporter.js @@ -0,0 +1,177 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _testResult = require('@jest/test-result'); + +var _jestMessageUtil = require('jest-message-util'); + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var jestNow = global[Symbol.for('jest-native-now')] || global.Date.now; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Promise = global[Symbol.for('jest-native-promise')] || global.Promise; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class Jasmine2Reporter { + constructor(globalConfig, config, testPath) { + _defineProperty(this, '_testResults', void 0); + + _defineProperty(this, '_globalConfig', void 0); + + _defineProperty(this, '_config', void 0); + + _defineProperty(this, '_currentSuites', void 0); + + _defineProperty(this, '_resolve', void 0); + + _defineProperty(this, '_resultsPromise', void 0); + + _defineProperty(this, '_startTimes', void 0); + + _defineProperty(this, '_testPath', void 0); + + this._globalConfig = globalConfig; + this._config = config; + this._testPath = testPath; + this._testResults = []; + this._currentSuites = []; + this._resolve = null; + this._resultsPromise = new Promise(resolve => (this._resolve = resolve)); + this._startTimes = new Map(); + } + + jasmineStarted(_runDetails) {} + + specStarted(spec) { + this._startTimes.set(spec.id, jestNow()); + } + + specDone(result) { + this._testResults.push( + this._extractSpecResults(result, this._currentSuites.slice(0)) + ); + } + + suiteStarted(suite) { + this._currentSuites.push(suite.description); + } + + suiteDone(_result) { + this._currentSuites.pop(); + } + + jasmineDone(_runDetails) { + let numFailingTests = 0; + let numPassingTests = 0; + let numPendingTests = 0; + let numTodoTests = 0; + const testResults = this._testResults; + testResults.forEach(testResult => { + if (testResult.status === 'failed') { + numFailingTests++; + } else if (testResult.status === 'pending') { + numPendingTests++; + } else if (testResult.status === 'todo') { + numTodoTests++; + } else { + numPassingTests++; + } + }); + const testResult = { + ...(0, _testResult.createEmptyTestResult)(), + console: null, + failureMessage: (0, _jestMessageUtil.formatResultsErrors)( + testResults, + this._config, + this._globalConfig, + this._testPath + ), + numFailingTests, + numPassingTests, + numPendingTests, + numTodoTests, + snapshot: { + added: 0, + fileDeleted: false, + matched: 0, + unchecked: 0, + unmatched: 0, + updated: 0 + }, + testFilePath: this._testPath, + testResults + }; + + this._resolve(testResult); + } + + getResults() { + return this._resultsPromise; + } + + _addMissingMessageToStack(stack, message) { + // Some errors (e.g. Angular injection error) don't prepend error.message + // to stack, instead the first line of the stack is just plain 'Error' + const ERROR_REGEX = /^Error:?\s*\n/; + + if (stack && message && !stack.includes(message)) { + return message + stack.replace(ERROR_REGEX, '\n'); + } + + return stack; + } + + _extractSpecResults(specResult, ancestorTitles) { + const start = this._startTimes.get(specResult.id); + + const duration = start ? jestNow() - start : undefined; + const status = + specResult.status === 'disabled' ? 'pending' : specResult.status; + const location = specResult.__callsite + ? { + column: specResult.__callsite.getColumnNumber(), + line: specResult.__callsite.getLineNumber() + } + : null; + const results = { + ancestorTitles, + duration, + failureDetails: [], + failureMessages: [], + fullName: specResult.fullName, + location, + numPassingAsserts: 0, + // Jasmine2 only returns an array of failed asserts. + status, + title: specResult.description + }; + specResult.failedExpectations.forEach(failed => { + const message = + !failed.matcherName && typeof failed.stack === 'string' + ? this._addMissingMessageToStack(failed.stack, failed.message) + : failed.message || ''; + results.failureMessages.push(message); + results.failureDetails.push(failed); + }); + return results; + } +} + +exports.default = Jasmine2Reporter; diff --git a/packages/jest-jasmine2/build/setup_jest_globals.d.ts b/packages/jest-jasmine2/build/setup_jest_globals.d.ts new file mode 100644 index 000000000000..ef40b616833c --- /dev/null +++ b/packages/jest-jasmine2/build/setup_jest_globals.d.ts @@ -0,0 +1,17 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import { SnapshotStateType } from 'jest-snapshot'; +import type { Plugin } from 'pretty-format'; +export declare type SetupOptions = { + config: Config.ProjectConfig; + globalConfig: Config.GlobalConfig; + localRequire: (moduleName: string) => Plugin; + testPath: Config.Path; +}; +declare const _default: ({ config, globalConfig, localRequire, testPath, }: SetupOptions) => SnapshotStateType; +export default _default; diff --git a/packages/jest-jasmine2/build/setup_jest_globals.js b/packages/jest-jasmine2/build/setup_jest_globals.js new file mode 100644 index 000000000000..1bd78117ceca --- /dev/null +++ b/packages/jest-jasmine2/build/setup_jest_globals.js @@ -0,0 +1,114 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _expect = require('expect'); + +var _jestSnapshot = require('jest-snapshot'); + +/** + * 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. + */ +// Get suppressed errors form jest-matchers that weren't throw during +// test execution and add them to the test result, potentially failing +// a passing test. +const addSuppressedErrors = result => { + const {suppressedErrors} = (0, _expect.getState)(); + (0, _expect.setState)({ + suppressedErrors: [] + }); + + if (suppressedErrors.length) { + result.status = 'failed'; + result.failedExpectations = suppressedErrors.map(error => ({ + actual: '', + // passing error for custom test reporters + error, + expected: '', + matcherName: '', + message: error.message, + passed: false, + stack: error.stack + })); + } +}; + +const addAssertionErrors = result => { + const assertionErrors = (0, _expect.extractExpectedAssertionsErrors)(); + + if (assertionErrors.length) { + const jasmineErrors = assertionErrors.map(({actual, error, expected}) => ({ + actual, + expected, + message: error.stack, + passed: false + })); + result.status = 'failed'; + result.failedExpectations = result.failedExpectations.concat(jasmineErrors); + } +}; + +const patchJasmine = () => { + global.jasmine.Spec = (realSpec => { + class Spec extends realSpec { + constructor(attr) { + const resultCallback = attr.resultCallback; + + attr.resultCallback = function (result) { + addSuppressedErrors(result); + addAssertionErrors(result); + resultCallback.call(attr, result); + }; + + const onStart = attr.onStart; + + attr.onStart = context => { + (0, _expect.setState)({ + currentTestName: context.getFullName() + }); + onStart && onStart.call(attr, context); + }; + + super(attr); + } + } + + return Spec; + })(global.jasmine.Spec); +}; + +var _default = ({config, globalConfig, localRequire, testPath}) => { + // Jest tests snapshotSerializers in order preceding built-in serializers. + // Therefore, add in reverse because the last added is the first tested. + config.snapshotSerializers + .concat() + .reverse() + .forEach(path => { + (0, _jestSnapshot.addSerializer)(localRequire(path)); + }); + patchJasmine(); + const {expand, updateSnapshot} = globalConfig; + const {prettierPath} = config; + const snapshotResolver = (0, _jestSnapshot.buildSnapshotResolver)(config); + const snapshotPath = snapshotResolver.resolveSnapshotPath(testPath); + const snapshotState = new _jestSnapshot.SnapshotState(snapshotPath, { + expand, + prettierPath, + updateSnapshot + }); // @ts-expect-error: snapshotState is a jest extension of `expect` + + (0, _expect.setState)({ + snapshotState, + testPath + }); // Return it back to the outer scope (test runner outside the VM). + + return snapshotState; +}; + +exports.default = _default; diff --git a/packages/jest-jasmine2/build/treeProcessor.d.ts b/packages/jest-jasmine2/build/treeProcessor.d.ts new file mode 100644 index 000000000000..fdeee94aa827 --- /dev/null +++ b/packages/jest-jasmine2/build/treeProcessor.d.ts @@ -0,0 +1,26 @@ +/** + * 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. + */ +import type Suite from './jasmine/Suite'; +declare type Options = { + nodeComplete: (suite: TreeNode) => void; + nodeStart: (suite: TreeNode) => void; + queueRunnerFactory: any; + runnableIds: Array; + tree: TreeNode; +}; +export declare type TreeNode = { + afterAllFns: Array; + beforeAllFns: Array; + disabled?: boolean; + execute: (onComplete: () => void, enabled: boolean) => void; + id: string; + onException: (error: Error) => void; + sharedUserContext: () => unknown; + children?: Array; +} & Pick; +export default function treeProcessor(options: Options): void; +export {}; diff --git a/packages/jest-jasmine2/build/treeProcessor.js b/packages/jest-jasmine2/build/treeProcessor.js new file mode 100644 index 000000000000..cd8e48976533 --- /dev/null +++ b/packages/jest-jasmine2/build/treeProcessor.js @@ -0,0 +1,87 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = treeProcessor; + +/** + * 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. + */ +function treeProcessor(options) { + const { + nodeComplete, + nodeStart, + queueRunnerFactory, + runnableIds, + tree + } = options; + + function isEnabled(node, parentEnabled) { + return parentEnabled || runnableIds.indexOf(node.id) !== -1; + } + + function getNodeHandler(node, parentEnabled) { + const enabled = isEnabled(node, parentEnabled); + return node.children + ? getNodeWithChildrenHandler(node, enabled) + : getNodeWithoutChildrenHandler(node, enabled); + } + + function getNodeWithoutChildrenHandler(node, enabled) { + return function fn(done = () => {}) { + node.execute(done, enabled); + }; + } + + function getNodeWithChildrenHandler(node, enabled) { + return async function fn(done = () => {}) { + nodeStart(node); + await queueRunnerFactory({ + onException: error => node.onException(error), + queueableFns: wrapChildren(node, enabled), + userContext: node.sharedUserContext() + }); + nodeComplete(node); + done(); + }; + } + + function hasNoEnabledTest(node) { + var _node$children$every, _node$children; + + return ( + node.disabled || + node.markedPending || + ((_node$children$every = + (_node$children = node.children) === null || _node$children === void 0 + ? void 0 + : _node$children.every(hasNoEnabledTest)) !== null && + _node$children$every !== void 0 + ? _node$children$every + : false) + ); + } + + function wrapChildren(node, enabled) { + if (!node.children) { + throw new Error('`node.children` is not defined.'); + } + + const children = node.children.map(child => ({ + fn: getNodeHandler(child, enabled) + })); + + if (hasNoEnabledTest(node)) { + return children; + } + + return node.beforeAllFns.concat(children).concat(node.afterAllFns); + } + + const treeHandler = getNodeHandler(tree, false); + return treeHandler(); +} diff --git a/packages/jest-jasmine2/build/types.d.ts b/packages/jest-jasmine2/build/types.d.ts new file mode 100644 index 000000000000..2e65dbf79f0f --- /dev/null +++ b/packages/jest-jasmine2/build/types.d.ts @@ -0,0 +1,82 @@ +/** + * 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. + */ +/// +import type { AssertionError } from 'assert'; +import type { Config } from '@jest/types'; +import expect = require('expect'); +import type CallTracker from './jasmine/CallTracker'; +import type Env from './jasmine/Env'; +import type JsApiReporter from './jasmine/JsApiReporter'; +import type ReportDispatcher from './jasmine/ReportDispatcher'; +import type { default as Spec, SpecResult } from './jasmine/Spec'; +import type SpyStrategy from './jasmine/SpyStrategy'; +import type { default as Suite, SuiteResult } from './jasmine/Suite'; +import type Timer from './jasmine/Timer'; +import type createSpy from './jasmine/createSpy'; +import type SpyRegistry from './jasmine/spyRegistry'; +export declare type SpecDefinitionsFn = () => void; +export interface AssertionErrorWithStack extends AssertionError { + stack: string; +} +export declare type SyncExpectationResult = { + pass: boolean; + message: () => string; +}; +export declare type AsyncExpectationResult = Promise; +export declare type ExpectationResult = SyncExpectationResult | AsyncExpectationResult; +export declare type RawMatcherFn = (expected: unknown, actual: unknown, options?: unknown) => ExpectationResult; +export declare type RunDetails = { + totalSpecsDefined?: number; + failedExpectations?: SuiteResult['failedExpectations']; +}; +export declare type Reporter = { + jasmineDone: (runDetails: RunDetails) => void; + jasmineStarted: (runDetails: RunDetails) => void; + specDone: (result: SpecResult) => void; + specStarted: (spec: SpecResult) => void; + suiteDone: (result: SuiteResult) => void; + suiteStarted: (result: SuiteResult) => void; +}; +export interface Spy extends Record { + (this: Record, ...args: Array): unknown; + and: SpyStrategy; + calls: CallTracker; + restoreObjectToOriginalState?: () => void; +} +declare type JasmineMatcher = { + (matchersUtil: unknown, context: unknown): JasmineMatcher; + compare: () => RawMatcherFn; + negativeCompare: () => RawMatcherFn; +}; +export declare type JasmineMatchersObject = { + [id: string]: JasmineMatcher; +}; +export declare type Jasmine = { + _DEFAULT_TIMEOUT_INTERVAL: number; + DEFAULT_TIMEOUT_INTERVAL: number; + currentEnv_: ReturnType['prototype']; + getEnv: (options?: Record) => ReturnType['prototype']; + createSpy: typeof createSpy; + Env: ReturnType; + JsApiReporter: typeof JsApiReporter; + ReportDispatcher: typeof ReportDispatcher; + Spec: typeof Spec; + SpyRegistry: typeof SpyRegistry; + Suite: typeof Suite; + Timer: typeof Timer; + version: string; + testPath: Config.Path; + addMatchers: (matchers: JasmineMatchersObject) => void; +} & typeof expect & NodeJS.Global; +declare global { + module NodeJS { + interface Global { + expect: typeof expect; + } + } +} +export {}; diff --git a/packages/jest-jasmine2/build/types.js b/packages/jest-jasmine2/build/types.js new file mode 100644 index 000000000000..d852d2412696 --- /dev/null +++ b/packages/jest-jasmine2/build/types.js @@ -0,0 +1,7 @@ +'use strict'; + +var _expect = _interopRequireDefault(require('expect')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} diff --git a/packages/jest-leak-detector/build/index.d.ts b/packages/jest-leak-detector/build/index.d.ts new file mode 100644 index 000000000000..b9ad4a642bca --- /dev/null +++ b/packages/jest-leak-detector/build/index.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ +export default class { + private _isReferenceBeingHeld; + constructor(value: unknown); + isLeaking(): Promise; + private _runGarbageCollector; +} diff --git a/packages/jest-leak-detector/build/index.js b/packages/jest-leak-detector/build/index.js new file mode 100644 index 000000000000..30a5feb8032c --- /dev/null +++ b/packages/jest-leak-detector/build/index.js @@ -0,0 +1,139 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _util() { + const data = require('util'); + + _util = function () { + return data; + }; + + return data; +} + +function _v() { + const data = require('v8'); + + _v = function () { + return data; + }; + + return data; +} + +function _vm() { + const data = require('vm'); + + _vm = function () { + return data; + }; + + return data; +} + +function _jestGetType() { + const data = require('jest-get-type'); + + _jestGetType = function () { + return data; + }; + + return data; +} + +function _prettyFormat() { + const data = _interopRequireDefault(require('pretty-format')); + + _prettyFormat = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const tick = (0, _util().promisify)(setImmediate); + +class _default { + constructor(value) { + _defineProperty(this, '_isReferenceBeingHeld', void 0); + + if ((0, _jestGetType().isPrimitive)(value)) { + throw new TypeError( + [ + 'Primitives cannot leak memory.', + 'You passed a ' + + typeof value + + ': <' + + (0, _prettyFormat().default)(value) + + '>' + ].join(' ') + ); + } + + let weak; + + try { + // eslint-disable-next-line import/no-extraneous-dependencies + weak = require('weak-napi'); + } catch (err) { + if (!err || err.code !== 'MODULE_NOT_FOUND') { + throw err; + } + + throw new Error( + 'The leaking detection mechanism requires the "weak-napi" package to be installed and work. ' + + 'Please install it as a dependency on your main project' + ); + } + + weak(value, () => (this._isReferenceBeingHeld = false)); + this._isReferenceBeingHeld = true; // Ensure value is not leaked by the closure created by the "weak" callback. + + value = null; + } + + async isLeaking() { + this._runGarbageCollector(); // wait some ticks to allow GC to run properly, see https://github.com/nodejs/node/issues/34636#issuecomment-669366235 + + for (let i = 0; i < 10; i++) { + await tick(); + } + + return this._isReferenceBeingHeld; + } + + _runGarbageCollector() { + const isGarbageCollectorHidden = !global.gc; // GC is usually hidden, so we have to expose it before running. + + (0, _v().setFlagsFromString)('--expose-gc'); + (0, _vm().runInNewContext)('gc')(); // The GC was not initially exposed, so let's hide it again. + + if (isGarbageCollectorHidden) { + (0, _v().setFlagsFromString)('--no-expose-gc'); + } + } +} + +exports.default = _default; diff --git a/packages/jest-matcher-utils/build/Replaceable.d.ts b/packages/jest-matcher-utils/build/Replaceable.d.ts new file mode 100644 index 000000000000..c02db00c6ed7 --- /dev/null +++ b/packages/jest-matcher-utils/build/Replaceable.d.ts @@ -0,0 +1,17 @@ +/** + * 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. + */ +declare type ReplaceableForEachCallBack = (value: unknown, key: unknown, object: unknown) => void; +export default class Replaceable { + object: any; + type: string; + constructor(object: any); + static isReplaceable(obj1: unknown, obj2: unknown): boolean; + forEach(cb: ReplaceableForEachCallBack): void; + get(key: any): any; + set(key: any, value: any): void; +} +export {}; diff --git a/packages/jest-matcher-utils/build/Replaceable.js b/packages/jest-matcher-utils/build/Replaceable.js new file mode 100644 index 000000000000..0bfa891ae607 --- /dev/null +++ b/packages/jest-matcher-utils/build/Replaceable.js @@ -0,0 +1,86 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _jestGetType = _interopRequireDefault(require('jest-get-type')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const supportTypes = ['map', 'array', 'object']; + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +class Replaceable { + constructor(object) { + _defineProperty(this, 'object', void 0); + + _defineProperty(this, 'type', void 0); + + this.object = object; + this.type = (0, _jestGetType.default)(object); + + if (!supportTypes.includes(this.type)) { + throw new Error(`Type ${this.type} is not support in Replaceable!`); + } + } + + static isReplaceable(obj1, obj2) { + const obj1Type = (0, _jestGetType.default)(obj1); + const obj2Type = (0, _jestGetType.default)(obj2); + return obj1Type === obj2Type && supportTypes.includes(obj1Type); + } + + forEach(cb) { + if (this.type === 'object') { + const descriptors = Object.getOwnPropertyDescriptors(this.object); + [ + ...Object.keys(descriptors), + ...Object.getOwnPropertySymbols(descriptors) + ] //@ts-expect-error because typescript do not support symbol key in object + //https://github.com/microsoft/TypeScript/issues/1863 + .filter(key => descriptors[key].enumerable) + .forEach(key => { + cb(this.object[key], key, this.object); + }); + } else { + this.object.forEach(cb); + } + } + + get(key) { + if (this.type === 'map') { + return this.object.get(key); + } + + return this.object[key]; + } + + set(key, value) { + if (this.type === 'map') { + this.object.set(key, value); + } else { + this.object[key] = value; + } + } +} +/* eslint-enable */ + +exports.default = Replaceable; diff --git a/packages/jest-matcher-utils/build/deepCyclicCopyReplaceable.d.ts b/packages/jest-matcher-utils/build/deepCyclicCopyReplaceable.d.ts new file mode 100644 index 000000000000..4c8e32ebd4fe --- /dev/null +++ b/packages/jest-matcher-utils/build/deepCyclicCopyReplaceable.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function deepCyclicCopyReplaceable(value: T, cycles?: WeakMap): T; diff --git a/packages/jest-matcher-utils/build/deepCyclicCopyReplaceable.js b/packages/jest-matcher-utils/build/deepCyclicCopyReplaceable.js new file mode 100644 index 000000000000..c3b65b02fcb7 --- /dev/null +++ b/packages/jest-matcher-utils/build/deepCyclicCopyReplaceable.js @@ -0,0 +1,107 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = deepCyclicCopyReplaceable; + +var _prettyFormat = require('pretty-format'); + +/** + * 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 builtInObject = [ + Array, + Buffer, + Date, + Float32Array, + Float64Array, + Int16Array, + Int32Array, + Int8Array, + Map, + Set, + RegExp, + Uint16Array, + Uint32Array, + Uint8Array, + Uint8ClampedArray +]; + +const isBuiltInObject = object => builtInObject.includes(object.constructor); + +const isMap = value => value.constructor === Map; + +function deepCyclicCopyReplaceable(value, cycles = new WeakMap()) { + if (typeof value !== 'object' || value === null) { + return value; + } else if (cycles.has(value)) { + return cycles.get(value); + } else if (Array.isArray(value)) { + return deepCyclicCopyArray(value, cycles); + } else if (isMap(value)) { + return deepCyclicCopyMap(value, cycles); + } else if (isBuiltInObject(value)) { + return value; + } else if (_prettyFormat.plugins.DOMElement.test(value)) { + return value.cloneNode(true); + } else { + return deepCyclicCopyObject(value, cycles); + } +} + +function deepCyclicCopyObject(object, cycles) { + const newObject = Object.create(Object.getPrototypeOf(object)); + const descriptors = Object.getOwnPropertyDescriptors(object); + cycles.set(object, newObject); + const newDescriptors = [ + ...Object.keys(descriptors), + ...Object.getOwnPropertySymbols(descriptors) + ].reduce( + //@ts-expect-error because typescript do not support symbol key in object + //https://github.com/microsoft/TypeScript/issues/1863 + (newDescriptors, key) => { + const enumerable = descriptors[key].enumerable; + newDescriptors[key] = { + configurable: true, + enumerable, + value: deepCyclicCopyReplaceable( + // this accesses the value or getter, depending. We just care about the value anyways, and this allows us to not mess with accessors + // it has the side effect of invoking the getter here though, rather than copying it over + object[key], + cycles + ), + writable: true + }; + return newDescriptors; + }, + {} + ); //@ts-expect-error because typescript do not support symbol key in object + //https://github.com/microsoft/TypeScript/issues/1863 + + return Object.defineProperties(newObject, newDescriptors); +} + +function deepCyclicCopyArray(array, cycles) { + const newArray = new (Object.getPrototypeOf(array).constructor)(array.length); + const length = array.length; + cycles.set(array, newArray); + + for (let i = 0; i < length; i++) { + newArray[i] = deepCyclicCopyReplaceable(array[i], cycles); + } + + return newArray; +} + +function deepCyclicCopyMap(map, cycles) { + const newMap = new Map(); + cycles.set(map, newMap); + map.forEach((value, key) => { + newMap.set(key, deepCyclicCopyReplaceable(value, cycles)); + }); + return newMap; +} diff --git a/packages/jest-matcher-utils/build/index.d.ts b/packages/jest-matcher-utils/build/index.d.ts new file mode 100644 index 000000000000..d4c66f492ab9 --- /dev/null +++ b/packages/jest-matcher-utils/build/index.d.ts @@ -0,0 +1,53 @@ +/** + * 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. + */ +import chalk = require('chalk'); +import { DiffOptions as ImportDiffOptions } from 'jest-diff'; +declare type MatcherHintColor = (arg: string) => string; +export declare type MatcherHintOptions = { + comment?: string; + expectedColor?: MatcherHintColor; + isDirectExpectCall?: boolean; + isNot?: boolean; + promise?: string; + receivedColor?: MatcherHintColor; + secondArgument?: string; + secondArgumentColor?: MatcherHintColor; +}; +export declare type DiffOptions = ImportDiffOptions; +export declare const EXPECTED_COLOR: chalk.Chalk; +export declare const RECEIVED_COLOR: chalk.Chalk; +export declare const INVERTED_COLOR: chalk.Chalk; +export declare const BOLD_WEIGHT: chalk.Chalk; +export declare const DIM_COLOR: chalk.Chalk; +export declare const SUGGEST_TO_CONTAIN_EQUAL: string; +export declare const stringify: (object: unknown, maxDepth?: number) => string; +export declare const highlightTrailingWhitespace: (text: string) => string; +export declare const printReceived: (object: unknown) => string; +export declare const printExpected: (value: unknown) => string; +export declare const printWithType: (name: string, value: unknown, print: (value: unknown) => string) => string; +export declare const ensureNoExpected: (expected: unknown, matcherName: string, options?: MatcherHintOptions | undefined) => void; +/** + * Ensures that `actual` is of type `number | bigint` + */ +export declare const ensureActualIsNumber: (actual: unknown, matcherName: string, options?: MatcherHintOptions | undefined) => void; +/** + * Ensures that `expected` is of type `number | bigint` + */ +export declare const ensureExpectedIsNumber: (expected: unknown, matcherName: string, options?: MatcherHintOptions | undefined) => void; +/** + * Ensures that `actual` & `expected` are of type `number | bigint` + */ +export declare const ensureNumbers: (actual: unknown, expected: unknown, matcherName: string, options?: MatcherHintOptions | undefined) => void; +export declare const ensureExpectedIsNonNegativeInteger: (expected: unknown, matcherName: string, options?: MatcherHintOptions | undefined) => void; +export declare const printDiffOrStringify: (expected: unknown, received: unknown, expectedLabel: string, receivedLabel: string, expand: boolean) => string; +export declare const diff: (a: unknown, b: unknown, options?: ImportDiffOptions | undefined) => string | null; +export declare const pluralize: (word: string, count: number) => string; +declare type PrintLabel = (string: string) => string; +export declare const getLabelPrinter: (...strings: Array) => PrintLabel; +export declare const matcherErrorMessage: (hint: string, generic: string, specific?: string | undefined) => string; +export declare const matcherHint: (matcherName: string, received?: string, expected?: string, options?: MatcherHintOptions) => string; +export {}; diff --git a/packages/jest-matcher-utils/build/index.js b/packages/jest-matcher-utils/build/index.js new file mode 100644 index 000000000000..17c4f1846447 --- /dev/null +++ b/packages/jest-matcher-utils/build/index.js @@ -0,0 +1,615 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.matcherHint = exports.matcherErrorMessage = exports.getLabelPrinter = exports.pluralize = exports.diff = exports.printDiffOrStringify = exports.ensureExpectedIsNonNegativeInteger = exports.ensureNumbers = exports.ensureExpectedIsNumber = exports.ensureActualIsNumber = exports.ensureNoExpected = exports.printWithType = exports.printExpected = exports.printReceived = exports.highlightTrailingWhitespace = exports.stringify = exports.SUGGEST_TO_CONTAIN_EQUAL = exports.DIM_COLOR = exports.BOLD_WEIGHT = exports.INVERTED_COLOR = exports.RECEIVED_COLOR = exports.EXPECTED_COLOR = void 0; + +var _chalk = _interopRequireDefault(require('chalk')); + +var _jestDiff = _interopRequireWildcard(require('jest-diff')); + +var _jestGetType = _interopRequireDefault(require('jest-get-type')); + +var _prettyFormat = _interopRequireWildcard(require('pretty-format')); + +var _Replaceable = _interopRequireDefault(require('./Replaceable')); + +var _deepCyclicCopyReplaceable = _interopRequireDefault( + require('./deepCyclicCopyReplaceable') +); + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ + +/* eslint-disable local/ban-types-eventually */ +const { + AsymmetricMatcher, + DOMCollection, + DOMElement, + Immutable, + ReactElement, + ReactTestComponent +} = _prettyFormat.plugins; +const PLUGINS = [ + ReactTestComponent, + ReactElement, + DOMElement, + DOMCollection, + Immutable, + AsymmetricMatcher +]; +const EXPECTED_COLOR = _chalk.default.green; +exports.EXPECTED_COLOR = EXPECTED_COLOR; +const RECEIVED_COLOR = _chalk.default.red; +exports.RECEIVED_COLOR = RECEIVED_COLOR; +const INVERTED_COLOR = _chalk.default.inverse; +exports.INVERTED_COLOR = INVERTED_COLOR; +const BOLD_WEIGHT = _chalk.default.bold; +exports.BOLD_WEIGHT = BOLD_WEIGHT; +const DIM_COLOR = _chalk.default.dim; +exports.DIM_COLOR = DIM_COLOR; +const MULTILINE_REGEXP = /\n/; +const SPACE_SYMBOL = '\u{00B7}'; // middle dot + +const NUMBERS = [ + 'zero', + 'one', + 'two', + 'three', + 'four', + 'five', + 'six', + 'seven', + 'eight', + 'nine', + 'ten', + 'eleven', + 'twelve', + 'thirteen' +]; + +const SUGGEST_TO_CONTAIN_EQUAL = _chalk.default.dim( + 'Looks like you wanted to test for object/array equality with the stricter `toContain` matcher. You probably need to use `toContainEqual` instead.' +); + +exports.SUGGEST_TO_CONTAIN_EQUAL = SUGGEST_TO_CONTAIN_EQUAL; + +const stringify = (object, maxDepth = 10) => { + const MAX_LENGTH = 10000; + let result; + + try { + result = (0, _prettyFormat.default)(object, { + maxDepth, + min: true, + plugins: PLUGINS + }); + } catch { + result = (0, _prettyFormat.default)(object, { + callToJSON: false, + maxDepth, + min: true, + plugins: PLUGINS + }); + } + + return result.length >= MAX_LENGTH && maxDepth > 1 + ? stringify(object, Math.floor(maxDepth / 2)) + : result; +}; + +exports.stringify = stringify; + +const highlightTrailingWhitespace = text => + text.replace(/\s+$/gm, _chalk.default.inverse('$&')); // Instead of inverse highlight which now implies a change, +// replace common spaces with middle dot at the end of any line. + +exports.highlightTrailingWhitespace = highlightTrailingWhitespace; + +const replaceTrailingSpaces = text => + text.replace(/\s+$/gm, spaces => SPACE_SYMBOL.repeat(spaces.length)); + +const printReceived = object => + RECEIVED_COLOR(replaceTrailingSpaces(stringify(object))); + +exports.printReceived = printReceived; + +const printExpected = value => + EXPECTED_COLOR(replaceTrailingSpaces(stringify(value))); + +exports.printExpected = printExpected; + +const printWithType = (name, value, print) => { + const type = (0, _jestGetType.default)(value); + const hasType = + type !== 'null' && type !== 'undefined' + ? `${name} has type: ${type}\n` + : ''; + const hasValue = `${name} has value: ${print(value)}`; + return hasType + hasValue; +}; + +exports.printWithType = printWithType; + +const ensureNoExpected = (expected, matcherName, options) => { + if (typeof expected !== 'undefined') { + // Prepend maybe not only for backward compatibility. + const matcherString = (options ? '' : '[.not]') + matcherName; + throw new Error( + matcherErrorMessage( + matcherHint(matcherString, undefined, '', options), // Because expected is omitted in hint above, + // expected is black instead of green in message below. + 'this matcher must not have an expected argument', + printWithType('Expected', expected, printExpected) + ) + ); + } +}; +/** + * Ensures that `actual` is of type `number | bigint` + */ + +exports.ensureNoExpected = ensureNoExpected; + +const ensureActualIsNumber = (actual, matcherName, options) => { + if (typeof actual !== 'number' && typeof actual !== 'bigint') { + // Prepend maybe not only for backward compatibility. + const matcherString = (options ? '' : '[.not]') + matcherName; + throw new Error( + matcherErrorMessage( + matcherHint(matcherString, undefined, undefined, options), + `${RECEIVED_COLOR('received')} value must be a number or bigint`, + printWithType('Received', actual, printReceived) + ) + ); + } +}; +/** + * Ensures that `expected` is of type `number | bigint` + */ + +exports.ensureActualIsNumber = ensureActualIsNumber; + +const ensureExpectedIsNumber = (expected, matcherName, options) => { + if (typeof expected !== 'number' && typeof expected !== 'bigint') { + // Prepend maybe not only for backward compatibility. + const matcherString = (options ? '' : '[.not]') + matcherName; + throw new Error( + matcherErrorMessage( + matcherHint(matcherString, undefined, undefined, options), + `${EXPECTED_COLOR('expected')} value must be a number or bigint`, + printWithType('Expected', expected, printExpected) + ) + ); + } +}; +/** + * Ensures that `actual` & `expected` are of type `number | bigint` + */ + +exports.ensureExpectedIsNumber = ensureExpectedIsNumber; + +const ensureNumbers = (actual, expected, matcherName, options) => { + ensureActualIsNumber(actual, matcherName, options); + ensureExpectedIsNumber(expected, matcherName, options); +}; + +exports.ensureNumbers = ensureNumbers; + +const ensureExpectedIsNonNegativeInteger = (expected, matcherName, options) => { + if ( + typeof expected !== 'number' || + !Number.isSafeInteger(expected) || + expected < 0 + ) { + // Prepend maybe not only for backward compatibility. + const matcherString = (options ? '' : '[.not]') + matcherName; + throw new Error( + matcherErrorMessage( + matcherHint(matcherString, undefined, undefined, options), + `${EXPECTED_COLOR('expected')} value must be a non-negative integer`, + printWithType('Expected', expected, printExpected) + ) + ); + } +}; // Given array of diffs, return concatenated string: +// * include common substrings +// * exclude change substrings which have opposite op +// * include change substrings which have argument op +// with inverse highlight only if there is a common substring + +exports.ensureExpectedIsNonNegativeInteger = ensureExpectedIsNonNegativeInteger; + +const getCommonAndChangedSubstrings = (diffs, op, hasCommonDiff) => + diffs.reduce( + (reduced, diff) => + reduced + + (diff[0] === _jestDiff.DIFF_EQUAL + ? diff[1] + : diff[0] !== op + ? '' + : hasCommonDiff + ? INVERTED_COLOR(diff[1]) + : diff[1]), + '' + ); + +const isLineDiffable = (expected, received) => { + const expectedType = (0, _jestGetType.default)(expected); + const receivedType = (0, _jestGetType.default)(received); + + if (expectedType !== receivedType) { + return false; + } + + if (_jestGetType.default.isPrimitive(expected)) { + // Print generic line diff for strings only: + // * if neither string is empty + // * if either string has more than one line + return ( + typeof expected === 'string' && + typeof received === 'string' && + expected.length !== 0 && + received.length !== 0 && + (MULTILINE_REGEXP.test(expected) || MULTILINE_REGEXP.test(received)) + ); + } + + if ( + expectedType === 'date' || + expectedType === 'function' || + expectedType === 'regexp' + ) { + return false; + } + + if (expected instanceof Error && received instanceof Error) { + return false; + } + + if ( + expectedType === 'object' && + typeof expected.asymmetricMatch === 'function' + ) { + return false; + } + + if ( + receivedType === 'object' && + typeof received.asymmetricMatch === 'function' + ) { + return false; + } + + return true; +}; + +const MAX_DIFF_STRING_LENGTH = 20000; + +const printDiffOrStringify = ( + expected, + received, + expectedLabel, + receivedLabel, + expand +) => { + if ( + typeof expected === 'string' && + typeof received === 'string' && + expected.length !== 0 && + received.length !== 0 && + expected.length <= MAX_DIFF_STRING_LENGTH && + received.length <= MAX_DIFF_STRING_LENGTH && + expected !== received + ) { + if (expected.includes('\n') || received.includes('\n')) { + return (0, _jestDiff.diffStringsUnified)(expected, received, { + aAnnotation: expectedLabel, + bAnnotation: receivedLabel, + changeLineTrailingSpaceColor: _chalk.default.bgYellow, + commonLineTrailingSpaceColor: _chalk.default.bgYellow, + emptyFirstOrLastLinePlaceholder: '↵', + // U+21B5 + expand, + includeChangeCounts: true + }); + } + + const diffs = (0, _jestDiff.diffStringsRaw)(expected, received, true); + const hasCommonDiff = diffs.some(diff => diff[0] === _jestDiff.DIFF_EQUAL); + const printLabel = getLabelPrinter(expectedLabel, receivedLabel); + const expectedLine = + printLabel(expectedLabel) + + printExpected( + getCommonAndChangedSubstrings( + diffs, + _jestDiff.DIFF_DELETE, + hasCommonDiff + ) + ); + const receivedLine = + printLabel(receivedLabel) + + printReceived( + getCommonAndChangedSubstrings( + diffs, + _jestDiff.DIFF_INSERT, + hasCommonDiff + ) + ); + return expectedLine + '\n' + receivedLine; + } + + if (isLineDiffable(expected, received)) { + const { + replacedExpected, + replacedReceived + } = replaceMatchedToAsymmetricMatcher( + (0, _deepCyclicCopyReplaceable.default)(expected), + (0, _deepCyclicCopyReplaceable.default)(received), + [], + [] + ); + const difference = (0, _jestDiff.default)( + replacedExpected, + replacedReceived, + { + aAnnotation: expectedLabel, + bAnnotation: receivedLabel, + expand, + includeChangeCounts: true + } + ); + + if ( + typeof difference === 'string' && + difference.includes('- ' + expectedLabel) && + difference.includes('+ ' + receivedLabel) + ) { + return difference; + } + } + + const printLabel = getLabelPrinter(expectedLabel, receivedLabel); + const expectedLine = printLabel(expectedLabel) + printExpected(expected); + const receivedLine = + printLabel(receivedLabel) + + (stringify(expected) === stringify(received) + ? 'serializes to the same string' + : printReceived(received)); + return expectedLine + '\n' + receivedLine; +}; // Sometimes, e.g. when comparing two numbers, the output from jest-diff +// does not contain more information than the `Expected:` / `Received:` already gives. +// In those cases, we do not print a diff to make the output shorter and not redundant. + +exports.printDiffOrStringify = printDiffOrStringify; + +const shouldPrintDiff = (actual, expected) => { + if (typeof actual === 'number' && typeof expected === 'number') { + return false; + } + + if (typeof actual === 'bigint' && typeof expected === 'bigint') { + return false; + } + + if (typeof actual === 'boolean' && typeof expected === 'boolean') { + return false; + } + + return true; +}; + +function replaceMatchedToAsymmetricMatcher( + replacedExpected, + replacedReceived, + expectedCycles, + receivedCycles +) { + if (!_Replaceable.default.isReplaceable(replacedExpected, replacedReceived)) { + return { + replacedExpected, + replacedReceived + }; + } + + if ( + expectedCycles.includes(replacedExpected) || + receivedCycles.includes(replacedReceived) + ) { + return { + replacedExpected, + replacedReceived + }; + } + + expectedCycles.push(replacedExpected); + receivedCycles.push(replacedReceived); + const expectedReplaceable = new _Replaceable.default(replacedExpected); + const receivedReplaceable = new _Replaceable.default(replacedReceived); + expectedReplaceable.forEach((expectedValue, key) => { + const receivedValue = receivedReplaceable.get(key); + + if (isAsymmetricMatcher(expectedValue)) { + if (expectedValue.asymmetricMatch(receivedValue)) { + receivedReplaceable.set(key, expectedValue); + } + } else if (isAsymmetricMatcher(receivedValue)) { + if (receivedValue.asymmetricMatch(expectedValue)) { + expectedReplaceable.set(key, receivedValue); + } + } else if ( + _Replaceable.default.isReplaceable(expectedValue, receivedValue) + ) { + const replaced = replaceMatchedToAsymmetricMatcher( + expectedValue, + receivedValue, + expectedCycles, + receivedCycles + ); + expectedReplaceable.set(key, replaced.replacedExpected); + receivedReplaceable.set(key, replaced.replacedReceived); + } + }); + return { + replacedExpected: expectedReplaceable.object, + replacedReceived: receivedReplaceable.object + }; +} + +function isAsymmetricMatcher(data) { + const type = (0, _jestGetType.default)(data); + return type === 'object' && typeof data.asymmetricMatch === 'function'; +} + +const diff = (a, b, options) => + shouldPrintDiff(a, b) ? (0, _jestDiff.default)(a, b, options) : null; + +exports.diff = diff; + +const pluralize = (word, count) => + (NUMBERS[count] || count) + ' ' + word + (count === 1 ? '' : 's'); // To display lines of labeled values as two columns with monospace alignment: +// given the strings which will describe the values, +// return function which given each string, returns the label: +// string, colon, space, and enough padding spaces to align the value. + +exports.pluralize = pluralize; + +const getLabelPrinter = (...strings) => { + const maxLength = strings.reduce( + (max, string) => (string.length > max ? string.length : max), + 0 + ); + return string => `${string}: ${' '.repeat(maxLength - string.length)}`; +}; + +exports.getLabelPrinter = getLabelPrinter; + +const matcherErrorMessage = (hint, generic, specific) => + `${hint}\n\n${_chalk.default.bold('Matcher error')}: ${generic}${ + typeof specific === 'string' ? '\n\n' + specific : '' + }`; // Display assertion for the report when a test fails. +// New format: rejects/resolves, not, and matcher name have black color +// Old format: matcher name has dim color + +exports.matcherErrorMessage = matcherErrorMessage; + +const matcherHint = ( + matcherName, + received = 'received', + expected = 'expected', + options = {} +) => { + const { + comment = '', + expectedColor = EXPECTED_COLOR, + isDirectExpectCall = false, + // seems redundant with received === '' + isNot = false, + promise = '', + receivedColor = RECEIVED_COLOR, + secondArgument = '', + secondArgumentColor = EXPECTED_COLOR + } = options; + let hint = ''; + let dimString = 'expect'; // concatenate adjacent dim substrings + + if (!isDirectExpectCall && received !== '') { + hint += DIM_COLOR(dimString + '(') + receivedColor(received); + dimString = ')'; + } + + if (promise !== '') { + hint += DIM_COLOR(dimString + '.') + promise; + dimString = ''; + } + + if (isNot) { + hint += DIM_COLOR(dimString + '.') + 'not'; + dimString = ''; + } + + if (matcherName.includes('.')) { + // Old format: for backward compatibility, + // especially without promise or isNot options + dimString += matcherName; + } else { + // New format: omit period from matcherName arg + hint += DIM_COLOR(dimString + '.') + matcherName; + dimString = ''; + } + + if (expected === '') { + dimString += '()'; + } else { + hint += DIM_COLOR(dimString + '(') + expectedColor(expected); + + if (secondArgument) { + hint += DIM_COLOR(', ') + secondArgumentColor(secondArgument); + } + + dimString = ')'; + } + + if (comment !== '') { + dimString += ' // ' + comment; + } + + if (dimString !== '') { + hint += DIM_COLOR(dimString); + } + + return hint; +}; + +exports.matcherHint = matcherHint; diff --git a/packages/jest-message-util/build/index.d.ts b/packages/jest-message-util/build/index.d.ts new file mode 100644 index 000000000000..7be2d84128c5 --- /dev/null +++ b/packages/jest-message-util/build/index.d.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ +import type { Config, TestResult } from '@jest/types'; +import type { Frame } from './types'; +export type { Frame } from './types'; +export declare type StackTraceConfig = Pick; +export declare type StackTraceOptions = { + noStackTrace: boolean; + noCodeFrame?: boolean; +}; +export declare const formatExecError: (error: Error | TestResult.SerializableError | string | undefined, config: StackTraceConfig, options: StackTraceOptions, testPath?: string | undefined, reuseMessage?: boolean | undefined) => string; +export declare const getStackTraceLines: (stack: string, options?: StackTraceOptions) => Array; +export declare const getTopFrame: (lines: Array) => Frame | null; +export declare const formatStackTrace: (stack: string, config: StackTraceConfig, options: StackTraceOptions, testPath?: string | undefined) => string; +export declare const formatResultsErrors: (testResults: Array, config: StackTraceConfig, options: StackTraceOptions, testPath?: string | undefined) => string | null; +export declare const separateMessageFromStack: (content: string) => { + message: string; + stack: string; +}; diff --git a/packages/jest-message-util/build/index.js b/packages/jest-message-util/build/index.js new file mode 100644 index 000000000000..32b3f762b47c --- /dev/null +++ b/packages/jest-message-util/build/index.js @@ -0,0 +1,443 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.separateMessageFromStack = exports.formatResultsErrors = exports.formatStackTrace = exports.getTopFrame = exports.getStackTraceLines = exports.formatExecError = void 0; + +var path = _interopRequireWildcard(require('path')); + +var _codeFrame = require('@babel/code-frame'); + +var _chalk = _interopRequireDefault(require('chalk')); + +var fs = _interopRequireWildcard(require('graceful-fs')); + +var _micromatch = _interopRequireDefault(require('micromatch')); + +var _slash = _interopRequireDefault(require('slash')); + +var _stackUtils = _interopRequireDefault(require('stack-utils')); + +var _prettyFormat = _interopRequireDefault(require('pretty-format')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var jestReadFile = + global[Symbol.for('jest-native-read-file')] || fs.readFileSync; +// stack utils tries to create pretty stack by making paths relative. +const stackUtils = new _stackUtils.default({ + cwd: 'something which does not exist' +}); +let nodeInternals = []; + +try { + nodeInternals = _stackUtils.default.nodeInternals(); +} catch { + // `StackUtils.nodeInternals()` fails in browsers. We don't need to remove + // node internals in the browser though, so no issue. +} + +const PATH_NODE_MODULES = `${path.sep}node_modules${path.sep}`; +const PATH_JEST_PACKAGES = `${path.sep}jest${path.sep}packages${path.sep}`; // filter for noisy stack trace lines + +const JASMINE_IGNORE = /^\s+at(?:(?:.jasmine\-)|\s+jasmine\.buildExpectationResult)/; +const JEST_INTERNALS_IGNORE = /^\s+at.*?jest(-.*?)?(\/|\\)(build|node_modules|packages)(\/|\\)/; +const ANONYMOUS_FN_IGNORE = /^\s+at .*$/; +const ANONYMOUS_PROMISE_IGNORE = /^\s+at (new )?Promise \(\).*$/; +const ANONYMOUS_GENERATOR_IGNORE = /^\s+at Generator.next \(\).*$/; +const NATIVE_NEXT_IGNORE = /^\s+at next \(native\).*$/; +const TITLE_INDENT = ' '; +const MESSAGE_INDENT = ' '; +const STACK_INDENT = ' '; +const ANCESTRY_SEPARATOR = ' \u203A '; + +const TITLE_BULLET = _chalk.default.bold('\u25cf '); + +const STACK_TRACE_COLOR = _chalk.default.dim; +const STACK_PATH_REGEXP = /\s*at.*\(?(\:\d*\:\d*|native)\)?/; +const EXEC_ERROR_MESSAGE = 'Test suite failed to run'; +const NOT_EMPTY_LINE_REGEXP = /^(?!$)/gm; + +const indentAllLines = (lines, indent) => + lines.replace(NOT_EMPTY_LINE_REGEXP, indent); + +const trim = string => (string || '').trim(); // Some errors contain not only line numbers in stack traces +// e.g. SyntaxErrors can contain snippets of code, and we don't +// want to trim those, because they may have pointers to the column/character +// which will get misaligned. + +const trimPaths = string => + string.match(STACK_PATH_REGEXP) ? trim(string) : string; + +const getRenderedCallsite = (fileContent, line, column) => { + let renderedCallsite = (0, _codeFrame.codeFrameColumns)( + fileContent, + { + start: { + column, + line + } + }, + { + highlightCode: true + } + ); + renderedCallsite = indentAllLines(renderedCallsite, MESSAGE_INDENT); + renderedCallsite = `\n${renderedCallsite}\n`; + return renderedCallsite; +}; + +const blankStringRegexp = /^\s*$/; + +function checkForCommonEnvironmentErrors(error) { + if ( + error.includes('ReferenceError: document is not defined') || + error.includes('ReferenceError: window is not defined') || + error.includes('ReferenceError: navigator is not defined') + ) { + return warnAboutWrongTestEnvironment(error, 'jsdom'); + } else if (error.includes('.unref is not a function')) { + return warnAboutWrongTestEnvironment(error, 'node'); + } + + return error; +} + +function warnAboutWrongTestEnvironment(error, env) { + return ( + _chalk.default.bold.red( + `The error below may be caused by using the wrong test environment, see ${_chalk.default.dim.underline( + 'https://jestjs.io/docs/en/configuration#testenvironment-string' + )}.\nConsider using the "${env}" test environment.\n\n` + ) + error + ); +} // ExecError is an error thrown outside of the test suite (not inside an `it` or +// `before/after each` hooks). If it's thrown, none of the tests in the file +// are executed. + +const formatExecError = (error, config, options, testPath, reuseMessage) => { + if (!error || typeof error === 'number') { + error = new Error(`Expected an Error, but "${String(error)}" was thrown`); + error.stack = ''; + } + + let message, stack; + + if (typeof error === 'string' || !error) { + error || (error = 'EMPTY ERROR'); + message = ''; + stack = error; + } else { + message = error.message; + stack = + typeof error.stack === 'string' + ? error.stack + : `thrown: ${(0, _prettyFormat.default)(error, { + maxDepth: 3 + })}`; + } + + const separated = separateMessageFromStack(stack || ''); + stack = separated.stack; + + if (separated.message.includes(trim(message))) { + // Often stack trace already contains the duplicate of the message + message = separated.message; + } + + message = checkForCommonEnvironmentErrors(message); + message = indentAllLines(message, MESSAGE_INDENT); + stack = + stack && !options.noStackTrace + ? '\n' + formatStackTrace(stack, config, options, testPath) + : ''; + + if ( + typeof stack !== 'string' || + (blankStringRegexp.test(message) && blankStringRegexp.test(stack)) + ) { + // this can happen if an empty object is thrown. + message = `thrown: ${(0, _prettyFormat.default)(error, { + maxDepth: 3 + })}`; + } + + let messageToUse; + + if (reuseMessage) { + messageToUse = ` ${message.trim()}`; + } else { + messageToUse = `${EXEC_ERROR_MESSAGE}\n\n${message}`; + } + + return TITLE_INDENT + TITLE_BULLET + messageToUse + stack + '\n'; +}; + +exports.formatExecError = formatExecError; + +const removeInternalStackEntries = (lines, options) => { + let pathCounter = 0; + return lines.filter(line => { + if (ANONYMOUS_FN_IGNORE.test(line)) { + return false; + } + + if (ANONYMOUS_PROMISE_IGNORE.test(line)) { + return false; + } + + if (ANONYMOUS_GENERATOR_IGNORE.test(line)) { + return false; + } + + if (NATIVE_NEXT_IGNORE.test(line)) { + return false; + } + + if (nodeInternals.some(internal => internal.test(line))) { + return false; + } + + if (!STACK_PATH_REGEXP.test(line)) { + return true; + } + + if (JASMINE_IGNORE.test(line)) { + return false; + } + + if (++pathCounter === 1) { + return true; // always keep the first line even if it's from Jest + } + + if (options.noStackTrace) { + return false; + } + + if (JEST_INTERNALS_IGNORE.test(line)) { + return false; + } + + return true; + }); +}; + +const formatPaths = (config, relativeTestPath, line) => { + // Extract the file path from the trace line. + const match = line.match(/(^\s*at .*?\(?)([^()]+)(:[0-9]+:[0-9]+\)?.*$)/); + + if (!match) { + return line; + } + + let filePath = (0, _slash.default)(path.relative(config.rootDir, match[2])); // highlight paths from the current test file + + if ( + (config.testMatch && + config.testMatch.length && + (0, _micromatch.default)([filePath], config.testMatch).length > 0) || + filePath === relativeTestPath + ) { + filePath = _chalk.default.reset.cyan(filePath); + } + + return STACK_TRACE_COLOR(match[1]) + filePath + STACK_TRACE_COLOR(match[3]); +}; + +const getStackTraceLines = ( + stack, + options = { + noCodeFrame: false, + noStackTrace: false + } +) => removeInternalStackEntries(stack.split(/\n/), options); + +exports.getStackTraceLines = getStackTraceLines; + +const getTopFrame = lines => { + for (const line of lines) { + if (line.includes(PATH_NODE_MODULES) || line.includes(PATH_JEST_PACKAGES)) { + continue; + } + + const parsedFrame = stackUtils.parseLine(line.trim()); + + if (parsedFrame && parsedFrame.file) { + return parsedFrame; + } + } + + return null; +}; + +exports.getTopFrame = getTopFrame; + +const formatStackTrace = (stack, config, options, testPath) => { + const lines = getStackTraceLines(stack, options); + let renderedCallsite = ''; + const relativeTestPath = testPath + ? (0, _slash.default)(path.relative(config.rootDir, testPath)) + : null; + + if (!options.noStackTrace && !options.noCodeFrame) { + const topFrame = getTopFrame(lines); + + if (topFrame) { + const {column, file: filename, line} = topFrame; + + if (line && filename && path.isAbsolute(filename)) { + let fileContent; + + try { + // TODO: check & read HasteFS instead of reading the filesystem: + // see: https://github.com/facebook/jest/pull/5405#discussion_r164281696 + fileContent = jestReadFile(filename, 'utf8'); + renderedCallsite = getRenderedCallsite(fileContent, line, column); + } catch { + // the file does not exist or is inaccessible, we ignore + } + } + } + } + + const stacktrace = lines + .filter(Boolean) + .map( + line => + STACK_INDENT + formatPaths(config, relativeTestPath, trimPaths(line)) + ) + .join('\n'); + return renderedCallsite + ? `${renderedCallsite}\n${stacktrace}` + : `\n${stacktrace}`; +}; + +exports.formatStackTrace = formatStackTrace; + +const formatResultsErrors = (testResults, config, options, testPath) => { + const failedResults = testResults.reduce((errors, result) => { + result.failureMessages + .map(checkForCommonEnvironmentErrors) + .forEach(content => + errors.push({ + content, + result + }) + ); + return errors; + }, []); + + if (!failedResults.length) { + return null; + } + + return failedResults + .map(({result, content}) => { + let {message, stack} = separateMessageFromStack(content); + stack = options.noStackTrace + ? '' + : STACK_TRACE_COLOR( + formatStackTrace(stack, config, options, testPath) + ) + '\n'; + message = indentAllLines(message, MESSAGE_INDENT); + const title = + _chalk.default.bold.red( + TITLE_INDENT + + TITLE_BULLET + + result.ancestorTitles.join(ANCESTRY_SEPARATOR) + + (result.ancestorTitles.length ? ANCESTRY_SEPARATOR : '') + + result.title + ) + '\n'; + return title + '\n' + message + '\n' + stack; + }) + .join('\n'); +}; + +exports.formatResultsErrors = formatResultsErrors; +const errorRegexp = /^Error:?\s*$/; + +const removeBlankErrorLine = str => + str + .split('\n') // Lines saying just `Error:` are useless + .filter(line => !errorRegexp.test(line)) + .join('\n') + .trimRight(); // jasmine and worker farm sometimes don't give us access to the actual +// Error object, so we have to regexp out the message from the stack string +// to format it. + +const separateMessageFromStack = content => { + if (!content) { + return { + message: '', + stack: '' + }; + } // All lines up to what looks like a stack -- or if nothing looks like a stack + // (maybe it's a code frame instead), just the first non-empty line. + // If the error is a plain "Error:" instead of a SyntaxError or TypeError we + // remove the prefix from the message because it is generally not useful. + + const messageMatch = content.match( + /^(?:Error: )?([\s\S]*?(?=\n\s*at\s.*:\d*:\d*)|\s*.*)([\s\S]*)$/ + ); + + if (!messageMatch) { + // For typescript + throw new Error('If you hit this error, the regex above is buggy.'); + } + + const message = removeBlankErrorLine(messageMatch[1]); + const stack = removeBlankErrorLine(messageMatch[2]); + return { + message, + stack + }; +}; + +exports.separateMessageFromStack = separateMessageFromStack; diff --git a/packages/jest-message-util/build/types.d.ts b/packages/jest-message-util/build/types.d.ts new file mode 100644 index 000000000000..9a95b778a43f --- /dev/null +++ b/packages/jest-message-util/build/types.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import type { StackData } from 'stack-utils'; +export interface Frame extends StackData { + file: string; +} diff --git a/packages/jest-message-util/build/types.js b/packages/jest-message-util/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-message-util/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-mock/build/index.d.ts b/packages/jest-mock/build/index.d.ts new file mode 100644 index 000000000000..e9e3aa047d1d --- /dev/null +++ b/packages/jest-mock/build/index.d.ts @@ -0,0 +1,135 @@ +/** + * 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. + */ +/// +declare type Global = NodeJS.Global; +export declare type MockFunctionMetadataType = 'object' | 'array' | 'regexp' | 'function' | 'constant' | 'collection' | 'null' | 'undefined'; +export declare type MockFunctionMetadata, Type = MockFunctionMetadataType> = { + ref?: number; + members?: Record>; + mockImpl?: (...args: Y) => T; + name?: string; + refID?: number; + type?: Type; + value?: T; + length?: number; +}; +export interface Mock = Array> extends Function, MockInstance { + new (...args: Y): T; + (...args: Y): T; +} +export interface SpyInstance> extends MockInstance { +} +export interface MockInstance> { + _isMockFunction: true; + _protoImpl: Function; + getMockName(): string; + getMockImplementation(): Function | undefined; + mock: MockFunctionState; + mockClear(): this; + mockReset(): this; + mockRestore(): void; + mockImplementation(fn: (...args: Y) => T): this; + mockImplementation(fn: () => Promise): this; + mockImplementationOnce(fn: (...args: Y) => T): this; + mockImplementationOnce(fn: () => Promise): this; + mockName(name: string): this; + mockReturnThis(): this; + mockReturnValue(value: T): this; + mockReturnValueOnce(value: T): this; + mockResolvedValue(value: Unpromisify): this; + mockResolvedValueOnce(value: Unpromisify): this; + mockRejectedValue(value: unknown): this; + mockRejectedValueOnce(value: unknown): this; +} +declare type Unpromisify = T extends Promise ? R : never; +/** + * Possible types of a MockFunctionResult. + * 'return': The call completed by returning normally. + * 'throw': The call completed by throwing a value. + * 'incomplete': The call has not completed yet. This is possible if you read + * the mock function result from within the mock function itself + * (or a function called by the mock function). + */ +declare type MockFunctionResultType = 'return' | 'throw' | 'incomplete'; +/** + * Represents the result of a single call to a mock function. + */ +declare type MockFunctionResult = { + /** + * Indicates how the call completed. + */ + type: MockFunctionResultType; + /** + * The value that was either thrown or returned by the function. + * Undefined when type === 'incomplete'. + */ + value: unknown; +}; +declare type MockFunctionState> = { + calls: Array; + instances: Array; + invocationCallOrder: Array; + /** + * List of results of calls to the mock function. + */ + results: Array; +}; +declare type NonFunctionPropertyNames = { + [K in keyof T]: T[K] extends (...args: Array) => any ? never : K; +}[keyof T] & string; +declare type FunctionPropertyNames = { + [K in keyof T]: T[K] extends (...args: Array) => any ? K : never; +}[keyof T] & string; +export declare class ModuleMocker { + private _environmentGlobal; + private _mockState; + private _mockConfigRegistry; + private _spyState; + private _invocationCallCounter; + /** + * @see README.md + * @param global Global object of the test environment, used to create + * mocks + */ + constructor(global: Global); + private _getSlots; + private _ensureMockConfig; + private _ensureMockState; + private _defaultMockConfig; + private _defaultMockState; + private _makeComponent; + private _createMockFunction; + private _generateMock; + /** + * @see README.md + * @param _metadata Metadata for the mock in the schema returned by the + * getMetadata method of this module. + */ + generateFromMetadata>(_metadata: MockFunctionMetadata): Mock; + /** + * @see README.md + * @param component The component for which to retrieve metadata. + */ + getMetadata>(component: T, _refs?: Map): MockFunctionMetadata | null; + isMockFunction(fn: unknown): fn is Mock; + fn>(implementation?: (...args: Y) => T): Mock; + spyOn>(object: T, methodName: M, accessType: 'get'): SpyInstance; + spyOn>(object: T, methodName: M, accessType: 'set'): SpyInstance; + spyOn>(object: T, methodName: M): T[M] extends (...args: Array) => any ? SpyInstance, Parameters> : never; + private _spyOnProperty; + clearAllMocks(): void; + resetAllMocks(): void; + restoreAllMocks(): void; + private _typeOf; +} +export declare const fn: (implementation?: ((...args: Y) => T) | undefined) => Mock; +export declare const spyOn: { + >(object: T, methodName: M, accessType: 'get'): SpyInstance; + >(object: T_2, methodName: M_2, accessType: 'set'): SpyInstance; + >(object: T_4, methodName: M_4): T_4[M_4] extends (...args: Array) => any ? SpyInstance, Parameters> : never; +}; +export {}; diff --git a/packages/jest-mock/build/index.js b/packages/jest-mock/build/index.js new file mode 100644 index 000000000000..4664f4973461 --- /dev/null +++ b/packages/jest-mock/build/index.js @@ -0,0 +1,952 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.spyOn = exports.fn = exports.ModuleMocker = void 0; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * 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. + */ + +/* eslint-disable local/ban-types-eventually, local/prefer-rest-params-eventually */ +// | Window – add once TS improves typings; + +/** + * Possible types of a MockFunctionResult. + * 'return': The call completed by returning normally. + * 'throw': The call completed by throwing a value. + * 'incomplete': The call has not completed yet. This is possible if you read + * the mock function result from within the mock function itself + * (or a function called by the mock function). + */ + +/** + * Represents the result of a single call to a mock function. + */ +// see https://github.com/Microsoft/TypeScript/issues/25215 +const MOCK_CONSTRUCTOR_NAME = 'mockConstructor'; +const FUNCTION_NAME_RESERVED_PATTERN = /[\s!-\/:-@\[-`{-~]/; +const FUNCTION_NAME_RESERVED_REPLACE = new RegExp( + FUNCTION_NAME_RESERVED_PATTERN.source, + 'g' +); +const RESERVED_KEYWORDS = new Set([ + 'arguments', + 'await', + 'break', + 'case', + 'catch', + 'class', + 'const', + 'continue', + 'debugger', + 'default', + 'delete', + 'do', + 'else', + 'enum', + 'eval', + 'export', + 'extends', + 'false', + 'finally', + 'for', + 'function', + 'if', + 'implements', + 'import', + 'in', + 'instanceof', + 'interface', + 'let', + 'new', + 'null', + 'package', + 'private', + 'protected', + 'public', + 'return', + 'static', + 'super', + 'switch', + 'this', + 'throw', + 'true', + 'try', + 'typeof', + 'var', + 'void', + 'while', + 'with', + 'yield' +]); + +function matchArity(fn, length) { + let mockConstructor; + + switch (length) { + case 1: + mockConstructor = function (_a) { + return fn.apply(this, arguments); + }; + + break; + + case 2: + mockConstructor = function (_a, _b) { + return fn.apply(this, arguments); + }; + + break; + + case 3: + mockConstructor = function (_a, _b, _c) { + return fn.apply(this, arguments); + }; + + break; + + case 4: + mockConstructor = function (_a, _b, _c, _d) { + return fn.apply(this, arguments); + }; + + break; + + case 5: + mockConstructor = function (_a, _b, _c, _d, _e) { + return fn.apply(this, arguments); + }; + + break; + + case 6: + mockConstructor = function (_a, _b, _c, _d, _e, _f) { + return fn.apply(this, arguments); + }; + + break; + + case 7: + mockConstructor = function (_a, _b, _c, _d, _e, _f, _g) { + return fn.apply(this, arguments); + }; + + break; + + case 8: + mockConstructor = function (_a, _b, _c, _d, _e, _f, _g, _h) { + return fn.apply(this, arguments); + }; + + break; + + case 9: + mockConstructor = function (_a, _b, _c, _d, _e, _f, _g, _h, _i) { + return fn.apply(this, arguments); + }; + + break; + + default: + mockConstructor = function () { + return fn.apply(this, arguments); + }; + + break; + } + + return mockConstructor; +} + +function getObjectType(value) { + return Object.prototype.toString.apply(value).slice(8, -1); +} + +function getType(ref) { + const typeName = getObjectType(ref); + + if ( + typeName === 'Function' || + typeName === 'AsyncFunction' || + typeName === 'GeneratorFunction' + ) { + return 'function'; + } else if (Array.isArray(ref)) { + return 'array'; + } else if (typeName === 'Object') { + return 'object'; + } else if ( + typeName === 'Number' || + typeName === 'String' || + typeName === 'Boolean' || + typeName === 'Symbol' + ) { + return 'constant'; + } else if ( + typeName === 'Map' || + typeName === 'WeakMap' || + typeName === 'Set' + ) { + return 'collection'; + } else if (typeName === 'RegExp') { + return 'regexp'; + } else if (ref === undefined) { + return 'undefined'; + } else if (ref === null) { + return 'null'; + } else { + return null; + } +} + +function isReadonlyProp(object, prop) { + if ( + prop === 'arguments' || + prop === 'caller' || + prop === 'callee' || + prop === 'name' || + prop === 'length' + ) { + const typeName = getObjectType(object); + return ( + typeName === 'Function' || + typeName === 'AsyncFunction' || + typeName === 'GeneratorFunction' + ); + } + + if ( + prop === 'source' || + prop === 'global' || + prop === 'ignoreCase' || + prop === 'multiline' + ) { + return getObjectType(object) === 'RegExp'; + } + + return false; +} + +class ModuleMocker { + /** + * @see README.md + * @param global Global object of the test environment, used to create + * mocks + */ + constructor(global) { + _defineProperty(this, '_environmentGlobal', void 0); + + _defineProperty(this, '_mockState', void 0); + + _defineProperty(this, '_mockConfigRegistry', void 0); + + _defineProperty(this, '_spyState', void 0); + + _defineProperty(this, '_invocationCallCounter', void 0); + + this._environmentGlobal = global; + this._mockState = new WeakMap(); + this._mockConfigRegistry = new WeakMap(); + this._spyState = new Set(); + this._invocationCallCounter = 1; + } + + _getSlots(object) { + if (!object) { + return []; + } + + const slots = new Set(); + const EnvObjectProto = this._environmentGlobal.Object.prototype; + const EnvFunctionProto = this._environmentGlobal.Function.prototype; + const EnvRegExpProto = this._environmentGlobal.RegExp.prototype; // Also check the builtins in the current context as they leak through + // core node modules. + + const ObjectProto = Object.prototype; + const FunctionProto = Function.prototype; + const RegExpProto = RegExp.prototype; // Properties of Object.prototype, Function.prototype and RegExp.prototype + // are never reported as slots + + while ( + object != null && + object !== EnvObjectProto && + object !== EnvFunctionProto && + object !== EnvRegExpProto && + object !== ObjectProto && + object !== FunctionProto && + object !== RegExpProto + ) { + const ownNames = Object.getOwnPropertyNames(object); + + for (let i = 0; i < ownNames.length; i++) { + const prop = ownNames[i]; + + if (!isReadonlyProp(object, prop)) { + const propDesc = Object.getOwnPropertyDescriptor(object, prop); + + if ((propDesc !== undefined && !propDesc.get) || object.__esModule) { + slots.add(prop); + } + } + } + + object = Object.getPrototypeOf(object); + } + + return Array.from(slots); + } + + _ensureMockConfig(f) { + let config = this._mockConfigRegistry.get(f); + + if (!config) { + config = this._defaultMockConfig(); + + this._mockConfigRegistry.set(f, config); + } + + return config; + } + + _ensureMockState(f) { + let state = this._mockState.get(f); + + if (!state) { + state = this._defaultMockState(); + + this._mockState.set(f, state); + } + + return state; + } + + _defaultMockConfig() { + return { + mockImpl: undefined, + mockName: 'jest.fn()', + specificMockImpls: [], + specificReturnValues: [] + }; + } + + _defaultMockState() { + return { + calls: [], + instances: [], + invocationCallOrder: [], + results: [] + }; + } + + _makeComponent(metadata, restore) { + if (metadata.type === 'object') { + return new this._environmentGlobal.Object(); + } else if (metadata.type === 'array') { + return new this._environmentGlobal.Array(); + } else if (metadata.type === 'regexp') { + return new this._environmentGlobal.RegExp(''); + } else if ( + metadata.type === 'constant' || + metadata.type === 'collection' || + metadata.type === 'null' || + metadata.type === 'undefined' + ) { + return metadata.value; + } else if (metadata.type === 'function') { + const prototype = + (metadata.members && + metadata.members.prototype && + metadata.members.prototype.members) || + {}; + + const prototypeSlots = this._getSlots(prototype); + + const mocker = this; + const mockConstructor = matchArity(function (...args) { + const mockState = mocker._ensureMockState(f); + + const mockConfig = mocker._ensureMockConfig(f); + + mockState.instances.push(this); + mockState.calls.push(args); // Create and record an "incomplete" mock result immediately upon + // calling rather than waiting for the mock to return. This avoids + // issues caused by recursion where results can be recorded in the + // wrong order. + + const mockResult = { + type: 'incomplete', + value: undefined + }; + mockState.results.push(mockResult); + mockState.invocationCallOrder.push(mocker._invocationCallCounter++); // Will be set to the return value of the mock if an error is not thrown + + let finalReturnValue; // Will be set to the error that is thrown by the mock (if it throws) + + let thrownError; // Will be set to true if the mock throws an error. The presence of a + // value in `thrownError` is not a 100% reliable indicator because a + // function could throw a value of undefined. + + let callDidThrowError = false; + + try { + // The bulk of the implementation is wrapped in an immediately + // executed arrow function so the return value of the mock function + // can be easily captured and recorded, despite the many separate + // return points within the logic. + finalReturnValue = (() => { + if (this instanceof f) { + // This is probably being called as a constructor + prototypeSlots.forEach(slot => { + // Copy prototype methods to the instance to make + // it easier to interact with mock instance call and + // return values + if (prototype[slot].type === 'function') { + // @ts-expect-error no index signature + const protoImpl = this[slot]; // @ts-expect-error no index signature + + this[slot] = mocker.generateFromMetadata(prototype[slot]); // @ts-expect-error no index signature + + this[slot]._protoImpl = protoImpl; + } + }); // Run the mock constructor implementation + + const mockImpl = mockConfig.specificMockImpls.length + ? mockConfig.specificMockImpls.shift() + : mockConfig.mockImpl; + return mockImpl && mockImpl.apply(this, arguments); + } // If mockImplementationOnce()/mockImplementation() is last set, + // implementation use the mock + + let specificMockImpl = mockConfig.specificMockImpls.shift(); + + if (specificMockImpl === undefined) { + specificMockImpl = mockConfig.mockImpl; + } + + if (specificMockImpl) { + return specificMockImpl.apply(this, arguments); + } // Otherwise use prototype implementation + + if (f._protoImpl) { + return f._protoImpl.apply(this, arguments); + } + + return undefined; + })(); + } catch (error) { + // Store the thrown error so we can record it, then re-throw it. + thrownError = error; + callDidThrowError = true; + throw error; + } finally { + // Record the result of the function. + // NOTE: Intentionally NOT pushing/indexing into the array of mock + // results here to avoid corrupting results data if mockClear() + // is called during the execution of the mock. + mockResult.type = callDidThrowError ? 'throw' : 'return'; + mockResult.value = callDidThrowError ? thrownError : finalReturnValue; + } + + return finalReturnValue; + }, metadata.length || 0); + + const f = this._createMockFunction(metadata, mockConstructor); + + f._isMockFunction = true; + + f.getMockImplementation = () => this._ensureMockConfig(f).mockImpl; + + if (typeof restore === 'function') { + this._spyState.add(restore); + } + + this._mockState.set(f, this._defaultMockState()); + + this._mockConfigRegistry.set(f, this._defaultMockConfig()); + + Object.defineProperty(f, 'mock', { + configurable: false, + enumerable: true, + get: () => this._ensureMockState(f), + set: val => this._mockState.set(f, val) + }); + + f.mockClear = () => { + this._mockState.delete(f); + + return f; + }; + + f.mockReset = () => { + f.mockClear(); + + this._mockConfigRegistry.delete(f); + + return f; + }; + + f.mockRestore = () => { + f.mockReset(); + return restore ? restore() : undefined; + }; + + f.mockReturnValueOnce = ( + value // next function call will return this value or default return value + ) => f.mockImplementationOnce(() => value); + + f.mockResolvedValueOnce = value => + f.mockImplementationOnce(() => Promise.resolve(value)); + + f.mockRejectedValueOnce = value => + f.mockImplementationOnce(() => Promise.reject(value)); + + f.mockReturnValue = ( + value // next function call will return specified return value or this one + ) => f.mockImplementation(() => value); + + f.mockResolvedValue = value => + f.mockImplementation(() => Promise.resolve(value)); + + f.mockRejectedValue = value => + f.mockImplementation(() => Promise.reject(value)); + + f.mockImplementationOnce = fn => { + // next function call will use this mock implementation return value + // or default mock implementation return value + const mockConfig = this._ensureMockConfig(f); + + mockConfig.specificMockImpls.push(fn); + return f; + }; + + f.mockImplementation = fn => { + // next function call will use mock implementation return value + const mockConfig = this._ensureMockConfig(f); + + mockConfig.mockImpl = fn; + return f; + }; + + f.mockReturnThis = () => + f.mockImplementation(function () { + return this; + }); + + f.mockName = name => { + if (name) { + const mockConfig = this._ensureMockConfig(f); + + mockConfig.mockName = name; + } + + return f; + }; + + f.getMockName = () => { + const mockConfig = this._ensureMockConfig(f); + + return mockConfig.mockName || 'jest.fn()'; + }; + + if (metadata.mockImpl) { + f.mockImplementation(metadata.mockImpl); + } + + return f; + } else { + const unknownType = metadata.type || 'undefined type'; + throw new Error('Unrecognized type ' + unknownType); + } + } + + _createMockFunction(metadata, mockConstructor) { + let name = metadata.name; + + if (!name) { + return mockConstructor; + } // Preserve `name` property of mocked function. + + const boundFunctionPrefix = 'bound '; + let bindCall = ''; // if-do-while for perf reasons. The common case is for the if to fail. + + if (name && name.startsWith(boundFunctionPrefix)) { + do { + name = name.substring(boundFunctionPrefix.length); // Call bind() just to alter the function name. + + bindCall = '.bind(null)'; + } while (name && name.startsWith(boundFunctionPrefix)); + } // Special case functions named `mockConstructor` to guard for infinite + // loops. + + if (name === MOCK_CONSTRUCTOR_NAME) { + return mockConstructor; + } + + if ( + // It's a syntax error to define functions with a reserved keyword + // as name. + RESERVED_KEYWORDS.has(name) || // It's also a syntax error to define functions with a name that starts with a number + /^\d/.test(name) + ) { + name = '$' + name; + } // It's also a syntax error to define a function with a reserved character + // as part of it's name. + + if (FUNCTION_NAME_RESERVED_PATTERN.test(name)) { + name = name.replace(FUNCTION_NAME_RESERVED_REPLACE, '$'); + } + + const body = + 'return function ' + + name + + '() {' + + 'return ' + + MOCK_CONSTRUCTOR_NAME + + '.apply(this,arguments);' + + '}' + + bindCall; + const createConstructor = new this._environmentGlobal.Function( + MOCK_CONSTRUCTOR_NAME, + body + ); + return createConstructor(mockConstructor); + } + + _generateMock(metadata, callbacks, refs) { + // metadata not compatible but it's the same type, maybe problem with + // overloading of _makeComponent and not _generateMock? + // @ts-expect-error + const mock = this._makeComponent(metadata); + + if (metadata.refID != null) { + refs[metadata.refID] = mock; + } + + this._getSlots(metadata.members).forEach(slot => { + const slotMetadata = (metadata.members && metadata.members[slot]) || {}; + + if (slotMetadata.ref != null) { + callbacks.push( + (function (ref) { + return () => (mock[slot] = refs[ref]); + })(slotMetadata.ref) + ); + } else { + mock[slot] = this._generateMock(slotMetadata, callbacks, refs); + } + }); + + if ( + metadata.type !== 'undefined' && + metadata.type !== 'null' && + mock.prototype && + typeof mock.prototype === 'object' + ) { + mock.prototype.constructor = mock; + } + + return mock; + } + /** + * @see README.md + * @param _metadata Metadata for the mock in the schema returned by the + * getMetadata method of this module. + */ + + generateFromMetadata(_metadata) { + const callbacks = []; + const refs = {}; + + const mock = this._generateMock(_metadata, callbacks, refs); + + callbacks.forEach(setter => setter()); + return mock; + } + /** + * @see README.md + * @param component The component for which to retrieve metadata. + */ + + getMetadata(component, _refs) { + const refs = _refs || new Map(); + const ref = refs.get(component); + + if (ref != null) { + return { + ref + }; + } + + const type = getType(component); + + if (!type) { + return null; + } + + const metadata = { + type + }; + + if ( + type === 'constant' || + type === 'collection' || + type === 'undefined' || + type === 'null' + ) { + metadata.value = component; + return metadata; + } else if (type === 'function') { + // @ts-expect-error this is a function so it has a name + metadata.name = component.name; // @ts-expect-error may be a mock + + if (component._isMockFunction === true) { + // @ts-expect-error may be a mock + metadata.mockImpl = component.getMockImplementation(); + } + } + + metadata.refID = refs.size; + refs.set(component, metadata.refID); + let members = null; // Leave arrays alone + + if (type !== 'array') { + this._getSlots(component).forEach(slot => { + if ( + type === 'function' && // @ts-expect-error may be a mock + component._isMockFunction === true && + slot.match(/^mock/) + ) { + return; + } // @ts-expect-error no index signature + + const slotMetadata = this.getMetadata(component[slot], refs); + + if (slotMetadata) { + if (!members) { + members = {}; + } + + members[slot] = slotMetadata; + } + }); + } + + if (members) { + metadata.members = members; + } + + return metadata; + } + + isMockFunction(fn) { + return !!fn && fn._isMockFunction === true; + } + + fn(implementation) { + const length = implementation ? implementation.length : 0; + + const fn = this._makeComponent({ + length, + type: 'function' + }); + + if (implementation) { + fn.mockImplementation(implementation); + } + + return fn; + } + + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + spyOn(object, methodName, accessType) { + if (accessType) { + return this._spyOnProperty(object, methodName, accessType); + } + + if (typeof object !== 'object' && typeof object !== 'function') { + throw new Error( + 'Cannot spyOn on a primitive value; ' + this._typeOf(object) + ' given' + ); + } + + const original = object[methodName]; + + if (!this.isMockFunction(original)) { + if (typeof original !== 'function') { + throw new Error( + 'Cannot spy the ' + + methodName + + ' property because it is not a function; ' + + this._typeOf(original) + + ' given instead' + ); + } + + const isMethodOwner = object.hasOwnProperty(methodName); + let descriptor = Object.getOwnPropertyDescriptor(object, methodName); + let proto = Object.getPrototypeOf(object); + + while (!descriptor && proto !== null) { + descriptor = Object.getOwnPropertyDescriptor(proto, methodName); + proto = Object.getPrototypeOf(proto); + } + + let mock; + + if (descriptor && descriptor.get) { + const originalGet = descriptor.get; + mock = this._makeComponent( + { + type: 'function' + }, + () => { + descriptor.get = originalGet; + Object.defineProperty(object, methodName, descriptor); + } + ); + + descriptor.get = () => mock; + + Object.defineProperty(object, methodName, descriptor); + } else { + mock = this._makeComponent( + { + type: 'function' + }, + () => { + if (isMethodOwner) { + object[methodName] = original; + } else { + delete object[methodName]; + } + } + ); // @ts-expect-error overriding original method with a Mock + + object[methodName] = mock; + } + + mock.mockImplementation(function () { + return original.apply(this, arguments); + }); + } + + return object[methodName]; + } + + _spyOnProperty(obj, propertyName, accessType = 'get') { + if (typeof obj !== 'object' && typeof obj !== 'function') { + throw new Error( + 'Cannot spyOn on a primitive value; ' + this._typeOf(obj) + ' given' + ); + } + + if (!obj) { + throw new Error( + 'spyOn could not find an object to spy upon for ' + propertyName + '' + ); + } + + if (!propertyName) { + throw new Error('No property name supplied'); + } + + let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); + let proto = Object.getPrototypeOf(obj); + + while (!descriptor && proto !== null) { + descriptor = Object.getOwnPropertyDescriptor(proto, propertyName); + proto = Object.getPrototypeOf(proto); + } + + if (!descriptor) { + throw new Error(propertyName + ' property does not exist'); + } + + if (!descriptor.configurable) { + throw new Error(propertyName + ' is not declared configurable'); + } + + if (!descriptor[accessType]) { + throw new Error( + 'Property ' + propertyName + ' does not have access type ' + accessType + ); + } + + const original = descriptor[accessType]; + + if (!this.isMockFunction(original)) { + if (typeof original !== 'function') { + throw new Error( + 'Cannot spy the ' + + propertyName + + ' property because it is not a function; ' + + this._typeOf(original) + + ' given instead' + ); + } // @ts-expect-error: mock is assignable + + descriptor[accessType] = this._makeComponent( + { + type: 'function' + }, + () => { + // @ts-expect-error: mock is assignable + descriptor[accessType] = original; + Object.defineProperty(obj, propertyName, descriptor); + } + ); + descriptor[accessType].mockImplementation(function () { + // @ts-expect-error + return original.apply(this, arguments); + }); + } + + Object.defineProperty(obj, propertyName, descriptor); + return descriptor[accessType]; + } + + clearAllMocks() { + this._mockState = new WeakMap(); + } + + resetAllMocks() { + this._mockConfigRegistry = new WeakMap(); + this._mockState = new WeakMap(); + } + + restoreAllMocks() { + this._spyState.forEach(restore => restore()); + + this._spyState = new Set(); + } + + _typeOf(value) { + return value == null ? '' + value : typeof value; + } +} + +exports.ModuleMocker = ModuleMocker; +const JestMock = new ModuleMocker(global); +const fn = JestMock.fn; +exports.fn = fn; +const spyOn = JestMock.spyOn; +exports.spyOn = spyOn; diff --git a/packages/jest-phabricator/build/index.d.ts b/packages/jest-phabricator/build/index.d.ts new file mode 100644 index 000000000000..92e094a40efc --- /dev/null +++ b/packages/jest-phabricator/build/index.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { AggregatedResult } from '@jest/test-result'; +declare const _default: (results: AggregatedResult) => AggregatedResult; +export = _default; diff --git a/packages/jest-phabricator/build/index.js b/packages/jest-phabricator/build/index.js new file mode 100644 index 000000000000..2d908aa8aff5 --- /dev/null +++ b/packages/jest-phabricator/build/index.js @@ -0,0 +1,37 @@ +'use strict'; + +/** + * 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. + */ +function summarize(coverageMap) { + if (!coverageMap) { + return coverageMap; + } + + const summaries = Object.create(null); + coverageMap.files().forEach(file => { + const covered = []; + const lineCoverage = coverageMap.fileCoverageFor(file).getLineCoverage(); + Object.keys(lineCoverage).forEach(lineNumber => { + const number = parseInt(lineNumber, 10); // Line numbers start at one + + covered[number - 1] = lineCoverage[number] ? 'C' : 'U'; + }); + + for (let i = 0; i < covered.length; i++) { + if (!covered[i]) { + covered[i] = 'N'; + } + } + + summaries[file] = covered.join(''); + }); + return summaries; +} + +module.exports = function (results) { + return {...results, coverageMap: summarize(results.coverageMap)}; +}; diff --git a/packages/jest-regex-util/build/index.d.ts b/packages/jest-regex-util/build/index.d.ts new file mode 100644 index 000000000000..a15e9d4a125a --- /dev/null +++ b/packages/jest-regex-util/build/index.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + * + */ +export declare const escapePathForRegex: (dir: string) => string; +export declare const escapeStrForRegex: (string: string) => string; +export declare const replacePathSepForRegex: (string: string) => string; diff --git a/packages/jest-regex-util/build/index.js b/packages/jest-regex-util/build/index.js new file mode 100644 index 000000000000..3c86d68004f7 --- /dev/null +++ b/packages/jest-regex-util/build/index.js @@ -0,0 +1,45 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.replacePathSepForRegex = exports.escapeStrForRegex = exports.escapePathForRegex = void 0; + +var _path = require('path'); + +/** + * 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 escapePathForRegex = dir => { + if (_path.sep === '\\') { + // Replace "\" with "/" so it's not escaped by escapeStrForRegex. + // replacePathSepForRegex will convert it back. + dir = dir.replace(/\\/g, '/'); + } + + return replacePathSepForRegex(escapeStrForRegex(dir)); +}; + +exports.escapePathForRegex = escapePathForRegex; + +const escapeStrForRegex = string => + string.replace(/[[\]{}()*+?.\\^$|]/g, '\\$&'); + +exports.escapeStrForRegex = escapeStrForRegex; + +const replacePathSepForRegex = string => { + if (_path.sep === '\\') { + return string.replace( + /(\/|(.)?\\(?![[\]{}()*+?.^$|\\]))/g, + (_match, _, p2) => (p2 && p2 !== '\\' ? p2 + '\\\\' : '\\\\') + ); + } + + return string; +}; + +exports.replacePathSepForRegex = replacePathSepForRegex; diff --git a/packages/jest-repl/build/cli/args.d.ts b/packages/jest-repl/build/cli/args.d.ts new file mode 100644 index 000000000000..d1c852cfb893 --- /dev/null +++ b/packages/jest-repl/build/cli/args.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + * + */ +import type { Options } from 'yargs'; +export declare const usage = "Usage: $0 [--config=]"; +export declare const options: Record; diff --git a/packages/jest-repl/build/cli/args.js b/packages/jest-repl/build/cli/args.js new file mode 100644 index 000000000000..cfe3d124a5e9 --- /dev/null +++ b/packages/jest-repl/build/cli/args.js @@ -0,0 +1,57 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.options = exports.usage = void 0; + +/** + * 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 usage = 'Usage: $0 [--config=]'; +exports.usage = usage; +const runtimeCLIOptions = { + cache: { + default: true, + description: + 'Whether to use the preprocessor cache. Disable ' + + 'the cache using --no-cache.', + type: 'boolean' + }, + config: { + alias: 'c', + description: 'The path to a Jest config file.', + type: 'string' + }, + debug: { + description: 'Print debugging info about your jest config.', + type: 'boolean' + }, + version: { + alias: 'v', + description: 'Print the version and exit', + type: 'boolean' + }, + watchman: { + default: true, + description: + 'Whether to use watchman for file crawling. Disable using ' + + '--no-watchman.', + type: 'boolean' + } +}; +const options = { + ...runtimeCLIOptions, + replname: { + alias: 'r', + description: + 'The "name" of the file given to transformers to be ' + + 'transformed. For example, "repl.ts" if using a TypeScript transformer.', + type: 'string' + } +}; +exports.options = options; diff --git a/packages/jest-repl/build/cli/index.d.ts b/packages/jest-repl/build/cli/index.d.ts new file mode 100644 index 000000000000..8ac47d4e2164 --- /dev/null +++ b/packages/jest-repl/build/cli/index.d.ts @@ -0,0 +1,10 @@ +#!/usr/bin/env node +/** + * 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. + * + */ +declare const _default: () => void; +export = _default; diff --git a/packages/jest-repl/build/cli/index.js b/packages/jest-repl/build/cli/index.js new file mode 100644 index 000000000000..a803bfadca11 --- /dev/null +++ b/packages/jest-repl/build/cli/index.js @@ -0,0 +1,105 @@ +#!/usr/bin/env node + +/** + * 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. + * + */ +'use strict'; + +function _yargs() { + const data = _interopRequireDefault(require('yargs')); + + _yargs = function () { + return data; + }; + + return data; +} + +function _jestConfig() { + const data = require('jest-config'); + + _jestConfig = function () { + return data; + }; + + return data; +} + +function _jestValidate() { + const data = require('jest-validate'); + + _jestValidate = function () { + return data; + }; + + return data; +} + +var args = _interopRequireWildcard(require('./args')); + +var _runtimeCli = require('./runtime-cli'); + +var _version = require('./version'); + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +const REPL_SCRIPT = require.resolve('./repl.js'); + +module.exports = function () { + const argv = _yargs().default.usage(args.usage).options(args.options).argv; + + (0, _jestValidate().validateCLIOptions)(argv, { + ...args.options, + deprecationEntries: _jestConfig().deprecationEntries + }); + argv._ = [REPL_SCRIPT]; + (0, _runtimeCli.run)(argv, [`Jest REPL v${_version.VERSION}`]); +}; diff --git a/packages/jest-repl/build/cli/repl.d.ts b/packages/jest-repl/build/cli/repl.d.ts new file mode 100644 index 000000000000..fac0c7e359c7 --- /dev/null +++ b/packages/jest-repl/build/cli/repl.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export {}; diff --git a/packages/jest-repl/build/cli/repl.js b/packages/jest-repl/build/cli/repl.js new file mode 100644 index 000000000000..b0a62a252de9 --- /dev/null +++ b/packages/jest-repl/build/cli/repl.js @@ -0,0 +1,166 @@ +'use strict'; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function repl() { + const data = _interopRequireWildcard(require('repl')); + + repl = function () { + return data; + }; + + return data; +} + +function _vm() { + const data = require('vm'); + + _vm = function () { + return data; + }; + + return data; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +let transformer; +let transformerConfig; + +const evalCommand = (cmd, _context, _filename, callback) => { + let result; + + try { + if (transformer) { + const transformResult = transformer.process( + cmd, + jestGlobalConfig.replname || 'jest.js', + { + cacheFS: new Map(), + config: jestProjectConfig, + configString: JSON.stringify(jestProjectConfig), + instrument: false, + supportsDynamicImport: false, + supportsExportNamespaceFrom: false, + supportsStaticESM: false, + supportsTopLevelAwait: false, + transformerConfig + } + ); + cmd = + typeof transformResult === 'string' + ? transformResult + : transformResult.code; + } + + result = (0, _vm().runInThisContext)(cmd); + } catch (e) { + return callback(isRecoverableError(e) ? new (repl().Recoverable)(e) : e); + } + + return callback(null, result); +}; + +const isRecoverableError = error => { + if (error && error.name === 'SyntaxError') { + return [ + 'Unterminated template', + 'Missing } in template expression', + 'Unexpected end of input', + 'missing ) after argument list', + 'Unexpected token' + ].some(exception => error.message.includes(exception)); + } + + return false; +}; + +if (jestProjectConfig.transform) { + let transformerPath = null; + + for (let i = 0; i < jestProjectConfig.transform.length; i++) { + if (new RegExp(jestProjectConfig.transform[i][0]).test('foobar.js')) { + transformerPath = jestProjectConfig.transform[i][1]; + transformerConfig = jestProjectConfig.transform[i][2]; + break; + } + } + + if (transformerPath) { + transformer = require(transformerPath); + + if (typeof transformer.process !== 'function') { + throw new TypeError( + 'Jest: a transformer must export a `process` function.' + ); + } + } +} + +const replInstance = repl().start({ + eval: evalCommand, + prompt: '\u203A ', + useGlobal: true +}); + +replInstance.context.require = moduleName => { + if (/(\/|\\|\.)/.test(moduleName)) { + moduleName = path().resolve(process.cwd(), moduleName); + } + + return require(moduleName); +}; diff --git a/packages/jest-repl/build/cli/runtime-cli.d.ts b/packages/jest-repl/build/cli/runtime-cli.d.ts new file mode 100644 index 000000000000..7f3973e605e1 --- /dev/null +++ b/packages/jest-repl/build/cli/runtime-cli.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export declare function run(cliArgv?: Config.Argv, cliInfo?: Array): Promise; diff --git a/packages/jest-repl/build/cli/runtime-cli.js b/packages/jest-repl/build/cli/runtime-cli.js new file mode 100644 index 000000000000..85fe2f60b364 --- /dev/null +++ b/packages/jest-repl/build/cli/runtime-cli.js @@ -0,0 +1,270 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.run = run; + +function _os() { + const data = require('os'); + + _os = 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 _yargs() { + const data = _interopRequireDefault(require('yargs')); + + _yargs = function () { + return data; + }; + + return data; +} + +function _console() { + const data = require('@jest/console'); + + _console = function () { + return data; + }; + + return data; +} + +function _transform() { + const data = require('@jest/transform'); + + _transform = function () { + return data; + }; + + return data; +} + +function _jestConfig() { + const data = require('jest-config'); + + _jestConfig = function () { + return data; + }; + + return data; +} + +function _jestRuntime() { + const data = _interopRequireDefault(require('jest-runtime')); + + _jestRuntime = 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 args = _interopRequireWildcard(require('./args')); + +var _version = require('./version'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +async function run(cliArgv, cliInfo) { + let argv; + + if (cliArgv) { + argv = cliArgv; + } else { + argv = _yargs() + .default.usage(args.usage) + .help(false) + .version(false) + .options(args.options).argv; + (0, _jestValidate().validateCLIOptions)(argv, { + ...args.options, + deprecationEntries: _jestConfig().deprecationEntries + }); + } + + if (argv.help) { + _yargs().default.showHelp(); + + process.on('exit', () => (process.exitCode = 1)); + return; + } + + if (argv.version) { + console.log(`v${_version.VERSION}\n`); + return; + } + + if (!argv._.length) { + console.log('Please provide a path to a script. (See --help for details)'); + process.on('exit', () => (process.exitCode = 1)); + return; + } + + const root = (0, _jestUtil().tryRealpath)(process.cwd()); + const filePath = path().resolve(root, argv._[0].toString()); + + if (argv.debug) { + const info = cliInfo ? ', ' + cliInfo.join(', ') : ''; + console.log(`Using Jest Runtime v${_version.VERSION}${info}`); + } + + const options = await (0, _jestConfig().readConfig)(argv, root); + const globalConfig = options.globalConfig; // Always disable automocking in scripts. + + const config = {...options.projectConfig, automock: false}; + + try { + const hasteMap = await _jestRuntime().default.createContext(config, { + maxWorkers: Math.max((0, _os().cpus)().length - 1, 1), + watchman: globalConfig.watchman + }); + const transformer = new (_transform().ScriptTransformer)(config); + const Environment = (0, _jestUtil().interopRequireDefault)( + transformer.requireAndTranspileModule(config.testEnvironment) + ).default; + const environment = new Environment(config); + (0, _jestUtil().setGlobal)( + environment.global, + 'console', + new (_console().CustomConsole)(process.stdout, process.stderr) + ); + (0, _jestUtil().setGlobal)(environment.global, 'jestProjectConfig', config); + (0, _jestUtil().setGlobal)( + environment.global, + 'jestGlobalConfig', + globalConfig + ); + const runtime = new (_jestRuntime().default)( + config, + environment, + hasteMap.resolver, + new Map(), + { + changedFiles: undefined, + collectCoverage: false, + collectCoverageFrom: [], + collectCoverageOnlyFrom: undefined, + coverageProvider: 'v8', + sourcesRelatedToTestsInChangedFiles: undefined + }, + filePath + ); + + for (const path of config.setupFiles) { + const esm = runtime.unstable_shouldLoadAsEsm(path); + + if (esm) { + await runtime.unstable_importModule(path); + } else { + runtime.requireModule(path); + } + } + + const esm = runtime.unstable_shouldLoadAsEsm(filePath); + + if (esm) { + await runtime.unstable_importModule(filePath); + } else { + runtime.requireModule(filePath); + } + } catch (e) { + console.error(_chalk().default.red(e.stack || e)); + process.on('exit', () => (process.exitCode = 1)); + } +} diff --git a/packages/jest-repl/build/cli/version.d.ts b/packages/jest-repl/build/cli/version.d.ts new file mode 100644 index 000000000000..f74a8838d27e --- /dev/null +++ b/packages/jest-repl/build/cli/version.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export declare const VERSION: string; diff --git a/packages/jest-repl/build/cli/version.js b/packages/jest-repl/build/cli/version.js new file mode 100644 index 000000000000..0e0f04fc485e --- /dev/null +++ b/packages/jest-repl/build/cli/version.js @@ -0,0 +1,17 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.VERSION = void 0; + +/** + * 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. + */ +// For some reason, doing `require`ing here works, while inside `cli` fails +const VERSION = require('../../package.json').version; + +exports.VERSION = VERSION; diff --git a/packages/jest-reporters/build/BaseReporter.d.ts b/packages/jest-reporters/build/BaseReporter.d.ts new file mode 100644 index 000000000000..17b36a034407 --- /dev/null +++ b/packages/jest-reporters/build/BaseReporter.d.ts @@ -0,0 +1,19 @@ +/** + * 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. + */ +import type { AggregatedResult, TestCaseResult, TestResult } from '@jest/test-result'; +import type { Context, Reporter, ReporterOnStartOptions, Test } from './types'; +export default class BaseReporter implements Reporter { + private _error?; + log(message: string): void; + onRunStart(_results?: AggregatedResult, _options?: ReporterOnStartOptions): void; + onTestCaseResult(_test: Test, _testCaseResult: TestCaseResult): void; + onTestResult(_test?: Test, _testResult?: TestResult, _results?: AggregatedResult): void; + onTestStart(_test?: Test): void; + onRunComplete(_contexts?: Set, _aggregatedResults?: AggregatedResult): Promise | void; + protected _setError(error: Error): void; + getLastError(): Error | undefined; +} diff --git a/packages/jest-reporters/build/BaseReporter.js b/packages/jest-reporters/build/BaseReporter.js new file mode 100644 index 000000000000..d457307af968 --- /dev/null +++ b/packages/jest-reporters/build/BaseReporter.js @@ -0,0 +1,65 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const {remove: preRunMessageRemove} = _jestUtil().preRunMessage; + +class BaseReporter { + constructor() { + _defineProperty(this, '_error', void 0); + } + + log(message) { + process.stderr.write(message + '\n'); + } + + onRunStart(_results, _options) { + preRunMessageRemove(process.stderr); + } + + onTestCaseResult(_test, _testCaseResult) {} + + onTestResult(_test, _testResult, _results) {} + + onTestStart(_test) {} + + onRunComplete(_contexts, _aggregatedResults) {} + + _setError(error) { + this._error = error; + } // Return an error that occurred during reporting. This error will + // define whether the test run was successful or failed. + + getLastError() { + return this._error; + } +} + +exports.default = BaseReporter; diff --git a/packages/jest-reporters/build/CoverageReporter.d.ts b/packages/jest-reporters/build/CoverageReporter.d.ts new file mode 100644 index 000000000000..fd600f375240 --- /dev/null +++ b/packages/jest-reporters/build/CoverageReporter.d.ts @@ -0,0 +1,24 @@ +/** + * 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. + */ +import type { AggregatedResult, TestResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import BaseReporter from './BaseReporter'; +import type { Context, CoverageReporterOptions, Test } from './types'; +export default class CoverageReporter extends BaseReporter { + private _coverageMap; + private _globalConfig; + private _sourceMapStore; + private _options; + private _v8CoverageResults; + static readonly filename: string; + constructor(globalConfig: Config.GlobalConfig, options?: CoverageReporterOptions); + onTestResult(_test: Test, testResult: TestResult): void; + onRunComplete(contexts: Set, aggregatedResults: AggregatedResult): Promise; + private _addUntestedFiles; + private _checkThreshold; + private _getCoverageResult; +} diff --git a/packages/jest-reporters/build/CoverageReporter.js b/packages/jest-reporters/build/CoverageReporter.js new file mode 100644 index 000000000000..47bd5f36b629 --- /dev/null +++ b/packages/jest-reporters/build/CoverageReporter.js @@ -0,0 +1,665 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _v8Coverage() { + const data = require('@bcoe/v8-coverage'); + + _v8Coverage = function () { + return data; + }; + + return data; +} + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _glob() { + const data = _interopRequireDefault(require('glob')); + + _glob = function () { + return data; + }; + + return data; +} + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _istanbulLibCoverage() { + const data = _interopRequireDefault(require('istanbul-lib-coverage')); + + _istanbulLibCoverage = function () { + return data; + }; + + return data; +} + +function _istanbulLibReport() { + const data = _interopRequireDefault(require('istanbul-lib-report')); + + _istanbulLibReport = function () { + return data; + }; + + return data; +} + +function _istanbulLibSourceMaps() { + const data = _interopRequireDefault(require('istanbul-lib-source-maps')); + + _istanbulLibSourceMaps = function () { + return data; + }; + + return data; +} + +function _istanbulReports() { + const data = _interopRequireDefault(require('istanbul-reports')); + + _istanbulReports = function () { + return data; + }; + + return data; +} + +function _v8ToIstanbul() { + const data = _interopRequireDefault(require('v8-to-istanbul')); + + _v8ToIstanbul = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _jestWorker() { + const data = require('jest-worker'); + + _jestWorker = function () { + return data; + }; + + return data; +} + +var _BaseReporter = _interopRequireDefault(require('./BaseReporter')); + +var _getWatermarks = _interopRequireDefault(require('./getWatermarks')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const FAIL_COLOR = _chalk().default.bold.red; + +const RUNNING_TEST_COLOR = _chalk().default.bold.dim; + +class CoverageReporter extends _BaseReporter.default { + constructor(globalConfig, options) { + super(); + + _defineProperty(this, '_coverageMap', void 0); + + _defineProperty(this, '_globalConfig', void 0); + + _defineProperty(this, '_sourceMapStore', void 0); + + _defineProperty(this, '_options', void 0); + + _defineProperty(this, '_v8CoverageResults', void 0); + + this._coverageMap = _istanbulLibCoverage().default.createCoverageMap({}); + this._globalConfig = globalConfig; + this._sourceMapStore = _istanbulLibSourceMaps().default.createSourceMapStore(); + this._v8CoverageResults = []; + this._options = options || {}; + } + + onTestResult(_test, testResult) { + if (testResult.v8Coverage) { + this._v8CoverageResults.push(testResult.v8Coverage); + + return; + } + + if (testResult.coverage) { + this._coverageMap.merge(testResult.coverage); + } + } + + async onRunComplete(contexts, aggregatedResults) { + await this._addUntestedFiles(contexts); + const {map, reportContext} = await this._getCoverageResult(); + + try { + const coverageReporters = this._globalConfig.coverageReporters || []; + + if (!this._globalConfig.useStderr && coverageReporters.length < 1) { + coverageReporters.push('text-summary'); + } + + coverageReporters.forEach(reporter => { + let additionalOptions = {}; + + if (Array.isArray(reporter)) { + [reporter, additionalOptions] = reporter; + } + + _istanbulReports() + .default.create(reporter, { + maxCols: process.stdout.columns || Infinity, + ...additionalOptions + }) // @ts-expect-error + .execute(reportContext); + }); + aggregatedResults.coverageMap = map; + } catch (e) { + console.error( + _chalk().default.red(` + Failed to write coverage reports: + ERROR: ${e.toString()} + STACK: ${e.stack} + `) + ); + } + + this._checkThreshold(map); + } + + async _addUntestedFiles(contexts) { + const files = []; + contexts.forEach(context => { + const config = context.config; + + if ( + this._globalConfig.collectCoverageFrom && + this._globalConfig.collectCoverageFrom.length + ) { + context.hasteFS + .matchFilesWithGlob( + this._globalConfig.collectCoverageFrom, + config.rootDir + ) + .forEach(filePath => + files.push({ + config, + path: filePath + }) + ); + } + }); + + if (!files.length) { + return; + } + + if (_jestUtil().isInteractive) { + process.stderr.write( + RUNNING_TEST_COLOR('Running coverage on untested files...') + ); + } + + let worker; + + if (this._globalConfig.maxWorkers <= 1) { + worker = require('./CoverageWorker'); + } else { + worker = new (_jestWorker().Worker)(require.resolve('./CoverageWorker'), { + exposedMethods: ['worker'], + maxRetries: 2, + numWorkers: this._globalConfig.maxWorkers + }); + } + + const instrumentation = files.map(async fileObj => { + const filename = fileObj.path; + const config = fileObj.config; + + const hasCoverageData = this._v8CoverageResults.some(v8Res => + v8Res.some(innerRes => innerRes.result.url === filename) + ); + + if ( + !hasCoverageData && + !this._coverageMap.data[filename] && + 'worker' in worker + ) { + try { + const result = await worker.worker({ + config, + globalConfig: this._globalConfig, + options: { + ...this._options, + changedFiles: + this._options.changedFiles && + Array.from(this._options.changedFiles), + sourcesRelatedToTestsInChangedFiles: + this._options.sourcesRelatedToTestsInChangedFiles && + Array.from(this._options.sourcesRelatedToTestsInChangedFiles) + }, + path: filename + }); + + if (result) { + if (result.kind === 'V8Coverage') { + this._v8CoverageResults.push([ + { + codeTransformResult: undefined, + result: result.result + } + ]); + } else { + this._coverageMap.addFileCoverage(result.coverage); + } + } + } catch (error) { + console.error( + _chalk().default.red( + [ + `Failed to collect coverage from ${filename}`, + `ERROR: ${error.message}`, + `STACK: ${error.stack}` + ].join('\n') + ) + ); + } + } + }); + + try { + await Promise.all(instrumentation); + } catch { + // Do nothing; errors were reported earlier to the console. + } + + if (_jestUtil().isInteractive) { + (0, _jestUtil().clearLine)(process.stderr); + } + + if (worker && 'end' in worker && typeof worker.end === 'function') { + await worker.end(); + } + } + + _checkThreshold(map) { + const {coverageThreshold} = this._globalConfig; + + if (coverageThreshold) { + function check(name, thresholds, actuals) { + return ['statements', 'branches', 'lines', 'functions'].reduce( + (errors, key) => { + const actual = actuals[key].pct; + const actualUncovered = actuals[key].total - actuals[key].covered; + const threshold = thresholds[key]; + + if (threshold !== undefined) { + if (threshold < 0) { + if (threshold * -1 < actualUncovered) { + errors.push( + `Jest: Uncovered count for ${key} (${actualUncovered}) ` + + `exceeds ${name} threshold (${-1 * threshold})` + ); + } + } else if (actual < threshold) { + errors.push( + `Jest: "${name}" coverage threshold for ${key} (${threshold}%) not met: ${actual}%` + ); + } + } + + return errors; + }, + [] + ); + } + + const THRESHOLD_GROUP_TYPES = { + GLOB: 'glob', + GLOBAL: 'global', + PATH: 'path' + }; + const coveredFiles = map.files(); + const thresholdGroups = Object.keys(coverageThreshold); + const groupTypeByThresholdGroup = {}; + const filesByGlob = {}; + const coveredFilesSortedIntoThresholdGroup = coveredFiles.reduce( + (files, file) => { + const pathOrGlobMatches = thresholdGroups.reduce( + (agg, thresholdGroup) => { + const absoluteThresholdGroup = path().resolve(thresholdGroup); // The threshold group might be a path: + + if (file.indexOf(absoluteThresholdGroup) === 0) { + groupTypeByThresholdGroup[thresholdGroup] = + THRESHOLD_GROUP_TYPES.PATH; + return agg.concat([[file, thresholdGroup]]); + } // If the threshold group is not a path it might be a glob: + // Note: glob.sync is slow. By memoizing the files matching each glob + // (rather than recalculating it for each covered file) we save a tonne + // of execution time. + + if (filesByGlob[absoluteThresholdGroup] === undefined) { + filesByGlob[absoluteThresholdGroup] = _glob() + .default.sync(absoluteThresholdGroup) + .map(filePath => path().resolve(filePath)); + } + + if (filesByGlob[absoluteThresholdGroup].indexOf(file) > -1) { + groupTypeByThresholdGroup[thresholdGroup] = + THRESHOLD_GROUP_TYPES.GLOB; + return agg.concat([[file, thresholdGroup]]); + } + + return agg; + }, + [] + ); + + if (pathOrGlobMatches.length > 0) { + return files.concat(pathOrGlobMatches); + } // Neither a glob or a path? Toss it in global if there's a global threshold: + + if (thresholdGroups.indexOf(THRESHOLD_GROUP_TYPES.GLOBAL) > -1) { + groupTypeByThresholdGroup[THRESHOLD_GROUP_TYPES.GLOBAL] = + THRESHOLD_GROUP_TYPES.GLOBAL; + return files.concat([[file, THRESHOLD_GROUP_TYPES.GLOBAL]]); + } // A covered file that doesn't have a threshold: + + return files.concat([[file, undefined]]); + }, + [] + ); + + const getFilesInThresholdGroup = thresholdGroup => + coveredFilesSortedIntoThresholdGroup + .filter(fileAndGroup => fileAndGroup[1] === thresholdGroup) + .map(fileAndGroup => fileAndGroup[0]); + + function combineCoverage(filePaths) { + return filePaths + .map(filePath => map.fileCoverageFor(filePath)) + .reduce((combinedCoverage, nextFileCoverage) => { + if (combinedCoverage === undefined || combinedCoverage === null) { + return nextFileCoverage.toSummary(); + } + + return combinedCoverage.merge(nextFileCoverage.toSummary()); + }, undefined); + } + + let errors = []; + thresholdGroups.forEach(thresholdGroup => { + switch (groupTypeByThresholdGroup[thresholdGroup]) { + case THRESHOLD_GROUP_TYPES.GLOBAL: { + const coverage = combineCoverage( + getFilesInThresholdGroup(THRESHOLD_GROUP_TYPES.GLOBAL) + ); + + if (coverage) { + errors = errors.concat( + check( + thresholdGroup, + coverageThreshold[thresholdGroup], + coverage + ) + ); + } + + break; + } + + case THRESHOLD_GROUP_TYPES.PATH: { + const coverage = combineCoverage( + getFilesInThresholdGroup(thresholdGroup) + ); + + if (coverage) { + errors = errors.concat( + check( + thresholdGroup, + coverageThreshold[thresholdGroup], + coverage + ) + ); + } + + break; + } + + case THRESHOLD_GROUP_TYPES.GLOB: + getFilesInThresholdGroup(thresholdGroup).forEach( + fileMatchingGlob => { + errors = errors.concat( + check( + fileMatchingGlob, + coverageThreshold[thresholdGroup], + map.fileCoverageFor(fileMatchingGlob).toSummary() + ) + ); + } + ); + break; + + default: + // If the file specified by path is not found, error is returned. + if (thresholdGroup !== THRESHOLD_GROUP_TYPES.GLOBAL) { + errors = errors.concat( + `Jest: Coverage data for ${thresholdGroup} was not found.` + ); + } + + // Sometimes all files in the coverage data are matched by + // PATH and GLOB threshold groups in which case, don't error when + // the global threshold group doesn't match any files. + } + }); + errors = errors.filter( + err => err !== undefined && err !== null && err.length > 0 + ); + + if (errors.length > 0) { + this.log(`${FAIL_COLOR(errors.join('\n'))}`); + + this._setError(new Error(errors.join('\n'))); + } + } + } + + async _getCoverageResult() { + if (this._globalConfig.coverageProvider === 'v8') { + const mergedCoverages = (0, _v8Coverage().mergeProcessCovs)( + this._v8CoverageResults.map(cov => ({ + result: cov.map(r => r.result) + })) + ); + const fileTransforms = new Map(); + + this._v8CoverageResults.forEach(res => + res.forEach(r => { + if (r.codeTransformResult && !fileTransforms.has(r.result.url)) { + fileTransforms.set(r.result.url, r.codeTransformResult); + } + }) + ); + + const transformedCoverage = await Promise.all( + mergedCoverages.result.map(async res => { + var _fileTransform$wrappe; + + const fileTransform = fileTransforms.get(res.url); + let sourcemapContent = undefined; + + if ( + fileTransform !== null && + fileTransform !== void 0 && + fileTransform.sourceMapPath && + fs().existsSync(fileTransform.sourceMapPath) + ) { + sourcemapContent = JSON.parse( + fs().readFileSync(fileTransform.sourceMapPath, 'utf8') + ); + } + + const converter = (0, _v8ToIstanbul().default)( + res.url, + (_fileTransform$wrappe = + fileTransform === null || fileTransform === void 0 + ? void 0 + : fileTransform.wrapperLength) !== null && + _fileTransform$wrappe !== void 0 + ? _fileTransform$wrappe + : 0, + fileTransform && sourcemapContent + ? { + originalSource: fileTransform.originalCode, + source: fileTransform.code, + sourceMap: { + sourcemap: { + file: res.url, + ...sourcemapContent + } + } + } + : { + source: fs().readFileSync(res.url, 'utf8') + } + ); + await converter.load(); + converter.applyCoverage(res.functions); + return converter.toIstanbul(); + }) + ); + + const map = _istanbulLibCoverage().default.createCoverageMap({}); + + transformedCoverage.forEach(res => map.merge(res)); + + const reportContext = _istanbulLibReport().default.createContext({ + coverageMap: map, + dir: this._globalConfig.coverageDirectory, + watermarks: (0, _getWatermarks.default)(this._globalConfig) + }); + + return { + map, + reportContext + }; + } + + const map = await this._sourceMapStore.transformCoverage(this._coverageMap); + + const reportContext = _istanbulLibReport().default.createContext({ + coverageMap: map, + dir: this._globalConfig.coverageDirectory, + sourceFinder: this._sourceMapStore.sourceFinder, + watermarks: (0, _getWatermarks.default)(this._globalConfig) + }); + + return { + map, + reportContext + }; + } +} + +exports.default = CoverageReporter; + +_defineProperty(CoverageReporter, 'filename', __filename); diff --git a/packages/jest-reporters/build/CoverageWorker.d.ts b/packages/jest-reporters/build/CoverageWorker.d.ts new file mode 100644 index 000000000000..cad8b2579101 --- /dev/null +++ b/packages/jest-reporters/build/CoverageWorker.d.ts @@ -0,0 +1,17 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import { CoverageWorkerResult } from './generateEmptyCoverage'; +import type { CoverageReporterSerializedOptions } from './types'; +export declare type CoverageWorkerData = { + globalConfig: Config.GlobalConfig; + config: Config.ProjectConfig; + path: Config.Path; + options?: CoverageReporterSerializedOptions; +}; +export type { CoverageWorkerResult }; +export declare function worker({ config, globalConfig, path, options, }: CoverageWorkerData): CoverageWorkerResult | null; diff --git a/packages/jest-reporters/build/CoverageWorker.js b/packages/jest-reporters/build/CoverageWorker.js new file mode 100644 index 000000000000..15399731ef95 --- /dev/null +++ b/packages/jest-reporters/build/CoverageWorker.js @@ -0,0 +1,103 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.worker = worker; + +function _exit() { + const data = _interopRequireDefault(require('exit')); + + _exit = function () { + return data; + }; + + return data; +} + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +var _generateEmptyCoverage = _interopRequireDefault( + require('./generateEmptyCoverage') +); + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +// Make sure uncaught errors are logged before we exit. +process.on('uncaughtException', err => { + console.error(err.stack); + (0, _exit().default)(1); +}); + +function worker({config, globalConfig, path, options}) { + return (0, _generateEmptyCoverage.default)( + fs().readFileSync(path, 'utf8'), + path, + globalConfig, + config, + (options === null || options === void 0 ? void 0 : options.changedFiles) && + new Set(options.changedFiles), + (options === null || options === void 0 + ? void 0 + : options.sourcesRelatedToTestsInChangedFiles) && + new Set(options.sourcesRelatedToTestsInChangedFiles) + ); +} diff --git a/packages/jest-reporters/build/DefaultReporter.d.ts b/packages/jest-reporters/build/DefaultReporter.d.ts new file mode 100644 index 000000000000..6680f3925883 --- /dev/null +++ b/packages/jest-reporters/build/DefaultReporter.d.ts @@ -0,0 +1,32 @@ +/** + * 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. + */ +import type { AggregatedResult, TestCaseResult, TestResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import BaseReporter from './BaseReporter'; +import type { ReporterOnStartOptions, Test } from './types'; +export default class DefaultReporter extends BaseReporter { + private _clear; + private _err; + protected _globalConfig: Config.GlobalConfig; + private _out; + private _status; + private _bufferedOutput; + static readonly filename: string; + constructor(globalConfig: Config.GlobalConfig); + private _wrapStdio; + forceFlushBufferedOutput(): void; + private _clearStatus; + private _printStatus; + onRunStart(aggregatedResults: AggregatedResult, options: ReporterOnStartOptions): void; + onTestStart(test: Test): void; + onTestCaseResult(test: Test, testCaseResult: TestCaseResult): void; + onRunComplete(): void; + onTestResult(test: Test, testResult: TestResult, aggregatedResults: AggregatedResult): void; + testFinished(config: Config.ProjectConfig, testResult: TestResult, aggregatedResults: AggregatedResult): void; + printTestFileHeader(_testPath: Config.Path, config: Config.ProjectConfig, result: TestResult): void; + printTestFileFailureMessage(_testPath: Config.Path, _config: Config.ProjectConfig, result: TestResult): void; +} diff --git a/packages/jest-reporters/build/DefaultReporter.js b/packages/jest-reporters/build/DefaultReporter.js new file mode 100644 index 000000000000..d11b91abcadd --- /dev/null +++ b/packages/jest-reporters/build/DefaultReporter.js @@ -0,0 +1,254 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _console() { + const data = require('@jest/console'); + + _console = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _BaseReporter = _interopRequireDefault(require('./BaseReporter')); + +var _Status = _interopRequireDefault(require('./Status')); + +var _getResultHeader = _interopRequireDefault(require('./getResultHeader')); + +var _getSnapshotStatus = _interopRequireDefault(require('./getSnapshotStatus')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const TITLE_BULLET = _chalk().default.bold('\u25cf '); + +class DefaultReporter extends _BaseReporter.default { + // ANSI clear sequence for the last printed status + constructor(globalConfig) { + super(); + + _defineProperty(this, '_clear', void 0); + + _defineProperty(this, '_err', void 0); + + _defineProperty(this, '_globalConfig', void 0); + + _defineProperty(this, '_out', void 0); + + _defineProperty(this, '_status', void 0); + + _defineProperty(this, '_bufferedOutput', void 0); + + this._globalConfig = globalConfig; + this._clear = ''; + this._out = process.stdout.write.bind(process.stdout); + this._err = process.stderr.write.bind(process.stderr); + this._status = new _Status.default(); + this._bufferedOutput = new Set(); + + this._wrapStdio(process.stdout); + + this._wrapStdio(process.stderr); + + this._status.onChange(() => { + this._clearStatus(); + + this._printStatus(); + }); + } + + _wrapStdio(stream) { + const originalWrite = stream.write; + let buffer = []; + let timeout = null; + + const flushBufferedOutput = () => { + const string = buffer.join(''); + buffer = []; // This is to avoid conflicts between random output and status text + + this._clearStatus(); + + if (string) { + originalWrite.call(stream, string); + } + + this._printStatus(); + + this._bufferedOutput.delete(flushBufferedOutput); + }; + + this._bufferedOutput.add(flushBufferedOutput); + + const debouncedFlush = () => { + // If the process blows up no errors would be printed. + // There should be a smart way to buffer stderr, but for now + // we just won't buffer it. + if (stream === process.stderr) { + flushBufferedOutput(); + } else { + if (!timeout) { + timeout = setTimeout(() => { + flushBufferedOutput(); + timeout = null; + }, 100); + } + } + }; + + stream.write = chunk => { + buffer.push(chunk); + debouncedFlush(); + return true; + }; + } // Don't wait for the debounced call and flush all output immediately. + + forceFlushBufferedOutput() { + for (const flushBufferedOutput of this._bufferedOutput) { + flushBufferedOutput(); + } + } + + _clearStatus() { + if (_jestUtil().isInteractive) { + if (this._globalConfig.useStderr) { + this._err(this._clear); + } else { + this._out(this._clear); + } + } + } + + _printStatus() { + const {content, clear} = this._status.get(); + + this._clear = clear; + + if (_jestUtil().isInteractive) { + if (this._globalConfig.useStderr) { + this._err(content); + } else { + this._out(content); + } + } + } + + onRunStart(aggregatedResults, options) { + this._status.runStarted(aggregatedResults, options); + } + + onTestStart(test) { + this._status.testStarted(test.path, test.context.config); + } + + onTestCaseResult(test, testCaseResult) { + this._status.addTestCaseResult(test, testCaseResult); + } + + onRunComplete() { + this.forceFlushBufferedOutput(); + + this._status.runFinished(); + + process.stdout.write = this._out; + process.stderr.write = this._err; + (0, _jestUtil().clearLine)(process.stderr); + } + + onTestResult(test, testResult, aggregatedResults) { + this.testFinished(test.context.config, testResult, aggregatedResults); + + if (!testResult.skipped) { + this.printTestFileHeader( + testResult.testFilePath, + test.context.config, + testResult + ); + this.printTestFileFailureMessage( + testResult.testFilePath, + test.context.config, + testResult + ); + } + + this.forceFlushBufferedOutput(); + } + + testFinished(config, testResult, aggregatedResults) { + this._status.testFinished(config, testResult, aggregatedResults); + } + + printTestFileHeader(_testPath, config, result) { + this.log((0, _getResultHeader.default)(result, this._globalConfig, config)); + + if (result.console) { + this.log( + ' ' + + TITLE_BULLET + + 'Console\n\n' + + (0, _console().getConsoleOutput)( + result.console, + config, + this._globalConfig + ) + ); + } + } + + printTestFileFailureMessage(_testPath, _config, result) { + if (result.failureMessage) { + this.log(result.failureMessage); + } + + const didUpdate = this._globalConfig.updateSnapshot === 'all'; + const snapshotStatuses = (0, _getSnapshotStatus.default)( + result.snapshot, + didUpdate + ); + snapshotStatuses.forEach(this.log); + } +} + +exports.default = DefaultReporter; + +_defineProperty(DefaultReporter, 'filename', __filename); diff --git a/packages/jest-reporters/build/NotifyReporter.d.ts b/packages/jest-reporters/build/NotifyReporter.d.ts new file mode 100644 index 000000000000..eaa0f7fc25b0 --- /dev/null +++ b/packages/jest-reporters/build/NotifyReporter.d.ts @@ -0,0 +1,19 @@ +/** + * 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. + */ +import type { AggregatedResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import BaseReporter from './BaseReporter'; +import type { Context, TestSchedulerContext } from './types'; +export default class NotifyReporter extends BaseReporter { + private _notifier; + private _startRun; + private _globalConfig; + private _context; + static readonly filename: string; + constructor(globalConfig: Config.GlobalConfig, startRun: (globalConfig: Config.GlobalConfig) => unknown, context: TestSchedulerContext); + onRunComplete(contexts: Set, result: AggregatedResult): void; +} diff --git a/packages/jest-reporters/build/NotifyReporter.js b/packages/jest-reporters/build/NotifyReporter.js new file mode 100644 index 000000000000..1d463e0af8f5 --- /dev/null +++ b/packages/jest-reporters/build/NotifyReporter.js @@ -0,0 +1,256 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function util() { + const data = _interopRequireWildcard(require('util')); + + util = function () { + return data; + }; + + return data; +} + +function _exit() { + const data = _interopRequireDefault(require('exit')); + + _exit = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _BaseReporter = _interopRequireDefault(require('./BaseReporter')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const isDarwin = process.platform === 'darwin'; +const icon = path().resolve(__dirname, '../assets/jest_logo.png'); + +class NotifyReporter extends _BaseReporter.default { + constructor(globalConfig, startRun, context) { + super(); + + _defineProperty(this, '_notifier', loadNotifier()); + + _defineProperty(this, '_startRun', void 0); + + _defineProperty(this, '_globalConfig', void 0); + + _defineProperty(this, '_context', void 0); + + this._globalConfig = globalConfig; + this._startRun = startRun; + this._context = context; + } + + onRunComplete(contexts, result) { + const success = + result.numFailedTests === 0 && result.numRuntimeErrorTestSuites === 0; + const firstContext = contexts.values().next(); + const hasteFS = + firstContext && firstContext.value && firstContext.value.hasteFS; + let packageName; + + if (hasteFS != null) { + // assuming root package.json is the first one + const [filePath] = hasteFS.matchFiles('package.json'); + packageName = + filePath != null + ? hasteFS.getModuleName(filePath) + : this._globalConfig.rootDir; + } else { + packageName = this._globalConfig.rootDir; + } + + packageName = packageName != null ? `${packageName} - ` : ''; + const notifyMode = this._globalConfig.notifyMode; + const statusChanged = + this._context.previousSuccess !== success || this._context.firstRun; + const testsHaveRun = result.numTotalTests !== 0; + + if ( + testsHaveRun && + success && + (notifyMode === 'always' || + notifyMode === 'success' || + notifyMode === 'success-change' || + (notifyMode === 'change' && statusChanged) || + (notifyMode === 'failure-change' && statusChanged)) + ) { + const title = util().format('%s%d%% Passed', packageName, 100); + const message = `${isDarwin ? '\u2705 ' : ''}${(0, _jestUtil().pluralize)( + 'test', + result.numPassedTests + )} passed`; + + this._notifier.notify({ + icon, + message, + timeout: false, + title + }); + } else if ( + testsHaveRun && + !success && + (notifyMode === 'always' || + notifyMode === 'failure' || + notifyMode === 'failure-change' || + (notifyMode === 'change' && statusChanged) || + (notifyMode === 'success-change' && statusChanged)) + ) { + const failed = result.numFailedTests / result.numTotalTests; + const title = util().format( + '%s%d%% Failed', + packageName, + Math.ceil(Number.isNaN(failed) ? 0 : failed * 100) + ); + const message = util().format( + (isDarwin ? '\u26D4\uFE0F ' : '') + '%d of %d tests failed', + result.numFailedTests, + result.numTotalTests + ); + const watchMode = this._globalConfig.watch || this._globalConfig.watchAll; + const restartAnswer = 'Run again'; + const quitAnswer = 'Exit tests'; + + if (!watchMode) { + this._notifier.notify({ + icon, + message, + timeout: false, + title + }); + } else { + this._notifier.notify( + { + actions: [restartAnswer, quitAnswer], + closeLabel: 'Close', + icon, + message, + timeout: false, + title + }, + (err, _, metadata) => { + if (err || !metadata) { + return; + } + + if (metadata.activationValue === quitAnswer) { + (0, _exit().default)(0); + return; + } + + if (metadata.activationValue === restartAnswer) { + this._startRun(this._globalConfig); + } + } + ); + } + } + + this._context.previousSuccess = success; + this._context.firstRun = false; + } +} + +exports.default = NotifyReporter; + +_defineProperty(NotifyReporter, 'filename', __filename); + +function loadNotifier() { + try { + return require('node-notifier'); + } catch (err) { + if (err.code !== 'MODULE_NOT_FOUND') { + throw err; + } + + throw Error( + 'notify reporter requires optional peer dependency "node-notifier" but it was not found' + ); + } +} diff --git a/packages/jest-reporters/build/Status.d.ts b/packages/jest-reporters/build/Status.d.ts new file mode 100644 index 000000000000..36bb7d9478a5 --- /dev/null +++ b/packages/jest-reporters/build/Status.d.ts @@ -0,0 +1,42 @@ +/** + * 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. + */ +import type { AggregatedResult, TestCaseResult, TestResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import type { ReporterOnStartOptions, Test } from './types'; +declare type Cache = { + content: string; + clear: string; +}; +/** + * A class that generates the CLI status of currently running tests + * and also provides an ANSI escape sequence to remove status lines + * from the terminal. + */ +export default class Status { + private _cache; + private _callback?; + private _currentTests; + private _currentTestCases; + private _done; + private _emitScheduled; + private _estimatedTime; + private _interval?; + private _aggregatedResults?; + private _showStatus; + constructor(); + onChange(callback: () => void): void; + runStarted(aggregatedResults: AggregatedResult, options: ReporterOnStartOptions): void; + runFinished(): void; + addTestCaseResult(test: Test, testCaseResult: TestCaseResult): void; + testStarted(testPath: Config.Path, config: Config.ProjectConfig): void; + testFinished(_config: Config.ProjectConfig, testResult: TestResult, aggregatedResults: AggregatedResult): void; + get(): Cache; + private _emit; + private _debouncedEmit; + private _tick; +} +export {}; diff --git a/packages/jest-reporters/build/Status.js b/packages/jest-reporters/build/Status.js new file mode 100644 index 000000000000..72f4fc6acc02 --- /dev/null +++ b/packages/jest-reporters/build/Status.js @@ -0,0 +1,272 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _stringLength() { + const data = _interopRequireDefault(require('string-length')); + + _stringLength = function () { + return data; + }; + + return data; +} + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const RUNNING_TEXT = ' RUNS '; +const RUNNING = _chalk().default.reset.inverse.yellow.bold(RUNNING_TEXT) + ' '; +/** + * This class is a perf optimization for sorting the list of currently + * running tests. It tries to keep tests in the same positions without + * shifting the whole list. + */ + +class CurrentTestList { + constructor() { + _defineProperty(this, '_array', void 0); + + this._array = []; + } + + add(testPath, config) { + const index = this._array.indexOf(null); + + const record = { + config, + testPath + }; + + if (index !== -1) { + this._array[index] = record; + } else { + this._array.push(record); + } + } + + delete(testPath) { + const record = this._array.find( + record => record !== null && record.testPath === testPath + ); + + this._array[this._array.indexOf(record || null)] = null; + } + + get() { + return this._array; + } +} + +/** + * A class that generates the CLI status of currently running tests + * and also provides an ANSI escape sequence to remove status lines + * from the terminal. + */ +class Status { + constructor() { + _defineProperty(this, '_cache', void 0); + + _defineProperty(this, '_callback', void 0); + + _defineProperty(this, '_currentTests', void 0); + + _defineProperty(this, '_currentTestCases', void 0); + + _defineProperty(this, '_done', void 0); + + _defineProperty(this, '_emitScheduled', void 0); + + _defineProperty(this, '_estimatedTime', void 0); + + _defineProperty(this, '_interval', void 0); + + _defineProperty(this, '_aggregatedResults', void 0); + + _defineProperty(this, '_showStatus', void 0); + + this._cache = null; + this._currentTests = new CurrentTestList(); + this._currentTestCases = []; + this._done = false; + this._emitScheduled = false; + this._estimatedTime = 0; + this._showStatus = false; + } + + onChange(callback) { + this._callback = callback; + } + + runStarted(aggregatedResults, options) { + this._estimatedTime = (options && options.estimatedTime) || 0; + this._showStatus = options && options.showStatus; + this._interval = setInterval(() => this._tick(), 1000); + this._aggregatedResults = aggregatedResults; + + this._debouncedEmit(); + } + + runFinished() { + this._done = true; + if (this._interval) clearInterval(this._interval); + + this._emit(); + } + + addTestCaseResult(test, testCaseResult) { + this._currentTestCases.push({ + test, + testCaseResult + }); + + if (!this._showStatus) { + this._emit(); + } else { + this._debouncedEmit(); + } + } + + testStarted(testPath, config) { + this._currentTests.add(testPath, config); + + if (!this._showStatus) { + this._emit(); + } else { + this._debouncedEmit(); + } + } + + testFinished(_config, testResult, aggregatedResults) { + const {testFilePath} = testResult; + this._aggregatedResults = aggregatedResults; + + this._currentTests.delete(testFilePath); + + this._currentTestCases = this._currentTestCases.filter(({test}) => { + if (_config !== test.context.config) { + return true; + } + + return test.path !== testFilePath; + }); + + this._debouncedEmit(); + } + + get() { + if (this._cache) { + return this._cache; + } + + if (this._done) { + return { + clear: '', + content: '' + }; + } + + const width = process.stdout.columns; + let content = '\n'; + + this._currentTests.get().forEach(record => { + if (record) { + const {config, testPath} = record; + const projectDisplayName = config.displayName + ? (0, _utils.printDisplayName)(config) + ' ' + : ''; + const prefix = RUNNING + projectDisplayName; + content += + (0, _utils.wrapAnsiString)( + prefix + + (0, _utils.trimAndFormatPath)( + (0, _stringLength().default)(prefix), + config, + testPath, + width + ), + width + ) + '\n'; + } + }); + + if (this._showStatus && this._aggregatedResults) { + content += + '\n' + + (0, _utils.getSummary)(this._aggregatedResults, { + currentTestCases: this._currentTestCases, + estimatedTime: this._estimatedTime, + roundTime: true, + width + }); + } + + let height = 0; + + for (let i = 0; i < content.length; i++) { + if (content[i] === '\n') { + height++; + } + } + + const clear = '\r\x1B[K\r\x1B[1A'.repeat(height); + return (this._cache = { + clear, + content + }); + } + + _emit() { + this._cache = null; + if (this._callback) this._callback(); + } + + _debouncedEmit() { + if (!this._emitScheduled) { + // Perf optimization to avoid two separate renders When + // one test finishes and another test starts executing. + this._emitScheduled = true; + setTimeout(() => { + this._emit(); + + this._emitScheduled = false; + }, 100); + } + } + + _tick() { + this._debouncedEmit(); + } +} + +exports.default = Status; diff --git a/packages/jest-reporters/build/SummaryReporter.d.ts b/packages/jest-reporters/build/SummaryReporter.d.ts new file mode 100644 index 000000000000..3d7570d495c2 --- /dev/null +++ b/packages/jest-reporters/build/SummaryReporter.d.ts @@ -0,0 +1,22 @@ +/** + * 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. + */ +import type { AggregatedResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import BaseReporter from './BaseReporter'; +import type { Context, ReporterOnStartOptions } from './types'; +export default class SummaryReporter extends BaseReporter { + private _estimatedTime; + private _globalConfig; + static readonly filename: string; + constructor(globalConfig: Config.GlobalConfig); + private _write; + onRunStart(aggregatedResults: AggregatedResult, options: ReporterOnStartOptions): void; + onRunComplete(contexts: Set, aggregatedResults: AggregatedResult): void; + private _printSnapshotSummary; + private _printSummary; + private _getTestSummary; +} diff --git a/packages/jest-reporters/build/SummaryReporter.js b/packages/jest-reporters/build/SummaryReporter.js new file mode 100644 index 000000000000..87aac951f6ef --- /dev/null +++ b/packages/jest-reporters/build/SummaryReporter.js @@ -0,0 +1,274 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _BaseReporter = _interopRequireDefault(require('./BaseReporter')); + +var _getResultHeader = _interopRequireDefault(require('./getResultHeader')); + +var _getSnapshotSummary = _interopRequireDefault( + require('./getSnapshotSummary') +); + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const TEST_SUMMARY_THRESHOLD = 20; +const NPM_EVENTS = new Set([ + 'prepublish', + 'publish', + 'postpublish', + 'preinstall', + 'install', + 'postinstall', + 'preuninstall', + 'uninstall', + 'postuninstall', + 'preversion', + 'version', + 'postversion', + 'pretest', + 'test', + 'posttest', + 'prestop', + 'stop', + 'poststop', + 'prestart', + 'start', + 'poststart', + 'prerestart', + 'restart', + 'postrestart' +]); +const { + npm_config_user_agent, + npm_lifecycle_event, + npm_lifecycle_script +} = process.env; + +class SummaryReporter extends _BaseReporter.default { + constructor(globalConfig) { + super(); + + _defineProperty(this, '_estimatedTime', void 0); + + _defineProperty(this, '_globalConfig', void 0); + + this._globalConfig = globalConfig; + this._estimatedTime = 0; + } // If we write more than one character at a time it is possible that + // Node.js exits in the middle of printing the result. This was first observed + // in Node.js 0.10 and still persists in Node.js 6.7+. + // Let's print the test failure summary character by character which is safer + // when hundreds of tests are failing. + + _write(string) { + for (let i = 0; i < string.length; i++) { + process.stderr.write(string.charAt(i)); + } + } + + onRunStart(aggregatedResults, options) { + super.onRunStart(aggregatedResults, options); + this._estimatedTime = options.estimatedTime; + } + + onRunComplete(contexts, aggregatedResults) { + const {numTotalTestSuites, testResults, wasInterrupted} = aggregatedResults; + + if (numTotalTestSuites) { + const lastResult = testResults[testResults.length - 1]; // Print a newline if the last test did not fail to line up newlines + // similar to when an error would have been thrown in the test. + + if ( + !this._globalConfig.verbose && + lastResult && + !lastResult.numFailingTests && + !lastResult.testExecError + ) { + this.log(''); + } + + this._printSummary(aggregatedResults, this._globalConfig); + + this._printSnapshotSummary( + aggregatedResults.snapshot, + this._globalConfig + ); + + if (numTotalTestSuites) { + let message = (0, _utils.getSummary)(aggregatedResults, { + estimatedTime: this._estimatedTime + }); + + if (!this._globalConfig.silent) { + message += + '\n' + + (wasInterrupted + ? _chalk().default.bold.red('Test run was interrupted.') + : this._getTestSummary(contexts, this._globalConfig)); + } + + this.log(message); + } + } + } + + _printSnapshotSummary(snapshots, globalConfig) { + if ( + snapshots.added || + snapshots.filesRemoved || + snapshots.unchecked || + snapshots.unmatched || + snapshots.updated + ) { + let updateCommand; + const event = npm_lifecycle_event || ''; + const prefix = NPM_EVENTS.has(event) ? '' : 'run '; + const isYarn = + typeof npm_config_user_agent === 'string' && + npm_config_user_agent.includes('yarn'); + const client = isYarn ? 'yarn' : 'npm'; + const scriptUsesJest = + typeof npm_lifecycle_script === 'string' && + npm_lifecycle_script.includes('jest'); + + if (globalConfig.watch || globalConfig.watchAll) { + updateCommand = 'press `u`'; + } else if (event && scriptUsesJest) { + updateCommand = `run \`${ + client + ' ' + prefix + event + (isYarn ? '' : ' --') + } -u\``; + } else { + updateCommand = 're-run jest with `-u`'; + } + + const snapshotSummary = (0, _getSnapshotSummary.default)( + snapshots, + globalConfig, + updateCommand + ); + snapshotSummary.forEach(this.log); + this.log(''); // print empty line + } + } + + _printSummary(aggregatedResults, globalConfig) { + // If there were any failing tests and there was a large number of tests + // executed, re-print the failing results at the end of execution output. + const failedTests = aggregatedResults.numFailedTests; + const runtimeErrors = aggregatedResults.numRuntimeErrorTestSuites; + + if ( + failedTests + runtimeErrors > 0 && + aggregatedResults.numTotalTestSuites > TEST_SUMMARY_THRESHOLD + ) { + this.log(_chalk().default.bold('Summary of all failing tests')); + aggregatedResults.testResults.forEach(testResult => { + const {failureMessage} = testResult; + + if (failureMessage) { + this._write( + (0, _getResultHeader.default)(testResult, globalConfig) + + '\n' + + failureMessage + + '\n' + ); + } + }); + this.log(''); // print empty line + } + } + + _getTestSummary(contexts, globalConfig) { + const getMatchingTestsInfo = () => { + const prefix = globalConfig.findRelatedTests + ? ' related to files matching ' + : ' matching '; + return ( + _chalk().default.dim(prefix) + + (0, _jestUtil().testPathPatternToRegExp)( + globalConfig.testPathPattern + ).toString() + ); + }; + + let testInfo = ''; + + if (globalConfig.runTestsByPath) { + testInfo = _chalk().default.dim(' within paths'); + } else if (globalConfig.onlyChanged) { + testInfo = _chalk().default.dim(' related to changed files'); + } else if (globalConfig.testPathPattern) { + testInfo = getMatchingTestsInfo(); + } + + let nameInfo = ''; + + if (globalConfig.runTestsByPath) { + nameInfo = ' ' + globalConfig.nonFlagArgs.map(p => `"${p}"`).join(', '); + } else if (globalConfig.testNamePattern) { + nameInfo = + _chalk().default.dim(' with tests matching ') + + `"${globalConfig.testNamePattern}"`; + } + + const contextInfo = + contexts.size > 1 + ? _chalk().default.dim(' in ') + + contexts.size + + _chalk().default.dim(' projects') + : ''; + return ( + _chalk().default.dim('Ran all test suites') + + testInfo + + nameInfo + + contextInfo + + _chalk().default.dim('.') + ); + } +} + +exports.default = SummaryReporter; + +_defineProperty(SummaryReporter, 'filename', __filename); diff --git a/packages/jest-reporters/build/VerboseReporter.d.ts b/packages/jest-reporters/build/VerboseReporter.d.ts new file mode 100644 index 000000000000..9aa46c65634f --- /dev/null +++ b/packages/jest-reporters/build/VerboseReporter.d.ts @@ -0,0 +1,25 @@ +/** + * 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. + */ +import type { AggregatedResult, AssertionResult, Suite, TestResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import DefaultReporter from './DefaultReporter'; +import type { Test } from './types'; +export default class VerboseReporter extends DefaultReporter { + protected _globalConfig: Config.GlobalConfig; + static readonly filename: string; + constructor(globalConfig: Config.GlobalConfig); + static filterTestResults(testResults: Array): Array; + static groupTestsBySuites(testResults: Array): Suite; + onTestResult(test: Test, result: TestResult, aggregatedResults: AggregatedResult): void; + private _logTestResults; + private _logSuite; + private _getIcon; + private _logTest; + private _logTests; + private _logTodoOrPendingTest; + private _logLine; +} diff --git a/packages/jest-reporters/build/VerboseReporter.js b/packages/jest-reporters/build/VerboseReporter.js new file mode 100644 index 000000000000..65639f987e79 --- /dev/null +++ b/packages/jest-reporters/build/VerboseReporter.js @@ -0,0 +1,211 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _DefaultReporter = _interopRequireDefault(require('./DefaultReporter')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const {ICONS} = _jestUtil().specialChars; + +class VerboseReporter extends _DefaultReporter.default { + constructor(globalConfig) { + super(globalConfig); + + _defineProperty(this, '_globalConfig', void 0); + + this._globalConfig = globalConfig; + } + + static filterTestResults(testResults) { + return testResults.filter(({status}) => status !== 'pending'); + } + + static groupTestsBySuites(testResults) { + const root = { + suites: [], + tests: [], + title: '' + }; + testResults.forEach(testResult => { + let targetSuite = root; // Find the target suite for this test, + // creating nested suites as necessary. + + for (const title of testResult.ancestorTitles) { + let matchingSuite = targetSuite.suites.find(s => s.title === title); + + if (!matchingSuite) { + matchingSuite = { + suites: [], + tests: [], + title + }; + targetSuite.suites.push(matchingSuite); + } + + targetSuite = matchingSuite; + } + + targetSuite.tests.push(testResult); + }); + return root; + } + + onTestResult(test, result, aggregatedResults) { + super.testFinished(test.context.config, result, aggregatedResults); + + if (!result.skipped) { + this.printTestFileHeader( + result.testFilePath, + test.context.config, + result + ); + + if (!result.testExecError && !result.skipped) { + this._logTestResults(result.testResults); + } + + this.printTestFileFailureMessage( + result.testFilePath, + test.context.config, + result + ); + } + + super.forceFlushBufferedOutput(); + } + + _logTestResults(testResults) { + this._logSuite(VerboseReporter.groupTestsBySuites(testResults), 0); + + this._logLine(); + } + + _logSuite(suite, indentLevel) { + if (suite.title) { + this._logLine(suite.title, indentLevel); + } + + this._logTests(suite.tests, indentLevel + 1); + + suite.suites.forEach(suite => this._logSuite(suite, indentLevel + 1)); + } + + _getIcon(status) { + if (status === 'failed') { + return _chalk().default.red(ICONS.failed); + } else if (status === 'pending') { + return _chalk().default.yellow(ICONS.pending); + } else if (status === 'todo') { + return _chalk().default.magenta(ICONS.todo); + } else { + return _chalk().default.green(ICONS.success); + } + } + + _logTest(test, indentLevel) { + const status = this._getIcon(test.status); + + const time = test.duration + ? ` (${(0, _jestUtil().formatTime)(Math.round(test.duration))})` + : ''; + + this._logLine( + status + ' ' + _chalk().default.dim(test.title + time), + indentLevel + ); + } + + _logTests(tests, indentLevel) { + if (this._globalConfig.expand) { + tests.forEach(test => this._logTest(test, indentLevel)); + } else { + const summedTests = tests.reduce( + (result, test) => { + if (test.status === 'pending') { + result.pending.push(test); + } else if (test.status === 'todo') { + result.todo.push(test); + } else { + this._logTest(test, indentLevel); + } + + return result; + }, + { + pending: [], + todo: [] + } + ); + + if (summedTests.pending.length > 0) { + summedTests.pending.forEach(this._logTodoOrPendingTest(indentLevel)); + } + + if (summedTests.todo.length > 0) { + summedTests.todo.forEach(this._logTodoOrPendingTest(indentLevel)); + } + } + } + + _logTodoOrPendingTest(indentLevel) { + return test => { + const printedTestStatus = + test.status === 'pending' ? 'skipped' : test.status; + + const icon = this._getIcon(test.status); + + const text = _chalk().default.dim(`${printedTestStatus} ${test.title}`); + + this._logLine(`${icon} ${text}`, indentLevel); + }; + } + + _logLine(str, indentLevel) { + const indentation = ' '.repeat(indentLevel || 0); + this.log(indentation + (str || '')); + } +} + +exports.default = VerboseReporter; + +_defineProperty(VerboseReporter, 'filename', __filename); diff --git a/packages/jest-reporters/build/generateEmptyCoverage.d.ts b/packages/jest-reporters/build/generateEmptyCoverage.d.ts new file mode 100644 index 000000000000..0581741177c4 --- /dev/null +++ b/packages/jest-reporters/build/generateEmptyCoverage.d.ts @@ -0,0 +1,19 @@ +/** + * 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. + */ +import type { V8Coverage } from 'collect-v8-coverage'; +import { FileCoverage } from 'istanbul-lib-coverage'; +import type { Config } from '@jest/types'; +declare type SingleV8Coverage = V8Coverage[number]; +export declare type CoverageWorkerResult = { + kind: 'BabelCoverage'; + coverage: FileCoverage; +} | { + kind: 'V8Coverage'; + result: SingleV8Coverage; +}; +export default function (source: string, filename: Config.Path, globalConfig: Config.GlobalConfig, config: Config.ProjectConfig, changedFiles?: Set, sourcesRelatedToTestsInChangedFiles?: Set): CoverageWorkerResult | null; +export {}; diff --git a/packages/jest-reporters/build/generateEmptyCoverage.js b/packages/jest-reporters/build/generateEmptyCoverage.js new file mode 100644 index 000000000000..0799a95c66b6 --- /dev/null +++ b/packages/jest-reporters/build/generateEmptyCoverage.js @@ -0,0 +1,164 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = _default; + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _istanbulLibCoverage() { + const data = require('istanbul-lib-coverage'); + + _istanbulLibCoverage = function () { + return data; + }; + + return data; +} + +function _istanbulLibInstrument() { + const data = require('istanbul-lib-instrument'); + + _istanbulLibInstrument = function () { + return data; + }; + + return data; +} + +function _transform() { + const data = require('@jest/transform'); + + _transform = function () { + return data; + }; + + return data; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +function _default( + source, + filename, + globalConfig, + config, + changedFiles, + sourcesRelatedToTestsInChangedFiles +) { + const coverageOptions = { + changedFiles, + collectCoverage: globalConfig.collectCoverage, + collectCoverageFrom: globalConfig.collectCoverageFrom, + collectCoverageOnlyFrom: globalConfig.collectCoverageOnlyFrom, + coverageProvider: globalConfig.coverageProvider, + sourcesRelatedToTestsInChangedFiles + }; + let coverageWorkerResult = null; + + if ((0, _transform().shouldInstrument)(filename, coverageOptions, config)) { + if (coverageOptions.coverageProvider === 'v8') { + const stat = fs().statSync(filename); + return { + kind: 'V8Coverage', + result: { + functions: [ + { + functionName: '(empty-report)', + isBlockCoverage: true, + ranges: [ + { + count: 0, + endOffset: stat.size, + startOffset: 0 + } + ] + } + ], + scriptId: '0', + url: filename + } + }; + } // Transform file with instrumentation to make sure initial coverage data is well mapped to original code. + + const {code} = new (_transform().ScriptTransformer)(config).transformSource( + filename, + source, + { + instrument: true, + supportsDynamicImport: true, + supportsExportNamespaceFrom: true, + supportsStaticESM: true, + supportsTopLevelAwait: true + } + ); // TODO: consider passing AST + + const extracted = (0, _istanbulLibInstrument().readInitialCoverage)(code); // Check extracted initial coverage is not null, this can happen when using /* istanbul ignore file */ + + if (extracted) { + coverageWorkerResult = { + coverage: (0, _istanbulLibCoverage().createFileCoverage)( + extracted.coverageData + ), + kind: 'BabelCoverage' + }; + } + } + + return coverageWorkerResult; +} diff --git a/packages/jest-reporters/build/getResultHeader.d.ts b/packages/jest-reporters/build/getResultHeader.d.ts new file mode 100644 index 000000000000..575e74578723 --- /dev/null +++ b/packages/jest-reporters/build/getResultHeader.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import type { TestResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +declare const _default: (result: TestResult, globalConfig: Config.GlobalConfig, projectConfig?: Config.ProjectConfig | undefined) => string; +export default _default; diff --git a/packages/jest-reporters/build/getResultHeader.js b/packages/jest-reporters/build/getResultHeader.js new file mode 100644 index 000000000000..d8b7036776bb --- /dev/null +++ b/packages/jest-reporters/build/getResultHeader.js @@ -0,0 +1,106 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _terminalLink() { + const data = _interopRequireDefault(require('terminal-link')); + + _terminalLink = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 LONG_TEST_COLOR = _chalk().default.reset.bold.bgRed; // Explicitly reset for these messages since they can get written out in the +// middle of error logging + +const FAIL_TEXT = 'FAIL'; +const PASS_TEXT = 'PASS'; +const FAIL = _chalk().default.supportsColor + ? _chalk().default.reset.inverse.bold.red(` ${FAIL_TEXT} `) + : FAIL_TEXT; +const PASS = _chalk().default.supportsColor + ? _chalk().default.reset.inverse.bold.green(` ${PASS_TEXT} `) + : PASS_TEXT; + +var _default = (result, globalConfig, projectConfig) => { + var _result$perfStats; + + const testPath = result.testFilePath; + const formattedTestPath = (0, _utils.formatTestPath)( + projectConfig ? projectConfig : globalConfig, + testPath + ); + const fileLink = (0, _terminalLink().default)( + formattedTestPath, + `file://${testPath}`, + { + fallback: () => formattedTestPath + } + ); + const status = + result.numFailingTests > 0 || result.testExecError ? FAIL : PASS; + const testDetail = []; + + if ( + (_result$perfStats = result.perfStats) !== null && + _result$perfStats !== void 0 && + _result$perfStats.slow + ) { + const runTime = result.perfStats.runtime / 1000; + testDetail.push(LONG_TEST_COLOR((0, _jestUtil().formatTime)(runTime, 0))); + } + + if (result.memoryUsage) { + const toMB = bytes => Math.floor(bytes / 1024 / 1024); + + testDetail.push(`${toMB(result.memoryUsage)} MB heap size`); + } + + const projectDisplayName = + projectConfig && projectConfig.displayName + ? (0, _utils.printDisplayName)(projectConfig) + ' ' + : ''; + return ( + `${status} ${projectDisplayName}${fileLink}` + + (testDetail.length ? ` (${testDetail.join(', ')})` : '') + ); +}; + +exports.default = _default; diff --git a/packages/jest-reporters/build/getSnapshotStatus.d.ts b/packages/jest-reporters/build/getSnapshotStatus.d.ts new file mode 100644 index 000000000000..4bbabbaf0423 --- /dev/null +++ b/packages/jest-reporters/build/getSnapshotStatus.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { TestResult } from '@jest/test-result'; +declare const _default: (snapshot: TestResult['snapshot'], afterUpdate: boolean) => Array; +export default _default; diff --git a/packages/jest-reporters/build/getSnapshotStatus.js b/packages/jest-reporters/build/getSnapshotStatus.js new file mode 100644 index 000000000000..7bec660300ba --- /dev/null +++ b/packages/jest-reporters/build/getSnapshotStatus.js @@ -0,0 +1,113 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 ARROW = ' \u203A '; +const DOT = ' \u2022 '; + +const FAIL_COLOR = _chalk().default.bold.red; + +const SNAPSHOT_ADDED = _chalk().default.bold.green; + +const SNAPSHOT_UPDATED = _chalk().default.bold.green; + +const SNAPSHOT_OUTDATED = _chalk().default.bold.yellow; + +var _default = (snapshot, afterUpdate) => { + const statuses = []; + + if (snapshot.added) { + statuses.push( + SNAPSHOT_ADDED( + ARROW + + (0, _jestUtil().pluralize)('snapshot', snapshot.added) + + ' written.' + ) + ); + } + + if (snapshot.updated) { + statuses.push( + SNAPSHOT_UPDATED( + ARROW + + (0, _jestUtil().pluralize)('snapshot', snapshot.updated) + + ' updated.' + ) + ); + } + + if (snapshot.unmatched) { + statuses.push( + FAIL_COLOR( + ARROW + + (0, _jestUtil().pluralize)('snapshot', snapshot.unmatched) + + ' failed.' + ) + ); + } + + if (snapshot.unchecked) { + if (afterUpdate) { + statuses.push( + SNAPSHOT_UPDATED( + ARROW + + (0, _jestUtil().pluralize)('snapshot', snapshot.unchecked) + + ' removed.' + ) + ); + } else { + statuses.push( + SNAPSHOT_OUTDATED( + ARROW + + (0, _jestUtil().pluralize)('snapshot', snapshot.unchecked) + + ' obsolete' + ) + '.' + ); + } + + snapshot.uncheckedKeys.forEach(key => { + statuses.push(` ${DOT}${key}`); + }); + } + + if (snapshot.fileDeleted) { + statuses.push(SNAPSHOT_UPDATED(ARROW + 'snapshot file removed.')); + } + + return statuses; +}; + +exports.default = _default; diff --git a/packages/jest-reporters/build/getSnapshotSummary.d.ts b/packages/jest-reporters/build/getSnapshotSummary.d.ts new file mode 100644 index 000000000000..b207e242ffb3 --- /dev/null +++ b/packages/jest-reporters/build/getSnapshotSummary.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import type { SnapshotSummary } from '@jest/test-result'; +import type { Config } from '@jest/types'; +declare const _default: (snapshots: SnapshotSummary, globalConfig: Config.GlobalConfig, updateCommand: string) => Array; +export default _default; diff --git a/packages/jest-reporters/build/getSnapshotSummary.js b/packages/jest-reporters/build/getSnapshotSummary.js new file mode 100644 index 000000000000..eb62f3105f15 --- /dev/null +++ b/packages/jest-reporters/build/getSnapshotSummary.js @@ -0,0 +1,205 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 ARROW = ' \u203A '; +const DOWN_ARROW = ' \u21B3 '; +const DOT = ' \u2022 '; + +const FAIL_COLOR = _chalk().default.bold.red; + +const OBSOLETE_COLOR = _chalk().default.bold.yellow; + +const SNAPSHOT_ADDED = _chalk().default.bold.green; + +const SNAPSHOT_NOTE = _chalk().default.dim; + +const SNAPSHOT_REMOVED = _chalk().default.bold.green; + +const SNAPSHOT_SUMMARY = _chalk().default.bold; + +const SNAPSHOT_UPDATED = _chalk().default.bold.green; + +var _default = (snapshots, globalConfig, updateCommand) => { + const summary = []; + summary.push(SNAPSHOT_SUMMARY('Snapshot Summary')); + + if (snapshots.added) { + summary.push( + SNAPSHOT_ADDED( + ARROW + + (0, _jestUtil().pluralize)('snapshot', snapshots.added) + + ' written ' + ) + + `from ${(0, _jestUtil().pluralize)( + 'test suite', + snapshots.filesAdded + )}.` + ); + } + + if (snapshots.unmatched) { + summary.push( + FAIL_COLOR( + `${ARROW}${(0, _jestUtil().pluralize)( + 'snapshot', + snapshots.unmatched + )} failed` + ) + + ` from ${(0, _jestUtil().pluralize)( + 'test suite', + snapshots.filesUnmatched + )}. ` + + SNAPSHOT_NOTE( + 'Inspect your code changes or ' + updateCommand + ' to update them.' + ) + ); + } + + if (snapshots.updated) { + summary.push( + SNAPSHOT_UPDATED( + ARROW + + (0, _jestUtil().pluralize)('snapshot', snapshots.updated) + + ' updated ' + ) + + `from ${(0, _jestUtil().pluralize)( + 'test suite', + snapshots.filesUpdated + )}.` + ); + } + + if (snapshots.filesRemoved) { + if (snapshots.didUpdate) { + summary.push( + SNAPSHOT_REMOVED( + `${ARROW}${(0, _jestUtil().pluralize)( + 'snapshot file', + snapshots.filesRemoved + )} removed ` + ) + + `from ${(0, _jestUtil().pluralize)( + 'test suite', + snapshots.filesRemoved + )}.` + ); + } else { + summary.push( + OBSOLETE_COLOR( + `${ARROW}${(0, _jestUtil().pluralize)( + 'snapshot file', + snapshots.filesRemoved + )} obsolete ` + ) + + `from ${(0, _jestUtil().pluralize)( + 'test suite', + snapshots.filesRemoved + )}. ` + + SNAPSHOT_NOTE( + `To remove ${ + snapshots.filesRemoved === 1 ? 'it' : 'them all' + }, ${updateCommand}.` + ) + ); + } + } + + if (snapshots.filesRemovedList && snapshots.filesRemovedList.length) { + const [head, ...tail] = snapshots.filesRemovedList; + summary.push( + ` ${DOWN_ARROW} ${DOT}${(0, _utils.formatTestPath)(globalConfig, head)}` + ); + tail.forEach(key => { + summary.push( + ` ${DOT}${(0, _utils.formatTestPath)(globalConfig, key)}` + ); + }); + } + + if (snapshots.unchecked) { + if (snapshots.didUpdate) { + summary.push( + SNAPSHOT_REMOVED( + `${ARROW}${(0, _jestUtil().pluralize)( + 'snapshot', + snapshots.unchecked + )} removed ` + ) + + `from ${(0, _jestUtil().pluralize)( + 'test suite', + snapshots.uncheckedKeysByFile.length + )}.` + ); + } else { + summary.push( + OBSOLETE_COLOR( + `${ARROW}${(0, _jestUtil().pluralize)( + 'snapshot', + snapshots.unchecked + )} obsolete ` + ) + + `from ${(0, _jestUtil().pluralize)( + 'test suite', + snapshots.uncheckedKeysByFile.length + )}. ` + + SNAPSHOT_NOTE( + `To remove ${ + snapshots.unchecked === 1 ? 'it' : 'them all' + }, ${updateCommand}.` + ) + ); + } + + snapshots.uncheckedKeysByFile.forEach(uncheckedFile => { + summary.push( + ` ${DOWN_ARROW}${(0, _utils.formatTestPath)( + globalConfig, + uncheckedFile.filePath + )}` + ); + uncheckedFile.keys.forEach(key => { + summary.push(` ${DOT}${key}`); + }); + }); + } + + return summary; +}; + +exports.default = _default; diff --git a/packages/jest-reporters/build/getWatermarks.d.ts b/packages/jest-reporters/build/getWatermarks.d.ts new file mode 100644 index 000000000000..a64751e0b403 --- /dev/null +++ b/packages/jest-reporters/build/getWatermarks.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import istanbulReport = require('istanbul-lib-report'); +import type { Config } from '@jest/types'; +export default function getWatermarks(config: Config.GlobalConfig): istanbulReport.Watermarks; diff --git a/packages/jest-reporters/build/getWatermarks.js b/packages/jest-reporters/build/getWatermarks.js new file mode 100644 index 000000000000..5e27e4197467 --- /dev/null +++ b/packages/jest-reporters/build/getWatermarks.js @@ -0,0 +1,47 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = getWatermarks; + +function _istanbulLibReport() { + const data = _interopRequireDefault(require('istanbul-lib-report')); + + _istanbulLibReport = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +function getWatermarks(config) { + const defaultWatermarks = _istanbulLibReport().default.getDefaultWatermarks(); + + const {coverageThreshold} = config; + + if (!coverageThreshold || !coverageThreshold.global) { + return defaultWatermarks; + } + + const keys = ['branches', 'functions', 'lines', 'statements']; + return keys.reduce((watermarks, key) => { + const value = coverageThreshold.global[key]; + + if (value !== undefined) { + watermarks[key][1] = value; + } + + return watermarks; + }, defaultWatermarks); +} diff --git a/packages/jest-reporters/build/index.d.ts b/packages/jest-reporters/build/index.d.ts new file mode 100644 index 000000000000..259dc45542a3 --- /dev/null +++ b/packages/jest-reporters/build/index.d.ts @@ -0,0 +1,24 @@ +/** + * 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. + */ +export type { Config } from '@jest/types'; +export type { AggregatedResult, SnapshotSummary, TestResult, } from '@jest/test-result'; +export { default as BaseReporter } from './BaseReporter'; +export { default as CoverageReporter } from './CoverageReporter'; +export { default as DefaultReporter } from './DefaultReporter'; +export { default as NotifyReporter } from './NotifyReporter'; +export { default as SummaryReporter } from './SummaryReporter'; +export { default as VerboseReporter } from './VerboseReporter'; +export type { Context, Reporter, ReporterOnStartOptions, SummaryOptions, Test, } from './types'; +export declare const utils: { + formatTestPath: (config: import("@jest/types/build/Config").GlobalConfig | import("@jest/types/build/Config").ProjectConfig, testPath: string) => string; + printDisplayName: (config: import("@jest/types/build/Config").ProjectConfig) => string; + relativePath: (config: import("@jest/types/build/Config").GlobalConfig | import("@jest/types/build/Config").ProjectConfig, testPath: string) => { + basename: string; + dirname: string; + }; + trimAndFormatPath: (pad: number, config: import("@jest/types/build/Config").GlobalConfig | import("@jest/types/build/Config").ProjectConfig, testPath: string, columns: number) => string; +}; diff --git a/packages/jest-reporters/build/index.js b/packages/jest-reporters/build/index.js new file mode 100644 index 000000000000..94fea962b439 --- /dev/null +++ b/packages/jest-reporters/build/index.js @@ -0,0 +1,74 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'BaseReporter', { + enumerable: true, + get: function () { + return _BaseReporter.default; + } +}); +Object.defineProperty(exports, 'CoverageReporter', { + enumerable: true, + get: function () { + return _CoverageReporter.default; + } +}); +Object.defineProperty(exports, 'DefaultReporter', { + enumerable: true, + get: function () { + return _DefaultReporter.default; + } +}); +Object.defineProperty(exports, 'NotifyReporter', { + enumerable: true, + get: function () { + return _NotifyReporter.default; + } +}); +Object.defineProperty(exports, 'SummaryReporter', { + enumerable: true, + get: function () { + return _SummaryReporter.default; + } +}); +Object.defineProperty(exports, 'VerboseReporter', { + enumerable: true, + get: function () { + return _VerboseReporter.default; + } +}); +exports.utils = void 0; + +var _utils = require('./utils'); + +var _BaseReporter = _interopRequireDefault(require('./BaseReporter')); + +var _CoverageReporter = _interopRequireDefault(require('./CoverageReporter')); + +var _DefaultReporter = _interopRequireDefault(require('./DefaultReporter')); + +var _NotifyReporter = _interopRequireDefault(require('./NotifyReporter')); + +var _SummaryReporter = _interopRequireDefault(require('./SummaryReporter')); + +var _VerboseReporter = _interopRequireDefault(require('./VerboseReporter')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 utils = { + formatTestPath: _utils.formatTestPath, + printDisplayName: _utils.printDisplayName, + relativePath: _utils.relativePath, + trimAndFormatPath: _utils.trimAndFormatPath +}; +exports.utils = utils; diff --git a/packages/jest-reporters/build/types.d.ts b/packages/jest-reporters/build/types.d.ts new file mode 100644 index 000000000000..0dcbe261c26e --- /dev/null +++ b/packages/jest-reporters/build/types.d.ts @@ -0,0 +1,75 @@ +/** + * 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. + */ +import type { AggregatedResult, SerializableError, TestCaseResult, TestResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import type { FS as HasteFS, ModuleMap } from 'jest-haste-map'; +import type Resolver from 'jest-resolve'; +import type { worker } from './CoverageWorker'; +export declare type ReporterOnStartOptions = { + estimatedTime: number; + showStatus: boolean; +}; +export declare type Context = { + config: Config.ProjectConfig; + hasteFS: HasteFS; + moduleMap: ModuleMap; + resolver: Resolver; +}; +export declare type Test = { + context: Context; + duration?: number; + path: Config.Path; +}; +export declare type CoverageWorker = { + worker: typeof worker; +}; +export declare type CoverageReporterOptions = { + changedFiles?: Set; + sourcesRelatedToTestsInChangedFiles?: Set; +}; +export declare type CoverageReporterSerializedOptions = { + changedFiles?: Array; + sourcesRelatedToTestsInChangedFiles?: Array; +}; +export declare type OnTestStart = (test: Test) => Promise; +export declare type OnTestFailure = (test: Test, error: SerializableError) => Promise; +export declare type OnTestSuccess = (test: Test, result: TestResult) => Promise; +export interface Reporter { + readonly onTestResult?: (test: Test, testResult: TestResult, aggregatedResult: AggregatedResult) => Promise | void; + readonly onTestFileResult?: (test: Test, testResult: TestResult, aggregatedResult: AggregatedResult) => Promise | void; + readonly onTestCaseResult?: (test: Test, testCaseResult: TestCaseResult) => Promise | void; + readonly onRunStart: (results: AggregatedResult, options: ReporterOnStartOptions) => Promise | void; + readonly onTestStart?: (test: Test) => Promise | void; + readonly onTestFileStart?: (test: Test) => Promise | void; + readonly onRunComplete: (contexts: Set, results: AggregatedResult) => Promise | void; + readonly getLastError: () => Error | void; +} +export declare type SummaryOptions = { + currentTestCases?: Array<{ + test: Test; + testCaseResult: TestCaseResult; + }>; + estimatedTime?: number; + roundTime?: boolean; + width?: number; +}; +export declare type TestRunnerOptions = { + serial: boolean; +}; +export declare type TestRunData = Array<{ + context: Context; + matches: { + allTests: number; + tests: Array; + total: number; + }; +}>; +export declare type TestSchedulerContext = { + firstRun: boolean; + previousSuccess: boolean; + changedFiles?: Set; +}; diff --git a/packages/jest-reporters/build/types.js b/packages/jest-reporters/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-reporters/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-reporters/build/utils.d.ts b/packages/jest-reporters/build/utils.d.ts new file mode 100644 index 000000000000..ed676be2ea41 --- /dev/null +++ b/packages/jest-reporters/build/utils.d.ts @@ -0,0 +1,18 @@ +/** + * 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. + */ +import type { AggregatedResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import type { SummaryOptions } from './types'; +export declare const printDisplayName: (config: Config.ProjectConfig) => string; +export declare const trimAndFormatPath: (pad: number, config: Config.ProjectConfig | Config.GlobalConfig, testPath: Config.Path, columns: number) => string; +export declare const formatTestPath: (config: Config.GlobalConfig | Config.ProjectConfig, testPath: Config.Path) => string; +export declare const relativePath: (config: Config.GlobalConfig | Config.ProjectConfig, testPath: Config.Path) => { + basename: string; + dirname: string; +}; +export declare const getSummary: (aggregatedResults: AggregatedResult, options?: SummaryOptions | undefined) => string; +export declare const wrapAnsiString: (string: string, terminalWidth: number) => string; diff --git a/packages/jest-reporters/build/utils.js b/packages/jest-reporters/build/utils.js new file mode 100644 index 000000000000..f369879336cd --- /dev/null +++ b/packages/jest-reporters/build/utils.js @@ -0,0 +1,429 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.wrapAnsiString = exports.getSummary = exports.relativePath = exports.formatTestPath = exports.trimAndFormatPath = exports.printDisplayName = void 0; + +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 _slash() { + const data = _interopRequireDefault(require('slash')); + + _slash = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 PROGRESS_BAR_WIDTH = 40; + +const printDisplayName = config => { + const {displayName} = config; + + const white = _chalk().default.reset.inverse.white; + + if (!displayName) { + return ''; + } + + const {name, color} = displayName; + const chosenColor = _chalk().default.reset.inverse[color] + ? _chalk().default.reset.inverse[color] + : white; + return _chalk().default.supportsColor ? chosenColor(` ${name} `) : name; +}; + +exports.printDisplayName = printDisplayName; + +const trimAndFormatPath = (pad, config, testPath, columns) => { + const maxLength = columns - pad; + const relative = relativePath(config, testPath); + const {basename} = relative; + let {dirname} = relative; // length is ok + + if ((dirname + path().sep + basename).length <= maxLength) { + return (0, _slash().default)( + _chalk().default.dim(dirname + path().sep) + + _chalk().default.bold(basename) + ); + } // we can fit trimmed dirname and full basename + + const basenameLength = basename.length; + + if (basenameLength + 4 < maxLength) { + const dirnameLength = maxLength - 4 - basenameLength; + dirname = + '...' + dirname.slice(dirname.length - dirnameLength, dirname.length); + return (0, _slash().default)( + _chalk().default.dim(dirname + path().sep) + + _chalk().default.bold(basename) + ); + } + + if (basenameLength + 4 === maxLength) { + return (0, _slash().default)( + _chalk().default.dim('...' + path().sep) + _chalk().default.bold(basename) + ); + } // can't fit dirname, but can fit trimmed basename + + return (0, _slash().default)( + _chalk().default.bold( + '...' + basename.slice(basename.length - maxLength - 4, basename.length) + ) + ); +}; + +exports.trimAndFormatPath = trimAndFormatPath; + +const formatTestPath = (config, testPath) => { + const {dirname, basename} = relativePath(config, testPath); + return (0, _slash().default)( + _chalk().default.dim(dirname + path().sep) + _chalk().default.bold(basename) + ); +}; + +exports.formatTestPath = formatTestPath; + +const relativePath = (config, testPath) => { + // this function can be called with ProjectConfigs or GlobalConfigs. GlobalConfigs + // do not have config.cwd, only config.rootDir. Try using config.cwd, fallback + // to config.rootDir. (Also, some unit just use config.rootDir, which is ok) + testPath = path().relative(config.cwd || config.rootDir, testPath); + const dirname = path().dirname(testPath); + const basename = path().basename(testPath); + return { + basename, + dirname + }; +}; + +exports.relativePath = relativePath; + +const getValuesCurrentTestCases = (currentTestCases = []) => { + let numFailingTests = 0; + let numPassingTests = 0; + let numPendingTests = 0; + let numTodoTests = 0; + let numTotalTests = 0; + currentTestCases.forEach(testCase => { + switch (testCase.testCaseResult.status) { + case 'failed': { + numFailingTests++; + break; + } + + case 'passed': { + numPassingTests++; + break; + } + + case 'skipped': { + numPendingTests++; + break; + } + + case 'todo': { + numTodoTests++; + break; + } + } + + numTotalTests++; + }); + return { + numFailingTests, + numPassingTests, + numPendingTests, + numTodoTests, + numTotalTests + }; +}; + +const getSummary = (aggregatedResults, options) => { + let runTime = (Date.now() - aggregatedResults.startTime) / 1000; + + if (options && options.roundTime) { + runTime = Math.floor(runTime); + } + + const valuesForCurrentTestCases = getValuesCurrentTestCases( + options === null || options === void 0 ? void 0 : options.currentTestCases + ); + const estimatedTime = (options && options.estimatedTime) || 0; + const snapshotResults = aggregatedResults.snapshot; + const snapshotsAdded = snapshotResults.added; + const snapshotsFailed = snapshotResults.unmatched; + const snapshotsOutdated = snapshotResults.unchecked; + const snapshotsFilesRemoved = snapshotResults.filesRemoved; + const snapshotsDidUpdate = snapshotResults.didUpdate; + const snapshotsPassed = snapshotResults.matched; + const snapshotsTotal = snapshotResults.total; + const snapshotsUpdated = snapshotResults.updated; + const suitesFailed = aggregatedResults.numFailedTestSuites; + const suitesPassed = aggregatedResults.numPassedTestSuites; + const suitesPending = aggregatedResults.numPendingTestSuites; + const suitesRun = suitesFailed + suitesPassed; + const suitesTotal = aggregatedResults.numTotalTestSuites; + const testsFailed = aggregatedResults.numFailedTests; + const testsPassed = aggregatedResults.numPassedTests; + const testsPending = aggregatedResults.numPendingTests; + const testsTodo = aggregatedResults.numTodoTests; + const testsTotal = aggregatedResults.numTotalTests; + const width = (options && options.width) || 0; + const suites = + _chalk().default.bold('Test Suites: ') + + (suitesFailed + ? _chalk().default.bold.red(`${suitesFailed} failed`) + ', ' + : '') + + (suitesPending + ? _chalk().default.bold.yellow(`${suitesPending} skipped`) + ', ' + : '') + + (suitesPassed + ? _chalk().default.bold.green(`${suitesPassed} passed`) + ', ' + : '') + + (suitesRun !== suitesTotal + ? suitesRun + ' of ' + suitesTotal + : suitesTotal) + + ` total`; + const updatedTestsFailed = + testsFailed + valuesForCurrentTestCases.numFailingTests; + const updatedTestsPending = + testsPending + valuesForCurrentTestCases.numPendingTests; + const updatedTestsTodo = testsTodo + valuesForCurrentTestCases.numTodoTests; + const updatedTestsPassed = + testsPassed + valuesForCurrentTestCases.numPassingTests; + const updatedTestsTotal = + testsTotal + valuesForCurrentTestCases.numTotalTests; + const tests = + _chalk().default.bold('Tests: ') + + (updatedTestsFailed > 0 + ? _chalk().default.bold.red(`${updatedTestsFailed} failed`) + ', ' + : '') + + (updatedTestsPending > 0 + ? _chalk().default.bold.yellow(`${updatedTestsPending} skipped`) + ', ' + : '') + + (updatedTestsTodo > 0 + ? _chalk().default.bold.magenta(`${updatedTestsTodo} todo`) + ', ' + : '') + + (updatedTestsPassed > 0 + ? _chalk().default.bold.green(`${updatedTestsPassed} passed`) + ', ' + : '') + + `${updatedTestsTotal} total`; + const snapshots = + _chalk().default.bold('Snapshots: ') + + (snapshotsFailed + ? _chalk().default.bold.red(`${snapshotsFailed} failed`) + ', ' + : '') + + (snapshotsOutdated && !snapshotsDidUpdate + ? _chalk().default.bold.yellow(`${snapshotsOutdated} obsolete`) + ', ' + : '') + + (snapshotsOutdated && snapshotsDidUpdate + ? _chalk().default.bold.green(`${snapshotsOutdated} removed`) + ', ' + : '') + + (snapshotsFilesRemoved && !snapshotsDidUpdate + ? _chalk().default.bold.yellow( + (0, _jestUtil().pluralize)('file', snapshotsFilesRemoved) + + ' obsolete' + ) + ', ' + : '') + + (snapshotsFilesRemoved && snapshotsDidUpdate + ? _chalk().default.bold.green( + (0, _jestUtil().pluralize)('file', snapshotsFilesRemoved) + ' removed' + ) + ', ' + : '') + + (snapshotsUpdated + ? _chalk().default.bold.green(`${snapshotsUpdated} updated`) + ', ' + : '') + + (snapshotsAdded + ? _chalk().default.bold.green(`${snapshotsAdded} written`) + ', ' + : '') + + (snapshotsPassed + ? _chalk().default.bold.green(`${snapshotsPassed} passed`) + ', ' + : '') + + `${snapshotsTotal} total`; + const time = renderTime(runTime, estimatedTime, width); + return [suites, tests, snapshots, time].join('\n'); +}; + +exports.getSummary = getSummary; + +const renderTime = (runTime, estimatedTime, width) => { + // If we are more than one second over the estimated time, highlight it. + const renderedTime = + estimatedTime && runTime >= estimatedTime + 1 + ? _chalk().default.bold.yellow((0, _jestUtil().formatTime)(runTime, 0)) + : (0, _jestUtil().formatTime)(runTime, 0); + let time = _chalk().default.bold(`Time:`) + ` ${renderedTime}`; + + if (runTime < estimatedTime) { + time += `, estimated ${(0, _jestUtil().formatTime)(estimatedTime, 0)}`; + } // Only show a progress bar if the test run is actually going to take + // some time. + + if (estimatedTime > 2 && runTime < estimatedTime && width) { + const availableWidth = Math.min(PROGRESS_BAR_WIDTH, width); + const length = Math.min( + Math.floor((runTime / estimatedTime) * availableWidth), + availableWidth + ); + + if (availableWidth >= 2) { + time += + '\n' + + _chalk().default.green('█').repeat(length) + + _chalk() + .default.white('█') + .repeat(availableWidth - length); + } + } + + return time; +}; // word-wrap a string that contains ANSI escape sequences. +// ANSI escape sequences do not add to the string length. + +const wrapAnsiString = (string, terminalWidth) => { + if (terminalWidth === 0) { + // if the terminal width is zero, don't bother word-wrapping + return string; + } + + const ANSI_REGEXP = /[\u001b\u009b]\[\d{1,2}m/g; + const tokens = []; + let lastIndex = 0; + let match; + + while ((match = ANSI_REGEXP.exec(string))) { + const ansi = match[0]; + const index = match['index']; + + if (index != lastIndex) { + tokens.push(['string', string.slice(lastIndex, index)]); + } + + tokens.push(['ansi', ansi]); + lastIndex = index + ansi.length; + } + + if (lastIndex != string.length - 1) { + tokens.push(['string', string.slice(lastIndex, string.length)]); + } + + let lastLineLength = 0; + return tokens + .reduce( + (lines, [kind, token]) => { + if (kind === 'string') { + if (lastLineLength + token.length > terminalWidth) { + while (token.length) { + const chunk = token.slice(0, terminalWidth - lastLineLength); + const remaining = token.slice( + terminalWidth - lastLineLength, + token.length + ); + lines[lines.length - 1] += chunk; + lastLineLength += chunk.length; + token = remaining; + + if (token.length) { + lines.push(''); + lastLineLength = 0; + } + } + } else { + lines[lines.length - 1] += token; + lastLineLength += token.length; + } + } else { + lines[lines.length - 1] += token; + } + + return lines; + }, + [''] + ) + .join('\n'); +}; + +exports.wrapAnsiString = wrapAnsiString; diff --git a/packages/jest-resolve-dependencies/build/index.d.ts b/packages/jest-resolve-dependencies/build/index.d.ts new file mode 100644 index 000000000000..9e557861a8b9 --- /dev/null +++ b/packages/jest-resolve-dependencies/build/index.d.ts @@ -0,0 +1,27 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { FS as HasteFS } from 'jest-haste-map'; +import type { ResolveModuleConfig, default as Resolver } from 'jest-resolve'; +import { SnapshotResolver } from 'jest-snapshot'; +export declare type ResolvedModule = { + file: Config.Path; + dependencies: Array; +}; +/** + * DependencyResolver is used to resolve the direct dependencies of a module or + * to retrieve a list of all transitive inverse dependencies. + */ +export declare class DependencyResolver { + private _hasteFS; + private _resolver; + private _snapshotResolver; + constructor(resolver: Resolver, hasteFS: HasteFS, snapshotResolver: SnapshotResolver); + resolve(file: Config.Path, options?: ResolveModuleConfig): Array; + resolveInverseModuleMap(paths: Set, filter: (file: Config.Path) => boolean, options?: ResolveModuleConfig): Array; + resolveInverse(paths: Set, filter: (file: Config.Path) => boolean, options?: ResolveModuleConfig): Array; +} diff --git a/packages/jest-resolve-dependencies/build/index.js b/packages/jest-resolve-dependencies/build/index.js new file mode 100644 index 000000000000..45377abb39e8 --- /dev/null +++ b/packages/jest-resolve-dependencies/build/index.js @@ -0,0 +1,238 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.DependencyResolver = void 0; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _jestSnapshot() { + const data = require('jest-snapshot'); + + _jestSnapshot = function () { + return data; + }; + + return data; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * DependencyResolver is used to resolve the direct dependencies of a module or + * to retrieve a list of all transitive inverse dependencies. + */ +class DependencyResolver { + constructor(resolver, hasteFS, snapshotResolver) { + _defineProperty(this, '_hasteFS', void 0); + + _defineProperty(this, '_resolver', void 0); + + _defineProperty(this, '_snapshotResolver', void 0); + + this._resolver = resolver; + this._hasteFS = hasteFS; + this._snapshotResolver = snapshotResolver; + } + + resolve(file, options) { + const dependencies = this._hasteFS.getDependencies(file); + + if (!dependencies) { + return []; + } + + return dependencies.reduce((acc, dependency) => { + if (this._resolver.isCoreModule(dependency)) { + return acc; + } + + let resolvedDependency; + let resolvedMockDependency; + + try { + resolvedDependency = this._resolver.resolveModule( + file, + dependency, + options + ); + } catch { + try { + resolvedDependency = this._resolver.getMockModule(file, dependency); + } catch { + // leave resolvedDependency as undefined if nothing can be found + } + } + + if (!resolvedDependency) { + return acc; + } + + acc.push(resolvedDependency); // If we resolve a dependency, then look for a mock dependency + // of the same name in that dependency's directory. + + try { + resolvedMockDependency = this._resolver.getMockModule( + resolvedDependency, + path().basename(dependency) + ); + } catch { + // leave resolvedMockDependency as undefined if nothing can be found + } + + if (resolvedMockDependency) { + const dependencyMockDir = path().resolve( + path().dirname(resolvedDependency), + '__mocks__' + ); + resolvedMockDependency = path().resolve(resolvedMockDependency); // make sure mock is in the correct directory + + if (dependencyMockDir === path().dirname(resolvedMockDependency)) { + acc.push(resolvedMockDependency); + } + } + + return acc; + }, []); + } + + resolveInverseModuleMap(paths, filter, options) { + if (!paths.size) { + return []; + } + + const collectModules = (related, moduleMap, changed) => { + const visitedModules = new Set(); + const result = []; + + while (changed.size) { + changed = new Set( + moduleMap.reduce((acc, module) => { + if ( + visitedModules.has(module.file) || + !module.dependencies.some(dep => changed.has(dep)) + ) { + return acc; + } + + const file = module.file; + + if (filter(file)) { + result.push(module); + related.delete(file); + } + + visitedModules.add(file); + acc.push(file); + return acc; + }, []) + ); + } + + return result.concat( + Array.from(related).map(file => ({ + dependencies: [], + file + })) + ); + }; + + const relatedPaths = new Set(); + const changed = new Set(); + + for (const path of paths) { + if (this._hasteFS.exists(path)) { + const modulePath = (0, _jestSnapshot().isSnapshotPath)(path) + ? this._snapshotResolver.resolveTestPath(path) + : path; + changed.add(modulePath); + + if (filter(modulePath)) { + relatedPaths.add(modulePath); + } + } + } + + const modules = []; + + for (const file of this._hasteFS.getAbsoluteFileIterator()) { + modules.push({ + dependencies: this.resolve(file, options), + file + }); + } + + return collectModules(relatedPaths, modules, changed); + } + + resolveInverse(paths, filter, options) { + return this.resolveInverseModuleMap(paths, filter, options).map( + module => module.file + ); + } +} + +exports.DependencyResolver = DependencyResolver; diff --git a/packages/jest-resolve/build/ModuleNotFoundError.d.ts b/packages/jest-resolve/build/ModuleNotFoundError.d.ts new file mode 100644 index 000000000000..711983a4c01b --- /dev/null +++ b/packages/jest-resolve/build/ModuleNotFoundError.d.ts @@ -0,0 +1,18 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default class ModuleNotFoundError extends Error { + code: string; + hint?: string; + requireStack?: Array; + siblingWithSimilarExtensionFound?: boolean; + moduleName?: string; + private _originalMessage?; + constructor(message: string, moduleName?: string); + buildMessage(rootDir: Config.Path): void; + static duckType(error: ModuleNotFoundError): ModuleNotFoundError; +} diff --git a/packages/jest-resolve/build/ModuleNotFoundError.js b/packages/jest-resolve/build/ModuleNotFoundError.js new file mode 100644 index 000000000000..2da89edc1880 --- /dev/null +++ b/packages/jest-resolve/build/ModuleNotFoundError.js @@ -0,0 +1,146 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _slash() { + const data = _interopRequireDefault(require('slash')); + + _slash = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class ModuleNotFoundError extends Error { + constructor(message, moduleName) { + super(message); + + _defineProperty(this, 'code', 'MODULE_NOT_FOUND'); + + _defineProperty(this, 'hint', void 0); + + _defineProperty(this, 'requireStack', void 0); + + _defineProperty(this, 'siblingWithSimilarExtensionFound', void 0); + + _defineProperty(this, 'moduleName', void 0); + + _defineProperty(this, '_originalMessage', void 0); + + this._originalMessage = message; + this.moduleName = moduleName; + } + + buildMessage(rootDir) { + var _this$requireStack; + + if (!this._originalMessage) { + this._originalMessage = this.message || ''; + } + + let message = this._originalMessage; + + if ( + (_this$requireStack = this.requireStack) !== null && + _this$requireStack !== void 0 && + _this$requireStack.length && + this.requireStack.length > 1 + ) { + message += ` + +Require stack: + ${this.requireStack + .map(p => p.replace(`${rootDir}${path().sep}`, '')) + .map(_slash().default) + .join('\n ')} +`; + } + + if (this.hint) { + message += this.hint; + } + + this.message = message; + } + + static duckType(error) { + error.buildMessage = ModuleNotFoundError.prototype.buildMessage; + return error; + } +} + +exports.default = ModuleNotFoundError; diff --git a/packages/jest-resolve/build/defaultResolver.d.ts b/packages/jest-resolve/build/defaultResolver.d.ts new file mode 100644 index 000000000000..237d25268145 --- /dev/null +++ b/packages/jest-resolve/build/defaultResolver.d.ts @@ -0,0 +1,28 @@ +/** + * 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. + */ +import { Opts as ResolveOpts } from 'resolve'; +import type { Config } from '@jest/types'; +declare type ResolverOptions = { + basedir: Config.Path; + browser?: boolean; + defaultResolver: typeof defaultResolver; + extensions?: Array; + moduleDirectory?: Array; + paths?: Array; + rootDir?: Config.Path; + packageFilter?: ResolveOpts['packageFilter']; +}; +declare global { + namespace NodeJS { + interface ProcessVersions { + pnp?: any; + } + } +} +export default function defaultResolver(path: Config.Path, options: ResolverOptions): Config.Path; +export declare function clearDefaultResolverCache(): void; +export {}; diff --git a/packages/jest-resolve/build/defaultResolver.js b/packages/jest-resolve/build/defaultResolver.js new file mode 100644 index 000000000000..bee7a8ebbbba --- /dev/null +++ b/packages/jest-resolve/build/defaultResolver.js @@ -0,0 +1,203 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = defaultResolver; +exports.clearDefaultResolverCache = clearDefaultResolverCache; + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _jestPnpResolver() { + const data = _interopRequireDefault(require('jest-pnp-resolver')); + + _jestPnpResolver = function () { + return data; + }; + + return data; +} + +function _resolve() { + const data = require('resolve'); + + _resolve = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +function defaultResolver(path, options) { + // Yarn 2 adds support to `resolve` automatically so the pnpResolver is only + // needed for Yarn 1 which implements version 1 of the pnp spec + if (process.versions.pnp === '1') { + return (0, _jestPnpResolver().default)(path, options); + } + + const result = (0, _resolve().sync)(path, { + basedir: options.basedir, + extensions: options.extensions, + isDirectory, + isFile, + moduleDirectory: options.moduleDirectory, + packageFilter: options.packageFilter, + paths: options.paths, + preserveSymlinks: false, + realpathSync + }); // Dereference symlinks to ensure we don't create a separate + // module instance depending on how it was referenced. + + return realpathSync(result); +} + +function clearDefaultResolverCache() { + checkedPaths.clear(); + checkedRealpathPaths.clear(); +} + +var IPathType; + +(function (IPathType) { + IPathType[(IPathType['FILE'] = 1)] = 'FILE'; + IPathType[(IPathType['DIRECTORY'] = 2)] = 'DIRECTORY'; + IPathType[(IPathType['OTHER'] = 3)] = 'OTHER'; +})(IPathType || (IPathType = {})); + +const checkedPaths = new Map(); + +function statSyncCached(path) { + const result = checkedPaths.get(path); + + if (result !== undefined) { + return result; + } + + let stat; + + try { + stat = fs().statSync(path); + } catch (e) { + if (!(e && (e.code === 'ENOENT' || e.code === 'ENOTDIR'))) { + throw e; + } + } + + if (stat) { + if (stat.isFile() || stat.isFIFO()) { + checkedPaths.set(path, IPathType.FILE); + return IPathType.FILE; + } else if (stat.isDirectory()) { + checkedPaths.set(path, IPathType.DIRECTORY); + return IPathType.DIRECTORY; + } + } + + checkedPaths.set(path, IPathType.OTHER); + return IPathType.OTHER; +} + +const checkedRealpathPaths = new Map(); + +function realpathCached(path) { + let result = checkedRealpathPaths.get(path); + + if (result !== undefined) { + return result; + } + + result = (0, _jestUtil().tryRealpath)(path); + checkedRealpathPaths.set(path, result); + + if (path !== result) { + // also cache the result in case it's ever referenced directly - no reason to `realpath` that as well + checkedRealpathPaths.set(result, result); + } + + return result; +} +/* + * helper functions + */ + +function isFile(file) { + return statSyncCached(file) === IPathType.FILE; +} + +function isDirectory(dir) { + return statSyncCached(dir) === IPathType.DIRECTORY; +} + +function realpathSync(file) { + return realpathCached(file); +} diff --git a/packages/jest-resolve/build/index.d.ts b/packages/jest-resolve/build/index.d.ts new file mode 100644 index 000000000000..5983d2971b49 --- /dev/null +++ b/packages/jest-resolve/build/index.d.ts @@ -0,0 +1,56 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { ModuleMap } from 'jest-haste-map'; +import ModuleNotFoundError from './ModuleNotFoundError'; +import shouldLoadAsEsm from './shouldLoadAsEsm'; +import type { ResolverConfig } from './types'; +declare type FindNodeModuleConfig = { + basedir: Config.Path; + browser?: boolean; + extensions?: Array; + moduleDirectory?: Array; + paths?: Array; + resolver?: Config.Path | null; + rootDir?: Config.Path; + throwIfNotFound?: boolean; +}; +export declare type ResolveModuleConfig = { + skipNodeResolution?: boolean; + paths?: Array; +}; +declare class Resolver { + private readonly _options; + private readonly _moduleMap; + private readonly _moduleIDCache; + private readonly _moduleNameCache; + private readonly _modulePathCache; + private readonly _supportsNativePlatform; + constructor(moduleMap: ModuleMap, options: ResolverConfig); + static ModuleNotFoundError: typeof ModuleNotFoundError; + static tryCastModuleNotFoundError(error: unknown): ModuleNotFoundError | null; + static clearDefaultResolverCache(): void; + static findNodeModule(path: Config.Path, options: FindNodeModuleConfig): Config.Path | null; + static unstable_shouldLoadAsEsm: typeof shouldLoadAsEsm; + resolveModuleFromDirIfExists(dirname: Config.Path, moduleName: string, options?: ResolveModuleConfig): Config.Path | null; + resolveModule(from: Config.Path, moduleName: string, options?: ResolveModuleConfig): Config.Path; + private _isAliasModule; + isCoreModule(moduleName: string): boolean; + getModule(name: string): Config.Path | null; + getModulePath(from: Config.Path, moduleName: string): Config.Path; + getPackage(name: string): Config.Path | null; + getMockModule(from: Config.Path, name: string): Config.Path | null; + getModulePaths(from: Config.Path): Array; + getModuleID(virtualMocks: Map, from: Config.Path, _moduleName?: string): string; + private _getModuleType; + private _getAbsolutePath; + private _getMockPath; + private _getVirtualMockPath; + private _isModuleResolved; + resolveStubModuleName(from: Config.Path, moduleName: string): Config.Path | null; +} +export default Resolver; diff --git a/packages/jest-resolve/build/index.js b/packages/jest-resolve/build/index.js new file mode 100644 index 000000000000..af75507c634b --- /dev/null +++ b/packages/jest-resolve/build/index.js @@ -0,0 +1,575 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +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 _slash() { + const data = _interopRequireDefault(require('slash')); + + _slash = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _ModuleNotFoundError = _interopRequireDefault( + require('./ModuleNotFoundError') +); + +var _defaultResolver = _interopRequireWildcard(require('./defaultResolver')); + +var _isBuiltinModule = _interopRequireDefault(require('./isBuiltinModule')); + +var _nodeModulesPaths = _interopRequireDefault(require('./nodeModulesPaths')); + +var _shouldLoadAsEsm = _interopRequireWildcard(require('./shouldLoadAsEsm')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const NATIVE_PLATFORM = 'native'; // We might be inside a symlink. + +const resolvedCwd = (0, _jestUtil().tryRealpath)(process.cwd()); +const {NODE_PATH} = process.env; +const nodePaths = NODE_PATH + ? NODE_PATH.split(path().delimiter) + .filter(Boolean) // The resolver expects absolute paths. + .map(p => path().resolve(resolvedCwd, p)) + : undefined; + +class Resolver { + constructor(moduleMap, options) { + _defineProperty(this, '_options', void 0); + + _defineProperty(this, '_moduleMap', void 0); + + _defineProperty(this, '_moduleIDCache', void 0); + + _defineProperty(this, '_moduleNameCache', void 0); + + _defineProperty(this, '_modulePathCache', void 0); + + _defineProperty(this, '_supportsNativePlatform', void 0); + + this._options = { + defaultPlatform: options.defaultPlatform, + extensions: options.extensions, + hasCoreModules: + options.hasCoreModules === undefined ? true : options.hasCoreModules, + moduleDirectories: options.moduleDirectories || ['node_modules'], + moduleNameMapper: options.moduleNameMapper, + modulePaths: options.modulePaths, + platforms: options.platforms, + resolver: options.resolver, + rootDir: options.rootDir + }; + this._supportsNativePlatform = options.platforms + ? options.platforms.includes(NATIVE_PLATFORM) + : false; + this._moduleMap = moduleMap; + this._moduleIDCache = new Map(); + this._moduleNameCache = new Map(); + this._modulePathCache = new Map(); + } + + static tryCastModuleNotFoundError(error) { + if (error instanceof _ModuleNotFoundError.default) { + return error; + } + + const casted = error; + + if (casted.code === 'MODULE_NOT_FOUND') { + return _ModuleNotFoundError.default.duckType(casted); + } + + return null; + } + + static clearDefaultResolverCache() { + (0, _defaultResolver.clearDefaultResolverCache)(); + (0, _shouldLoadAsEsm.clearCachedLookups)(); + } + + static findNodeModule(path, options) { + const resolver = options.resolver + ? require(options.resolver) + : _defaultResolver.default; + const paths = options.paths; + + try { + return resolver(path, { + basedir: options.basedir, + browser: options.browser, + defaultResolver: _defaultResolver.default, + extensions: options.extensions, + moduleDirectory: options.moduleDirectory, + paths: paths ? (nodePaths || []).concat(paths) : nodePaths, + rootDir: options.rootDir + }); + } catch (e) { + if (options.throwIfNotFound) { + throw e; + } + } + + return null; + } // unstable as it should be replaced by https://github.com/nodejs/modules/issues/393, and we don't want people to use it + + resolveModuleFromDirIfExists(dirname, moduleName, options) { + const paths = (options && options.paths) || this._options.modulePaths; + const moduleDirectory = this._options.moduleDirectories; + const key = dirname + path().delimiter + moduleName; + const defaultPlatform = this._options.defaultPlatform; + + const extensions = this._options.extensions.slice(); + + let module; + + if (this._supportsNativePlatform) { + extensions.unshift( + ...this._options.extensions.map(ext => '.' + NATIVE_PLATFORM + ext) + ); + } + + if (defaultPlatform) { + extensions.unshift( + ...this._options.extensions.map(ext => '.' + defaultPlatform + ext) + ); + } // 1. If we have already resolved this module for this directory name, + // return a value from the cache. + + const cacheResult = this._moduleNameCache.get(key); + + if (cacheResult) { + return cacheResult; + } // 2. Check if the module is a haste module. + + module = this.getModule(moduleName); + + if (module) { + this._moduleNameCache.set(key, module); + + return module; + } // 3. Check if the module is a node module and resolve it based on + // the node module resolution algorithm. If skipNodeResolution is given we + // ignore all modules that look like node modules (ie. are not relative + // requires). This enables us to speed up resolution when we build a + // dependency graph because we don't have to look at modules that may not + // exist and aren't mocked. + + const skipResolution = + options && options.skipNodeResolution && !moduleName.includes(path().sep); + + const resolveNodeModule = (name, throwIfNotFound = false) => + Resolver.findNodeModule(name, { + basedir: dirname, + extensions, + moduleDirectory, + paths, + resolver: this._options.resolver, + rootDir: this._options.rootDir, + throwIfNotFound + }); + + if (!skipResolution) { + module = resolveNodeModule(moduleName, Boolean(process.versions.pnp)); + + if (module) { + this._moduleNameCache.set(key, module); + + return module; + } + } // 4. Resolve "haste packages" which are `package.json` files outside of + // `node_modules` folders anywhere in the file system. + + const parts = moduleName.split('/'); + const hastePackage = this.getPackage(parts.shift()); + + if (hastePackage) { + try { + const module = path().join.apply( + path(), + [path().dirname(hastePackage)].concat(parts) + ); // try resolving with custom resolver first to support extensions, + // then fallback to require.resolve + + const resolvedModule = + resolveNodeModule(module) || require.resolve(module); + + this._moduleNameCache.set(key, resolvedModule); + + return resolvedModule; + } catch {} + } + + return null; + } + + resolveModule(from, moduleName, options) { + const dirname = path().dirname(from); + const module = + this.resolveStubModuleName(from, moduleName) || + this.resolveModuleFromDirIfExists(dirname, moduleName, options); + if (module) return module; // 5. Throw an error if the module could not be found. `resolve.sync` only + // produces an error based on the dirname but we have the actual current + // module name available. + + const relativePath = + (0, _slash().default)(path().relative(this._options.rootDir, from)) || + '.'; + throw new _ModuleNotFoundError.default( + `Cannot find module '${moduleName}' from '${relativePath}'`, + moduleName + ); + } + + _isAliasModule(moduleName) { + const moduleNameMapper = this._options.moduleNameMapper; + + if (!moduleNameMapper) { + return false; + } + + return moduleNameMapper.some(({regex}) => regex.test(moduleName)); + } + + isCoreModule(moduleName) { + return ( + this._options.hasCoreModules && + (0, _isBuiltinModule.default)(moduleName) && + !this._isAliasModule(moduleName) + ); + } + + getModule(name) { + return this._moduleMap.getModule( + name, + this._options.defaultPlatform, + this._supportsNativePlatform + ); + } + + getModulePath(from, moduleName) { + if (moduleName[0] !== '.' || path().isAbsolute(moduleName)) { + return moduleName; + } + + return path().normalize(path().dirname(from) + '/' + moduleName); + } + + getPackage(name) { + return this._moduleMap.getPackage( + name, + this._options.defaultPlatform, + this._supportsNativePlatform + ); + } + + getMockModule(from, name) { + const mock = this._moduleMap.getMockModule(name); + + if (mock) { + return mock; + } else { + const moduleName = this.resolveStubModuleName(from, name); + + if (moduleName) { + return this.getModule(moduleName) || moduleName; + } + } + + return null; + } + + getModulePaths(from) { + const cachedModule = this._modulePathCache.get(from); + + if (cachedModule) { + return cachedModule; + } + + const moduleDirectory = this._options.moduleDirectories; + const paths = (0, _nodeModulesPaths.default)(from, { + moduleDirectory + }); + + if (paths[paths.length - 1] === undefined) { + // circumvent node-resolve bug that adds `undefined` as last item. + paths.pop(); + } + + this._modulePathCache.set(from, paths); + + return paths; + } + + getModuleID(virtualMocks, from, _moduleName) { + const moduleName = _moduleName || ''; + const key = from + path().delimiter + moduleName; + + const cachedModuleID = this._moduleIDCache.get(key); + + if (cachedModuleID) { + return cachedModuleID; + } + + const moduleType = this._getModuleType(moduleName); + + const absolutePath = this._getAbsolutePath(virtualMocks, from, moduleName); + + const mockPath = this._getMockPath(from, moduleName); + + const sep = path().delimiter; + const id = + moduleType + + sep + + (absolutePath ? absolutePath + sep : '') + + (mockPath ? mockPath + sep : ''); + + this._moduleIDCache.set(key, id); + + return id; + } + + _getModuleType(moduleName) { + return this.isCoreModule(moduleName) ? 'node' : 'user'; + } + + _getAbsolutePath(virtualMocks, from, moduleName) { + if (this.isCoreModule(moduleName)) { + return moduleName; + } + + return this._isModuleResolved(from, moduleName) + ? this.getModule(moduleName) + : this._getVirtualMockPath(virtualMocks, from, moduleName); + } + + _getMockPath(from, moduleName) { + return !this.isCoreModule(moduleName) + ? this.getMockModule(from, moduleName) + : null; + } + + _getVirtualMockPath(virtualMocks, from, moduleName) { + const virtualMockPath = this.getModulePath(from, moduleName); + return virtualMocks.get(virtualMockPath) + ? virtualMockPath + : moduleName + ? this.resolveModule(from, moduleName) + : from; + } + + _isModuleResolved(from, moduleName) { + return !!( + this.getModule(moduleName) || this.getMockModule(from, moduleName) + ); + } + + resolveStubModuleName(from, moduleName) { + const dirname = path().dirname(from); + const paths = this._options.modulePaths; + + const extensions = this._options.extensions.slice(); + + const moduleDirectory = this._options.moduleDirectories; + const moduleNameMapper = this._options.moduleNameMapper; + const resolver = this._options.resolver; + const defaultPlatform = this._options.defaultPlatform; + + if (this._supportsNativePlatform) { + extensions.unshift( + ...this._options.extensions.map(ext => '.' + NATIVE_PLATFORM + ext) + ); + } + + if (defaultPlatform) { + extensions.unshift( + ...this._options.extensions.map(ext => '.' + defaultPlatform + ext) + ); + } + + if (moduleNameMapper) { + for (const {moduleName: mappedModuleName, regex} of moduleNameMapper) { + if (regex.test(moduleName)) { + // Note: once a moduleNameMapper matches the name, it must result + // in a module, or else an error is thrown. + const matches = moduleName.match(regex); + const mapModuleName = matches + ? moduleName => + moduleName.replace( + /\$([0-9]+)/g, + (_, index) => matches[parseInt(index, 10)] + ) + : moduleName => moduleName; + const possibleModuleNames = Array.isArray(mappedModuleName) + ? mappedModuleName + : [mappedModuleName]; + let module = null; + + for (const possibleModuleName of possibleModuleNames) { + const updatedName = mapModuleName(possibleModuleName); + module = + this.getModule(updatedName) || + Resolver.findNodeModule(updatedName, { + basedir: dirname, + extensions, + moduleDirectory, + paths, + resolver, + rootDir: this._options.rootDir + }); + + if (module) { + break; + } + } + + if (!module) { + throw createNoMappedModuleFoundError( + moduleName, + mapModuleName, + mappedModuleName, + regex, + resolver + ); + } + + return module; + } + } + } + + return null; + } +} + +_defineProperty(Resolver, 'ModuleNotFoundError', _ModuleNotFoundError.default); + +_defineProperty(Resolver, 'unstable_shouldLoadAsEsm', _shouldLoadAsEsm.default); + +const createNoMappedModuleFoundError = ( + moduleName, + mapModuleName, + mappedModuleName, + regex, + resolver +) => { + const mappedAs = Array.isArray(mappedModuleName) + ? JSON.stringify(mappedModuleName.map(mapModuleName), null, 2) + : mappedModuleName; + const original = Array.isArray(mappedModuleName) + ? JSON.stringify(mappedModuleName, null, 6) // using 6 because of misalignment when nested below + .slice(0, -1) + ' ]' /// align last bracket correctly as well + : mappedModuleName; + const error = new Error( + _chalk().default.red(`${_chalk().default.bold('Configuration error')}: + +Could not locate module ${_chalk().default.bold(moduleName)} mapped as: +${_chalk().default.bold(mappedAs)}. + +Please check your configuration for these entries: +{ + "moduleNameMapper": { + "${regex.toString()}": "${_chalk().default.bold(original)}" + }, + "resolver": ${_chalk().default.bold(String(resolver))} +}`) + ); + error.name = ''; + return error; +}; + +var _default = Resolver; +exports.default = _default; diff --git a/packages/jest-resolve/build/isBuiltinModule.d.ts b/packages/jest-resolve/build/isBuiltinModule.d.ts new file mode 100644 index 000000000000..70ede3ee9caa --- /dev/null +++ b/packages/jest-resolve/build/isBuiltinModule.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function isBuiltinModule(module: string): boolean; diff --git a/packages/jest-resolve/build/isBuiltinModule.js b/packages/jest-resolve/build/isBuiltinModule.js new file mode 100644 index 000000000000..8569160fe5c8 --- /dev/null +++ b/packages/jest-resolve/build/isBuiltinModule.js @@ -0,0 +1,39 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = isBuiltinModule; + +function _module() { + const data = _interopRequireDefault(require('module')); + + _module = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 EXPERIMENTAL_MODULES = ['worker_threads']; +const BUILTIN_MODULES = new Set( + _module().default.builtinModules + ? _module().default.builtinModules.concat(EXPERIMENTAL_MODULES) + : Object.keys(process.binding('natives')) + .filter(module => !/^internal\//.test(module)) + .concat(EXPERIMENTAL_MODULES) +); + +function isBuiltinModule(module) { + return BUILTIN_MODULES.has(module); +} diff --git a/packages/jest-resolve/build/nodeModulesPaths.d.ts b/packages/jest-resolve/build/nodeModulesPaths.d.ts new file mode 100644 index 000000000000..0c3773152039 --- /dev/null +++ b/packages/jest-resolve/build/nodeModulesPaths.d.ts @@ -0,0 +1,15 @@ +/** + * 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. + * + * Adapted from: https://github.com/substack/node-resolve + */ +import type { Config } from '@jest/types'; +declare type NodeModulesPathsOptions = { + moduleDirectory?: Array; + paths?: Array; +}; +export default function nodeModulesPaths(basedir: Config.Path, options: NodeModulesPathsOptions): Array; +export {}; diff --git a/packages/jest-resolve/build/nodeModulesPaths.js b/packages/jest-resolve/build/nodeModulesPaths.js new file mode 100644 index 000000000000..c55b43a737ea --- /dev/null +++ b/packages/jest-resolve/build/nodeModulesPaths.js @@ -0,0 +1,128 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = nodeModulesPaths; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + * + * Adapted from: https://github.com/substack/node-resolve + */ +function nodeModulesPaths(basedir, options) { + const modules = + options && options.moduleDirectory + ? Array.from(options.moduleDirectory) + : ['node_modules']; // ensure that `basedir` is an absolute path at this point, + // resolving against the process' current working directory + + const basedirAbs = path().resolve(basedir); + let prefix = '/'; + + if (/^([A-Za-z]:)/.test(basedirAbs)) { + prefix = ''; + } else if (/^\\\\/.test(basedirAbs)) { + prefix = '\\\\'; + } // The node resolution algorithm (as implemented by NodeJS and TypeScript) + // traverses parents of the physical path, not the symlinked path + + let physicalBasedir; + + try { + physicalBasedir = (0, _jestUtil().tryRealpath)(basedirAbs); + } catch { + // realpath can throw, e.g. on mapped drives + physicalBasedir = basedirAbs; + } + + const paths = [physicalBasedir]; + let parsed = path().parse(physicalBasedir); + + while (parsed.dir !== paths[paths.length - 1]) { + paths.push(parsed.dir); + parsed = path().parse(parsed.dir); + } + + const dirs = paths + .reduce( + (dirs, aPath) => + dirs.concat( + modules.map(moduleDir => + path().isAbsolute(moduleDir) + ? aPath === basedirAbs + ? moduleDir + : '' + : path().join(prefix, aPath, moduleDir) + ) + ), + [] + ) + .filter(dir => dir !== ''); + return options.paths ? dirs.concat(options.paths) : dirs; +} diff --git a/packages/jest-resolve/build/shouldLoadAsEsm.d.ts b/packages/jest-resolve/build/shouldLoadAsEsm.d.ts new file mode 100644 index 000000000000..dcb89194701f --- /dev/null +++ b/packages/jest-resolve/build/shouldLoadAsEsm.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export declare function clearCachedLookups(): void; +export default function cachedShouldLoadAsEsm(path: Config.Path, extensionsToTreatAsEsm: Array): boolean; diff --git a/packages/jest-resolve/build/shouldLoadAsEsm.js b/packages/jest-resolve/build/shouldLoadAsEsm.js new file mode 100644 index 000000000000..22c8118dce19 --- /dev/null +++ b/packages/jest-resolve/build/shouldLoadAsEsm.js @@ -0,0 +1,141 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.clearCachedLookups = clearCachedLookups; +exports.default = cachedShouldLoadAsEsm; + +function _path() { + const data = require('path'); + + _path = function () { + return data; + }; + + return data; +} + +function _vm() { + const data = require('vm'); + + _vm = function () { + return data; + }; + + return data; +} + +function _sync() { + const data = _interopRequireDefault(require('escalade/sync')); + + _sync = function () { + return data; + }; + + return data; +} + +function _gracefulFs() { + const data = require('graceful-fs'); + + _gracefulFs = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +// @ts-expect-error: experimental, not added to the types +const runtimeSupportsVmModules = typeof _vm().SyntheticModule === 'function'; +const cachedFileLookups = new Map(); +const cachedDirLookups = new Map(); +const cachedChecks = new Map(); + +function clearCachedLookups() { + cachedFileLookups.clear(); + cachedDirLookups.clear(); + cachedChecks.clear(); +} + +function cachedShouldLoadAsEsm(path, extensionsToTreatAsEsm) { + if (!runtimeSupportsVmModules) { + return false; + } + + let cachedLookup = cachedFileLookups.get(path); + + if (cachedLookup === undefined) { + cachedLookup = shouldLoadAsEsm(path, extensionsToTreatAsEsm); + cachedFileLookups.set(path, cachedLookup); + } + + return cachedLookup; +} // this is a bad version of what https://github.com/nodejs/modules/issues/393 would provide + +function shouldLoadAsEsm(path, extensionsToTreatAsEsm) { + const extension = (0, _path().extname)(path); + + if (extension === '.mjs') { + return true; + } + + if (extension === '.cjs') { + return false; + } + + if (extension !== '.js') { + return extensionsToTreatAsEsm.includes(extension); + } + + const cwd = (0, _path().dirname)(path); + let cachedLookup = cachedDirLookups.get(cwd); + + if (cachedLookup === undefined) { + cachedLookup = cachedPkgCheck(cwd); + cachedFileLookups.set(cwd, cachedLookup); + } + + return cachedLookup; +} + +function cachedPkgCheck(cwd) { + const pkgPath = (0, _sync().default)(cwd, (_dir, names) => { + if (names.includes('package.json')) { + // will be resolved into absolute + return 'package.json'; + } + + return false; + }); + + if (!pkgPath) { + return false; + } + + let hasModuleField = cachedChecks.get(pkgPath); + + if (hasModuleField != null) { + return hasModuleField; + } + + try { + const pkg = JSON.parse((0, _gracefulFs().readFileSync)(pkgPath, 'utf-8')); + hasModuleField = pkg.type === 'module'; + } catch { + hasModuleField = false; + } + + cachedChecks.set(pkgPath, hasModuleField); + return hasModuleField; +} diff --git a/packages/jest-resolve/build/types.d.ts b/packages/jest-resolve/build/types.d.ts new file mode 100644 index 000000000000..cb5428c5169e --- /dev/null +++ b/packages/jest-resolve/build/types.d.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export declare type ResolverConfig = { + defaultPlatform?: string | null; + extensions: Array; + hasCoreModules: boolean; + moduleDirectories: Array; + moduleNameMapper?: Array | null; + modulePaths?: Array; + platforms?: Array; + resolver?: Config.Path | null; + rootDir: Config.Path; +}; +declare type ModuleNameMapperConfig = { + regex: RegExp; + moduleName: string | Array; +}; +export {}; diff --git a/packages/jest-resolve/build/types.js b/packages/jest-resolve/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-resolve/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-runner/build/index.d.ts b/packages/jest-runner/build/index.d.ts new file mode 100644 index 000000000000..19b273db4369 --- /dev/null +++ b/packages/jest-runner/build/index.d.ts @@ -0,0 +1,25 @@ +/** + * 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. + */ +import Emittery = require('emittery'); +import type { Config } from '@jest/types'; +import type { OnTestFailure, OnTestStart, OnTestSuccess, Test, TestEvents, TestRunnerContext, TestRunnerOptions, TestWatcher } from './types'; +export type { Test, OnTestFailure, OnTestStart, OnTestSuccess, TestWatcher, TestRunnerContext, TestRunnerOptions, TestFileEvent, } from './types'; +export default class TestRunner { + private readonly _globalConfig; + private readonly _context; + private readonly eventEmitter; + readonly __PRIVATE_UNSTABLE_API_supportsEventEmitters__: boolean; + readonly isSerial?: boolean; + constructor(globalConfig: Config.GlobalConfig, context?: TestRunnerContext); + runTests(tests: Array, watcher: TestWatcher, onStart: OnTestStart | undefined, onResult: OnTestSuccess | undefined, onFailure: OnTestFailure | undefined, options: TestRunnerOptions): Promise; + private _createInBandTestRun; + private _createParallelTestRun; + on: { + (eventName: Name, listener: (eventData: TestEvents[Name]) => void | Promise): Emittery.UnsubscribeFn; + (eventName: typeof Emittery.listenerAdded | typeof Emittery.listenerRemoved, listener: (eventData: Emittery.ListenerChangedData) => void | Promise): Emittery.UnsubscribeFn; + }; +} diff --git a/packages/jest-runner/build/index.js b/packages/jest-runner/build/index.js new file mode 100644 index 000000000000..d56702214176 --- /dev/null +++ b/packages/jest-runner/build/index.js @@ -0,0 +1,327 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _emittery() { + const data = _interopRequireDefault(require('emittery')); + + _emittery = function () { + return data; + }; + + return data; +} + +function _exit() { + const data = _interopRequireDefault(require('exit')); + + _exit = function () { + return data; + }; + + return data; +} + +function _throat() { + const data = _interopRequireDefault(require('throat')); + + _throat = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _jestWorker() { + const data = require('jest-worker'); + + _jestWorker = function () { + return data; + }; + + return data; +} + +var _runTest = _interopRequireDefault(require('./runTest')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const TEST_WORKER_PATH = require.resolve('./testWorker'); + +class TestRunner { + constructor(globalConfig, context) { + _defineProperty(this, '_globalConfig', void 0); + + _defineProperty(this, '_context', void 0); + + _defineProperty(this, 'eventEmitter', new (_emittery().default)()); + + _defineProperty( + this, + '__PRIVATE_UNSTABLE_API_supportsEventEmitters__', + true + ); + + _defineProperty(this, 'isSerial', void 0); + + _defineProperty(this, 'on', this.eventEmitter.on.bind(this.eventEmitter)); + + this._globalConfig = globalConfig; + this._context = context || {}; + } + + async runTests(tests, watcher, onStart, onResult, onFailure, options) { + return await (options.serial + ? this._createInBandTestRun(tests, watcher, onStart, onResult, onFailure) + : this._createParallelTestRun( + tests, + watcher, + onStart, + onResult, + onFailure + )); + } + + async _createInBandTestRun(tests, watcher, onStart, onResult, onFailure) { + process.env.JEST_WORKER_ID = '1'; + const mutex = (0, _throat().default)(1); + return tests.reduce( + (promise, test) => + mutex(() => + promise + .then(async () => { + if (watcher.isInterrupted()) { + throw new CancelRun(); + } + + let sendMessageToJest; // Remove `if(onStart)` in Jest 27 + + if (onStart) { + await onStart(test); + return (0, _runTest.default)( + test.path, + this._globalConfig, + test.context.config, + test.context.resolver, + this._context, + undefined + ); + } else { + // `deepCyclicCopy` used here to avoid mem-leak + sendMessageToJest = (eventName, args) => + this.eventEmitter.emit( + eventName, + (0, _jestUtil().deepCyclicCopy)(args, { + keepPrototype: false + }) + ); + + await this.eventEmitter.emit('test-file-start', [test]); + return (0, _runTest.default)( + test.path, + this._globalConfig, + test.context.config, + test.context.resolver, + this._context, + sendMessageToJest + ); + } + }) + .then(result => { + if (onResult) { + return onResult(test, result); + } else { + return this.eventEmitter.emit('test-file-success', [ + test, + result + ]); + } + }) + .catch(err => { + if (onFailure) { + return onFailure(test, err); + } else { + return this.eventEmitter.emit('test-file-failure', [test, err]); + } + }) + ), + Promise.resolve() + ); + } + + async _createParallelTestRun(tests, watcher, onStart, onResult, onFailure) { + const resolvers = new Map(); + + for (const test of tests) { + if (!resolvers.has(test.context.config.name)) { + resolvers.set(test.context.config.name, { + config: test.context.config, + serializableModuleMap: test.context.moduleMap.toJSON() + }); + } + } + + const worker = new (_jestWorker().Worker)(TEST_WORKER_PATH, { + exposedMethods: ['worker'], + forkOptions: { + // use advanced serialization in order to transfer objects with circular references + // @ts-expect-error: option does not exist on the node 10 types + serialization: 'advanced', + stdio: 'pipe' + }, + maxRetries: 3, + numWorkers: this._globalConfig.maxWorkers, + setupArgs: [ + { + serializableResolvers: Array.from(resolvers.values()) + } + ] + }); + if (worker.getStdout()) worker.getStdout().pipe(process.stdout); + if (worker.getStderr()) worker.getStderr().pipe(process.stderr); + const mutex = (0, _throat().default)(this._globalConfig.maxWorkers); // Send test suites to workers continuously instead of all at once to track + // the start time of individual tests. + + const runTestInWorker = test => + mutex(async () => { + if (watcher.isInterrupted()) { + return Promise.reject(); + } // Remove `if(onStart)` in Jest 27 + + if (onStart) { + await onStart(test); + } else { + await this.eventEmitter.emit('test-file-start', [test]); + } + + const promise = worker.worker({ + config: test.context.config, + context: { + ...this._context, + changedFiles: + this._context.changedFiles && + Array.from(this._context.changedFiles), + sourcesRelatedToTestsInChangedFiles: + this._context.sourcesRelatedToTestsInChangedFiles && + Array.from(this._context.sourcesRelatedToTestsInChangedFiles) + }, + globalConfig: this._globalConfig, + path: test.path + }); + + if (promise.UNSTABLE_onCustomMessage) { + // TODO: Get appropriate type for `onCustomMessage` + promise.UNSTABLE_onCustomMessage(([event, payload]) => { + this.eventEmitter.emit(event, payload); + }); + } + + return promise; + }); + + const onError = async (err, test) => { + // Remove `if(onFailure)` in Jest 27 + if (onFailure) { + await onFailure(test, err); + } else { + await this.eventEmitter.emit('test-file-failure', [test, err]); + } + + if (err.type === 'ProcessTerminatedError') { + console.error( + 'A worker process has quit unexpectedly! ' + + 'Most likely this is an initialization error.' + ); + (0, _exit().default)(1); + } + }; + + const onInterrupt = new Promise((_, reject) => { + watcher.on('change', state => { + if (state.interrupted) { + reject(new CancelRun()); + } + }); + }); + const runAllTests = Promise.all( + tests.map(test => + runTestInWorker(test) + .then(result => { + if (onResult) { + return onResult(test, result); + } else { + return this.eventEmitter.emit('test-file-success', [ + test, + result + ]); + } + }) + .catch(error => onError(error, test)) + ) + ); + + const cleanup = async () => { + const {forceExited} = await worker.end(); + + if (forceExited) { + console.error( + _chalk().default.yellow( + 'A worker process has failed to exit gracefully and has been force exited. ' + + 'This is likely caused by tests leaking due to improper teardown. ' + + 'Try running with --detectOpenHandles to find leaks.' + ) + ); + } + }; + + return Promise.race([runAllTests, onInterrupt]).then(cleanup, cleanup); + } +} + +exports.default = TestRunner; + +class CancelRun extends Error { + constructor(message) { + super(message); + this.name = 'CancelRun'; + } +} diff --git a/packages/jest-runner/build/runTest.d.ts b/packages/jest-runner/build/runTest.d.ts new file mode 100644 index 000000000000..944b500d8031 --- /dev/null +++ b/packages/jest-runner/build/runTest.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + * + */ +import type { TestResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import type Resolver from 'jest-resolve'; +import type { TestFileEvent, TestRunnerContext } from './types'; +export default function runTest(path: Config.Path, globalConfig: Config.GlobalConfig, config: Config.ProjectConfig, resolver: Resolver, context?: TestRunnerContext, sendMessageToJest?: TestFileEvent): Promise; diff --git a/packages/jest-runner/build/runTest.js b/packages/jest-runner/build/runTest.js new file mode 100644 index 000000000000..16f66a5c6125 --- /dev/null +++ b/packages/jest-runner/build/runTest.js @@ -0,0 +1,487 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = runTest; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _sourceMapSupport() { + const data = _interopRequireDefault(require('source-map-support')); + + _sourceMapSupport = function () { + return data; + }; + + return data; +} + +function _console() { + const data = require('@jest/console'); + + _console = function () { + return data; + }; + + return data; +} + +function _transform() { + const data = require('@jest/transform'); + + _transform = function () { + return data; + }; + + return data; +} + +function _jestConfig() { + const data = require('jest-config'); + + _jestConfig = function () { + return data; + }; + + return data; +} + +function docblock() { + const data = _interopRequireWildcard(require('jest-docblock')); + + docblock = function () { + return data; + }; + + return data; +} + +function _jestLeakDetector() { + const data = _interopRequireDefault(require('jest-leak-detector')); + + _jestLeakDetector = function () { + return data; + }; + + return data; +} + +function _jestMessageUtil() { + const data = require('jest-message-util'); + + _jestMessageUtil = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + * + */ +function freezeConsole(testConsole, config) { + // @ts-expect-error: `_log` is `private` - we should figure out some proper API here + testConsole._log = function fakeConsolePush(_type, message) { + const error = new (_jestUtil().ErrorWithStack)( + `${_chalk().default.red( + `${_chalk().default.bold( + 'Cannot log after tests are done.' + )} Did you forget to wait for something async in your test?` + )}\nAttempted to log "${message}".`, + fakeConsolePush + ); + const formattedError = (0, _jestMessageUtil().formatExecError)( + error, + config, + { + noStackTrace: false + }, + undefined, + true + ); + process.stderr.write('\n' + formattedError + '\n'); + process.exitCode = 1; + }; +} // Keeping the core of "runTest" as a separate function (as "runTestInternal") +// is key to be able to detect memory leaks. Since all variables are local to +// the function, when "runTestInternal" finishes its execution, they can all be +// freed, UNLESS something else is leaking them (and that's why we can detect +// the leak!). +// +// If we had all the code in a single function, we should manually nullify all +// references to verify if there is a leak, which is not maintainable and error +// prone. That's why "runTestInternal" CANNOT be inlined inside "runTest". + +async function runTestInternal( + path, + globalConfig, + config, + resolver, + context, + sendMessageToJest +) { + const testSource = fs().readFileSync(path, 'utf8'); + const docblockPragmas = docblock().parse(docblock().extract(testSource)); + const customEnvironment = docblockPragmas['jest-environment']; + let testEnvironment = config.testEnvironment; + + if (customEnvironment) { + if (Array.isArray(customEnvironment)) { + throw new Error( + `You can only define a single test environment through docblocks, got "${customEnvironment.join( + ', ' + )}"` + ); + } + + testEnvironment = (0, _jestConfig().getTestEnvironment)({ + ...config, + testEnvironment: customEnvironment + }); + } + + const transformer = new (_transform().ScriptTransformer)(config); + const TestEnvironment = (0, _jestUtil().interopRequireDefault)( + transformer.requireAndTranspileModule(testEnvironment) + ).default; + const testFramework = (0, _jestUtil().interopRequireDefault)( + transformer.requireAndTranspileModule( + process.env.JEST_JASMINE === '1' ? 'jest-jasmine2' : config.testRunner + ) + ).default; + const Runtime = (0, _jestUtil().interopRequireDefault)( + config.moduleLoader ? require(config.moduleLoader) : require('jest-runtime') + ).default; + const consoleOut = globalConfig.useStderr ? process.stderr : process.stdout; + + const consoleFormatter = (type, message) => + (0, _console().getConsoleOutput)( + // 4 = the console call is buried 4 stack frames deep + _console().BufferedConsole.write([], type, message, 4), + config, + globalConfig + ); + + let testConsole; + + if (globalConfig.silent) { + testConsole = new (_console().NullConsole)( + consoleOut, + consoleOut, + consoleFormatter + ); + } else if (globalConfig.verbose) { + testConsole = new (_console().CustomConsole)( + consoleOut, + consoleOut, + consoleFormatter + ); + } else { + testConsole = new (_console().BufferedConsole)(); + } + + const environment = new TestEnvironment(config, { + console: testConsole, + docblockPragmas, + testPath: path + }); + const leakDetector = config.detectLeaks + ? new (_jestLeakDetector().default)(environment) + : null; + const cacheFS = new Map([[path, testSource]]); + (0, _jestUtil().setGlobal)(environment.global, 'console', testConsole); + const runtime = new Runtime( + config, + environment, + resolver, + cacheFS, + { + changedFiles: + context === null || context === void 0 ? void 0 : context.changedFiles, + collectCoverage: globalConfig.collectCoverage, + collectCoverageFrom: globalConfig.collectCoverageFrom, + collectCoverageOnlyFrom: globalConfig.collectCoverageOnlyFrom, + coverageProvider: globalConfig.coverageProvider, + sourcesRelatedToTestsInChangedFiles: + context === null || context === void 0 + ? void 0 + : context.sourcesRelatedToTestsInChangedFiles + }, + path + ); + const start = Date.now(); + + for (const path of config.setupFiles) { + const esm = runtime.unstable_shouldLoadAsEsm(path); + + if (esm) { + await runtime.unstable_importModule(path); + } else { + runtime.requireModule(path); + } + } + + const sourcemapOptions = { + environment: 'node', + handleUncaughtExceptions: false, + retrieveSourceMap: source => { + var _runtime$getSourceMap; + + const sourceMapSource = + (_runtime$getSourceMap = runtime.getSourceMaps()) === null || + _runtime$getSourceMap === void 0 + ? void 0 + : _runtime$getSourceMap.get(source); + + if (sourceMapSource) { + try { + return { + map: JSON.parse(fs().readFileSync(sourceMapSource, 'utf8')), + url: source + }; + } catch {} + } + + return null; + } + }; // For tests + + runtime + .requireInternalModule( + require.resolve('source-map-support'), + 'source-map-support' + ) + .install(sourcemapOptions); // For runtime errors + + _sourceMapSupport().default.install(sourcemapOptions); + + if ( + environment.global && + environment.global.process && + environment.global.process.exit + ) { + const realExit = environment.global.process.exit; + + environment.global.process.exit = function exit(...args) { + const error = new (_jestUtil().ErrorWithStack)( + `process.exit called with "${args.join(', ')}"`, + exit + ); + const formattedError = (0, _jestMessageUtil().formatExecError)( + error, + config, + { + noStackTrace: false + }, + undefined, + true + ); + process.stderr.write(formattedError); + return realExit(...args); + }; + } // if we don't have `getVmContext` on the env skip coverage + + const collectV8Coverage = + globalConfig.coverageProvider === 'v8' && + typeof environment.getVmContext === 'function'; + + try { + await environment.setup(); + let result; + + try { + if (collectV8Coverage) { + await runtime.collectV8Coverage(); + } + + result = await testFramework( + globalConfig, + config, + environment, + runtime, + path, + sendMessageToJest + ); + } catch (err) { + // Access stack before uninstalling sourcemaps + err.stack; + throw err; + } finally { + if (collectV8Coverage) { + await runtime.stopCollectingV8Coverage(); + } + } + + freezeConsole(testConsole, config); + const testCount = + result.numPassingTests + + result.numFailingTests + + result.numPendingTests + + result.numTodoTests; + const end = Date.now(); + const testRuntime = end - start; + result.perfStats = { + end, + runtime: testRuntime, + slow: testRuntime / 1000 > config.slowTestThreshold, + start + }; + result.testFilePath = path; + result.console = testConsole.getBuffer(); + result.skipped = testCount === result.numPendingTests; + result.displayName = config.displayName; + const coverage = runtime.getAllCoverageInfoCopy(); + + if (coverage) { + const coverageKeys = Object.keys(coverage); + + if (coverageKeys.length) { + result.coverage = coverage; + } + } + + if (collectV8Coverage) { + const v8Coverage = runtime.getAllV8CoverageInfoCopy(); + + if (v8Coverage && v8Coverage.length > 0) { + result.v8Coverage = v8Coverage; + } + } + + if (globalConfig.logHeapUsage) { + if (global.gc) { + global.gc(); + } + + result.memoryUsage = process.memoryUsage().heapUsed; + } // Delay the resolution to allow log messages to be output. + + return new Promise(resolve => { + setImmediate(() => + resolve({ + leakDetector, + result + }) + ); + }); + } finally { + var _runtime$teardown; + + await environment.teardown(); // TODO: this function might be missing, remove ? in Jest 26 + + (_runtime$teardown = runtime.teardown) === null || + _runtime$teardown === void 0 + ? void 0 + : _runtime$teardown.call(runtime); + + _sourceMapSupport().default.resetRetrieveHandlers(); + } +} + +async function runTest( + path, + globalConfig, + config, + resolver, + context, + sendMessageToJest +) { + const {leakDetector, result} = await runTestInternal( + path, + globalConfig, + config, + resolver, + context, + sendMessageToJest + ); + + if (leakDetector) { + // We wanna allow a tiny but time to pass to allow last-minute cleanup + await new Promise(resolve => setTimeout(resolve, 100)); // Resolve leak detector, outside the "runTestInternal" closure. + + result.leaks = await leakDetector.isLeaking(); + } else { + result.leaks = false; + } + + return result; +} diff --git a/packages/jest-runner/build/testWorker.d.ts b/packages/jest-runner/build/testWorker.d.ts new file mode 100644 index 000000000000..a74bb88eb4b8 --- /dev/null +++ b/packages/jest-runner/build/testWorker.d.ts @@ -0,0 +1,26 @@ +/** + * 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. + * + */ +import type { TestResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import { SerializableModuleMap } from 'jest-haste-map'; +import type { TestRunnerSerializedContext } from './types'; +export declare type SerializableResolver = { + config: Config.ProjectConfig; + serializableModuleMap: SerializableModuleMap; +}; +declare type WorkerData = { + config: Config.ProjectConfig; + globalConfig: Config.GlobalConfig; + path: Config.Path; + context?: TestRunnerSerializedContext; +}; +export declare function setup(setupData: { + serializableResolvers: Array; +}): void; +export declare function worker({ config, globalConfig, path, context, }: WorkerData): Promise; +export {}; diff --git a/packages/jest-runner/build/testWorker.js b/packages/jest-runner/build/testWorker.js new file mode 100644 index 000000000000..7bb2e9202d0c --- /dev/null +++ b/packages/jest-runner/build/testWorker.js @@ -0,0 +1,148 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.setup = setup; +exports.worker = worker; + +function _exit() { + const data = _interopRequireDefault(require('exit')); + + _exit = function () { + return data; + }; + + return data; +} + +function _jestHasteMap() { + const data = require('jest-haste-map'); + + _jestHasteMap = function () { + return data; + }; + + return data; +} + +function _jestMessageUtil() { + const data = require('jest-message-util'); + + _jestMessageUtil = function () { + return data; + }; + + return data; +} + +function _jestRuntime() { + const data = _interopRequireDefault(require('jest-runtime')); + + _jestRuntime = function () { + return data; + }; + + return data; +} + +function _jestWorker() { + const data = require('jest-worker'); + + _jestWorker = function () { + return data; + }; + + return data; +} + +var _runTest = _interopRequireDefault(require('./runTest')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + * + */ +// Make sure uncaught errors are logged before we exit. +process.on('uncaughtException', err => { + console.error(err.stack); + (0, _exit().default)(1); +}); + +const formatError = error => { + if (typeof error === 'string') { + const {message, stack} = (0, _jestMessageUtil().separateMessageFromStack)( + error + ); + return { + message, + stack, + type: 'Error' + }; + } + + return { + code: error.code || undefined, + message: error.message, + stack: error.stack, + type: 'Error' + }; +}; + +const resolvers = new Map(); + +const getResolver = config => { + const resolver = resolvers.get(config.name); + + if (!resolver) { + throw new Error('Cannot find resolver for: ' + config.name); + } + + return resolver; +}; + +function setup(setupData) { + // Module maps that will be needed for the test runs are passed. + for (const { + config, + serializableModuleMap + } of setupData.serializableResolvers) { + const moduleMap = _jestHasteMap().ModuleMap.fromJSON(serializableModuleMap); + + resolvers.set( + config.name, + _jestRuntime().default.createResolver(config, moduleMap) + ); + } +} + +const sendMessageToJest = (eventName, args) => { + (0, _jestWorker().messageParent)([eventName, args]); +}; + +async function worker({config, globalConfig, path, context}) { + try { + return await (0, _runTest.default)( + path, + globalConfig, + config, + getResolver(config), + context && { + ...context, + changedFiles: context.changedFiles && new Set(context.changedFiles), + sourcesRelatedToTestsInChangedFiles: + context.sourcesRelatedToTestsInChangedFiles && + new Set(context.sourcesRelatedToTestsInChangedFiles) + }, + sendMessageToJest + ); + } catch (error) { + throw formatError(error); + } +} diff --git a/packages/jest-runner/build/types.d.ts b/packages/jest-runner/build/types.d.ts new file mode 100644 index 000000000000..62bd4eae2357 --- /dev/null +++ b/packages/jest-runner/build/types.d.ts @@ -0,0 +1,61 @@ +/** + * 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. + */ +import Emittery = require('emittery'); +import type { JestEnvironment } from '@jest/environment'; +import type { AssertionResult, SerializableError, TestResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +import type { FS as HasteFS, ModuleMap } from 'jest-haste-map'; +import type Resolver from 'jest-resolve'; +import type RuntimeType from 'jest-runtime'; +export declare type ErrorWithCode = Error & { + code?: string; +}; +export declare type Test = { + context: Context; + duration?: number; + path: Config.Path; +}; +export declare type Context = { + config: Config.ProjectConfig; + hasteFS: HasteFS; + moduleMap: ModuleMap; + resolver: Resolver; +}; +export declare type OnTestStart = (test: Test) => Promise; +export declare type OnTestFailure = (test: Test, serializableError: SerializableError) => Promise; +export declare type OnTestSuccess = (test: Test, testResult: TestResult) => Promise; +export declare type TestEvents = { + 'test-file-start': [Test]; + 'test-file-success': [Test, TestResult]; + 'test-file-failure': [Test, SerializableError]; + 'test-case-result': [Config.Path, AssertionResult]; +}; +export declare type TestFileEvent = (eventName: T, args: TestEvents[T]) => unknown; +export declare type TestFramework = (globalConfig: Config.GlobalConfig, config: Config.ProjectConfig, environment: JestEnvironment, runtime: RuntimeType, testPath: string, sendMessageToJest?: TestFileEvent) => Promise; +export declare type TestRunnerOptions = { + serial: boolean; +}; +export declare type TestRunnerContext = { + changedFiles?: Set; + sourcesRelatedToTestsInChangedFiles?: Set; +}; +export declare type TestRunnerSerializedContext = { + changedFiles?: Array; + sourcesRelatedToTestsInChangedFiles?: Array; +}; +declare type WatcherState = { + interrupted: boolean; +}; +export interface TestWatcher extends Emittery<{ + change: WatcherState; +}> { + state: WatcherState; + setState(state: WatcherState): void; + isInterrupted(): boolean; + isWatchMode(): boolean; +} +export {}; diff --git a/packages/jest-runner/build/types.js b/packages/jest-runner/build/types.js new file mode 100644 index 000000000000..9ddbe87fd766 --- /dev/null +++ b/packages/jest-runner/build/types.js @@ -0,0 +1,15 @@ +'use strict'; + +function _emittery() { + const data = _interopRequireDefault(require('emittery')); + + _emittery = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} diff --git a/packages/jest-runtime/build/helpers.d.ts b/packages/jest-runtime/build/helpers.d.ts new file mode 100644 index 000000000000..be13d0c5c580 --- /dev/null +++ b/packages/jest-runtime/build/helpers.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export declare const createOutsideJestVmPath: (path: string) => string; +export declare const decodePossibleOutsideJestVmPath: (outsideJestVmPath: string) => string | undefined; +export declare const findSiblingsWithFileExtension: (moduleFileExtensions: Config.ProjectConfig['moduleFileExtensions'], from: Config.Path, moduleName: string) => string; diff --git a/packages/jest-runtime/build/helpers.js b/packages/jest-runtime/build/helpers.js new file mode 100644 index 000000000000..bd3f19c957fe --- /dev/null +++ b/packages/jest-runtime/build/helpers.js @@ -0,0 +1,154 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.findSiblingsWithFileExtension = exports.decodePossibleOutsideJestVmPath = exports.createOutsideJestVmPath = void 0; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _glob() { + const data = _interopRequireDefault(require('glob')); + + _glob = function () { + return data; + }; + + return data; +} + +function _slash() { + const data = _interopRequireDefault(require('slash')); + + _slash = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 OUTSIDE_JEST_VM_PROTOCOL = 'jest-main:'; // String manipulation is easier here, fileURLToPath is only in newer Nodes, +// plus setting non-standard protocols on URL objects is difficult. + +const createOutsideJestVmPath = path => + OUTSIDE_JEST_VM_PROTOCOL + '//' + encodeURIComponent(path); + +exports.createOutsideJestVmPath = createOutsideJestVmPath; + +const decodePossibleOutsideJestVmPath = outsideJestVmPath => { + if (outsideJestVmPath.startsWith(OUTSIDE_JEST_VM_PROTOCOL)) { + return decodeURIComponent( + outsideJestVmPath.replace( + new RegExp('^' + OUTSIDE_JEST_VM_PROTOCOL + '//'), + '' + ) + ); + } + + return undefined; +}; + +exports.decodePossibleOutsideJestVmPath = decodePossibleOutsideJestVmPath; + +const findSiblingsWithFileExtension = ( + moduleFileExtensions, + from, + moduleName +) => { + if (!path().isAbsolute(moduleName) && path().extname(moduleName) === '') { + const dirname = path().dirname(from); + const pathToModule = path().resolve(dirname, moduleName); + + try { + const slashedDirname = (0, _slash().default)(dirname); + + const matches = _glob() + .default.sync(`${pathToModule}.*`) + .map(match => (0, _slash().default)(match)) + .map(match => { + const relativePath = path().posix.relative(slashedDirname, match); + return path().posix.dirname(match) === slashedDirname + ? `./${relativePath}` + : relativePath; + }) + .map(match => `\t'${match}'`) + .join('\n'); + + if (matches) { + const foundMessage = `\n\nHowever, Jest was able to find:\n${matches}`; + const mappedModuleFileExtensions = moduleFileExtensions + .map(ext => `'${ext}'`) + .join(', '); + return ( + foundMessage + + "\n\nYou might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently " + + `[${mappedModuleFileExtensions}].\n\nSee https://jestjs.io/docs/en/configuration#modulefileextensions-arraystring` + ); + } + } catch {} + } + + return ''; +}; + +exports.findSiblingsWithFileExtension = findSiblingsWithFileExtension; diff --git a/packages/jest-runtime/build/index.d.ts b/packages/jest-runtime/build/index.d.ts new file mode 100644 index 000000000000..ed335020ea1f --- /dev/null +++ b/packages/jest-runtime/build/index.d.ts @@ -0,0 +1,128 @@ +/** + * 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. + */ +import type { JestEnvironment } from '@jest/environment'; +import type * as JestGlobals from '@jest/globals'; +import type { SourceMapRegistry } from '@jest/source-map'; +import type { V8CoverageResult } from '@jest/test-result'; +import { CallerTransformOptions, ShouldInstrumentOptions, shouldInstrument } from '@jest/transform'; +import type { Config, Global } from '@jest/types'; +import HasteMap, { ModuleMap } from 'jest-haste-map'; +import Resolver from 'jest-resolve'; +import type { Context } from './types'; +export type { Context } from './types'; +interface JestGlobals extends Global.TestFrameworkGlobals { + expect: typeof JestGlobals.expect; +} +declare type HasteMapOptions = { + console?: Console; + maxWorkers: number; + resetCache: boolean; + watch?: boolean; + watchman: boolean; +}; +interface InternalModuleOptions extends Required { + isInternalModule: boolean; +} +export default class Runtime { + private readonly _cacheFS; + private readonly _config; + private readonly _coverageOptions; + private _currentlyExecutingModulePath; + private readonly _environment; + private readonly _explicitShouldMock; + private _fakeTimersImplementation; + private readonly _internalModuleRegistry; + private _isCurrentlyExecutingManualMock; + private _mainModule; + private readonly _mockFactories; + private readonly _mockMetaDataCache; + private _mockRegistry; + private _isolatedMockRegistry; + private readonly _moduleMocker; + private _isolatedModuleRegistry; + private _moduleRegistry; + private readonly _esmoduleRegistry; + private readonly _cjsNamedExports; + private readonly _testPath; + private readonly _resolver; + private _shouldAutoMock; + private readonly _shouldMockModuleCache; + private readonly _shouldUnmockTransitiveDependenciesCache; + private readonly _sourceMapRegistry; + private readonly _scriptTransformer; + private readonly _fileTransforms; + private _v8CoverageInstrumenter; + private _v8CoverageResult; + private readonly _transitiveShouldMock; + private _unmockList; + private readonly _virtualMocks; + private _moduleImplementation?; + private readonly jestObjectCaches; + private jestGlobals?; + constructor(config: Config.ProjectConfig, environment: JestEnvironment, resolver: Resolver, cacheFS: Map, coverageOptions: ShouldInstrumentOptions, testPath: Config.Path); + static shouldInstrument: typeof shouldInstrument; + static createContext(config: Config.ProjectConfig, options: { + console?: Console; + maxWorkers: number; + watch?: boolean; + watchman: boolean; + }): Promise; + static createHasteMap(config: Config.ProjectConfig, options?: HasteMapOptions): HasteMap; + static createResolver(config: Config.ProjectConfig, moduleMap: ModuleMap): Resolver; + static runCLI(): Promise; + static getCLIOptions(): never; + unstable_shouldLoadAsEsm(path: Config.Path): boolean; + private loadEsmModule; + private linkModules; + unstable_importModule(from: Config.Path, moduleName?: string): Promise; + private loadCjsAsEsm; + private getExportsOfCjs; + requireModule(from: Config.Path, moduleName?: string, options?: InternalModuleOptions, isRequireActual?: boolean | null): T; + requireInternalModule(from: Config.Path, to?: string): T; + requireActual(from: Config.Path, moduleName: string): T; + requireMock(from: Config.Path, moduleName: string): T; + private _loadModule; + private _getFullTransformationOptions; + requireModuleOrMock(from: Config.Path, moduleName: string): T; + isolateModules(fn: () => void): void; + resetModules(): void; + collectV8Coverage(): Promise; + stopCollectingV8Coverage(): Promise; + getAllCoverageInfoCopy(): JestEnvironment['global']['__coverage__']; + getAllV8CoverageInfoCopy(): V8CoverageResult; + getSourceMaps(): SourceMapRegistry; + setMock(from: string, moduleName: string, mockFactory: () => unknown, options?: { + virtual?: boolean; + }): void; + restoreAllMocks(): void; + resetAllMocks(): void; + clearAllMocks(): void; + teardown(): void; + private _resolveModule; + private _requireResolve; + private _requireResolvePaths; + private _execModule; + private transformFile; + private createScriptFromCode; + private _requireCoreModule; + private _importCoreModule; + private _getMockedNativeModule; + private _generateMock; + private _shouldMock; + private _createRequireImplementation; + private _createJestObjectFor; + private _logFormattedReferenceError; + private wrapCodeInModuleWrapper; + private constructModuleWrapperStart; + private constructInjectedModuleParameters; + private handleExecutionError; + private getGlobalsForCjs; + private getGlobalsForEsm; + private getGlobalsFromEnvironment; + private readFile; + setGlobalsForRuntime(globals: JestGlobals): void; +} diff --git a/packages/jest-runtime/build/index.js b/packages/jest-runtime/build/index.js new file mode 100644 index 000000000000..639762540aba --- /dev/null +++ b/packages/jest-runtime/build/index.js @@ -0,0 +1,2065 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function nativeModule() { + const data = _interopRequireWildcard(require('module')); + + nativeModule = function () { + return data; + }; + + return data; +} + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _url() { + const data = require('url'); + + _url = function () { + return data; + }; + + return data; +} + +function _vm() { + const data = require('vm'); + + _vm = function () { + return data; + }; + + return data; +} + +function _cjsModuleLexer() { + const data = require('cjs-module-lexer'); + + _cjsModuleLexer = function () { + return data; + }; + + return data; +} + +function _collectV8Coverage() { + const data = require('collect-v8-coverage'); + + _collectV8Coverage = function () { + return data; + }; + + return data; +} + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _stripBom() { + const data = _interopRequireDefault(require('strip-bom')); + + _stripBom = function () { + return data; + }; + + return data; +} + +function _fakeTimers() { + const data = require('@jest/fake-timers'); + + _fakeTimers = function () { + return data; + }; + + return data; +} + +function _transform() { + const data = require('@jest/transform'); + + _transform = function () { + return data; + }; + + return data; +} + +function _jestHasteMap() { + const data = _interopRequireDefault(require('jest-haste-map')); + + _jestHasteMap = function () { + return data; + }; + + return data; +} + +function _jestMessageUtil() { + const data = require('jest-message-util'); + + _jestMessageUtil = function () { + return data; + }; + + return data; +} + +function _jestRegexUtil() { + const data = require('jest-regex-util'); + + _jestRegexUtil = function () { + return data; + }; + + return data; +} + +function _jestResolve() { + const data = _interopRequireDefault(require('jest-resolve')); + + _jestResolve = function () { + return data; + }; + + return data; +} + +function _jestSnapshot() { + const data = _interopRequireDefault(require('jest-snapshot')); + + _jestSnapshot = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _helpers = require('./helpers'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const esmIsAvailable = typeof _vm().SourceTextModule === 'function'; +const defaultTransformOptions = { + isInternalModule: false, + supportsDynamicImport: esmIsAvailable, + supportsExportNamespaceFrom: false, + supportsStaticESM: false, + supportsTopLevelAwait: false +}; +// These are modules that we know +// * are safe to require from the outside (not stateful, not prone to errors passing in instances from different realms), and +// * take sufficiently long to require to warrant an optimization. +// When required from the outside, they use the worker's require cache and are thus +// only loaded once per worker, not once per test file. +// Use /benchmarks/test-file-overhead to measure the impact. +// Note that this only applies when they are required in an internal context; +// users who require one of these modules in their tests will still get the module from inside the VM. +// Prefer listing a module here only if it is impractical to use the jest-resolve-outside-vm-option where it is required, +// e.g. because there are many require sites spread across the dependency graph. +const INTERNAL_MODULE_REQUIRE_OUTSIDE_OPTIMIZED_MODULES = new Set(['chalk']); +const JEST_RESOLVE_OUTSIDE_VM_OPTION = Symbol.for( + 'jest-resolve-outside-vm-option' +); +const testTimeoutSymbol = Symbol.for('TEST_TIMEOUT_SYMBOL'); +const retryTimesSymbol = Symbol.for('RETRY_TIMES'); +const NODE_MODULES = path().sep + 'node_modules' + path().sep; + +const getModuleNameMapper = config => { + if ( + Array.isArray(config.moduleNameMapper) && + config.moduleNameMapper.length + ) { + return config.moduleNameMapper.map(([regex, moduleName]) => ({ + moduleName, + regex: new RegExp(regex) + })); + } + + return null; +}; + +const unmockRegExpCache = new WeakMap(); +const EVAL_RESULT_VARIABLE = 'Object.'; +const runtimeSupportsVmModules = typeof _vm().SyntheticModule === 'function'; + +const supportsTopLevelAwait = + runtimeSupportsVmModules && + (() => { + try { + // eslint-disable-next-line no-new + new (_vm().SourceTextModule)('await Promise.resolve()'); + return true; + } catch { + return false; + } + })(); + +class Runtime { + constructor( + config, + environment, + resolver, + cacheFS, + coverageOptions, + testPath + ) { + _defineProperty(this, '_cacheFS', void 0); + + _defineProperty(this, '_config', void 0); + + _defineProperty(this, '_coverageOptions', void 0); + + _defineProperty(this, '_currentlyExecutingModulePath', void 0); + + _defineProperty(this, '_environment', void 0); + + _defineProperty(this, '_explicitShouldMock', void 0); + + _defineProperty(this, '_fakeTimersImplementation', void 0); + + _defineProperty(this, '_internalModuleRegistry', void 0); + + _defineProperty(this, '_isCurrentlyExecutingManualMock', void 0); + + _defineProperty(this, '_mainModule', void 0); + + _defineProperty(this, '_mockFactories', void 0); + + _defineProperty(this, '_mockMetaDataCache', void 0); + + _defineProperty(this, '_mockRegistry', void 0); + + _defineProperty(this, '_isolatedMockRegistry', void 0); + + _defineProperty(this, '_moduleMocker', void 0); + + _defineProperty(this, '_isolatedModuleRegistry', void 0); + + _defineProperty(this, '_moduleRegistry', void 0); + + _defineProperty(this, '_esmoduleRegistry', void 0); + + _defineProperty(this, '_cjsNamedExports', void 0); + + _defineProperty(this, '_testPath', void 0); + + _defineProperty(this, '_resolver', void 0); + + _defineProperty(this, '_shouldAutoMock', void 0); + + _defineProperty(this, '_shouldMockModuleCache', void 0); + + _defineProperty(this, '_shouldUnmockTransitiveDependenciesCache', void 0); + + _defineProperty(this, '_sourceMapRegistry', void 0); + + _defineProperty(this, '_scriptTransformer', void 0); + + _defineProperty(this, '_fileTransforms', void 0); + + _defineProperty(this, '_v8CoverageInstrumenter', void 0); + + _defineProperty(this, '_v8CoverageResult', void 0); + + _defineProperty(this, '_transitiveShouldMock', void 0); + + _defineProperty(this, '_unmockList', void 0); + + _defineProperty(this, '_virtualMocks', void 0); + + _defineProperty(this, '_moduleImplementation', void 0); + + _defineProperty(this, 'jestObjectCaches', void 0); + + _defineProperty(this, 'jestGlobals', void 0); + + this._cacheFS = cacheFS; + this._config = config; + this._coverageOptions = coverageOptions; + this._currentlyExecutingModulePath = ''; + this._environment = environment; + this._explicitShouldMock = new Map(); + this._internalModuleRegistry = new Map(); + this._isCurrentlyExecutingManualMock = null; + this._mainModule = null; + this._mockFactories = new Map(); + this._mockRegistry = new Map(); + invariant( + this._environment.moduleMocker, + '`moduleMocker` must be set on an environment when created' + ); + this._moduleMocker = this._environment.moduleMocker; + this._isolatedModuleRegistry = null; + this._isolatedMockRegistry = null; + this._moduleRegistry = new Map(); + this._esmoduleRegistry = new Map(); + this._cjsNamedExports = new Map(); + this._testPath = testPath; + this._resolver = resolver; + this._scriptTransformer = new (_transform().ScriptTransformer)( + config, + this._cacheFS + ); + this._shouldAutoMock = config.automock; + this._sourceMapRegistry = new Map(); + this._fileTransforms = new Map(); + this._virtualMocks = new Map(); + this.jestObjectCaches = new Map(); + this._mockMetaDataCache = new Map(); + this._shouldMockModuleCache = new Map(); + this._shouldUnmockTransitiveDependenciesCache = new Map(); + this._transitiveShouldMock = new Map(); + this._fakeTimersImplementation = + config.timers === 'legacy' + ? this._environment.fakeTimers + : this._environment.fakeTimersModern; + this._unmockList = unmockRegExpCache.get(config); + + if (!this._unmockList && config.unmockedModulePathPatterns) { + this._unmockList = new RegExp( + config.unmockedModulePathPatterns.join('|') + ); + unmockRegExpCache.set(config, this._unmockList); + } + + if (config.automock) { + config.setupFiles.forEach(filePath => { + if (filePath.includes(NODE_MODULES)) { + const moduleID = this._resolver.getModuleID( + this._virtualMocks, + filePath + ); + + this._transitiveShouldMock.set(moduleID, false); + } + }); + } + + this.resetModules(); + } + + static createContext(config, options) { + (0, _jestUtil().createDirectory)(config.cacheDirectory); + const instance = Runtime.createHasteMap(config, { + console: options.console, + maxWorkers: options.maxWorkers, + resetCache: !config.cache, + watch: options.watch, + watchman: options.watchman + }); + return instance.build().then( + hasteMap => ({ + config, + hasteFS: hasteMap.hasteFS, + moduleMap: hasteMap.moduleMap, + resolver: Runtime.createResolver(config, hasteMap.moduleMap) + }), + error => { + throw error; + } + ); + } + + static createHasteMap(config, options) { + const ignorePatternParts = [ + ...config.modulePathIgnorePatterns, + ...(options && options.watch ? config.watchPathIgnorePatterns : []), + config.cacheDirectory.startsWith(config.rootDir + path().sep) && + config.cacheDirectory + ].filter(Boolean); + const ignorePattern = + ignorePatternParts.length > 0 + ? new RegExp(ignorePatternParts.join('|')) + : undefined; + return new (_jestHasteMap().default)({ + cacheDirectory: config.cacheDirectory, + computeSha1: config.haste.computeSha1, + console: options && options.console, + dependencyExtractor: config.dependencyExtractor, + extensions: [_jestSnapshot().default.EXTENSION].concat( + config.moduleFileExtensions + ), + hasteImplModulePath: config.haste.hasteImplModulePath, + ignorePattern, + maxWorkers: (options && options.maxWorkers) || 1, + mocksPattern: (0, _jestRegexUtil().escapePathForRegex)( + path().sep + '__mocks__' + path().sep + ), + name: config.name, + platforms: config.haste.platforms || ['ios', 'android'], + resetCache: options && options.resetCache, + retainAllFiles: config.haste.retainAllFiles || false, + rootDir: config.rootDir, + roots: config.roots, + throwOnModuleCollision: config.haste.throwOnModuleCollision, + useWatchman: options && options.watchman, + watch: options && options.watch + }); + } + + static createResolver(config, moduleMap) { + return new (_jestResolve().default)(moduleMap, { + defaultPlatform: config.haste.defaultPlatform, + extensions: config.moduleFileExtensions.map(extension => '.' + extension), + hasCoreModules: true, + moduleDirectories: config.moduleDirectories, + moduleNameMapper: getModuleNameMapper(config), + modulePaths: config.modulePaths, + platforms: config.haste.platforms, + resolver: config.resolver, + rootDir: config.rootDir + }); + } + + static async runCLI() { + throw new Error('The jest-runtime CLI has been moved into jest-repl'); + } + + static getCLIOptions() { + throw new Error('The jest-runtime CLI has been moved into jest-repl'); + } // unstable as it should be replaced by https://github.com/nodejs/modules/issues/393, and we don't want people to use it + + unstable_shouldLoadAsEsm(path) { + return _jestResolve().default.unstable_shouldLoadAsEsm( + path, + this._config.extensionsToTreatAsEsm + ); + } + + async loadEsmModule(modulePath, query = '', isStaticImport = false) { + const cacheKey = modulePath + query; + + if (!this._esmoduleRegistry.has(cacheKey)) { + invariant( + typeof this._environment.getVmContext === 'function', + 'ES Modules are only supported if your test environment has the `getVmContext` function' + ); + + const context = this._environment.getVmContext(); + + invariant(context); + + if (this._resolver.isCoreModule(modulePath)) { + const core = this._importCoreModule(modulePath, context); + + this._esmoduleRegistry.set(cacheKey, { + beforeEvaluation: core, + fullyEvaluated: core + }); + + return core; + } + + const transformedCode = this.transformFile(modulePath, { + isInternalModule: false, + supportsDynamicImport: true, + supportsExportNamespaceFrom: true, + supportsStaticESM: true, + supportsTopLevelAwait + }); + const module = new (_vm().SourceTextModule)(transformedCode, { + context, + identifier: modulePath, + importModuleDynamically: (specifier, referencingModule) => + this.linkModules( + specifier, + referencingModule.identifier, + referencingModule.context, + false + ), + + initializeImportMeta(meta) { + meta.url = (0, _url().pathToFileURL)(modulePath).href; + } + }); + let resolve; + let reject; + const promise = new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; + }); // add to registry before link so that circular import won't end up stack overflow + + this._esmoduleRegistry.set( + cacheKey, // we wanna put the linking promise in the cache so modules loaded in + // parallel can all await it. We then await it synchronously below, so + // we shouldn't get any unhandled rejections + { + beforeEvaluation: Promise.resolve(module), + fullyEvaluated: promise + } + ); + + module + .link((specifier, referencingModule) => + this.linkModules( + specifier, + referencingModule.identifier, + referencingModule.context, + true + ) + ) + .then(() => module.evaluate()) + .then( + () => resolve(module), + e => reject(e) + ); + } + + const entry = this._esmoduleRegistry.get(cacheKey); // return the already resolved, pre-evaluation promise + // is loaded through static import to prevent promise deadlock + // because module is evaluated after all static import is resolved + + const module = isStaticImport + ? entry === null || entry === void 0 + ? void 0 + : entry.beforeEvaluation + : entry === null || entry === void 0 + ? void 0 + : entry.fullyEvaluated; + invariant(module); + return module; + } + + linkModules(specifier, referencingIdentifier, context, isStaticImport) { + if (specifier === '@jest/globals') { + const fromCache = this._esmoduleRegistry.get('@jest/globals'); + + if (fromCache) { + return isStaticImport + ? fromCache.beforeEvaluation + : fromCache.fullyEvaluated; + } + + const globals = this.getGlobalsForEsm(referencingIdentifier, context); + + this._esmoduleRegistry.set('@jest/globals', { + beforeEvaluation: globals, + fullyEvaluated: globals + }); + + return globals; + } + + if (specifier.startsWith('file://')) { + specifier = (0, _url().fileURLToPath)(specifier); + } + + const [path, query] = specifier.split('?'); + + const resolved = this._resolveModule(referencingIdentifier, path); + + if ( + this._resolver.isCoreModule(resolved) || + this.unstable_shouldLoadAsEsm(resolved) + ) { + return this.loadEsmModule(resolved, query, isStaticImport); + } + + return this.loadCjsAsEsm(referencingIdentifier, resolved, context); + } + + async unstable_importModule(from, moduleName) { + invariant( + runtimeSupportsVmModules, + 'You need to run with a version of node that supports ES Modules in the VM API.' + ); + const [path, query] = (moduleName !== null && moduleName !== void 0 + ? moduleName + : '' + ).split('?'); + + const modulePath = this._resolveModule(from, path); + + return this.loadEsmModule(modulePath, query); + } + + loadCjsAsEsm(from, modulePath, context) { + // CJS loaded via `import` should share cache with other CJS: https://github.com/nodejs/modules/issues/503 + const cjs = this.requireModuleOrMock(from, modulePath); + const parsedExports = this.getExportsOfCjs(modulePath); + const cjsExports = [...parsedExports].filter(exportName => { + // we don't wanna respect any exports _named_ default as a named export + if (exportName === 'default') { + return false; + } + + return Object.hasOwnProperty.call(cjs, exportName); + }); + const module = new (_vm().SyntheticModule)( + [...cjsExports, 'default'], + function () { + cjsExports.forEach(exportName => { + // @ts-expect-error + this.setExport(exportName, cjs[exportName]); + }); // @ts-expect-error: TS doesn't know what `this` is + + this.setExport('default', cjs); + }, + { + context, + identifier: modulePath + } + ); + return evaluateSyntheticModule(module); + } + + getExportsOfCjs(modulePath) { + var _this$_fileTransforms, _this$_fileTransforms2; + + const cachedNamedExports = this._cjsNamedExports.get(modulePath); + + if (cachedNamedExports) { + return cachedNamedExports; + } + + const transformedCode = + (_this$_fileTransforms = + (_this$_fileTransforms2 = this._fileTransforms.get(modulePath)) === + null || _this$_fileTransforms2 === void 0 + ? void 0 + : _this$_fileTransforms2.code) !== null && + _this$_fileTransforms !== void 0 + ? _this$_fileTransforms + : this.readFile(modulePath); + const {exports, reexports} = (0, _cjsModuleLexer().parse)(transformedCode); + const namedExports = new Set(exports); + reexports.forEach(reexport => { + const resolved = this._resolveModule(modulePath, reexport); + + const exports = this.getExportsOfCjs(resolved); + exports.forEach(namedExports.add, namedExports); + }); + + this._cjsNamedExports.set(modulePath, namedExports); + + return namedExports; + } + + requireModule(from, moduleName, options, isRequireActual) { + const moduleID = this._resolver.getModuleID( + this._virtualMocks, + from, + moduleName + ); + + let modulePath; // Some old tests rely on this mocking behavior. Ideally we'll change this + // to be more explicit. + + const moduleResource = moduleName && this._resolver.getModule(moduleName); + + const manualMock = + moduleName && this._resolver.getMockModule(from, moduleName); + + if ( + !(options !== null && options !== void 0 && options.isInternalModule) && + !isRequireActual && + !moduleResource && + manualMock && + manualMock !== this._isCurrentlyExecutingManualMock && + this._explicitShouldMock.get(moduleID) !== false + ) { + modulePath = manualMock; + } + + if (moduleName && this._resolver.isCoreModule(moduleName)) { + return this._requireCoreModule(moduleName); + } + + if (!modulePath) { + modulePath = this._resolveModule(from, moduleName); + } + + let moduleRegistry; + + if (options !== null && options !== void 0 && options.isInternalModule) { + moduleRegistry = this._internalModuleRegistry; + } else { + if (this._isolatedModuleRegistry) { + moduleRegistry = this._isolatedModuleRegistry; + } else { + moduleRegistry = this._moduleRegistry; + } + } + + const module = moduleRegistry.get(modulePath); + + if (module) { + return module.exports; + } // We must register the pre-allocated module object first so that any + // circular dependencies that may arise while evaluating the module can + // be satisfied. + + const localModule = { + children: [], + exports: {}, + filename: modulePath, + id: modulePath, + loaded: false, + path: path().dirname(modulePath) + }; + moduleRegistry.set(modulePath, localModule); + + this._loadModule( + localModule, + from, + moduleName, + modulePath, + options, + moduleRegistry + ); + + return localModule.exports; + } + + requireInternalModule(from, to) { + if (to) { + var _nativeModule$createR; + + const require = ((_nativeModule$createR = nativeModule() + .createRequire) !== null && _nativeModule$createR !== void 0 + ? _nativeModule$createR + : nativeModule().createRequireFromPath)(from); + + if (INTERNAL_MODULE_REQUIRE_OUTSIDE_OPTIMIZED_MODULES.has(to)) { + return require(to); + } + + const outsideJestVmPath = (0, _helpers.decodePossibleOutsideJestVmPath)( + to + ); + + if (outsideJestVmPath) { + return require(outsideJestVmPath); + } + } + + return this.requireModule(from, to, { + isInternalModule: true, + supportsDynamicImport: esmIsAvailable, + supportsExportNamespaceFrom: false, + supportsStaticESM: false, + supportsTopLevelAwait: false + }); + } + + requireActual(from, moduleName) { + return this.requireModule(from, moduleName, undefined, true); + } + + requireMock(from, moduleName) { + const moduleID = this._resolver.getModuleID( + this._virtualMocks, + from, + moduleName + ); + + if ( + this._isolatedMockRegistry && + this._isolatedMockRegistry.get(moduleID) + ) { + return this._isolatedMockRegistry.get(moduleID); + } else if (this._mockRegistry.get(moduleID)) { + return this._mockRegistry.get(moduleID); + } + + const mockRegistry = this._isolatedMockRegistry || this._mockRegistry; + + if (this._mockFactories.has(moduleID)) { + // has check above makes this ok + const module = this._mockFactories.get(moduleID)(); + + mockRegistry.set(moduleID, module); + return module; + } + + const manualMockOrStub = this._resolver.getMockModule(from, moduleName); + + let modulePath = + this._resolver.getMockModule(from, moduleName) || + this._resolveModule(from, moduleName); + + let isManualMock = + manualMockOrStub && + !this._resolver.resolveStubModuleName(from, moduleName); + + if (!isManualMock) { + // If the actual module file has a __mocks__ dir sitting immediately next + // to it, look to see if there is a manual mock for this file. + // + // subDir1/my_module.js + // subDir1/__mocks__/my_module.js + // subDir2/my_module.js + // subDir2/__mocks__/my_module.js + // + // Where some other module does a relative require into each of the + // respective subDir{1,2} directories and expects a manual mock + // corresponding to that particular my_module.js file. + const moduleDir = path().dirname(modulePath); + const moduleFileName = path().basename(modulePath); + const potentialManualMock = path().join( + moduleDir, + '__mocks__', + moduleFileName + ); + + if (fs().existsSync(potentialManualMock)) { + isManualMock = true; + modulePath = potentialManualMock; + } + } + + if (isManualMock) { + const localModule = { + children: [], + exports: {}, + filename: modulePath, + id: modulePath, + loaded: false, + path: path().dirname(modulePath) + }; + + this._loadModule( + localModule, + from, + moduleName, + modulePath, + undefined, + mockRegistry + ); + + mockRegistry.set(moduleID, localModule.exports); + } else { + // Look for a real module to generate an automock from + mockRegistry.set(moduleID, this._generateMock(from, moduleName)); + } + + return mockRegistry.get(moduleID); + } + + _loadModule( + localModule, + from, + moduleName, + modulePath, + options, + moduleRegistry + ) { + if (path().extname(modulePath) === '.json') { + const text = (0, _stripBom().default)(this.readFile(modulePath)); + + const transformedFile = this._scriptTransformer.transformJson( + modulePath, + this._getFullTransformationOptions(options), + text + ); + + localModule.exports = this._environment.global.JSON.parse( + transformedFile + ); + } else if (path().extname(modulePath) === '.node') { + localModule.exports = require(modulePath); + } else { + // Only include the fromPath if a moduleName is given. Else treat as root. + const fromPath = moduleName ? from : null; + + this._execModule(localModule, options, moduleRegistry, fromPath); + } + + localModule.loaded = true; + } + + _getFullTransformationOptions(options = defaultTransformOptions) { + return {...options, ...this._coverageOptions}; + } + + requireModuleOrMock(from, moduleName) { + // this module is unmockable + if (moduleName === '@jest/globals') { + // @ts-expect-error: we don't care that it's not assignable to T + return this.getGlobalsForCjs(from); + } + + try { + if (this._shouldMock(from, moduleName)) { + return this.requireMock(from, moduleName); + } else { + return this.requireModule(from, moduleName); + } + } catch (e) { + const moduleNotFound = _jestResolve().default.tryCastModuleNotFoundError( + e + ); + + if (moduleNotFound) { + if ( + moduleNotFound.siblingWithSimilarExtensionFound === null || + moduleNotFound.siblingWithSimilarExtensionFound === undefined + ) { + moduleNotFound.hint = (0, _helpers.findSiblingsWithFileExtension)( + this._config.moduleFileExtensions, + from, + moduleNotFound.moduleName || moduleName + ); + moduleNotFound.siblingWithSimilarExtensionFound = Boolean( + moduleNotFound.hint + ); + } + + moduleNotFound.buildMessage(this._config.rootDir); + throw moduleNotFound; + } + + throw e; + } + } + + isolateModules(fn) { + if (this._isolatedModuleRegistry || this._isolatedMockRegistry) { + throw new Error( + 'isolateModules cannot be nested inside another isolateModules.' + ); + } + + this._isolatedModuleRegistry = new Map(); + this._isolatedMockRegistry = new Map(); + + try { + fn(); + } finally { + var _this$_isolatedModule, _this$_isolatedMockRe; + + // might be cleared within the callback + (_this$_isolatedModule = this._isolatedModuleRegistry) === null || + _this$_isolatedModule === void 0 + ? void 0 + : _this$_isolatedModule.clear(); + (_this$_isolatedMockRe = this._isolatedMockRegistry) === null || + _this$_isolatedMockRe === void 0 + ? void 0 + : _this$_isolatedMockRe.clear(); + this._isolatedModuleRegistry = null; + this._isolatedMockRegistry = null; + } + } + + resetModules() { + var _this$_isolatedModule2, _this$_isolatedMockRe2; + + (_this$_isolatedModule2 = this._isolatedModuleRegistry) === null || + _this$_isolatedModule2 === void 0 + ? void 0 + : _this$_isolatedModule2.clear(); + (_this$_isolatedMockRe2 = this._isolatedMockRegistry) === null || + _this$_isolatedMockRe2 === void 0 + ? void 0 + : _this$_isolatedMockRe2.clear(); + this._isolatedModuleRegistry = null; + this._isolatedMockRegistry = null; + + this._mockRegistry.clear(); + + this._moduleRegistry.clear(); + + this._esmoduleRegistry.clear(); + + this._cjsNamedExports.clear(); + + if (this._environment) { + if (this._environment.global) { + const envGlobal = this._environment.global; + Object.keys(envGlobal).forEach(key => { + const globalMock = envGlobal[key]; + + if ( + ((typeof globalMock === 'object' && globalMock !== null) || + typeof globalMock === 'function') && + globalMock._isMockFunction === true + ) { + globalMock.mockClear(); + } + }); + } + + if (this._environment.fakeTimers) { + this._environment.fakeTimers.clearAllTimers(); + } + } + } + + async collectV8Coverage() { + this._v8CoverageInstrumenter = new (_collectV8Coverage().CoverageInstrumenter)(); + await this._v8CoverageInstrumenter.startInstrumenting(); + } + + async stopCollectingV8Coverage() { + if (!this._v8CoverageInstrumenter) { + throw new Error('You need to call `collectV8Coverage` first.'); + } + + this._v8CoverageResult = await this._v8CoverageInstrumenter.stopInstrumenting(); + } + + getAllCoverageInfoCopy() { + return (0, _jestUtil().deepCyclicCopy)( + this._environment.global.__coverage__ + ); + } + + getAllV8CoverageInfoCopy() { + if (!this._v8CoverageResult) { + throw new Error('You need to `stopCollectingV8Coverage` first'); + } + + return this._v8CoverageResult + .filter(res => res.url.startsWith('file://')) + .map(res => ({...res, url: (0, _url().fileURLToPath)(res.url)})) + .filter( + ( + res // TODO: will this work on windows? It might be better if `shouldInstrument` deals with it anyways + ) => + res.url.startsWith(this._config.rootDir) && + this._fileTransforms.has(res.url) && + (0, _transform().shouldInstrument)( + res.url, + this._coverageOptions, + this._config + ) + ) + .map(result => { + const transformedFile = this._fileTransforms.get(result.url); + + return { + codeTransformResult: transformedFile, + result + }; + }); + } + + getSourceMaps() { + return this._sourceMapRegistry; + } + + setMock(from, moduleName, mockFactory, options) { + if (options !== null && options !== void 0 && options.virtual) { + const mockPath = this._resolver.getModulePath(from, moduleName); + + this._virtualMocks.set(mockPath, true); + } + + const moduleID = this._resolver.getModuleID( + this._virtualMocks, + from, + moduleName + ); + + this._explicitShouldMock.set(moduleID, true); + + this._mockFactories.set(moduleID, mockFactory); + } + + restoreAllMocks() { + this._moduleMocker.restoreAllMocks(); + } + + resetAllMocks() { + this._moduleMocker.resetAllMocks(); + } + + clearAllMocks() { + this._moduleMocker.clearAllMocks(); + } + + teardown() { + this.restoreAllMocks(); + this.resetAllMocks(); + this.resetModules(); + + this._internalModuleRegistry.clear(); + + this._mainModule = null; + + this._mockFactories.clear(); + + this._mockMetaDataCache.clear(); + + this._shouldMockModuleCache.clear(); + + this._shouldUnmockTransitiveDependenciesCache.clear(); + + this._explicitShouldMock.clear(); + + this._transitiveShouldMock.clear(); + + this._virtualMocks.clear(); + + this._cacheFS.clear(); + + this._unmockList = undefined; + + this._sourceMapRegistry.clear(); + + this._fileTransforms.clear(); + + this.jestObjectCaches.clear(); + this._v8CoverageResult = []; + this._v8CoverageInstrumenter = undefined; + this._moduleImplementation = undefined; + } + + _resolveModule(from, to) { + return to ? this._resolver.resolveModule(from, to) : from; + } + + _requireResolve(from, moduleName, options = {}) { + if (moduleName == null) { + throw new Error( + 'The first argument to require.resolve must be a string. Received null or undefined.' + ); + } + + const {paths} = options; + + if (paths) { + for (const p of paths) { + const absolutePath = path().resolve(from, '..', p); + + const module = this._resolver.resolveModuleFromDirIfExists( + absolutePath, + moduleName, // required to also resolve files without leading './' directly in the path + { + paths: [absolutePath] + } + ); + + if (module) { + return module; + } + } + + throw new (_jestResolve().default.ModuleNotFoundError)( + `Cannot resolve module '${moduleName}' from paths ['${paths.join( + "', '" + )}'] from ${from}` + ); + } + + try { + return this._resolveModule(from, moduleName); + } catch (err) { + const module = this._resolver.getMockModule(from, moduleName); + + if (module) { + return module; + } else { + throw err; + } + } + } + + _requireResolvePaths(from, moduleName) { + if (moduleName == null) { + throw new Error( + 'The first argument to require.resolve.paths must be a string. Received null or undefined.' + ); + } + + if (!moduleName.length) { + throw new Error( + 'The first argument to require.resolve.paths must not be the empty string.' + ); + } + + if (moduleName[0] === '.') { + return [path().resolve(from, '..')]; + } + + if (this._resolver.isCoreModule(moduleName)) { + return null; + } + + return this._resolver.getModulePaths(path().resolve(from, '..')); + } + + _execModule(localModule, options, moduleRegistry, from) { + // If the environment was disposed, prevent this module from being executed. + if (!this._environment.global) { + return; + } + + const module = localModule; + const filename = module.filename; + const lastExecutingModulePath = this._currentlyExecutingModulePath; + this._currentlyExecutingModulePath = filename; + const origCurrExecutingManualMock = this._isCurrentlyExecutingManualMock; + this._isCurrentlyExecutingManualMock = filename; + module.children = []; + Object.defineProperty(module, 'parent', { + enumerable: true, + + get() { + const key = from || ''; + return moduleRegistry.get(key) || null; + } + }); + module.paths = this._resolver.getModulePaths(module.path); + Object.defineProperty(module, 'require', { + value: this._createRequireImplementation(module, options) + }); + const transformedCode = this.transformFile(filename, options); + let compiledFunction = null; + const script = this.createScriptFromCode(transformedCode, filename); + let runScript = null; // Use this if available instead of deprecated `JestEnvironment.runScript` + + if (typeof this._environment.getVmContext === 'function') { + const vmContext = this._environment.getVmContext(); + + if (vmContext) { + runScript = script.runInContext(vmContext, { + filename + }); + } + } else { + runScript = this._environment.runScript(script); + } + + if (runScript !== null) { + compiledFunction = runScript[EVAL_RESULT_VARIABLE]; + } + + if (compiledFunction === null) { + this._logFormattedReferenceError( + 'You are trying to `import` a file after the Jest environment has been torn down.' + ); + + process.exitCode = 1; + return; + } + + const jestObject = this._createJestObjectFor(filename); + + this.jestObjectCaches.set(filename, jestObject); + const lastArgs = [ + this._config.injectGlobals ? jestObject : undefined, // jest object + ...this._config.extraGlobals.map(globalVariable => { + if (this._environment.global[globalVariable]) { + return this._environment.global[globalVariable]; + } + + throw new Error( + `You have requested '${globalVariable}' as a global variable, but it was not present. Please check your config or your global environment.` + ); + }) + ]; + + if (!this._mainModule && filename === this._testPath) { + this._mainModule = module; + } + + Object.defineProperty(module, 'main', { + enumerable: true, + value: this._mainModule + }); + + try { + compiledFunction.call( + module.exports, + module, // module object + module.exports, // module exports + module.require, // require implementation + module.path, // __dirname + module.filename, // __filename + // @ts-expect-error + ...lastArgs.filter(notEmpty) + ); + } catch (error) { + this.handleExecutionError(error, module); + } + + this._isCurrentlyExecutingManualMock = origCurrExecutingManualMock; + this._currentlyExecutingModulePath = lastExecutingModulePath; + } + + transformFile(filename, options) { + const source = this.readFile(filename); + + if (options !== null && options !== void 0 && options.isInternalModule) { + return source; + } + + const transformedFile = this._scriptTransformer.transform( + filename, + this._getFullTransformationOptions(options), + source + ); + + this._fileTransforms.set(filename, { + ...transformedFile, + wrapperLength: this.constructModuleWrapperStart().length + }); + + if (transformedFile.sourceMapPath) { + this._sourceMapRegistry.set(filename, transformedFile.sourceMapPath); + } + + return transformedFile.code; + } + + createScriptFromCode(scriptSource, filename) { + try { + const scriptFilename = this._resolver.isCoreModule(filename) + ? `jest-nodejs-core-${filename}` + : filename; + return new (_vm().Script)(this.wrapCodeInModuleWrapper(scriptSource), { + displayErrors: true, + filename: scriptFilename, + // @ts-expect-error: Experimental ESM API + importModuleDynamically: specifier => { + var _this$_environment$ge, _this$_environment; + + const context = + (_this$_environment$ge = (_this$_environment = this._environment) + .getVmContext) === null || _this$_environment$ge === void 0 + ? void 0 + : _this$_environment$ge.call(_this$_environment); + invariant(context); + return this.linkModules(specifier, scriptFilename, context, false); + } + }); + } catch (e) { + throw (0, _transform().handlePotentialSyntaxError)(e); + } + } + + _requireCoreModule(moduleName) { + if (moduleName === 'process') { + return this._environment.global.process; + } + + if (moduleName === 'module') { + return this._getMockedNativeModule(); + } + + return require(moduleName); + } + + _importCoreModule(moduleName, context) { + const required = this._requireCoreModule(moduleName); + + const module = new (_vm().SyntheticModule)( + ['default', ...Object.keys(required)], + function () { + // @ts-expect-error: TS doesn't know what `this` is + this.setExport('default', required); + Object.entries(required).forEach(([key, value]) => { + // @ts-expect-error: TS doesn't know what `this` is + this.setExport(key, value); + }); + }, // should identifier be `node://${moduleName}`? + { + context, + identifier: moduleName + } + ); + return evaluateSyntheticModule(module); + } + + _getMockedNativeModule() { + if (this._moduleImplementation) { + return this._moduleImplementation; + } + + const createRequire = modulePath => { + const filename = + typeof modulePath === 'string' + ? modulePath.startsWith('file:///') + ? (0, _url().fileURLToPath)(new (_url().URL)(modulePath)) + : modulePath + : (0, _url().fileURLToPath)(modulePath); + + if (!path().isAbsolute(filename)) { + const error = new TypeError( + `The argument 'filename' must be a file URL object, file URL string, or absolute path string. Received '${filename}'` + ); // @ts-expect-error + + error.code = 'ERR_INVALID_ARG_TYPE'; + throw error; + } + + return this._createRequireImplementation({ + children: [], + exports: {}, + filename, + id: filename, + loaded: false, + path: path().dirname(filename) + }); + }; // should we implement the class ourselves? + + class Module extends nativeModule().Module {} + + Object.entries(nativeModule().Module).forEach(([key, value]) => { + // @ts-expect-error + Module[key] = value; + }); + Module.Module = Module; + + if ('createRequire' in nativeModule()) { + Module.createRequire = createRequire; + } + + if ('createRequireFromPath' in nativeModule()) { + Module.createRequireFromPath = function createRequireFromPath(filename) { + if (typeof filename !== 'string') { + const error = new TypeError( + `The argument 'filename' must be string. Received '${filename}'.${ + filename instanceof _url().URL + ? ' Use createRequire for URL filename.' + : '' + }` + ); // @ts-expect-error + + error.code = 'ERR_INVALID_ARG_TYPE'; + throw error; + } + + return createRequire(filename); + }; + } + + if ('syncBuiltinESMExports' in nativeModule()) { + Module.syncBuiltinESMExports = function syncBuiltinESMExports() {}; + } + + this._moduleImplementation = Module; + return Module; + } + + _generateMock(from, moduleName) { + const modulePath = + this._resolver.resolveStubModuleName(from, moduleName) || + this._resolveModule(from, moduleName); + + if (!this._mockMetaDataCache.has(modulePath)) { + // This allows us to handle circular dependencies while generating an + // automock + this._mockMetaDataCache.set( + modulePath, + this._moduleMocker.getMetadata({}) || {} + ); // In order to avoid it being possible for automocking to potentially + // cause side-effects within the module environment, we need to execute + // the module in isolation. This could cause issues if the module being + // mocked has calls into side-effectful APIs on another module. + + const origMockRegistry = this._mockRegistry; + const origModuleRegistry = this._moduleRegistry; + this._mockRegistry = new Map(); + this._moduleRegistry = new Map(); + const moduleExports = this.requireModule(from, moduleName); // Restore the "real" module/mock registries + + this._mockRegistry = origMockRegistry; + this._moduleRegistry = origModuleRegistry; + + const mockMetadata = this._moduleMocker.getMetadata(moduleExports); + + if (mockMetadata == null) { + throw new Error( + `Failed to get mock metadata: ${modulePath}\n\n` + + `See: https://jestjs.io/docs/manual-mocks.html#content` + ); + } + + this._mockMetaDataCache.set(modulePath, mockMetadata); + } + + return this._moduleMocker.generateFromMetadata( + // added above if missing + this._mockMetaDataCache.get(modulePath) + ); + } + + _shouldMock(from, moduleName) { + const explicitShouldMock = this._explicitShouldMock; + + const moduleID = this._resolver.getModuleID( + this._virtualMocks, + from, + moduleName + ); + + const key = from + path().delimiter + moduleID; + + if (explicitShouldMock.has(moduleID)) { + // guaranteed by `has` above + return explicitShouldMock.get(moduleID); + } + + if ( + !this._shouldAutoMock || + this._resolver.isCoreModule(moduleName) || + this._shouldUnmockTransitiveDependenciesCache.get(key) + ) { + return false; + } + + if (this._shouldMockModuleCache.has(moduleID)) { + // guaranteed by `has` above + return this._shouldMockModuleCache.get(moduleID); + } + + let modulePath; + + try { + modulePath = this._resolveModule(from, moduleName); + } catch (e) { + const manualMock = this._resolver.getMockModule(from, moduleName); + + if (manualMock) { + this._shouldMockModuleCache.set(moduleID, true); + + return true; + } + + throw e; + } + + if (this._unmockList && this._unmockList.test(modulePath)) { + this._shouldMockModuleCache.set(moduleID, false); + + return false; + } // transitive unmocking for package managers that store flat packages (npm3) + + const currentModuleID = this._resolver.getModuleID( + this._virtualMocks, + from + ); + + if ( + this._transitiveShouldMock.get(currentModuleID) === false || + (from.includes(NODE_MODULES) && + modulePath.includes(NODE_MODULES) && + ((this._unmockList && this._unmockList.test(from)) || + explicitShouldMock.get(currentModuleID) === false)) + ) { + this._transitiveShouldMock.set(moduleID, false); + + this._shouldUnmockTransitiveDependenciesCache.set(key, true); + + return false; + } + + this._shouldMockModuleCache.set(moduleID, true); + + return true; + } + + _createRequireImplementation(from, options) { + const resolve = (moduleName, resolveOptions) => { + const resolved = this._requireResolve( + from.filename, + moduleName, + resolveOptions + ); + + if ( + resolveOptions !== null && + resolveOptions !== void 0 && + resolveOptions[JEST_RESOLVE_OUTSIDE_VM_OPTION] && + options !== null && + options !== void 0 && + options.isInternalModule + ) { + return (0, _helpers.createOutsideJestVmPath)(resolved); + } + + return resolved; + }; + + resolve.paths = moduleName => + this._requireResolvePaths(from.filename, moduleName); + + const moduleRequire = + options !== null && options !== void 0 && options.isInternalModule + ? moduleName => this.requireInternalModule(from.filename, moduleName) + : this.requireModuleOrMock.bind(this, from.filename); + moduleRequire.extensions = Object.create(null); + moduleRequire.resolve = resolve; + + moduleRequire.cache = (() => { + // TODO: consider warning somehow that this does nothing. We should support deletions, anyways + const notPermittedMethod = () => true; + + return new Proxy(Object.create(null), { + defineProperty: notPermittedMethod, + deleteProperty: notPermittedMethod, + get: (_target, key) => + typeof key === 'string' ? this._moduleRegistry.get(key) : undefined, + + getOwnPropertyDescriptor() { + return { + configurable: true, + enumerable: true + }; + }, + + has: (_target, key) => + typeof key === 'string' && this._moduleRegistry.has(key), + ownKeys: () => Array.from(this._moduleRegistry.keys()), + set: notPermittedMethod + }); + })(); + + Object.defineProperty(moduleRequire, 'main', { + enumerable: true, + value: this._mainModule + }); + return moduleRequire; + } + + _createJestObjectFor(from) { + const disableAutomock = () => { + this._shouldAutoMock = false; + return jestObject; + }; + + const enableAutomock = () => { + this._shouldAutoMock = true; + return jestObject; + }; + + const unmock = moduleName => { + const moduleID = this._resolver.getModuleID( + this._virtualMocks, + from, + moduleName + ); + + this._explicitShouldMock.set(moduleID, false); + + return jestObject; + }; + + const deepUnmock = moduleName => { + const moduleID = this._resolver.getModuleID( + this._virtualMocks, + from, + moduleName + ); + + this._explicitShouldMock.set(moduleID, false); + + this._transitiveShouldMock.set(moduleID, false); + + return jestObject; + }; + + const mock = (moduleName, mockFactory, options) => { + if (mockFactory !== undefined) { + return setMockFactory(moduleName, mockFactory, options); + } + + const moduleID = this._resolver.getModuleID( + this._virtualMocks, + from, + moduleName + ); + + this._explicitShouldMock.set(moduleID, true); + + return jestObject; + }; + + const setMockFactory = (moduleName, mockFactory, options) => { + this.setMock(from, moduleName, mockFactory, options); + return jestObject; + }; + + const clearAllMocks = () => { + this.clearAllMocks(); + return jestObject; + }; + + const resetAllMocks = () => { + this.resetAllMocks(); + return jestObject; + }; + + const restoreAllMocks = () => { + this.restoreAllMocks(); + return jestObject; + }; + + const _getFakeTimers = () => { + if ( + !(this._environment.fakeTimers || this._environment.fakeTimersModern) + ) { + this._logFormattedReferenceError( + 'You are trying to access a property or method of the Jest environment after it has been torn down.' + ); + + process.exitCode = 1; + } + + return this._fakeTimersImplementation; + }; + + const useFakeTimers = (type = 'modern') => { + if (type === 'legacy') { + this._fakeTimersImplementation = this._environment.fakeTimers; + } else { + this._fakeTimersImplementation = this._environment.fakeTimersModern; + } + + this._fakeTimersImplementation.useFakeTimers(); + + return jestObject; + }; + + const useRealTimers = () => { + _getFakeTimers().useRealTimers(); + + return jestObject; + }; + + const resetModules = () => { + this.resetModules(); + return jestObject; + }; + + const isolateModules = fn => { + this.isolateModules(fn); + return jestObject; + }; + + const fn = this._moduleMocker.fn.bind(this._moduleMocker); + + const spyOn = this._moduleMocker.spyOn.bind(this._moduleMocker); + + const setTimeout = timeout => { + if (this._environment.global.jasmine) { + this._environment.global.jasmine._DEFAULT_TIMEOUT_INTERVAL = timeout; + } else { + // @ts-expect-error: https://github.com/Microsoft/TypeScript/issues/24587 + this._environment.global[testTimeoutSymbol] = timeout; + } + + return jestObject; + }; + + const retryTimes = numTestRetries => { + // @ts-expect-error: https://github.com/Microsoft/TypeScript/issues/24587 + this._environment.global[retryTimesSymbol] = numTestRetries; + return jestObject; + }; + + const jestObject = { + advanceTimersByTime: msToRun => + _getFakeTimers().advanceTimersByTime(msToRun), + advanceTimersToNextTimer: steps => + _getFakeTimers().advanceTimersToNextTimer(steps), + autoMockOff: disableAutomock, + autoMockOn: enableAutomock, + clearAllMocks, + clearAllTimers: () => _getFakeTimers().clearAllTimers(), + createMockFromModule: moduleName => this._generateMock(from, moduleName), + deepUnmock, + disableAutomock, + doMock: mock, + dontMock: unmock, + enableAutomock, + fn, + genMockFromModule: moduleName => this._generateMock(from, moduleName), + getRealSystemTime: () => { + const fakeTimers = _getFakeTimers(); + + if (fakeTimers instanceof _fakeTimers().ModernFakeTimers) { + return fakeTimers.getRealSystemTime(); + } else { + throw new TypeError( + 'getRealSystemTime is not available when not using modern timers' + ); + } + }, + getTimerCount: () => _getFakeTimers().getTimerCount(), + isMockFunction: this._moduleMocker.isMockFunction, + isolateModules, + mock, + requireActual: this.requireActual.bind(this, from), + requireMock: this.requireMock.bind(this, from), + resetAllMocks, + resetModules, + restoreAllMocks, + retryTimes, + runAllImmediates: () => { + const fakeTimers = _getFakeTimers(); + + if (fakeTimers instanceof _fakeTimers().LegacyFakeTimers) { + fakeTimers.runAllImmediates(); + } else { + throw new TypeError( + 'runAllImmediates is not available when using modern timers' + ); + } + }, + runAllTicks: () => _getFakeTimers().runAllTicks(), + runAllTimers: () => _getFakeTimers().runAllTimers(), + runOnlyPendingTimers: () => _getFakeTimers().runOnlyPendingTimers(), + setMock: (moduleName, mock) => setMockFactory(moduleName, () => mock), + setSystemTime: now => { + const fakeTimers = _getFakeTimers(); + + if (fakeTimers instanceof _fakeTimers().ModernFakeTimers) { + fakeTimers.setSystemTime(now); + } else { + throw new TypeError( + 'setSystemTime is not available when not using modern timers' + ); + } + }, + setTimeout, + spyOn, + unmock, + useFakeTimers, + useRealTimers + }; + return jestObject; + } + + _logFormattedReferenceError(errorMessage) { + const originalStack = new ReferenceError(errorMessage).stack + .split('\n') // Remove this file from the stack (jest-message-utils will keep one line) + .filter(line => line.indexOf(__filename) === -1) + .join('\n'); + const {message, stack} = (0, _jestMessageUtil().separateMessageFromStack)( + originalStack + ); + console.error( + `\n${message}\n` + + (0, _jestMessageUtil().formatStackTrace)(stack, this._config, { + noStackTrace: false + }) + ); + } + + wrapCodeInModuleWrapper(content) { + return this.constructModuleWrapperStart() + content + '\n}});'; + } + + constructModuleWrapperStart() { + const args = this.constructInjectedModuleParameters(); + return '({"' + EVAL_RESULT_VARIABLE + `":function(${args.join(',')}){`; + } + + constructInjectedModuleParameters() { + return [ + 'module', + 'exports', + 'require', + '__dirname', + '__filename', + this._config.injectGlobals ? 'jest' : undefined, + ...this._config.extraGlobals + ].filter(notEmpty); + } + + handleExecutionError(e, module) { + const moduleNotFoundError = _jestResolve().default.tryCastModuleNotFoundError( + e + ); + + if (moduleNotFoundError) { + if (!moduleNotFoundError.requireStack) { + moduleNotFoundError.requireStack = [module.filename || module.id]; + + for (let cursor = module.parent; cursor; cursor = cursor.parent) { + moduleNotFoundError.requireStack.push(cursor.filename || cursor.id); + } + + moduleNotFoundError.buildMessage(this._config.rootDir); + } + + throw moduleNotFoundError; + } + + throw e; + } + + getGlobalsForCjs(from) { + const jest = this.jestObjectCaches.get(from); + invariant(jest, 'There should always be a Jest object already'); + return {...this.getGlobalsFromEnvironment(), jest}; + } + + getGlobalsForEsm(from, context) { + let jest = this.jestObjectCaches.get(from); + + if (!jest) { + jest = this._createJestObjectFor(from); + this.jestObjectCaches.set(from, jest); + } + + const globals = {...this.getGlobalsFromEnvironment(), jest}; + const module = new (_vm().SyntheticModule)( + Object.keys(globals), + function () { + Object.entries(globals).forEach(([key, value]) => { + // @ts-expect-error: TS doesn't know what `this` is + this.setExport(key, value); + }); + }, + { + context, + identifier: '@jest/globals' + } + ); + return evaluateSyntheticModule(module); + } + + getGlobalsFromEnvironment() { + if (this.jestGlobals) { + return {...this.jestGlobals}; + } + + return { + afterAll: this._environment.global.afterAll, + afterEach: this._environment.global.afterEach, + beforeAll: this._environment.global.beforeAll, + beforeEach: this._environment.global.beforeEach, + describe: this._environment.global.describe, + expect: this._environment.global.expect, + fdescribe: this._environment.global.fdescribe, + fit: this._environment.global.fit, + it: this._environment.global.it, + test: this._environment.global.test, + xdescribe: this._environment.global.xdescribe, + xit: this._environment.global.xit, + xtest: this._environment.global.xtest + }; + } + + readFile(filename) { + let source = this._cacheFS.get(filename); + + if (!source) { + source = fs().readFileSync(filename, 'utf8'); + + this._cacheFS.set(filename, source); + } + + return source; + } + + setGlobalsForRuntime(globals) { + this.jestGlobals = globals; + } +} + +exports.default = Runtime; + +_defineProperty(Runtime, 'shouldInstrument', _transform().shouldInstrument); + +function invariant(condition, message) { + if (!condition) { + throw new Error(message); + } +} + +function notEmpty(value) { + return value !== null && value !== undefined; +} + +async function evaluateSyntheticModule(module) { + await module.link(() => { + throw new Error('This should never happen'); + }); + await module.evaluate(); + return module; +} diff --git a/packages/jest-runtime/build/types.d.ts b/packages/jest-runtime/build/types.d.ts new file mode 100644 index 000000000000..63229fdf4660 --- /dev/null +++ b/packages/jest-runtime/build/types.d.ts @@ -0,0 +1,15 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { FS as HasteFS, ModuleMap } from 'jest-haste-map'; +import type Resolver from 'jest-resolve'; +export declare type Context = { + config: Config.ProjectConfig; + hasteFS: HasteFS; + moduleMap: ModuleMap; + resolver: Resolver; +}; diff --git a/packages/jest-runtime/build/types.js b/packages/jest-runtime/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-runtime/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-serializer/build/index.d.ts b/packages/jest-serializer/build/index.d.ts new file mode 100644 index 000000000000..af6694e62569 --- /dev/null +++ b/packages/jest-serializer/build/index.d.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ +/// +/// +declare type Path = string; +export declare function deserialize(buffer: Buffer): unknown; +export declare function serialize(content: unknown): Buffer; +export declare function readFileSync(filePath: Path): unknown; +export declare function writeFileSync(filePath: Path, content: unknown): void; +declare const _default: { + deserialize: typeof deserialize; + readFileSync: typeof readFileSync; + serialize: typeof serialize; + writeFileSync: typeof writeFileSync; +}; +export default _default; diff --git a/packages/jest-serializer/build/index.js b/packages/jest-serializer/build/index.js new file mode 100644 index 000000000000..f2bec8ba8d94 --- /dev/null +++ b/packages/jest-serializer/build/index.js @@ -0,0 +1,109 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.deserialize = deserialize; +exports.serialize = serialize; +exports.readFileSync = readFileSync; +exports.writeFileSync = writeFileSync; +exports.default = void 0; + +function _v() { + const data = require('v8'); + + _v = function () { + return data; + }; + + return data; +} + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +// TODO: Remove this +/// +// JSON and V8 serializers are both stable when it comes to compatibility. The +// current JSON specification is well defined in RFC 8259, and V8 ensures that +// the versions are compatible by encoding the serialization version in the own +// generated buffer. +// In memory functions. +function deserialize(buffer) { + return (0, _v().deserialize)(buffer); +} + +function serialize(content) { + return (0, _v().serialize)(content); +} // Synchronous filesystem functions. + +function readFileSync(filePath) { + return (0, _v().deserialize)(fs().readFileSync(filePath)); +} + +function writeFileSync(filePath, content) { + return fs().writeFileSync(filePath, (0, _v().serialize)(content)); +} + +var _default = { + deserialize, + readFileSync, + serialize, + writeFileSync +}; +exports.default = _default; diff --git a/packages/jest-snapshot/build/InlineSnapshots.d.ts b/packages/jest-snapshot/build/InlineSnapshots.d.ts new file mode 100644 index 000000000000..a93ff53df257 --- /dev/null +++ b/packages/jest-snapshot/build/InlineSnapshots.d.ts @@ -0,0 +1,15 @@ +/** + * 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. + */ +import type { Expression } from '@babel/types'; +import type { Config } from '@jest/types'; +import type { Frame } from 'jest-message-util'; +export declare type InlineSnapshot = { + snapshot: string; + frame: Frame; + node?: Expression; +}; +export declare function saveInlineSnapshots(snapshots: Array, prettierPath: Config.Path): void; diff --git a/packages/jest-snapshot/build/InlineSnapshots.js b/packages/jest-snapshot/build/InlineSnapshots.js new file mode 100644 index 000000000000..651093953771 --- /dev/null +++ b/packages/jest-snapshot/build/InlineSnapshots.js @@ -0,0 +1,428 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.saveInlineSnapshots = saveInlineSnapshots; + +var path = _interopRequireWildcard(require('path')); + +var fs = _interopRequireWildcard(require('graceful-fs')); + +var _semver = _interopRequireDefault(require('semver')); + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var jestWriteFile = + global[Symbol.for('jest-native-write-file')] || fs.writeFileSync; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var jestReadFile = + global[Symbol.for('jest-native-read-file')] || fs.readFileSync; + +// @ts-expect-error requireOutside Babel transform +const babelTraverse = require(require.resolve('@babel/traverse', { + [(global['jest-symbol-do-not-touch'] || global.Symbol).for( + 'jest-resolve-outside-vm-option' + )]: true +})).default; // @ts-expect-error requireOutside Babel transform + +const generate = require(require.resolve('@babel/generator', { + [(global['jest-symbol-do-not-touch'] || global.Symbol).for( + 'jest-resolve-outside-vm-option' + )]: true +})).default; // @ts-expect-error requireOutside Babel transform + +const {file, templateElement, templateLiteral} = require(require.resolve( + '@babel/types', + { + [(global['jest-symbol-do-not-touch'] || global.Symbol).for( + 'jest-resolve-outside-vm-option' + )]: true + } +)); // @ts-expect-error requireOutside Babel transform + +const {parseSync} = require(require.resolve('@babel/core', { + [(global['jest-symbol-do-not-touch'] || global.Symbol).for( + 'jest-resolve-outside-vm-option' + )]: true +})); + +function saveInlineSnapshots(snapshots, prettierPath) { + const prettier = prettierPath // @ts-expect-error requireOutside Babel transform + ? require(require.resolve(prettierPath, { + [(global['jest-symbol-do-not-touch'] || global.Symbol).for( + 'jest-resolve-outside-vm-option' + )]: true + })) + : undefined; + const snapshotsByFile = groupSnapshotsByFile(snapshots); + + for (const sourceFilePath of Object.keys(snapshotsByFile)) { + saveSnapshotsForFile( + snapshotsByFile[sourceFilePath], + sourceFilePath, + prettier && _semver.default.gte(prettier.version, '1.5.0') + ? prettier + : undefined + ); + } +} + +const saveSnapshotsForFile = (snapshots, sourceFilePath, prettier) => { + const sourceFile = jestReadFile(sourceFilePath, 'utf8'); // TypeScript projects may not have a babel config; make sure they can be parsed anyway. + + const presets = [require.resolve('babel-preset-current-node-syntax')]; + const plugins = []; + + if (/\.tsx?$/.test(sourceFilePath)) { + plugins.push([ + require.resolve('@babel/plugin-syntax-typescript'), + { + isTSX: sourceFilePath.endsWith('x') + }, // unique name to make sure Babel does not complain about a possible duplicate plugin. + 'TypeScript syntax plugin added by Jest snapshot' + ]); + } // Record the matcher names seen during traversal and pass them down one + // by one to formatting parser. + + const snapshotMatcherNames = []; + const ast = parseSync(sourceFile, { + filename: sourceFilePath, + plugins, + presets, + root: path.dirname(sourceFilePath) + }); + + if (!ast) { + throw new Error(`jest-snapshot: Failed to parse ${sourceFilePath}`); + } + + traverseAst(snapshots, ast, snapshotMatcherNames); // substitute in the snapshots in reverse order, so slice calculations aren't thrown off. + + const sourceFileWithSnapshots = snapshots.reduceRight( + (sourceSoFar, nextSnapshot) => { + if ( + !nextSnapshot.node || + typeof nextSnapshot.node.start !== 'number' || + typeof nextSnapshot.node.end !== 'number' + ) { + throw new Error('Jest: no snapshot insert location found'); + } + + return ( + sourceSoFar.slice(0, nextSnapshot.node.start) + + generate(nextSnapshot.node, { + retainLines: true + }).code.trim() + + sourceSoFar.slice(nextSnapshot.node.end) + ); + }, + sourceFile + ); + const newSourceFile = prettier + ? runPrettier( + prettier, + sourceFilePath, + sourceFileWithSnapshots, + snapshotMatcherNames + ) + : sourceFileWithSnapshots; + + if (newSourceFile !== sourceFile) { + jestWriteFile(sourceFilePath, newSourceFile); + } +}; + +const groupSnapshotsBy = createKey => snapshots => + snapshots.reduce((object, inlineSnapshot) => { + const key = createKey(inlineSnapshot); + return {...object, [key]: (object[key] || []).concat(inlineSnapshot)}; + }, {}); + +const groupSnapshotsByFrame = groupSnapshotsBy(({frame: {line, column}}) => + typeof line === 'number' && typeof column === 'number' + ? `${line}:${column - 1}` + : '' +); +const groupSnapshotsByFile = groupSnapshotsBy(({frame: {file}}) => file); + +const indent = (snapshot, numIndents, indentation) => { + const lines = snapshot.split('\n'); // Prevent re-indentation of inline snapshots. + + if ( + lines.length >= 2 && + lines[1].startsWith(indentation.repeat(numIndents + 1)) + ) { + return snapshot; + } + + return lines + .map((line, index) => { + if (index === 0) { + // First line is either a 1-line snapshot or a blank line. + return line; + } else if (index !== lines.length - 1) { + // Do not indent empty lines. + if (line === '') { + return line; + } // Not last line, indent one level deeper than expect call. + + return indentation.repeat(numIndents + 1) + line; + } else { + // The last line should be placed on the same level as the expect call. + return indentation.repeat(numIndents) + line; + } + }) + .join('\n'); +}; + +const resolveAst = fileOrProgram => { + // Flow uses a 'Program' parent node, babel expects a 'File'. + let ast = fileOrProgram; + + if (ast.type !== 'File') { + ast = file(ast, ast.comments, ast.tokens); + delete ast.program.comments; + } + + return ast; +}; + +const traverseAst = (snapshots, fileOrProgram, snapshotMatcherNames) => { + const ast = resolveAst(fileOrProgram); + const groupedSnapshots = groupSnapshotsByFrame(snapshots); + const remainingSnapshots = new Set(snapshots.map(({snapshot}) => snapshot)); + babelTraverse(ast, { + CallExpression({node}) { + const {arguments: args, callee} = node; + + if ( + callee.type !== 'MemberExpression' || + callee.property.type !== 'Identifier' || + callee.property.loc == null + ) { + return; + } + + const {line, column} = callee.property.loc.start; + const snapshotsForFrame = groupedSnapshots[`${line}:${column}`]; + + if (!snapshotsForFrame) { + return; + } + + if (snapshotsForFrame.length > 1) { + throw new Error( + 'Jest: Multiple inline snapshots for the same call are not supported.' + ); + } + + snapshotMatcherNames.push(callee.property.name); + const snapshotIndex = args.findIndex( + ({type}) => type === 'TemplateLiteral' + ); + const values = snapshotsForFrame.map(inlineSnapshot => { + inlineSnapshot.node = node; + const {snapshot} = inlineSnapshot; + remainingSnapshots.delete(snapshot); + return templateLiteral( + [ + templateElement({ + raw: (0, _utils.escapeBacktickString)(snapshot) + }) + ], + [] + ); + }); + const replacementNode = values[0]; + + if (snapshotIndex > -1) { + args[snapshotIndex] = replacementNode; + } else { + args.push(replacementNode); + } + } + }); + + if (remainingSnapshots.size) { + throw new Error(`Jest: Couldn't locate all inline snapshots.`); + } +}; + +const runPrettier = ( + prettier, + sourceFilePath, + sourceFileWithSnapshots, + snapshotMatcherNames +) => { + // Resolve project configuration. + // For older versions of Prettier, do not load configuration. + const config = prettier.resolveConfig + ? prettier.resolveConfig.sync(sourceFilePath, { + editorconfig: true + }) + : null; // Detect the parser for the test file. + // For older versions of Prettier, fallback to a simple parser detection. + // @ts-expect-error + + const inferredParser = prettier.getFileInfo + ? prettier.getFileInfo.sync(sourceFilePath).inferredParser + : (config && typeof config.parser === 'string' && config.parser) || + simpleDetectParser(sourceFilePath); + + if (!inferredParser) { + throw new Error( + `Could not infer Prettier parser for file ${sourceFilePath}` + ); + } // Snapshots have now been inserted. Run prettier to make sure that the code is + // formatted, except snapshot indentation. Snapshots cannot be formatted until + // after the initial format because we don't know where the call expression + // will be placed (specifically its indentation). + + let newSourceFile = prettier.format(sourceFileWithSnapshots, { + ...config, + filepath: sourceFilePath + }); + + if (newSourceFile !== sourceFileWithSnapshots) { + // prettier moved things around, run it again to fix snapshot indentations. + newSourceFile = prettier.format(newSourceFile, { + ...config, + filepath: sourceFilePath, + parser: createFormattingParser(snapshotMatcherNames, inferredParser) + }); + } + + return newSourceFile; +}; // This parser formats snapshots to the correct indentation. + +const createFormattingParser = (snapshotMatcherNames, inferredParser) => ( + text, + parsers, + options +) => { + // Workaround for https://github.com/prettier/prettier/issues/3150 + options.parser = inferredParser; + const ast = resolveAst(parsers[inferredParser](text, options)); + babelTraverse(ast, { + CallExpression({node: {arguments: args, callee}}) { + var _options$tabWidth, _options$tabWidth2; + + if ( + callee.type !== 'MemberExpression' || + callee.property.type !== 'Identifier' || + !snapshotMatcherNames.includes(callee.property.name) || + !callee.loc || + callee.computed + ) { + return; + } + + let snapshotIndex; + let snapshot; + + for (let i = 0; i < args.length; i++) { + const node = args[i]; + + if (node.type === 'TemplateLiteral') { + snapshotIndex = i; + snapshot = node.quasis[0].value.raw; + } + } + + if (snapshot === undefined || snapshotIndex === undefined) { + return; + } + + const useSpaces = !options.useTabs; + snapshot = indent( + snapshot, + Math.ceil( + useSpaces + ? callee.loc.start.column / + ((_options$tabWidth = options.tabWidth) !== null && + _options$tabWidth !== void 0 + ? _options$tabWidth + : 1) + : callee.loc.start.column / 2 // Each tab is 2 characters. + ), + useSpaces + ? ' '.repeat( + (_options$tabWidth2 = options.tabWidth) !== null && + _options$tabWidth2 !== void 0 + ? _options$tabWidth2 + : 1 + ) + : '\t' + ); + const replacementNode = templateLiteral( + [ + templateElement({ + raw: snapshot + }) + ], + [] + ); + args[snapshotIndex] = replacementNode; + } + }); + return ast; +}; + +const simpleDetectParser = filePath => { + const extname = path.extname(filePath); + + if (/\.tsx?$/.test(extname)) { + return 'typescript'; + } + + return 'babel'; +}; diff --git a/packages/jest-snapshot/build/SnapshotResolver.d.ts b/packages/jest-snapshot/build/SnapshotResolver.d.ts new file mode 100644 index 000000000000..88a30e9881df --- /dev/null +++ b/packages/jest-snapshot/build/SnapshotResolver.d.ts @@ -0,0 +1,16 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export declare type SnapshotResolver = { + testPathForConsistencyCheck: string; + resolveSnapshotPath(testPath: Config.Path, extension?: string): Config.Path; + resolveTestPath(snapshotPath: Config.Path, extension?: string): Config.Path; +}; +export declare const EXTENSION = "snap"; +export declare const DOT_EXTENSION: string; +export declare const isSnapshotPath: (path: string) => boolean; +export declare const buildSnapshotResolver: (config: Config.ProjectConfig) => SnapshotResolver; diff --git a/packages/jest-snapshot/build/SnapshotResolver.js b/packages/jest-snapshot/build/SnapshotResolver.js new file mode 100644 index 000000000000..c45dc54be9b5 --- /dev/null +++ b/packages/jest-snapshot/build/SnapshotResolver.js @@ -0,0 +1,159 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.buildSnapshotResolver = exports.isSnapshotPath = exports.DOT_EXTENSION = exports.EXTENSION = void 0; + +var path = _interopRequireWildcard(require('path')); + +var _chalk = _interopRequireDefault(require('chalk')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 EXTENSION = 'snap'; +exports.EXTENSION = EXTENSION; +const DOT_EXTENSION = '.' + EXTENSION; +exports.DOT_EXTENSION = DOT_EXTENSION; + +const isSnapshotPath = path => path.endsWith(DOT_EXTENSION); + +exports.isSnapshotPath = isSnapshotPath; +const cache = new Map(); + +const buildSnapshotResolver = config => { + const key = config.rootDir; + + if (!cache.has(key)) { + cache.set(key, createSnapshotResolver(config.snapshotResolver)); + } + + return cache.get(key); +}; + +exports.buildSnapshotResolver = buildSnapshotResolver; + +function createSnapshotResolver(snapshotResolverPath) { + return typeof snapshotResolverPath === 'string' + ? createCustomSnapshotResolver(snapshotResolverPath) + : createDefaultSnapshotResolver(); +} + +function createDefaultSnapshotResolver() { + return { + resolveSnapshotPath: testPath => + path.join( + path.join(path.dirname(testPath), '__snapshots__'), + path.basename(testPath) + DOT_EXTENSION + ), + resolveTestPath: snapshotPath => + path.resolve( + path.dirname(snapshotPath), + '..', + path.basename(snapshotPath, DOT_EXTENSION) + ), + testPathForConsistencyCheck: path.posix.join( + 'consistency_check', + '__tests__', + 'example.test.js' + ) + }; +} + +function createCustomSnapshotResolver(snapshotResolverPath) { + const custom = require(snapshotResolverPath); + + const keys = [ + ['resolveSnapshotPath', 'function'], + ['resolveTestPath', 'function'], + ['testPathForConsistencyCheck', 'string'] + ]; + keys.forEach(([propName, requiredType]) => { + if (typeof custom[propName] !== requiredType) { + throw new TypeError(mustImplement(propName, requiredType)); + } + }); + const customResolver = { + resolveSnapshotPath: testPath => + custom.resolveSnapshotPath(testPath, DOT_EXTENSION), + resolveTestPath: snapshotPath => + custom.resolveTestPath(snapshotPath, DOT_EXTENSION), + testPathForConsistencyCheck: custom.testPathForConsistencyCheck + }; + verifyConsistentTransformations(customResolver); + return customResolver; +} + +function mustImplement(propName, requiredType) { + return ( + _chalk.default.bold( + `Custom snapshot resolver must implement a \`${propName}\` as a ${requiredType}.` + ) + + '\nDocumentation: https://facebook.github.io/jest/docs/en/configuration.html#snapshotResolver' + ); +} + +function verifyConsistentTransformations(custom) { + const resolvedSnapshotPath = custom.resolveSnapshotPath( + custom.testPathForConsistencyCheck + ); + const resolvedTestPath = custom.resolveTestPath(resolvedSnapshotPath); + + if (resolvedTestPath !== custom.testPathForConsistencyCheck) { + throw new Error( + _chalk.default.bold( + `Custom snapshot resolver functions must transform paths consistently, i.e. expects resolveTestPath(resolveSnapshotPath('${custom.testPathForConsistencyCheck}')) === ${resolvedTestPath}` + ) + ); + } +} diff --git a/packages/jest-snapshot/build/State.d.ts b/packages/jest-snapshot/build/State.d.ts new file mode 100644 index 000000000000..d312a0f576ea --- /dev/null +++ b/packages/jest-snapshot/build/State.d.ts @@ -0,0 +1,59 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export declare type SnapshotStateOptions = { + updateSnapshot: Config.SnapshotUpdateState; + prettierPath: Config.Path; + expand?: boolean; +}; +export declare type SnapshotMatchOptions = { + testName: string; + received: unknown; + key?: string; + inlineSnapshot?: string; + isInline: boolean; + error?: Error; +}; +declare type SnapshotReturnOptions = { + actual: string; + count: number; + expected?: string; + key: string; + pass: boolean; +}; +declare type SaveStatus = { + deleted: boolean; + saved: boolean; +}; +export default class SnapshotState { + private _counters; + private _dirty; + private _index; + private _updateSnapshot; + private _snapshotData; + private _initialData; + private _snapshotPath; + private _inlineSnapshots; + private _uncheckedKeys; + private _prettierPath; + added: number; + expand: boolean; + matched: number; + unmatched: number; + updated: number; + constructor(snapshotPath: Config.Path, options: SnapshotStateOptions); + markSnapshotsAsCheckedForTest(testName: string): void; + private _addSnapshot; + clear(): void; + save(): SaveStatus; + getUncheckedCount(): number; + getUncheckedKeys(): Array; + removeUncheckedKeys(): void; + match({ testName, received, key, inlineSnapshot, isInline, error, }: SnapshotMatchOptions): SnapshotReturnOptions; + fail(testName: string, _received: unknown, key?: string): string; +} +export {}; diff --git a/packages/jest-snapshot/build/State.js b/packages/jest-snapshot/build/State.js new file mode 100644 index 000000000000..48d16c9a68e5 --- /dev/null +++ b/packages/jest-snapshot/build/State.js @@ -0,0 +1,343 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var fs = _interopRequireWildcard(require('graceful-fs')); + +var _jestMessageUtil = require('jest-message-util'); + +var _InlineSnapshots = require('./InlineSnapshots'); + +var _utils = require('./utils'); + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var jestExistsFile = + global[Symbol.for('jest-native-exists-file')] || fs.existsSync; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class SnapshotState { + // @ts-expect-error + constructor(snapshotPath, options) { + _defineProperty(this, '_counters', void 0); + + _defineProperty(this, '_dirty', void 0); + + _defineProperty(this, '_index', void 0); + + _defineProperty(this, '_updateSnapshot', void 0); + + _defineProperty(this, '_snapshotData', void 0); + + _defineProperty(this, '_initialData', void 0); + + _defineProperty(this, '_snapshotPath', void 0); + + _defineProperty(this, '_inlineSnapshots', void 0); + + _defineProperty(this, '_uncheckedKeys', void 0); + + _defineProperty(this, '_prettierPath', void 0); + + _defineProperty(this, 'added', void 0); + + _defineProperty(this, 'expand', void 0); + + _defineProperty(this, 'matched', void 0); + + _defineProperty(this, 'unmatched', void 0); + + _defineProperty(this, 'updated', void 0); + + this._snapshotPath = snapshotPath; + const {data, dirty} = (0, _utils.getSnapshotData)( + this._snapshotPath, + options.updateSnapshot + ); + this._initialData = data; + this._snapshotData = data; + this._dirty = dirty; + this._prettierPath = options.prettierPath; + this._inlineSnapshots = []; + this._uncheckedKeys = new Set(Object.keys(this._snapshotData)); + this._counters = new Map(); + this._index = 0; + this.expand = options.expand || false; + this.added = 0; + this.matched = 0; + this.unmatched = 0; + this._updateSnapshot = options.updateSnapshot; + this.updated = 0; + } + + markSnapshotsAsCheckedForTest(testName) { + this._uncheckedKeys.forEach(uncheckedKey => { + if ((0, _utils.keyToTestName)(uncheckedKey) === testName) { + this._uncheckedKeys.delete(uncheckedKey); + } + }); + } + + _addSnapshot(key, receivedSerialized, options) { + this._dirty = true; + + if (options.isInline) { + const error = options.error || new Error(); + const lines = (0, _jestMessageUtil.getStackTraceLines)( + (0, _utils.removeLinesBeforeExternalMatcherTrap)(error.stack || '') + ); + const frame = (0, _jestMessageUtil.getTopFrame)(lines); + + if (!frame) { + throw new Error( + "Jest: Couldn't infer stack frame for inline snapshot." + ); + } + + this._inlineSnapshots.push({ + frame, + snapshot: receivedSerialized + }); + } else { + this._snapshotData[key] = receivedSerialized; + } + } + + clear() { + this._snapshotData = this._initialData; + this._inlineSnapshots = []; + this._counters = new Map(); + this._index = 0; + this.added = 0; + this.matched = 0; + this.unmatched = 0; + this.updated = 0; + } + + save() { + const hasExternalSnapshots = Object.keys(this._snapshotData).length; + const hasInlineSnapshots = this._inlineSnapshots.length; + const isEmpty = !hasExternalSnapshots && !hasInlineSnapshots; + const status = { + deleted: false, + saved: false + }; + + if ((this._dirty || this._uncheckedKeys.size) && !isEmpty) { + if (hasExternalSnapshots) { + (0, _utils.saveSnapshotFile)(this._snapshotData, this._snapshotPath); + } + + if (hasInlineSnapshots) { + (0, _InlineSnapshots.saveInlineSnapshots)( + this._inlineSnapshots, + this._prettierPath + ); + } + + status.saved = true; + } else if (!hasExternalSnapshots && jestExistsFile(this._snapshotPath)) { + if (this._updateSnapshot === 'all') { + fs.unlinkSync(this._snapshotPath); + } + + status.deleted = true; + } + + return status; + } + + getUncheckedCount() { + return this._uncheckedKeys.size || 0; + } + + getUncheckedKeys() { + return Array.from(this._uncheckedKeys); + } + + removeUncheckedKeys() { + if (this._updateSnapshot === 'all' && this._uncheckedKeys.size) { + this._dirty = true; + + this._uncheckedKeys.forEach(key => delete this._snapshotData[key]); + + this._uncheckedKeys.clear(); + } + } + + match({testName, received, key, inlineSnapshot, isInline, error}) { + this._counters.set(testName, (this._counters.get(testName) || 0) + 1); + + const count = Number(this._counters.get(testName)); + + if (!key) { + key = (0, _utils.testNameToKey)(testName, count); + } // Do not mark the snapshot as "checked" if the snapshot is inline and + // there's an external snapshot. This way the external snapshot can be + // removed with `--updateSnapshot`. + + if (!(isInline && this._snapshotData[key] !== undefined)) { + this._uncheckedKeys.delete(key); + } + + const receivedSerialized = (0, _utils.addExtraLineBreaks)( + (0, _utils.serialize)(received) + ); + const expected = isInline ? inlineSnapshot : this._snapshotData[key]; + const pass = expected === receivedSerialized; + const hasSnapshot = expected !== undefined; + const snapshotIsPersisted = isInline || fs.existsSync(this._snapshotPath); + + if (pass && !isInline) { + // Executing a snapshot file as JavaScript and writing the strings back + // when other snapshots have changed loses the proper escaping for some + // characters. Since we check every snapshot in every test, use the newly + // generated formatted string. + // Note that this is only relevant when a snapshot is added and the dirty + // flag is set. + this._snapshotData[key] = receivedSerialized; + } // These are the conditions on when to write snapshots: + // * There's no snapshot file in a non-CI environment. + // * There is a snapshot file and we decided to update the snapshot. + // * There is a snapshot file, but it doesn't have this snaphsot. + // These are the conditions on when not to write snapshots: + // * The update flag is set to 'none'. + // * There's no snapshot file or a file without this snapshot on a CI environment. + + if ( + (hasSnapshot && this._updateSnapshot === 'all') || + ((!hasSnapshot || !snapshotIsPersisted) && + (this._updateSnapshot === 'new' || this._updateSnapshot === 'all')) + ) { + if (this._updateSnapshot === 'all') { + if (!pass) { + if (hasSnapshot) { + this.updated++; + } else { + this.added++; + } + + this._addSnapshot(key, receivedSerialized, { + error, + isInline + }); + } else { + this.matched++; + } + } else { + this._addSnapshot(key, receivedSerialized, { + error, + isInline + }); + + this.added++; + } + + return { + actual: '', + count, + expected: '', + key, + pass: true + }; + } else { + if (!pass) { + this.unmatched++; + return { + actual: (0, _utils.removeExtraLineBreaks)(receivedSerialized), + count, + expected: + expected !== undefined + ? (0, _utils.removeExtraLineBreaks)(expected) + : undefined, + key, + pass: false + }; + } else { + this.matched++; + return { + actual: '', + count, + expected: '', + key, + pass: true + }; + } + } + } + + fail(testName, _received, key) { + this._counters.set(testName, (this._counters.get(testName) || 0) + 1); + + const count = Number(this._counters.get(testName)); + + if (!key) { + key = (0, _utils.testNameToKey)(testName, count); + } + + this._uncheckedKeys.delete(key); + + this.unmatched++; + return key; + } +} + +exports.default = SnapshotState; diff --git a/packages/jest-snapshot/build/colors.d.ts b/packages/jest-snapshot/build/colors.d.ts new file mode 100644 index 000000000000..23e290582ee8 --- /dev/null +++ b/packages/jest-snapshot/build/colors.d.ts @@ -0,0 +1,15 @@ +/** + * 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. + */ +export declare const aForeground2 = 90; +export declare const aBackground2 = 225; +export declare const bForeground2 = 23; +export declare const bBackground2 = 195; +export declare type RGB = [number, number, number]; +export declare const aForeground3: RGB; +export declare const aBackground3: RGB; +export declare const bForeground3: RGB; +export declare const bBackground3: RGB; diff --git a/packages/jest-snapshot/build/colors.js b/packages/jest-snapshot/build/colors.js new file mode 100644 index 000000000000..a48b31241a87 --- /dev/null +++ b/packages/jest-snapshot/build/colors.js @@ -0,0 +1,30 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.bBackground3 = exports.bForeground3 = exports.aBackground3 = exports.aForeground3 = exports.bBackground2 = exports.bForeground2 = exports.aBackground2 = exports.aForeground2 = void 0; + +/** + * 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. + */ +// https://jonasjacek.github.io/colors/ +const aForeground2 = 90; +exports.aForeground2 = aForeground2; +const aBackground2 = 225; +exports.aBackground2 = aBackground2; +const bForeground2 = 23; +exports.bForeground2 = bForeground2; +const bBackground2 = 195; +exports.bBackground2 = bBackground2; +const aForeground3 = [0x80, 0, 0x80]; +exports.aForeground3 = aForeground3; +const aBackground3 = [0xff, 0xd7, 0xff]; +exports.aBackground3 = aBackground3; +const bForeground3 = [0, 0x5f, 0x5f]; +exports.bForeground3 = bForeground3; +const bBackground3 = [0xd7, 0xff, 0xff]; +exports.bBackground3 = bBackground3; diff --git a/packages/jest-snapshot/build/dedentLines.d.ts b/packages/jest-snapshot/build/dedentLines.d.ts new file mode 100644 index 000000000000..1ebd410710a1 --- /dev/null +++ b/packages/jest-snapshot/build/dedentLines.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export declare const dedentLines: (input: Array) => Array | null; diff --git a/packages/jest-snapshot/build/dedentLines.js b/packages/jest-snapshot/build/dedentLines.js new file mode 100644 index 000000000000..b860c4d9ddc9 --- /dev/null +++ b/packages/jest-snapshot/build/dedentLines.js @@ -0,0 +1,149 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.dedentLines = void 0; + +/** + * 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 getIndentationLength = line => { + const result = /^( {2})+/.exec(line); + return result === null ? 0 : result[0].length; +}; + +const dedentLine = line => line.slice(getIndentationLength(line)); // Return true if: +// "key": "value has multiple lines\n… +// "key has multiple lines\n… + +const hasUnmatchedDoubleQuoteMarks = string => { + let n = 0; + let i = string.indexOf('"', 0); + + while (i !== -1) { + if (i === 0 || string[i - 1] !== '\\') { + n += 1; + } + + i = string.indexOf('"', i + 1); + } + + return n % 2 !== 0; +}; + +const isFirstLineOfTag = line => /^( {2})*\ { + let line = input[output.length]; + output.push(dedentLine(line)); + + if (line.includes('>')) { + return true; + } + + while (output.length < input.length) { + line = input[output.length]; + + if (hasUnmatchedDoubleQuoteMarks(line)) { + return false; // because props include a multiline string + } else if (isFirstLineOfTag(line)) { + // Recursion only if props have markup. + if (!dedentMarkup(input, output)) { + return false; + } + } else { + output.push(dedentLine(line)); + + if (line.includes('>')) { + return true; + } + } + } + + return false; +}; // Push dedented lines of markup onto output and return true; +// otherwise return false because: +// * props include a multiline string +// * text has more than one adjacent line +// * markup does not close + +const dedentMarkup = (input, output) => { + let line = input[output.length]; + + if (!dedentStartTag(input, output)) { + return false; + } + + if (input[output.length - 1].includes('/>')) { + return true; + } + + let isText = false; + const stack = []; + stack.push(getIndentationLength(line)); + + while (stack.length > 0 && output.length < input.length) { + line = input[output.length]; + + if (isFirstLineOfTag(line)) { + if (line.includes('')) { + stack.push(getIndentationLength(line)); + } + } + + isText = false; + } else { + if (isText) { + return false; // because text has more than one adjacent line + } + + const indentationLengthOfTag = stack[stack.length - 1]; + output.push(line.slice(indentationLengthOfTag + 2)); + isText = true; + } + } + + return stack.length === 0; +}; // Return lines unindented by heuristic; +// otherwise return null because: +// * props include a multiline string +// * text has more than one adjacent line +// * markup does not close + +const dedentLines = input => { + const output = []; + + while (output.length < input.length) { + const line = input[output.length]; + + if (hasUnmatchedDoubleQuoteMarks(line)) { + return null; + } else if (isFirstLineOfTag(line)) { + if (!dedentMarkup(input, output)) { + return null; + } + } else { + output.push(dedentLine(line)); + } + } + + return output; +}; + +exports.dedentLines = dedentLines; diff --git a/packages/jest-snapshot/build/index.d.ts b/packages/jest-snapshot/build/index.d.ts new file mode 100644 index 000000000000..87a4f9c934e9 --- /dev/null +++ b/packages/jest-snapshot/build/index.d.ts @@ -0,0 +1,34 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { FS as HasteFS } from 'jest-haste-map'; +import { SnapshotResolver as JestSnapshotResolver } from './SnapshotResolver'; +import SnapshotState from './State'; +import type { Context, ExpectationResult } from './types'; +import * as utils from './utils'; +declare const JestSnapshot: { + EXTENSION: string; + SnapshotState: typeof SnapshotState; + addSerializer: (plugin: import("pretty-format").Plugin) => void; + buildSnapshotResolver: (config: Config.ProjectConfig) => JestSnapshotResolver; + cleanup: (hasteFS: HasteFS, update: Config.SnapshotUpdateState, snapshotResolver: JestSnapshotResolver, testPathIgnorePatterns?: string[] | undefined) => { + filesRemoved: number; + filesRemovedList: Array; + }; + getSerializers: () => import("pretty-format").Plugins; + isSnapshotPath: (path: string) => boolean; + toMatchInlineSnapshot: (this: Context, received: unknown, propertiesOrSnapshot?: string | object | undefined, inlineSnapshot?: string | undefined) => ExpectationResult; + toMatchSnapshot: (this: Context, received: unknown, propertiesOrHint?: string | object | undefined, hint?: string | undefined) => ExpectationResult; + toThrowErrorMatchingInlineSnapshot: (this: Context, received: unknown, inlineSnapshot?: string | undefined, fromPromise?: boolean | undefined) => ExpectationResult; + toThrowErrorMatchingSnapshot: (this: Context, received: unknown, hint: string | undefined, fromPromise: boolean) => ExpectationResult; + utils: typeof utils; +}; +declare namespace JestSnapshot { + type SnapshotResolver = JestSnapshotResolver; + type SnapshotStateType = SnapshotState; +} +export = JestSnapshot; diff --git a/packages/jest-snapshot/build/index.js b/packages/jest-snapshot/build/index.js new file mode 100644 index 000000000000..b8b176963275 --- /dev/null +++ b/packages/jest-snapshot/build/index.js @@ -0,0 +1,612 @@ +'use strict'; + +var fs = _interopRequireWildcard(require('graceful-fs')); + +var _jestMatcherUtils = require('jest-matcher-utils'); + +var _SnapshotResolver = require('./SnapshotResolver'); + +var _State = _interopRequireDefault(require('./State')); + +var _plugins = require('./plugins'); + +var _printSnapshot = require('./printSnapshot'); + +var utils = _interopRequireWildcard(require('./utils')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var jestExistsFile = + global[Symbol.for('jest-native-exists-file')] || fs.existsSync; +const DID_NOT_THROW = 'Received function did not throw'; // same as toThrow + +const NOT_SNAPSHOT_MATCHERS = `Snapshot matchers cannot be used with ${(0, +_jestMatcherUtils.BOLD_WEIGHT)('not')}`; +const INDENTATION_REGEX = /^([^\S\n]*)\S/m; // Display name in report when matcher fails same as in snapshot file, +// but with optional hint argument in bold weight. + +const printSnapshotName = (concatenatedBlockNames = '', hint = '', count) => { + const hasNames = concatenatedBlockNames.length !== 0; + const hasHint = hint.length !== 0; + return ( + 'Snapshot name: `' + + (hasNames ? utils.escapeBacktickString(concatenatedBlockNames) : '') + + (hasNames && hasHint ? ': ' : '') + + (hasHint + ? (0, _jestMatcherUtils.BOLD_WEIGHT)(utils.escapeBacktickString(hint)) + : '') + + ' ' + + count + + '`' + ); +}; + +function stripAddedIndentation(inlineSnapshot) { + // Find indentation if exists. + const match = inlineSnapshot.match(INDENTATION_REGEX); + + if (!match || !match[1]) { + // No indentation. + return inlineSnapshot; + } + + const indentation = match[1]; + const lines = inlineSnapshot.split('\n'); + + if (lines.length <= 2) { + // Must be at least 3 lines. + return inlineSnapshot; + } + + if (lines[0].trim() !== '' || lines[lines.length - 1].trim() !== '') { + // If not blank first and last lines, abort. + return inlineSnapshot; + } + + for (let i = 1; i < lines.length - 1; i++) { + if (lines[i] !== '') { + if (lines[i].indexOf(indentation) !== 0) { + // All lines except first and last should either be blank or have the same + // indent as the first line (or more). If this isn't the case we don't + // want to touch the snapshot at all. + return inlineSnapshot; + } + + lines[i] = lines[i].substr(indentation.length); + } + } // Last line is a special case because it won't have the same indent as others + // but may still have been given some indent to line up. + + lines[lines.length - 1] = ''; // Return inline snapshot, now at indent 0. + + inlineSnapshot = lines.join('\n'); + return inlineSnapshot; +} + +const fileExists = (filePath, hasteFS) => + hasteFS.exists(filePath) || jestExistsFile(filePath); + +const cleanup = (hasteFS, update, snapshotResolver, testPathIgnorePatterns) => { + const pattern = '\\.' + _SnapshotResolver.EXTENSION + '$'; + const files = hasteFS.matchFiles(pattern); + let testIgnorePatternsRegex = null; + + if (testPathIgnorePatterns && testPathIgnorePatterns.length > 0) { + testIgnorePatternsRegex = new RegExp(testPathIgnorePatterns.join('|')); + } + + const list = files.filter(snapshotFile => { + const testPath = snapshotResolver.resolveTestPath(snapshotFile); // ignore snapshots of ignored tests + + if (testIgnorePatternsRegex && testIgnorePatternsRegex.test(testPath)) { + return false; + } + + if (!fileExists(testPath, hasteFS)) { + if (update === 'all') { + fs.unlinkSync(snapshotFile); + } + + return true; + } + + return false; + }); + return { + filesRemoved: list.length, + filesRemovedList: list + }; +}; + +const toMatchSnapshot = function (received, propertiesOrHint, hint) { + const matcherName = 'toMatchSnapshot'; + let properties; + const length = arguments.length; + + if (length === 2 && typeof propertiesOrHint === 'string') { + hint = propertiesOrHint; + } else if (length >= 2) { + if (typeof propertiesOrHint !== 'object' || propertiesOrHint === null) { + const options = { + isNot: this.isNot, + promise: this.promise + }; + let printedWithType = (0, _jestMatcherUtils.printWithType)( + 'Expected properties', + propertiesOrHint, + _printSnapshot.printExpected + ); + + if (length === 3) { + options.secondArgument = 'hint'; + options.secondArgumentColor = _jestMatcherUtils.BOLD_WEIGHT; + + if (propertiesOrHint == null) { + printedWithType += `\n\nTo provide a hint without properties: toMatchSnapshot('hint')`; + } + } + + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + _printSnapshot.PROPERTIES_ARG, + options + ), + `Expected ${(0, _jestMatcherUtils.EXPECTED_COLOR)( + 'properties' + )} must be an object`, + printedWithType + ) + ); + } // Future breaking change: Snapshot hint must be a string + // if (arguments.length === 3 && typeof hint !== 'string') {} + + properties = propertiesOrHint; + } + + return _toMatchSnapshot({ + context: this, + hint, + isInline: false, + matcherName, + properties, + received + }); +}; + +const toMatchInlineSnapshot = function ( + received, + propertiesOrSnapshot, + inlineSnapshot +) { + const matcherName = 'toMatchInlineSnapshot'; + let properties; + const length = arguments.length; + + if (length === 2 && typeof propertiesOrSnapshot === 'string') { + inlineSnapshot = propertiesOrSnapshot; + } else if (length >= 2) { + const options = { + isNot: this.isNot, + promise: this.promise + }; + + if (length === 3) { + options.secondArgument = _printSnapshot.SNAPSHOT_ARG; + options.secondArgumentColor = _printSnapshot.noColor; + } + + if ( + typeof propertiesOrSnapshot !== 'object' || + propertiesOrSnapshot === null + ) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + _printSnapshot.PROPERTIES_ARG, + options + ), + `Expected ${(0, _jestMatcherUtils.EXPECTED_COLOR)( + 'properties' + )} must be an object`, + (0, _jestMatcherUtils.printWithType)( + 'Expected properties', + propertiesOrSnapshot, + _printSnapshot.printExpected + ) + ) + ); + } + + if (length === 3 && typeof inlineSnapshot !== 'string') { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + _printSnapshot.PROPERTIES_ARG, + options + ), + `Inline snapshot must be a string`, + (0, _jestMatcherUtils.printWithType)( + 'Inline snapshot', + inlineSnapshot, + utils.serialize + ) + ) + ); + } + + properties = propertiesOrSnapshot; + } + + return _toMatchSnapshot({ + context: this, + inlineSnapshot: + inlineSnapshot !== undefined + ? stripAddedIndentation(inlineSnapshot) + : undefined, + isInline: true, + matcherName, + properties, + received + }); +}; + +const _toMatchSnapshot = config => { + const { + context, + hint, + inlineSnapshot, + isInline, + matcherName, + properties + } = config; + let {received} = config; + context.dontThrow && context.dontThrow(); + const {currentTestName, isNot, snapshotState} = context; + + if (isNot) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _printSnapshot.matcherHintFromConfig)(config, false), + NOT_SNAPSHOT_MATCHERS + ) + ); + } + + if (snapshotState == null) { + // Because the state is the problem, this is not a matcher error. + // Call generic stringify from jest-matcher-utils package + // because uninitialized snapshot state does not need snapshot serializers. + throw new Error( + (0, _printSnapshot.matcherHintFromConfig)(config, false) + + '\n\n' + + `Snapshot state must be initialized` + + '\n\n' + + (0, _jestMatcherUtils.printWithType)( + 'Snapshot state', + snapshotState, + _jestMatcherUtils.stringify + ) + ); + } + + const fullTestName = + currentTestName && hint + ? `${currentTestName}: ${hint}` + : currentTestName || ''; // future BREAKING change: || hint + + if (typeof properties === 'object') { + if (typeof received !== 'object' || received === null) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _printSnapshot.matcherHintFromConfig)(config, false), + `${(0, _jestMatcherUtils.RECEIVED_COLOR)( + 'received' + )} value must be an object when the matcher has ${(0, + _jestMatcherUtils.EXPECTED_COLOR)('properties')}`, + (0, _jestMatcherUtils.printWithType)( + 'Received', + received, + _printSnapshot.printReceived + ) + ) + ); + } + + const propertyPass = context.equals(received, properties, [ + context.utils.iterableEquality, + context.utils.subsetEquality + ]); + + if (!propertyPass) { + const key = snapshotState.fail(fullTestName, received); + const matched = /(\d+)$/.exec(key); + const count = matched === null ? 1 : Number(matched[1]); + + const message = () => + (0, _printSnapshot.matcherHintFromConfig)(config, false) + + '\n\n' + + printSnapshotName(currentTestName, hint, count) + + '\n\n' + + (0, _printSnapshot.printPropertiesAndReceived)( + properties, + received, + snapshotState.expand + ); + + return { + message, + name: matcherName, + pass: false + }; + } else { + received = utils.deepMerge(received, properties); + } + } + + const result = snapshotState.match({ + error: context.error, + inlineSnapshot, + isInline, + received, + testName: fullTestName + }); + const {actual, count, expected, pass} = result; + + if (pass) { + return { + message: () => '', + pass: true + }; + } + + const message = + expected === undefined + ? () => + (0, _printSnapshot.matcherHintFromConfig)(config, true) + + '\n\n' + + printSnapshotName(currentTestName, hint, count) + + '\n\n' + + `New snapshot was ${(0, _jestMatcherUtils.BOLD_WEIGHT)( + 'not written' + )}. The update flag ` + + `must be explicitly passed to write a new snapshot.\n\n` + + `This is likely because this test is run in a continuous integration ` + + `(CI) environment in which snapshots are not written by default.\n\n` + + `Received:${actual.includes('\n') ? '\n' : ' '}${(0, + _printSnapshot.bReceivedColor)(actual)}` + : () => + (0, _printSnapshot.matcherHintFromConfig)(config, true) + + '\n\n' + + printSnapshotName(currentTestName, hint, count) + + '\n\n' + + (0, _printSnapshot.printSnapshotAndReceived)( + expected, + actual, + received, + snapshotState.expand + ); // Passing the actual and expected objects so that a custom reporter + // could access them, for example in order to display a custom visual diff, + // or create a different error message + + return { + actual, + expected, + message, + name: matcherName, + pass: false + }; +}; + +const toThrowErrorMatchingSnapshot = function ( + received, + hint, // because error TS1016 for hint?: string + fromPromise +) { + const matcherName = 'toThrowErrorMatchingSnapshot'; // Future breaking change: Snapshot hint must be a string + // if (hint !== undefined && typeof hint !== string) {} + + return _toThrowErrorMatchingSnapshot( + { + context: this, + hint, + isInline: false, + matcherName, + received + }, + fromPromise + ); +}; + +const toThrowErrorMatchingInlineSnapshot = function ( + received, + inlineSnapshot, + fromPromise +) { + const matcherName = 'toThrowErrorMatchingInlineSnapshot'; + + if (inlineSnapshot !== undefined && typeof inlineSnapshot !== 'string') { + const options = { + expectedColor: _printSnapshot.noColor, + isNot: this.isNot, + promise: this.promise + }; + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + _printSnapshot.SNAPSHOT_ARG, + options + ), + `Inline snapshot must be a string`, + (0, _jestMatcherUtils.printWithType)( + 'Inline snapshot', + inlineSnapshot, + utils.serialize + ) + ) + ); + } + + return _toThrowErrorMatchingSnapshot( + { + context: this, + inlineSnapshot: + inlineSnapshot !== undefined + ? stripAddedIndentation(inlineSnapshot) + : undefined, + isInline: true, + matcherName, + received + }, + fromPromise + ); +}; + +const _toThrowErrorMatchingSnapshot = (config, fromPromise) => { + const { + context, + hint, + inlineSnapshot, + isInline, + matcherName, + received + } = config; + context.dontThrow && context.dontThrow(); + const {isNot, promise} = context; + + if (!fromPromise) { + if (typeof received !== 'function') { + const options = { + isNot, + promise + }; + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + '', + options + ), + `${(0, _jestMatcherUtils.RECEIVED_COLOR)( + 'received' + )} value must be a function`, + (0, _jestMatcherUtils.printWithType)( + 'Received', + received, + _printSnapshot.printReceived + ) + ) + ); + } + } + + if (isNot) { + throw new Error( + (0, _jestMatcherUtils.matcherErrorMessage)( + (0, _printSnapshot.matcherHintFromConfig)(config, false), + NOT_SNAPSHOT_MATCHERS + ) + ); + } + + let error; + + if (fromPromise) { + error = received; + } else { + try { + received(); + } catch (e) { + error = e; + } + } + + if (error === undefined) { + // Because the received value is a function, this is not a matcher error. + throw new Error( + (0, _printSnapshot.matcherHintFromConfig)(config, false) + + '\n\n' + + DID_NOT_THROW + ); + } + + return _toMatchSnapshot({ + context, + hint, + inlineSnapshot, + isInline, + matcherName, + received: error.message + }); +}; + +const JestSnapshot = { + EXTENSION: _SnapshotResolver.EXTENSION, + SnapshotState: _State.default, + addSerializer: _plugins.addSerializer, + buildSnapshotResolver: _SnapshotResolver.buildSnapshotResolver, + cleanup, + getSerializers: _plugins.getSerializers, + isSnapshotPath: _SnapshotResolver.isSnapshotPath, + toMatchInlineSnapshot, + toMatchSnapshot, + toThrowErrorMatchingInlineSnapshot, + toThrowErrorMatchingSnapshot, + utils +}; +module.exports = JestSnapshot; diff --git a/packages/jest-snapshot/build/mockSerializer.d.ts b/packages/jest-snapshot/build/mockSerializer.d.ts new file mode 100644 index 000000000000..828986e2cd4a --- /dev/null +++ b/packages/jest-snapshot/build/mockSerializer.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +import type { NewPlugin } from 'pretty-format'; +export declare const serialize: NewPlugin['serialize']; +export declare const test: NewPlugin['test']; +declare const plugin: NewPlugin; +export default plugin; diff --git a/packages/jest-snapshot/build/mockSerializer.js b/packages/jest-snapshot/build/mockSerializer.js new file mode 100644 index 000000000000..b7a339c6271c --- /dev/null +++ b/packages/jest-snapshot/build/mockSerializer.js @@ -0,0 +1,52 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = exports.test = exports.serialize = void 0; + +/** + * 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 serialize = (val, config, indentation, depth, refs, printer) => { + // Serialize a non-default name, even if config.printFunctionName is false. + const name = val.getMockName(); + const nameString = name === 'jest.fn()' ? '' : ' ' + name; + let callsString = ''; + + if (val.mock.calls.length !== 0) { + const indentationNext = indentation + config.indent; + callsString = + ' {' + + config.spacingOuter + + indentationNext + + '"calls": ' + + printer(val.mock.calls, config, indentationNext, depth, refs) + + (config.min ? ', ' : ',') + + config.spacingOuter + + indentationNext + + '"results": ' + + printer(val.mock.results, config, indentationNext, depth, refs) + + (config.min ? '' : ',') + + config.spacingOuter + + indentation + + '}'; + } + + return '[MockFunction' + nameString + ']' + callsString; +}; + +exports.serialize = serialize; + +const test = val => val && !!val._isMockFunction; + +exports.test = test; +const plugin = { + serialize, + test +}; +var _default = plugin; +exports.default = _default; diff --git a/packages/jest-snapshot/build/plugins.d.ts b/packages/jest-snapshot/build/plugins.d.ts new file mode 100644 index 000000000000..89b6d4800285 --- /dev/null +++ b/packages/jest-snapshot/build/plugins.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import { Plugin as PrettyFormatPlugin, Plugins as PrettyFormatPlugins } from 'pretty-format'; +export declare const addSerializer: (plugin: PrettyFormatPlugin) => void; +export declare const getSerializers: () => PrettyFormatPlugins; diff --git a/packages/jest-snapshot/build/plugins.js b/packages/jest-snapshot/build/plugins.js new file mode 100644 index 000000000000..6a3625b86e7a --- /dev/null +++ b/packages/jest-snapshot/build/plugins.js @@ -0,0 +1,48 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.getSerializers = exports.addSerializer = void 0; + +var _prettyFormat = require('pretty-format'); + +var _mockSerializer = _interopRequireDefault(require('./mockSerializer')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 { + DOMCollection, + DOMElement, + Immutable, + ReactElement, + ReactTestComponent, + AsymmetricMatcher +} = _prettyFormat.plugins; +let PLUGINS = [ + ReactTestComponent, + ReactElement, + DOMElement, + DOMCollection, + Immutable, + _mockSerializer.default, + AsymmetricMatcher +]; // Prepend to list so the last added is the first tested. + +const addSerializer = plugin => { + PLUGINS = [plugin].concat(PLUGINS); +}; + +exports.addSerializer = addSerializer; + +const getSerializers = () => PLUGINS; + +exports.getSerializers = getSerializers; diff --git a/packages/jest-snapshot/build/printSnapshot.d.ts b/packages/jest-snapshot/build/printSnapshot.d.ts new file mode 100644 index 000000000000..0325346453a8 --- /dev/null +++ b/packages/jest-snapshot/build/printSnapshot.d.ts @@ -0,0 +1,24 @@ +/** + * 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. + */ +import chalk = require('chalk'); +import { DiffOptionsColor } from 'jest-diff'; +import type { MatchSnapshotConfig } from './types'; +declare type Chalk = chalk.Chalk; +export declare const getSnapshotColorForChalkInstance: (chalkInstance: Chalk) => DiffOptionsColor; +export declare const getReceivedColorForChalkInstance: (chalkInstance: Chalk) => DiffOptionsColor; +export declare const aSnapshotColor: DiffOptionsColor; +export declare const bReceivedColor: DiffOptionsColor; +export declare const noColor: (string: string) => string; +export declare const HINT_ARG = "hint"; +export declare const SNAPSHOT_ARG = "snapshot"; +export declare const PROPERTIES_ARG = "properties"; +export declare const matcherHintFromConfig: ({ context: { isNot, promise }, hint, inlineSnapshot, matcherName, properties, }: MatchSnapshotConfig, isUpdatable: boolean) => string; +export declare const printExpected: (val: unknown) => string; +export declare const printReceived: (val: unknown) => string; +export declare const printPropertiesAndReceived: (properties: object, received: object, expand: boolean) => string; +export declare const printSnapshotAndReceived: (a: string, b: string, received: unknown, expand: boolean) => string; +export {}; diff --git a/packages/jest-snapshot/build/printSnapshot.js b/packages/jest-snapshot/build/printSnapshot.js new file mode 100644 index 000000000000..46a8c3150e7d --- /dev/null +++ b/packages/jest-snapshot/build/printSnapshot.js @@ -0,0 +1,385 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.printSnapshotAndReceived = exports.printPropertiesAndReceived = exports.printReceived = exports.printExpected = exports.matcherHintFromConfig = exports.PROPERTIES_ARG = exports.SNAPSHOT_ARG = exports.HINT_ARG = exports.noColor = exports.bReceivedColor = exports.aSnapshotColor = exports.getReceivedColorForChalkInstance = exports.getSnapshotColorForChalkInstance = void 0; + +var _chalk = _interopRequireDefault(require('chalk')); + +var _utils = require('expect/build/utils'); + +var _jestDiff = require('jest-diff'); + +var _jestGetType = _interopRequireDefault(require('jest-get-type')); + +var _jestMatcherUtils = require('jest-matcher-utils'); + +var _prettyFormat = _interopRequireDefault(require('pretty-format')); + +var _colors = require('./colors'); + +var _dedentLines = require('./dedentLines'); + +var _utils2 = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ + +/* eslint-disable local/ban-types-eventually */ +// Temporary hack because getObjectSubset has known limitations, +// is not in the public interface of the expect package, +// and the long-term goal is to use a non-serialization diff. +// Make sure to remove file from `exports` in `expect/package.json`. +const getSnapshotColorForChalkInstance = chalkInstance => { + const level = chalkInstance.level; + + if (level === 3) { + return chalkInstance + .rgb( + _colors.aForeground3[0], + _colors.aForeground3[1], + _colors.aForeground3[2] + ) + .bgRgb( + _colors.aBackground3[0], + _colors.aBackground3[1], + _colors.aBackground3[2] + ); + } + + if (level === 2) { + return chalkInstance + .ansi256(_colors.aForeground2) + .bgAnsi256(_colors.aBackground2); + } + + return chalkInstance.magenta.bgYellowBright; +}; + +exports.getSnapshotColorForChalkInstance = getSnapshotColorForChalkInstance; + +const getReceivedColorForChalkInstance = chalkInstance => { + const level = chalkInstance.level; + + if (level === 3) { + return chalkInstance + .rgb( + _colors.bForeground3[0], + _colors.bForeground3[1], + _colors.bForeground3[2] + ) + .bgRgb( + _colors.bBackground3[0], + _colors.bBackground3[1], + _colors.bBackground3[2] + ); + } + + if (level === 2) { + return chalkInstance + .ansi256(_colors.bForeground2) + .bgAnsi256(_colors.bBackground2); + } + + return chalkInstance.cyan.bgWhiteBright; // also known as teal +}; + +exports.getReceivedColorForChalkInstance = getReceivedColorForChalkInstance; +const aSnapshotColor = getSnapshotColorForChalkInstance(_chalk.default); +exports.aSnapshotColor = aSnapshotColor; +const bReceivedColor = getReceivedColorForChalkInstance(_chalk.default); +exports.bReceivedColor = bReceivedColor; + +const noColor = string => string; + +exports.noColor = noColor; +const HINT_ARG = 'hint'; +exports.HINT_ARG = HINT_ARG; +const SNAPSHOT_ARG = 'snapshot'; +exports.SNAPSHOT_ARG = SNAPSHOT_ARG; +const PROPERTIES_ARG = 'properties'; +exports.PROPERTIES_ARG = PROPERTIES_ARG; + +const matcherHintFromConfig = ( + {context: {isNot, promise}, hint, inlineSnapshot, matcherName, properties}, + isUpdatable +) => { + const options = { + isNot, + promise + }; + + if (isUpdatable) { + options.receivedColor = bReceivedColor; + } + + let expectedArgument = ''; + + if (typeof properties === 'object') { + expectedArgument = PROPERTIES_ARG; + + if (isUpdatable) { + options.expectedColor = noColor; + } + + if (typeof hint === 'string' && hint.length !== 0) { + options.secondArgument = HINT_ARG; + options.secondArgumentColor = _jestMatcherUtils.BOLD_WEIGHT; + } else if (typeof inlineSnapshot === 'string') { + options.secondArgument = SNAPSHOT_ARG; + + if (isUpdatable) { + options.secondArgumentColor = aSnapshotColor; + } else { + options.secondArgumentColor = noColor; + } + } + } else { + if (typeof hint === 'string' && hint.length !== 0) { + expectedArgument = HINT_ARG; + options.expectedColor = _jestMatcherUtils.BOLD_WEIGHT; + } else if (typeof inlineSnapshot === 'string') { + expectedArgument = SNAPSHOT_ARG; + + if (isUpdatable) { + options.expectedColor = aSnapshotColor; + } + } + } + + return (0, _jestMatcherUtils.matcherHint)( + matcherName, + undefined, + expectedArgument, + options + ); +}; // Given array of diffs, return string: +// * include common substrings +// * exclude change substrings which have opposite op +// * include change substrings which have argument op +// with change color only if there is a common substring + +exports.matcherHintFromConfig = matcherHintFromConfig; + +const joinDiffs = (diffs, op, hasCommon) => + diffs.reduce( + (reduced, diff) => + reduced + + (diff[0] === _jestDiff.DIFF_EQUAL + ? diff[1] + : diff[0] !== op + ? '' + : hasCommon + ? (0, _jestMatcherUtils.INVERTED_COLOR)(diff[1]) + : diff[1]), + '' + ); + +const isLineDiffable = received => { + const receivedType = (0, _jestGetType.default)(received); + + if (_jestGetType.default.isPrimitive(received)) { + return typeof received === 'string'; + } + + if ( + receivedType === 'date' || + receivedType === 'function' || + receivedType === 'regexp' + ) { + return false; + } + + if (received instanceof Error) { + return false; + } + + if ( + receivedType === 'object' && + typeof received.asymmetricMatch === 'function' + ) { + return false; + } + + return true; +}; + +const printExpected = val => + (0, _jestMatcherUtils.EXPECTED_COLOR)((0, _utils2.minify)(val)); + +exports.printExpected = printExpected; + +const printReceived = val => + (0, _jestMatcherUtils.RECEIVED_COLOR)((0, _utils2.minify)(val)); + +exports.printReceived = printReceived; + +const printPropertiesAndReceived = (properties, received, expand) => { + const aAnnotation = 'Expected properties'; + const bAnnotation = 'Received value'; + + if (isLineDiffable(properties) && isLineDiffable(received)) { + return (0, _jestDiff.diffLinesUnified)( + (0, _utils2.serialize)(properties).split('\n'), + (0, _utils2.serialize)( + (0, _utils.getObjectSubset)(received, properties) + ).split('\n'), + { + aAnnotation, + aColor: _jestMatcherUtils.EXPECTED_COLOR, + bAnnotation, + bColor: _jestMatcherUtils.RECEIVED_COLOR, + changeLineTrailingSpaceColor: _chalk.default.bgYellow, + commonLineTrailingSpaceColor: _chalk.default.bgYellow, + emptyFirstOrLastLinePlaceholder: '↵', + // U+21B5 + expand, + includeChangeCounts: true + } + ); + } + + const printLabel = (0, _jestMatcherUtils.getLabelPrinter)( + aAnnotation, + bAnnotation + ); + return ( + printLabel(aAnnotation) + + printExpected(properties) + + '\n' + + printLabel(bAnnotation) + + printReceived(received) + ); +}; + +exports.printPropertiesAndReceived = printPropertiesAndReceived; +const MAX_DIFF_STRING_LENGTH = 20000; + +const printSnapshotAndReceived = (a, b, received, expand) => { + const aAnnotation = 'Snapshot'; + const bAnnotation = 'Received'; + const aColor = aSnapshotColor; + const bColor = bReceivedColor; + const options = { + aAnnotation, + aColor, + bAnnotation, + bColor, + changeLineTrailingSpaceColor: noColor, + commonLineTrailingSpaceColor: _chalk.default.bgYellow, + emptyFirstOrLastLinePlaceholder: '↵', + // U+21B5 + expand, + includeChangeCounts: true + }; + + if (typeof received === 'string') { + if ( + a.length >= 2 && + a.startsWith('"') && + a.endsWith('"') && + b === (0, _prettyFormat.default)(received) + ) { + // If snapshot looks like default serialization of a string + // and received is string which has default serialization. + if (!a.includes('\n') && !b.includes('\n')) { + // If neither string is multiline, + // display as labels and quoted strings. + let aQuoted = a; + let bQuoted = b; + + if ( + a.length - 2 <= MAX_DIFF_STRING_LENGTH && + b.length - 2 <= MAX_DIFF_STRING_LENGTH + ) { + const diffs = (0, _jestDiff.diffStringsRaw)( + a.slice(1, -1), + b.slice(1, -1), + true + ); + const hasCommon = diffs.some( + diff => diff[0] === _jestDiff.DIFF_EQUAL + ); + aQuoted = + '"' + joinDiffs(diffs, _jestDiff.DIFF_DELETE, hasCommon) + '"'; + bQuoted = + '"' + joinDiffs(diffs, _jestDiff.DIFF_INSERT, hasCommon) + '"'; + } + + const printLabel = (0, _jestMatcherUtils.getLabelPrinter)( + aAnnotation, + bAnnotation + ); + return ( + printLabel(aAnnotation) + + aColor(aQuoted) + + '\n' + + printLabel(bAnnotation) + + bColor(bQuoted) + ); + } // Else either string is multiline, so display as unquoted strings. + + a = (0, _utils2.deserializeString)(a); // hypothetical expected string + + b = received; // not serialized + } // Else expected had custom serialization or was not a string + // or received has custom serialization. + + return a.length <= MAX_DIFF_STRING_LENGTH && + b.length <= MAX_DIFF_STRING_LENGTH + ? (0, _jestDiff.diffStringsUnified)(a, b, options) + : (0, _jestDiff.diffLinesUnified)(a.split('\n'), b.split('\n'), options); + } + + if (isLineDiffable(received)) { + const aLines2 = a.split('\n'); + const bLines2 = b.split('\n'); // Fall through to fix a regression for custom serializers + // like jest-snapshot-serializer-raw that ignore the indent option. + + const b0 = (0, _utils2.serialize)(received, 0); + + if (b0 !== b) { + const aLines0 = (0, _dedentLines.dedentLines)(aLines2); + + if (aLines0 !== null) { + // Compare lines without indentation. + const bLines0 = b0.split('\n'); + return (0, _jestDiff.diffLinesUnified2)( + aLines2, + bLines2, + aLines0, + bLines0, + options + ); + } + } // Fall back because: + // * props include a multiline string + // * text has more than one adjacent line + // * markup does not close + + return (0, _jestDiff.diffLinesUnified)(aLines2, bLines2, options); + } + + const printLabel = (0, _jestMatcherUtils.getLabelPrinter)( + aAnnotation, + bAnnotation + ); + return ( + printLabel(aAnnotation) + + aColor(a) + + '\n' + + printLabel(bAnnotation) + + bColor(b) + ); +}; + +exports.printSnapshotAndReceived = printSnapshotAndReceived; diff --git a/packages/jest-snapshot/build/types.d.ts b/packages/jest-snapshot/build/types.d.ts new file mode 100644 index 000000000000..627f1aca4157 --- /dev/null +++ b/packages/jest-snapshot/build/types.d.ts @@ -0,0 +1,25 @@ +/** + * 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. + */ +import type { MatcherState } from 'expect'; +import type SnapshotState from './State'; +export declare type Context = MatcherState & { + snapshotState: SnapshotState; +}; +export declare type MatchSnapshotConfig = { + context: Context; + hint?: string; + inlineSnapshot?: string; + isInline: boolean; + matcherName: string; + properties?: object; + received: any; +}; +export declare type SnapshotData = Record; +export declare type ExpectationResult = { + pass: boolean; + message: () => string; +}; diff --git a/packages/jest-snapshot/build/types.js b/packages/jest-snapshot/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-snapshot/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-snapshot/build/utils.d.ts b/packages/jest-snapshot/build/utils.d.ts new file mode 100644 index 000000000000..2fcb58934dce --- /dev/null +++ b/packages/jest-snapshot/build/utils.d.ts @@ -0,0 +1,27 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { SnapshotData } from './types'; +export declare const SNAPSHOT_VERSION = "1"; +export declare const SNAPSHOT_GUIDE_LINK = "https://goo.gl/fbAQLP"; +export declare const SNAPSHOT_VERSION_WARNING: string; +export declare const testNameToKey: (testName: Config.Path, count: number) => string; +export declare const keyToTestName: (key: string) => string; +export declare const getSnapshotData: (snapshotPath: Config.Path, update: Config.SnapshotUpdateState) => { + data: SnapshotData; + dirty: boolean; +}; +export declare const addExtraLineBreaks: (string: string) => string; +export declare const removeExtraLineBreaks: (string: string) => string; +export declare const removeLinesBeforeExternalMatcherTrap: (stack: string) => string; +export declare const serialize: (val: unknown, indent?: number) => string; +export declare const minify: (val: unknown) => string; +export declare const deserializeString: (stringified: string) => string; +export declare const escapeBacktickString: (str: string) => string; +export declare const ensureDirectoryExists: (filePath: Config.Path) => void; +export declare const saveSnapshotFile: (snapshotData: SnapshotData, snapshotPath: Config.Path) => void; +export declare const deepMerge: (target: any, source: any) => any; diff --git a/packages/jest-snapshot/build/utils.js b/packages/jest-snapshot/build/utils.js new file mode 100644 index 000000000000..e51bad01331f --- /dev/null +++ b/packages/jest-snapshot/build/utils.js @@ -0,0 +1,338 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.deepMerge = exports.saveSnapshotFile = exports.ensureDirectoryExists = exports.escapeBacktickString = exports.deserializeString = exports.minify = exports.serialize = exports.removeLinesBeforeExternalMatcherTrap = exports.removeExtraLineBreaks = exports.addExtraLineBreaks = exports.getSnapshotData = exports.keyToTestName = exports.testNameToKey = exports.SNAPSHOT_VERSION_WARNING = exports.SNAPSHOT_GUIDE_LINK = exports.SNAPSHOT_VERSION = void 0; + +var path = _interopRequireWildcard(require('path')); + +var _chalk = _interopRequireDefault(require('chalk')); + +var fs = _interopRequireWildcard(require('graceful-fs')); + +var _naturalCompare = _interopRequireDefault(require('natural-compare')); + +var _prettyFormat = _interopRequireDefault(require('pretty-format')); + +var _plugins = require('./plugins'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var jestWriteFile = + global[Symbol.for('jest-native-write-file')] || fs.writeFileSync; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var jestReadFile = + global[Symbol.for('jest-native-read-file')] || fs.readFileSync; +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +var jestExistsFile = + global[Symbol.for('jest-native-exists-file')] || fs.existsSync; +const SNAPSHOT_VERSION = '1'; +exports.SNAPSHOT_VERSION = SNAPSHOT_VERSION; +const SNAPSHOT_VERSION_REGEXP = /^\/\/ Jest Snapshot v(.+),/; +const SNAPSHOT_GUIDE_LINK = 'https://goo.gl/fbAQLP'; +exports.SNAPSHOT_GUIDE_LINK = SNAPSHOT_GUIDE_LINK; + +const SNAPSHOT_VERSION_WARNING = _chalk.default.yellow( + `${_chalk.default.bold('Warning')}: Before you upgrade snapshots, ` + + `we recommend that you revert any local changes to tests or other code, ` + + `to ensure that you do not store invalid state.` +); + +exports.SNAPSHOT_VERSION_WARNING = SNAPSHOT_VERSION_WARNING; + +const writeSnapshotVersion = () => + `// Jest Snapshot v${SNAPSHOT_VERSION}, ${SNAPSHOT_GUIDE_LINK}`; + +const validateSnapshotVersion = snapshotContents => { + const versionTest = SNAPSHOT_VERSION_REGEXP.exec(snapshotContents); + const version = versionTest && versionTest[1]; + + if (!version) { + return new Error( + _chalk.default.red( + `${_chalk.default.bold( + 'Outdated snapshot' + )}: No snapshot header found. ` + + `Jest 19 introduced versioned snapshots to ensure all developers ` + + `on a project are using the same version of Jest. ` + + `Please update all snapshots during this upgrade of Jest.\n\n` + ) + SNAPSHOT_VERSION_WARNING + ); + } + + if (version < SNAPSHOT_VERSION) { + return new Error( + _chalk.default.red( + `${_chalk.default.red.bold( + 'Outdated snapshot' + )}: The version of the snapshot ` + + `file associated with this test is outdated. The snapshot file ` + + `version ensures that all developers on a project are using ` + + `the same version of Jest. ` + + `Please update all snapshots during this upgrade of Jest.\n\n` + ) + + `Expected: v${SNAPSHOT_VERSION}\n` + + `Received: v${version}\n\n` + + SNAPSHOT_VERSION_WARNING + ); + } + + if (version > SNAPSHOT_VERSION) { + return new Error( + _chalk.default.red( + `${_chalk.default.red.bold( + 'Outdated Jest version' + )}: The version of this ` + + `snapshot file indicates that this project is meant to be used ` + + `with a newer version of Jest. The snapshot file version ensures ` + + `that all developers on a project are using the same version of ` + + `Jest. Please update your version of Jest and re-run the tests.\n\n` + ) + + `Expected: v${SNAPSHOT_VERSION}\n` + + `Received: v${version}` + ); + } + + return null; +}; + +function isObject(item) { + return item && typeof item === 'object' && !Array.isArray(item); +} + +const testNameToKey = (testName, count) => testName + ' ' + count; + +exports.testNameToKey = testNameToKey; + +const keyToTestName = key => { + if (!/ \d+$/.test(key)) { + throw new Error('Snapshot keys must end with a number.'); + } + + return key.replace(/ \d+$/, ''); +}; + +exports.keyToTestName = keyToTestName; + +const getSnapshotData = (snapshotPath, update) => { + const data = Object.create(null); + let snapshotContents = ''; + let dirty = false; + + if (jestExistsFile(snapshotPath)) { + try { + snapshotContents = jestReadFile(snapshotPath, 'utf8'); // eslint-disable-next-line no-new-func + + const populate = new Function('exports', snapshotContents); + populate(data); + } catch {} + } + + const validationResult = validateSnapshotVersion(snapshotContents); + const isInvalid = snapshotContents && validationResult; + + if (update === 'none' && isInvalid) { + throw validationResult; + } + + if ((update === 'all' || update === 'new') && isInvalid) { + dirty = true; + } + + return { + data, + dirty + }; +}; // Add extra line breaks at beginning and end of multiline snapshot +// to make the content easier to read. + +exports.getSnapshotData = getSnapshotData; + +const addExtraLineBreaks = string => + string.includes('\n') ? `\n${string}\n` : string; // Remove extra line breaks at beginning and end of multiline snapshot. +// Instead of trim, which can remove additional newlines or spaces +// at beginning or end of the content from a custom serializer. + +exports.addExtraLineBreaks = addExtraLineBreaks; + +const removeExtraLineBreaks = string => + string.length > 2 && string.startsWith('\n') && string.endsWith('\n') + ? string.slice(1, -1) + : string; + +exports.removeExtraLineBreaks = removeExtraLineBreaks; + +const removeLinesBeforeExternalMatcherTrap = stack => { + const lines = stack.split('\n'); + + for (let i = 0; i < lines.length; i += 1) { + // It's a function name specified in `packages/expect/src/index.ts` + // for external custom matchers. + if (lines[i].includes('__EXTERNAL_MATCHER_TRAP__')) { + return lines.slice(i + 1).join('\n'); + } + } + + return stack; +}; + +exports.removeLinesBeforeExternalMatcherTrap = removeLinesBeforeExternalMatcherTrap; +const escapeRegex = true; +const printFunctionName = false; + +const serialize = (val, indent = 2) => + normalizeNewlines( + (0, _prettyFormat.default)(val, { + escapeRegex, + indent, + plugins: (0, _plugins.getSerializers)(), + printFunctionName + }) + ); + +exports.serialize = serialize; + +const minify = val => + (0, _prettyFormat.default)(val, { + escapeRegex, + min: true, + plugins: (0, _plugins.getSerializers)(), + printFunctionName + }); // Remove double quote marks and unescape double quotes and backslashes. + +exports.minify = minify; + +const deserializeString = stringified => + stringified.slice(1, -1).replace(/\\("|\\)/g, '$1'); + +exports.deserializeString = deserializeString; + +const escapeBacktickString = str => str.replace(/`|\\|\${/g, '\\$&'); + +exports.escapeBacktickString = escapeBacktickString; + +const printBacktickString = str => '`' + escapeBacktickString(str) + '`'; + +const ensureDirectoryExists = filePath => { + try { + fs.mkdirSync(path.join(path.dirname(filePath)), { + recursive: true + }); + } catch {} +}; + +exports.ensureDirectoryExists = ensureDirectoryExists; + +const normalizeNewlines = string => string.replace(/\r\n|\r/g, '\n'); + +const saveSnapshotFile = (snapshotData, snapshotPath) => { + const snapshots = Object.keys(snapshotData) + .sort(_naturalCompare.default) + .map( + key => + 'exports[' + + printBacktickString(key) + + '] = ' + + printBacktickString(normalizeNewlines(snapshotData[key])) + + ';' + ); + ensureDirectoryExists(snapshotPath); + jestWriteFile( + snapshotPath, + writeSnapshotVersion() + '\n\n' + snapshots.join('\n\n') + '\n' + ); +}; + +exports.saveSnapshotFile = saveSnapshotFile; + +const deepMergeArray = (target, source) => { + const mergedOutput = Array.from(target); + source.forEach((sourceElement, index) => { + const targetElement = mergedOutput[index]; + + if (Array.isArray(target[index])) { + mergedOutput[index] = deepMergeArray(target[index], sourceElement); + } else if (isObject(targetElement)) { + mergedOutput[index] = deepMerge(target[index], sourceElement); + } else { + // Source does not exist in target or target is primitive and cannot be deep merged + mergedOutput[index] = sourceElement; + } + }); + return mergedOutput; +}; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + +const deepMerge = (target, source) => { + if (isObject(target) && isObject(source)) { + const mergedOutput = {...target}; + Object.keys(source).forEach(key => { + if (isObject(source[key]) && !source[key].$$typeof) { + if (!(key in target)) + Object.assign(mergedOutput, { + [key]: source[key] + }); + else mergedOutput[key] = deepMerge(target[key], source[key]); + } else if (Array.isArray(source[key])) { + mergedOutput[key] = deepMergeArray(target[key], source[key]); + } else { + Object.assign(mergedOutput, { + [key]: source[key] + }); + } + }); + return mergedOutput; + } else if (Array.isArray(target) && Array.isArray(source)) { + return deepMergeArray(target, source); + } + + return target; +}; + +exports.deepMerge = deepMerge; diff --git a/packages/jest-source-map/build/getCallsite.d.ts b/packages/jest-source-map/build/getCallsite.d.ts new file mode 100644 index 000000000000..bfb62ea08fd5 --- /dev/null +++ b/packages/jest-source-map/build/getCallsite.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +import callsites = require('callsites'); +import type { SourceMapRegistry } from './types'; +declare const _default: (level: number, sourceMaps?: SourceMapRegistry | null | undefined) => callsites.CallSite; +export default _default; diff --git a/packages/jest-source-map/build/getCallsite.js b/packages/jest-source-map/build/getCallsite.js new file mode 100644 index 000000000000..3415b74c2c0c --- /dev/null +++ b/packages/jest-source-map/build/getCallsite.js @@ -0,0 +1,110 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _callsites() { + const data = _interopRequireDefault(require('callsites')); + + _callsites = function () { + return data; + }; + + return data; +} + +function _gracefulFs() { + const data = require('graceful-fs'); + + _gracefulFs = function () { + return data; + }; + + return data; +} + +function _sourceMap() { + const data = require('source-map'); + + _sourceMap = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +// Copied from https://github.com/rexxars/sourcemap-decorate-callsites/blob/5b9735a156964973a75dc62fd2c7f0c1975458e8/lib/index.js#L113-L158 +const addSourceMapConsumer = (callsite, consumer) => { + const getLineNumber = callsite.getLineNumber; + const getColumnNumber = callsite.getColumnNumber; + let position = null; + + function getPosition() { + if (!position) { + position = consumer.originalPositionFor({ + column: getColumnNumber.call(callsite) || -1, + line: getLineNumber.call(callsite) || -1 + }); + } + + return position; + } + + Object.defineProperties(callsite, { + getColumnNumber: { + value() { + return getPosition().column || getColumnNumber.call(callsite); + }, + + writable: false + }, + getLineNumber: { + value() { + return getPosition().line || getLineNumber.call(callsite); + }, + + writable: false + } + }); +}; + +var _default = (level, sourceMaps) => { + const levelAfterThisCall = level + 1; + const stack = (0, _callsites().default)()[levelAfterThisCall]; + const sourceMapFileName = + sourceMaps === null || sourceMaps === void 0 + ? void 0 + : sourceMaps.get(stack.getFileName() || ''); + + if (sourceMapFileName) { + try { + const sourceMap = (0, _gracefulFs().readFileSync)( + sourceMapFileName, + 'utf8' + ); // @ts-expect-error: Not allowed to pass string + + addSourceMapConsumer( + stack, + new (_sourceMap().SourceMapConsumer)(sourceMap) + ); + } catch { + // ignore + } + } + + return stack; +}; + +exports.default = _default; diff --git a/packages/jest-source-map/build/index.d.ts b/packages/jest-source-map/build/index.d.ts new file mode 100644 index 000000000000..a6ffae945ef3 --- /dev/null +++ b/packages/jest-source-map/build/index.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +export { default as getCallsite } from './getCallsite'; +export type { SourceMapRegistry } from './types'; diff --git a/packages/jest-source-map/build/index.js b/packages/jest-source-map/build/index.js new file mode 100644 index 000000000000..34116059ede3 --- /dev/null +++ b/packages/jest-source-map/build/index.js @@ -0,0 +1,17 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'getCallsite', { + enumerable: true, + get: function () { + return _getCallsite.default; + } +}); + +var _getCallsite = _interopRequireDefault(require('./getCallsite')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} diff --git a/packages/jest-source-map/build/types.d.ts b/packages/jest-source-map/build/types.d.ts new file mode 100644 index 000000000000..3910875a0ed0 --- /dev/null +++ b/packages/jest-source-map/build/types.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export declare type SourceMapRegistry = Map; diff --git a/packages/jest-source-map/build/types.js b/packages/jest-source-map/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-source-map/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-test-result/build/formatTestResults.d.ts b/packages/jest-test-result/build/formatTestResults.d.ts new file mode 100644 index 000000000000..6a3bf03a4aa3 --- /dev/null +++ b/packages/jest-test-result/build/formatTestResults.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { AggregatedResult, CodeCoverageFormatter, CodeCoverageReporter, FormattedTestResults } from './types'; +export default function formatTestResults(results: AggregatedResult, codeCoverageFormatter?: CodeCoverageFormatter, reporter?: CodeCoverageReporter): FormattedTestResults; diff --git a/packages/jest-test-result/build/formatTestResults.js b/packages/jest-test-result/build/formatTestResults.js new file mode 100644 index 000000000000..3de0cdfa3e63 --- /dev/null +++ b/packages/jest-test-result/build/formatTestResults.js @@ -0,0 +1,70 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = formatTestResults; + +/** + * 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 formatTestResult = (testResult, codeCoverageFormatter, reporter) => { + const assertionResults = testResult.testResults.map(formatTestAssertion); + + if (testResult.testExecError) { + const now = Date.now(); + return { + assertionResults, + coverage: {}, + endTime: now, + message: testResult.failureMessage + ? testResult.failureMessage + : testResult.testExecError.message, + name: testResult.testFilePath, + startTime: now, + status: 'failed', + summary: '' + }; + } else { + const allTestsPassed = testResult.numFailingTests === 0; + return { + assertionResults, + coverage: codeCoverageFormatter + ? codeCoverageFormatter(testResult.coverage, reporter) + : testResult.coverage, + endTime: testResult.perfStats.end, + message: testResult.failureMessage || '', + name: testResult.testFilePath, + startTime: testResult.perfStats.start, + status: allTestsPassed ? 'passed' : 'failed', + summary: '' + }; + } +}; + +function formatTestAssertion(assertion) { + const result = { + ancestorTitles: assertion.ancestorTitles, + failureMessages: null, + fullName: assertion.fullName, + location: assertion.location, + status: assertion.status, + title: assertion.title + }; + + if (assertion.failureMessages) { + result.failureMessages = assertion.failureMessages; + } + + return result; +} + +function formatTestResults(results, codeCoverageFormatter, reporter) { + const testResults = results.testResults.map(testResult => + formatTestResult(testResult, codeCoverageFormatter, reporter) + ); + return {...results, testResults}; +} diff --git a/packages/jest-test-result/build/helpers.d.ts b/packages/jest-test-result/build/helpers.d.ts new file mode 100644 index 000000000000..480c7ab3e521 --- /dev/null +++ b/packages/jest-test-result/build/helpers.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { AggregatedResult, SerializableError, TestResult } from './types'; +export declare const makeEmptyAggregatedTestResult: () => AggregatedResult; +export declare const buildFailureTestResult: (testPath: Config.Path, err: SerializableError) => TestResult; +export declare const addResult: (aggregatedResults: AggregatedResult, testResult: TestResult) => void; +export declare const createEmptyTestResult: () => TestResult; diff --git a/packages/jest-test-result/build/helpers.js b/packages/jest-test-result/build/helpers.js new file mode 100644 index 000000000000..7e99d8a25970 --- /dev/null +++ b/packages/jest-test-result/build/helpers.js @@ -0,0 +1,184 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.createEmptyTestResult = exports.addResult = exports.buildFailureTestResult = exports.makeEmptyAggregatedTestResult = void 0; + +/** + * 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 makeEmptyAggregatedTestResult = () => ({ + numFailedTestSuites: 0, + numFailedTests: 0, + numPassedTestSuites: 0, + numPassedTests: 0, + numPendingTestSuites: 0, + numPendingTests: 0, + numRuntimeErrorTestSuites: 0, + numTodoTests: 0, + numTotalTestSuites: 0, + numTotalTests: 0, + openHandles: [], + snapshot: { + added: 0, + didUpdate: false, + // is set only after the full run + failure: false, + filesAdded: 0, + // combines individual test results + removed files after the full run + filesRemoved: 0, + filesRemovedList: [], + filesUnmatched: 0, + filesUpdated: 0, + matched: 0, + total: 0, + unchecked: 0, + uncheckedKeysByFile: [], + unmatched: 0, + updated: 0 + }, + startTime: 0, + success: true, + testResults: [], + wasInterrupted: false +}); + +exports.makeEmptyAggregatedTestResult = makeEmptyAggregatedTestResult; + +const buildFailureTestResult = (testPath, err) => ({ + console: undefined, + displayName: undefined, + failureMessage: null, + leaks: false, + numFailingTests: 0, + numPassingTests: 0, + numPendingTests: 0, + numTodoTests: 0, + openHandles: [], + perfStats: { + end: 0, + runtime: 0, + slow: false, + start: 0 + }, + skipped: false, + snapshot: { + added: 0, + fileDeleted: false, + matched: 0, + unchecked: 0, + uncheckedKeys: [], + unmatched: 0, + updated: 0 + }, + testExecError: err, + testFilePath: testPath, + testResults: [] +}); // Add individual test result to an aggregated test result + +exports.buildFailureTestResult = buildFailureTestResult; + +const addResult = (aggregatedResults, testResult) => { + // `todos` are new as of Jest 24, and not all runners return it. + // Set it to `0` to avoid `NaN` + if (!testResult.numTodoTests) { + testResult.numTodoTests = 0; + } + + aggregatedResults.testResults.push(testResult); + aggregatedResults.numTotalTests += + testResult.numPassingTests + + testResult.numFailingTests + + testResult.numPendingTests + + testResult.numTodoTests; + aggregatedResults.numFailedTests += testResult.numFailingTests; + aggregatedResults.numPassedTests += testResult.numPassingTests; + aggregatedResults.numPendingTests += testResult.numPendingTests; + aggregatedResults.numTodoTests += testResult.numTodoTests; + + if (testResult.testExecError) { + aggregatedResults.numRuntimeErrorTestSuites++; + } + + if (testResult.skipped) { + aggregatedResults.numPendingTestSuites++; + } else if (testResult.numFailingTests > 0 || testResult.testExecError) { + aggregatedResults.numFailedTestSuites++; + } else { + aggregatedResults.numPassedTestSuites++; + } // Snapshot data + + if (testResult.snapshot.added) { + aggregatedResults.snapshot.filesAdded++; + } + + if (testResult.snapshot.fileDeleted) { + aggregatedResults.snapshot.filesRemoved++; + } + + if (testResult.snapshot.unmatched) { + aggregatedResults.snapshot.filesUnmatched++; + } + + if (testResult.snapshot.updated) { + aggregatedResults.snapshot.filesUpdated++; + } + + aggregatedResults.snapshot.added += testResult.snapshot.added; + aggregatedResults.snapshot.matched += testResult.snapshot.matched; + aggregatedResults.snapshot.unchecked += testResult.snapshot.unchecked; + + if ( + testResult.snapshot.uncheckedKeys && + testResult.snapshot.uncheckedKeys.length > 0 + ) { + aggregatedResults.snapshot.uncheckedKeysByFile.push({ + filePath: testResult.testFilePath, + keys: testResult.snapshot.uncheckedKeys + }); + } + + aggregatedResults.snapshot.unmatched += testResult.snapshot.unmatched; + aggregatedResults.snapshot.updated += testResult.snapshot.updated; + aggregatedResults.snapshot.total += + testResult.snapshot.added + + testResult.snapshot.matched + + testResult.snapshot.unmatched + + testResult.snapshot.updated; +}; + +exports.addResult = addResult; + +const createEmptyTestResult = () => ({ + leaks: false, + // That's legacy code, just adding it as needed for typing + numFailingTests: 0, + numPassingTests: 0, + numPendingTests: 0, + numTodoTests: 0, + openHandles: [], + perfStats: { + end: 0, + runtime: 0, + slow: false, + start: 0 + }, + skipped: false, + snapshot: { + added: 0, + fileDeleted: false, + matched: 0, + unchecked: 0, + uncheckedKeys: [], + unmatched: 0, + updated: 0 + }, + testFilePath: '', + testResults: [] +}); + +exports.createEmptyTestResult = createEmptyTestResult; diff --git a/packages/jest-test-result/build/index.d.ts b/packages/jest-test-result/build/index.d.ts new file mode 100644 index 000000000000..1d83f7b9b80e --- /dev/null +++ b/packages/jest-test-result/build/index.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +export { default as formatTestResults } from './formatTestResults'; +export { addResult, buildFailureTestResult, createEmptyTestResult, makeEmptyAggregatedTestResult, } from './helpers'; +export type { AggregatedResult, AssertionLocation, AssertionResult, FailedAssertion, FormattedTestResults, Milliseconds, RuntimeTransformResult, SerializableError, SnapshotSummary, Status, Suite, TestResult, TestCaseResult, V8CoverageResult, } from './types'; diff --git a/packages/jest-test-result/build/index.js b/packages/jest-test-result/build/index.js new file mode 100644 index 000000000000..243e6682fc7c --- /dev/null +++ b/packages/jest-test-result/build/index.js @@ -0,0 +1,43 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'formatTestResults', { + enumerable: true, + get: function () { + return _formatTestResults.default; + } +}); +Object.defineProperty(exports, 'addResult', { + enumerable: true, + get: function () { + return _helpers.addResult; + } +}); +Object.defineProperty(exports, 'buildFailureTestResult', { + enumerable: true, + get: function () { + return _helpers.buildFailureTestResult; + } +}); +Object.defineProperty(exports, 'createEmptyTestResult', { + enumerable: true, + get: function () { + return _helpers.createEmptyTestResult; + } +}); +Object.defineProperty(exports, 'makeEmptyAggregatedTestResult', { + enumerable: true, + get: function () { + return _helpers.makeEmptyAggregatedTestResult; + } +}); + +var _formatTestResults = _interopRequireDefault(require('./formatTestResults')); + +var _helpers = require('./helpers'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} diff --git a/packages/jest-test-result/build/types.d.ts b/packages/jest-test-result/build/types.d.ts new file mode 100644 index 000000000000..3c6c0c8859dd --- /dev/null +++ b/packages/jest-test-result/build/types.d.ts @@ -0,0 +1,149 @@ +/** + * 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. + */ +import type { V8Coverage } from 'collect-v8-coverage'; +import type { CoverageMap, CoverageMapData } from 'istanbul-lib-coverage'; +import type { ConsoleBuffer } from '@jest/console'; +import type { Config, TestResult, TransformTypes } from '@jest/types'; +export interface RuntimeTransformResult extends TransformTypes.TransformResult { + wrapperLength: number; +} +export declare type V8CoverageResult = Array<{ + codeTransformResult: RuntimeTransformResult | undefined; + result: V8Coverage[number]; +}>; +export declare type SerializableError = TestResult.SerializableError; +export declare type FailedAssertion = { + matcherName?: string; + message?: string; + actual?: unknown; + pass?: boolean; + passed?: boolean; + expected?: unknown; + isNot?: boolean; + stack?: string; + error?: unknown; +}; +export declare type AssertionLocation = { + fullName: string; + path: string; +}; +export declare type Status = AssertionResult['status']; +export declare type Bytes = number; +export declare type Milliseconds = TestResult.Milliseconds; +export declare type AssertionResult = TestResult.AssertionResult; +export declare type FormattedAssertionResult = Pick & { + failureMessages: AssertionResult['failureMessages'] | null; +}; +export declare type AggregatedResultWithoutCoverage = { + numFailedTests: number; + numFailedTestSuites: number; + numPassedTests: number; + numPassedTestSuites: number; + numPendingTests: number; + numTodoTests: number; + numPendingTestSuites: number; + numRuntimeErrorTestSuites: number; + numTotalTests: number; + numTotalTestSuites: number; + openHandles: Array; + snapshot: SnapshotSummary; + startTime: number; + success: boolean; + testResults: Array; + wasInterrupted: boolean; +}; +export declare type AggregatedResult = AggregatedResultWithoutCoverage & { + coverageMap?: CoverageMap | null; +}; +export declare type Suite = { + title: string; + suites: Array; + tests: Array; +}; +export declare type TestCaseResult = AssertionResult; +export declare type TestResult = { + console?: ConsoleBuffer; + coverage?: CoverageMapData; + displayName?: Config.DisplayName; + failureMessage?: string | null; + leaks: boolean; + memoryUsage?: Bytes; + numFailingTests: number; + numPassingTests: number; + numPendingTests: number; + numTodoTests: number; + openHandles: Array; + perfStats: { + end: Milliseconds; + runtime: Milliseconds; + slow: boolean; + start: Milliseconds; + }; + skipped: boolean; + snapshot: { + added: number; + fileDeleted: boolean; + matched: number; + unchecked: number; + uncheckedKeys: Array; + unmatched: number; + updated: number; + }; + testExecError?: SerializableError; + testFilePath: Config.Path; + testResults: Array; + v8Coverage?: V8CoverageResult; +}; +export declare type FormattedTestResult = { + message: string; + name: string; + summary: string; + status: 'failed' | 'passed'; + startTime: number; + endTime: number; + coverage: unknown; + assertionResults: Array; +}; +export declare type FormattedTestResults = { + coverageMap?: CoverageMap | null | undefined; + numFailedTests: number; + numFailedTestSuites: number; + numPassedTests: number; + numPassedTestSuites: number; + numPendingTests: number; + numPendingTestSuites: number; + numRuntimeErrorTestSuites: number; + numTotalTests: number; + numTotalTestSuites: number; + snapshot: SnapshotSummary; + startTime: number; + success: boolean; + testResults: Array; + wasInterrupted: boolean; +}; +export declare type CodeCoverageReporter = unknown; +export declare type CodeCoverageFormatter = (coverage: CoverageMapData | null | undefined, reporter: CodeCoverageReporter) => Record | null | undefined; +export declare type UncheckedSnapshot = { + filePath: string; + keys: Array; +}; +export declare type SnapshotSummary = { + added: number; + didUpdate: boolean; + failure: boolean; + filesAdded: number; + filesRemoved: number; + filesRemovedList: Array; + filesUnmatched: number; + filesUpdated: number; + matched: number; + total: number; + unchecked: number; + uncheckedKeysByFile: Array; + unmatched: number; + updated: number; +}; diff --git a/packages/jest-test-result/build/types.js b/packages/jest-test-result/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-test-result/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-test-sequencer/build/index.d.ts b/packages/jest-test-sequencer/build/index.d.ts new file mode 100644 index 000000000000..d3a6c44b384f --- /dev/null +++ b/packages/jest-test-sequencer/build/index.d.ts @@ -0,0 +1,52 @@ +/** + * 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. + */ +import type { AggregatedResult } from '@jest/test-result'; +import type { Test } from 'jest-runner'; +import type { Context } from 'jest-runtime'; +declare type Cache = { + [key: string]: [0 | 1, number]; +}; +/** + * The TestSequencer will ultimately decide which tests should run first. + * It is responsible for storing and reading from a local cache + * map that stores context information for a given test, such as how long it + * took to run during the last run and if it has failed or not. + * Such information is used on: + * TestSequencer.sort(tests: Array) + * to sort the order of the provided tests. + * + * After the results are collected, + * TestSequencer.cacheResults(tests: Array, results: AggregatedResult) + * is called to store/update this information on the cache map. + */ +export default class TestSequencer { + private _cache; + _getCachePath(context: Context): string; + _getCache(test: Test): Cache; + /** + * Sorting tests is very important because it has a great impact on the + * user-perceived responsiveness and speed of the test run. + * + * If such information is on cache, tests are sorted based on: + * -> Has it failed during the last run ? + * Since it's important to provide the most expected feedback as quickly + * as possible. + * -> How long it took to run ? + * Because running long tests first is an effort to minimize worker idle + * time at the end of a long test run. + * And if that information is not available they are sorted based on file size + * since big test files usually take longer to complete. + * + * Note that a possible improvement would be to analyse other information + * from the file other than its size. + * + */ + sort(tests: Array): Array; + allFailedTests(tests: Array): Array; + cacheResults(tests: Array, results: AggregatedResult): void; +} +export {}; diff --git a/packages/jest-test-sequencer/build/index.js b/packages/jest-test-sequencer/build/index.js new file mode 100644 index 000000000000..5b7c47df4ddc --- /dev/null +++ b/packages/jest-test-sequencer/build/index.js @@ -0,0 +1,234 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _jestHasteMap() { + const data = _interopRequireDefault(require('jest-haste-map')); + + _jestHasteMap = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const FAIL = 0; +const SUCCESS = 1; + +/** + * The TestSequencer will ultimately decide which tests should run first. + * It is responsible for storing and reading from a local cache + * map that stores context information for a given test, such as how long it + * took to run during the last run and if it has failed or not. + * Such information is used on: + * TestSequencer.sort(tests: Array) + * to sort the order of the provided tests. + * + * After the results are collected, + * TestSequencer.cacheResults(tests: Array, results: AggregatedResult) + * is called to store/update this information on the cache map. + */ +class TestSequencer { + constructor() { + _defineProperty(this, '_cache', new Map()); + } + + _getCachePath(context) { + const {config} = context; + return _jestHasteMap().default.getCacheFilePath( + config.cacheDirectory, + 'perf-cache-' + config.name + ); + } + + _getCache(test) { + const {context} = test; + + if (!this._cache.has(context) && context.config.cache) { + const cachePath = this._getCachePath(context); + + if (fs().existsSync(cachePath)) { + try { + this._cache.set( + context, + JSON.parse(fs().readFileSync(cachePath, 'utf8')) + ); + } catch {} + } + } + + let cache = this._cache.get(context); + + if (!cache) { + cache = {}; + + this._cache.set(context, cache); + } + + return cache; + } + /** + * Sorting tests is very important because it has a great impact on the + * user-perceived responsiveness and speed of the test run. + * + * If such information is on cache, tests are sorted based on: + * -> Has it failed during the last run ? + * Since it's important to provide the most expected feedback as quickly + * as possible. + * -> How long it took to run ? + * Because running long tests first is an effort to minimize worker idle + * time at the end of a long test run. + * And if that information is not available they are sorted based on file size + * since big test files usually take longer to complete. + * + * Note that a possible improvement would be to analyse other information + * from the file other than its size. + * + */ + + sort(tests) { + const stats = {}; + + const fileSize = ({path, context: {hasteFS}}) => + stats[path] || (stats[path] = hasteFS.getSize(path) || 0); + + const hasFailed = (cache, test) => + cache[test.path] && cache[test.path][0] === FAIL; + + const time = (cache, test) => cache[test.path] && cache[test.path][1]; + + tests.forEach(test => (test.duration = time(this._getCache(test), test))); + return tests.sort((testA, testB) => { + const cacheA = this._getCache(testA); + + const cacheB = this._getCache(testB); + + const failedA = hasFailed(cacheA, testA); + const failedB = hasFailed(cacheB, testB); + const hasTimeA = testA.duration != null; + + if (failedA !== failedB) { + return failedA ? -1 : 1; + } else if (hasTimeA != (testB.duration != null)) { + // If only one of two tests has timing information, run it last + return hasTimeA ? 1 : -1; + } else if (testA.duration != null && testB.duration != null) { + return testA.duration < testB.duration ? 1 : -1; + } else { + return fileSize(testA) < fileSize(testB) ? 1 : -1; + } + }); + } + + allFailedTests(tests) { + const hasFailed = (cache, test) => { + var _cache$test$path; + + return ( + ((_cache$test$path = cache[test.path]) === null || + _cache$test$path === void 0 + ? void 0 + : _cache$test$path[0]) === FAIL + ); + }; + + return this.sort( + tests.filter(test => hasFailed(this._getCache(test), test)) + ); + } + + cacheResults(tests, results) { + const map = Object.create(null); + tests.forEach(test => (map[test.path] = test)); + results.testResults.forEach(testResult => { + if (testResult && map[testResult.testFilePath] && !testResult.skipped) { + const cache = this._getCache(map[testResult.testFilePath]); + + const perf = testResult.perfStats; + cache[testResult.testFilePath] = [ + testResult.numFailingTests ? FAIL : SUCCESS, + perf.runtime || 0 + ]; + } + }); + + this._cache.forEach((cache, context) => + fs().writeFileSync(this._getCachePath(context), JSON.stringify(cache)) + ); + } +} + +exports.default = TestSequencer; diff --git a/packages/jest-transform/build/ScriptTransformer.d.ts b/packages/jest-transform/build/ScriptTransformer.d.ts new file mode 100644 index 000000000000..170f67e7f830 --- /dev/null +++ b/packages/jest-transform/build/ScriptTransformer.d.ts @@ -0,0 +1,30 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { Options, ReducedTransformOptions, StringMap, TransformResult } from './types'; +export default class ScriptTransformer { + private readonly _cache; + private readonly _cacheFS; + private readonly _config; + private readonly _transformCache; + private readonly _transformConfigCache; + constructor(config: Config.ProjectConfig, cacheFS?: StringMap); + private _getCacheKey; + private _getFileCachePath; + private _getTransformPath; + private _getTransformer; + private _instrumentFile; + preloadTransformer(filepath: Config.Path): void; + transformSource(filepath: Config.Path, content: string, options: ReducedTransformOptions): TransformResult; + private _transformAndBuildScript; + transform(filename: Config.Path, options: Options, fileSource?: string): TransformResult; + transformJson(filename: Config.Path, options: Options, fileSource: string): string; + requireAndTranspileModule(moduleName: string, callback?: (module: ModuleType) => void, transformOptions?: ReducedTransformOptions): ModuleType; + requireAndTranspileModule(moduleName: string, callback?: (module: ModuleType) => Promise, transformOptions?: ReducedTransformOptions): Promise; + shouldTransform(filename: Config.Path): boolean; +} +export declare function createTranspilingRequire(config: Config.ProjectConfig): (resolverPath: string, applyInteropRequireDefault?: boolean) => TModuleType; diff --git a/packages/jest-transform/build/ScriptTransformer.js b/packages/jest-transform/build/ScriptTransformer.js new file mode 100644 index 000000000000..ce4ca38d6372 --- /dev/null +++ b/packages/jest-transform/build/ScriptTransformer.js @@ -0,0 +1,853 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.createTranspilingRequire = createTranspilingRequire; +exports.default = void 0; + +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 _core() { + const data = require('@babel/core'); + + _core = function () { + return data; + }; + + return data; +} + +function _babelPluginIstanbul() { + const data = _interopRequireDefault(require('babel-plugin-istanbul')); + + _babelPluginIstanbul = function () { + return data; + }; + + return data; +} + +function _convertSourceMap() { + const data = require('convert-source-map'); + + _convertSourceMap = function () { + return data; + }; + + return data; +} + +function _fastJsonStableStringify() { + const data = _interopRequireDefault(require('fast-json-stable-stringify')); + + _fastJsonStableStringify = function () { + return data; + }; + + return data; +} + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _pirates() { + const data = require('pirates'); + + _pirates = function () { + return data; + }; + + return data; +} + +function _slash() { + const data = _interopRequireDefault(require('slash')); + + _slash = function () { + return data; + }; + + return data; +} + +function _writeFileAtomic() { + const data = require('write-file-atomic'); + + _writeFileAtomic = function () { + return data; + }; + + return data; +} + +function _jestHasteMap() { + const data = _interopRequireDefault(require('jest-haste-map')); + + _jestHasteMap = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +var _enhanceUnexpectedTokenMessage = _interopRequireDefault( + require('./enhanceUnexpectedTokenMessage') +); + +var _shouldInstrument = _interopRequireDefault(require('./shouldInstrument')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +// Use `require` to avoid TS rootDir +const {version: VERSION} = require('../package.json'); + +// This data structure is used to avoid recalculating some data every time that +// we need to transform a file. Since ScriptTransformer is instantiated for each +// file we need to keep this object in the local scope of this module. +const projectCaches = new Map(); // To reset the cache for specific changesets (rather than package version). + +const CACHE_VERSION = '1'; + +async function waitForPromiseWithCleanup(promise, cleanup) { + try { + await promise; + } finally { + cleanup(); + } +} + +class ScriptTransformer { + constructor(config, cacheFS = new Map()) { + _defineProperty(this, '_cache', void 0); + + _defineProperty(this, '_cacheFS', void 0); + + _defineProperty(this, '_config', void 0); + + _defineProperty(this, '_transformCache', void 0); + + _defineProperty(this, '_transformConfigCache', void 0); + + this._config = config; + this._cacheFS = cacheFS; + this._transformCache = new Map(); + this._transformConfigCache = new Map(); + const configString = (0, _fastJsonStableStringify().default)(this._config); + let projectCache = projectCaches.get(configString); + + if (!projectCache) { + projectCache = { + configString, + ignorePatternsRegExp: calcIgnorePatternRegExp(this._config), + transformRegExp: calcTransformRegExp(this._config), + transformedFiles: new Map() + }; + projectCaches.set(configString, projectCache); + } + + this._cache = projectCache; + } + + _getCacheKey(fileData, filename, options) { + const configString = this._cache.configString; + const {transformer, transformerConfig = {}} = + this._getTransformer(filename) || {}; + + if (transformer && typeof transformer.getCacheKey === 'function') { + return (0, _crypto().createHash)('md5') + .update( + transformer.getCacheKey(fileData, filename, { + ...options, + cacheFS: this._cacheFS, + config: this._config, + configString, + transformerConfig + }) + ) + .update(CACHE_VERSION) + .digest('hex'); + } else { + return (0, _crypto().createHash)('md5') + .update(fileData) + .update(configString) + .update(options.instrument ? 'instrument' : '') + .update(filename) + .update(CACHE_VERSION) + .digest('hex'); + } + } + + _getFileCachePath(filename, content, options) { + const baseCacheDir = _jestHasteMap().default.getCacheFilePath( + this._config.cacheDirectory, + 'jest-transform-cache-' + this._config.name, + VERSION + ); + + const cacheKey = this._getCacheKey(content, filename, options); // Create sub folders based on the cacheKey to avoid creating one + // directory with many files. + + const cacheDir = path().join(baseCacheDir, cacheKey[0] + cacheKey[1]); + const cacheFilenamePrefix = path() + .basename(filename, path().extname(filename)) + .replace(/\W/g, ''); + const cachePath = (0, _slash().default)( + path().join(cacheDir, cacheFilenamePrefix + '_' + cacheKey) + ); + (0, _jestUtil().createDirectory)(cacheDir); + return cachePath; + } + + _getTransformPath(filename) { + const transformRegExp = this._cache.transformRegExp; + + if (!transformRegExp) { + return undefined; + } + + for (let i = 0; i < transformRegExp.length; i++) { + if (transformRegExp[i][0].test(filename)) { + const transformPath = transformRegExp[i][1]; + + this._transformConfigCache.set(transformPath, transformRegExp[i][2]); + + return transformPath; + } + } + + return undefined; + } + + _getTransformer(filename) { + if (!this._config.transform || !this._config.transform.length) { + return null; + } + + const transformPath = this._getTransformPath(filename); + + if (!transformPath) { + return null; + } + + const cached = this._transformCache.get(transformPath); + + if (cached) { + return cached; + } + + let transformer = require(transformPath); + + if (!transformer) { + throw new TypeError('Jest: a transform must export something.'); + } + + const transformerConfig = + this._transformConfigCache.get(transformPath) || {}; + + if (typeof transformer.createTransformer === 'function') { + transformer = transformer.createTransformer(transformerConfig); + } + + if (typeof transformer.process !== 'function') { + throw new TypeError( + 'Jest: a transform must export a `process` function.' + ); + } + + const res = { + transformer, + transformerConfig + }; + + this._transformCache.set(transformPath, res); + + return res; + } + + _instrumentFile(filename, input, canMapToInput, options) { + const inputCode = typeof input === 'string' ? input : input.code; + const inputMap = typeof input === 'string' ? null : input.map; + const result = (0, _core().transformSync)(inputCode, { + auxiliaryCommentBefore: ' istanbul ignore next ', + babelrc: false, + caller: { + name: '@jest/transform', + supportsDynamicImport: options.supportsDynamicImport, + supportsExportNamespaceFrom: options.supportsExportNamespaceFrom, + supportsStaticESM: options.supportsStaticESM, + supportsTopLevelAwait: options.supportsTopLevelAwait + }, + configFile: false, + filename, + plugins: [ + [ + _babelPluginIstanbul().default, + { + compact: false, + // files outside `cwd` will not be instrumented + cwd: this._config.rootDir, + exclude: [], + extension: false, + inputSourceMap: inputMap, + useInlineSourceMaps: false + } + ] + ], + sourceMaps: canMapToInput ? 'both' : false + }); + + if (result && result.code) { + return result; + } + + return input; + } // We don't want to expose transformers to the outside - this function is just + // to warm up `this._transformCache` + + preloadTransformer(filepath) { + this._getTransformer(filepath); + } + + transformSource(filepath, content, options) { + const filename = (0, _jestUtil().tryRealpath)(filepath); + const {transformer, transformerConfig = {}} = + this._getTransformer(filename) || {}; + + const cacheFilePath = this._getFileCachePath(filename, content, options); + + let sourceMapPath = cacheFilePath + '.map'; // Ignore cache if `config.cache` is set (--no-cache) + + let code = this._config.cache ? readCodeCacheFile(cacheFilePath) : null; + const shouldCallTransform = transformer && this.shouldTransform(filename); // That means that the transform has a custom instrumentation + // logic and will handle it based on `config.collectCoverage` option + + const transformWillInstrument = + shouldCallTransform && transformer && transformer.canInstrument; + + if (code) { + // This is broken: we return the code, and a path for the source map + // directly from the cache. But, nothing ensures the source map actually + // matches that source code. They could have gotten out-of-sync in case + // two separate processes write concurrently to the same cache files. + return { + code, + originalCode: content, + sourceMapPath + }; + } + + let transformed = { + code: content, + map: null + }; + + if (transformer && shouldCallTransform) { + const processed = transformer.process(content, filename, { + ...options, + cacheFS: this._cacheFS, + config: this._config, + configString: this._cache.configString, + transformerConfig + }); + + if (typeof processed === 'string') { + transformed.code = processed; + } else if (processed != null && typeof processed.code === 'string') { + transformed = processed; + } else { + throw new TypeError( + "Jest: a transform's `process` function must return a string, " + + 'or an object with `code` key containing this string.' + ); + } + } + + if (!transformed.map) { + try { + //Could be a potential freeze here. + //See: https://github.com/facebook/jest/pull/5177#discussion_r158883570 + const inlineSourceMap = (0, _convertSourceMap().fromSource)( + transformed.code + ); + + if (inlineSourceMap) { + transformed.map = inlineSourceMap.toObject(); + } + } catch { + const transformPath = this._getTransformPath(filename); + + console.warn( + `jest-transform: The source map produced for the file ${filename} ` + + `by ${transformPath} was invalid. Proceeding without source ` + + 'mapping for that file.' + ); + } + } // Apply instrumentation to the code if necessary, keeping the instrumented code and new map + + let map = transformed.map; + + if (!transformWillInstrument && options.instrument) { + /** + * We can map the original source code to the instrumented code ONLY if + * - the process of transforming the code produced a source map e.g. ts-jest + * - we did not transform the source code + * + * Otherwise we cannot make any statements about how the instrumented code corresponds to the original code, + * and we should NOT emit any source maps + * + */ + const shouldEmitSourceMaps = + (transformer != null && map != null) || transformer == null; + + const instrumented = this._instrumentFile( + filename, + transformed, + shouldEmitSourceMaps, + options + ); + + code = + typeof instrumented === 'string' ? instrumented : instrumented.code; + map = typeof instrumented === 'string' ? null : instrumented.map; + } else { + code = transformed.code; + } + + if (map) { + const sourceMapContent = + typeof map === 'string' ? map : JSON.stringify(map); + writeCacheFile(sourceMapPath, sourceMapContent); + } else { + sourceMapPath = null; + } + + writeCodeCacheFile(cacheFilePath, code); + return { + code, + originalCode: content, + sourceMapPath + }; + } + + _transformAndBuildScript(filename, options, transformOptions, fileSource) { + const {isCoreModule, isInternalModule} = options; + let fileContent = + fileSource !== null && fileSource !== void 0 + ? fileSource + : this._cacheFS.get(filename); + + if (!fileContent) { + fileContent = fs().readFileSync(filename, 'utf8'); + + this._cacheFS.set(filename, fileContent); + } + + const content = stripShebang(fileContent); + let code = content; + let sourceMapPath = null; + const willTransform = + !isInternalModule && + !isCoreModule && + (transformOptions.instrument || this.shouldTransform(filename)); + + try { + if (willTransform) { + const transformedSource = this.transformSource( + filename, + content, + transformOptions + ); + code = transformedSource.code; + sourceMapPath = transformedSource.sourceMapPath; + } + + return { + code, + originalCode: content, + sourceMapPath + }; + } catch (e) { + throw (0, _enhanceUnexpectedTokenMessage.default)(e); + } + } + + transform(filename, options, fileSource) { + let scriptCacheKey = undefined; + let instrument = false; + + if (!options.isCoreModule) { + instrument = + options.coverageProvider === 'babel' && + (0, _shouldInstrument.default)(filename, options, this._config); + scriptCacheKey = getScriptCacheKey(filename, instrument); + + const result = this._cache.transformedFiles.get(scriptCacheKey); + + if (result) { + return result; + } + } + + const result = this._transformAndBuildScript( + filename, + options, + {...options, instrument}, + fileSource + ); + + if (scriptCacheKey) { + this._cache.transformedFiles.set(scriptCacheKey, result); + } + + return result; + } + + transformJson(filename, options, fileSource) { + const {isCoreModule, isInternalModule} = options; + const willTransform = + !isInternalModule && !isCoreModule && this.shouldTransform(filename); + + if (willTransform) { + const {code: transformedJsonSource} = this.transformSource( + filename, + fileSource, + {...options, instrument: false} + ); + return transformedJsonSource; + } + + return fileSource; + } + + requireAndTranspileModule( + moduleName, + callback, + transformOptions = { + instrument: false, + supportsDynamicImport: false, + supportsExportNamespaceFrom: false, + supportsStaticESM: false, + supportsTopLevelAwait: false + } + ) { + // Load the transformer to avoid a cycle where we need to load a + // transformer in order to transform it in the require hooks + this.preloadTransformer(moduleName); + let transforming = false; + const revertHook = (0, _pirates().addHook)( + (code, filename) => { + try { + transforming = true; + return ( + this.transformSource(filename, code, transformOptions).code || code + ); + } finally { + transforming = false; + } + }, + { + exts: this._config.moduleFileExtensions.map(ext => `.${ext}`), + ignoreNodeModules: false, + matcher: filename => { + if (transforming) { + // Don't transform any dependency required by the transformer itself + return false; + } + + return this.shouldTransform(filename); + } + } + ); + + const module = require(moduleName); + + if (!callback) { + revertHook(); + return module; + } + + try { + const cbResult = callback(module); + + if ((0, _jestUtil().isPromise)(cbResult)) { + return waitForPromiseWithCleanup(cbResult, revertHook).then( + () => module + ); + } + } finally { + revertHook(); + } + + return module; + } + + shouldTransform(filename) { + const ignoreRegexp = this._cache.ignorePatternsRegExp; + const isIgnored = ignoreRegexp ? ignoreRegexp.test(filename) : false; + return ( + !!this._config.transform && !!this._config.transform.length && !isIgnored + ); + } +} // TODO: do we need to define the generics twice? + +exports.default = ScriptTransformer; + +function createTranspilingRequire(config) { + const transformer = new ScriptTransformer(config); + return function requireAndTranspileModule( + resolverPath, + applyInteropRequireDefault = false + ) { + const transpiledModule = transformer.requireAndTranspileModule( + resolverPath + ); + return applyInteropRequireDefault + ? (0, _jestUtil().interopRequireDefault)(transpiledModule).default + : transpiledModule; + }; +} + +const removeFile = path => { + try { + fs().unlinkSync(path); + } catch {} +}; + +const stripShebang = content => { + // If the file data starts with a shebang remove it. Leaves the empty line + // to keep stack trace line numbers correct. + if (content.startsWith('#!')) { + return content.replace(/^#!.*/, ''); + } else { + return content; + } +}; +/** + * This is like `writeCacheFile` but with an additional sanity checksum. We + * cannot use the same technique for source maps because we expose source map + * cache file paths directly to callsites, with the expectation they can read + * it right away. This is not a great system, because source map cache file + * could get corrupted, out-of-sync, etc. + */ + +function writeCodeCacheFile(cachePath, code) { + const checksum = (0, _crypto().createHash)('md5').update(code).digest('hex'); + writeCacheFile(cachePath, checksum + '\n' + code); +} +/** + * Read counterpart of `writeCodeCacheFile`. We verify that the content of the + * file matches the checksum, in case some kind of corruption happened. This + * could happen if an older version of `jest-runtime` writes non-atomically to + * the same cache, for example. + */ + +function readCodeCacheFile(cachePath) { + const content = readCacheFile(cachePath); + + if (content == null) { + return null; + } + + const code = content.substr(33); + const checksum = (0, _crypto().createHash)('md5').update(code).digest('hex'); + + if (checksum === content.substr(0, 32)) { + return code; + } + + return null; +} +/** + * Writing to the cache atomically relies on 'rename' being atomic on most + * file systems. Doing atomic write reduces the risk of corruption by avoiding + * two processes to write to the same file at the same time. It also reduces + * the risk of reading a file that's being overwritten at the same time. + */ + +const writeCacheFile = (cachePath, fileData) => { + try { + (0, _writeFileAtomic().sync)(cachePath, fileData, { + encoding: 'utf8', + fsync: false + }); + } catch (e) { + if (cacheWriteErrorSafeToIgnore(e, cachePath)) { + return; + } + + e.message = + 'jest: failed to cache transform results in: ' + + cachePath + + '\nFailure message: ' + + e.message; + removeFile(cachePath); + throw e; + } +}; +/** + * On Windows, renames are not atomic, leading to EPERM exceptions when two + * processes attempt to rename to the same target file at the same time. + * If the target file exists we can be reasonably sure another process has + * legitimately won a cache write race and ignore the error. + */ + +const cacheWriteErrorSafeToIgnore = (e, cachePath) => + process.platform === 'win32' && + e.code === 'EPERM' && + fs().existsSync(cachePath); + +const readCacheFile = cachePath => { + if (!fs().existsSync(cachePath)) { + return null; + } + + let fileData; + + try { + fileData = fs().readFileSync(cachePath, 'utf8'); + } catch (e) { + e.message = + 'jest: failed to read cache file: ' + + cachePath + + '\nFailure message: ' + + e.message; + removeFile(cachePath); + throw e; + } + + if (fileData == null) { + // We must have somehow created the file but failed to write to it, + // let's delete it and retry. + removeFile(cachePath); + } + + return fileData; +}; + +const getScriptCacheKey = (filename, instrument) => { + const mtime = fs().statSync(filename).mtime; + return filename + '_' + mtime.getTime() + (instrument ? '_instrumented' : ''); +}; + +const calcIgnorePatternRegExp = config => { + if ( + !config.transformIgnorePatterns || + config.transformIgnorePatterns.length === 0 + ) { + return undefined; + } + + return new RegExp(config.transformIgnorePatterns.join('|')); +}; + +const calcTransformRegExp = config => { + if (!config.transform.length) { + return undefined; + } + + const transformRegexp = []; + + for (let i = 0; i < config.transform.length; i++) { + transformRegexp.push([ + new RegExp(config.transform[i][0]), + config.transform[i][1], + config.transform[i][2] + ]); + } + + return transformRegexp; +}; diff --git a/packages/jest-transform/build/enhanceUnexpectedTokenMessage.d.ts b/packages/jest-transform/build/enhanceUnexpectedTokenMessage.d.ts new file mode 100644 index 000000000000..7847dc7e9222 --- /dev/null +++ b/packages/jest-transform/build/enhanceUnexpectedTokenMessage.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ +interface ErrorWithCodeFrame extends Error { + codeFrame?: string; +} +export default function handlePotentialSyntaxError(e: ErrorWithCodeFrame): ErrorWithCodeFrame; +export declare function enhanceUnexpectedTokenMessage(e: Error): Error; +export {}; diff --git a/packages/jest-transform/build/enhanceUnexpectedTokenMessage.js b/packages/jest-transform/build/enhanceUnexpectedTokenMessage.js new file mode 100644 index 000000000000..c7171f073d89 --- /dev/null +++ b/packages/jest-transform/build/enhanceUnexpectedTokenMessage.js @@ -0,0 +1,80 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = handlePotentialSyntaxError; +exports.enhanceUnexpectedTokenMessage = enhanceUnexpectedTokenMessage; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 DOT = ' \u2022 '; + +function handlePotentialSyntaxError(e) { + if (e.codeFrame) { + e.stack = e.message + '\n' + e.codeFrame; + } + + if ( + // `instanceof` might come from the wrong context + e.name === 'SyntaxError' && + !e.message.includes(' expected') + ) { + throw enhanceUnexpectedTokenMessage(e); + } + + return e; +} + +function enhanceUnexpectedTokenMessage(e) { + e.stack = + `${_chalk().default.bold.red('Jest encountered an unexpected token')} + +Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax. + +Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration. + +By default "node_modules" folder is ignored by transformers. + +Here's what you can do: +${DOT}If you are trying to use ECMAScript Modules, see ${_chalk().default.underline( + 'https://jestjs.io/docs/en/ecmascript-modules' + )} for how to enable it. +${DOT}To have some of your "node_modules" files transformed, you can specify a custom ${_chalk().default.bold( + '"transformIgnorePatterns"' + )} in your config. +${DOT}If you need a custom transformation specify a ${_chalk().default.bold( + '"transform"' + )} option in your config. +${DOT}If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the ${_chalk().default.bold( + '"moduleNameMapper"' + )} config option. + +You'll find more details and examples of these config options in the docs: +${_chalk().default.cyan('https://jestjs.io/docs/en/configuration')} +For information about custom transformations, see: +${_chalk().default.cyan('https://jestjs.io/docs/en/code-transformation')} + +${_chalk().default.bold.red('Details:')} + +` + e.stack; + return e; +} diff --git a/packages/jest-transform/build/index.d.ts b/packages/jest-transform/build/index.d.ts new file mode 100644 index 000000000000..4b3e2ea6fc43 --- /dev/null +++ b/packages/jest-transform/build/index.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +export { default as ScriptTransformer, createTranspilingRequire, } from './ScriptTransformer'; +export { default as shouldInstrument } from './shouldInstrument'; +export type { CallerTransformOptions, Transformer, ShouldInstrumentOptions, Options as TransformationOptions, TransformOptions, TransformResult, TransformedSource, } from './types'; +export { default as handlePotentialSyntaxError } from './enhanceUnexpectedTokenMessage'; diff --git a/packages/jest-transform/build/index.js b/packages/jest-transform/build/index.js new file mode 100644 index 000000000000..b470f3a4544a --- /dev/null +++ b/packages/jest-transform/build/index.js @@ -0,0 +1,85 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'ScriptTransformer', { + enumerable: true, + get: function () { + return _ScriptTransformer.default; + } +}); +Object.defineProperty(exports, 'createTranspilingRequire', { + enumerable: true, + get: function () { + return _ScriptTransformer.createTranspilingRequire; + } +}); +Object.defineProperty(exports, 'shouldInstrument', { + enumerable: true, + get: function () { + return _shouldInstrument.default; + } +}); +Object.defineProperty(exports, 'handlePotentialSyntaxError', { + enumerable: true, + get: function () { + return _enhanceUnexpectedTokenMessage.default; + } +}); + +var _ScriptTransformer = _interopRequireWildcard( + require('./ScriptTransformer') +); + +var _shouldInstrument = _interopRequireDefault(require('./shouldInstrument')); + +var _enhanceUnexpectedTokenMessage = _interopRequireDefault( + require('./enhanceUnexpectedTokenMessage') +); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} diff --git a/packages/jest-transform/build/shouldInstrument.d.ts b/packages/jest-transform/build/shouldInstrument.d.ts new file mode 100644 index 000000000000..2b7b617bccb8 --- /dev/null +++ b/packages/jest-transform/build/shouldInstrument.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +import type { ShouldInstrumentOptions } from './types'; +export default function shouldInstrument(filename: Config.Path, options: ShouldInstrumentOptions, config: Config.ProjectConfig): boolean; diff --git a/packages/jest-transform/build/shouldInstrument.js b/packages/jest-transform/build/shouldInstrument.js new file mode 100644 index 000000000000..88d4eec998d7 --- /dev/null +++ b/packages/jest-transform/build/shouldInstrument.js @@ -0,0 +1,207 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = shouldInstrument; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = 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 _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 MOCKS_PATTERN = new RegExp( + (0, _jestRegexUtil().escapePathForRegex)( + path().sep + '__mocks__' + path().sep + ) +); +const cachedRegexes = new Map(); + +const getRegex = regexStr => { + if (!cachedRegexes.has(regexStr)) { + cachedRegexes.set(regexStr, new RegExp(regexStr)); + } + + const regex = cachedRegexes.get(regexStr); // prevent stateful regexes from breaking, just in case + + regex.lastIndex = 0; + return regex; +}; + +function shouldInstrument(filename, options, config) { + if (!options.collectCoverage) { + return false; + } + + if ( + config.forceCoverageMatch.length && + _micromatch().default.any(filename, config.forceCoverageMatch) + ) { + return true; + } + + if ( + !config.testPathIgnorePatterns.some(pattern => + getRegex(pattern).test(filename) + ) + ) { + if (config.testRegex.some(regex => new RegExp(regex).test(filename))) { + return false; + } + + if ( + (0, _jestUtil().globsToMatcher)(config.testMatch)( + (0, _jestUtil().replacePathSepForGlob)(filename) + ) + ) { + return false; + } + } + + if ( + // This configuration field contains an object in the form of: + // {'path/to/file.js': true} + options.collectCoverageOnlyFrom && + !options.collectCoverageOnlyFrom[filename] + ) { + return false; + } + + if ( + // still cover if `only` is specified + !options.collectCoverageOnlyFrom && + options.collectCoverageFrom.length && + !(0, _jestUtil().globsToMatcher)(options.collectCoverageFrom)( + (0, _jestUtil().replacePathSepForGlob)( + path().relative(config.rootDir, filename) + ) + ) + ) { + return false; + } + + if ( + config.coveragePathIgnorePatterns.some(pattern => !!filename.match(pattern)) + ) { + return false; + } + + if (config.globalSetup === filename) { + return false; + } + + if (config.globalTeardown === filename) { + return false; + } + + if (config.setupFiles.includes(filename)) { + return false; + } + + if (config.setupFilesAfterEnv.includes(filename)) { + return false; + } + + if (MOCKS_PATTERN.test(filename)) { + return false; + } + + if (options.changedFiles && !options.changedFiles.has(filename)) { + if (!options.sourcesRelatedToTestsInChangedFiles) { + return false; + } + + if (!options.sourcesRelatedToTestsInChangedFiles.has(filename)) { + return false; + } + } + + return true; +} diff --git a/packages/jest-transform/build/types.d.ts b/packages/jest-transform/build/types.d.ts new file mode 100644 index 000000000000..28dff4f86010 --- /dev/null +++ b/packages/jest-transform/build/types.d.ts @@ -0,0 +1,50 @@ +/** + * 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. + */ +import type { RawSourceMap } from 'source-map'; +import type { Config, TransformTypes } from '@jest/types'; +export declare type ShouldInstrumentOptions = Pick & { + changedFiles?: Set; + sourcesRelatedToTestsInChangedFiles?: Set; +}; +export declare type Options = ShouldInstrumentOptions & Partial<{ + isCoreModule: boolean; + isInternalModule: boolean; +}> & CallerTransformOptions; +interface FixedRawSourceMap extends Omit { + version: number; +} +export declare type TransformedSource = { + code: string; + map?: FixedRawSourceMap | string | null; +} | string; +export declare type TransformResult = TransformTypes.TransformResult; +export interface CallerTransformOptions { + supportsDynamicImport: boolean; + supportsExportNamespaceFrom: boolean; + supportsStaticESM: boolean; + supportsTopLevelAwait: boolean; +} +export interface ReducedTransformOptions extends CallerTransformOptions { + instrument: boolean; +} +export declare type StringMap = Map; +export interface TransformOptions extends ReducedTransformOptions { + /** a cached file system which is used in jest-runtime - useful to improve performance */ + cacheFS: StringMap; + config: Config.ProjectConfig; + /** A stringified version of the configuration - useful in cache busting */ + configString: string; + /** the options passed through Jest's config by the user */ + transformerConfig: OptionType; +} +export interface Transformer { + canInstrument?: boolean; + createTransformer?: (options?: OptionType) => Transformer; + getCacheKey?: (sourceText: string, sourcePath: Config.Path, options: TransformOptions) => string; + process: (sourceText: string, sourcePath: Config.Path, options: TransformOptions) => TransformedSource; +} +export {}; diff --git a/packages/jest-transform/build/types.js b/packages/jest-transform/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-transform/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-types/build/Circus.d.ts b/packages/jest-types/build/Circus.d.ts new file mode 100644 index 000000000000..683832eb1b4b --- /dev/null +++ b/packages/jest-types/build/Circus.d.ts @@ -0,0 +1,193 @@ +/** + * 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. + */ +/// +import type * as Global from './Global'; +declare type Process = NodeJS.Process; +export declare type DoneFn = Global.DoneFn; +export declare type BlockFn = Global.BlockFn; +export declare type BlockName = Global.BlockName; +export declare type BlockMode = void | 'skip' | 'only' | 'todo'; +export declare type TestMode = BlockMode; +export declare type TestName = Global.TestName; +export declare type TestFn = Global.TestFn; +export declare type HookFn = Global.HookFn; +export declare type AsyncFn = TestFn | HookFn; +export declare type SharedHookType = 'afterAll' | 'beforeAll'; +export declare type HookType = SharedHookType | 'afterEach' | 'beforeEach'; +export declare type TestContext = Global.TestContext; +export declare type Exception = any; +export declare type FormattedError = string; +export declare type Hook = { + asyncError: Error; + fn: HookFn; + type: HookType; + parent: DescribeBlock; + seenDone: boolean; + timeout: number | undefined | null; +}; +export interface EventHandler { + (event: AsyncEvent, state: State): void | Promise; + (event: SyncEvent, state: State): void; +} +export declare type Event = SyncEvent | AsyncEvent; +interface JestGlobals extends Global.TestFrameworkGlobals { + expect: unknown; +} +export declare type SyncEvent = { + asyncError: Error; + mode: BlockMode; + name: 'start_describe_definition'; + blockName: BlockName; +} | { + mode: BlockMode; + name: 'finish_describe_definition'; + blockName: BlockName; +} | { + asyncError: Error; + name: 'add_hook'; + hookType: HookType; + fn: HookFn; + timeout: number | undefined; +} | { + asyncError: Error; + name: 'add_test'; + testName: TestName; + fn: TestFn; + mode?: TestMode; + timeout: number | undefined; +} | { + name: 'error'; + error: Exception; +}; +export declare type AsyncEvent = { + name: 'setup'; + testNamePattern?: string; + runtimeGlobals: JestGlobals; + parentProcess: Process; +} | { + name: 'include_test_location_in_result'; +} | { + name: 'hook_start'; + hook: Hook; +} | { + name: 'hook_success'; + describeBlock?: DescribeBlock; + test?: TestEntry; + hook: Hook; +} | { + name: 'hook_failure'; + error: string | Exception; + describeBlock?: DescribeBlock; + test?: TestEntry; + hook: Hook; +} | { + name: 'test_fn_start'; + test: TestEntry; +} | { + name: 'test_fn_success'; + test: TestEntry; +} | { + name: 'test_fn_failure'; + error: Exception; + test: TestEntry; +} | { + name: 'test_retry'; + test: TestEntry; +} | { + name: 'test_start'; + test: TestEntry; +} | { + name: 'test_skip'; + test: TestEntry; +} | { + name: 'test_todo'; + test: TestEntry; +} | { + name: 'test_done'; + test: TestEntry; +} | { + name: 'run_describe_start'; + describeBlock: DescribeBlock; +} | { + name: 'run_describe_finish'; + describeBlock: DescribeBlock; +} | { + name: 'run_start'; +} | { + name: 'run_finish'; +} | { + name: 'teardown'; +}; +export declare type MatcherResults = { + actual: unknown; + expected: unknown; + name: string; + pass: boolean; +}; +export declare type TestStatus = 'skip' | 'done' | 'todo'; +export declare type TestResult = { + duration?: number | null; + errors: Array; + errorsDetailed: Array; + invocations: number; + status: TestStatus; + location?: { + column: number; + line: number; + } | null; + testPath: Array; +}; +export declare type RunResult = { + unhandledErrors: Array; + testResults: TestResults; +}; +export declare type TestResults = Array; +export declare type GlobalErrorHandlers = { + uncaughtException: Array<(exception: Exception) => void>; + unhandledRejection: Array<(exception: Exception, promise: Promise) => void>; +}; +export declare type State = { + currentDescribeBlock: DescribeBlock; + currentlyRunningTest?: TestEntry | null; + expand?: boolean; + hasFocusedTests: boolean; + hasStarted: boolean; + originalGlobalErrorHandlers?: GlobalErrorHandlers; + parentProcess: Process | null; + rootDescribeBlock: DescribeBlock; + testNamePattern?: RegExp | null; + testTimeout: number; + unhandledErrors: Array; + includeTestLocationInResult: boolean; +}; +export declare type DescribeBlock = { + type: 'describeBlock'; + children: Array; + hooks: Array; + mode: BlockMode; + name: BlockName; + parent?: DescribeBlock; + /** @deprecated Please get from `children` array instead */ + tests: Array; +}; +export declare type TestError = Exception | [Exception | undefined, Exception]; +export declare type TestEntry = { + type: 'test'; + asyncError: Exception; + errors: Array; + fn: TestFn; + invocations: number; + mode: TestMode; + name: TestName; + parent: DescribeBlock; + startedAt?: number | null; + duration?: number | null; + seenDone: boolean; + status?: TestStatus | null; + timeout?: number; +}; +export {}; diff --git a/packages/jest-types/build/Circus.js b/packages/jest-types/build/Circus.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-types/build/Circus.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-types/build/Config.d.ts b/packages/jest-types/build/Config.d.ts new file mode 100644 index 000000000000..f991b0116248 --- /dev/null +++ b/packages/jest-types/build/Config.d.ts @@ -0,0 +1,428 @@ +/** + * 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. + */ +/// +import type { ForegroundColor } from 'chalk'; +import type { ReportOptions } from 'istanbul-reports'; +import type { Arguments } from 'yargs'; +declare type CoverageProvider = 'babel' | 'v8'; +declare type Timers = 'real' | 'fake' | 'modern' | 'legacy'; +export declare type Path = string; +export declare type Glob = string; +export declare type HasteConfig = { + /** Whether to hash files using SHA-1. */ + computeSha1?: boolean; + /** The platform to use as the default, e.g. 'ios'. */ + defaultPlatform?: string | null; + /** Path to a custom implementation of Haste. */ + hasteImplModulePath?: string; + /** All platforms to target, e.g ['ios', 'android']. */ + platforms?: Array; + /** Whether to throw on error on module collision. */ + throwOnModuleCollision?: boolean; + /** Whether to search for tests in node_modules. */ + retainAllFiles?: boolean; +}; +export declare type CoverageReporterName = keyof ReportOptions; +export declare type CoverageReporterWithOptions = K extends CoverageReporterName ? ReportOptions[K] extends never ? never : [K, Partial] : never; +export declare type CoverageReporters = Array; +export declare type ReporterConfig = [string, Record]; +export declare type TransformerConfig = [string, Record]; +export interface ConfigGlobals { + [K: string]: unknown; +} +export declare type DefaultOptions = { + automock: boolean; + bail: number; + cache: boolean; + cacheDirectory: Path; + changedFilesWithAncestor: boolean; + clearMocks: boolean; + collectCoverage: boolean; + coveragePathIgnorePatterns: Array; + coverageReporters: Array; + coverageProvider: CoverageProvider; + errorOnDeprecated: boolean; + expand: boolean; + extensionsToTreatAsEsm: Array; + forceCoverageMatch: Array; + globals: ConfigGlobals; + haste: HasteConfig; + injectGlobals: boolean; + maxConcurrency: number; + maxWorkers: number | string; + moduleDirectories: Array; + moduleFileExtensions: Array; + moduleNameMapper: Record>; + modulePathIgnorePatterns: Array; + noStackTrace: boolean; + notify: boolean; + notifyMode: NotifyMode; + prettierPath: string; + resetMocks: boolean; + resetModules: boolean; + restoreMocks: boolean; + roots: Array; + runTestsByPath: boolean; + runner: 'jest-runner'; + setupFiles: Array; + setupFilesAfterEnv: Array; + skipFilter: boolean; + slowTestThreshold: number; + snapshotSerializers: Array; + testEnvironment: string; + testEnvironmentOptions: Record; + testFailureExitCode: string | number; + testLocationInResults: boolean; + testMatch: Array; + testPathIgnorePatterns: Array; + testRegex: Array; + testRunner: string; + testSequencer: string; + testURL: string; + timers: Timers; + transformIgnorePatterns: Array; + useStderr: boolean; + watch: boolean; + watchPathIgnorePatterns: Array; + watchman: boolean; +}; +export declare type DisplayName = { + name: string; + color: typeof ForegroundColor; +}; +export declare type InitialOptionsWithRootDir = InitialOptions & Required>; +export declare type InitialOptions = Partial<{ + automock: boolean; + bail: boolean | number; + cache: boolean; + cacheDirectory: Path; + clearMocks: boolean; + changedFilesWithAncestor: boolean; + changedSince: string; + collectCoverage: boolean; + collectCoverageFrom: Array; + collectCoverageOnlyFrom: { + [key: string]: boolean; + }; + coverageDirectory: string; + coveragePathIgnorePatterns: Array; + coverageProvider: CoverageProvider; + coverageReporters: CoverageReporters; + coverageThreshold: CoverageThreshold; + dependencyExtractor: string; + detectLeaks: boolean; + detectOpenHandles: boolean; + displayName: string | DisplayName; + expand: boolean; + extensionsToTreatAsEsm: Array; + extraGlobals: Array; + filter: Path; + findRelatedTests: boolean; + forceCoverageMatch: Array; + forceExit: boolean; + json: boolean; + globals: ConfigGlobals; + globalSetup: string | null | undefined; + globalTeardown: string | null | undefined; + haste: HasteConfig; + injectGlobals: boolean; + reporters: Array; + logHeapUsage: boolean; + lastCommit: boolean; + listTests: boolean; + maxConcurrency: number; + maxWorkers: number | string; + moduleDirectories: Array; + moduleFileExtensions: Array; + moduleLoader: Path; + moduleNameMapper: { + [key: string]: string | Array; + }; + modulePathIgnorePatterns: Array; + modulePaths: Array; + name: string; + noStackTrace: boolean; + notify: boolean; + notifyMode: string; + onlyChanged: boolean; + onlyFailures: boolean; + outputFile: Path; + passWithNoTests: boolean; + preprocessorIgnorePatterns: Array; + preset: string | null | undefined; + prettierPath: string | null | undefined; + projects: Array; + replname: string | null | undefined; + resetMocks: boolean; + resetModules: boolean; + resolver: Path | null | undefined; + restoreMocks: boolean; + rootDir: Path; + roots: Array; + runner: string; + runTestsByPath: boolean; + scriptPreprocessor: string; + setupFiles: Array; + setupTestFrameworkScriptFile: Path; + setupFilesAfterEnv: Array; + silent: boolean; + skipFilter: boolean; + skipNodeResolution: boolean; + slowTestThreshold: number; + snapshotResolver: Path; + snapshotSerializers: Array; + errorOnDeprecated: boolean; + testEnvironment: string; + testEnvironmentOptions: Record; + testFailureExitCode: string | number; + testLocationInResults: boolean; + testMatch: Array; + testNamePattern: string; + testPathDirs: Array; + testPathIgnorePatterns: Array; + testRegex: string | Array; + testResultsProcessor: string; + testRunner: string; + testSequencer: string; + testURL: string; + testTimeout: number; + timers: Timers; + transform: { + [regex: string]: Path | TransformerConfig; + }; + transformIgnorePatterns: Array; + watchPathIgnorePatterns: Array; + unmockedModulePathPatterns: Array; + updateSnapshot: boolean; + useStderr: boolean; + verbose?: boolean; + watch: boolean; + watchAll: boolean; + watchman: boolean; + watchPlugins: Array]>; +}>; +export declare type SnapshotUpdateState = 'all' | 'new' | 'none'; +declare type NotifyMode = 'always' | 'failure' | 'success' | 'change' | 'success-change' | 'failure-change'; +export declare type CoverageThresholdValue = { + branches?: number; + functions?: number; + lines?: number; + statements?: number; +}; +declare type CoverageThreshold = { + [path: string]: CoverageThresholdValue; + global: CoverageThresholdValue; +}; +export declare type GlobalConfig = { + bail: number; + changedSince?: string; + changedFilesWithAncestor: boolean; + collectCoverage: boolean; + collectCoverageFrom: Array; + collectCoverageOnlyFrom?: { + [key: string]: boolean; + }; + coverageDirectory: string; + coveragePathIgnorePatterns?: Array; + coverageProvider: CoverageProvider; + coverageReporters: CoverageReporters; + coverageThreshold?: CoverageThreshold; + detectLeaks: boolean; + detectOpenHandles: boolean; + expand: boolean; + filter?: Path; + findRelatedTests: boolean; + forceExit: boolean; + json: boolean; + globalSetup?: string; + globalTeardown?: string; + lastCommit: boolean; + logHeapUsage: boolean; + listTests: boolean; + maxConcurrency: number; + maxWorkers: number; + noStackTrace: boolean; + nonFlagArgs: Array; + noSCM?: boolean; + notify: boolean; + notifyMode: NotifyMode; + outputFile?: Path; + onlyChanged: boolean; + onlyFailures: boolean; + passWithNoTests: boolean; + projects: Array; + replname?: string; + reporters?: Array; + runTestsByPath: boolean; + rootDir: Path; + silent?: boolean; + skipFilter: boolean; + errorOnDeprecated: boolean; + testFailureExitCode: number; + testNamePattern?: string; + testPathPattern: string; + testResultsProcessor?: string; + testSequencer: string; + testTimeout?: number; + updateSnapshot: SnapshotUpdateState; + useStderr: boolean; + verbose?: boolean; + watch: boolean; + watchAll: boolean; + watchman: boolean; + watchPlugins?: Array<{ + path: string; + config: Record; + }> | null; +}; +export declare type ProjectConfig = { + automock: boolean; + cache: boolean; + cacheDirectory: Path; + clearMocks: boolean; + coveragePathIgnorePatterns: Array; + cwd: Path; + dependencyExtractor?: string; + detectLeaks: boolean; + detectOpenHandles: boolean; + displayName?: DisplayName; + errorOnDeprecated: boolean; + extensionsToTreatAsEsm: Array; + extraGlobals: Array; + filter?: Path; + forceCoverageMatch: Array; + globalSetup?: string; + globalTeardown?: string; + globals: ConfigGlobals; + haste: HasteConfig; + injectGlobals: boolean; + moduleDirectories: Array; + moduleFileExtensions: Array; + moduleLoader?: Path; + moduleNameMapper: Array<[string, string]>; + modulePathIgnorePatterns: Array; + modulePaths?: Array; + name: string; + prettierPath: string; + resetMocks: boolean; + resetModules: boolean; + resolver?: Path; + restoreMocks: boolean; + rootDir: Path; + roots: Array; + runner: string; + setupFiles: Array; + setupFilesAfterEnv: Array; + skipFilter: boolean; + skipNodeResolution?: boolean; + slowTestThreshold: number; + snapshotResolver?: Path; + snapshotSerializers: Array; + testEnvironment: string; + testEnvironmentOptions: Record; + testMatch: Array; + testLocationInResults: boolean; + testPathIgnorePatterns: Array; + testRegex: Array; + testRunner: string; + testURL: string; + timers: Timers; + transform: Array<[string, Path, Record]>; + transformIgnorePatterns: Array; + watchPathIgnorePatterns: Array; + unmockedModulePathPatterns?: Array; +}; +export declare type Argv = Arguments; + color: boolean; + colors: boolean; + config: string; + coverage: boolean; + coverageDirectory: string; + coveragePathIgnorePatterns: Array; + coverageReporters: Array; + coverageThreshold: string; + debug: boolean; + env: string; + expand: boolean; + findRelatedTests: boolean; + forceExit: boolean; + globals: string; + globalSetup: string | null | undefined; + globalTeardown: string | null | undefined; + haste: string; + init: boolean; + injectGlobals: boolean; + json: boolean; + lastCommit: boolean; + logHeapUsage: boolean; + maxWorkers: number | string; + moduleDirectories: Array; + moduleFileExtensions: Array; + moduleNameMapper: string; + modulePathIgnorePatterns: Array; + modulePaths: Array; + noStackTrace: boolean; + notify: boolean; + notifyMode: string; + onlyChanged: boolean; + onlyFailures: boolean; + outputFile: string; + preset: string | null | undefined; + projects: Array; + prettierPath: string | null | undefined; + resetMocks: boolean; + resetModules: boolean; + resolver: string | null | undefined; + restoreMocks: boolean; + rootDir: string; + roots: Array; + runInBand: boolean; + selectProjects: Array; + setupFiles: Array; + setupFilesAfterEnv: Array; + showConfig: boolean; + silent: boolean; + snapshotSerializers: Array; + testEnvironment: string; + testFailureExitCode: string | null | undefined; + testMatch: Array; + testNamePattern: string; + testPathIgnorePatterns: Array; + testPathPattern: Array; + testRegex: string | Array; + testResultsProcessor: string; + testRunner: string; + testSequencer: string; + testURL: string; + testTimeout: number | null | undefined; + timers: string; + transform: string; + transformIgnorePatterns: Array; + unmockedModulePathPatterns: Array | null | undefined; + updateSnapshot: boolean; + useStderr: boolean; + verbose: boolean; + version: boolean; + watch: boolean; + watchAll: boolean; + watchman: boolean; + watchPathIgnorePatterns: Array; +}>>; +export {}; diff --git a/packages/jest-types/build/Config.js b/packages/jest-types/build/Config.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-types/build/Config.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-types/build/Global.d.ts b/packages/jest-types/build/Global.d.ts new file mode 100644 index 000000000000..0d0311a30044 --- /dev/null +++ b/packages/jest-types/build/Global.d.ts @@ -0,0 +1,96 @@ +/** + * 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. + */ +/// +import type { CoverageMapData } from 'istanbul-lib-coverage'; +export declare type ValidTestReturnValues = void | undefined; +declare type TestReturnValuePromise = Promise; +declare type TestReturnValueGenerator = Generator; +export declare type TestReturnValue = ValidTestReturnValues | TestReturnValuePromise; +export declare type TestContext = Record; +export declare type DoneFn = (reason?: string | Error) => void; +export declare type DoneTakingTestFn = (this: TestContext | undefined, done: DoneFn) => ValidTestReturnValues; +export declare type PromiseReturningTestFn = (this: TestContext | undefined) => TestReturnValue; +export declare type GeneratorReturningTestFn = (this: TestContext | undefined) => TestReturnValueGenerator; +export declare type TestName = string; +export declare type TestFn = PromiseReturningTestFn | GeneratorReturningTestFn | DoneTakingTestFn; +export declare type ConcurrentTestFn = () => TestReturnValuePromise; +export declare type BlockFn = () => void; +export declare type BlockName = string; +export declare type HookFn = TestFn; +export declare type Col = unknown; +export declare type Row = Array; +export declare type Table = Array; +export declare type ArrayTable = Table | Row; +export declare type TemplateTable = TemplateStringsArray; +export declare type TemplateData = Array; +export declare type EachTable = ArrayTable | TemplateTable; +export declare type TestCallback = BlockFn | TestFn | ConcurrentTestFn; +export declare type EachTestFn = (...args: Array) => ReturnType; +declare type Jasmine = { + _DEFAULT_TIMEOUT_INTERVAL?: number; + addMatchers: (matchers: Record) => void; +}; +declare type Each = ((table: EachTable, ...taggedTemplateData: Array) => (title: string, test: EachTestFn, timeout?: number) => void) | (() => () => void); +export interface HookBase { + (fn: HookFn, timeout?: number): void; +} +export interface ItBase { + (testName: TestName, fn: TestFn, timeout?: number): void; + each: Each; +} +export interface It extends ItBase { + only: ItBase; + skip: ItBase; + todo: (testName: TestName) => void; +} +export interface ItConcurrentBase { + (testName: string, testFn: ConcurrentTestFn, timeout?: number): void; + each: Each; +} +export interface ItConcurrentExtended extends ItConcurrentBase { + only: ItConcurrentBase; + skip: ItConcurrentBase; +} +export interface ItConcurrent extends It { + concurrent: ItConcurrentExtended; +} +export interface DescribeBase { + (blockName: BlockName, blockFn: BlockFn): void; + each: Each; +} +export interface Describe extends DescribeBase { + only: DescribeBase; + skip: DescribeBase; +} +export interface TestFrameworkGlobals { + it: ItConcurrent; + test: ItConcurrent; + fit: ItBase & { + concurrent?: ItConcurrentBase; + }; + xit: ItBase; + xtest: ItBase; + describe: Describe; + xdescribe: DescribeBase; + fdescribe: DescribeBase; + beforeAll: HookBase; + beforeEach: HookBase; + afterEach: HookBase; + afterAll: HookBase; +} +export interface GlobalAdditions extends TestFrameworkGlobals { + __coverage__: CoverageMapData; + jasmine: Jasmine; + fail: () => void; + pending: () => void; + spyOn: () => void; + spyOnProperty: () => void; +} +export interface Global extends GlobalAdditions, Omit { + [extras: string]: unknown; +} +export {}; diff --git a/packages/jest-types/build/Global.js b/packages/jest-types/build/Global.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-types/build/Global.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-types/build/TestResult.d.ts b/packages/jest-types/build/TestResult.d.ts new file mode 100644 index 000000000000..a05dca9d0def --- /dev/null +++ b/packages/jest-types/build/TestResult.d.ts @@ -0,0 +1,31 @@ +/** + * 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. + */ +export declare type Milliseconds = number; +declare type Status = 'passed' | 'failed' | 'skipped' | 'pending' | 'todo' | 'disabled'; +declare type Callsite = { + column: number; + line: number; +}; +export declare type AssertionResult = { + ancestorTitles: Array; + duration?: Milliseconds | null; + failureDetails: Array; + failureMessages: Array; + fullName: string; + invocations?: number; + location?: Callsite | null; + numPassingAsserts: number; + status: Status; + title: string; +}; +export declare type SerializableError = { + code?: unknown; + message: string; + stack: string | null | undefined; + type?: string; +}; +export {}; diff --git a/packages/jest-types/build/TestResult.js b/packages/jest-types/build/TestResult.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-types/build/TestResult.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-types/build/Transform.d.ts b/packages/jest-types/build/Transform.d.ts new file mode 100644 index 000000000000..69abd9206aef --- /dev/null +++ b/packages/jest-types/build/Transform.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +export declare type TransformResult = { + code: string; + originalCode: string; + sourceMapPath: string | null; +}; diff --git a/packages/jest-types/build/Transform.js b/packages/jest-types/build/Transform.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-types/build/Transform.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-types/build/index.d.ts b/packages/jest-types/build/index.d.ts new file mode 100644 index 000000000000..be82158e4121 --- /dev/null +++ b/packages/jest-types/build/index.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ +import type * as Circus from './Circus'; +import type * as Config from './Config'; +import type * as Global from './Global'; +import type * as TestResult from './TestResult'; +import type * as TransformTypes from './Transform'; +export type { Circus, Config, Global, TestResult, TransformTypes }; diff --git a/packages/jest-types/build/index.js b/packages/jest-types/build/index.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-types/build/index.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-util/build/ErrorWithStack.d.ts b/packages/jest-util/build/ErrorWithStack.d.ts new file mode 100644 index 000000000000..79764bd67eac --- /dev/null +++ b/packages/jest-util/build/ErrorWithStack.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +export default class ErrorWithStack extends Error { + constructor(message: string | undefined, callsite: (...args: Array) => unknown); +} diff --git a/packages/jest-util/build/ErrorWithStack.js b/packages/jest-util/build/ErrorWithStack.js new file mode 100644 index 000000000000..da7b9d2a1b44 --- /dev/null +++ b/packages/jest-util/build/ErrorWithStack.js @@ -0,0 +1,24 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +/** + * 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. + */ +class ErrorWithStack extends Error { + constructor(message, callsite) { + super(message); + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, callsite); + } + } +} + +exports.default = ErrorWithStack; diff --git a/packages/jest-util/build/clearLine.d.ts b/packages/jest-util/build/clearLine.d.ts new file mode 100644 index 000000000000..cf56b982cbb1 --- /dev/null +++ b/packages/jest-util/build/clearLine.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +/// +export default function clearLine(stream: NodeJS.WriteStream): void; diff --git a/packages/jest-util/build/clearLine.js b/packages/jest-util/build/clearLine.js new file mode 100644 index 000000000000..36c258a8b2b0 --- /dev/null +++ b/packages/jest-util/build/clearLine.js @@ -0,0 +1,18 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = clearLine; + +/** + * 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. + */ +function clearLine(stream) { + if (stream.isTTY) { + stream.write('\x1b[999D\x1b[K'); + } +} diff --git a/packages/jest-util/build/convertDescriptorToString.d.ts b/packages/jest-util/build/convertDescriptorToString.d.ts new file mode 100644 index 000000000000..4d406660bb0f --- /dev/null +++ b/packages/jest-util/build/convertDescriptorToString.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function convertDescriptorToString(descriptor: T): T | string; diff --git a/packages/jest-util/build/convertDescriptorToString.js b/packages/jest-util/build/convertDescriptorToString.js new file mode 100644 index 000000000000..4b776af573d7 --- /dev/null +++ b/packages/jest-util/build/convertDescriptorToString.js @@ -0,0 +1,41 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = convertDescriptorToString; + +/** + * 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. + */ + +/* eslint-disable local/ban-types-eventually */ +// See: https://github.com/facebook/jest/pull/5154 +function convertDescriptorToString(descriptor) { + if ( + typeof descriptor === 'string' || + typeof descriptor === 'number' || + descriptor === undefined + ) { + return descriptor; + } + + if (typeof descriptor !== 'function') { + throw new Error('describe expects a class, function, number, or string.'); + } + + if (descriptor.name !== undefined) { + return descriptor.name; + } // Fallback for old browsers, pardon Flow + + const stringified = descriptor.toString(); + const typeDescriptorMatch = stringified.match(/class|function/); + const indexOfNameSpace = // @ts-expect-error: typeDescriptorMatch exists + typeDescriptorMatch.index + typeDescriptorMatch[0].length; + const indexOfNameAfterSpace = stringified.search(/\(|\{/); + const name = stringified.substring(indexOfNameSpace, indexOfNameAfterSpace); + return name.trim(); +} diff --git a/packages/jest-util/build/createDirectory.d.ts b/packages/jest-util/build/createDirectory.d.ts new file mode 100644 index 000000000000..7e3e01625737 --- /dev/null +++ b/packages/jest-util/build/createDirectory.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function createDirectory(path: Config.Path): void; diff --git a/packages/jest-util/build/createDirectory.js b/packages/jest-util/build/createDirectory.js new file mode 100644 index 000000000000..c90aa0af4ce6 --- /dev/null +++ b/packages/jest-util/build/createDirectory.js @@ -0,0 +1,76 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = createDirectory; + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +function createDirectory(path) { + try { + fs().mkdirSync(path, { + recursive: true + }); + } catch (e) { + if (e.code !== 'EEXIST') { + throw e; + } + } +} diff --git a/packages/jest-util/build/createProcessObject.d.ts b/packages/jest-util/build/createProcessObject.d.ts new file mode 100644 index 000000000000..b4468602e124 --- /dev/null +++ b/packages/jest-util/build/createProcessObject.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +/// +export default function (): NodeJS.Process; diff --git a/packages/jest-util/build/createProcessObject.js b/packages/jest-util/build/createProcessObject.js new file mode 100644 index 000000000000..7d60ddde024a --- /dev/null +++ b/packages/jest-util/build/createProcessObject.js @@ -0,0 +1,126 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = _default; + +var _deepCyclicCopy = _interopRequireDefault(require('./deepCyclicCopy')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 BLACKLIST = new Set(['env', 'mainModule', '_events']); +const isWin32 = process.platform === 'win32'; +const proto = Object.getPrototypeOf(process.env); // The "process.env" object has a bunch of particularities: first, it does not +// directly extend from Object; second, it converts any assigned value to a +// string; and third, it is case-insensitive in Windows. We use a proxy here to +// mimic it (see https://nodejs.org/api/process.html#process_process_env). + +function createProcessEnv() { + const real = Object.create(proto); + const lookup = {}; + + function deletePropertyWin32(_target, key) { + for (const name in real) { + if (real.hasOwnProperty(name)) { + if (typeof key === 'string') { + if (name.toLowerCase() === key.toLowerCase()) { + delete real[name]; + delete lookup[name.toLowerCase()]; + } + } else { + if (key === name) { + delete real[name]; + delete lookup[name]; + } + } + } + } + + return true; + } + + function deleteProperty(_target, key) { + delete real[key]; + delete lookup[key]; + return true; + } + + function getProperty(_target, key) { + return real[key]; + } + + function getPropertyWin32(_target, key) { + if (typeof key === 'string') { + return lookup[key in proto ? key : key.toLowerCase()]; + } else { + return real[key]; + } + } + + const proxy = new Proxy(real, { + deleteProperty: isWin32 ? deletePropertyWin32 : deleteProperty, + get: isWin32 ? getPropertyWin32 : getProperty, + + set(_target, key, value) { + const strValue = '' + value; + + if (typeof key === 'string') { + lookup[key.toLowerCase()] = strValue; + } + + real[key] = strValue; + return true; + } + }); + return Object.assign(proxy, process.env); +} + +function _default() { + const process = require('process'); + + const newProcess = (0, _deepCyclicCopy.default)(process, { + blacklist: BLACKLIST, + keepPrototype: true + }); + + try { + // This fails on Node 12, but it's already set to 'process' + newProcess[Symbol.toStringTag] = 'process'; + } catch (e) { + // Make sure it's actually set instead of potentially ignoring errors + if (newProcess[Symbol.toStringTag] !== 'process') { + e.message = + 'Unable to set toStringTag on process. Please open up an issue at https://github.com/facebook/jest\n\n' + + e.message; + throw e; + } + } // Sequentially execute all constructors over the object. + + let proto = process; + + while ((proto = Object.getPrototypeOf(proto))) { + if (typeof proto.constructor === 'function') { + proto.constructor.call(newProcess); + } + } + + newProcess.env = createProcessEnv(); + + newProcess.send = () => {}; + + Object.defineProperty(newProcess, 'domain', { + get() { + return process.domain; + } + }); + return newProcess; +} diff --git a/packages/jest-util/build/deepCyclicCopy.d.ts b/packages/jest-util/build/deepCyclicCopy.d.ts new file mode 100644 index 000000000000..f8bd716c5cf6 --- /dev/null +++ b/packages/jest-util/build/deepCyclicCopy.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +export declare type DeepCyclicCopyOptions = { + blacklist?: Set; + keepPrototype?: boolean; +}; +export default function deepCyclicCopy(value: T, options?: DeepCyclicCopyOptions, cycles?: WeakMap): T; diff --git a/packages/jest-util/build/deepCyclicCopy.js b/packages/jest-util/build/deepCyclicCopy.js new file mode 100644 index 000000000000..e76e34bf28d2 --- /dev/null +++ b/packages/jest-util/build/deepCyclicCopy.js @@ -0,0 +1,84 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = deepCyclicCopy; + +/** + * 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 EMPTY = new Set(); + +function deepCyclicCopy( + value, + options = { + blacklist: EMPTY, + keepPrototype: false + }, + cycles = new WeakMap() +) { + if (typeof value !== 'object' || value === null) { + return value; + } else if (cycles.has(value)) { + return cycles.get(value); + } else if (Array.isArray(value)) { + return deepCyclicCopyArray(value, options, cycles); + } else { + return deepCyclicCopyObject(value, options, cycles); + } +} + +function deepCyclicCopyObject(object, options, cycles) { + const newObject = options.keepPrototype + ? Object.create(Object.getPrototypeOf(object)) + : {}; + const descriptors = Object.getOwnPropertyDescriptors(object); + cycles.set(object, newObject); + Object.keys(descriptors).forEach(key => { + if (options.blacklist && options.blacklist.has(key)) { + delete descriptors[key]; + return; + } + + const descriptor = descriptors[key]; + + if (typeof descriptor.value !== 'undefined') { + descriptor.value = deepCyclicCopy( + descriptor.value, + { + blacklist: EMPTY, + keepPrototype: options.keepPrototype + }, + cycles + ); + } + + descriptor.configurable = true; + }); + return Object.defineProperties(newObject, descriptors); +} + +function deepCyclicCopyArray(array, options, cycles) { + const newArray = options.keepPrototype + ? new (Object.getPrototypeOf(array).constructor)(array.length) + : []; + const length = array.length; + cycles.set(array, newArray); + + for (let i = 0; i < length; i++) { + newArray[i] = deepCyclicCopy( + array[i], + { + blacklist: EMPTY, + keepPrototype: options.keepPrototype + }, + cycles + ); + } + + return newArray; +} diff --git a/packages/jest-util/build/formatTime.d.ts b/packages/jest-util/build/formatTime.d.ts new file mode 100644 index 000000000000..fc4cd8d75d0c --- /dev/null +++ b/packages/jest-util/build/formatTime.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function formatTime(time: number, prefixPower?: number, padLeftLength?: number): string; diff --git a/packages/jest-util/build/formatTime.js b/packages/jest-util/build/formatTime.js new file mode 100644 index 000000000000..fad4b8cc2b04 --- /dev/null +++ b/packages/jest-util/build/formatTime.js @@ -0,0 +1,24 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = formatTime; + +/** + * 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. + */ +function formatTime(time, prefixPower = -3, padLeftLength = 0) { + const prefixes = ['n', 'μ', 'm', '']; + const prefixIndex = Math.max( + 0, + Math.min( + Math.trunc(prefixPower / 3) + prefixes.length - 1, + prefixes.length - 1 + ) + ); + return `${String(time).padStart(padLeftLength)} ${prefixes[prefixIndex]}s`; +} diff --git a/packages/jest-util/build/globsToMatcher.d.ts b/packages/jest-util/build/globsToMatcher.d.ts new file mode 100644 index 000000000000..687684f57ecf --- /dev/null +++ b/packages/jest-util/build/globsToMatcher.d.ts @@ -0,0 +1,27 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +declare type Matcher = (str: Config.Path) => boolean; +/** + * Converts a list of globs into a function that matches a path against the + * globs. + * + * Every time micromatch is called, it will parse the glob strings and turn + * them into regexp instances. Instead of calling micromatch repeatedly with + * the same globs, we can use this function which will build the micromatch + * matchers ahead of time and then have an optimized path for determining + * whether an individual path matches. + * + * This function is intended to match the behavior of `micromatch()`. + * + * @example + * const isMatch = globsToMatcher(['*.js', '!*.test.js']); + * isMatch('pizza.js'); // true + * isMatch('pizza.test.js'); // false + */ +export default function globsToMatcher(globs: Array): Matcher; +export {}; diff --git a/packages/jest-util/build/globsToMatcher.js b/packages/jest-util/build/globsToMatcher.js new file mode 100644 index 000000000000..9e63c5c39186 --- /dev/null +++ b/packages/jest-util/build/globsToMatcher.js @@ -0,0 +1,109 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = globsToMatcher; + +function _micromatch() { + const data = _interopRequireDefault(require('micromatch')); + + _micromatch = function () { + return data; + }; + + return data; +} + +var _replacePathSepForGlob = _interopRequireDefault( + require('./replacePathSepForGlob') +); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 globsToMatchersMap = new Map(); +const micromatchOptions = { + dot: true +}; +/** + * Converts a list of globs into a function that matches a path against the + * globs. + * + * Every time micromatch is called, it will parse the glob strings and turn + * them into regexp instances. Instead of calling micromatch repeatedly with + * the same globs, we can use this function which will build the micromatch + * matchers ahead of time and then have an optimized path for determining + * whether an individual path matches. + * + * This function is intended to match the behavior of `micromatch()`. + * + * @example + * const isMatch = globsToMatcher(['*.js', '!*.test.js']); + * isMatch('pizza.js'); // true + * isMatch('pizza.test.js'); // false + */ + +function globsToMatcher(globs) { + if (globs.length === 0) { + // Since there were no globs given, we can simply have a fast path here and + // return with a very simple function. + return () => false; + } + + const matchers = globs.map(glob => { + if (!globsToMatchersMap.has(glob)) { + // Matchers that are negated have different behavior than matchers that + // are not negated, so we need to store this information ahead of time. + const {negated} = _micromatch().default.scan(glob, micromatchOptions); + + const matcher = { + isMatch: _micromatch().default.matcher(glob, micromatchOptions), + negated + }; + globsToMatchersMap.set(glob, matcher); + } + + return globsToMatchersMap.get(glob); + }); + return path => { + const replacedPath = (0, _replacePathSepForGlob.default)(path); + let kept = undefined; + let negatives = 0; + + for (let i = 0; i < matchers.length; i++) { + const {isMatch, negated} = matchers[i]; + + if (negated) { + negatives++; + } + + const matched = isMatch(replacedPath); + + if (!matched && negated) { + // The path was not matched, and the matcher is a negated matcher, so we + // want to omit the path. This means that the negative matcher is + // filtering the path out. + kept = false; + } else if (matched && !negated) { + // The path was matched, and the matcher is not a negated matcher, so we + // want to keep the path. + kept = true; + } + } // If all of the globs were negative globs, then we want to include the path + // as long as it was not explicitly not kept. Otherwise only include + // the path if it was kept. This allows sets of globs that are all negated + // to allow some paths to be matched, while sets of globs that are mixed + // negated and non-negated to cause the negated matchers to only omit paths + // and not keep them. + + return negatives === matchers.length ? kept !== false : !!kept; + }; +} diff --git a/packages/jest-util/build/index.d.ts b/packages/jest-util/build/index.d.ts new file mode 100644 index 000000000000..7e6daa5eae42 --- /dev/null +++ b/packages/jest-util/build/index.d.ts @@ -0,0 +1,24 @@ +/** + * 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. + */ +export { default as clearLine } from './clearLine'; +export { default as createDirectory } from './createDirectory'; +export { default as ErrorWithStack } from './ErrorWithStack'; +export { default as installCommonGlobals } from './installCommonGlobals'; +export { default as interopRequireDefault } from './interopRequireDefault'; +export { default as isInteractive } from './isInteractive'; +export { default as isPromise } from './isPromise'; +export { default as setGlobal } from './setGlobal'; +export { default as deepCyclicCopy } from './deepCyclicCopy'; +export { default as convertDescriptorToString } from './convertDescriptorToString'; +export * as specialChars from './specialChars'; +export { default as replacePathSepForGlob } from './replacePathSepForGlob'; +export { default as testPathPatternToRegExp } from './testPathPatternToRegExp'; +export { default as globsToMatcher } from './globsToMatcher'; +export * as preRunMessage from './preRunMessage'; +export { default as pluralize } from './pluralize'; +export { default as formatTime } from './formatTime'; +export { default as tryRealpath } from './tryRealpath'; diff --git a/packages/jest-util/build/index.js b/packages/jest-util/build/index.js new file mode 100644 index 000000000000..8426139cd816 --- /dev/null +++ b/packages/jest-util/build/index.js @@ -0,0 +1,198 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'clearLine', { + enumerable: true, + get: function () { + return _clearLine.default; + } +}); +Object.defineProperty(exports, 'createDirectory', { + enumerable: true, + get: function () { + return _createDirectory.default; + } +}); +Object.defineProperty(exports, 'ErrorWithStack', { + enumerable: true, + get: function () { + return _ErrorWithStack.default; + } +}); +Object.defineProperty(exports, 'installCommonGlobals', { + enumerable: true, + get: function () { + return _installCommonGlobals.default; + } +}); +Object.defineProperty(exports, 'interopRequireDefault', { + enumerable: true, + get: function () { + return _interopRequireDefault.default; + } +}); +Object.defineProperty(exports, 'isInteractive', { + enumerable: true, + get: function () { + return _isInteractive.default; + } +}); +Object.defineProperty(exports, 'isPromise', { + enumerable: true, + get: function () { + return _isPromise.default; + } +}); +Object.defineProperty(exports, 'setGlobal', { + enumerable: true, + get: function () { + return _setGlobal.default; + } +}); +Object.defineProperty(exports, 'deepCyclicCopy', { + enumerable: true, + get: function () { + return _deepCyclicCopy.default; + } +}); +Object.defineProperty(exports, 'convertDescriptorToString', { + enumerable: true, + get: function () { + return _convertDescriptorToString.default; + } +}); +Object.defineProperty(exports, 'replacePathSepForGlob', { + enumerable: true, + get: function () { + return _replacePathSepForGlob.default; + } +}); +Object.defineProperty(exports, 'testPathPatternToRegExp', { + enumerable: true, + get: function () { + return _testPathPatternToRegExp.default; + } +}); +Object.defineProperty(exports, 'globsToMatcher', { + enumerable: true, + get: function () { + return _globsToMatcher.default; + } +}); +Object.defineProperty(exports, 'pluralize', { + enumerable: true, + get: function () { + return _pluralize.default; + } +}); +Object.defineProperty(exports, 'formatTime', { + enumerable: true, + get: function () { + return _formatTime.default; + } +}); +Object.defineProperty(exports, 'tryRealpath', { + enumerable: true, + get: function () { + return _tryRealpath.default; + } +}); +exports.preRunMessage = exports.specialChars = void 0; + +var _clearLine = _interopRequireDefault2(require('./clearLine')); + +var _createDirectory = _interopRequireDefault2(require('./createDirectory')); + +var _ErrorWithStack = _interopRequireDefault2(require('./ErrorWithStack')); + +var _installCommonGlobals = _interopRequireDefault2( + require('./installCommonGlobals') +); + +var _interopRequireDefault = _interopRequireDefault2( + require('./interopRequireDefault') +); + +var _isInteractive = _interopRequireDefault2(require('./isInteractive')); + +var _isPromise = _interopRequireDefault2(require('./isPromise')); + +var _setGlobal = _interopRequireDefault2(require('./setGlobal')); + +var _deepCyclicCopy = _interopRequireDefault2(require('./deepCyclicCopy')); + +var _convertDescriptorToString = _interopRequireDefault2( + require('./convertDescriptorToString') +); + +var _specialChars = _interopRequireWildcard(require('./specialChars')); + +exports.specialChars = _specialChars; + +var _replacePathSepForGlob = _interopRequireDefault2( + require('./replacePathSepForGlob') +); + +var _testPathPatternToRegExp = _interopRequireDefault2( + require('./testPathPatternToRegExp') +); + +var _globsToMatcher = _interopRequireDefault2(require('./globsToMatcher')); + +var _preRunMessage = _interopRequireWildcard(require('./preRunMessage')); + +exports.preRunMessage = _preRunMessage; + +var _pluralize = _interopRequireDefault2(require('./pluralize')); + +var _formatTime = _interopRequireDefault2(require('./formatTime')); + +var _tryRealpath = _interopRequireDefault2(require('./tryRealpath')); + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _interopRequireDefault2(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} diff --git a/packages/jest-util/build/installCommonGlobals.d.ts b/packages/jest-util/build/installCommonGlobals.d.ts new file mode 100644 index 000000000000..6196196589f7 --- /dev/null +++ b/packages/jest-util/build/installCommonGlobals.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +/// +import type { Config } from '@jest/types'; +export default function (globalObject: NodeJS.Global, globals: Config.ConfigGlobals): NodeJS.Global & Config.ConfigGlobals; diff --git a/packages/jest-util/build/installCommonGlobals.js b/packages/jest-util/build/installCommonGlobals.js new file mode 100644 index 000000000000..1743b2306947 --- /dev/null +++ b/packages/jest-util/build/installCommonGlobals.js @@ -0,0 +1,127 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = _default; + +function fs() { + const data = _interopRequireWildcard(require('graceful-fs')); + + fs = function () { + return data; + }; + + return data; +} + +var _createProcessObject = _interopRequireDefault( + require('./createProcessObject') +); + +var _deepCyclicCopy = _interopRequireDefault(require('./deepCyclicCopy')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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 DTRACE = Object.keys(global).filter(key => key.startsWith('DTRACE')); + +function _default(globalObject, globals) { + globalObject.process = (0, _createProcessObject.default)(); + const symbol = globalObject.Symbol; // Keep a reference to some globals that Jest needs + + Object.defineProperties(globalObject, { + [symbol.for('jest-native-promise')]: { + enumerable: false, + value: Promise, + writable: false + }, + [symbol.for('jest-native-now')]: { + enumerable: false, + value: globalObject.Date.now.bind(globalObject.Date), + writable: false + }, + [symbol.for('jest-native-read-file')]: { + enumerable: false, + value: fs().readFileSync.bind(fs()), + writable: false + }, + [symbol.for('jest-native-write-file')]: { + enumerable: false, + value: fs().writeFileSync.bind(fs()), + writable: false + }, + [symbol.for('jest-native-exists-file')]: { + enumerable: false, + value: fs().existsSync.bind(fs()), + writable: false + }, + 'jest-symbol-do-not-touch': { + enumerable: false, + value: symbol, + writable: false + } + }); // Forward some APIs. + + DTRACE.forEach(dtrace => { + // @ts-expect-error: no index + globalObject[dtrace] = function (...args) { + // @ts-expect-error: no index + return global[dtrace].apply(this, args); + }; + }); // Forward some others (this breaks the sandbox but for now it's OK). + + globalObject.Buffer = global.Buffer; + globalObject.setImmediate = global.setImmediate; + globalObject.clearImmediate = global.clearImmediate; + return Object.assign(globalObject, (0, _deepCyclicCopy.default)(globals)); +} diff --git a/packages/jest-util/build/interopRequireDefault.d.ts b/packages/jest-util/build/interopRequireDefault.d.ts new file mode 100644 index 000000000000..91b66e4fc41a --- /dev/null +++ b/packages/jest-util/build/interopRequireDefault.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function interopRequireDefault(obj: any): any; diff --git a/packages/jest-util/build/interopRequireDefault.js b/packages/jest-util/build/interopRequireDefault.js new file mode 100644 index 000000000000..69f2ebaeb9e5 --- /dev/null +++ b/packages/jest-util/build/interopRequireDefault.js @@ -0,0 +1,22 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = interopRequireDefault; + +/** + * 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. + */ +// copied from https://github.com/babel/babel/blob/56044c7851d583d498f919e9546caddf8f80a72f/packages/babel-helpers/src/helpers.js#L558-L562 +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +function interopRequireDefault(obj) { + return obj && obj.__esModule + ? obj + : { + default: obj + }; +} diff --git a/packages/jest-util/build/isInteractive.d.ts b/packages/jest-util/build/isInteractive.d.ts new file mode 100644 index 000000000000..57ddbf0a7959 --- /dev/null +++ b/packages/jest-util/build/isInteractive.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +declare const _default: boolean; +export default _default; diff --git a/packages/jest-util/build/isInteractive.js b/packages/jest-util/build/isInteractive.js new file mode 100644 index 000000000000..e2207611a81b --- /dev/null +++ b/packages/jest-util/build/isInteractive.js @@ -0,0 +1,31 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _isCi() { + const data = _interopRequireDefault(require('is-ci')); + + _isCi = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +var _default = + !!process.stdout.isTTY && process.env.TERM !== 'dumb' && !_isCi().default; + +exports.default = _default; diff --git a/packages/jest-util/build/isPromise.d.ts b/packages/jest-util/build/isPromise.d.ts new file mode 100644 index 000000000000..ea8589b908c6 --- /dev/null +++ b/packages/jest-util/build/isPromise.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +declare const isPromise: (candidate: unknown) => candidate is Promise; +export default isPromise; diff --git a/packages/jest-util/build/isPromise.js b/packages/jest-util/build/isPromise.js new file mode 100644 index 000000000000..7158df850af7 --- /dev/null +++ b/packages/jest-util/build/isPromise.js @@ -0,0 +1,20 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +/** + * 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. + */ +// capture global.Promise before it may potentially be overwritten +const Promise = global.Promise; // see ES2015 spec 25.4.4.5, https://stackoverflow.com/a/38339199 + +const isPromise = candidate => Promise.resolve(candidate) === candidate; + +var _default = isPromise; +exports.default = _default; diff --git a/packages/jest-util/build/pluralize.d.ts b/packages/jest-util/build/pluralize.d.ts new file mode 100644 index 000000000000..9be3057928ed --- /dev/null +++ b/packages/jest-util/build/pluralize.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function pluralize(word: string, count: number): string; diff --git a/packages/jest-util/build/pluralize.js b/packages/jest-util/build/pluralize.js new file mode 100644 index 000000000000..55f533ee088f --- /dev/null +++ b/packages/jest-util/build/pluralize.js @@ -0,0 +1,16 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = pluralize; + +/** + * 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. + */ +function pluralize(word, count) { + return `${count} ${word}${count === 1 ? '' : 's'}`; +} diff --git a/packages/jest-util/build/preRunMessage.d.ts b/packages/jest-util/build/preRunMessage.d.ts new file mode 100644 index 000000000000..55acad973e1d --- /dev/null +++ b/packages/jest-util/build/preRunMessage.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +/// +export declare const print: (stream: NodeJS.WriteStream) => void; +export declare const remove: (stream: NodeJS.WriteStream) => void; diff --git a/packages/jest-util/build/preRunMessage.js b/packages/jest-util/build/preRunMessage.js new file mode 100644 index 000000000000..3704601670fa --- /dev/null +++ b/packages/jest-util/build/preRunMessage.js @@ -0,0 +1,48 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.remove = exports.print = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +var _clearLine = _interopRequireDefault(require('./clearLine')); + +var _isInteractive = _interopRequireDefault(require('./isInteractive')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 print = stream => { + if (_isInteractive.default) { + stream.write( + _chalk().default.bold.dim('Determining test suites to run...') + ); + } +}; + +exports.print = print; + +const remove = stream => { + if (_isInteractive.default) { + (0, _clearLine.default)(stream); + } +}; + +exports.remove = remove; diff --git a/packages/jest-util/build/replacePathSepForGlob.d.ts b/packages/jest-util/build/replacePathSepForGlob.d.ts new file mode 100644 index 000000000000..7f47d18bfa11 --- /dev/null +++ b/packages/jest-util/build/replacePathSepForGlob.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function replacePathSepForGlob(path: Config.Path): Config.Glob; diff --git a/packages/jest-util/build/replacePathSepForGlob.js b/packages/jest-util/build/replacePathSepForGlob.js new file mode 100644 index 000000000000..b7a1d0f90824 --- /dev/null +++ b/packages/jest-util/build/replacePathSepForGlob.js @@ -0,0 +1,16 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = replacePathSepForGlob; + +/** + * 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. + */ +function replacePathSepForGlob(path) { + return path.replace(/\\(?![{}()+?.^$])/g, '/'); +} diff --git a/packages/jest-util/build/setGlobal.d.ts b/packages/jest-util/build/setGlobal.d.ts new file mode 100644 index 000000000000..c8debef69526 --- /dev/null +++ b/packages/jest-util/build/setGlobal.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +/// +declare const _default: (globalToMutate: NodeJS.Global | Window, key: string, value: unknown) => void; +export default _default; diff --git a/packages/jest-util/build/setGlobal.js b/packages/jest-util/build/setGlobal.js new file mode 100644 index 000000000000..f1638ee10c2b --- /dev/null +++ b/packages/jest-util/build/setGlobal.js @@ -0,0 +1,19 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +/** + * 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. + */ +var _default = (globalToMutate, key, value) => { + // @ts-expect-error: no index + globalToMutate[key] = value; +}; + +exports.default = _default; diff --git a/packages/jest-util/build/specialChars.d.ts b/packages/jest-util/build/specialChars.d.ts new file mode 100644 index 000000000000..1a50d4954582 --- /dev/null +++ b/packages/jest-util/build/specialChars.d.ts @@ -0,0 +1,14 @@ +/** + * 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. + */ +export declare const ARROW = " \u203A "; +export declare const ICONS: { + failed: string; + pending: string; + success: string; + todo: string; +}; +export declare const CLEAR: string; diff --git a/packages/jest-util/build/specialChars.js b/packages/jest-util/build/specialChars.js new file mode 100644 index 000000000000..1f9ddcc4e1ef --- /dev/null +++ b/packages/jest-util/build/specialChars.js @@ -0,0 +1,25 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.CLEAR = exports.ICONS = exports.ARROW = void 0; + +/** + * 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 isWindows = process.platform === 'win32'; +const ARROW = ' \u203A '; +exports.ARROW = ARROW; +const ICONS = { + failed: isWindows ? '\u00D7' : '\u2715', + pending: '\u25CB', + success: isWindows ? '\u221A' : '\u2713', + todo: '\u270E' +}; +exports.ICONS = ICONS; +const CLEAR = isWindows ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H'; +exports.CLEAR = CLEAR; diff --git a/packages/jest-util/build/testPathPatternToRegExp.d.ts b/packages/jest-util/build/testPathPatternToRegExp.d.ts new file mode 100644 index 000000000000..6cd3f81f67f1 --- /dev/null +++ b/packages/jest-util/build/testPathPatternToRegExp.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +declare const _default: (testPathPattern: Config.GlobalConfig['testPathPattern']) => RegExp; +export default _default; diff --git a/packages/jest-util/build/testPathPatternToRegExp.js b/packages/jest-util/build/testPathPatternToRegExp.js new file mode 100644 index 000000000000..29fd338c9543 --- /dev/null +++ b/packages/jest-util/build/testPathPatternToRegExp.js @@ -0,0 +1,19 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +/** + * 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. + */ +// Because we serialize/deserialize globalConfig when we spawn workers, +// we can't pass regular expression. Using this shared function on both sides +// will ensure that we produce consistent regexp for testPathPattern. +var _default = testPathPattern => new RegExp(testPathPattern, 'i'); + +exports.default = _default; diff --git a/packages/jest-util/build/tryRealpath.d.ts b/packages/jest-util/build/tryRealpath.d.ts new file mode 100644 index 000000000000..cccdfa0e5f9f --- /dev/null +++ b/packages/jest-util/build/tryRealpath.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export default function tryRealpath(path: Config.Path): Config.Path; diff --git a/packages/jest-util/build/tryRealpath.js b/packages/jest-util/build/tryRealpath.js new file mode 100644 index 000000000000..445933861122 --- /dev/null +++ b/packages/jest-util/build/tryRealpath.js @@ -0,0 +1,34 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = tryRealpath; + +function _gracefulFs() { + const data = require('graceful-fs'); + + _gracefulFs = function () { + return data; + }; + + return data; +} + +/** + * 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. + */ +function tryRealpath(path) { + try { + path = _gracefulFs().realpathSync.native(path); + } catch (error) { + if (error.code !== 'ENOENT') { + throw error; + } + } + + return path; +} diff --git a/packages/jest-validate/build/condition.d.ts b/packages/jest-validate/build/condition.d.ts new file mode 100644 index 000000000000..4411c91ab06a --- /dev/null +++ b/packages/jest-validate/build/condition.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +export declare function getValues(validOption: T): Array; +export declare function validationCondition(option: unknown, validOption: unknown): boolean; +export declare function multipleValidOptions>(...args: T): T[number]; diff --git a/packages/jest-validate/build/condition.js b/packages/jest-validate/build/condition.js new file mode 100644 index 000000000000..1462c8b0a772 --- /dev/null +++ b/packages/jest-validate/build/condition.js @@ -0,0 +1,48 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.getValues = getValues; +exports.validationCondition = validationCondition; +exports.multipleValidOptions = multipleValidOptions; + +/** + * 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 toString = Object.prototype.toString; +const MULTIPLE_VALID_OPTIONS_SYMBOL = Symbol('JEST_MULTIPLE_VALID_OPTIONS'); + +function validationConditionSingle(option, validOption) { + return ( + option === null || + option === undefined || + (typeof option === 'function' && typeof validOption === 'function') || + toString.call(option) === toString.call(validOption) + ); +} + +function getValues(validOption) { + if ( + Array.isArray(validOption) && // @ts-expect-error + validOption[MULTIPLE_VALID_OPTIONS_SYMBOL] + ) { + return validOption; + } + + return [validOption]; +} + +function validationCondition(option, validOption) { + return getValues(validOption).some(e => validationConditionSingle(option, e)); +} + +function multipleValidOptions(...args) { + const options = [...args]; // @ts-expect-error + + options[MULTIPLE_VALID_OPTIONS_SYMBOL] = true; + return options; +} diff --git a/packages/jest-validate/build/defaultConfig.d.ts b/packages/jest-validate/build/defaultConfig.d.ts new file mode 100644 index 000000000000..fa3cba08178d --- /dev/null +++ b/packages/jest-validate/build/defaultConfig.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { ValidationOptions } from './types'; +declare const validationOptions: ValidationOptions; +export default validationOptions; diff --git a/packages/jest-validate/build/defaultConfig.js b/packages/jest-validate/build/defaultConfig.js new file mode 100644 index 000000000000..e179c05d6280 --- /dev/null +++ b/packages/jest-validate/build/defaultConfig.js @@ -0,0 +1,42 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _condition = require('./condition'); + +var _deprecated = require('./deprecated'); + +var _errors = require('./errors'); + +var _utils = require('./utils'); + +var _warnings = require('./warnings'); + +/** + * 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 validationOptions = { + comment: '', + condition: _condition.validationCondition, + deprecate: _deprecated.deprecationWarning, + deprecatedConfig: {}, + error: _errors.errorMessage, + exampleConfig: {}, + recursive: true, + // Allow NPM-sanctioned comments in package.json. Use a "//" key. + recursiveDenylist: ['//'], + title: { + deprecation: _utils.DEPRECATION, + error: _utils.ERROR, + warning: _utils.WARNING + }, + unknown: _warnings.unknownOptionWarning +}; +var _default = validationOptions; +exports.default = _default; diff --git a/packages/jest-validate/build/deprecated.d.ts b/packages/jest-validate/build/deprecated.d.ts new file mode 100644 index 000000000000..b8cf9e03e24d --- /dev/null +++ b/packages/jest-validate/build/deprecated.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { DeprecatedOptions, ValidationOptions } from './types'; +export declare const deprecationWarning: (config: Record, option: string, deprecatedOptions: DeprecatedOptions, options: ValidationOptions) => boolean; diff --git a/packages/jest-validate/build/deprecated.js b/packages/jest-validate/build/deprecated.js new file mode 100644 index 000000000000..08be871eddd4 --- /dev/null +++ b/packages/jest-validate/build/deprecated.js @@ -0,0 +1,32 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.deprecationWarning = void 0; + +var _utils = require('./utils'); + +/** + * 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 deprecationMessage = (message, options) => { + const comment = options.comment; + const name = + (options.title && options.title.deprecation) || _utils.DEPRECATION; + (0, _utils.logValidationWarning)(name, message, comment); +}; + +const deprecationWarning = (config, option, deprecatedOptions, options) => { + if (option in deprecatedOptions) { + deprecationMessage(deprecatedOptions[option](config), options); + return true; + } + + return false; +}; + +exports.deprecationWarning = deprecationWarning; diff --git a/packages/jest-validate/build/errors.d.ts b/packages/jest-validate/build/errors.d.ts new file mode 100644 index 000000000000..564667b6cf85 --- /dev/null +++ b/packages/jest-validate/build/errors.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { ValidationOptions } from './types'; +export declare const errorMessage: (option: string, received: unknown, defaultValue: unknown, options: ValidationOptions, path?: string[] | undefined) => void; diff --git a/packages/jest-validate/build/errors.js b/packages/jest-validate/build/errors.js new file mode 100644 index 000000000000..6bd8e0d59b2b --- /dev/null +++ b/packages/jest-validate/build/errors.js @@ -0,0 +1,75 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.errorMessage = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestGetType() { + const data = _interopRequireDefault(require('jest-get-type')); + + _jestGetType = function () { + return data; + }; + + return data; +} + +var _condition = require('./condition'); + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 errorMessage = (option, received, defaultValue, options, path) => { + const conditions = (0, _condition.getValues)(defaultValue); + const validTypes = Array.from( + new Set(conditions.map(_jestGetType().default)) + ); + const message = ` Option ${_chalk().default.bold( + `"${path && path.length > 0 ? path.join('.') + '.' : ''}${option}"` + )} must be of type: + ${validTypes.map(e => _chalk().default.bold.green(e)).join(' or ')} + but instead received: + ${_chalk().default.bold.red((0, _jestGetType().default)(received))} + + Example: +${formatExamples(option, conditions)}`; + const comment = options.comment; + const name = (options.title && options.title.error) || _utils.ERROR; + throw new _utils.ValidationError(name, message, comment); +}; + +exports.errorMessage = errorMessage; + +function formatExamples(option, examples) { + return examples.map( + e => ` { + ${_chalk().default.bold(`"${option}"`)}: ${_chalk().default.bold( + (0, _utils.formatPrettyObject)(e) + )} + }` + ).join(` + + or + +`); +} diff --git a/packages/jest-validate/build/exampleConfig.d.ts b/packages/jest-validate/build/exampleConfig.d.ts new file mode 100644 index 000000000000..9df7c32c645f --- /dev/null +++ b/packages/jest-validate/build/exampleConfig.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { ValidationOptions } from './types'; +declare const config: ValidationOptions; +export default config; diff --git a/packages/jest-validate/build/exampleConfig.js b/packages/jest-validate/build/exampleConfig.js new file mode 100644 index 000000000000..1cc8620abd8a --- /dev/null +++ b/packages/jest-validate/build/exampleConfig.js @@ -0,0 +1,36 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +/** + * 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 config = { + comment: ' A comment', + condition: () => true, + deprecate: () => false, + deprecatedConfig: { + key: () => 'Deprecation message' + }, + error: () => {}, + exampleConfig: { + key: 'value', + test: 'case' + }, + recursive: true, + recursiveDenylist: [], + title: { + deprecation: 'Deprecation Warning', + error: 'Validation Error', + warning: 'Validation Warning' + }, + unknown: () => {} +}; +var _default = config; +exports.default = _default; diff --git a/packages/jest-validate/build/index.d.ts b/packages/jest-validate/build/index.d.ts new file mode 100644 index 000000000000..86ae3ad7ed85 --- /dev/null +++ b/packages/jest-validate/build/index.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +export { ValidationError, createDidYouMeanMessage, format, logValidationWarning, } from './utils'; +export type { DeprecatedOptions } from './types'; +export { default as validate } from './validate'; +export { default as validateCLIOptions } from './validateCLIOptions'; +export { multipleValidOptions } from './condition'; diff --git a/packages/jest-validate/build/index.js b/packages/jest-validate/build/index.js new file mode 100644 index 000000000000..d92776b46d4b --- /dev/null +++ b/packages/jest-validate/build/index.js @@ -0,0 +1,61 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'ValidationError', { + enumerable: true, + get: function () { + return _utils.ValidationError; + } +}); +Object.defineProperty(exports, 'createDidYouMeanMessage', { + enumerable: true, + get: function () { + return _utils.createDidYouMeanMessage; + } +}); +Object.defineProperty(exports, 'format', { + enumerable: true, + get: function () { + return _utils.format; + } +}); +Object.defineProperty(exports, 'logValidationWarning', { + enumerable: true, + get: function () { + return _utils.logValidationWarning; + } +}); +Object.defineProperty(exports, 'validate', { + enumerable: true, + get: function () { + return _validate.default; + } +}); +Object.defineProperty(exports, 'validateCLIOptions', { + enumerable: true, + get: function () { + return _validateCLIOptions.default; + } +}); +Object.defineProperty(exports, 'multipleValidOptions', { + enumerable: true, + get: function () { + return _condition.multipleValidOptions; + } +}); + +var _utils = require('./utils'); + +var _validate = _interopRequireDefault(require('./validate')); + +var _validateCLIOptions = _interopRequireDefault( + require('./validateCLIOptions') +); + +var _condition = require('./condition'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} diff --git a/packages/jest-validate/build/types.d.ts b/packages/jest-validate/build/types.d.ts new file mode 100644 index 000000000000..9e8e1064e0c3 --- /dev/null +++ b/packages/jest-validate/build/types.d.ts @@ -0,0 +1,26 @@ +/** + * 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. + */ +declare type Title = { + deprecation?: string; + error?: string; + warning?: string; +}; +export declare type DeprecatedOptionFunc = (arg: Record) => string; +export declare type DeprecatedOptions = Record; +export declare type ValidationOptions = { + comment?: string; + condition?: (option: unknown, validOption: unknown) => boolean; + deprecate?: (config: Record, option: string, deprecatedOptions: DeprecatedOptions, options: ValidationOptions) => boolean; + deprecatedConfig?: DeprecatedOptions; + error?: (option: string, received: unknown, defaultValue: unknown, options: ValidationOptions, path?: Array) => void; + exampleConfig: Record; + recursive?: boolean; + recursiveDenylist?: Array; + title?: Title; + unknown?: (config: Record, exampleConfig: Record, option: string, options: ValidationOptions, path?: Array) => void; +}; +export {}; diff --git a/packages/jest-validate/build/types.js b/packages/jest-validate/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-validate/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-validate/build/utils.d.ts b/packages/jest-validate/build/utils.d.ts new file mode 100644 index 000000000000..e43e65cfb903 --- /dev/null +++ b/packages/jest-validate/build/utils.d.ts @@ -0,0 +1,18 @@ +/** + * 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. + */ +export declare const DEPRECATION: string; +export declare const ERROR: string; +export declare const WARNING: string; +export declare const format: (value: unknown) => string; +export declare const formatPrettyObject: (value: unknown) => string; +export declare class ValidationError extends Error { + name: string; + message: string; + constructor(name: string, message: string, comment?: string | null); +} +export declare const logValidationWarning: (name: string, message: string, comment?: string | null | undefined) => void; +export declare const createDidYouMeanMessage: (unrecognized: string, allowedOptions: Array) => string; diff --git a/packages/jest-validate/build/utils.js b/packages/jest-validate/build/utils.js new file mode 100644 index 000000000000..87025eb6524e --- /dev/null +++ b/packages/jest-validate/build/utils.js @@ -0,0 +1,121 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.createDidYouMeanMessage = exports.logValidationWarning = exports.ValidationError = exports.formatPrettyObject = exports.format = exports.WARNING = exports.ERROR = exports.DEPRECATION = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _leven() { + const data = _interopRequireDefault(require('leven')); + + _leven = function () { + return data; + }; + + return data; +} + +function _prettyFormat() { + const data = _interopRequireDefault(require('pretty-format')); + + _prettyFormat = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const BULLET = _chalk().default.bold('\u25cf'); + +const DEPRECATION = `${BULLET} Deprecation Warning`; +exports.DEPRECATION = DEPRECATION; +const ERROR = `${BULLET} Validation Error`; +exports.ERROR = ERROR; +const WARNING = `${BULLET} Validation Warning`; +exports.WARNING = WARNING; + +const format = value => + typeof value === 'function' + ? value.toString() + : (0, _prettyFormat().default)(value, { + min: true + }); + +exports.format = format; + +const formatPrettyObject = value => + typeof value === 'function' + ? value.toString() + : JSON.stringify(value, null, 2).split('\n').join('\n '); + +exports.formatPrettyObject = formatPrettyObject; + +class ValidationError extends Error { + constructor(name, message, comment) { + super(); + + _defineProperty(this, 'name', void 0); + + _defineProperty(this, 'message', void 0); + + comment = comment ? '\n\n' + comment : '\n'; + this.name = ''; + this.message = _chalk().default.red( + _chalk().default.bold(name) + ':\n\n' + message + comment + ); + Error.captureStackTrace(this, () => {}); + } +} + +exports.ValidationError = ValidationError; + +const logValidationWarning = (name, message, comment) => { + comment = comment ? '\n\n' + comment : '\n'; + console.warn( + _chalk().default.yellow( + _chalk().default.bold(name) + ':\n\n' + message + comment + ) + ); +}; + +exports.logValidationWarning = logValidationWarning; + +const createDidYouMeanMessage = (unrecognized, allowedOptions) => { + const suggestion = allowedOptions.find(option => { + const steps = (0, _leven().default)(option, unrecognized); + return steps < 3; + }); + return suggestion + ? `Did you mean ${_chalk().default.bold(format(suggestion))}?` + : ''; +}; + +exports.createDidYouMeanMessage = createDidYouMeanMessage; diff --git a/packages/jest-validate/build/validate.d.ts b/packages/jest-validate/build/validate.d.ts new file mode 100644 index 000000000000..24a49af55cba --- /dev/null +++ b/packages/jest-validate/build/validate.d.ts @@ -0,0 +1,13 @@ +/** + * 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. + */ +import type { ValidationOptions } from './types'; +declare let hasDeprecationWarnings: boolean; +declare const validate: (config: Record, options: ValidationOptions) => { + hasDeprecationWarnings: boolean; + isValid: boolean; +}; +export default validate; diff --git a/packages/jest-validate/build/validate.js b/packages/jest-validate/build/validate.js new file mode 100644 index 000000000000..fa8d4f4c52a1 --- /dev/null +++ b/packages/jest-validate/build/validate.js @@ -0,0 +1,131 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _defaultConfig = _interopRequireDefault(require('./defaultConfig')); + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +let hasDeprecationWarnings = false; + +const shouldSkipValidationForPath = (path, key, denylist) => + denylist ? denylist.includes([...path, key].join('.')) : false; + +const _validate = (config, exampleConfig, options, path = []) => { + if ( + typeof config !== 'object' || + config == null || + typeof exampleConfig !== 'object' || + exampleConfig == null + ) { + return { + hasDeprecationWarnings + }; + } + + for (const key in config) { + if ( + options.deprecatedConfig && + key in options.deprecatedConfig && + typeof options.deprecate === 'function' + ) { + const isDeprecatedKey = options.deprecate( + config, + key, + options.deprecatedConfig, + options + ); + hasDeprecationWarnings = hasDeprecationWarnings || isDeprecatedKey; + } else if (allowsMultipleTypes(key)) { + const value = config[key]; + + if ( + typeof options.condition === 'function' && + typeof options.error === 'function' + ) { + if (key === 'maxWorkers' && !isOfTypeStringOrNumber(value)) { + throw new _utils.ValidationError( + 'Validation Error', + `${key} has to be of type string or number`, + `maxWorkers=50% or\nmaxWorkers=3` + ); + } + } + } else if (Object.hasOwnProperty.call(exampleConfig, key)) { + if ( + typeof options.condition === 'function' && + typeof options.error === 'function' && + !options.condition(config[key], exampleConfig[key]) + ) { + options.error(key, config[key], exampleConfig[key], options, path); + } + } else if ( + shouldSkipValidationForPath(path, key, options.recursiveDenylist) + ) { + // skip validating unknown options inside blacklisted paths + } else { + options.unknown && + options.unknown(config, exampleConfig, key, options, path); + } + + if ( + options.recursive && + !Array.isArray(exampleConfig[key]) && + options.recursiveDenylist && + !shouldSkipValidationForPath(path, key, options.recursiveDenylist) + ) { + _validate(config[key], exampleConfig[key], options, [...path, key]); + } + } + + return { + hasDeprecationWarnings + }; +}; + +const allowsMultipleTypes = key => key === 'maxWorkers'; + +const isOfTypeStringOrNumber = value => + typeof value === 'number' || typeof value === 'string'; + +const validate = (config, options) => { + hasDeprecationWarnings = false; // Preserve default denylist entries even with user-supplied denylist + + const combinedDenylist = [ + ...(_defaultConfig.default.recursiveDenylist || []), + ...(options.recursiveDenylist || []) + ]; + const defaultedOptions = Object.assign({ + ..._defaultConfig.default, + ...options, + recursiveDenylist: combinedDenylist, + title: options.title || _defaultConfig.default.title + }); + + const {hasDeprecationWarnings: hdw} = _validate( + config, + options.exampleConfig, + defaultedOptions + ); + + return { + hasDeprecationWarnings: hdw, + isValid: true + }; +}; + +var _default = validate; +exports.default = _default; diff --git a/packages/jest-validate/build/validateCLIOptions.d.ts b/packages/jest-validate/build/validateCLIOptions.d.ts new file mode 100644 index 000000000000..e1e8102929ed --- /dev/null +++ b/packages/jest-validate/build/validateCLIOptions.d.ts @@ -0,0 +1,14 @@ +/** + * 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. + */ +import type { Options } from 'yargs'; +import type { Config } from '@jest/types'; +import type { DeprecatedOptions } from './types'; +export declare const DOCUMENTATION_NOTE: string; +export default function validateCLIOptions(argv: Config.Argv, options: { + deprecationEntries: DeprecatedOptions; + [s: string]: Options; +}, rawArgv?: Array): boolean; diff --git a/packages/jest-validate/build/validateCLIOptions.js b/packages/jest-validate/build/validateCLIOptions.js new file mode 100644 index 000000000000..78160e933e6c --- /dev/null +++ b/packages/jest-validate/build/validateCLIOptions.js @@ -0,0 +1,137 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = validateCLIOptions; +exports.DOCUMENTATION_NOTE = void 0; + +function _camelcase() { + const data = _interopRequireDefault(require('camelcase')); + + _camelcase = function () { + return data; + }; + + return data; +} + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +var _defaultConfig = _interopRequireDefault(require('./defaultConfig')); + +var _deprecated = require('./deprecated'); + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 BULLET = _chalk().default.bold('\u25cf'); + +const DOCUMENTATION_NOTE = ` ${_chalk().default.bold( + 'CLI Options Documentation:' +)} + https://jestjs.io/docs/en/cli.html +`; +exports.DOCUMENTATION_NOTE = DOCUMENTATION_NOTE; + +const createCLIValidationError = (unrecognizedOptions, allowedOptions) => { + let title = `${BULLET} Unrecognized CLI Parameter`; + let message; + const comment = + ` ${_chalk().default.bold('CLI Options Documentation')}:\n` + + ` https://jestjs.io/docs/en/cli.html\n`; + + if (unrecognizedOptions.length === 1) { + const unrecognized = unrecognizedOptions[0]; + const didYouMeanMessage = + unrecognized.length > 1 + ? (0, _utils.createDidYouMeanMessage)( + unrecognized, + Array.from(allowedOptions) + ) + : ''; + message = + ` Unrecognized option ${_chalk().default.bold( + (0, _utils.format)(unrecognized) + )}.` + (didYouMeanMessage ? ` ${didYouMeanMessage}` : ''); + } else { + title += 's'; + message = + ` Following options were not recognized:\n` + + ` ${_chalk().default.bold((0, _utils.format)(unrecognizedOptions))}`; + } + + return new _utils.ValidationError(title, message, comment); +}; + +const logDeprecatedOptions = (deprecatedOptions, deprecationEntries, argv) => { + deprecatedOptions.forEach(opt => { + (0, _deprecated.deprecationWarning)(argv, opt, deprecationEntries, { + ..._defaultConfig.default, + comment: DOCUMENTATION_NOTE + }); + }); +}; + +function validateCLIOptions(argv, options, rawArgv = []) { + const yargsSpecialOptions = ['$0', '_', 'help', 'h']; + const deprecationEntries = options.deprecationEntries || {}; + const allowedOptions = Object.keys(options).reduce( + (acc, option) => acc.add(option).add(options[option].alias || option), + new Set(yargsSpecialOptions) + ); + const unrecognizedOptions = Object.keys(argv).filter( + arg => + !allowedOptions.has((0, _camelcase().default)(arg)) && + !allowedOptions.has(arg) && + (!rawArgv.length || rawArgv.includes(arg)), + [] + ); + + if (unrecognizedOptions.length) { + throw createCLIValidationError(unrecognizedOptions, allowedOptions); + } + + const CLIDeprecations = Object.keys(deprecationEntries).reduce( + (acc, entry) => { + if (options[entry]) { + acc[entry] = deprecationEntries[entry]; + const alias = options[entry].alias; + + if (alias) { + acc[alias] = deprecationEntries[entry]; + } + } + + return acc; + }, + {} + ); + const deprecations = new Set(Object.keys(CLIDeprecations)); + const deprecatedOptions = Object.keys(argv).filter( + arg => deprecations.has(arg) && argv[arg] != null + ); + + if (deprecatedOptions.length) { + logDeprecatedOptions(deprecatedOptions, CLIDeprecations, argv); + } + + return true; +} diff --git a/packages/jest-validate/build/warnings.d.ts b/packages/jest-validate/build/warnings.d.ts new file mode 100644 index 000000000000..70ff76c91cdf --- /dev/null +++ b/packages/jest-validate/build/warnings.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { ValidationOptions } from './types'; +export declare const unknownOptionWarning: (config: Record, exampleConfig: Record, option: string, options: ValidationOptions, path?: string[] | undefined) => void; diff --git a/packages/jest-validate/build/warnings.js b/packages/jest-validate/build/warnings.js new file mode 100644 index 000000000000..a179cda9cbe0 --- /dev/null +++ b/packages/jest-validate/build/warnings.js @@ -0,0 +1,48 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.unknownOptionWarning = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 unknownOptionWarning = (config, exampleConfig, option, options, path) => { + const didYouMean = (0, _utils.createDidYouMeanMessage)( + option, + Object.keys(exampleConfig) + ); + const message = + ` Unknown option ${_chalk().default.bold( + `"${path && path.length > 0 ? path.join('.') + '.' : ''}${option}"` + )} with value ${_chalk().default.bold( + (0, _utils.format)(config[option]) + )} was found.` + + (didYouMean && ` ${didYouMean}`) + + `\n This is probably a typing mistake. Fixing it will remove this message.`; + const comment = options.comment; + const name = (options.title && options.title.warning) || _utils.WARNING; + (0, _utils.logValidationWarning)(name, message, comment); +}; + +exports.unknownOptionWarning = unknownOptionWarning; diff --git a/packages/jest-watcher/build/BaseWatchPlugin.d.ts b/packages/jest-watcher/build/BaseWatchPlugin.d.ts new file mode 100644 index 000000000000..2c96768edca2 --- /dev/null +++ b/packages/jest-watcher/build/BaseWatchPlugin.d.ts @@ -0,0 +1,22 @@ +/** + * 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. + */ +/// +import type { Config } from '@jest/types'; +import type { JestHookSubscriber, UpdateConfigCallback, UsageData, WatchPlugin } from './types'; +declare class BaseWatchPlugin implements WatchPlugin { + protected _stdin: NodeJS.ReadStream; + protected _stdout: NodeJS.WriteStream; + constructor({ stdin, stdout, }: { + stdin: NodeJS.ReadStream; + stdout: NodeJS.WriteStream; + }); + apply(_hooks: JestHookSubscriber): void; + getUsageInfo(_globalConfig: Config.GlobalConfig): UsageData | null; + onKey(_key: string): void; + run(_globalConfig: Config.GlobalConfig, _updateConfigAndRun: UpdateConfigCallback): Promise; +} +export default BaseWatchPlugin; diff --git a/packages/jest-watcher/build/BaseWatchPlugin.js b/packages/jest-watcher/build/BaseWatchPlugin.js new file mode 100644 index 000000000000..40b368cbdd91 --- /dev/null +++ b/packages/jest-watcher/build/BaseWatchPlugin.js @@ -0,0 +1,52 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * 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. + */ +class BaseWatchPlugin { + constructor({stdin, stdout}) { + _defineProperty(this, '_stdin', void 0); + + _defineProperty(this, '_stdout', void 0); + + this._stdin = stdin; + this._stdout = stdout; + } + + apply(_hooks) {} + + getUsageInfo(_globalConfig) { + return null; + } + + onKey(_key) {} + + run(_globalConfig, _updateConfigAndRun) { + return Promise.resolve(); + } +} + +var _default = BaseWatchPlugin; +exports.default = _default; diff --git a/packages/jest-watcher/build/JestHooks.d.ts b/packages/jest-watcher/build/JestHooks.d.ts new file mode 100644 index 000000000000..047d6a7f15eb --- /dev/null +++ b/packages/jest-watcher/build/JestHooks.d.ts @@ -0,0 +1,18 @@ +/** + * 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. + */ +import type { JestHookEmitter, JestHookSubscriber } from './types'; +declare type AvailableHooks = 'onFileChange' | 'onTestRunComplete' | 'shouldRunTestSuite'; +declare class JestHooks { + private _listeners; + private _subscriber; + private _emitter; + constructor(); + isUsed(hook: AvailableHooks): boolean; + getSubscriber(): Readonly; + getEmitter(): Readonly; +} +export default JestHooks; diff --git a/packages/jest-watcher/build/JestHooks.js b/packages/jest-watcher/build/JestHooks.js new file mode 100644 index 000000000000..d2fd5b931293 --- /dev/null +++ b/packages/jest-watcher/build/JestHooks.js @@ -0,0 +1,91 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * 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. + */ +class JestHooks { + constructor() { + _defineProperty(this, '_listeners', void 0); + + _defineProperty(this, '_subscriber', void 0); + + _defineProperty(this, '_emitter', void 0); + + this._listeners = { + onFileChange: [], + onTestRunComplete: [], + shouldRunTestSuite: [] + }; + this._subscriber = { + onFileChange: fn => { + this._listeners.onFileChange.push(fn); + }, + onTestRunComplete: fn => { + this._listeners.onTestRunComplete.push(fn); + }, + shouldRunTestSuite: fn => { + this._listeners.shouldRunTestSuite.push(fn); + } + }; + this._emitter = { + onFileChange: fs => + this._listeners.onFileChange.forEach(listener => listener(fs)), + onTestRunComplete: results => + this._listeners.onTestRunComplete.forEach(listener => + listener(results) + ), + shouldRunTestSuite: async testSuiteInfo => { + const result = await Promise.all( + this._listeners.shouldRunTestSuite.map(listener => + listener(testSuiteInfo) + ) + ); + return result.every(Boolean); + } + }; + } + + isUsed(hook) { + var _this$_listeners$hook; + + return ( + ((_this$_listeners$hook = this._listeners[hook]) === null || + _this$_listeners$hook === void 0 + ? void 0 + : _this$_listeners$hook.length) > 0 + ); + } + + getSubscriber() { + return this._subscriber; + } + + getEmitter() { + return this._emitter; + } +} + +var _default = JestHooks; +exports.default = _default; diff --git a/packages/jest-watcher/build/PatternPrompt.d.ts b/packages/jest-watcher/build/PatternPrompt.d.ts new file mode 100644 index 000000000000..993bc9df3f26 --- /dev/null +++ b/packages/jest-watcher/build/PatternPrompt.d.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ +/// +import type Prompt from './lib/Prompt'; +import type { ScrollOptions } from './types'; +export default class PatternPrompt { + protected _pipe: NodeJS.WritableStream; + protected _prompt: Prompt; + protected _entityName: string; + protected _currentUsageRows: number; + constructor(pipe: NodeJS.WritableStream, prompt: Prompt); + run(onSuccess: (value: string) => void, onCancel: () => void, options?: { + header: string; + }): void; + protected _onChange(_pattern: string, _options: ScrollOptions): void; +} diff --git a/packages/jest-watcher/build/PatternPrompt.js b/packages/jest-watcher/build/PatternPrompt.js new file mode 100644 index 000000000000..c60eceaba42b --- /dev/null +++ b/packages/jest-watcher/build/PatternPrompt.js @@ -0,0 +1,113 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _ansiEscapes() { + const data = _interopRequireDefault(require('ansi-escapes')); + + _ansiEscapes = function () { + return data; + }; + + return data; +} + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _jestUtil() { + const data = require('jest-util'); + + _jestUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const {CLEAR} = _jestUtil().specialChars; + +const usage = entity => + `\n${_chalk().default.bold('Pattern Mode Usage')}\n` + + ` ${_chalk().default.dim('\u203A Press')} Esc ${_chalk().default.dim( + 'to exit pattern mode.' + )}\n` + + ` ${_chalk().default.dim('\u203A Press')} Enter ` + + `${_chalk().default.dim(`to filter by a ${entity} regex pattern.`)}\n` + + `\n`; + +const usageRows = usage('').split('\n').length; + +class PatternPrompt { + constructor(pipe, prompt) { + _defineProperty(this, '_pipe', void 0); + + _defineProperty(this, '_prompt', void 0); + + _defineProperty(this, '_entityName', void 0); + + _defineProperty(this, '_currentUsageRows', void 0); + + // TODO: Should come in the constructor + this._entityName = ''; + this._pipe = pipe; + this._prompt = prompt; + this._currentUsageRows = usageRows; + } + + run(onSuccess, onCancel, options) { + this._pipe.write(_ansiEscapes().default.cursorHide); + + this._pipe.write(CLEAR); + + if (options && options.header) { + this._pipe.write(options.header + '\n'); + + this._currentUsageRows = usageRows + options.header.split('\n').length; + } else { + this._currentUsageRows = usageRows; + } + + this._pipe.write(usage(this._entityName)); + + this._pipe.write(_ansiEscapes().default.cursorShow); + + this._prompt.enter(this._onChange.bind(this), onSuccess, onCancel); + } + + _onChange(_pattern, _options) { + this._pipe.write(_ansiEscapes().default.eraseLine); + + this._pipe.write(_ansiEscapes().default.cursorLeft); + } +} + +exports.default = PatternPrompt; diff --git a/packages/jest-watcher/build/constants.d.ts b/packages/jest-watcher/build/constants.d.ts new file mode 100644 index 000000000000..ebdfd051052f --- /dev/null +++ b/packages/jest-watcher/build/constants.d.ts @@ -0,0 +1,17 @@ +/** + * 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. + */ +export declare const KEYS: { + ARROW_DOWN: string; + ARROW_LEFT: string; + ARROW_RIGHT: string; + ARROW_UP: string; + BACKSPACE: string; + CONTROL_C: string; + CONTROL_D: string; + ENTER: string; + ESCAPE: string; +}; diff --git a/packages/jest-watcher/build/constants.js b/packages/jest-watcher/build/constants.js new file mode 100644 index 000000000000..97b0bdd7b07b --- /dev/null +++ b/packages/jest-watcher/build/constants.js @@ -0,0 +1,26 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.KEYS = void 0; + +/** + * 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 isWindows = process.platform === 'win32'; +const KEYS = { + ARROW_DOWN: '\u001b[B', + ARROW_LEFT: '\u001b[D', + ARROW_RIGHT: '\u001b[C', + ARROW_UP: '\u001b[A', + BACKSPACE: Buffer.from(isWindows ? '08' : '7f', 'hex').toString(), + CONTROL_C: '\u0003', + CONTROL_D: '\u0004', + ENTER: '\r', + ESCAPE: '\u001b' +}; +exports.KEYS = KEYS; diff --git a/packages/jest-watcher/build/index.d.ts b/packages/jest-watcher/build/index.d.ts new file mode 100644 index 000000000000..433a369b768a --- /dev/null +++ b/packages/jest-watcher/build/index.d.ts @@ -0,0 +1,13 @@ +/** + * 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. + */ +export { default as BaseWatchPlugin } from './BaseWatchPlugin'; +export { default as JestHook } from './JestHooks'; +export { default as PatternPrompt } from './PatternPrompt'; +export * from './constants'; +export type { AllowedConfigOptions, JestHookEmitter, JestHookSubscriber, ScrollOptions, UpdateConfigCallback, UsageData, WatchPlugin, WatchPluginClass, } from './types'; +export { default as Prompt } from './lib/Prompt'; +export * from './lib/patternModeHelpers'; diff --git a/packages/jest-watcher/build/index.js b/packages/jest-watcher/build/index.js new file mode 100644 index 000000000000..d79b5260d305 --- /dev/null +++ b/packages/jest-watcher/build/index.js @@ -0,0 +1,75 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +var _exportNames = { + BaseWatchPlugin: true, + JestHook: true, + PatternPrompt: true, + Prompt: true +}; +Object.defineProperty(exports, 'BaseWatchPlugin', { + enumerable: true, + get: function () { + return _BaseWatchPlugin.default; + } +}); +Object.defineProperty(exports, 'JestHook', { + enumerable: true, + get: function () { + return _JestHooks.default; + } +}); +Object.defineProperty(exports, 'PatternPrompt', { + enumerable: true, + get: function () { + return _PatternPrompt.default; + } +}); +Object.defineProperty(exports, 'Prompt', { + enumerable: true, + get: function () { + return _Prompt.default; + } +}); + +var _BaseWatchPlugin = _interopRequireDefault(require('./BaseWatchPlugin')); + +var _JestHooks = _interopRequireDefault(require('./JestHooks')); + +var _PatternPrompt = _interopRequireDefault(require('./PatternPrompt')); + +var _constants = require('./constants'); + +Object.keys(_constants).forEach(function (key) { + if (key === 'default' || key === '__esModule') return; + if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; + if (key in exports && exports[key] === _constants[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _constants[key]; + } + }); +}); + +var _Prompt = _interopRequireDefault(require('./lib/Prompt')); + +var _patternModeHelpers = require('./lib/patternModeHelpers'); + +Object.keys(_patternModeHelpers).forEach(function (key) { + if (key === 'default' || key === '__esModule') return; + if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; + if (key in exports && exports[key] === _patternModeHelpers[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _patternModeHelpers[key]; + } + }); +}); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} diff --git a/packages/jest-watcher/build/lib/Prompt.d.ts b/packages/jest-watcher/build/lib/Prompt.d.ts new file mode 100644 index 000000000000..563b9f12301b --- /dev/null +++ b/packages/jest-watcher/build/lib/Prompt.d.ts @@ -0,0 +1,25 @@ +/** + * 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. + */ +import type { ScrollOptions } from '../types'; +export default class Prompt { + private _entering; + private _value; + private _onChange; + private _onSuccess; + private _onCancel; + private _offset; + private _promptLength; + private _selection; + constructor(); + private _onResize; + enter(onChange: (pattern: string, options: ScrollOptions) => void, onSuccess: (pattern: string) => void, onCancel: () => void): void; + setPromptLength(length: number): void; + setPromptSelection(selected: string): void; + put(key: string): void; + abort(): void; + isEntering(): boolean; +} diff --git a/packages/jest-watcher/build/lib/Prompt.js b/packages/jest-watcher/build/lib/Prompt.js new file mode 100644 index 000000000000..0a1bf4a5a148 --- /dev/null +++ b/packages/jest-watcher/build/lib/Prompt.js @@ -0,0 +1,157 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _constants() { + const data = require('../constants'); + + _constants = function () { + return data; + }; + + return data; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class Prompt { + constructor() { + _defineProperty(this, '_entering', void 0); + + _defineProperty(this, '_value', void 0); + + _defineProperty(this, '_onChange', void 0); + + _defineProperty(this, '_onSuccess', void 0); + + _defineProperty(this, '_onCancel', void 0); + + _defineProperty(this, '_offset', void 0); + + _defineProperty(this, '_promptLength', void 0); + + _defineProperty(this, '_selection', void 0); + + _defineProperty(this, '_onResize', () => { + this._onChange(); + }); + + // Copied from `enter` to satisfy TS + this._entering = true; + this._value = ''; + this._selection = null; + this._offset = -1; + this._promptLength = 0; + + this._onChange = () => {}; + + this._onSuccess = () => {}; + + this._onCancel = () => {}; + } + + enter(onChange, onSuccess, onCancel) { + this._entering = true; + this._value = ''; + this._onSuccess = onSuccess; + this._onCancel = onCancel; + this._selection = null; + this._offset = -1; + this._promptLength = 0; + + this._onChange = () => + onChange(this._value, { + max: 10, + offset: this._offset + }); + + this._onChange(); + + process.stdout.on('resize', this._onResize); + } + + setPromptLength(length) { + this._promptLength = length; + } + + setPromptSelection(selected) { + this._selection = selected; + } + + put(key) { + switch (key) { + case _constants().KEYS.ENTER: + this._entering = false; + + this._onSuccess(this._selection || this._value); + + this.abort(); + break; + + case _constants().KEYS.ESCAPE: + this._entering = false; + + this._onCancel(this._value); + + this.abort(); + break; + + case _constants().KEYS.ARROW_DOWN: + this._offset = Math.min(this._offset + 1, this._promptLength - 1); + + this._onChange(); + + break; + + case _constants().KEYS.ARROW_UP: + this._offset = Math.max(this._offset - 1, -1); + + this._onChange(); + + break; + + case _constants().KEYS.ARROW_LEFT: + case _constants().KEYS.ARROW_RIGHT: + break; + + default: + this._value = + key === _constants().KEYS.BACKSPACE + ? this._value.slice(0, -1) + : this._value + key; + this._offset = -1; + this._selection = null; + + this._onChange(); + + break; + } + } + + abort() { + this._entering = false; + this._value = ''; + process.stdout.removeListener('resize', this._onResize); + } + + isEntering() { + return this._entering; + } +} + +exports.default = Prompt; diff --git a/packages/jest-watcher/build/lib/colorize.d.ts b/packages/jest-watcher/build/lib/colorize.d.ts new file mode 100644 index 000000000000..9cecdb9481a5 --- /dev/null +++ b/packages/jest-watcher/build/lib/colorize.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +declare const _default: (str: string, start: number, end: number) => string; +export default _default; diff --git a/packages/jest-watcher/build/lib/colorize.js b/packages/jest-watcher/build/lib/colorize.js new file mode 100644 index 000000000000..1a1058623efc --- /dev/null +++ b/packages/jest-watcher/build/lib/colorize.js @@ -0,0 +1,33 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +var _default = (str, start, end) => + _chalk().default.dim(str.slice(0, start)) + + _chalk().default.reset(str.slice(start, end)) + + _chalk().default.dim(str.slice(end)); + +exports.default = _default; diff --git a/packages/jest-watcher/build/lib/formatTestNameByPattern.d.ts b/packages/jest-watcher/build/lib/formatTestNameByPattern.d.ts new file mode 100644 index 000000000000..57f795ffab55 --- /dev/null +++ b/packages/jest-watcher/build/lib/formatTestNameByPattern.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +declare const _default: (testName: string, pattern: string, width: number) => string; +export default _default; diff --git a/packages/jest-watcher/build/lib/formatTestNameByPattern.js b/packages/jest-watcher/build/lib/formatTestNameByPattern.js new file mode 100644 index 000000000000..41893ce9aa78 --- /dev/null +++ b/packages/jest-watcher/build/lib/formatTestNameByPattern.js @@ -0,0 +1,83 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +var _colorize = _interopRequireDefault(require('./colorize')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 DOTS = '...'; +const ENTER = '⏎'; + +var _default = (testName, pattern, width) => { + const inlineTestName = testName.replace(/(\r\n|\n|\r)/gm, ENTER); + let regexp; + + try { + regexp = new RegExp(pattern, 'i'); + } catch { + return _chalk().default.dim(inlineTestName); + } + + const match = inlineTestName.match(regexp); + + if (!match) { + return _chalk().default.dim(inlineTestName); + } + + const startPatternIndex = Math.max(match.index || 0, 0); + const endPatternIndex = startPatternIndex + match[0].length; + + if (inlineTestName.length <= width) { + return (0, _colorize.default)( + inlineTestName, + startPatternIndex, + endPatternIndex + ); + } + + const slicedTestName = inlineTestName.slice(0, width - DOTS.length); + + if (startPatternIndex < slicedTestName.length) { + if (endPatternIndex > slicedTestName.length) { + return (0, _colorize.default)( + slicedTestName + DOTS, + startPatternIndex, + slicedTestName.length + DOTS.length + ); + } else { + return (0, _colorize.default)( + slicedTestName + DOTS, + Math.min(startPatternIndex, slicedTestName.length), + endPatternIndex + ); + } + } + + return `${_chalk().default.dim(slicedTestName)}${_chalk().default.reset( + DOTS + )}`; +}; + +exports.default = _default; diff --git a/packages/jest-watcher/build/lib/patternModeHelpers.d.ts b/packages/jest-watcher/build/lib/patternModeHelpers.d.ts new file mode 100644 index 000000000000..99487c20dac4 --- /dev/null +++ b/packages/jest-watcher/build/lib/patternModeHelpers.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +/// +export declare const printPatternCaret: (pattern: string, pipe: NodeJS.WritableStream) => void; +export declare const printRestoredPatternCaret: (pattern: string, currentUsageRows: number, pipe: NodeJS.WritableStream) => void; diff --git a/packages/jest-watcher/build/lib/patternModeHelpers.js b/packages/jest-watcher/build/lib/patternModeHelpers.js new file mode 100644 index 000000000000..6e652e6951e3 --- /dev/null +++ b/packages/jest-watcher/build/lib/patternModeHelpers.js @@ -0,0 +1,68 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.printRestoredPatternCaret = exports.printPatternCaret = void 0; + +function _ansiEscapes() { + const data = _interopRequireDefault(require('ansi-escapes')); + + _ansiEscapes = function () { + return data; + }; + + return data; +} + +function _chalk() { + const data = _interopRequireDefault(require('chalk')); + + _chalk = function () { + return data; + }; + + return data; +} + +function _stringLength() { + const data = _interopRequireDefault(require('string-length')); + + _stringLength = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 printPatternCaret = (pattern, pipe) => { + const inputText = `${_chalk().default.dim(' pattern \u203A')} ${pattern}`; + pipe.write(_ansiEscapes().default.eraseDown); + pipe.write(inputText); + pipe.write(_ansiEscapes().default.cursorSavePosition); +}; + +exports.printPatternCaret = printPatternCaret; + +const printRestoredPatternCaret = (pattern, currentUsageRows, pipe) => { + const inputText = `${_chalk().default.dim(' pattern \u203A')} ${pattern}`; + pipe.write( + _ansiEscapes().default.cursorTo( + (0, _stringLength().default)(inputText), + currentUsageRows - 1 + ) + ); + pipe.write(_ansiEscapes().default.cursorRestorePosition); +}; + +exports.printRestoredPatternCaret = printRestoredPatternCaret; diff --git a/packages/jest-watcher/build/lib/scroll.d.ts b/packages/jest-watcher/build/lib/scroll.d.ts new file mode 100644 index 000000000000..536c3b7065c2 --- /dev/null +++ b/packages/jest-watcher/build/lib/scroll.d.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ +import type { ScrollOptions } from '../types'; +export default function scroll(size: number, { offset, max }: ScrollOptions): { + end: number; + index: number; + start: number; +}; diff --git a/packages/jest-watcher/build/lib/scroll.js b/packages/jest-watcher/build/lib/scroll.js new file mode 100644 index 000000000000..4a545041e5a7 --- /dev/null +++ b/packages/jest-watcher/build/lib/scroll.js @@ -0,0 +1,34 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = scroll; + +/** + * 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. + */ +function scroll(size, {offset, max}) { + let start = 0; + let index = Math.min(offset, size); + const halfScreen = max / 2; + + if (index <= halfScreen) { + start = 0; + } else { + if (size >= max) { + start = Math.min(index - halfScreen - 1, size - max); + } + + index = Math.min(index - start, size); + } + + return { + end: Math.min(size, start + max), + index, + start + }; +} diff --git a/packages/jest-watcher/build/types.d.ts b/packages/jest-watcher/build/types.d.ts new file mode 100644 index 000000000000..29c160b178cc --- /dev/null +++ b/packages/jest-watcher/build/types.d.ts @@ -0,0 +1,59 @@ +/** + * 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. + */ +/// +import type { AggregatedResult } from '@jest/test-result'; +import type { Config } from '@jest/types'; +declare type TestSuiteInfo = { + config: Config.ProjectConfig; + duration?: number; + testPath: string; +}; +export declare type JestHookExposedFS = { + projects: Array<{ + config: Config.ProjectConfig; + testPaths: Array; + }>; +}; +export declare type FileChange = (fs: JestHookExposedFS) => void; +export declare type ShouldRunTestSuite = (testSuiteInfo: TestSuiteInfo) => Promise; +export declare type TestRunComplete = (results: AggregatedResult) => void; +export declare type JestHookSubscriber = { + onFileChange: (fn: FileChange) => void; + onTestRunComplete: (fn: TestRunComplete) => void; + shouldRunTestSuite: (fn: ShouldRunTestSuite) => void; +}; +export declare type JestHookEmitter = { + onFileChange: (fs: JestHookExposedFS) => void; + onTestRunComplete: (results: AggregatedResult) => void; + shouldRunTestSuite: (testSuiteInfo: TestSuiteInfo) => Promise | boolean; +}; +export declare type UsageData = { + key: string; + prompt: string; +}; +export declare type AllowedConfigOptions = Partial & { + mode: 'watch' | 'watchAll'; +}>; +export declare type UpdateConfigCallback = (config?: AllowedConfigOptions) => void; +export interface WatchPlugin { + isInternal?: boolean; + apply?: (hooks: JestHookSubscriber) => void; + getUsageInfo?: (globalConfig: Config.GlobalConfig) => UsageData | null; + onKey?: (value: string) => void; + run?: (globalConfig: Config.GlobalConfig, updateConfigAndRun: UpdateConfigCallback) => Promise; +} +export interface WatchPluginClass { + new (options: { + stdin: NodeJS.ReadStream; + stdout: NodeJS.WriteStream; + }): WatchPlugin; +} +export declare type ScrollOptions = { + offset: number; + max: number; +}; +export {}; diff --git a/packages/jest-watcher/build/types.js b/packages/jest-watcher/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/jest-watcher/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/jest-worker/build/Farm.d.ts b/packages/jest-worker/build/Farm.d.ts new file mode 100644 index 000000000000..3fe79d0193e2 --- /dev/null +++ b/packages/jest-worker/build/Farm.d.ts @@ -0,0 +1,29 @@ +/** + * 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. + */ +import { FarmOptions, PromiseWithCustomMessage, TaskQueue } from './types'; +export default class Farm { + private _numOfWorkers; + private _callback; + private readonly _computeWorkerKey; + private readonly _workerSchedulingPolicy; + private readonly _cacheKeys; + private readonly _locks; + private _offset; + private readonly _taskQueue; + constructor(_numOfWorkers: number, _callback: Function, options?: { + computeWorkerKey?: FarmOptions['computeWorkerKey']; + workerSchedulingPolicy?: FarmOptions['workerSchedulingPolicy']; + taskQueue?: TaskQueue; + }); + doWork(method: string, ...args: Array): PromiseWithCustomMessage; + private _process; + private _push; + private _getNextWorkerOffset; + private _lock; + private _unlock; + private _isLocked; +} diff --git a/packages/jest-worker/build/Farm.js b/packages/jest-worker/build/Farm.js new file mode 100644 index 000000000000..d036eed89b42 --- /dev/null +++ b/packages/jest-worker/build/Farm.js @@ -0,0 +1,201 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _FifoQueue = _interopRequireDefault(require('./FifoQueue')); + +var _types = require('./types'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class Farm { + constructor(_numOfWorkers, _callback, options = {}) { + var _options$workerSchedu, _options$taskQueue; + + this._numOfWorkers = _numOfWorkers; + this._callback = _callback; + + _defineProperty(this, '_computeWorkerKey', void 0); + + _defineProperty(this, '_workerSchedulingPolicy', void 0); + + _defineProperty(this, '_cacheKeys', Object.create(null)); + + _defineProperty(this, '_locks', []); + + _defineProperty(this, '_offset', 0); + + _defineProperty(this, '_taskQueue', void 0); + + this._computeWorkerKey = options.computeWorkerKey; + this._workerSchedulingPolicy = + (_options$workerSchedu = options.workerSchedulingPolicy) !== null && + _options$workerSchedu !== void 0 + ? _options$workerSchedu + : 'round-robin'; + this._taskQueue = + (_options$taskQueue = options.taskQueue) !== null && + _options$taskQueue !== void 0 + ? _options$taskQueue + : new _FifoQueue.default(); + } + + doWork(method, ...args) { + const customMessageListeners = new Set(); + + const addCustomMessageListener = listener => { + customMessageListeners.add(listener); + return () => { + customMessageListeners.delete(listener); + }; + }; + + const onCustomMessage = message => { + customMessageListeners.forEach(listener => listener(message)); + }; + + const promise = new Promise((resolve, reject) => { + const computeWorkerKey = this._computeWorkerKey; + const request = [_types.CHILD_MESSAGE_CALL, false, method, args]; + let worker = null; + let hash = null; + + if (computeWorkerKey) { + hash = computeWorkerKey.call(this, method, ...args); + worker = hash == null ? null : this._cacheKeys[hash]; + } + + const onStart = worker => { + if (hash != null) { + this._cacheKeys[hash] = worker; + } + }; + + const onEnd = (error, result) => { + customMessageListeners.clear(); + + if (error) { + reject(error); + } else { + resolve(result); + } + }; + + const task = { + onCustomMessage, + onEnd, + onStart, + request + }; + + if (worker) { + this._taskQueue.enqueue(task, worker.getWorkerId()); + + this._process(worker.getWorkerId()); + } else { + this._push(task); + } + }); + promise.UNSTABLE_onCustomMessage = addCustomMessageListener; + return promise; + } + + _process(workerId) { + if (this._isLocked(workerId)) { + return this; + } + + const task = this._taskQueue.dequeue(workerId); + + if (!task) { + return this; + } + + if (task.request[1]) { + throw new Error('Queue implementation returned processed task'); + } + + const onEnd = (error, result) => { + task.onEnd(error, result); + + this._unlock(workerId); + + this._process(workerId); + }; + + task.request[1] = true; + + this._lock(workerId); + + this._callback( + workerId, + task.request, + task.onStart, + onEnd, + task.onCustomMessage + ); + + return this; + } + + _push(task) { + this._taskQueue.enqueue(task); + + const offset = this._getNextWorkerOffset(); + + for (let i = 0; i < this._numOfWorkers; i++) { + this._process((offset + i) % this._numOfWorkers); + + if (task.request[1]) { + break; + } + } + + return this; + } // Typescript ensures that the switch statement is exhaustive. + // Adding an explicit return at the end would disable the exhaustive check void. + // eslint-disable-next-line consistent-return + + _getNextWorkerOffset() { + switch (this._workerSchedulingPolicy) { + case 'in-order': + return 0; + + case 'round-robin': + return this._offset++; + } + } + + _lock(workerId) { + this._locks[workerId] = true; + } + + _unlock(workerId) { + this._locks[workerId] = false; + } + + _isLocked(workerId) { + return this._locks[workerId]; + } +} + +exports.default = Farm; diff --git a/packages/jest-worker/build/FifoQueue.d.ts b/packages/jest-worker/build/FifoQueue.d.ts new file mode 100644 index 000000000000..d78e9e2c7494 --- /dev/null +++ b/packages/jest-worker/build/FifoQueue.d.ts @@ -0,0 +1,18 @@ +/** + * 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. + */ +import type { QueueChildMessage, TaskQueue } from './types'; +/** + * First-in, First-out task queue that manages a dedicated pool + * for each worker as well as a shared queue. The FIFO ordering is guaranteed + * across the worker specific and shared queue. + */ +export default class FifoQueue implements TaskQueue { + private _workerQueues; + private _sharedQueue; + enqueue(task: QueueChildMessage, workerId?: number): void; + dequeue(workerId: number): QueueChildMessage | null; +} diff --git a/packages/jest-worker/build/FifoQueue.js b/packages/jest-worker/build/FifoQueue.js new file mode 100644 index 000000000000..bb52e077be89 --- /dev/null +++ b/packages/jest-worker/build/FifoQueue.js @@ -0,0 +1,171 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * 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. + */ + +/** + * First-in, First-out task queue that manages a dedicated pool + * for each worker as well as a shared queue. The FIFO ordering is guaranteed + * across the worker specific and shared queue. + */ +class FifoQueue { + constructor() { + _defineProperty(this, '_workerQueues', []); + + _defineProperty(this, '_sharedQueue', new InternalQueue()); + } + + enqueue(task, workerId) { + if (workerId == null) { + this._sharedQueue.enqueue(task); + + return; + } + + let workerQueue = this._workerQueues[workerId]; + + if (workerQueue == null) { + workerQueue = this._workerQueues[workerId] = new InternalQueue(); + } + + const sharedTop = this._sharedQueue.peekLast(); + + const item = { + previousSharedTask: sharedTop, + task + }; + workerQueue.enqueue(item); + } + + dequeue(workerId) { + var _this$_workerQueues$w, _workerTop$previousSh, _workerTop$previousSh2; + + const workerTop = + (_this$_workerQueues$w = this._workerQueues[workerId]) === null || + _this$_workerQueues$w === void 0 + ? void 0 + : _this$_workerQueues$w.peek(); + const sharedTaskIsProcessed = + (_workerTop$previousSh = + workerTop === null || workerTop === void 0 + ? void 0 + : (_workerTop$previousSh2 = workerTop.previousSharedTask) === null || + _workerTop$previousSh2 === void 0 + ? void 0 + : _workerTop$previousSh2.request[1]) !== null && + _workerTop$previousSh !== void 0 + ? _workerTop$previousSh + : true; // Process the top task from the shared queue if + // - there's no task in the worker specific queue or + // - if the non-worker-specific task after which this worker specifif task + // hasn been queued wasn't processed yet + + if (workerTop != null && sharedTaskIsProcessed) { + var _this$_workerQueues$w2, + _this$_workerQueues$w3, + _this$_workerQueues$w4; + + return (_this$_workerQueues$w2 = + (_this$_workerQueues$w3 = this._workerQueues[workerId]) === null || + _this$_workerQueues$w3 === void 0 + ? void 0 + : (_this$_workerQueues$w4 = _this$_workerQueues$w3.dequeue()) === + null || _this$_workerQueues$w4 === void 0 + ? void 0 + : _this$_workerQueues$w4.task) !== null && + _this$_workerQueues$w2 !== void 0 + ? _this$_workerQueues$w2 + : null; + } + + return this._sharedQueue.dequeue(); + } +} + +exports.default = FifoQueue; + +/** + * FIFO queue for a single worker / shared queue. + */ +class InternalQueue { + constructor() { + _defineProperty(this, '_head', null); + + _defineProperty(this, '_last', null); + } + + enqueue(value) { + const item = { + next: null, + value + }; + + if (this._last == null) { + this._head = item; + } else { + this._last.next = item; + } + + this._last = item; + } + + dequeue() { + if (this._head == null) { + return null; + } + + const item = this._head; + this._head = item.next; + + if (this._head == null) { + this._last = null; + } + + return item.value; + } + + peek() { + var _this$_head$value, _this$_head; + + return (_this$_head$value = + (_this$_head = this._head) === null || _this$_head === void 0 + ? void 0 + : _this$_head.value) !== null && _this$_head$value !== void 0 + ? _this$_head$value + : null; + } + + peekLast() { + var _this$_last$value, _this$_last; + + return (_this$_last$value = + (_this$_last = this._last) === null || _this$_last === void 0 + ? void 0 + : _this$_last.value) !== null && _this$_last$value !== void 0 + ? _this$_last$value + : null; + } +} diff --git a/packages/jest-worker/build/PriorityQueue.d.ts b/packages/jest-worker/build/PriorityQueue.d.ts new file mode 100644 index 000000000000..d6bcf4cf2619 --- /dev/null +++ b/packages/jest-worker/build/PriorityQueue.d.ts @@ -0,0 +1,41 @@ +/** + * 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. + */ +import type { QueueChildMessage, TaskQueue } from './types'; +export declare type ComputeTaskPriorityCallback = (method: string, ...args: Array) => number; +declare type QueueItem = { + task: QueueChildMessage; + priority: number; +}; +/** + * Priority queue that processes tasks in natural ordering (lower priority first) + * accoridng to the priority computed by the function passed in the constructor. + * + * FIFO ordering isn't guaranteed for tasks with the same priority. + * + * Worker specific tasks with the same priority as a non-worker specific task + * are always processed first. + */ +export default class PriorityQueue implements TaskQueue { + private _computePriority; + private _queue; + private _sharedQueue; + constructor(_computePriority: ComputeTaskPriorityCallback); + enqueue(task: QueueChildMessage, workerId?: number): void; + _enqueue(task: QueueChildMessage, queue: MinHeap): void; + dequeue(workerId: number): QueueChildMessage | null; + _getWorkerQueue(workerId: number): MinHeap; +} +declare type HeapItem = { + priority: number; +}; +declare class MinHeap { + private _heap; + peek(): TItem | null; + add(item: TItem): void; + poll(): TItem | null; +} +export {}; diff --git a/packages/jest-worker/build/PriorityQueue.js b/packages/jest-worker/build/PriorityQueue.js new file mode 100644 index 000000000000..5ffe03be1300 --- /dev/null +++ b/packages/jest-worker/build/PriorityQueue.js @@ -0,0 +1,188 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +/** + * 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. + */ + +/** + * Priority queue that processes tasks in natural ordering (lower priority first) + * accoridng to the priority computed by the function passed in the constructor. + * + * FIFO ordering isn't guaranteed for tasks with the same priority. + * + * Worker specific tasks with the same priority as a non-worker specific task + * are always processed first. + */ +class PriorityQueue { + constructor(_computePriority) { + this._computePriority = _computePriority; + + _defineProperty(this, '_queue', []); + + _defineProperty(this, '_sharedQueue', new MinHeap()); + } + + enqueue(task, workerId) { + if (workerId == null) { + this._enqueue(task, this._sharedQueue); + } else { + const queue = this._getWorkerQueue(workerId); + + this._enqueue(task, queue); + } + } + + _enqueue(task, queue) { + const item = { + priority: this._computePriority(task.request[2], ...task.request[3]), + task + }; + queue.add(item); + } + + dequeue(workerId) { + const workerQueue = this._getWorkerQueue(workerId); + + const workerTop = workerQueue.peek(); + + const sharedTop = this._sharedQueue.peek(); // use the task from the worker queue if there's no task in the shared queue + // or if the priority of the worker queue is smaller or equal to the + // priority of the top task in the shared queue. The tasks of the + // worker specific queue are preferred because no other worker can pick this + // specific task up. + + if ( + sharedTop == null || + (workerTop != null && workerTop.priority <= sharedTop.priority) + ) { + var _workerQueue$poll$tas, _workerQueue$poll; + + return (_workerQueue$poll$tas = + (_workerQueue$poll = workerQueue.poll()) === null || + _workerQueue$poll === void 0 + ? void 0 + : _workerQueue$poll.task) !== null && _workerQueue$poll$tas !== void 0 + ? _workerQueue$poll$tas + : null; + } + + return this._sharedQueue.poll().task; + } + + _getWorkerQueue(workerId) { + let queue = this._queue[workerId]; + + if (queue == null) { + queue = this._queue[workerId] = new MinHeap(); + } + + return queue; + } +} + +exports.default = PriorityQueue; + +class MinHeap { + constructor() { + _defineProperty(this, '_heap', []); + } + + peek() { + var _this$_heap$; + + return (_this$_heap$ = this._heap[0]) !== null && _this$_heap$ !== void 0 + ? _this$_heap$ + : null; + } + + add(item) { + const nodes = this._heap; + nodes.push(item); + + if (nodes.length === 1) { + return; + } + + let currentIndex = nodes.length - 1; // Bubble up the added node as long as the parent is bigger + + while (currentIndex > 0) { + const parentIndex = Math.floor((currentIndex + 1) / 2) - 1; + const parent = nodes[parentIndex]; + + if (parent.priority <= item.priority) { + break; + } + + nodes[currentIndex] = parent; + nodes[parentIndex] = item; + currentIndex = parentIndex; + } + } + + poll() { + const nodes = this._heap; + const result = nodes[0]; + const lastElement = nodes.pop(); // heap was empty or removed the last element + + if (result == null || nodes.length === 0) { + return result !== null && result !== void 0 ? result : null; + } + + let index = 0; + nodes[0] = + lastElement !== null && lastElement !== void 0 ? lastElement : null; + const element = nodes[0]; + + while (true) { + let swapIndex = null; + const rightChildIndex = (index + 1) * 2; + const leftChildIndex = rightChildIndex - 1; + const rightChild = nodes[rightChildIndex]; + const leftChild = nodes[leftChildIndex]; // if the left child is smaller, swap with the left + + if (leftChild != null && leftChild.priority < element.priority) { + swapIndex = leftChildIndex; + } // If the right child is smaller or the right child is smaller than the left + // then swap with the right child + + if ( + rightChild != null && + rightChild.priority < (swapIndex == null ? element : leftChild).priority + ) { + swapIndex = rightChildIndex; + } + + if (swapIndex == null) { + break; + } + + nodes[index] = nodes[swapIndex]; + nodes[swapIndex] = element; + index = swapIndex; + } + + return result; + } +} diff --git a/packages/jest-worker/build/WorkerPool.d.ts b/packages/jest-worker/build/WorkerPool.d.ts new file mode 100644 index 000000000000..9f13ff7d3b38 --- /dev/null +++ b/packages/jest-worker/build/WorkerPool.d.ts @@ -0,0 +1,13 @@ +/** + * 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. + */ +import BaseWorkerPool from './base/BaseWorkerPool'; +import type { ChildMessage, OnCustomMessage, OnEnd, OnStart, WorkerInterface, WorkerOptions, WorkerPoolInterface } from './types'; +declare class WorkerPool extends BaseWorkerPool implements WorkerPoolInterface { + send(workerId: number, request: ChildMessage, onStart: OnStart, onEnd: OnEnd, onCustomMessage: OnCustomMessage): void; + createWorker(workerOptions: WorkerOptions): WorkerInterface; +} +export default WorkerPool; diff --git a/packages/jest-worker/build/WorkerPool.js b/packages/jest-worker/build/WorkerPool.js new file mode 100644 index 000000000000..b19a679ed46b --- /dev/null +++ b/packages/jest-worker/build/WorkerPool.js @@ -0,0 +1,49 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +var _BaseWorkerPool = _interopRequireDefault(require('./base/BaseWorkerPool')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 canUseWorkerThreads = () => { + try { + require('worker_threads'); + + return true; + } catch { + return false; + } +}; + +class WorkerPool extends _BaseWorkerPool.default { + send(workerId, request, onStart, onEnd, onCustomMessage) { + this.getWorkerById(workerId).send(request, onStart, onEnd, onCustomMessage); + } + + createWorker(workerOptions) { + let Worker; + + if (this._options.enableWorkerThreads && canUseWorkerThreads()) { + Worker = require('./workers/NodeThreadsWorker').default; + } else { + Worker = require('./workers/ChildProcessWorker').default; + } + + return new Worker(workerOptions); + } +} + +var _default = WorkerPool; +exports.default = _default; diff --git a/packages/jest-worker/build/__performance_tests__/test.js b/packages/jest-worker/build/__performance_tests__/test.js new file mode 100644 index 000000000000..fd8b87679960 --- /dev/null +++ b/packages/jest-worker/build/__performance_tests__/test.js @@ -0,0 +1,180 @@ +/** + * 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. + */ +'use strict'; + +const assert = require('assert'); + +const {performance} = require('perf_hooks'); // eslint-disable-next-line import/no-extraneous-dependencies + +const workerFarm = require('worker-farm'); + +const JestWorker = require('../../build').Worker; // Typical tests: node --expose-gc test.js empty 100000 +// node --expose-gc test.js loadTest 10000 + +assert(process.argv[2], 'Pass a child method name'); +assert(process.argv[3], 'Pass the number of iterations'); + +const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); + +const method = process.argv[2]; +const calls = +process.argv[3]; +const threads = 6; +const iterations = 10; + +function testWorkerFarm() { + return new Promise(async resolve => { + const startTime = performance.now(); + let count = 0; + + async function countToFinish() { + if (++count === calls) { + workerFarm.end(api); + const endTime = performance.now(); // Let all workers go down. + + await sleep(2000); + resolve({ + globalTime: endTime - startTime - 2000, + processingTime: endTime - startProcess + }); + } + } + + const api = workerFarm( + { + autoStart: true, + maxConcurrentCallsPerWorker: 1, + maxConcurrentWorkers: threads + }, + require.resolve('./workers/worker_farm'), + [method] + ); // Let all workers come up. + + await sleep(2000); + const startProcess = performance.now(); + + for (let i = 0; i < calls; i++) { + const promisified = new Promise((resolve, reject) => { + api[method]((err, result) => { + if (err) { + reject(err); + } else { + resolve(result); + } + }); + }); + promisified.then(countToFinish); + } + }); +} + +function testJestWorker() { + return new Promise(async resolve => { + const startTime = performance.now(); + let count = 0; + + async function countToFinish() { + if (++count === calls) { + farm.end(); + const endTime = performance.now(); // Let all workers go down. + + await sleep(2000); + resolve({ + globalTime: endTime - startTime - 2000, + processingTime: endTime - startProcess + }); + } + } + + const farm = new JestWorker(require.resolve('./workers/jest_worker'), { + exposedMethods: [method], + forkOptions: { + execArgv: [] + }, + numWorkers: threads + }); + farm.getStdout().pipe(process.stdout); + farm.getStderr().pipe(process.stderr); // Let all workers come up. + + await sleep(2000); + const startProcess = performance.now(); + + for (let i = 0; i < calls; i++) { + const promisified = farm[method](); + promisified.then(countToFinish); + } + }); +} + +function profile(x) { + console.profile(x); +} + +function profileEnd(x) { + console.profileEnd(x); +} + +async function main() { + if (!global.gc) { + console.warn('GC not present, start with node --expose-gc'); + } + + const wFResults = []; + const jWResults = []; + + for (let i = 0; i < iterations; i++) { + console.log('-'.repeat(75)); + profile('worker farm'); + const wF = await testWorkerFarm(); + profileEnd('worker farm'); + await sleep(3000); // eslint-disable-next-line no-undef + + global.gc && gc(); + profile('jest worker'); + const jW = await testJestWorker(); + profileEnd('jest worker'); + await sleep(3000); // eslint-disable-next-line no-undef + + global.gc && gc(); + wFResults.push(wF); + jWResults.push(jW); + console.log('jest-worker:', jW); + console.log('worker-farm:', wF); + } + + let wFGT = 0; + let wFPT = 0; + let jWGT = 0; + let jWPT = 0; + + for (let i = 0; i < iterations; i++) { + wFGT += wFResults[i].globalTime; + wFPT += wFResults[i].processingTime; + jWGT += jWResults[i].globalTime; + jWPT += jWResults[i].processingTime; + } + + console.log('-'.repeat(75)); + console.log('total worker-farm:', { + wFGT, + wFPT + }); + console.log('total jest-worker:', { + jWGT, + jWPT + }); + console.log('-'.repeat(75)); + console.log( + `% improvement over ${calls} calls (global time):`, + (100 * (wFGT - jWGT)) / wFGT + ); + console.log( + `% improvement over ${calls} calls (processing time):`, + (100 * (wFPT - jWPT)) / wFPT + ); +} + +main(); diff --git a/packages/jest-worker/build/__performance_tests__/workers/jest_worker.js b/packages/jest-worker/build/__performance_tests__/workers/jest_worker.js new file mode 100644 index 000000000000..2239816f26fa --- /dev/null +++ b/packages/jest-worker/build/__performance_tests__/workers/jest_worker.js @@ -0,0 +1,17 @@ +/** + * 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. + */ +'use strict'; + +const pi = require('./pi'); + +module.exports.loadTest = function () { + return pi(); +}; + +module.exports.empty = function () { + // Do nothing. +}; diff --git a/packages/jest-worker/build/__performance_tests__/workers/pi.js b/packages/jest-worker/build/__performance_tests__/workers/pi.js new file mode 100644 index 000000000000..60febff06ddb --- /dev/null +++ b/packages/jest-worker/build/__performance_tests__/workers/pi.js @@ -0,0 +1,20 @@ +/** + * 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. + */ +'use strict'; + +module.exports = function () { + const points = 10000; + let inside = 0; + + for (let i = 0; i < points; i++) { + if (Math.pow(Math.random(), 2) + Math.pow(Math.random(), 2) <= 1) { + inside++; + } + } + + return (4 * inside) / points; +}; diff --git a/packages/jest-worker/build/__performance_tests__/workers/worker_farm.js b/packages/jest-worker/build/__performance_tests__/workers/worker_farm.js new file mode 100644 index 000000000000..a9ea50877295 --- /dev/null +++ b/packages/jest-worker/build/__performance_tests__/workers/worker_farm.js @@ -0,0 +1,18 @@ +/** + * 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. + */ +'use strict'; + +const pi = require('./pi'); + +module.exports.loadTest = function (callback) { + callback(null, pi()); +}; + +module.exports.empty = function (callback) { + // Do nothing. + callback(); +}; diff --git a/packages/jest-worker/build/base/BaseWorkerPool.d.ts b/packages/jest-worker/build/base/BaseWorkerPool.d.ts new file mode 100644 index 000000000000..311309e6c746 --- /dev/null +++ b/packages/jest-worker/build/base/BaseWorkerPool.d.ts @@ -0,0 +1,21 @@ +/** + * 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. + */ +/// +import { PoolExitResult, WorkerInterface, WorkerOptions, WorkerPoolOptions } from '../types'; +export default class BaseWorkerPool { + private readonly _stderr; + private readonly _stdout; + protected readonly _options: WorkerPoolOptions; + private readonly _workers; + constructor(workerPath: string, options: WorkerPoolOptions); + getStderr(): NodeJS.ReadableStream; + getStdout(): NodeJS.ReadableStream; + getWorkers(): Array; + getWorkerById(workerId: number): WorkerInterface; + createWorker(_workerOptions: WorkerOptions): WorkerInterface; + end(): Promise; +} diff --git a/packages/jest-worker/build/base/BaseWorkerPool.js b/packages/jest-worker/build/base/BaseWorkerPool.js new file mode 100644 index 000000000000..022e3a0997db --- /dev/null +++ b/packages/jest-worker/build/base/BaseWorkerPool.js @@ -0,0 +1,209 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _mergeStream() { + const data = _interopRequireDefault(require('merge-stream')); + + _mergeStream = function () { + return data; + }; + + return data; +} + +function _types() { + const data = require('../types'); + + _types = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +// How long to wait for the child process to terminate +// after CHILD_MESSAGE_END before sending force exiting. +const FORCE_EXIT_DELAY = 500; +/* istanbul ignore next */ + +const emptyMethod = () => {}; + +class BaseWorkerPool { + constructor(workerPath, options) { + _defineProperty(this, '_stderr', void 0); + + _defineProperty(this, '_stdout', void 0); + + _defineProperty(this, '_options', void 0); + + _defineProperty(this, '_workers', void 0); + + this._options = options; + this._workers = new Array(options.numWorkers); + + if (!path().isAbsolute(workerPath)) { + workerPath = require.resolve(workerPath); + } + + const stdout = (0, _mergeStream().default)(); + const stderr = (0, _mergeStream().default)(); + const {forkOptions, maxRetries, resourceLimits, setupArgs} = options; + + for (let i = 0; i < options.numWorkers; i++) { + const workerOptions = { + forkOptions, + maxRetries, + resourceLimits, + setupArgs, + workerId: i, + workerPath + }; + const worker = this.createWorker(workerOptions); + const workerStdout = worker.getStdout(); + const workerStderr = worker.getStderr(); + + if (workerStdout) { + stdout.add(workerStdout); + } + + if (workerStderr) { + stderr.add(workerStderr); + } + + this._workers[i] = worker; + } + + this._stdout = stdout; + this._stderr = stderr; + } + + getStderr() { + return this._stderr; + } + + getStdout() { + return this._stdout; + } + + getWorkers() { + return this._workers; + } + + getWorkerById(workerId) { + return this._workers[workerId]; + } + + createWorker(_workerOptions) { + throw Error('Missing method createWorker in WorkerPool'); + } + + async end() { + // We do not cache the request object here. If so, it would only be only + // processed by one of the workers, and we want them all to close. + const workerExitPromises = this._workers.map(async worker => { + worker.send( + [_types().CHILD_MESSAGE_END, false], + emptyMethod, + emptyMethod, + emptyMethod + ); // Schedule a force exit in case worker fails to exit gracefully so + // await worker.waitForExit() never takes longer than FORCE_EXIT_DELAY + + let forceExited = false; + const forceExitTimeout = setTimeout(() => { + worker.forceExit(); + forceExited = true; + }, FORCE_EXIT_DELAY); + await worker.waitForExit(); // Worker ideally exited gracefully, don't send force exit then + + clearTimeout(forceExitTimeout); + return forceExited; + }); + + const workerExits = await Promise.all(workerExitPromises); + return workerExits.reduce( + (result, forceExited) => ({ + forceExited: result.forceExited || forceExited + }), + { + forceExited: false + } + ); + } +} + +exports.default = BaseWorkerPool; diff --git a/packages/jest-worker/build/index.d.ts b/packages/jest-worker/build/index.d.ts new file mode 100644 index 000000000000..5908dbbddac4 --- /dev/null +++ b/packages/jest-worker/build/index.d.ts @@ -0,0 +1,49 @@ +/** + * 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. + */ +/// +import type { FarmOptions, PoolExitResult, PromiseWithCustomMessage, TaskQueue } from './types'; +export { default as PriorityQueue } from './PriorityQueue'; +export { default as FifoQueue } from './FifoQueue'; +export { default as messageParent } from './workers/messageParent'; +/** + * The Jest farm (publicly called "Worker") is a class that allows you to queue + * methods across multiple child processes, in order to parallelize work. This + * is done by providing an absolute path to a module that will be loaded on each + * of the child processes, and bridged to the main process. + * + * Bridged methods are specified by using the "exposedMethods" property of the + * "options" object. This is an array of strings, where each of them corresponds + * to the exported name in the loaded module. + * + * You can also control the amount of workers by using the "numWorkers" property + * of the "options" object, and the settings passed to fork the process through + * the "forkOptions" property. The amount of workers defaults to the amount of + * CPUS minus one. + * + * Queueing calls can be done in two ways: + * - Standard method: calls will be redirected to the first available worker, + * so they will get executed as soon as they can. + * + * - Sticky method: if a "computeWorkerKey" method is provided within the + * config, the resulting string of this method will be used as a key. + * Every time this key is returned, it is guaranteed that your job will be + * processed by the same worker. This is specially useful if your workers + * are caching results. + */ +export declare class Worker { + private _ending; + private _farm; + private _options; + private _workerPool; + constructor(workerPath: string, options?: FarmOptions); + private _bindExposedWorkerMethods; + private _callFunctionWithArgs; + getStderr(): NodeJS.ReadableStream; + getStdout(): NodeJS.ReadableStream; + end(): Promise; +} +export type { PromiseWithCustomMessage, TaskQueue }; diff --git a/packages/jest-worker/build/index.js b/packages/jest-worker/build/index.js new file mode 100644 index 000000000000..ac263d85930a --- /dev/null +++ b/packages/jest-worker/build/index.js @@ -0,0 +1,223 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'PriorityQueue', { + enumerable: true, + get: function () { + return _PriorityQueue.default; + } +}); +Object.defineProperty(exports, 'FifoQueue', { + enumerable: true, + get: function () { + return _FifoQueue.default; + } +}); +Object.defineProperty(exports, 'messageParent', { + enumerable: true, + get: function () { + return _messageParent.default; + } +}); +exports.Worker = void 0; + +function _os() { + const data = require('os'); + + _os = function () { + return data; + }; + + return data; +} + +var _Farm = _interopRequireDefault(require('./Farm')); + +var _WorkerPool = _interopRequireDefault(require('./WorkerPool')); + +var _PriorityQueue = _interopRequireDefault(require('./PriorityQueue')); + +var _FifoQueue = _interopRequireDefault(require('./FifoQueue')); + +var _messageParent = _interopRequireDefault(require('./workers/messageParent')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +function getExposedMethods(workerPath, options) { + let exposedMethods = options.exposedMethods; // If no methods list is given, try getting it by auto-requiring the module. + + if (!exposedMethods) { + const module = require(workerPath); + + exposedMethods = Object.keys(module).filter( + // @ts-expect-error: no index + name => typeof module[name] === 'function' + ); + + if (typeof module === 'function') { + exposedMethods = [...exposedMethods, 'default']; + } + } + + return exposedMethods; +} +/** + * The Jest farm (publicly called "Worker") is a class that allows you to queue + * methods across multiple child processes, in order to parallelize work. This + * is done by providing an absolute path to a module that will be loaded on each + * of the child processes, and bridged to the main process. + * + * Bridged methods are specified by using the "exposedMethods" property of the + * "options" object. This is an array of strings, where each of them corresponds + * to the exported name in the loaded module. + * + * You can also control the amount of workers by using the "numWorkers" property + * of the "options" object, and the settings passed to fork the process through + * the "forkOptions" property. The amount of workers defaults to the amount of + * CPUS minus one. + * + * Queueing calls can be done in two ways: + * - Standard method: calls will be redirected to the first available worker, + * so they will get executed as soon as they can. + * + * - Sticky method: if a "computeWorkerKey" method is provided within the + * config, the resulting string of this method will be used as a key. + * Every time this key is returned, it is guaranteed that your job will be + * processed by the same worker. This is specially useful if your workers + * are caching results. + */ + +class Worker { + constructor(workerPath, options) { + var _this$_options$enable, + _this$_options$forkOp, + _this$_options$maxRet, + _this$_options$numWor, + _this$_options$resour, + _this$_options$setupA; + + _defineProperty(this, '_ending', void 0); + + _defineProperty(this, '_farm', void 0); + + _defineProperty(this, '_options', void 0); + + _defineProperty(this, '_workerPool', void 0); + + this._options = {...options}; + this._ending = false; + const workerPoolOptions = { + enableWorkerThreads: + (_this$_options$enable = this._options.enableWorkerThreads) !== null && + _this$_options$enable !== void 0 + ? _this$_options$enable + : false, + forkOptions: + (_this$_options$forkOp = this._options.forkOptions) !== null && + _this$_options$forkOp !== void 0 + ? _this$_options$forkOp + : {}, + maxRetries: + (_this$_options$maxRet = this._options.maxRetries) !== null && + _this$_options$maxRet !== void 0 + ? _this$_options$maxRet + : 3, + numWorkers: + (_this$_options$numWor = this._options.numWorkers) !== null && + _this$_options$numWor !== void 0 + ? _this$_options$numWor + : Math.max((0, _os().cpus)().length - 1, 1), + resourceLimits: + (_this$_options$resour = this._options.resourceLimits) !== null && + _this$_options$resour !== void 0 + ? _this$_options$resour + : {}, + setupArgs: + (_this$_options$setupA = this._options.setupArgs) !== null && + _this$_options$setupA !== void 0 + ? _this$_options$setupA + : [] + }; + + if (this._options.WorkerPool) { + // @ts-expect-error: constructor target any? + this._workerPool = new this._options.WorkerPool( + workerPath, + workerPoolOptions + ); + } else { + this._workerPool = new _WorkerPool.default(workerPath, workerPoolOptions); + } + + this._farm = new _Farm.default( + workerPoolOptions.numWorkers, + this._workerPool.send.bind(this._workerPool), + { + computeWorkerKey: this._options.computeWorkerKey, + taskQueue: this._options.taskQueue, + workerSchedulingPolicy: this._options.workerSchedulingPolicy + } + ); + + this._bindExposedWorkerMethods(workerPath, this._options); + } + + _bindExposedWorkerMethods(workerPath, options) { + getExposedMethods(workerPath, options).forEach(name => { + if (name.startsWith('_')) { + return; + } + + if (this.constructor.prototype.hasOwnProperty(name)) { + throw new TypeError('Cannot define a method called ' + name); + } // @ts-expect-error: dynamic extension of the class instance is expected. + + this[name] = this._callFunctionWithArgs.bind(this, name); + }); + } + + _callFunctionWithArgs(method, ...args) { + if (this._ending) { + throw new Error('Farm is ended, no more calls can be done to it'); + } + + return this._farm.doWork(method, ...args); + } + + getStderr() { + return this._workerPool.getStderr(); + } + + getStdout() { + return this._workerPool.getStdout(); + } + + async end() { + if (this._ending) { + throw new Error('Farm is ended, no more calls can be done to it'); + } + + this._ending = true; + return this._workerPool.end(); + } +} + +exports.Worker = Worker; diff --git a/packages/jest-worker/build/types.d.ts b/packages/jest-worker/build/types.d.ts new file mode 100644 index 000000000000..613864354fc1 --- /dev/null +++ b/packages/jest-worker/build/types.d.ts @@ -0,0 +1,141 @@ +/** + * 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. + */ +/// +import type { ForkOptions } from 'child_process'; +import type { EventEmitter } from 'events'; +export interface ResourceLimits { + maxYoungGenerationSizeMb?: number; + maxOldGenerationSizeMb?: number; + codeRangeSizeMb?: number; +} +export declare const CHILD_MESSAGE_INITIALIZE: 0; +export declare const CHILD_MESSAGE_CALL: 1; +export declare const CHILD_MESSAGE_END: 2; +export declare const PARENT_MESSAGE_OK: 0; +export declare const PARENT_MESSAGE_CLIENT_ERROR: 1; +export declare const PARENT_MESSAGE_SETUP_ERROR: 2; +export declare const PARENT_MESSAGE_CUSTOM: 3; +export declare type PARENT_MESSAGE_ERROR = typeof PARENT_MESSAGE_CLIENT_ERROR | typeof PARENT_MESSAGE_SETUP_ERROR; +export interface WorkerPoolInterface { + getStderr(): NodeJS.ReadableStream; + getStdout(): NodeJS.ReadableStream; + getWorkers(): Array; + createWorker(options: WorkerOptions): WorkerInterface; + send(workerId: number, request: ChildMessage, onStart: OnStart, onEnd: OnEnd, onCustomMessage: OnCustomMessage): void; + end(): Promise; +} +export interface WorkerInterface { + send(request: ChildMessage, onProcessStart: OnStart, onProcessEnd: OnEnd, onCustomMessage: OnCustomMessage): void; + waitForExit(): Promise; + forceExit(): void; + getWorkerId(): number; + getStderr(): NodeJS.ReadableStream | null; + getStdout(): NodeJS.ReadableStream | null; +} +export declare type PoolExitResult = { + forceExited: boolean; +}; +export interface PromiseWithCustomMessage extends Promise { + UNSTABLE_onCustomMessage?: (listener: OnCustomMessage) => () => void; +} +export type { ForkOptions }; +export interface TaskQueue { + /** + * Enqueues the task in the queue for the specified worker or adds it to the + * queue shared by all workers + * @param task the task to queue + * @param workerId the id of the worker that should process this task or undefined + * if there's no preference. + */ + enqueue(task: QueueChildMessage, workerId?: number): void; + /** + * Dequeues the next item from the queue for the speified worker + * @param workerId the id of the worker for which the next task should be retrieved + */ + dequeue(workerId: number): QueueChildMessage | null; +} +export declare type FarmOptions = { + computeWorkerKey?: (method: string, ...args: Array) => string | null; + exposedMethods?: ReadonlyArray; + forkOptions?: ForkOptions; + workerSchedulingPolicy?: 'round-robin' | 'in-order'; + resourceLimits?: ResourceLimits; + setupArgs?: Array; + maxRetries?: number; + numWorkers?: number; + taskQueue?: TaskQueue; + WorkerPool?: (workerPath: string, options?: WorkerPoolOptions) => WorkerPoolInterface; + enableWorkerThreads?: boolean; +}; +export declare type WorkerPoolOptions = { + setupArgs: Array; + forkOptions: ForkOptions; + resourceLimits: ResourceLimits; + maxRetries: number; + numWorkers: number; + enableWorkerThreads: boolean; +}; +export declare type WorkerOptions = { + forkOptions: ForkOptions; + resourceLimits: ResourceLimits; + setupArgs: Array; + maxRetries: number; + workerId: number; + workerPath: string; +}; +export declare type MessagePort = typeof EventEmitter & { + postMessage(message: unknown): void; +}; +export declare type MessageChannel = { + port1: MessagePort; + port2: MessagePort; +}; +export declare type ChildMessageInitialize = [ + typeof CHILD_MESSAGE_INITIALIZE, + boolean, + string, + // file + Array | undefined, + // setupArgs + MessagePort | undefined +]; +export declare type ChildMessageCall = [ + typeof CHILD_MESSAGE_CALL, + boolean, + string, + Array +]; +export declare type ChildMessageEnd = [ + typeof CHILD_MESSAGE_END, + boolean +]; +export declare type ChildMessage = ChildMessageInitialize | ChildMessageCall | ChildMessageEnd; +export declare type ParentMessageCustom = [ + typeof PARENT_MESSAGE_CUSTOM, + unknown +]; +export declare type ParentMessageOk = [ + typeof PARENT_MESSAGE_OK, + unknown +]; +export declare type ParentMessageError = [ + PARENT_MESSAGE_ERROR, + string, + string, + string, + unknown +]; +export declare type ParentMessage = ParentMessageOk | ParentMessageError | ParentMessageCustom; +export declare type OnStart = (worker: WorkerInterface) => void; +export declare type OnEnd = (err: Error | null, result: unknown) => void; +export declare type OnCustomMessage = (message: Array | unknown) => void; +export declare type QueueChildMessage = { + request: ChildMessageCall; + onStart: OnStart; + onEnd: OnEnd; + onCustomMessage: OnCustomMessage; +}; diff --git a/packages/jest-worker/build/types.js b/packages/jest-worker/build/types.js new file mode 100644 index 000000000000..56d03de0c625 --- /dev/null +++ b/packages/jest-worker/build/types.js @@ -0,0 +1,32 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.PARENT_MESSAGE_CUSTOM = exports.PARENT_MESSAGE_SETUP_ERROR = exports.PARENT_MESSAGE_CLIENT_ERROR = exports.PARENT_MESSAGE_OK = exports.CHILD_MESSAGE_END = exports.CHILD_MESSAGE_CALL = exports.CHILD_MESSAGE_INITIALIZE = void 0; + +/** + * 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. + */ +// import type {ResourceLimits} from 'worker_threads'; +// This is not present in the Node 12 typings +// Because of the dynamic nature of a worker communication process, all messages +// coming from any of the other processes cannot be typed. Thus, many types +// include "unknown" as a TS type, which is (unfortunately) correct here. +const CHILD_MESSAGE_INITIALIZE = 0; +exports.CHILD_MESSAGE_INITIALIZE = CHILD_MESSAGE_INITIALIZE; +const CHILD_MESSAGE_CALL = 1; +exports.CHILD_MESSAGE_CALL = CHILD_MESSAGE_CALL; +const CHILD_MESSAGE_END = 2; +exports.CHILD_MESSAGE_END = CHILD_MESSAGE_END; +const PARENT_MESSAGE_OK = 0; +exports.PARENT_MESSAGE_OK = PARENT_MESSAGE_OK; +const PARENT_MESSAGE_CLIENT_ERROR = 1; +exports.PARENT_MESSAGE_CLIENT_ERROR = PARENT_MESSAGE_CLIENT_ERROR; +const PARENT_MESSAGE_SETUP_ERROR = 2; +exports.PARENT_MESSAGE_SETUP_ERROR = PARENT_MESSAGE_SETUP_ERROR; +const PARENT_MESSAGE_CUSTOM = 3; +exports.PARENT_MESSAGE_CUSTOM = PARENT_MESSAGE_CUSTOM; diff --git a/packages/jest-worker/build/workers/ChildProcessWorker.d.ts b/packages/jest-worker/build/workers/ChildProcessWorker.d.ts new file mode 100644 index 000000000000..4a8dcf12bb9e --- /dev/null +++ b/packages/jest-worker/build/workers/ChildProcessWorker.d.ts @@ -0,0 +1,51 @@ +/** + * 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. + */ +/// +import { ChildMessage, OnCustomMessage, OnEnd, OnStart, WorkerInterface, WorkerOptions } from '../types'; +/** + * This class wraps the child process and provides a nice interface to + * communicate with. It takes care of: + * + * - Re-spawning the process if it dies. + * - Queues calls while the worker is busy. + * - Re-sends the requests if the worker blew up. + * + * The reason for queueing them here (since childProcess.send also has an + * internal queue) is because the worker could be doing asynchronous work, and + * this would lead to the child process to read its receiving buffer and start a + * second call. By queueing calls here, we don't send the next call to the + * children until we receive the result of the previous one. + * + * As soon as a request starts to be processed by a worker, its "processed" + * field is changed to "true", so that other workers which might encounter the + * same call skip it. + */ +export default class ChildProcessWorker implements WorkerInterface { + private _child; + private _options; + private _request; + private _retries; + private _onProcessEnd; + private _onCustomMessage; + private _fakeStream; + private _stdout; + private _stderr; + private _exitPromise; + private _resolveExitPromise; + constructor(options: WorkerOptions); + initialize(): void; + private _shutdown; + private _onMessage; + private _onExit; + send(request: ChildMessage, onProcessStart: OnStart, onProcessEnd: OnEnd, onCustomMessage: OnCustomMessage): void; + waitForExit(): Promise; + forceExit(): void; + getWorkerId(): number; + getStdout(): NodeJS.ReadableStream | null; + getStderr(): NodeJS.ReadableStream | null; + private _getFakeStream; +} diff --git a/packages/jest-worker/build/workers/ChildProcessWorker.js b/packages/jest-worker/build/workers/ChildProcessWorker.js new file mode 100644 index 000000000000..46e244374c04 --- /dev/null +++ b/packages/jest-worker/build/workers/ChildProcessWorker.js @@ -0,0 +1,338 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function _child_process() { + const data = require('child_process'); + + _child_process = function () { + return data; + }; + + return data; +} + +function _stream() { + const data = require('stream'); + + _stream = function () { + return data; + }; + + return data; +} + +function _mergeStream() { + const data = _interopRequireDefault(require('merge-stream')); + + _mergeStream = function () { + return data; + }; + + return data; +} + +function _supportsColor() { + const data = require('supports-color'); + + _supportsColor = function () { + return data; + }; + + return data; +} + +function _types() { + const data = require('../types'); + + _types = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +const SIGNAL_BASE_EXIT_CODE = 128; +const SIGKILL_EXIT_CODE = SIGNAL_BASE_EXIT_CODE + 9; +const SIGTERM_EXIT_CODE = SIGNAL_BASE_EXIT_CODE + 15; // How long to wait after SIGTERM before sending SIGKILL + +const SIGKILL_DELAY = 500; +/** + * This class wraps the child process and provides a nice interface to + * communicate with. It takes care of: + * + * - Re-spawning the process if it dies. + * - Queues calls while the worker is busy. + * - Re-sends the requests if the worker blew up. + * + * The reason for queueing them here (since childProcess.send also has an + * internal queue) is because the worker could be doing asynchronous work, and + * this would lead to the child process to read its receiving buffer and start a + * second call. By queueing calls here, we don't send the next call to the + * children until we receive the result of the previous one. + * + * As soon as a request starts to be processed by a worker, its "processed" + * field is changed to "true", so that other workers which might encounter the + * same call skip it. + */ + +class ChildProcessWorker { + constructor(options) { + _defineProperty(this, '_child', void 0); + + _defineProperty(this, '_options', void 0); + + _defineProperty(this, '_request', void 0); + + _defineProperty(this, '_retries', void 0); + + _defineProperty(this, '_onProcessEnd', void 0); + + _defineProperty(this, '_onCustomMessage', void 0); + + _defineProperty(this, '_fakeStream', void 0); + + _defineProperty(this, '_stdout', void 0); + + _defineProperty(this, '_stderr', void 0); + + _defineProperty(this, '_exitPromise', void 0); + + _defineProperty(this, '_resolveExitPromise', void 0); + + this._options = options; + this._request = null; + this._fakeStream = null; + this._stdout = null; + this._stderr = null; + this._exitPromise = new Promise(resolve => { + this._resolveExitPromise = resolve; + }); + this.initialize(); + } + + initialize() { + const forceColor = _supportsColor().stdout + ? { + FORCE_COLOR: '1' + } + : {}; + const child = (0, _child_process().fork)( + require.resolve('./processChild'), + [], + { + cwd: process.cwd(), + env: { + ...process.env, + JEST_WORKER_ID: String(this._options.workerId + 1), + // 0-indexed workerId, 1-indexed JEST_WORKER_ID + ...forceColor + }, + // Suppress --debug / --inspect flags while preserving others (like --harmony). + execArgv: process.execArgv.filter(v => !/^--(debug|inspect)/.test(v)), + silent: true, + ...this._options.forkOptions + } + ); + + if (child.stdout) { + if (!this._stdout) { + // We need to add a permanent stream to the merged stream to prevent it + // from ending when the subprocess stream ends + this._stdout = (0, _mergeStream().default)(this._getFakeStream()); + } + + this._stdout.add(child.stdout); + } + + if (child.stderr) { + if (!this._stderr) { + // We need to add a permanent stream to the merged stream to prevent it + // from ending when the subprocess stream ends + this._stderr = (0, _mergeStream().default)(this._getFakeStream()); + } + + this._stderr.add(child.stderr); + } + + child.on('message', this._onMessage.bind(this)); + child.on('exit', this._onExit.bind(this)); + child.send([ + _types().CHILD_MESSAGE_INITIALIZE, + false, + this._options.workerPath, + this._options.setupArgs + ]); + this._child = child; + this._retries++; // If we exceeded the amount of retries, we will emulate an error reply + // coming from the child. This avoids code duplication related with cleaning + // the queue, and scheduling the next call. + + if (this._retries > this._options.maxRetries) { + const error = new Error('Call retries were exceeded'); + + this._onMessage([ + _types().PARENT_MESSAGE_CLIENT_ERROR, + error.name, + error.message, + error.stack, + { + type: 'WorkerError' + } + ]); + } + } + + _shutdown() { + // End the temporary streams so the merged streams end too + if (this._fakeStream) { + this._fakeStream.end(); + + this._fakeStream = null; + } + + this._resolveExitPromise(); + } + + _onMessage(response) { + // TODO: Add appropriate type check + let error; + + switch (response[0]) { + case _types().PARENT_MESSAGE_OK: + this._onProcessEnd(null, response[1]); + + break; + + case _types().PARENT_MESSAGE_CLIENT_ERROR: + error = response[4]; + + if (error != null && typeof error === 'object') { + const extra = error; // @ts-expect-error: no index + + const NativeCtor = global[response[1]]; + const Ctor = typeof NativeCtor === 'function' ? NativeCtor : Error; + error = new Ctor(response[2]); + error.type = response[1]; + error.stack = response[3]; + + for (const key in extra) { + error[key] = extra[key]; + } + } + + this._onProcessEnd(error, null); + + break; + + case _types().PARENT_MESSAGE_SETUP_ERROR: + error = new Error('Error when calling setup: ' + response[2]); + error.type = response[1]; + error.stack = response[3]; + + this._onProcessEnd(error, null); + + break; + + case _types().PARENT_MESSAGE_CUSTOM: + this._onCustomMessage(response[1]); + + break; + + default: + throw new TypeError('Unexpected response from worker: ' + response[0]); + } + } + + _onExit(exitCode) { + if ( + exitCode !== 0 && + exitCode !== SIGTERM_EXIT_CODE && + exitCode !== SIGKILL_EXIT_CODE + ) { + this.initialize(); + + if (this._request) { + this._child.send(this._request); + } + } else { + this._shutdown(); + } + } + + send(request, onProcessStart, onProcessEnd, onCustomMessage) { + onProcessStart(this); + + this._onProcessEnd = (...args) => { + // Clean the request to avoid sending past requests to workers that fail + // while waiting for a new request (timers, unhandled rejections...) + this._request = null; + return onProcessEnd(...args); + }; + + this._onCustomMessage = (...arg) => onCustomMessage(...arg); + + this._request = request; + this._retries = 0; + + this._child.send(request); + } + + waitForExit() { + return this._exitPromise; + } + + forceExit() { + this._child.kill('SIGTERM'); + + const sigkillTimeout = setTimeout( + () => this._child.kill('SIGKILL'), + SIGKILL_DELAY + ); + + this._exitPromise.then(() => clearTimeout(sigkillTimeout)); + } + + getWorkerId() { + return this._options.workerId; + } + + getStdout() { + return this._stdout; + } + + getStderr() { + return this._stderr; + } + + _getFakeStream() { + if (!this._fakeStream) { + this._fakeStream = new (_stream().PassThrough)(); + } + + return this._fakeStream; + } +} + +exports.default = ChildProcessWorker; diff --git a/packages/jest-worker/build/workers/NodeThreadsWorker.d.ts b/packages/jest-worker/build/workers/NodeThreadsWorker.d.ts new file mode 100644 index 000000000000..2555942e07ea --- /dev/null +++ b/packages/jest-worker/build/workers/NodeThreadsWorker.d.ts @@ -0,0 +1,34 @@ +/** + * 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. + */ +/// +import { ChildMessage, OnCustomMessage, OnEnd, OnStart, WorkerInterface, WorkerOptions } from '../types'; +export default class ExperimentalWorker implements WorkerInterface { + private _worker; + private _options; + private _request; + private _retries; + private _onProcessEnd; + private _onCustomMessage; + private _fakeStream; + private _stdout; + private _stderr; + private _exitPromise; + private _resolveExitPromise; + private _forceExited; + constructor(options: WorkerOptions); + initialize(): void; + private _shutdown; + private _onMessage; + private _onExit; + waitForExit(): Promise; + forceExit(): void; + send(request: ChildMessage, onProcessStart: OnStart, onProcessEnd: OnEnd, onCustomMessage: OnCustomMessage): void; + getWorkerId(): number; + getStdout(): NodeJS.ReadableStream | null; + getStderr(): NodeJS.ReadableStream | null; + private _getFakeStream; +} diff --git a/packages/jest-worker/build/workers/NodeThreadsWorker.js b/packages/jest-worker/build/workers/NodeThreadsWorker.js new file mode 100644 index 000000000000..ad32d0b16091 --- /dev/null +++ b/packages/jest-worker/build/workers/NodeThreadsWorker.js @@ -0,0 +1,352 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = void 0; + +function path() { + const data = _interopRequireWildcard(require('path')); + + path = function () { + return data; + }; + + return data; +} + +function _stream() { + const data = require('stream'); + + _stream = function () { + return data; + }; + + return data; +} + +function _worker_threads() { + const data = require('worker_threads'); + + _worker_threads = function () { + return data; + }; + + return data; +} + +function _mergeStream() { + const data = _interopRequireDefault(require('merge-stream')); + + _mergeStream = function () { + return data; + }; + + return data; +} + +function _types() { + const data = require('../types'); + + _types = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} + +class ExperimentalWorker { + constructor(options) { + _defineProperty(this, '_worker', void 0); + + _defineProperty(this, '_options', void 0); + + _defineProperty(this, '_request', void 0); + + _defineProperty(this, '_retries', void 0); + + _defineProperty(this, '_onProcessEnd', void 0); + + _defineProperty(this, '_onCustomMessage', void 0); + + _defineProperty(this, '_fakeStream', void 0); + + _defineProperty(this, '_stdout', void 0); + + _defineProperty(this, '_stderr', void 0); + + _defineProperty(this, '_exitPromise', void 0); + + _defineProperty(this, '_resolveExitPromise', void 0); + + _defineProperty(this, '_forceExited', void 0); + + this._options = options; + this._request = null; + this._fakeStream = null; + this._stdout = null; + this._stderr = null; + this._exitPromise = new Promise(resolve => { + this._resolveExitPromise = resolve; + }); + this._forceExited = false; + this.initialize(); + } + + initialize() { + this._worker = new (_worker_threads().Worker)( + path().resolve(__dirname, './threadChild.js'), + { + eval: false, + // @ts-expect-error: added in newer versions + resourceLimits: this._options.resourceLimits, + stderr: true, + stdout: true, + workerData: { + cwd: process.cwd(), + env: { + ...process.env, + JEST_WORKER_ID: String(this._options.workerId + 1) // 0-indexed workerId, 1-indexed JEST_WORKER_ID + }, + // Suppress --debug / --inspect flags while preserving others (like --harmony). + execArgv: process.execArgv.filter(v => !/^--(debug|inspect)/.test(v)), + silent: true, + ...this._options.forkOptions + } + } + ); + + if (this._worker.stdout) { + if (!this._stdout) { + // We need to add a permanent stream to the merged stream to prevent it + // from ending when the subprocess stream ends + this._stdout = (0, _mergeStream().default)(this._getFakeStream()); + } + + this._stdout.add(this._worker.stdout); + } + + if (this._worker.stderr) { + if (!this._stderr) { + // We need to add a permanent stream to the merged stream to prevent it + // from ending when the subprocess stream ends + this._stderr = (0, _mergeStream().default)(this._getFakeStream()); + } + + this._stderr.add(this._worker.stderr); + } + + this._worker.on('message', this._onMessage.bind(this)); + + this._worker.on('exit', this._onExit.bind(this)); + + this._worker.postMessage([ + _types().CHILD_MESSAGE_INITIALIZE, + false, + this._options.workerPath, + this._options.setupArgs + ]); + + this._retries++; // If we exceeded the amount of retries, we will emulate an error reply + // coming from the child. This avoids code duplication related with cleaning + // the queue, and scheduling the next call. + + if (this._retries > this._options.maxRetries) { + const error = new Error('Call retries were exceeded'); + + this._onMessage([ + _types().PARENT_MESSAGE_CLIENT_ERROR, + error.name, + error.message, + error.stack, + { + type: 'WorkerError' + } + ]); + } + } + + _shutdown() { + // End the permanent stream so the merged stream end too + if (this._fakeStream) { + this._fakeStream.end(); + + this._fakeStream = null; + } + + this._resolveExitPromise(); + } + + _onMessage(response) { + let error; + + switch (response[0]) { + case _types().PARENT_MESSAGE_OK: + this._onProcessEnd(null, response[1]); + + break; + + case _types().PARENT_MESSAGE_CLIENT_ERROR: + error = response[4]; + + if (error != null && typeof error === 'object') { + const extra = error; // @ts-expect-error: no index + + const NativeCtor = global[response[1]]; + const Ctor = typeof NativeCtor === 'function' ? NativeCtor : Error; + error = new Ctor(response[2]); + error.type = response[1]; + error.stack = response[3]; + + for (const key in extra) { + // @ts-expect-error: no index + error[key] = extra[key]; + } + } + + this._onProcessEnd(error, null); + + break; + + case _types().PARENT_MESSAGE_SETUP_ERROR: + error = new Error('Error when calling setup: ' + response[2]); // @ts-expect-error: adding custom properties to errors. + + error.type = response[1]; + error.stack = response[3]; + + this._onProcessEnd(error, null); + + break; + + case _types().PARENT_MESSAGE_CUSTOM: + this._onCustomMessage(response[1]); + + break; + + default: + throw new TypeError('Unexpected response from worker: ' + response[0]); + } + } + + _onExit(exitCode) { + if (exitCode !== 0 && !this._forceExited) { + this.initialize(); + + if (this._request) { + this._worker.postMessage(this._request); + } + } else { + this._shutdown(); + } + } + + waitForExit() { + return this._exitPromise; + } + + forceExit() { + this._forceExited = true; + + this._worker.terminate(); + } + + send(request, onProcessStart, onProcessEnd, onCustomMessage) { + onProcessStart(this); + + this._onProcessEnd = (...args) => { + // Clean the request to avoid sending past requests to workers that fail + // while waiting for a new request (timers, unhandled rejections...) + this._request = null; + return onProcessEnd(...args); + }; + + this._onCustomMessage = (...arg) => onCustomMessage(...arg); + + this._request = request; + this._retries = 0; + + this._worker.postMessage(request); + } + + getWorkerId() { + return this._options.workerId; + } + + getStdout() { + return this._stdout; + } + + getStderr() { + return this._stderr; + } + + _getFakeStream() { + if (!this._fakeStream) { + this._fakeStream = new (_stream().PassThrough)(); + } + + return this._fakeStream; + } +} + +exports.default = ExperimentalWorker; diff --git a/packages/jest-worker/build/workers/messageParent.d.ts b/packages/jest-worker/build/workers/messageParent.d.ts new file mode 100644 index 000000000000..795bb3511c12 --- /dev/null +++ b/packages/jest-worker/build/workers/messageParent.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +/// +export default function messageParent(message: unknown, parentProcess?: NodeJS.Process): void; diff --git a/packages/jest-worker/build/workers/messageParent.js b/packages/jest-worker/build/workers/messageParent.js new file mode 100644 index 000000000000..54f2bf3181f6 --- /dev/null +++ b/packages/jest-worker/build/workers/messageParent.js @@ -0,0 +1,46 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = messageParent; + +function _types() { + const data = require('../types'); + + _types = function () { + return data; + }; + + return data; +} + +/** + * 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 isWorkerThread = (() => { + try { + // `Require` here to support Node v10 + const {isMainThread, parentPort} = require('worker_threads'); + + return !isMainThread && parentPort != null; + } catch { + return false; + } +})(); + +function messageParent(message, parentProcess = process) { + if (isWorkerThread) { + // `Require` here to support Node v10 + const {parentPort} = require('worker_threads'); // ! is safe due to `null` check in `isWorkerThread` + + parentPort.postMessage([_types().PARENT_MESSAGE_CUSTOM, message]); + } else if (typeof parentProcess.send === 'function') { + parentProcess.send([_types().PARENT_MESSAGE_CUSTOM, message]); + } else { + throw new Error('"messageParent" can only be used inside a worker'); + } +} diff --git a/packages/jest-worker/build/workers/processChild.d.ts b/packages/jest-worker/build/workers/processChild.d.ts new file mode 100644 index 000000000000..fac0c7e359c7 --- /dev/null +++ b/packages/jest-worker/build/workers/processChild.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export {}; diff --git a/packages/jest-worker/build/workers/processChild.js b/packages/jest-worker/build/workers/processChild.js new file mode 100644 index 000000000000..a4f5acba658a --- /dev/null +++ b/packages/jest-worker/build/workers/processChild.js @@ -0,0 +1,156 @@ +'use strict'; + +function _types() { + const data = require('../types'); + + _types = function () { + return data; + }; + + return data; +} + +/** + * 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. + */ +let file = null; +let setupArgs = []; +let initialized = false; +/** + * This file is a small bootstrapper for workers. It sets up the communication + * between the worker and the parent process, interpreting parent messages and + * sending results back. + * + * The file loaded will be lazily initialized the first time any of the workers + * is called. This is done for optimal performance: if the farm is initialized, + * but no call is made to it, child Node processes will be consuming the least + * possible amount of memory. + * + * If an invalid message is detected, the child will exit (by throwing) with a + * non-zero exit code. + */ + +const messageListener = request => { + switch (request[0]) { + case _types().CHILD_MESSAGE_INITIALIZE: + const init = request; + file = init[2]; + setupArgs = request[3]; + break; + + case _types().CHILD_MESSAGE_CALL: + const call = request; + execMethod(call[2], call[3]); + break; + + case _types().CHILD_MESSAGE_END: + end(); + break; + + default: + throw new TypeError( + 'Unexpected request from parent process: ' + request[0] + ); + } +}; + +process.on('message', messageListener); + +function reportSuccess(result) { + if (!process || !process.send) { + throw new Error('Child can only be used on a forked process'); + } + + process.send([_types().PARENT_MESSAGE_OK, result]); +} + +function reportClientError(error) { + return reportError(error, _types().PARENT_MESSAGE_CLIENT_ERROR); +} + +function reportInitializeError(error) { + return reportError(error, _types().PARENT_MESSAGE_SETUP_ERROR); +} + +function reportError(error, type) { + if (!process || !process.send) { + throw new Error('Child can only be used on a forked process'); + } + + if (error == null) { + error = new Error('"null" or "undefined" thrown'); + } + + process.send([ + type, + error.constructor && error.constructor.name, + error.message, + error.stack, + typeof error === 'object' ? {...error} : error + ]); +} + +function end() { + const main = require(file); + + if (!main.teardown) { + exitProcess(); + return; + } + + execFunction(main.teardown, main, [], exitProcess, exitProcess); +} + +function exitProcess() { + // Clean up open handles so the process ideally exits gracefully + process.removeListener('message', messageListener); +} + +function execMethod(method, args) { + const main = require(file); + + let fn; + + if (method === 'default') { + fn = main.__esModule ? main['default'] : main; + } else { + fn = main[method]; + } + + function execHelper() { + execFunction(fn, main, args, reportSuccess, reportClientError); + } + + if (initialized || !main.setup) { + execHelper(); + return; + } + + initialized = true; + execFunction(main.setup, main, setupArgs, execHelper, reportInitializeError); +} + +const isPromise = obj => + !!obj && + (typeof obj === 'object' || typeof obj === 'function') && + typeof obj.then === 'function'; + +function execFunction(fn, ctx, args, onResult, onError) { + let result; + + try { + result = fn.apply(ctx, args); + } catch (err) { + onError(err); + return; + } + + if (isPromise(result)) { + result.then(onResult, onError); + } else { + onResult(result); + } +} diff --git a/packages/jest-worker/build/workers/threadChild.d.ts b/packages/jest-worker/build/workers/threadChild.d.ts new file mode 100644 index 000000000000..fac0c7e359c7 --- /dev/null +++ b/packages/jest-worker/build/workers/threadChild.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export {}; diff --git a/packages/jest-worker/build/workers/threadChild.js b/packages/jest-worker/build/workers/threadChild.js new file mode 100644 index 000000000000..ef32c5bcca56 --- /dev/null +++ b/packages/jest-worker/build/workers/threadChild.js @@ -0,0 +1,169 @@ +'use strict'; + +function _worker_threads() { + const data = require('worker_threads'); + + _worker_threads = function () { + return data; + }; + + return data; +} + +function _types() { + const data = require('../types'); + + _types = function () { + return data; + }; + + return data; +} + +/** + * 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. + */ +let file = null; +let setupArgs = []; +let initialized = false; +/** + * This file is a small bootstrapper for workers. It sets up the communication + * between the worker and the parent process, interpreting parent messages and + * sending results back. + * + * The file loaded will be lazily initialized the first time any of the workers + * is called. This is done for optimal performance: if the farm is initialized, + * but no call is made to it, child Node processes will be consuming the least + * possible amount of memory. + * + * If an invalid message is detected, the child will exit (by throwing) with a + * non-zero exit code. + */ + +const messageListener = request => { + switch (request[0]) { + case _types().CHILD_MESSAGE_INITIALIZE: + const init = request; + file = init[2]; + setupArgs = request[3]; + break; + + case _types().CHILD_MESSAGE_CALL: + const call = request; + execMethod(call[2], call[3]); + break; + + case _types().CHILD_MESSAGE_END: + end(); + break; + + default: + throw new TypeError( + 'Unexpected request from parent process: ' + request[0] + ); + } +}; + +_worker_threads().parentPort.on('message', messageListener); + +function reportSuccess(result) { + if (_worker_threads().isMainThread) { + throw new Error('Child can only be used on a forked process'); + } + + _worker_threads().parentPort.postMessage([ + _types().PARENT_MESSAGE_OK, + result + ]); +} + +function reportClientError(error) { + return reportError(error, _types().PARENT_MESSAGE_CLIENT_ERROR); +} + +function reportInitializeError(error) { + return reportError(error, _types().PARENT_MESSAGE_SETUP_ERROR); +} + +function reportError(error, type) { + if (_worker_threads().isMainThread) { + throw new Error('Child can only be used on a forked process'); + } + + if (error == null) { + error = new Error('"null" or "undefined" thrown'); + } + + _worker_threads().parentPort.postMessage([ + type, + error.constructor && error.constructor.name, + error.message, + error.stack, + typeof error === 'object' ? {...error} : error + ]); +} + +function end() { + const main = require(file); + + if (!main.teardown) { + exitProcess(); + return; + } + + execFunction(main.teardown, main, [], exitProcess, exitProcess); +} + +function exitProcess() { + // Clean up open handles so the worker ideally exits gracefully + _worker_threads().parentPort.removeListener('message', messageListener); +} + +function execMethod(method, args) { + const main = require(file); + + let fn; + + if (method === 'default') { + fn = main.__esModule ? main['default'] : main; + } else { + fn = main[method]; + } + + function execHelper() { + execFunction(fn, main, args, reportSuccess, reportClientError); + } + + if (initialized || !main.setup) { + execHelper(); + return; + } + + initialized = true; + execFunction(main.setup, main, setupArgs, execHelper, reportInitializeError); +} + +const isPromise = obj => + !!obj && + (typeof obj === 'object' || typeof obj === 'function') && + typeof obj.then === 'function'; + +function execFunction(fn, ctx, args, onResult, onError) { + let result; + + try { + result = fn.apply(ctx, args); + } catch (err) { + onError(err); + return; + } + + if (isPromise(result)) { + result.then(onResult, onError); + } else { + onResult(result); + } +} diff --git a/packages/jest/build/jest.d.ts b/packages/jest/build/jest.d.ts new file mode 100644 index 000000000000..a264eabedf76 --- /dev/null +++ b/packages/jest/build/jest.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +export { SearchSource, TestScheduler, TestWatcher, getVersion, runCLI, } from '@jest/core'; +export { run } from 'jest-cli'; diff --git a/packages/jest/build/jest.js b/packages/jest/build/jest.js new file mode 100644 index 000000000000..997d6229dd4d --- /dev/null +++ b/packages/jest/build/jest.js @@ -0,0 +1,61 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'SearchSource', { + enumerable: true, + get: function () { + return _core().SearchSource; + } +}); +Object.defineProperty(exports, 'TestScheduler', { + enumerable: true, + get: function () { + return _core().TestScheduler; + } +}); +Object.defineProperty(exports, 'TestWatcher', { + enumerable: true, + get: function () { + return _core().TestWatcher; + } +}); +Object.defineProperty(exports, 'getVersion', { + enumerable: true, + get: function () { + return _core().getVersion; + } +}); +Object.defineProperty(exports, 'runCLI', { + enumerable: true, + get: function () { + return _core().runCLI; + } +}); +Object.defineProperty(exports, 'run', { + enumerable: true, + get: function () { + return _jestCli().run; + } +}); + +function _core() { + const data = require('@jest/core'); + + _core = function () { + return data; + }; + + return data; +} + +function _jestCli() { + const data = require('jest-cli'); + + _jestCli = function () { + return data; + }; + + return data; +} diff --git a/packages/pretty-format/build/collections.d.ts b/packages/pretty-format/build/collections.d.ts new file mode 100644 index 000000000000..eaf7e611efe6 --- /dev/null +++ b/packages/pretty-format/build/collections.d.ts @@ -0,0 +1,32 @@ +/** + * 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. + * + */ +import type { Config, Printer, Refs } from './types'; +/** + * Return entries (for example, of a map) + * with spacing, indentation, and comma + * without surrounding punctuation (for example, braces) + */ +export declare function printIteratorEntries(iterator: Iterator<[unknown, unknown]>, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer, separator?: string): string; +/** + * Return values (for example, of a set) + * with spacing, indentation, and comma + * without surrounding punctuation (braces or brackets) + */ +export declare function printIteratorValues(iterator: Iterator, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer): string; +/** + * Return items (for example, of an array) + * with spacing, indentation, and comma + * without surrounding punctuation (for example, brackets) + **/ +export declare function printListItems(list: ArrayLike, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer): string; +/** + * Return properties of an object + * with spacing, indentation, and comma + * without surrounding punctuation (for example, braces) + */ +export declare function printObjectProperties(val: Record, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer): string; diff --git a/packages/pretty-format/build/collections.js b/packages/pretty-format/build/collections.js new file mode 100644 index 000000000000..c31d21e94cb6 --- /dev/null +++ b/packages/pretty-format/build/collections.js @@ -0,0 +1,185 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.printIteratorEntries = printIteratorEntries; +exports.printIteratorValues = printIteratorValues; +exports.printListItems = printListItems; +exports.printObjectProperties = printObjectProperties; + +/** + * 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 getKeysOfEnumerableProperties = object => { + const keys = Object.keys(object).sort(); + + if (Object.getOwnPropertySymbols) { + Object.getOwnPropertySymbols(object).forEach(symbol => { + if (Object.getOwnPropertyDescriptor(object, symbol).enumerable) { + keys.push(symbol); + } + }); + } + + return keys; +}; +/** + * Return entries (for example, of a map) + * with spacing, indentation, and comma + * without surrounding punctuation (for example, braces) + */ + +function printIteratorEntries( + iterator, + config, + indentation, + depth, + refs, + printer, // Too bad, so sad that separator for ECMAScript Map has been ' => ' + // What a distracting diff if you change a data structure to/from + // ECMAScript Object or Immutable.Map/OrderedMap which use the default. + separator = ': ' +) { + let result = ''; + let current = iterator.next(); + + if (!current.done) { + result += config.spacingOuter; + const indentationNext = indentation + config.indent; + + while (!current.done) { + const name = printer( + current.value[0], + config, + indentationNext, + depth, + refs + ); + const value = printer( + current.value[1], + config, + indentationNext, + depth, + refs + ); + result += indentationNext + name + separator + value; + current = iterator.next(); + + if (!current.done) { + result += ',' + config.spacingInner; + } else if (!config.min) { + result += ','; + } + } + + result += config.spacingOuter + indentation; + } + + return result; +} +/** + * Return values (for example, of a set) + * with spacing, indentation, and comma + * without surrounding punctuation (braces or brackets) + */ + +function printIteratorValues( + iterator, + config, + indentation, + depth, + refs, + printer +) { + let result = ''; + let current = iterator.next(); + + if (!current.done) { + result += config.spacingOuter; + const indentationNext = indentation + config.indent; + + while (!current.done) { + result += + indentationNext + + printer(current.value, config, indentationNext, depth, refs); + current = iterator.next(); + + if (!current.done) { + result += ',' + config.spacingInner; + } else if (!config.min) { + result += ','; + } + } + + result += config.spacingOuter + indentation; + } + + return result; +} +/** + * Return items (for example, of an array) + * with spacing, indentation, and comma + * without surrounding punctuation (for example, brackets) + **/ + +function printListItems(list, config, indentation, depth, refs, printer) { + let result = ''; + + if (list.length) { + result += config.spacingOuter; + const indentationNext = indentation + config.indent; + + for (let i = 0; i < list.length; i++) { + result += + indentationNext + + printer(list[i], config, indentationNext, depth, refs); + + if (i < list.length - 1) { + result += ',' + config.spacingInner; + } else if (!config.min) { + result += ','; + } + } + + result += config.spacingOuter + indentation; + } + + return result; +} +/** + * Return properties of an object + * with spacing, indentation, and comma + * without surrounding punctuation (for example, braces) + */ + +function printObjectProperties(val, config, indentation, depth, refs, printer) { + let result = ''; + const keys = getKeysOfEnumerableProperties(val); + + if (keys.length) { + result += config.spacingOuter; + const indentationNext = indentation + config.indent; + + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const name = printer(key, config, indentationNext, depth, refs); + const value = printer(val[key], config, indentationNext, depth, refs); + result += indentationNext + name + ': ' + value; + + if (i < keys.length - 1) { + result += ',' + config.spacingInner; + } else if (!config.min) { + result += ','; + } + } + + result += config.spacingOuter + indentation; + } + + return result; +} diff --git a/packages/pretty-format/build/index.d.ts b/packages/pretty-format/build/index.d.ts new file mode 100644 index 000000000000..1bf83441761d --- /dev/null +++ b/packages/pretty-format/build/index.d.ts @@ -0,0 +1,24 @@ +/** + * 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. + */ +import type { NewPlugin, OptionsReceived } from './types'; +export type { Colors, Config, Options, OptionsReceived, OldPlugin, NewPlugin, Plugin, Plugins, Refs, Theme, } from './types'; +/** + * Returns a presentation string of your `val` object + * @param val any potential JavaScript object + * @param options Custom settings + */ +export declare function format(val: unknown, options?: OptionsReceived): string; +export declare const plugins: { + AsymmetricMatcher: NewPlugin; + ConvertAnsi: NewPlugin; + DOMCollection: NewPlugin; + DOMElement: NewPlugin; + Immutable: NewPlugin; + ReactElement: NewPlugin; + ReactTestComponent: NewPlugin; +}; +export default format; diff --git a/packages/pretty-format/build/index.js b/packages/pretty-format/build/index.js new file mode 100644 index 000000000000..2f38f93ff7e3 --- /dev/null +++ b/packages/pretty-format/build/index.js @@ -0,0 +1,568 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.format = format; +exports.default = exports.plugins = void 0; + +var _ansiStyles = _interopRequireDefault(require('ansi-styles')); + +var _collections = require('./collections'); + +var _AsymmetricMatcher = _interopRequireDefault( + require('./plugins/AsymmetricMatcher') +); + +var _ConvertAnsi = _interopRequireDefault(require('./plugins/ConvertAnsi')); + +var _DOMCollection = _interopRequireDefault(require('./plugins/DOMCollection')); + +var _DOMElement = _interopRequireDefault(require('./plugins/DOMElement')); + +var _Immutable = _interopRequireDefault(require('./plugins/Immutable')); + +var _ReactElement = _interopRequireDefault(require('./plugins/ReactElement')); + +var _ReactTestComponent = _interopRequireDefault( + require('./plugins/ReactTestComponent') +); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ + +/* eslint-disable local/ban-types-eventually */ +const toString = Object.prototype.toString; +const toISOString = Date.prototype.toISOString; +const errorToString = Error.prototype.toString; +const regExpToString = RegExp.prototype.toString; +/** + * Explicitly comparing typeof constructor to function avoids undefined as name + * when mock identity-obj-proxy returns the key as the value for any key. + */ + +const getConstructorName = val => + (typeof val.constructor === 'function' && val.constructor.name) || 'Object'; +/* global window */ + +/** Is val is equal to global window object? Works even if it does not exist :) */ + +const isWindow = val => typeof window !== 'undefined' && val === window; + +const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/; +const NEWLINE_REGEXP = /\n/gi; + +class PrettyFormatPluginError extends Error { + constructor(message, stack) { + super(message); + this.stack = stack; + this.name = this.constructor.name; + } +} + +function isToStringedArrayType(toStringed) { + return ( + toStringed === '[object Array]' || + toStringed === '[object ArrayBuffer]' || + toStringed === '[object DataView]' || + toStringed === '[object Float32Array]' || + toStringed === '[object Float64Array]' || + toStringed === '[object Int8Array]' || + toStringed === '[object Int16Array]' || + toStringed === '[object Int32Array]' || + toStringed === '[object Uint8Array]' || + toStringed === '[object Uint8ClampedArray]' || + toStringed === '[object Uint16Array]' || + toStringed === '[object Uint32Array]' + ); +} + +function printNumber(val) { + return Object.is(val, -0) ? '-0' : String(val); +} + +function printBigInt(val) { + return String(`${val}n`); +} + +function printFunction(val, printFunctionName) { + if (!printFunctionName) { + return '[Function]'; + } + + return '[Function ' + (val.name || 'anonymous') + ']'; +} + +function printSymbol(val) { + return String(val).replace(SYMBOL_REGEXP, 'Symbol($1)'); +} + +function printError(val) { + return '[' + errorToString.call(val) + ']'; +} +/** + * The first port of call for printing an object, handles most of the + * data-types in JS. + */ + +function printBasicValue(val, printFunctionName, escapeRegex, escapeString) { + if (val === true || val === false) { + return '' + val; + } + + if (val === undefined) { + return 'undefined'; + } + + if (val === null) { + return 'null'; + } + + const typeOf = typeof val; + + if (typeOf === 'number') { + return printNumber(val); + } + + if (typeOf === 'bigint') { + return printBigInt(val); + } + + if (typeOf === 'string') { + if (escapeString) { + return '"' + val.replace(/"|\\/g, '\\$&') + '"'; + } + + return '"' + val + '"'; + } + + if (typeOf === 'function') { + return printFunction(val, printFunctionName); + } + + if (typeOf === 'symbol') { + return printSymbol(val); + } + + const toStringed = toString.call(val); + + if (toStringed === '[object WeakMap]') { + return 'WeakMap {}'; + } + + if (toStringed === '[object WeakSet]') { + return 'WeakSet {}'; + } + + if ( + toStringed === '[object Function]' || + toStringed === '[object GeneratorFunction]' + ) { + return printFunction(val, printFunctionName); + } + + if (toStringed === '[object Symbol]') { + return printSymbol(val); + } + + if (toStringed === '[object Date]') { + return isNaN(+val) ? 'Date { NaN }' : toISOString.call(val); + } + + if (toStringed === '[object Error]') { + return printError(val); + } + + if (toStringed === '[object RegExp]') { + if (escapeRegex) { + // https://github.com/benjamingr/RegExp.escape/blob/master/polyfill.js + return regExpToString.call(val).replace(/[\\^$*+?.()|[\]{}]/g, '\\$&'); + } + + return regExpToString.call(val); + } + + if (val instanceof Error) { + return printError(val); + } + + return null; +} +/** + * Handles more complex objects ( such as objects with circular references. + * maps and sets etc ) + */ + +function printComplexValue( + val, + config, + indentation, + depth, + refs, + hasCalledToJSON +) { + if (refs.indexOf(val) !== -1) { + return '[Circular]'; + } + + refs = refs.slice(); + refs.push(val); + const hitMaxDepth = ++depth > config.maxDepth; + const min = config.min; + + if ( + config.callToJSON && + !hitMaxDepth && + val.toJSON && + typeof val.toJSON === 'function' && + !hasCalledToJSON + ) { + return printer(val.toJSON(), config, indentation, depth, refs, true); + } + + const toStringed = toString.call(val); + + if (toStringed === '[object Arguments]') { + return hitMaxDepth + ? '[Arguments]' + : (min ? '' : 'Arguments ') + + '[' + + (0, _collections.printListItems)( + val, + config, + indentation, + depth, + refs, + printer + ) + + ']'; + } + + if (isToStringedArrayType(toStringed)) { + return hitMaxDepth + ? '[' + val.constructor.name + ']' + : (min ? '' : val.constructor.name + ' ') + + '[' + + (0, _collections.printListItems)( + val, + config, + indentation, + depth, + refs, + printer + ) + + ']'; + } + + if (toStringed === '[object Map]') { + return hitMaxDepth + ? '[Map]' + : 'Map {' + + (0, _collections.printIteratorEntries)( + val.entries(), + config, + indentation, + depth, + refs, + printer, + ' => ' + ) + + '}'; + } + + if (toStringed === '[object Set]') { + return hitMaxDepth + ? '[Set]' + : 'Set {' + + (0, _collections.printIteratorValues)( + val.values(), + config, + indentation, + depth, + refs, + printer + ) + + '}'; + } // Avoid failure to serialize global window object in jsdom test environment. + // For example, not even relevant if window is prop of React element. + + return hitMaxDepth || isWindow(val) + ? '[' + getConstructorName(val) + ']' + : (min ? '' : getConstructorName(val) + ' ') + + '{' + + (0, _collections.printObjectProperties)( + val, + config, + indentation, + depth, + refs, + printer + ) + + '}'; +} + +function isNewPlugin(plugin) { + return plugin.serialize != null; +} + +function printPlugin(plugin, val, config, indentation, depth, refs) { + let printed; + + try { + printed = isNewPlugin(plugin) + ? plugin.serialize(val, config, indentation, depth, refs, printer) + : plugin.print( + val, + valChild => printer(valChild, config, indentation, depth, refs), + str => { + const indentationNext = indentation + config.indent; + return ( + indentationNext + + str.replace(NEWLINE_REGEXP, '\n' + indentationNext) + ); + }, + { + edgeSpacing: config.spacingOuter, + min: config.min, + spacing: config.spacingInner + }, + config.colors + ); + } catch (error) { + throw new PrettyFormatPluginError(error.message, error.stack); + } + + if (typeof printed !== 'string') { + throw new Error( + `pretty-format: Plugin must return type "string" but instead returned "${typeof printed}".` + ); + } + + return printed; +} + +function findPlugin(plugins, val) { + for (let p = 0; p < plugins.length; p++) { + try { + if (plugins[p].test(val)) { + return plugins[p]; + } + } catch (error) { + throw new PrettyFormatPluginError(error.message, error.stack); + } + } + + return null; +} + +function printer(val, config, indentation, depth, refs, hasCalledToJSON) { + const plugin = findPlugin(config.plugins, val); + + if (plugin !== null) { + return printPlugin(plugin, val, config, indentation, depth, refs); + } + + const basicResult = printBasicValue( + val, + config.printFunctionName, + config.escapeRegex, + config.escapeString + ); + + if (basicResult !== null) { + return basicResult; + } + + return printComplexValue( + val, + config, + indentation, + depth, + refs, + hasCalledToJSON + ); +} + +const DEFAULT_THEME = { + comment: 'gray', + content: 'reset', + prop: 'yellow', + tag: 'cyan', + value: 'green' +}; +const DEFAULT_THEME_KEYS = Object.keys(DEFAULT_THEME); +const DEFAULT_OPTIONS = { + callToJSON: true, + escapeRegex: false, + escapeString: true, + highlight: false, + indent: 2, + maxDepth: Infinity, + min: false, + plugins: [], + printFunctionName: true, + theme: DEFAULT_THEME +}; + +function validateOptions(options) { + Object.keys(options).forEach(key => { + if (!DEFAULT_OPTIONS.hasOwnProperty(key)) { + throw new Error(`pretty-format: Unknown option "${key}".`); + } + }); + + if (options.min && options.indent !== undefined && options.indent !== 0) { + throw new Error( + 'pretty-format: Options "min" and "indent" cannot be used together.' + ); + } + + if (options.theme !== undefined) { + if (options.theme === null) { + throw new Error(`pretty-format: Option "theme" must not be null.`); + } + + if (typeof options.theme !== 'object') { + throw new Error( + `pretty-format: Option "theme" must be of type "object" but instead received "${typeof options.theme}".` + ); + } + } +} + +const getColorsHighlight = options => + DEFAULT_THEME_KEYS.reduce((colors, key) => { + const value = + options.theme && options.theme[key] !== undefined + ? options.theme[key] + : DEFAULT_THEME[key]; + const color = value && _ansiStyles.default[value]; + + if ( + color && + typeof color.close === 'string' && + typeof color.open === 'string' + ) { + colors[key] = color; + } else { + throw new Error( + `pretty-format: Option "theme" has a key "${key}" whose value "${value}" is undefined in ansi-styles.` + ); + } + + return colors; + }, Object.create(null)); + +const getColorsEmpty = () => + DEFAULT_THEME_KEYS.reduce((colors, key) => { + colors[key] = { + close: '', + open: '' + }; + return colors; + }, Object.create(null)); + +const getPrintFunctionName = options => + options && options.printFunctionName !== undefined + ? options.printFunctionName + : DEFAULT_OPTIONS.printFunctionName; + +const getEscapeRegex = options => + options && options.escapeRegex !== undefined + ? options.escapeRegex + : DEFAULT_OPTIONS.escapeRegex; + +const getEscapeString = options => + options && options.escapeString !== undefined + ? options.escapeString + : DEFAULT_OPTIONS.escapeString; + +const getConfig = options => ({ + callToJSON: + options && options.callToJSON !== undefined + ? options.callToJSON + : DEFAULT_OPTIONS.callToJSON, + colors: + options && options.highlight + ? getColorsHighlight(options) + : getColorsEmpty(), + escapeRegex: getEscapeRegex(options), + escapeString: getEscapeString(options), + indent: + options && options.min + ? '' + : createIndent( + options && options.indent !== undefined + ? options.indent + : DEFAULT_OPTIONS.indent + ), + maxDepth: + options && options.maxDepth !== undefined + ? options.maxDepth + : DEFAULT_OPTIONS.maxDepth, + min: options && options.min !== undefined ? options.min : DEFAULT_OPTIONS.min, + plugins: + options && options.plugins !== undefined + ? options.plugins + : DEFAULT_OPTIONS.plugins, + printFunctionName: getPrintFunctionName(options), + spacingInner: options && options.min ? ' ' : '\n', + spacingOuter: options && options.min ? '' : '\n' +}); + +function createIndent(indent) { + return new Array(indent + 1).join(' '); +} +/** + * Returns a presentation string of your `val` object + * @param val any potential JavaScript object + * @param options Custom settings + */ + +function format(val, options) { + if (options) { + validateOptions(options); + + if (options.plugins) { + const plugin = findPlugin(options.plugins, val); + + if (plugin !== null) { + return printPlugin(plugin, val, getConfig(options), '', 0, []); + } + } + } + + const basicResult = printBasicValue( + val, + getPrintFunctionName(options), + getEscapeRegex(options), + getEscapeString(options) + ); + + if (basicResult !== null) { + return basicResult; + } + + return printComplexValue(val, getConfig(options), '', 0, []); +} + +const plugins = { + AsymmetricMatcher: _AsymmetricMatcher.default, + ConvertAnsi: _ConvertAnsi.default, + DOMCollection: _DOMCollection.default, + DOMElement: _DOMElement.default, + Immutable: _Immutable.default, + ReactElement: _ReactElement.default, + ReactTestComponent: _ReactTestComponent.default +}; +exports.plugins = plugins; +var _default = format; +exports.default = _default; diff --git a/packages/pretty-format/build/plugins/AsymmetricMatcher.d.ts b/packages/pretty-format/build/plugins/AsymmetricMatcher.d.ts new file mode 100644 index 000000000000..fd2531b888de --- /dev/null +++ b/packages/pretty-format/build/plugins/AsymmetricMatcher.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +import type { NewPlugin } from '../types'; +export declare const serialize: NewPlugin['serialize']; +export declare const test: NewPlugin['test']; +declare const plugin: NewPlugin; +export default plugin; diff --git a/packages/pretty-format/build/plugins/AsymmetricMatcher.js b/packages/pretty-format/build/plugins/AsymmetricMatcher.js new file mode 100644 index 000000000000..a09dec2154b0 --- /dev/null +++ b/packages/pretty-format/build/plugins/AsymmetricMatcher.js @@ -0,0 +1,103 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = exports.test = exports.serialize = void 0; + +var _collections = require('../collections'); + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +const asymmetricMatcher = + typeof Symbol === 'function' && Symbol.for + ? Symbol.for('jest.asymmetricMatcher') + : 0x1357a5; +const SPACE = ' '; + +const serialize = (val, config, indentation, depth, refs, printer) => { + const stringedValue = val.toString(); + + if ( + stringedValue === 'ArrayContaining' || + stringedValue === 'ArrayNotContaining' + ) { + if (++depth > config.maxDepth) { + return '[' + stringedValue + ']'; + } + + return ( + stringedValue + + SPACE + + '[' + + (0, _collections.printListItems)( + val.sample, + config, + indentation, + depth, + refs, + printer + ) + + ']' + ); + } + + if ( + stringedValue === 'ObjectContaining' || + stringedValue === 'ObjectNotContaining' + ) { + if (++depth > config.maxDepth) { + return '[' + stringedValue + ']'; + } + + return ( + stringedValue + + SPACE + + '{' + + (0, _collections.printObjectProperties)( + val.sample, + config, + indentation, + depth, + refs, + printer + ) + + '}' + ); + } + + if ( + stringedValue === 'StringMatching' || + stringedValue === 'StringNotMatching' + ) { + return ( + stringedValue + + SPACE + + printer(val.sample, config, indentation, depth, refs) + ); + } + + if ( + stringedValue === 'StringContaining' || + stringedValue === 'StringNotContaining' + ) { + return ( + stringedValue + + SPACE + + printer(val.sample, config, indentation, depth, refs) + ); + } + + return val.toAsymmetricMatcher(); +}; + +exports.serialize = serialize; + +const test = val => val && val.$$typeof === asymmetricMatcher; + +exports.test = test; +const plugin = { + serialize, + test +}; +var _default = plugin; +exports.default = _default; diff --git a/packages/pretty-format/build/plugins/ConvertAnsi.d.ts b/packages/pretty-format/build/plugins/ConvertAnsi.d.ts new file mode 100644 index 000000000000..bb0f507b781d --- /dev/null +++ b/packages/pretty-format/build/plugins/ConvertAnsi.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +import type { NewPlugin } from '../types'; +export declare const test: NewPlugin['test']; +export declare const serialize: NewPlugin['serialize']; +declare const plugin: NewPlugin; +export default plugin; diff --git a/packages/pretty-format/build/plugins/ConvertAnsi.js b/packages/pretty-format/build/plugins/ConvertAnsi.js new file mode 100644 index 000000000000..3d2d8fcbe1cb --- /dev/null +++ b/packages/pretty-format/build/plugins/ConvertAnsi.js @@ -0,0 +1,96 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = exports.serialize = exports.test = void 0; + +var _ansiRegex = _interopRequireDefault(require('ansi-regex')); + +var _ansiStyles = _interopRequireDefault(require('ansi-styles')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 toHumanReadableAnsi = text => + text.replace((0, _ansiRegex.default)(), match => { + switch (match) { + case _ansiStyles.default.red.close: + case _ansiStyles.default.green.close: + case _ansiStyles.default.cyan.close: + case _ansiStyles.default.gray.close: + case _ansiStyles.default.white.close: + case _ansiStyles.default.yellow.close: + case _ansiStyles.default.bgRed.close: + case _ansiStyles.default.bgGreen.close: + case _ansiStyles.default.bgYellow.close: + case _ansiStyles.default.inverse.close: + case _ansiStyles.default.dim.close: + case _ansiStyles.default.bold.close: + case _ansiStyles.default.reset.open: + case _ansiStyles.default.reset.close: + return ''; + + case _ansiStyles.default.red.open: + return ''; + + case _ansiStyles.default.green.open: + return ''; + + case _ansiStyles.default.cyan.open: + return ''; + + case _ansiStyles.default.gray.open: + return ''; + + case _ansiStyles.default.white.open: + return ''; + + case _ansiStyles.default.yellow.open: + return ''; + + case _ansiStyles.default.bgRed.open: + return ''; + + case _ansiStyles.default.bgGreen.open: + return ''; + + case _ansiStyles.default.bgYellow.open: + return ''; + + case _ansiStyles.default.inverse.open: + return ''; + + case _ansiStyles.default.dim.open: + return ''; + + case _ansiStyles.default.bold.open: + return ''; + + default: + return ''; + } + }); + +const test = val => + typeof val === 'string' && !!val.match((0, _ansiRegex.default)()); + +exports.test = test; + +const serialize = (val, config, indentation, depth, refs, printer) => + printer(toHumanReadableAnsi(val), config, indentation, depth, refs); + +exports.serialize = serialize; +const plugin = { + serialize, + test +}; +var _default = plugin; +exports.default = _default; diff --git a/packages/pretty-format/build/plugins/DOMCollection.d.ts b/packages/pretty-format/build/plugins/DOMCollection.d.ts new file mode 100644 index 000000000000..bb0f507b781d --- /dev/null +++ b/packages/pretty-format/build/plugins/DOMCollection.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +import type { NewPlugin } from '../types'; +export declare const test: NewPlugin['test']; +export declare const serialize: NewPlugin['serialize']; +declare const plugin: NewPlugin; +export default plugin; diff --git a/packages/pretty-format/build/plugins/DOMCollection.js b/packages/pretty-format/build/plugins/DOMCollection.js new file mode 100644 index 000000000000..1ae3750118a0 --- /dev/null +++ b/packages/pretty-format/build/plugins/DOMCollection.js @@ -0,0 +1,80 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = exports.serialize = exports.test = void 0; + +var _collections = require('../collections'); + +/** + * 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. + */ + +/* eslint-disable local/ban-types-eventually */ +const SPACE = ' '; +const OBJECT_NAMES = ['DOMStringMap', 'NamedNodeMap']; +const ARRAY_REGEXP = /^(HTML\w*Collection|NodeList)$/; + +const testName = name => + OBJECT_NAMES.indexOf(name) !== -1 || ARRAY_REGEXP.test(name); + +const test = val => + val && + val.constructor && + !!val.constructor.name && + testName(val.constructor.name); + +exports.test = test; + +const isNamedNodeMap = collection => + collection.constructor.name === 'NamedNodeMap'; + +const serialize = (collection, config, indentation, depth, refs, printer) => { + const name = collection.constructor.name; + + if (++depth > config.maxDepth) { + return '[' + name + ']'; + } + + return ( + (config.min ? '' : name + SPACE) + + (OBJECT_NAMES.indexOf(name) !== -1 + ? '{' + + (0, _collections.printObjectProperties)( + isNamedNodeMap(collection) + ? Array.from(collection).reduce((props, attribute) => { + props[attribute.name] = attribute.value; + return props; + }, {}) + : {...collection}, + config, + indentation, + depth, + refs, + printer + ) + + '}' + : '[' + + (0, _collections.printListItems)( + Array.from(collection), + config, + indentation, + depth, + refs, + printer + ) + + ']') + ); +}; + +exports.serialize = serialize; +const plugin = { + serialize, + test +}; +var _default = plugin; +exports.default = _default; diff --git a/packages/pretty-format/build/plugins/DOMElement.d.ts b/packages/pretty-format/build/plugins/DOMElement.d.ts new file mode 100644 index 000000000000..bb0f507b781d --- /dev/null +++ b/packages/pretty-format/build/plugins/DOMElement.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +import type { NewPlugin } from '../types'; +export declare const test: NewPlugin['test']; +export declare const serialize: NewPlugin['serialize']; +declare const plugin: NewPlugin; +export default plugin; diff --git a/packages/pretty-format/build/plugins/DOMElement.js b/packages/pretty-format/build/plugins/DOMElement.js new file mode 100644 index 000000000000..1d1b3572d7e9 --- /dev/null +++ b/packages/pretty-format/build/plugins/DOMElement.js @@ -0,0 +1,120 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = exports.serialize = exports.test = void 0; + +var _markup = require('./lib/markup'); + +/** + * 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 ELEMENT_NODE = 1; +const TEXT_NODE = 3; +const COMMENT_NODE = 8; +const FRAGMENT_NODE = 11; +const ELEMENT_REGEXP = /^((HTML|SVG)\w*)?Element$/; + +const testNode = val => { + const constructorName = val.constructor.name; + const {nodeType, tagName} = val; + const isCustomElement = + (typeof tagName === 'string' && tagName.includes('-')) || + (typeof val.hasAttribute === 'function' && val.hasAttribute('is')); + return ( + (nodeType === ELEMENT_NODE && + (ELEMENT_REGEXP.test(constructorName) || isCustomElement)) || + (nodeType === TEXT_NODE && constructorName === 'Text') || + (nodeType === COMMENT_NODE && constructorName === 'Comment') || + (nodeType === FRAGMENT_NODE && constructorName === 'DocumentFragment') + ); +}; + +const test = val => { + var _val$constructor; + + return ( + (val === null || val === void 0 + ? void 0 + : (_val$constructor = val.constructor) === null || + _val$constructor === void 0 + ? void 0 + : _val$constructor.name) && testNode(val) + ); +}; + +exports.test = test; + +function nodeIsText(node) { + return node.nodeType === TEXT_NODE; +} + +function nodeIsComment(node) { + return node.nodeType === COMMENT_NODE; +} + +function nodeIsFragment(node) { + return node.nodeType === FRAGMENT_NODE; +} + +const serialize = (node, config, indentation, depth, refs, printer) => { + if (nodeIsText(node)) { + return (0, _markup.printText)(node.data, config); + } + + if (nodeIsComment(node)) { + return (0, _markup.printComment)(node.data, config); + } + + const type = nodeIsFragment(node) + ? `DocumentFragment` + : node.tagName.toLowerCase(); + + if (++depth > config.maxDepth) { + return (0, _markup.printElementAsLeaf)(type, config); + } + + return (0, _markup.printElement)( + type, + (0, _markup.printProps)( + nodeIsFragment(node) + ? [] + : Array.from(node.attributes) + .map(attr => attr.name) + .sort(), + nodeIsFragment(node) + ? {} + : Array.from(node.attributes).reduce((props, attribute) => { + props[attribute.name] = attribute.value; + return props; + }, {}), + config, + indentation + config.indent, + depth, + refs, + printer + ), + (0, _markup.printChildren)( + Array.prototype.slice.call(node.childNodes || node.children), + config, + indentation + config.indent, + depth, + refs, + printer + ), + config, + indentation + ); +}; + +exports.serialize = serialize; +const plugin = { + serialize, + test +}; +var _default = plugin; +exports.default = _default; diff --git a/packages/pretty-format/build/plugins/Immutable.d.ts b/packages/pretty-format/build/plugins/Immutable.d.ts new file mode 100644 index 000000000000..fd2531b888de --- /dev/null +++ b/packages/pretty-format/build/plugins/Immutable.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +import type { NewPlugin } from '../types'; +export declare const serialize: NewPlugin['serialize']; +export declare const test: NewPlugin['test']; +declare const plugin: NewPlugin; +export default plugin; diff --git a/packages/pretty-format/build/plugins/Immutable.js b/packages/pretty-format/build/plugins/Immutable.js new file mode 100644 index 000000000000..92684017d270 --- /dev/null +++ b/packages/pretty-format/build/plugins/Immutable.js @@ -0,0 +1,247 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = exports.test = exports.serialize = void 0; + +var _collections = require('../collections'); + +/** + * 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. + */ +// SENTINEL constants are from https://github.com/facebook/immutable-js +const IS_ITERABLE_SENTINEL = '@@__IMMUTABLE_ITERABLE__@@'; +const IS_LIST_SENTINEL = '@@__IMMUTABLE_LIST__@@'; +const IS_KEYED_SENTINEL = '@@__IMMUTABLE_KEYED__@@'; +const IS_MAP_SENTINEL = '@@__IMMUTABLE_MAP__@@'; +const IS_ORDERED_SENTINEL = '@@__IMMUTABLE_ORDERED__@@'; +const IS_RECORD_SENTINEL = '@@__IMMUTABLE_RECORD__@@'; // immutable v4 + +const IS_SEQ_SENTINEL = '@@__IMMUTABLE_SEQ__@@'; +const IS_SET_SENTINEL = '@@__IMMUTABLE_SET__@@'; +const IS_STACK_SENTINEL = '@@__IMMUTABLE_STACK__@@'; + +const getImmutableName = name => 'Immutable.' + name; + +const printAsLeaf = name => '[' + name + ']'; + +const SPACE = ' '; +const LAZY = '…'; // Seq is lazy if it calls a method like filter + +const printImmutableEntries = ( + val, + config, + indentation, + depth, + refs, + printer, + type +) => + ++depth > config.maxDepth + ? printAsLeaf(getImmutableName(type)) + : getImmutableName(type) + + SPACE + + '{' + + (0, _collections.printIteratorEntries)( + val.entries(), + config, + indentation, + depth, + refs, + printer + ) + + '}'; // Record has an entries method because it is a collection in immutable v3. +// Return an iterator for Immutable Record from version v3 or v4. + +function getRecordEntries(val) { + let i = 0; + return { + next() { + if (i < val._keys.length) { + const key = val._keys[i++]; + return { + done: false, + value: [key, val.get(key)] + }; + } + + return { + done: true, + value: undefined + }; + } + }; +} + +const printImmutableRecord = ( + val, + config, + indentation, + depth, + refs, + printer +) => { + // _name property is defined only for an Immutable Record instance + // which was constructed with a second optional descriptive name arg + const name = getImmutableName(val._name || 'Record'); + return ++depth > config.maxDepth + ? printAsLeaf(name) + : name + + SPACE + + '{' + + (0, _collections.printIteratorEntries)( + getRecordEntries(val), + config, + indentation, + depth, + refs, + printer + ) + + '}'; +}; + +const printImmutableSeq = (val, config, indentation, depth, refs, printer) => { + const name = getImmutableName('Seq'); + + if (++depth > config.maxDepth) { + return printAsLeaf(name); + } + + if (val[IS_KEYED_SENTINEL]) { + return ( + name + + SPACE + + '{' + // from Immutable collection of entries or from ECMAScript object + (val._iter || val._object + ? (0, _collections.printIteratorEntries)( + val.entries(), + config, + indentation, + depth, + refs, + printer + ) + : LAZY) + + '}' + ); + } + + return ( + name + + SPACE + + '[' + + (val._iter || // from Immutable collection of values + val._array || // from ECMAScript array + val._collection || // from ECMAScript collection in immutable v4 + val._iterable // from ECMAScript collection in immutable v3 + ? (0, _collections.printIteratorValues)( + val.values(), + config, + indentation, + depth, + refs, + printer + ) + : LAZY) + + ']' + ); +}; + +const printImmutableValues = ( + val, + config, + indentation, + depth, + refs, + printer, + type +) => + ++depth > config.maxDepth + ? printAsLeaf(getImmutableName(type)) + : getImmutableName(type) + + SPACE + + '[' + + (0, _collections.printIteratorValues)( + val.values(), + config, + indentation, + depth, + refs, + printer + ) + + ']'; + +const serialize = (val, config, indentation, depth, refs, printer) => { + if (val[IS_MAP_SENTINEL]) { + return printImmutableEntries( + val, + config, + indentation, + depth, + refs, + printer, + val[IS_ORDERED_SENTINEL] ? 'OrderedMap' : 'Map' + ); + } + + if (val[IS_LIST_SENTINEL]) { + return printImmutableValues( + val, + config, + indentation, + depth, + refs, + printer, + 'List' + ); + } + + if (val[IS_SET_SENTINEL]) { + return printImmutableValues( + val, + config, + indentation, + depth, + refs, + printer, + val[IS_ORDERED_SENTINEL] ? 'OrderedSet' : 'Set' + ); + } + + if (val[IS_STACK_SENTINEL]) { + return printImmutableValues( + val, + config, + indentation, + depth, + refs, + printer, + 'Stack' + ); + } + + if (val[IS_SEQ_SENTINEL]) { + return printImmutableSeq(val, config, indentation, depth, refs, printer); + } // For compatibility with immutable v3 and v4, let record be the default. + + return printImmutableRecord(val, config, indentation, depth, refs, printer); +}; // Explicitly comparing sentinel properties to true avoids false positive +// when mock identity-obj-proxy returns the key as the value for any key. + +exports.serialize = serialize; + +const test = val => + val && + (val[IS_ITERABLE_SENTINEL] === true || val[IS_RECORD_SENTINEL] === true); + +exports.test = test; +const plugin = { + serialize, + test +}; +var _default = plugin; +exports.default = _default; diff --git a/packages/pretty-format/build/plugins/ReactElement.d.ts b/packages/pretty-format/build/plugins/ReactElement.d.ts new file mode 100644 index 000000000000..fd2531b888de --- /dev/null +++ b/packages/pretty-format/build/plugins/ReactElement.d.ts @@ -0,0 +1,11 @@ +/** + * 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. + */ +import type { NewPlugin } from '../types'; +export declare const serialize: NewPlugin['serialize']; +export declare const test: NewPlugin['test']; +declare const plugin: NewPlugin; +export default plugin; diff --git a/packages/pretty-format/build/plugins/ReactElement.js b/packages/pretty-format/build/plugins/ReactElement.js new file mode 100644 index 000000000000..ad2cca0839a6 --- /dev/null +++ b/packages/pretty-format/build/plugins/ReactElement.js @@ -0,0 +1,166 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = exports.test = exports.serialize = void 0; + +var ReactIs = _interopRequireWildcard(require('react-is')); + +var _markup = require('./lib/markup'); + +function _getRequireWildcardCache() { + if (typeof WeakMap !== 'function') return null; + var cache = new WeakMap(); + _getRequireWildcardCache = function () { + return cache; + }; + return cache; +} + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (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. + */ +// Given element.props.children, or subtree during recursive traversal, +// return flattened array of children. +const getChildren = (arg, children = []) => { + if (Array.isArray(arg)) { + arg.forEach(item => { + getChildren(item, children); + }); + } else if (arg != null && arg !== false) { + children.push(arg); + } + + return children; +}; + +const getType = element => { + const type = element.type; + + if (typeof type === 'string') { + return type; + } + + if (typeof type === 'function') { + return type.displayName || type.name || 'Unknown'; + } + + if (ReactIs.isFragment(element)) { + return 'React.Fragment'; + } + + if (ReactIs.isSuspense(element)) { + return 'React.Suspense'; + } + + if (typeof type === 'object' && type !== null) { + if (ReactIs.isContextProvider(element)) { + return 'Context.Provider'; + } + + if (ReactIs.isContextConsumer(element)) { + return 'Context.Consumer'; + } + + if (ReactIs.isForwardRef(element)) { + if (type.displayName) { + return type.displayName; + } + + const functionName = type.render.displayName || type.render.name || ''; + return functionName !== '' + ? 'ForwardRef(' + functionName + ')' + : 'ForwardRef'; + } + + if (ReactIs.isMemo(element)) { + const functionName = + type.displayName || type.type.displayName || type.type.name || ''; + return functionName !== '' ? 'Memo(' + functionName + ')' : 'Memo'; + } + } + + return 'UNDEFINED'; +}; + +const getPropKeys = element => { + const {props} = element; + return Object.keys(props) + .filter(key => key !== 'children' && props[key] !== undefined) + .sort(); +}; + +const serialize = (element, config, indentation, depth, refs, printer) => + ++depth > config.maxDepth + ? (0, _markup.printElementAsLeaf)(getType(element), config) + : (0, _markup.printElement)( + getType(element), + (0, _markup.printProps)( + getPropKeys(element), + element.props, + config, + indentation + config.indent, + depth, + refs, + printer + ), + (0, _markup.printChildren)( + getChildren(element.props.children), + config, + indentation + config.indent, + depth, + refs, + printer + ), + config, + indentation + ); + +exports.serialize = serialize; + +const test = val => val && ReactIs.isElement(val); + +exports.test = test; +const plugin = { + serialize, + test +}; +var _default = plugin; +exports.default = _default; diff --git a/packages/pretty-format/build/plugins/ReactTestComponent.d.ts b/packages/pretty-format/build/plugins/ReactTestComponent.d.ts new file mode 100644 index 000000000000..91542d9856c3 --- /dev/null +++ b/packages/pretty-format/build/plugins/ReactTestComponent.d.ts @@ -0,0 +1,18 @@ +/** + * 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. + */ +import type { NewPlugin } from '../types'; +export declare type ReactTestObject = { + $$typeof: symbol; + type: string; + props?: Record; + children?: null | Array; +}; +declare type ReactTestChild = ReactTestObject | string | number; +export declare const serialize: NewPlugin['serialize']; +export declare const test: NewPlugin['test']; +declare const plugin: NewPlugin; +export default plugin; diff --git a/packages/pretty-format/build/plugins/ReactTestComponent.js b/packages/pretty-format/build/plugins/ReactTestComponent.js new file mode 100644 index 000000000000..9399f78b0f0c --- /dev/null +++ b/packages/pretty-format/build/plugins/ReactTestComponent.js @@ -0,0 +1,65 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = exports.test = exports.serialize = void 0; + +var _markup = require('./lib/markup'); + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; +const testSymbol = + typeof Symbol === 'function' && Symbol.for + ? Symbol.for('react.test.json') + : 0xea71357; + +const getPropKeys = object => { + const {props} = object; + return props + ? Object.keys(props) + .filter(key => props[key] !== undefined) + .sort() + : []; +}; + +const serialize = (object, config, indentation, depth, refs, printer) => + ++depth > config.maxDepth + ? (0, _markup.printElementAsLeaf)(object.type, config) + : (0, _markup.printElement)( + object.type, + object.props + ? (0, _markup.printProps)( + getPropKeys(object), + object.props, + config, + indentation + config.indent, + depth, + refs, + printer + ) + : '', + object.children + ? (0, _markup.printChildren)( + object.children, + config, + indentation + config.indent, + depth, + refs, + printer + ) + : '', + config, + indentation + ); + +exports.serialize = serialize; + +const test = val => val && val.$$typeof === testSymbol; + +exports.test = test; +const plugin = { + serialize, + test +}; +var _default = plugin; +exports.default = _default; diff --git a/packages/pretty-format/build/plugins/lib/escapeHTML.d.ts b/packages/pretty-format/build/plugins/lib/escapeHTML.d.ts new file mode 100644 index 000000000000..aee0d3d06377 --- /dev/null +++ b/packages/pretty-format/build/plugins/lib/escapeHTML.d.ts @@ -0,0 +1,7 @@ +/** + * 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. + */ +export default function escapeHTML(str: string): string; diff --git a/packages/pretty-format/build/plugins/lib/escapeHTML.js b/packages/pretty-format/build/plugins/lib/escapeHTML.js new file mode 100644 index 000000000000..50dda2590947 --- /dev/null +++ b/packages/pretty-format/build/plugins/lib/escapeHTML.js @@ -0,0 +1,16 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.default = escapeHTML; + +/** + * 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. + */ +function escapeHTML(str) { + return str.replace(//g, '>'); +} diff --git a/packages/pretty-format/build/plugins/lib/markup.d.ts b/packages/pretty-format/build/plugins/lib/markup.d.ts new file mode 100644 index 000000000000..60d99ab90649 --- /dev/null +++ b/packages/pretty-format/build/plugins/lib/markup.d.ts @@ -0,0 +1,13 @@ +/** + * 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. + */ +import type { Config, Printer, Refs } from '../../types'; +export declare const printProps: (keys: Array, props: Record, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer) => string; +export declare const printChildren: (children: Array, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer) => string; +export declare const printText: (text: string, config: Config) => string; +export declare const printComment: (comment: string, config: Config) => string; +export declare const printElement: (type: string, printedProps: string, printedChildren: string, config: Config, indentation: string) => string; +export declare const printElementAsLeaf: (type: string, config: Config) => string; diff --git a/packages/pretty-format/build/plugins/lib/markup.js b/packages/pretty-format/build/plugins/lib/markup.js new file mode 100644 index 000000000000..d47b88f7b138 --- /dev/null +++ b/packages/pretty-format/build/plugins/lib/markup.js @@ -0,0 +1,147 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.printElementAsLeaf = exports.printElement = exports.printComment = exports.printText = exports.printChildren = exports.printProps = void 0; + +var _escapeHTML = _interopRequireDefault(require('./escapeHTML')); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ +// Return empty string if keys is empty. +const printProps = (keys, props, config, indentation, depth, refs, printer) => { + const indentationNext = indentation + config.indent; + const colors = config.colors; + return keys + .map(key => { + const value = props[key]; + let printed = printer(value, config, indentationNext, depth, refs); + + if (typeof value !== 'string') { + if (printed.indexOf('\n') !== -1) { + printed = + config.spacingOuter + + indentationNext + + printed + + config.spacingOuter + + indentation; + } + + printed = '{' + printed + '}'; + } + + return ( + config.spacingInner + + indentation + + colors.prop.open + + key + + colors.prop.close + + '=' + + colors.value.open + + printed + + colors.value.close + ); + }) + .join(''); +}; // Return empty string if children is empty. + +exports.printProps = printProps; + +const printChildren = (children, config, indentation, depth, refs, printer) => + children + .map( + child => + config.spacingOuter + + indentation + + (typeof child === 'string' + ? printText(child, config) + : printer(child, config, indentation, depth, refs)) + ) + .join(''); + +exports.printChildren = printChildren; + +const printText = (text, config) => { + const contentColor = config.colors.content; + return ( + contentColor.open + (0, _escapeHTML.default)(text) + contentColor.close + ); +}; + +exports.printText = printText; + +const printComment = (comment, config) => { + const commentColor = config.colors.comment; + return ( + commentColor.open + + '' + + commentColor.close + ); +}; // Separate the functions to format props, children, and element, +// so a plugin could override a particular function, if needed. +// Too bad, so sad: the traditional (but unnecessary) space +// in a self-closing tagColor requires a second test of printedProps. + +exports.printComment = printComment; + +const printElement = ( + type, + printedProps, + printedChildren, + config, + indentation +) => { + const tagColor = config.colors.tag; + return ( + tagColor.open + + '<' + + type + + (printedProps && + tagColor.close + + printedProps + + config.spacingOuter + + indentation + + tagColor.open) + + (printedChildren + ? '>' + + tagColor.close + + printedChildren + + config.spacingOuter + + indentation + + tagColor.open + + '' + + tagColor.close + ); +}; + +exports.printElement = printElement; + +const printElementAsLeaf = (type, config) => { + const tagColor = config.colors.tag; + return ( + tagColor.open + + '<' + + type + + tagColor.close + + ' …' + + tagColor.open + + ' />' + + tagColor.close + ); +}; + +exports.printElementAsLeaf = printElementAsLeaf; diff --git a/packages/pretty-format/build/types.d.ts b/packages/pretty-format/build/types.d.ts new file mode 100644 index 000000000000..5f1e06dbdc44 --- /dev/null +++ b/packages/pretty-format/build/types.d.ts @@ -0,0 +1,100 @@ +/** + * 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. + */ +export declare type Colors = { + comment: { + close: string; + open: string; + }; + content: { + close: string; + open: string; + }; + prop: { + close: string; + open: string; + }; + tag: { + close: string; + open: string; + }; + value: { + close: string; + open: string; + }; +}; +declare type Indent = (arg0: string) => string; +export declare type Refs = Array; +declare type Print = (arg0: unknown) => string; +export declare type Theme = { + comment: string; + content: string; + prop: string; + tag: string; + value: string; +}; +declare type ThemeReceived = { + comment?: string; + content?: string; + prop?: string; + tag?: string; + value?: string; +}; +export declare type Options = { + callToJSON: boolean; + escapeRegex: boolean; + escapeString: boolean; + highlight: boolean; + indent: number; + maxDepth: number; + min: boolean; + plugins: Plugins; + printFunctionName: boolean; + theme: Theme; +}; +export declare type OptionsReceived = { + callToJSON?: boolean; + escapeRegex?: boolean; + escapeString?: boolean; + highlight?: boolean; + indent?: number; + maxDepth?: number; + min?: boolean; + plugins?: Plugins; + printFunctionName?: boolean; + theme?: ThemeReceived; +}; +export declare type Config = { + callToJSON: boolean; + colors: Colors; + escapeRegex: boolean; + escapeString: boolean; + indent: string; + maxDepth: number; + min: boolean; + plugins: Plugins; + printFunctionName: boolean; + spacingInner: string; + spacingOuter: string; +}; +export declare type Printer = (val: unknown, config: Config, indentation: string, depth: number, refs: Refs, hasCalledToJSON?: boolean) => string; +declare type Test = (arg0: any) => boolean; +export declare type NewPlugin = { + serialize: (val: any, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer) => string; + test: Test; +}; +declare type PluginOptions = { + edgeSpacing: string; + min: boolean; + spacing: string; +}; +export declare type OldPlugin = { + print: (val: unknown, print: Print, indent: Indent, options: PluginOptions, colors: Colors) => string; + test: Test; +}; +export declare type Plugin = NewPlugin | OldPlugin; +export declare type Plugins = Array; +export {}; diff --git a/packages/pretty-format/build/types.js b/packages/pretty-format/build/types.js new file mode 100644 index 000000000000..ad9a93a7c160 --- /dev/null +++ b/packages/pretty-format/build/types.js @@ -0,0 +1 @@ +'use strict'; diff --git a/packages/test-utils/build/ConditionalTest.d.ts b/packages/test-utils/build/ConditionalTest.d.ts new file mode 100644 index 000000000000..6828240a1147 --- /dev/null +++ b/packages/test-utils/build/ConditionalTest.d.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ +export declare function isJestJasmineRun(): boolean; +export declare function skipSuiteOnJasmine(): void; +export declare function skipSuiteOnJestCircus(): void; +export declare function onNodeVersions(versionRange: string, testBody: () => void): void; diff --git a/packages/test-utils/build/ConditionalTest.js b/packages/test-utils/build/ConditionalTest.js new file mode 100644 index 000000000000..26e2e6db4e9d --- /dev/null +++ b/packages/test-utils/build/ConditionalTest.js @@ -0,0 +1,65 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.isJestJasmineRun = isJestJasmineRun; +exports.skipSuiteOnJasmine = skipSuiteOnJasmine; +exports.skipSuiteOnJestCircus = skipSuiteOnJestCircus; +exports.onNodeVersions = onNodeVersions; + +function _semver() { + const data = _interopRequireDefault(require('semver')); + + _semver = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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. + */ + +/* eslint-disable jest/no-focused-tests */ +function isJestJasmineRun() { + return process.env.JEST_JASMINE === '1'; +} + +function skipSuiteOnJasmine() { + if (isJestJasmineRun()) { + test.only('does not work on Jasmine', () => { + console.warn('[SKIP] Does not work on Jasmine'); + }); + } +} + +function skipSuiteOnJestCircus() { + if (!isJestJasmineRun()) { + test.only('does not work on jest-circus', () => { + console.warn('[SKIP] Does not work on jest-circus'); + }); + } +} + +function onNodeVersions(versionRange, testBody) { + const description = `on node ${versionRange}`; + + if (_semver().default.satisfies(process.versions.node, versionRange)) { + describe(description, () => { + testBody(); + }); + } else { + describe.skip(description, () => { + testBody(); + }); + } +} diff --git a/packages/test-utils/build/alignedAnsiStyleSerializer.d.ts b/packages/test-utils/build/alignedAnsiStyleSerializer.d.ts new file mode 100644 index 000000000000..d8f281a5128f --- /dev/null +++ b/packages/test-utils/build/alignedAnsiStyleSerializer.d.ts @@ -0,0 +1,8 @@ +/** + * 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. + */ +import type { NewPlugin } from 'pretty-format'; +export declare const alignedAnsiStyleSerializer: NewPlugin; diff --git a/packages/test-utils/build/alignedAnsiStyleSerializer.js b/packages/test-utils/build/alignedAnsiStyleSerializer.js new file mode 100644 index 000000000000..084432b75ed6 --- /dev/null +++ b/packages/test-utils/build/alignedAnsiStyleSerializer.js @@ -0,0 +1,86 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.alignedAnsiStyleSerializer = void 0; + +function _ansiRegex() { + const data = _interopRequireDefault(require('ansi-regex')); + + _ansiRegex = function () { + return data; + }; + + return data; +} + +function _ansiStyles() { + const data = _interopRequireDefault(require('ansi-styles')); + + _ansiStyles = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +/** + * 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 alignedAnsiStyleSerializer = { + serialize(val) { + // Return the string itself, not escaped nor enclosed in double quote marks. + return val.replace((0, _ansiRegex().default)(), match => { + switch (match) { + case _ansiStyles().default.inverse.open: + return ''; + + case _ansiStyles().default.inverse.close: + return ''; + + case _ansiStyles().default.bold.open: + return ''; + + case _ansiStyles().default.dim.open: + return ''; + + case _ansiStyles().default.green.open: + return ''; + + case _ansiStyles().default.red.open: + return ''; + + case _ansiStyles().default.yellow.open: + return ''; + + case _ansiStyles().default.bgYellow.open: + return ''; + + case _ansiStyles().default.bold.close: + case _ansiStyles().default.dim.close: + case _ansiStyles().default.green.close: + case _ansiStyles().default.red.close: + case _ansiStyles().default.yellow.close: + case _ansiStyles().default.bgYellow.close: + return ''; + + default: + return match; + // unexpected escape sequence + } + }); + }, + + test(val) { + return typeof val === 'string'; + } +}; +exports.alignedAnsiStyleSerializer = alignedAnsiStyleSerializer; diff --git a/packages/test-utils/build/config.d.ts b/packages/test-utils/build/config.d.ts new file mode 100644 index 000000000000..062443e2ea76 --- /dev/null +++ b/packages/test-utils/build/config.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +import type { Config } from '@jest/types'; +export declare const makeGlobalConfig: (overrides?: Partial) => Config.GlobalConfig; +export declare const makeProjectConfig: (overrides?: Partial) => Config.ProjectConfig; diff --git a/packages/test-utils/build/config.js b/packages/test-utils/build/config.js new file mode 100644 index 000000000000..ac1237735099 --- /dev/null +++ b/packages/test-utils/build/config.js @@ -0,0 +1,159 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.makeProjectConfig = exports.makeGlobalConfig = void 0; + +/** + * 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 DEFAULT_GLOBAL_CONFIG = { + bail: 0, + changedFilesWithAncestor: false, + changedSince: '', + collectCoverage: false, + collectCoverageFrom: [], + collectCoverageOnlyFrom: undefined, + coverageDirectory: 'coverage', + coverageProvider: 'babel', + coverageReporters: [], + coverageThreshold: { + global: {} + }, + detectLeaks: false, + detectOpenHandles: false, + errorOnDeprecated: false, + expand: false, + filter: undefined, + findRelatedTests: false, + forceExit: false, + globalSetup: undefined, + globalTeardown: undefined, + json: false, + lastCommit: false, + listTests: false, + logHeapUsage: false, + maxConcurrency: 5, + maxWorkers: 2, + noSCM: undefined, + noStackTrace: false, + nonFlagArgs: [], + notify: false, + notifyMode: 'failure-change', + onlyChanged: false, + onlyFailures: false, + outputFile: undefined, + passWithNoTests: false, + projects: [], + replname: undefined, + reporters: [], + rootDir: '/test_root_dir/', + runTestsByPath: false, + silent: false, + skipFilter: false, + testFailureExitCode: 1, + testNamePattern: '', + testPathPattern: '', + testResultsProcessor: undefined, + testSequencer: '@jest/test-sequencer', + testTimeout: 5000, + updateSnapshot: 'none', + useStderr: false, + verbose: false, + watch: false, + watchAll: false, + watchPlugins: [], + watchman: false +}; +const DEFAULT_PROJECT_CONFIG = { + automock: false, + cache: false, + cacheDirectory: '/test_cache_dir/', + clearMocks: false, + coveragePathIgnorePatterns: [], + cwd: '/test_root_dir/', + detectLeaks: false, + detectOpenHandles: false, + displayName: undefined, + errorOnDeprecated: false, + extensionsToTreatAsEsm: [], + extraGlobals: [], + filter: undefined, + forceCoverageMatch: [], + globalSetup: undefined, + globalTeardown: undefined, + globals: {}, + haste: {}, + injectGlobals: true, + moduleDirectories: [], + moduleFileExtensions: ['js'], + moduleLoader: '/test_module_loader_path', + moduleNameMapper: [], + modulePathIgnorePatterns: [], + modulePaths: [], + name: 'test_name', + prettierPath: 'prettier', + resetMocks: false, + resetModules: false, + resolver: undefined, + restoreMocks: false, + rootDir: '/test_root_dir/', + roots: [], + runner: 'jest-runner', + setupFiles: [], + setupFilesAfterEnv: [], + skipFilter: false, + skipNodeResolution: false, + slowTestThreshold: 5, + snapshotResolver: undefined, + snapshotSerializers: [], + testEnvironment: 'node', + testEnvironmentOptions: {}, + testLocationInResults: false, + testMatch: [], + testPathIgnorePatterns: [], + testRegex: ['\\.test\\.js$'], + testRunner: 'jest-circus/runner', + testURL: 'http://localhost', + timers: 'real', + transform: [], + transformIgnorePatterns: [], + unmockedModulePathPatterns: undefined, + watchPathIgnorePatterns: [] +}; + +const makeGlobalConfig = (overrides = {}) => { + const overridesKeys = new Set(Object.keys(overrides)); + Object.keys(DEFAULT_GLOBAL_CONFIG).forEach(key => overridesKeys.delete(key)); + + if (overridesKeys.size > 0) { + throw new Error(` + Properties that are not part of GlobalConfig type were passed: + ${JSON.stringify(Array.from(overridesKeys))} + `); + } + + return {...DEFAULT_GLOBAL_CONFIG, ...overrides}; +}; + +exports.makeGlobalConfig = makeGlobalConfig; + +const makeProjectConfig = (overrides = {}) => { + const overridesKeys = new Set(Object.keys(overrides)); + Object.keys(DEFAULT_PROJECT_CONFIG).forEach(key => overridesKeys.delete(key)); + + if (overridesKeys.size > 0) { + throw new Error(` + Properties that are not part of ProjectConfig type were passed: + ${JSON.stringify(Array.from(overridesKeys))} + `); + } + + return {...DEFAULT_PROJECT_CONFIG, ...overrides}; +}; + +exports.makeProjectConfig = makeProjectConfig; diff --git a/packages/test-utils/build/index.d.ts b/packages/test-utils/build/index.d.ts new file mode 100644 index 000000000000..c87b80369100 --- /dev/null +++ b/packages/test-utils/build/index.d.ts @@ -0,0 +1,9 @@ +/** + * 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. + */ +export { alignedAnsiStyleSerializer } from './alignedAnsiStyleSerializer'; +export { isJestJasmineRun, skipSuiteOnJasmine, skipSuiteOnJestCircus, onNodeVersions, } from './ConditionalTest'; +export { makeGlobalConfig, makeProjectConfig } from './config'; diff --git a/packages/test-utils/build/index.js b/packages/test-utils/build/index.js new file mode 100644 index 000000000000..5a83cb0f7fe0 --- /dev/null +++ b/packages/test-utils/build/index.js @@ -0,0 +1,53 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +Object.defineProperty(exports, 'alignedAnsiStyleSerializer', { + enumerable: true, + get: function () { + return _alignedAnsiStyleSerializer.alignedAnsiStyleSerializer; + } +}); +Object.defineProperty(exports, 'isJestJasmineRun', { + enumerable: true, + get: function () { + return _ConditionalTest.isJestJasmineRun; + } +}); +Object.defineProperty(exports, 'skipSuiteOnJasmine', { + enumerable: true, + get: function () { + return _ConditionalTest.skipSuiteOnJasmine; + } +}); +Object.defineProperty(exports, 'skipSuiteOnJestCircus', { + enumerable: true, + get: function () { + return _ConditionalTest.skipSuiteOnJestCircus; + } +}); +Object.defineProperty(exports, 'onNodeVersions', { + enumerable: true, + get: function () { + return _ConditionalTest.onNodeVersions; + } +}); +Object.defineProperty(exports, 'makeGlobalConfig', { + enumerable: true, + get: function () { + return _config.makeGlobalConfig; + } +}); +Object.defineProperty(exports, 'makeProjectConfig', { + enumerable: true, + get: function () { + return _config.makeProjectConfig; + } +}); + +var _alignedAnsiStyleSerializer = require('./alignedAnsiStyleSerializer'); + +var _ConditionalTest = require('./ConditionalTest'); + +var _config = require('./config');