Skip to content

Commit

Permalink
refactor(@jest/fake-timers): improve internal typings of legacy fake …
Browse files Browse the repository at this point in the history
…timers (#12567)
  • Loading branch information
mrazauskas committed Mar 24, 2022
1 parent 6056cd0 commit ad22b08
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 33 deletions.
1 change: 0 additions & 1 deletion .eslintrc.js
Expand Up @@ -95,7 +95,6 @@ module.exports = {
'packages/jest-core/src/TestScheduler.ts',
'packages/jest-core/src/collectHandles.ts',
'packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts',
'packages/jest-fake-timers/src/legacyFakeTimers.ts',
'packages/jest-haste-map/src/index.ts',
'packages/jest-haste-map/src/watchers/FSEventsWatcher.ts',
'packages/jest-jasmine2/src/jasmine/SpyStrategy.ts',
Expand Down
66 changes: 35 additions & 31 deletions packages/jest-fake-timers/src/legacyFakeTimers.ts
Expand Up @@ -5,11 +5,16 @@
* LICENSE file in the root directory of this source tree.
*/

/* eslint-disable local/ban-types-eventually, local/prefer-spread-eventually */
/* eslint-disable local/prefer-spread-eventually */

import util = require('util');
import {promisify} from 'util';
import {StackTraceConfig, formatStackTrace} from 'jest-message-util';
import type {ModuleMocker} from 'jest-mock';
import type {
FunctionLike,
Mock,
ModuleMocker,
UnknownFunction,
} from 'jest-mock';
import {setGlobal} from 'jest-util';

type Callback = (...args: Array<unknown>) => void;
Expand All @@ -29,38 +34,42 @@ type Timer = {
};

type TimerAPI = {
cancelAnimationFrame: FakeTimersGlobal['cancelAnimationFrame'];
cancelAnimationFrame: typeof globalThis.cancelAnimationFrame;
clearImmediate: typeof globalThis.clearImmediate;
clearInterval: typeof globalThis.clearInterval;
clearTimeout: typeof globalThis.clearTimeout;
nextTick: typeof process.nextTick;

requestAnimationFrame: FakeTimersGlobal['requestAnimationFrame'];
requestAnimationFrame: typeof globalThis.requestAnimationFrame;
setImmediate: typeof globalThis.setImmediate;
setInterval: typeof globalThis.setInterval;
setTimeout: typeof globalThis.setTimeout;
};

type FakeTimerAPI = {
cancelAnimationFrame: Mock<FakeTimers['_fakeClearTimer']>;
clearImmediate: Mock<FakeTimers['_fakeClearImmediate']>;
clearInterval: Mock<FakeTimers['_fakeClearTimer']>;
clearTimeout: Mock<FakeTimers['_fakeClearTimer']>;
nextTick: Mock<FakeTimers['_fakeNextTick']>;
requestAnimationFrame: Mock<FakeTimers['_fakeRequestAnimationFrame']>;
setImmediate: Mock<FakeTimers['_fakeSetImmediate']>;
setInterval: Mock<FakeTimers['_fakeSetInterval']>;
setTimeout: Mock<FakeTimers['_fakeSetTimeout']>;
};

type TimerConfig<Ref> = {
idToRef: (id: number) => Ref;
refToId: (ref: Ref) => number | void;
};

const MS_IN_A_YEAR = 31536000000;

type GlobalThis = typeof globalThis;

interface FakeTimersGlobal extends GlobalThis {
cancelAnimationFrame: (handle: number) => void;
requestAnimationFrame: (callback: (time: number) => void) => number;
}

export default class FakeTimers<TimerRef> {
export default class FakeTimers<TimerRef = unknown> {
private _cancelledTicks!: Record<string, boolean>;
private _config: StackTraceConfig;
private _disposed?: boolean;
private _fakeTimerAPIs!: TimerAPI;
private _global: FakeTimersGlobal;
private _fakeTimerAPIs!: FakeTimerAPI;
private _global: typeof globalThis;
private _immediates!: Array<Tick>;
private _maxLoops: number;
private _moduleMocker: ModuleMocker;
Expand All @@ -78,7 +87,7 @@ export default class FakeTimers<TimerRef> {
config,
maxLoops,
}: {
global: FakeTimersGlobal;
global: typeof globalThis;
moduleMocker: ModuleMocker;
timerConfig: TimerConfig<TimerRef>;
config: StackTraceConfig;
Expand Down Expand Up @@ -390,6 +399,7 @@ export default class FakeTimers<TimerRef> {
}

private _checkFakeTimers() {
// @ts-expect-error: condition always returns 'true'
if (this._global.setTimeout !== this._fakeTimerAPIs?.setTimeout) {
this._global.console.warn(
'A function to advance timers was called but the timers API is not ' +
Expand All @@ -407,32 +417,26 @@ export default class FakeTimers<TimerRef> {
}

private _createMocks() {
const fn = (impl: Function) =>
// @ts-expect-error TODO: figure out better typings here
this._moduleMocker.fn().mockImplementation(impl);
const fn = <T extends FunctionLike = UnknownFunction>(implementation?: T) =>
this._moduleMocker.fn(implementation);

const promisifiableFakeSetTimeout = fn(this._fakeSetTimeout.bind(this));
// @ts-expect-error TODO: figure out better typings here
promisifiableFakeSetTimeout[util.promisify.custom] = (
// @ts-expect-error: no index
promisifiableFakeSetTimeout[promisify.custom] = (
delay?: number,
arg?: unknown,
) =>
new Promise(resolve => promisifiableFakeSetTimeout(resolve, delay, arg));

// TODO: add better typings; these are mocks, but typed as regular timers
this._fakeTimerAPIs = {
cancelAnimationFrame: fn(this._fakeClearTimer.bind(this)),
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
requestAnimationFrame: fn(this._fakeRequestAnimationFrame.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,
};
}
Expand All @@ -451,7 +455,7 @@ export default class FakeTimers<TimerRef> {
);
}

private _fakeNextTick(callback: Callback, ...args: Array<any>) {
private _fakeNextTick(callback: Callback, ...args: Array<unknown>) {
if (this._disposed) {
return;
}
Expand Down Expand Up @@ -480,7 +484,7 @@ export default class FakeTimers<TimerRef> {
}, 1000 / 60);
}

private _fakeSetImmediate(callback: Callback, ...args: Array<any>) {
private _fakeSetImmediate(callback: Callback, ...args: Array<unknown>) {
if (this._disposed) {
return null;
}
Expand Down Expand Up @@ -508,7 +512,7 @@ export default class FakeTimers<TimerRef> {
private _fakeSetInterval(
callback: Callback,
intervalDelay?: number,
...args: Array<any>
...args: Array<unknown>
) {
if (this._disposed) {
return null;
Expand All @@ -533,7 +537,7 @@ export default class FakeTimers<TimerRef> {
private _fakeSetTimeout(
callback: Callback,
delay?: number,
...args: Array<any>
...args: Array<unknown>
) {
if (this._disposed) {
return null;
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-mock/src/index.ts
Expand Up @@ -112,7 +112,7 @@ export type MockedClass<T extends ClassLike> = MockInstance<
prototype: T extends {prototype: any} ? Mocked<T['prototype']> : never;
} & T;

type UnknownFunction = (...args: Array<unknown>) => unknown;
export type UnknownFunction = (...args: Array<unknown>) => unknown;

/**
* All what the internal typings need is to be sure that we have any-function.
Expand Down

0 comments on commit ad22b08

Please sign in to comment.