From 94ab7fd2ae18f5262154444467ceed1964790434 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 29 Nov 2020 16:08:22 +0100 Subject: [PATCH 1/3] chore: migrate jest-mock to ESM --- packages/jest-environment/src/index.ts | 17 +- .../src/__tests__/legacyFakeTimers.test.ts | 6 +- packages/jest-mock/README.md | 4 + packages/jest-mock/src/index.ts | 194 +++++++++--------- packages/jest-runtime/src/index.ts | 6 +- 5 files changed, 113 insertions(+), 114 deletions(-) diff --git a/packages/jest-environment/src/index.ts b/packages/jest-environment/src/index.ts index f83d0cb5c595..3b858b922160 100644 --- a/packages/jest-environment/src/index.ts +++ b/packages/jest-environment/src/index.ts @@ -8,10 +8,11 @@ import type {Context, Script} from 'vm'; import type {LegacyFakeTimers, ModernFakeTimers} from '@jest/fake-timers'; import type {Circus, Config, Global} from '@jest/types'; -import jestMock = require('jest-mock'); - -type JestMockFn = typeof jestMock.fn; -type JestMockSpyOn = typeof jestMock.spyOn; +import type { + fn as JestMockFn, + spyOn as JestMockSpyOn, + ModuleMocker, +} from 'jest-mock'; // In Jest 25, remove `Partial` since it's incorrect. The properties are always // passed, or not. The context itself is optional, not properties within it. @@ -38,7 +39,7 @@ export declare class JestEnvironment { global: Global.Global; fakeTimers: LegacyFakeTimers | null; fakeTimersModern: ModernFakeTimers | null; - moduleMocker: jestMock.ModuleMocker | null; + moduleMocker: ModuleMocker | null; /** * @deprecated implement getVmContext instead */ @@ -112,7 +113,7 @@ export interface Jest { /** * Creates a mock function. Optionally takes a mock implementation. */ - fn: JestMockFn; + fn: typeof JestMockFn; /** * Given the name of a module, use the automatic mocking system to generate a * mocked version of the module for you. @@ -136,7 +137,7 @@ export interface Jest { */ isMockFunction( fn: (...args: Array) => unknown, - ): fn is ReturnType; + ): fn is ReturnType; /** * Mocks a module with an auto-mocked version when it is being required. */ @@ -252,7 +253,7 @@ export interface Jest { * Note: By default, jest.spyOn also calls the spied method. This is * different behavior from most other test libraries. */ - spyOn: JestMockSpyOn; + 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 diff --git a/packages/jest-fake-timers/src/__tests__/legacyFakeTimers.test.ts b/packages/jest-fake-timers/src/__tests__/legacyFakeTimers.test.ts index 28a335152a11..55fb8c75567a 100644 --- a/packages/jest-fake-timers/src/__tests__/legacyFakeTimers.test.ts +++ b/packages/jest-fake-timers/src/__tests__/legacyFakeTimers.test.ts @@ -8,7 +8,7 @@ import * as util from 'util'; import {runInNewContext} from 'vm'; import wrap from 'jest-snapshot-serializer-raw'; -import mock = require('jest-mock'); +import {ModuleMocker} from 'jest-mock'; import FakeTimers from '../legacyFakeTimers'; const timerConfig = { @@ -22,11 +22,11 @@ const config = { }; describe('FakeTimers', () => { - let moduleMocker: mock.ModuleMocker; + let moduleMocker: ModuleMocker; beforeEach(() => { const global = runInNewContext('this'); - moduleMocker = new mock.ModuleMocker(global); + moduleMocker = new ModuleMocker(global); }); describe('construction', () => { diff --git a/packages/jest-mock/README.md b/packages/jest-mock/README.md index bb7329834289..da440ff63994 100644 --- a/packages/jest-mock/README.md +++ b/packages/jest-mock/README.md @@ -2,6 +2,10 @@ ## API +```js +import {ModuleMocker} from 'jest-mock'; +``` + ### `constructor(global)` Creates a new module mocker that generates mocks as if they were created in an environment with the given global object. diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts index 3d3dcd50e199..f323730fb765 100644 --- a/packages/jest-mock/src/index.ts +++ b/packages/jest-mock/src/index.ts @@ -9,65 +9,62 @@ type Global = NodeJS.Global; // | Window – add once TS improves typings; -declare namespace JestMock { - export type ModuleMocker = ModuleMockerClass; - export type MockFunctionMetadataType = - | 'object' - | 'array' - | 'regexp' - | 'function' - | 'constant' - | 'collection' - | 'null' - | 'undefined'; - - export type MockFunctionMetadata< - T, - Y extends Array, - 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 type MockFunctionMetadataType = + | 'object' + | 'array' + | 'regexp' + | 'function' + | 'constant' + | 'collection' + | 'null' + | 'undefined'; + +export type MockFunctionMetadata< + T, + Y extends Array, + Type = MockFunctionMetadataType +> = { + ref?: number; + members?: Record>; + mockImpl?: (...args: Y) => T; + name?: string; + refID?: number; + type?: Type; + value?: T; + length?: number; +}; - 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; - } +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; } type Unpromisify = T extends Promise ? R : never; @@ -302,7 +299,7 @@ function getObjectType(value: unknown): string { return Object.prototype.toString.apply(value).slice(8, -1); } -function getType(ref?: unknown): JestMock.MockFunctionMetadataType | null { +function getType(ref?: unknown): MockFunctionMetadataType | null { const typeName = getObjectType(ref); if ( typeName === 'Function' || @@ -366,16 +363,12 @@ function isReadonlyProp(object: any, prop: string): boolean { return false; } -class ModuleMockerClass { +export class ModuleMocker { private _environmentGlobal: Global; - private _mockState: WeakMap< - JestMock.Mock, - MockFunctionState - >; + private _mockState: WeakMap, MockFunctionState>; private _mockConfigRegistry: WeakMap; private _spyState: Set<() => void>; private _invocationCallCounter: number; - ModuleMocker: typeof ModuleMockerClass; /** * @see README.md @@ -387,7 +380,6 @@ class ModuleMockerClass { this._mockState = new WeakMap(); this._mockConfigRegistry = new WeakMap(); this._spyState = new Set(); - this.ModuleMocker = ModuleMockerClass; this._invocationCallCounter = 1; } @@ -438,7 +430,7 @@ class ModuleMockerClass { } private _ensureMockConfig>( - f: JestMock.Mock, + f: Mock, ): MockFunctionConfig { let config = this._mockConfigRegistry.get(f); if (!config) { @@ -449,7 +441,7 @@ class ModuleMockerClass { } private _ensureMockState>( - f: JestMock.Mock, + f: Mock, ): MockFunctionState { let state = this._mockState.get(f); if (!state) { @@ -481,19 +473,19 @@ class ModuleMockerClass { } private _makeComponent>( - metadata: JestMock.MockFunctionMetadata, + metadata: MockFunctionMetadata, restore?: () => void, ): Record; private _makeComponent>( - metadata: JestMock.MockFunctionMetadata, + metadata: MockFunctionMetadata, restore?: () => void, ): Array; private _makeComponent>( - metadata: JestMock.MockFunctionMetadata, + metadata: MockFunctionMetadata, restore?: () => void, ): RegExp; private _makeComponent>( - metadata: JestMock.MockFunctionMetadata< + metadata: MockFunctionMetadata< T, Y, 'constant' | 'collection' | 'null' | 'undefined' @@ -501,11 +493,11 @@ class ModuleMockerClass { restore?: () => void, ): T; private _makeComponent>( - metadata: JestMock.MockFunctionMetadata, + metadata: MockFunctionMetadata, restore?: () => void, - ): JestMock.Mock; + ): Mock; private _makeComponent>( - metadata: JestMock.MockFunctionMetadata, + metadata: MockFunctionMetadata, restore?: () => void, ): | Record @@ -513,7 +505,7 @@ class ModuleMockerClass { | RegExp | T | undefined - | JestMock.Mock { + | Mock { if (metadata.type === 'object') { return new this._environmentGlobal.Object(); } else if (metadata.type === 'array') { @@ -625,7 +617,7 @@ class ModuleMockerClass { const f = (this._createMockFunction( metadata, mockConstructor, - ) as unknown) as JestMock.Mock; + ) as unknown) as Mock; f._isMockFunction = true; f.getMockImplementation = () => this._ensureMockConfig(f).mockImpl; @@ -681,7 +673,7 @@ class ModuleMockerClass { f.mockImplementationOnce = ( fn: ((...args: Y) => T) | (() => Promise), - ): JestMock.Mock => { + ): Mock => { // next function call will use this mock implementation return value // or default mock implementation return value const mockConfig = this._ensureMockConfig(f); @@ -691,7 +683,7 @@ class ModuleMockerClass { f.mockImplementation = ( fn: ((...args: Y) => T) | (() => Promise), - ): JestMock.Mock => { + ): Mock => { // next function call will use mock implementation return value const mockConfig = this._ensureMockConfig(f); mockConfig.mockImpl = fn; @@ -728,7 +720,7 @@ class ModuleMockerClass { } private _createMockFunction>( - metadata: JestMock.MockFunctionMetadata, + metadata: MockFunctionMetadata, mockConstructor: Function, ): Function { let name = metadata.name; @@ -788,7 +780,7 @@ class ModuleMockerClass { } private _generateMock>( - metadata: JestMock.MockFunctionMetadata, + metadata: MockFunctionMetadata, callbacks: Array, refs: { [key: string]: @@ -797,9 +789,9 @@ class ModuleMockerClass { | RegExp | T | undefined - | JestMock.Mock; + | Mock; }, - ): JestMock.Mock { + ): Mock { // metadata not compatible but it's the same type, maybe problem with // overloading of _makeComponent and not _generateMock? // @ts-expect-error @@ -830,7 +822,7 @@ class ModuleMockerClass { mock.prototype.constructor = mock; } - return mock as JestMock.Mock; + return mock as Mock; } /** @@ -839,8 +831,8 @@ class ModuleMockerClass { * getMetadata method of this module. */ generateFromMetadata>( - _metadata: JestMock.MockFunctionMetadata, - ): JestMock.Mock { + _metadata: MockFunctionMetadata, + ): Mock { const callbacks: Array = []; const refs = {}; const mock = this._generateMock(_metadata, callbacks, refs); @@ -855,7 +847,7 @@ class ModuleMockerClass { getMetadata>( component: T, _refs?: Map, - ): JestMock.MockFunctionMetadata | null { + ): MockFunctionMetadata | null { const refs = _refs || new Map(); const ref = refs.get(component); if (ref != null) { @@ -867,7 +859,7 @@ class ModuleMockerClass { return null; } - const metadata: JestMock.MockFunctionMetadata = {type}; + const metadata: MockFunctionMetadata = {type}; if ( type === 'constant' || type === 'collection' || @@ -890,7 +882,7 @@ class ModuleMockerClass { refs.set(component, metadata.refID); let members: { - [key: string]: JestMock.MockFunctionMetadata; + [key: string]: MockFunctionMetadata; } | null = null; // Leave arrays alone if (type !== 'array') { @@ -921,13 +913,13 @@ class ModuleMockerClass { return metadata; } - isMockFunction(fn: any): fn is JestMock.Mock { + isMockFunction(fn: any): fn is Mock { return !!fn && fn._isMockFunction === true; } fn>( implementation?: (...args: Y) => T, - ): JestMock.Mock { + ): Mock { const length = implementation ? implementation.length : 0; const fn = this._makeComponent({length, type: 'function'}); if (implementation) { @@ -940,19 +932,19 @@ class ModuleMockerClass { object: T, methodName: M, accessType: 'get', - ): JestMock.SpyInstance; + ): SpyInstance; spyOn>( object: T, methodName: M, accessType: 'set', - ): JestMock.SpyInstance; + ): SpyInstance; spyOn>( object: T, methodName: M, ): T[M] extends (...args: Array) => any - ? JestMock.SpyInstance, Parameters> + ? SpyInstance, Parameters> : never; spyOn>( @@ -993,7 +985,7 @@ class ModuleMockerClass { proto = Object.getPrototypeOf(proto); } - let mock: JestMock.Mock>; + let mock: Mock>; if (descriptor && descriptor.get) { const originalGet = descriptor.get; @@ -1027,7 +1019,7 @@ class ModuleMockerClass { obj: T, propertyName: M, accessType: 'get' | 'set' = 'get', - ): JestMock.Mock { + ): Mock { if (typeof obj !== 'object' && typeof obj !== 'function') { throw new Error( 'Cannot spyOn on a primitive value; ' + this._typeOf(obj) + ' given', @@ -1086,7 +1078,7 @@ class ModuleMockerClass { Object.defineProperty(obj, propertyName, descriptor!); }); - (descriptor[accessType] as JestMock.Mock).mockImplementation(function ( + (descriptor[accessType] as Mock).mockImplementation(function ( this: unknown, ) { // @ts-expect-error @@ -1095,7 +1087,7 @@ class ModuleMockerClass { } Object.defineProperty(obj, propertyName, descriptor); - return descriptor[accessType] as JestMock.Mock; + return descriptor[accessType] as Mock; } clearAllMocks() { @@ -1117,5 +1109,7 @@ class ModuleMockerClass { } } -const JestMock = new ModuleMockerClass(global); -export = JestMock; +const JestMock = new ModuleMocker(global); + +export const fn = JestMock.fn; +export const spyOn = JestMock.spyOn; diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index c843b74dd338..22d8c46ca4f0 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -43,7 +43,7 @@ import { import type {Config, Global} from '@jest/types'; import HasteMap, {ModuleMap} from 'jest-haste-map'; import {formatStackTrace, separateMessageFromStack} from 'jest-message-util'; -import jestMock = require('jest-mock'); +import type {MockFunctionMetadata, ModuleMocker} from 'jest-mock'; import {escapePathForRegex} from 'jest-regex-util'; import Resolver from 'jest-resolve'; import Snapshot = require('jest-snapshot'); @@ -165,11 +165,11 @@ export default class Runtime { private _mockFactories: Map unknown>; private _mockMetaDataCache: Map< string, - jestMock.MockFunctionMetadata> + MockFunctionMetadata> >; private _mockRegistry: Map; private _isolatedMockRegistry: Map | null; - private _moduleMocker: typeof jestMock; + private _moduleMocker: ModuleMocker; private _isolatedModuleRegistry: ModuleRegistry | null; private _moduleRegistry: ModuleRegistry; private _esmoduleRegistry: Map>; From 0a3bace577480645996d2576413f1ca45ba35d02 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 29 Nov 2020 16:10:21 +0100 Subject: [PATCH 2/3] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55020af9e4f8..f5ef3be2f2f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ - `[jest-repl, jest-runtime]` [**BREAKING**] Move the `jest-runtime` CLI into `jest-repl` ([#10016](https://github.com/facebook/jest/pull/10016)) - `[jest-resolve]` [**BREAKING**] Migrate to ESM ([#10688](https://github.com/facebook/jest/pull/10688)) - `[jest-resolve-dependencies]` [**BREAKING**] Migrate to ESM ([#10876](https://github.com/facebook/jest/pull/10876)) +- `[jest-mock]` [**BREAKING**] Migrate to ESM ([#10887](https://github.com/facebook/jest/pull/10887)) - `[jest-util]` No longer checking `enumerable` when adding `process.domain` ([#10862](https://github.com/facebook/jest/pull/10862)) ### Performance From ba17fab7cf192f2c15f5e8fef8970875ddaceb66 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 29 Nov 2020 16:28:21 +0100 Subject: [PATCH 3/3] eslint --- packages/jest-mock/src/index.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts index f323730fb765..2ed41294e06b 100644 --- a/packages/jest-mock/src/index.ts +++ b/packages/jest-mock/src/index.ts @@ -913,8 +913,8 @@ export class ModuleMocker { return metadata; } - isMockFunction(fn: any): fn is Mock { - return !!fn && fn._isMockFunction === true; + isMockFunction(fn: unknown): fn is Mock { + return !!fn && (fn as any)._isMockFunction === true; } fn>( @@ -947,6 +947,7 @@ export class ModuleMocker { ? SpyInstance, Parameters> : never; + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types spyOn>( object: T, methodName: M, @@ -1090,16 +1091,16 @@ export class ModuleMocker { return descriptor[accessType] as Mock; } - clearAllMocks() { + clearAllMocks(): void { this._mockState = new WeakMap(); } - resetAllMocks() { + resetAllMocks(): void { this._mockConfigRegistry = new WeakMap(); this._mockState = new WeakMap(); } - restoreAllMocks() { + restoreAllMocks(): void { this._spyState.forEach(restore => restore()); this._spyState = new Set(); }