diff --git a/CHANGELOG.md b/CHANGELOG.md index 144909b5c314..181243fc7757 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ - `[jest-circus]`: Migrate to TypeScript ([#7916](https://github.com/facebook/jest/pull/7916)) - `[jest-phabricator]`: Migrate to TypeScript ([#7965](https://github.com/facebook/jest/pull/7965)) - `[jest-runner]`: Migrate to TypeScript ([#7968](https://github.com/facebook/jest/pull/7968)) +- `[jest-runtime]`: Migrate to TypeScript ([#7964](https://github.com/facebook/jest/pull/7964)) ### Performance diff --git a/packages/jest-circus/package.json b/packages/jest-circus/package.json index 1986ebe211cf..d137fb0d5273 100644 --- a/packages/jest-circus/package.json +++ b/packages/jest-circus/package.json @@ -11,6 +11,7 @@ "types": "build/index.d.ts", "dependencies": { "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.1.0", "@jest/types": "^24.1.0", "@types/node": "*", "chalk": "^2.0.1", diff --git a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts index 84f32c39e2f4..7064ad335553 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts @@ -6,9 +6,10 @@ */ import path from 'path'; -import {Config, TestResult, Environment} from '@jest/types'; -// @ts-ignore TODO Remove ignore when jest-runtime is migrated to ts -import Runtime from 'jest-runtime'; // eslint-disable-line import/no-extraneous-dependencies +import {Config, TestResult} from '@jest/types'; +import {JestEnvironment} from '@jest/environment'; +// eslint-disable-next-line import/no-extraneous-dependencies +import Runtime from 'jest-runtime'; import {SnapshotState} from 'jest-snapshot'; const FRAMEWORK_INITIALIZER = require.resolve('./jestAdapterInit'); @@ -16,7 +17,7 @@ const FRAMEWORK_INITIALIZER = require.resolve('./jestAdapterInit'); const jestAdapter = async ( globalConfig: Config.GlobalConfig, config: Config.ProjectConfig, - environment: Environment.$JestEnvironment, + environment: JestEnvironment, runtime: Runtime, testPath: string, ): Promise => { diff --git a/packages/jest-circus/tsconfig.json b/packages/jest-circus/tsconfig.json index 3cb4125e6ac7..726c0ac6b1e5 100644 --- a/packages/jest-circus/tsconfig.json +++ b/packages/jest-circus/tsconfig.json @@ -4,5 +4,15 @@ "outDir": "build", "rootDir": "src" }, - "references": [{"path": "../jest-types"}, {"path": "../jest-snapshot"}, {"path": "../jest-matcher-utils"}, {"path": "../jest-message-util"}, {"path": "../jest-util"}, {"path": "../pretty-format"}, {"path": "../jest-diff"}] + "references": [ + {"path": "../jest-diff"}, + {"path": "../jest-environment"}, + {"path": "../jest-matcher-utils"}, + {"path": "../jest-message-util"}, + {"path": "../jest-runtime"}, + {"path": "../jest-snapshot"}, + {"path": "../jest-types"}, + {"path": "../jest-util"}, + {"path": "../pretty-format"} + ] } diff --git a/packages/jest-environment/.npmignore b/packages/jest-environment/.npmignore new file mode 100644 index 000000000000..85e48fe7b0a4 --- /dev/null +++ b/packages/jest-environment/.npmignore @@ -0,0 +1,3 @@ +**/__mocks__/** +**/__tests__/** +src diff --git a/packages/jest-environment/package.json b/packages/jest-environment/package.json new file mode 100644 index 000000000000..d1888425032d --- /dev/null +++ b/packages/jest-environment/package.json @@ -0,0 +1,22 @@ +{ + "name": "@jest/environment", + "version": "24.1.0", + "repository": { + "type": "git", + "url": "https://github.com/facebook/jest.git", + "directory": "packages/jest-environment" + }, + "license": "MIT", + "main": "build/index.js", + "types": "build/index.d.ts", + "dependencies": { + "@jest/transform": "^24.1.0", + "@jest/types": "^24.1.0", + "@types/node": "*", + "jest-mock": "^24.0.0" + }, + "engines": { + "node": ">= 6" + }, + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" +} diff --git a/packages/jest-environment/src/index.ts b/packages/jest-environment/src/index.ts new file mode 100644 index 000000000000..8f9d1e08502e --- /dev/null +++ b/packages/jest-environment/src/index.ts @@ -0,0 +1,260 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All 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 {Script} from 'vm'; +import {Config, Global} from '@jest/types'; +import moduleMocker from 'jest-mock'; +import {ScriptTransformer} from '@jest/transform'; + +export type EnvironmentContext = { + console?: Console; + testPath?: Config.Path; +}; + +// TODO: type this better: https://nodejs.org/api/modules.html#modules_the_module_wrapper +type ModuleWrapper = (...args: Array) => unknown; + +export interface JestEnvironment { + new ( + config: Config.ProjectConfig, + context?: EnvironmentContext, + ): JestEnvironment; + runScript( + script: Script, + ): {[ScriptTransformer.EVAL_RESULT_VARIABLE]: ModuleWrapper} | null; + global: Global.Global; + // TODO: When `jest-util` is ESM, this can just be `fakeTimers: import('jest-util').FakeTimers` + fakeTimers: { + clearAllTimers(): void; + runAllImmediates(): void; + runAllTicks(): void; + runAllTimers(): void; + advanceTimersByTime(msToRun: number): void; + runOnlyPendingTimers(): void; + runWithRealTimers(callback: () => void): void; + getTimerCount(): number; + useFakeTimers(): void; + useRealTimers(): void; + }; + testFilePath: Config.Path; + moduleMocker: typeof moduleMocker; + setup(): Promise; + teardown(): Promise; +} + +export type Module = typeof module; + +export interface LocalModuleRequire extends NodeRequire { + requireActual(moduleName: string): unknown; + requireMock(moduleName: string): unknown; +} + +// TODO: Move to some separate package +export interface Jest { + /** + * Provides a way to add Jasmine-compatible matchers into your Jest context. + * + * @deprecated Use `expect.extend` instead + */ + addMatchers(matchers: Object): 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 moduleMocker.fn; + /** + * 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. + */ + genMockFromModule(moduleName: string): unknown; + /** + * Determines if the given function is a mocked function. + */ + isMockFunction(fn: Function): 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. + */ + 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. + * + * @deprecated Use `jest.resetModules()` + */ + resetModuleRegistry(): 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 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(). + */ + 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; + /** + * Executes only the macro task queue (i.e. all tasks queued by setTimeout() + * or setInterval() and setImmediate()). + * + * @deprecated Use `jest.advanceTimersByTime()` + */ + runTimersToTime(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 moduleMocker.spyOn; + /** + * 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(): 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; +} diff --git a/packages/jest-environment/tsconfig.json b/packages/jest-environment/tsconfig.json new file mode 100644 index 000000000000..8824863bd93b --- /dev/null +++ b/packages/jest-environment/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../jest-transform"}, + {"path": "../jest-types"}, + {"path": "../jest-util"} + ] +} diff --git a/packages/jest-resolve/src/index.ts b/packages/jest-resolve/src/index.ts index c08f54cb9eab..12ccc4fe0e22 100644 --- a/packages/jest-resolve/src/index.ts +++ b/packages/jest-resolve/src/index.ts @@ -21,7 +21,7 @@ type FindNodeModuleConfig = { extensions?: Array; moduleDirectory?: Array; paths?: Array; - resolver?: Config.Path; + resolver?: Config.Path | null; rootDir?: Config.Path; }; @@ -403,7 +403,7 @@ const createNoMappedModuleFoundError = ( updatedName: string, mappedModuleName: string, regex: RegExp, - resolver: Function | string, + resolver?: Function | string | null, ) => { const error = new Error( chalk.red(`${chalk.bold('Configuration error')}: diff --git a/packages/jest-resolve/src/types.ts b/packages/jest-resolve/src/types.ts index b765f0333107..1c65ac50af57 100644 --- a/packages/jest-resolve/src/types.ts +++ b/packages/jest-resolve/src/types.ts @@ -9,14 +9,14 @@ import {Config} from '@jest/types'; export type ResolverConfig = { browser?: boolean; - defaultPlatform?: string; + defaultPlatform?: string | null; extensions: Array; hasCoreModules: boolean; moduleDirectories: Array; - moduleNameMapper?: Array; + moduleNameMapper?: Array | null; modulePaths: Array; platforms?: Array; - resolver: Config.Path; + resolver?: Config.Path | null; rootDir: Config.Path; }; diff --git a/packages/jest-runner/package.json b/packages/jest-runner/package.json index 67cd2369cb63..bb54f544ac33 100644 --- a/packages/jest-runner/package.json +++ b/packages/jest-runner/package.json @@ -10,6 +10,7 @@ "main": "build/index.js", "types": "build/index.d.ts", "dependencies": { + "@jest/environment": "^24.1.0", "@jest/types": "^24.1.0", "chalk": "^2.4.2", "exit": "^0.1.2", diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 7b581863bb2a..94c759a5188f 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -6,13 +6,8 @@ * */ -import { - Environment, - Config, - TestResult, - Console as ConsoleType, -} from '@jest/types'; -// @ts-ignore: not migrated to TS +import {Config, TestResult, Console as ConsoleType} from '@jest/types'; +import {JestEnvironment} from '@jest/environment'; import RuntimeClass from 'jest-runtime'; import fs from 'graceful-fs'; import { @@ -101,16 +96,16 @@ async function runTestInternal( }); } - const TestEnvironment: Environment.EnvironmentClass = require(testEnvironment); + const TestEnvironment: JestEnvironment = require(testEnvironment); const testFramework: TestFramework = process.env.JEST_CIRCUS === '1' ? require('jest-circus/runner') // eslint-disable-line import/no-extraneous-dependencies : require(config.testRunner); - const Runtime: RuntimeClass = config.moduleLoader + const Runtime: typeof RuntimeClass = config.moduleLoader ? require(config.moduleLoader) : require('jest-runtime'); - let runtime: RuntimeClass = undefined; + let runtime: RuntimeClass | undefined = undefined; const consoleOut = globalConfig.useStderr ? process.stderr : process.stdout; const consoleFormatter = ( diff --git a/packages/jest-runner/src/testWorker.ts b/packages/jest-runner/src/testWorker.ts index 51c8fbc3dd1c..fed1e1e4a669 100644 --- a/packages/jest-runner/src/testWorker.ts +++ b/packages/jest-runner/src/testWorker.ts @@ -10,7 +10,6 @@ import {Config, TestResult} from '@jest/types'; import HasteMap, {SerializableModuleMap, ModuleMap} from 'jest-haste-map'; import exit from 'exit'; import {separateMessageFromStack} from 'jest-message-util'; -// @ts-ignore: Not migrated to TS import Runtime from 'jest-runtime'; import {ErrorWithCode, TestRunnerContext} from './types'; import runTest from './runTest'; diff --git a/packages/jest-runner/src/types.ts b/packages/jest-runner/src/types.ts index 5f1aeb7c557a..86f26d086fa2 100644 --- a/packages/jest-runner/src/types.ts +++ b/packages/jest-runner/src/types.ts @@ -6,10 +6,10 @@ */ import {EventEmitter} from 'events'; -import {Environment, Config, TestResult} from '@jest/types'; +import {Config, TestResult} from '@jest/types'; +import {JestEnvironment} from '@jest/environment'; import {ModuleMap, FS as HasteFS} from 'jest-haste-map'; import HasteResolver from 'jest-resolve'; -// @ts-ignore: not migrated to TS import Runtime from 'jest-runtime'; export type ErrorWithCode = Error & {code?: string}; @@ -63,7 +63,7 @@ export type Reporter = { export type TestFramework = ( globalConfig: Config.GlobalConfig, config: Config.ProjectConfig, - environment: Environment.Environment, + environment: JestEnvironment, runtime: Runtime, testPath: string, ) => Promise; diff --git a/packages/jest-runner/tsconfig.json b/packages/jest-runner/tsconfig.json index 59665ff7c74e..72bf161339bc 100644 --- a/packages/jest-runner/tsconfig.json +++ b/packages/jest-runner/tsconfig.json @@ -6,10 +6,12 @@ }, "references": [ {"path": "../jest-docblock"}, + {"path": "../jest-environment"}, {"path": "../jest-haste-map"}, {"path": "../jest-leak-detector"}, {"path": "../jest-message-util"}, {"path": "../jest-resolve"}, + {"path": "../jest-runtime"}, {"path": "../jest-types"}, {"path": "../jest-worker"}, {"path": "../jest-util"} diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index a4890ce50f79..f2f36459e08b 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -8,8 +8,12 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { + "@jest/environment": "^24.1.0", "@jest/transform": "^24.1.0", + "@jest/types": "^24.1.0", + "@types/yargs": "^12.0.2", "chalk": "^2.0.1", "exit": "^0.1.2", "glob": "^7.1.3", @@ -17,6 +21,7 @@ "jest-config": "^24.1.0", "jest-haste-map": "^24.0.0", "jest-message-util": "^24.0.0", + "jest-mock": "^24.0.0", "jest-regex-util": "^24.0.0", "jest-resolve": "^24.1.0", "jest-snapshot": "^24.1.0", @@ -33,7 +38,6 @@ "@types/graceful-fs": "^4.1.2", "@types/slash": "^2.0.0", "@types/strip-bom": "^3.0.0", - "@types/yargs": "^12.0.2", "jest-environment-node": "^24.0.0" }, "bin": { diff --git a/packages/jest-runtime/src/cli/args.js b/packages/jest-runtime/src/cli/args.ts similarity index 87% rename from packages/jest-runtime/src/cli/args.js rename to packages/jest-runtime/src/cli/args.ts index cce2e3cecda3..7af5ba6c24f5 100644 --- a/packages/jest-runtime/src/cli/args.js +++ b/packages/jest-runtime/src/cli/args.ts @@ -3,13 +3,16 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ +import {Options} from 'yargs'; + export const usage = 'Usage: $0 [--config=] '; -export const options = { +export const options: Record< + 'cache' | 'config' | 'debug' | 'version' | 'watchman', + Options +> = { cache: { default: true, description: diff --git a/packages/jest-runtime/src/cli/index.js b/packages/jest-runtime/src/cli/index.ts similarity index 79% rename from packages/jest-runtime/src/cli/index.js rename to packages/jest-runtime/src/cli/index.ts index 2662efe5407b..cb4527e312ec 100644 --- a/packages/jest-runtime/src/cli/index.js +++ b/packages/jest-runtime/src/cli/index.ts @@ -3,26 +3,25 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Argv} from 'types/Argv'; -import type {EnvironmentClass} from 'types/Environment'; - -import chalk from 'chalk'; import os from 'os'; import path from 'path'; +import chalk from 'chalk'; import {sync as realpath} from 'realpath-native'; import yargs from 'yargs'; +import {Config} from '@jest/types'; +import {JestEnvironment} from '@jest/environment'; import {Console, setGlobal} from 'jest-util'; +// @ts-ignore: Not migrated to TS import {validateCLIOptions} from 'jest-validate'; +// @ts-ignore: Not migrated to TS import {readConfig, deprecationEntries} from 'jest-config'; +import {VERSION} from '../version'; +import {Context} from '../types'; import * as args from './args'; -const VERSION = (require('../../package.json').version: string); - -export function run(cliArgv?: Argv, cliInfo?: Array) { +export function run(cliArgv?: Config.Argv, cliInfo?: Array) { const realFs = require('fs'); const fs = require('graceful-fs'); fs.gracefulify(realFs); @@ -74,23 +73,22 @@ export function run(cliArgv?: Argv, cliInfo?: Array) { }; // Break circular dependency - const Runtime = require('..'); + const Runtime: any = require('..'); - Runtime.createContext(config, { + (Runtime.createContext(config, { maxWorkers: Math.max(os.cpus().length - 1, 1), watchman: globalConfig.watchman, - }) + }) as Promise) .then(hasteMap => { - /* $FlowFixMe */ - const Environment = (require(config.testEnvironment): EnvironmentClass); + const Environment: JestEnvironment = require(config.testEnvironment); const environment = new Environment(config); setGlobal( environment.global, 'console', new Console(process.stdout, process.stderr), ); - environment.global.jestProjectConfig = config; - environment.global.jestGlobalConfig = globalConfig; + setGlobal(environment.global, 'jestProjectConfig', config); + setGlobal(environment.global, 'jestGlobalConfig', globalConfig); const runtime = new Runtime(config, environment, hasteMap.resolver); runtime.requireModule(filePath); diff --git a/packages/jest-runtime/src/helpers.js b/packages/jest-runtime/src/helpers.ts similarity index 91% rename from packages/jest-runtime/src/helpers.js rename to packages/jest-runtime/src/helpers.ts index 19265d836044..5b12d3aeb3aa 100644 --- a/packages/jest-runtime/src/helpers.js +++ b/packages/jest-runtime/src/helpers.ts @@ -1,16 +1,13 @@ // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. -// @flow - -import type {Path} from 'types/Config'; - import path from 'path'; import slash from 'slash'; import glob from 'glob'; +import {Config} from '@jest/types'; export const findSiblingsWithFileExtension = ( - moduleFileExtensions: Array, - from: Path, + moduleFileExtensions: Config.ProjectConfig['moduleFileExtensions'], + from: Config.Path, moduleName: string, ): string => { if (!path.isAbsolute(moduleName) && path.extname(moduleName) === '') { diff --git a/packages/jest-runtime/src/index.js b/packages/jest-runtime/src/index.ts similarity index 79% rename from packages/jest-runtime/src/index.js rename to packages/jest-runtime/src/index.ts index e6f393eef733..6c8849a792ee 100644 --- a/packages/jest-runtime/src/index.js +++ b/packages/jest-runtime/src/index.ts @@ -3,72 +3,61 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Console} from 'console'; -import type {Argv} from 'types/Argv'; -import type {Glob, Path, ProjectConfig} from 'types/Config'; -import type {Environment} from 'types/Environment'; -import type {Context} from 'types/Context'; -import type {Jest, LocalModuleRequire} from 'types/Jest'; -import type {ModuleMap} from 'jest-haste-map'; -import type {MockFunctionMetadata, ModuleMocker} from 'types/Mock'; -import type {SourceMapRegistry} from 'types/SourceMaps'; - import path from 'path'; -import HasteMap from 'jest-haste-map'; +import {Config, SourceMaps} from '@jest/types'; +import { + Jest, + JestEnvironment, + LocalModuleRequire, + Module, +} from '@jest/environment'; +import jestMock, {MockFunctionMetadata} from 'jest-mock'; +import HasteMap, {ModuleMap} from 'jest-haste-map'; import {formatStackTrace, separateMessageFromStack} from 'jest-message-util'; import Resolver from 'jest-resolve'; import {createDirectory, deepCyclicCopy} from 'jest-util'; import {escapePathForRegex} from 'jest-regex-util'; import Snapshot from 'jest-snapshot'; -import {ScriptTransformer, shouldInstrument} from '@jest/transform'; +import { + ScriptTransformer, + ShouldInstrumentOptions, + shouldInstrument, +} from '@jest/transform'; import fs from 'graceful-fs'; import stripBOM from 'strip-bom'; import {run as cliRun} from './cli'; import {options as cliOptions} from './cli/args'; import {findSiblingsWithFileExtension} from './helpers'; +import {Context} from './types'; + +type HasteMapOptions = { + console?: Console; + maxWorkers: number; + resetCache: boolean; + watch?: boolean; + watchman: boolean; +}; -type Module = {| - children: Array, - exports: any, - filename: string, - id: string, - loaded: boolean, - parent?: Module, - paths?: Array, - require?: (id: string) => any, -|}; - -type HasteMapOptions = {| - console?: Console, - maxWorkers: number, - resetCache: boolean, - watch?: boolean, - watchman: boolean, -|}; - -type InternalModuleOptions = {| - isInternalModule: boolean, -|}; - -type CoverageOptions = { - changedFiles: ?Set, - collectCoverage: boolean, - collectCoverageFrom: Array, - collectCoverageOnlyFrom: ?{[key: string]: boolean, __proto__: null}, +type InternalModuleOptions = { + isInternalModule: boolean; }; -type ModuleRegistry = {[key: string]: Module, __proto__: null}; +type InitialModule = Partial & + Pick; +type ModuleRegistry = {[key: string]: InitialModule | Module}; +type ResolveOptions = Parameters[1]; + +type BooleanObject = {[key: string]: boolean}; +type CacheFS = {[path: string]: string}; -type BooleanObject = {[key: string]: boolean, __proto__: null}; -type CacheFS = {[path: Path]: string, __proto__: null}; +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: ProjectConfig) => { +const getModuleNameMapper = (config: Config.ProjectConfig) => { if ( Array.isArray(config.moduleNameMapper) && config.moduleNameMapper.length @@ -84,45 +73,47 @@ const getModuleNameMapper = (config: ProjectConfig) => { const unmockRegExpCache = new WeakMap(); class Runtime { - static ScriptTransformer: Class; - - _cacheFS: CacheFS; - _config: ProjectConfig; - _coverageOptions: CoverageOptions; - _currentlyExecutingModulePath: string; - _environment: Environment; - _explicitShouldMock: BooleanObject; - _internalModuleRegistry: ModuleRegistry; - _isCurrentlyExecutingManualMock: ?string; - _mockFactories: {[key: string]: () => any, __proto__: null}; - _mockMetaDataCache: {[key: string]: MockFunctionMetadata, __proto__: null}; - _mockRegistry: {[key: string]: any, __proto__: null}; - _isolatedMockRegistry: ?{[key: string]: any, __proto__: null}; - _moduleMocker: ModuleMocker; - _isolatedModuleRegistry: ?ModuleRegistry; - _moduleRegistry: ModuleRegistry; - _needsCoverageMapped: Set; - _resolver: Resolver; - _shouldAutoMock: boolean; - _shouldMockModuleCache: BooleanObject; - _shouldUnmockTransitiveDependenciesCache: BooleanObject; - _sourceMapRegistry: SourceMapRegistry; - _scriptTransformer: ScriptTransformer; - _transitiveShouldMock: BooleanObject; - _unmockList: ?RegExp; - _virtualMocks: BooleanObject; + static ScriptTransformer: typeof ScriptTransformer; + + private _cacheFS: CacheFS; + private _config: Config.ProjectConfig; + private _coverageOptions: ShouldInstrumentOptions; + private _currentlyExecutingModulePath: string; + private _environment: JestEnvironment; + private _explicitShouldMock: BooleanObject; + private _internalModuleRegistry: ModuleRegistry; + private _isCurrentlyExecutingManualMock: string | null; + private _mockFactories: {[key: string]: () => unknown}; + private _mockMetaDataCache: { + [key: string]: MockFunctionMetadata; + }; + private _mockRegistry: {[key: string]: any}; + private _isolatedMockRegistry: {[key: string]: any} | null; + private _moduleMocker: typeof jestMock; + private _isolatedModuleRegistry: ModuleRegistry | null; + private _moduleRegistry: ModuleRegistry; + private _needsCoverageMapped: Set; + private _resolver: Resolver; + private _shouldAutoMock: boolean; + private _shouldMockModuleCache: BooleanObject; + private _shouldUnmockTransitiveDependenciesCache: BooleanObject; + private _sourceMapRegistry: SourceMaps.SourceMapRegistry; + private _scriptTransformer: ScriptTransformer; + private _transitiveShouldMock: BooleanObject; + private _unmockList: RegExp | undefined; + private _virtualMocks: BooleanObject; constructor( - config: ProjectConfig, - environment: Environment, + config: Config.ProjectConfig, + environment: JestEnvironment, resolver: Resolver, cacheFS?: CacheFS, - coverageOptions?: CoverageOptions, + coverageOptions?: ShouldInstrumentOptions, ) { this._cacheFS = cacheFS || Object.create(null); this._config = config; this._coverageOptions = coverageOptions || { - changedFiles: null, + changedFiles: undefined, collectCoverage: false, collectCoverageFrom: [], collectCoverageOnlyFrom: null, @@ -136,6 +127,7 @@ class Runtime { this._mockRegistry = Object.create(null); this._moduleMocker = this._environment.moduleMocker; this._isolatedModuleRegistry = null; + this._isolatedMockRegistry = null; this._moduleRegistry = Object.create(null); this._needsCoverageMapped = new Set(); this._resolver = resolver; @@ -178,30 +170,22 @@ class Runtime { } } + // TODO: Can this be `static shouldInstrument = shouldInstrument;`? static shouldInstrument( - filename: Path, - options: CoverageOptions, - config: ProjectConfig, + filename: Config.Path, + options: ShouldInstrumentOptions, + config: Config.ProjectConfig, ) { - return shouldInstrument( - filename, - { - changedFiles: options.changedFiles, - collectCoverage: options.collectCoverage, - collectCoverageFrom: options.collectCoverageFrom, - collectCoverageOnlyFrom: options.collectCoverageOnlyFrom, - }, - config, - ); + return shouldInstrument(filename, options, config); } static createContext( - config: ProjectConfig, + config: Config.ProjectConfig, options: { - console?: Console, - maxWorkers: number, - watch?: boolean, - watchman: boolean, + console?: Console; + maxWorkers: number; + watch?: boolean; + watchman: boolean; }, ): Promise { createDirectory(config.cacheDirectory); @@ -226,7 +210,7 @@ class Runtime { } static createHasteMap( - config: ProjectConfig, + config: Config.ProjectConfig, options?: HasteMapOptions, ): HasteMap { const ignorePatternParts = [ @@ -238,7 +222,7 @@ class Runtime { const ignorePattern = ignorePatternParts.length > 0 ? new RegExp(ignorePatternParts.join('|')) - : null; + : undefined; return new HasteMap({ cacheDirectory: config.cacheDirectory, @@ -262,7 +246,10 @@ class Runtime { }); } - static createResolver(config: ProjectConfig, moduleMap: ModuleMap): Resolver { + static createResolver( + config: Config.ProjectConfig, + moduleMap: ModuleMap, + ): Resolver { return new Resolver(moduleMap, { browser: config.browser, defaultPlatform: config.haste.defaultPlatform, @@ -277,7 +264,7 @@ class Runtime { }); } - static runCLI(args?: Argv, info?: Array) { + static runCLI(args?: Config.Argv, info?: Array) { return cliRun(args, info); } @@ -286,10 +273,10 @@ class Runtime { } requireModule( - from: Path, + from: Config.Path, moduleName?: string, - options: ?InternalModuleOptions, - isRequireActual: ?boolean, + options?: InternalModuleOptions, + isRequireActual?: boolean | null, ) { const moduleID = this._resolver.getModuleID( this._virtualMocks, @@ -338,7 +325,7 @@ class Runtime { // 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: Module = { + const localModule: InitialModule = { children: [], exports: {}, filename: modulePath, @@ -351,7 +338,6 @@ class Runtime { stripBOM(fs.readFileSync(modulePath, 'utf8')), ); } else if (path.extname(modulePath) === '.node') { - // $FlowFixMe localModule.exports = require(modulePath); } else { // Only include the fromPath if a moduleName is given. Else treat as root. @@ -364,15 +350,15 @@ class Runtime { return moduleRegistry[modulePath].exports; } - requireInternalModule(from: Path, to?: string) { + requireInternalModule(from: Config.Path, to?: string) { return this.requireModule(from, to, {isInternalModule: true}); } - requireActual(from: Path, moduleName: string) { + requireActual(from: Config.Path, moduleName: string) { return this.requireModule(from, moduleName, undefined, true); } - requireMock(from: Path, moduleName: string) { + requireMock(from: Config.Path, moduleName: string) { const moduleID = this._resolver.getModuleID( this._virtualMocks, from, @@ -428,7 +414,7 @@ class Runtime { } } if (isManualMock) { - const localModule: Module = { + const localModule: InitialModule = { children: [], exports: {}, filename: modulePath, @@ -449,7 +435,7 @@ class Runtime { return mockRegistry[moduleID]; } - requireModuleOrMock(from: Path, moduleName: string) { + requireModuleOrMock(from: Config.Path, moduleName: string) { try { if (this._shouldMock(from, moduleName)) { return this.requireMock(from, moduleName); @@ -494,13 +480,14 @@ class Runtime { if (this._environment) { if (this._environment.global) { const envGlobal = this._environment.global; - Object.keys(envGlobal).forEach(key => { + (Object.keys(envGlobal) as Array).forEach(key => { const globalMock = envGlobal[key]; if ( - (typeof globalMock === 'object' && globalMock !== null) || - typeof globalMock === 'function' + ((typeof globalMock === 'object' && globalMock !== null) || + typeof globalMock === 'function') && + globalMock._isMockFunction === true ) { - globalMock._isMockFunction === true && globalMock.mockClear(); + globalMock.mockClear(); } }); } @@ -516,7 +503,9 @@ class Runtime { } getSourceMapInfo(coveredFiles: Set) { - return Object.keys(this._sourceMapRegistry).reduce((result, sourcePath) => { + return Object.keys(this._sourceMapRegistry).reduce<{ + [path: string]: string; + }>((result, sourcePath) => { if ( coveredFiles.has(sourcePath) && this._needsCoverageMapped.has(sourcePath) && @@ -528,15 +517,15 @@ class Runtime { }, {}); } - getSourceMaps(): SourceMapRegistry { + getSourceMaps(): SourceMaps.SourceMapRegistry { return this._sourceMapRegistry; } setMock( from: string, moduleName: string, - mockFactory: () => any, - options?: {virtual: boolean}, + mockFactory: () => unknown, + options?: {virtual?: boolean}, ) { if (options && options.virtual) { const mockPath = this._resolver.getModulePath(from, moduleName); @@ -563,14 +552,14 @@ class Runtime { this._moduleMocker.clearAllMocks(); } - _resolveModule(from: Path, to?: ?string) { + private _resolveModule(from: Config.Path, to?: string) { return to ? this._resolver.resolveModule(from, to) : from; } - _requireResolve( - from: Path, + private _requireResolve( + from: Config.Path, moduleName?: string, - {paths}: {paths?: Path[]} = {}, + options: ResolveOptions = {}, ) { if (moduleName == null) { throw new Error( @@ -578,6 +567,8 @@ class Runtime { ); } + const {paths} = options; + if (paths) { for (const p of paths) { const absolutePath = path.resolve(from, '..', p); @@ -610,7 +601,7 @@ class Runtime { } } - _requireResolvePaths(from: Path, moduleName?: string) { + private _requireResolvePaths(from: Config.Path, moduleName?: string) { if (moduleName == null) { throw new Error( 'The first argument to require.resolve.paths must be a string. Received null or undefined.', @@ -631,11 +622,11 @@ class Runtime { return this._resolver.getModulePaths(path.resolve(from, '..')); } - _execModule( - localModule: Module, - options: ?InternalModuleOptions, + private _execModule( + localModule: InitialModule, + options: InternalModuleOptions | undefined, moduleRegistry: ModuleRegistry, - from: ?Path, + from: Config.Path | null, ) { // If the environment was disposed, prevent this module from being executed. if (!this._environment.global) { @@ -652,18 +643,13 @@ class Runtime { const dirname = path.dirname(filename); localModule.children = []; - Object.defineProperty( - localModule, - 'parent', - // https://github.com/facebook/flow/issues/285#issuecomment-270810619 - ({ - enumerable: true, - get() { - const key = from || ''; - return moduleRegistry[key] || null; - }, - }: Object), - ); + Object.defineProperty(localModule, 'parent', { + enumerable: true, + get() { + const key = from || ''; + return moduleRegistry[key] || null; + }, + }); localModule.paths = this._resolver.getModulePaths(dirname); Object.defineProperty(localModule, 'require', { @@ -710,8 +696,7 @@ class Runtime { this._environment.global, // global object this._createJestObjectFor( filename, - // $FlowFixMe - (localModule.require: LocalModuleRequire), + localModule.require as LocalModuleRequire, ), // jest object ...extraGlobals.map(globalVariable => { if (this._environment.global[globalVariable]) { @@ -729,25 +714,24 @@ class Runtime { this._currentlyExecutingModulePath = lastExecutingModulePath; } - _requireCoreModule(moduleName: string) { + private _requireCoreModule(moduleName: string) { if (moduleName === 'process') { return this._environment.global.process; } - // $FlowFixMe return require(moduleName); } - _generateMock(from: Path, moduleName: string) { + private _generateMock(from: Config.Path, moduleName: string) { const modulePath = this._resolver.resolveStubModuleName(from, moduleName) || this._resolveModule(from, moduleName); if (!(modulePath in this._mockMetaDataCache)) { // This allows us to handle circular dependencies while generating an // automock - this._mockMetaDataCache[modulePath] = (this._moduleMocker.getMetadata( + this._mockMetaDataCache[modulePath] = this._moduleMocker.getMetadata( {}, - ): any); + ) as any; // In order to avoid it being possible for automocking to potentially // cause side-effects within the module environment, we need to execute @@ -778,7 +762,7 @@ class Runtime { ); } - _shouldMock(from: Path, moduleName: string) { + private _shouldMock(from: Config.Path, moduleName: string) { const mockPath = this._resolver.getModulePath(from, moduleName); if (mockPath in this._virtualMocks) { return true; @@ -845,45 +829,50 @@ class Runtime { return (this._shouldMockModuleCache[moduleID] = true); } - _createRequireImplementation( - from: Module, - options: ?InternalModuleOptions, + private _createRequireImplementation( + from: InitialModule, + options?: InternalModuleOptions, ): LocalModuleRequire { - const moduleRequire = - options && options.isInternalModule - ? (moduleName: string) => - this.requireInternalModule(from.filename, moduleName) - : this.requireModuleOrMock.bind(this, from.filename); + // TODO: somehow avoid having to type the arguments - they should come from `NodeRequire/LocalModuleRequire.resolve` + const resolve = (moduleName: string, options: ResolveOptions) => + this._requireResolve(from.filename, moduleName, options); + resolve.paths = (moduleName: string) => + this._requireResolvePaths(from.filename, moduleName); + + const moduleRequire = (options && options.isInternalModule + ? (moduleName: string) => + this.requireInternalModule(from.filename, moduleName) + : this.requireModuleOrMock.bind( + this, + from.filename, + )) as LocalModuleRequire; moduleRequire.cache = Object.create(null); moduleRequire.extensions = Object.create(null); moduleRequire.requireActual = this.requireActual.bind(this, from.filename); moduleRequire.requireMock = this.requireMock.bind(this, from.filename); - moduleRequire.resolve = (moduleName, options) => - this._requireResolve(from.filename, moduleName, options); - moduleRequire.resolve.paths = moduleName => - this._requireResolvePaths(from.filename, moduleName); - Object.defineProperty( - moduleRequire, - 'main', - ({ - enumerable: true, - get() { - let mainModule = from.parent; - while ( - mainModule && - mainModule.parent && - mainModule.id !== mainModule.parent.id - ) { - mainModule = mainModule.parent; - } - return mainModule; - }, - }: Object), - ); + moduleRequire.resolve = resolve; + + Object.defineProperty(moduleRequire, 'main', { + enumerable: true, + get() { + let mainModule = from.parent; + while ( + mainModule && + mainModule.parent && + mainModule.id !== mainModule.parent.id + ) { + mainModule = mainModule.parent; + } + return mainModule; + }, + }); return moduleRequire; } - _createJestObjectFor(from: Path, localRequire: LocalModuleRequire): Jest { + private _createJestObjectFor( + from: Config.Path, + localRequire: LocalModuleRequire, + ): Jest { const disableAutomock = () => { this._shouldAutoMock = false; return jestObject; @@ -911,11 +900,7 @@ class Runtime { this._transitiveShouldMock[moduleID] = false; return jestObject; }; - const mock = ( - moduleName: string, - mockFactory?: Object, - options?: {virtual: boolean}, - ) => { + const mock: Jest['mock'] = (moduleName, mockFactory, options) => { if (mockFactory !== undefined) { return setMockFactory(moduleName, mockFactory, options); } @@ -928,7 +913,11 @@ class Runtime { this._explicitShouldMock[moduleID] = true; return jestObject; }; - const setMockFactory = (moduleName, mockFactory, options) => { + const setMockFactory = ( + moduleName: string, + mockFactory: () => unknown, + options?: {virtual?: boolean}, + ) => { this.setMock(from, moduleName, mockFactory, options); return jestObject; }; @@ -964,16 +953,18 @@ class Runtime { const spyOn = this._moduleMocker.spyOn.bind(this._moduleMocker); const setTimeout = (timeout: number) => { - this._environment.global.jasmine - ? (this._environment.global.jasmine._DEFAULT_TIMEOUT_INTERVAL = timeout) - : (this._environment.global[ - Symbol.for('TEST_TIMEOUT_SYMBOL') - ] = timeout); + if (this._environment.global.jasmine) { + this._environment.global.jasmine._DEFAULT_TIMEOUT_INTERVAL = timeout; + } else { + // @ts-ignore: https://github.com/Microsoft/TypeScript/issues/24587 + this._environment.global[testTimeoutSymbol] = timeout; + } return jestObject; }; const retryTimes = (numTestRetries: number) => { - this._environment.global[Symbol.for('RETRY_TIMES')] = numTestRetries; + // @ts-ignore: https://github.com/Microsoft/TypeScript/issues/24587 + this._environment.global[retryTimesSymbol] = numTestRetries; return jestObject; }; @@ -988,7 +979,7 @@ class Runtime { return this._environment.fakeTimers; }; - const jestObject = { + const jestObject: Jest = { addMatchers: (matchers: Object) => this._environment.global.jasmine.addMatchers(matchers), advanceTimersByTime: (msToRun: number) => @@ -1022,7 +1013,7 @@ class Runtime { runOnlyPendingTimers: () => _getFakeTimers().runOnlyPendingTimers(), runTimersToTime: (msToRun: number) => _getFakeTimers().advanceTimersByTime(msToRun), - setMock: (moduleName: string, mock: Object) => + setMock: (moduleName: string, mock: unknown) => setMockFactory(moduleName, () => mock), setTimeout, spyOn, @@ -1034,8 +1025,8 @@ class Runtime { } _logFormattedReferenceError(errorMessage: string) { - const originalStack = new ReferenceError(errorMessage).stack - .split('\n') + 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'); @@ -1051,4 +1042,4 @@ class Runtime { Runtime.ScriptTransformer = ScriptTransformer; -module.exports = Runtime; +export = Runtime; diff --git a/packages/jest-runtime/src/types.ts b/packages/jest-runtime/src/types.ts new file mode 100644 index 000000000000..954bcf8d6e71 --- /dev/null +++ b/packages/jest-runtime/src/types.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 {Config} from '@jest/types'; +import HasteResolver from 'jest-resolve'; +import {FS as HasteFS, ModuleMap} from 'jest-haste-map'; + +export type Context = { + config: Config.ProjectConfig; + hasteFS: HasteFS; + moduleMap: ModuleMap; + resolver: HasteResolver; +}; diff --git a/packages/jest-runtime/src/version.ts b/packages/jest-runtime/src/version.ts new file mode 100644 index 000000000000..6c68483e5054 --- /dev/null +++ b/packages/jest-runtime/src/version.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. + */ + +// For some reason, doing `require`ing here works, while inside `cli` fails +export const VERSION: string = require('../package.json').version; diff --git a/packages/jest-runtime/tsconfig.json b/packages/jest-runtime/tsconfig.json new file mode 100644 index 000000000000..b73127d8e1fd --- /dev/null +++ b/packages/jest-runtime/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + // TODO: Missing `jest-config`, `jest-validate` and `jest-environment-node` + "references": [ + {"path": "../jest-environment"}, + {"path": "../jest-haste-map"}, + {"path": "../jest-message-util"}, + {"path": "../jest-mock"}, + {"path": "../jest-regex-util"}, + {"path": "../jest-resolve"}, + {"path": "../jest-snapshot"}, + {"path": "../jest-util"}, + {"path": "../jest-types"}, + {"path": "../pretty-format"} + ] +} diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index 3d92b9f94cee..2fbf03a78ecf 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -50,7 +50,7 @@ const projectCaches: WeakMap< const CACHE_VERSION = '1'; export default class ScriptTransformer { - static EVAL_RESULT_VARIABLE: string; + static EVAL_RESULT_VARIABLE: 'Object.'; private _cache: ProjectCache; private _config: Config.ProjectConfig; private _transformCache: Map; @@ -565,4 +565,5 @@ const wrap = (content: string, ...extras: Array) => { ); }; +// TODO: Can this be added to the static property? ScriptTransformer.EVAL_RESULT_VARIABLE = 'Object.'; diff --git a/packages/jest-transform/src/index.ts b/packages/jest-transform/src/index.ts index 6f10151c3051..4a757f5ad9fd 100644 --- a/packages/jest-transform/src/index.ts +++ b/packages/jest-transform/src/index.ts @@ -7,4 +7,4 @@ export {default as ScriptTransformer} from './ScriptTransformer'; export {default as shouldInstrument} from './shouldInstrument'; -export {Transformer} from './types'; +export {Transformer, ShouldInstrumentOptions} from './types'; diff --git a/packages/jest-transform/src/shouldInstrument.ts b/packages/jest-transform/src/shouldInstrument.ts index 2020e724ddb5..0aa7d2f7d74e 100644 --- a/packages/jest-transform/src/shouldInstrument.ts +++ b/packages/jest-transform/src/shouldInstrument.ts @@ -10,7 +10,7 @@ import {Config} from '@jest/types'; import {escapePathForRegex} from 'jest-regex-util'; import {replacePathSepForGlob} from 'jest-util'; import micromatch from 'micromatch'; -import {Options} from './types'; +import {ShouldInstrumentOptions} from './types'; const MOCKS_PATTERN = new RegExp( escapePathForRegex(path.sep + '__mocks__' + path.sep), @@ -18,7 +18,7 @@ const MOCKS_PATTERN = new RegExp( export default function shouldInstrument( filename: Config.Path, - options: Options, + options: ShouldInstrumentOptions, config: Config.ProjectConfig, ): boolean { if (!options.collectCoverage) { diff --git a/packages/jest-transform/src/types.ts b/packages/jest-transform/src/types.ts index 91df3f394de9..636cff4c27e2 100644 --- a/packages/jest-transform/src/types.ts +++ b/packages/jest-transform/src/types.ts @@ -9,18 +9,19 @@ import {Script} from 'vm'; import {RawSourceMap} from 'source-map'; import {Config} from '@jest/types'; -export type Options = Pick< +export type ShouldInstrumentOptions = Pick< Config.GlobalConfig, - | 'collectCoverage' - | 'collectCoverageFrom' - | 'collectCoverageOnlyFrom' - | 'extraGlobals' + 'collectCoverage' | 'collectCoverageFrom' | 'collectCoverageOnlyFrom' > & { changedFiles: Set | undefined; - isCoreModule?: boolean; - isInternalModule?: boolean; }; +export type Options = ShouldInstrumentOptions & + Pick & { + isCoreModule?: boolean; + isInternalModule?: boolean; + }; + // https://stackoverflow.com/a/48216010/1850276 type Omit = Pick>; diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index 85a5db4df75f..bdd9c64efc39 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -5,6 +5,8 @@ * LICENSE file in the root directory of this source tree. */ +import {Arguments} from 'yargs'; + export type Path = string; export type Glob = string; @@ -313,7 +315,7 @@ export type ProjectConfig = { detectOpenHandles: boolean; displayName: string | null | undefined; errorOnDeprecated: boolean; - extraGlobals: Array; + extraGlobals: Array; filter: Path | null | undefined; forceCoverageMatch: Array; globalSetup: string | null | undefined; @@ -355,3 +357,93 @@ export type ProjectConfig = { watchPathIgnorePatterns: Array; unmockedModulePathPatterns: Array | null | undefined; }; + +export type Argv = Arguments<{ + all: boolean; + automock: boolean; + bail: boolean | number; + browser: boolean; + cache: boolean; + cacheDirectory: string; + changedFilesWithAncestor: boolean; + changedSince: string; + ci: boolean; + clearCache: boolean; + clearMocks: boolean; + collectCoverage: boolean; + collectCoverageFrom: Array; + collectCoverageOnlyFrom: Array; + 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; + h: boolean; + haste: string; + help: boolean; + init: boolean; + json: boolean; + lastCommit: boolean; + logHeapUsage: boolean; + maxWorkers: number; + moduleDirectories: Array; + moduleFileExtensions: Array; + moduleLoader: string; + moduleNameMapper: string; + modulePathIgnorePatterns: Array; + modulePaths: Array; + name: string; + noSCM: boolean; + noStackTrace: boolean; + notify: boolean; + notifyMode: string; + onlyChanged: boolean; + outputFile: string; + preset: string | null | undefined; + projects: Array; + prettierPath: string | null | undefined; + replname: string | null | undefined; + resetMocks: boolean; + resetModules: boolean; + resolver: string | null | undefined; + restoreMocks: boolean; + rootDir: string; + roots: Array; + runInBand: boolean; + 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 | null | undefined; + testRunner: string; + testURL: string; + timers: 'real' | 'fake'; + transform: string; + transformIgnorePatterns: Array; + unmockedModulePathPatterns: Array | null | undefined; + updateSnapshot: boolean; + useStderr: boolean; + verbose: boolean | null | undefined; + version: boolean; + watch: boolean; + watchAll: boolean; + watchman: boolean; + watchPathIgnorePatterns: Array; +}>; diff --git a/packages/jest-types/src/Environment.ts b/packages/jest-types/src/Environment.ts deleted file mode 100644 index 1af4493db851..000000000000 --- a/packages/jest-types/src/Environment.ts +++ /dev/null @@ -1,44 +0,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 {Script} from 'vm'; -import {ProjectConfig} from './Config'; -import {Global} from './Global'; - -// TODO Fix jest-mock and @jest/types has circular dependency -// import {ModuleMocker} from 'jest-mock'; -type ModuleMocker = any; - -export type EnvironmentContext = { - console?: Object; - testPath?: string; -}; - -export declare class $JestEnvironment { - constructor(config: ProjectConfig, context?: EnvironmentContext); - runScript(script: Script): any; - global: Global; - fakeTimers: { - clearAllTimers(): void; - runAllImmediates(): void; - runAllTicks(): void; - runAllTimers(): void; - advanceTimersByTime(msToRun: number): void; - runOnlyPendingTimers(): void; - runWithRealTimers(callback: any): void; - getTimerCount(): number; - useFakeTimers(): void; - useRealTimers(): void; - }; - testFilePath: string; - moduleMocker: ModuleMocker; - setup(): Promise; - teardown(): Promise; -} - -export type Environment = $JestEnvironment; -export type EnvironmentClass = typeof $JestEnvironment; diff --git a/packages/jest-types/src/Global.ts b/packages/jest-types/src/Global.ts index 1a782eaaa386..de0d500ca9cd 100644 --- a/packages/jest-types/src/Global.ts +++ b/packages/jest-types/src/Global.ts @@ -5,12 +5,17 @@ * LICENSE file in the root directory of this source tree. */ +import {CoverageMapData} from 'istanbul-lib-coverage'; + export type DoneFn = (reason?: string | Error) => void; export type TestName = string; export type TestFn = (done?: DoneFn) => Promise | void | undefined; export type BlockFn = () => void; export type BlockName = string; +// TODO: Get rid of this at some point +type JasmineType = {_DEFAULT_TIMEOUT_INTERVAL?: number; addMatchers: Function}; + // TODO Replace with actual type when `jest-each` is ready type Each = () => void; @@ -48,6 +53,7 @@ export interface Describe extends DescribeBase { skip: ItBase; } +// TODO: Maybe add `| Window` in the future? export interface Global extends NodeJS.Global { it: It; test: ItConcurrent; @@ -57,6 +63,8 @@ export interface Global extends NodeJS.Global { describe: Describe; xdescribe: DescribeBase; fdescribe: DescribeBase; + __coverage__: CoverageMapData; + jasmine: JasmineType; } declare global { diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts index c99939515bae..458831633460 100644 --- a/packages/jest-types/src/index.ts +++ b/packages/jest-types/src/index.ts @@ -11,6 +11,5 @@ import * as Matchers from './Matchers'; import * as SourceMaps from './SourceMaps'; import * as TestResult from './TestResult'; import * as Global from './Global'; -import * as Environment from './Environment'; -export {Config, Console, Matchers, SourceMaps, TestResult, Global, Environment}; +export {Config, Console, Matchers, SourceMaps, TestResult, Global};