diff --git a/CHANGELOG.md b/CHANGELOG.md index 0528254517c8..52b1dd49f480 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ ### Chore & Maintenance +- `[jest-jasmine2]` Convert `PCancelable` to TypeScript ([#10215](https://github.com/facebook/jest/pull/10215)) +- `[jest-jasmine2]` Refine typings of `queueRunner` ([#10215](https://github.com/facebook/jest/pull/10215)) + ### Performance ## 26.1.0 diff --git a/packages/jest-jasmine2/src/PCancelable.js b/packages/jest-jasmine2/src/PCancelable.js deleted file mode 100644 index 8b3bc3c71629..000000000000 --- a/packages/jest-jasmine2/src/PCancelable.js +++ /dev/null @@ -1,84 +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. - */ - -'use strict'; - -class CancelError extends Error { - constructor() { - super('Promise was canceled'); - this.name = 'CancelError'; - } -} - -class PCancelable { - static fn(fn) { - return function () { - const args = [].slice.apply(arguments); - return new PCancelable((onCancel, resolve, reject) => { - args.unshift(onCancel); - fn.apply(null, args).then(resolve, reject); - }); - }; - } - - constructor(executor) { - this._pending = true; - this._canceled = false; - - 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() { - return this._promise.then.apply(this._promise, arguments); - } - - catch() { - return this._promise.catch.apply(this._promise, arguments); - } - - 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()); - } - - get canceled() { - return this._canceled; - } -} - -Object.setPrototypeOf(PCancelable.prototype, Promise.prototype); - -module.exports = PCancelable; -module.exports.CancelError = CancelError; diff --git a/packages/jest-jasmine2/src/PCancelable.ts b/packages/jest-jasmine2/src/PCancelable.ts new file mode 100644 index 000000000000..91a2e537e756 --- /dev/null +++ b/packages/jest-jasmine2/src/PCancelable.ts @@ -0,0 +1,88 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All 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'; + } +} + +export default class PCancelable extends Promise { + private _pending = true; + private _canceled = false; + private _promise: Promise; + private _cancel?: () => void; + private _reject: (reason?: unknown) => void = () => {}; + + constructor( + executor: ( + onCancel: (cancelHandler: () => void) => void, + resolve: (value?: T | PromiseLike) => void, + reject: (reason?: unknown) => void, + ) => void, + ) { + super(resolve => resolve()); + + 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?: + | ((value: T) => TResult1 | PromiseLike) + | undefined + | null, + onRejected?: + | ((reason: any) => TResult2 | PromiseLike) + | undefined + | null, + ): Promise { + return this._promise.then(onFulfilled, onRejected); + } + + catch( + onRejected?: + | ((reason: any) => TResult | PromiseLike) + | undefined + | null, + ): Promise { + return this._promise.catch(onRejected); + } + + cancel(): void { + 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()); + } +} diff --git a/packages/jest-jasmine2/src/__tests__/pTimeout.test.ts b/packages/jest-jasmine2/src/__tests__/pTimeout.test.ts index 9907d1f5945f..e31c59df0eb0 100644 --- a/packages/jest-jasmine2/src/__tests__/pTimeout.test.ts +++ b/packages/jest-jasmine2/src/__tests__/pTimeout.test.ts @@ -39,7 +39,7 @@ describe('pTimeout', () => { it('calls `onTimeout` on timeout.', async () => { const onTimeout = jest.fn(); // A Promise that never resolves or rejects. - const promise = new Promise(() => {}); + const promise = new Promise(() => {}); const timeoutPromise = pTimeout( promise, 1000, diff --git a/packages/jest-jasmine2/src/pTimeout.ts b/packages/jest-jasmine2/src/pTimeout.ts index dfe96c134e8c..d982cf732d4b 100644 --- a/packages/jest-jasmine2/src/pTimeout.ts +++ b/packages/jest-jasmine2/src/pTimeout.ts @@ -8,12 +8,12 @@ // A specialized version of `p-timeout` that does not touch globals. // It does not throw on timeout. export default function pTimeout( - promise: Promise, + promise: Promise, ms: number, clearTimeout: NodeJS.Global['clearTimeout'], setTimeout: NodeJS.Global['setTimeout'], - onTimeout: () => any, -): Promise { + onTimeout: () => void, +): Promise { return new Promise((resolve, reject) => { const timer = setTimeout(() => resolve(onTimeout()), ms); promise.then( diff --git a/packages/jest-jasmine2/src/queueRunner.ts b/packages/jest-jasmine2/src/queueRunner.ts index 21af10a51de6..789b618a3824 100644 --- a/packages/jest-jasmine2/src/queueRunner.ts +++ b/packages/jest-jasmine2/src/queueRunner.ts @@ -6,7 +6,6 @@ */ import {formatTime} from 'jest-util'; -// @ts-expect-error ignore vendor file import PCancelable from './PCancelable'; import pTimeout from './pTimeout'; @@ -27,15 +26,20 @@ export type QueueableFn = { initError?: Error; }; -// har to type :( -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export default function queueRunner(options: Options) { - const token = new PCancelable((onCancel: Function, resolve: Function) => { +type PromiseCallback = (() => void | PromiseLike) | undefined | null; + +export default function queueRunner( + options: Options, +): PromiseLike & { + cancel: () => void; + catch: (onRejected?: PromiseCallback) => Promise; +} { + const token = new PCancelable((onCancel, resolve) => { onCancel(resolve); }); const mapper = ({fn, timeout, initError = new Error()}: QueueableFn) => { - let promise = new Promise(resolve => { + let promise = new Promise(resolve => { const next = function (...args: [Error]) { const err = args[0]; if (err) { @@ -56,7 +60,7 @@ export default function queueRunner(options: Options) { } }); - promise = Promise.race([promise, token]); + promise = Promise.race([promise, token]); if (!timeout) { return promise;