diff --git a/e2e/__tests__/__snapshots__/testFailingJasmine.test.ts.snap b/e2e/__tests__/__snapshots__/testFailingJasmine.test.ts.snap index 320d535918e6..3e1f84140356 100644 --- a/e2e/__tests__/__snapshots__/testFailingJasmine.test.ts.snap +++ b/e2e/__tests__/__snapshots__/testFailingJasmine.test.ts.snap @@ -46,7 +46,7 @@ FAIL __tests__/worksWithConcurrentMode.test.js 15 | }); 16 | - at Function.failing (../../packages/jest-jasmine2/build/jasmineAsyncInstall.js:198:11) + at Function.failing (../../packages/jest-jasmine2/build/jasmineAsyncInstall.js:195:11) at Suite.failing (__tests__/worksWithConcurrentMode.test.js:13:17) at Object.describe (__tests__/worksWithConcurrentMode.test.js:8:1) @@ -80,7 +80,7 @@ FAIL __tests__/worksWithConcurrentOnlyMode.test.js 15 | }); 16 | - at Function.failing (../../packages/jest-jasmine2/build/jasmineAsyncInstall.js:198:11) + at Function.failing (../../packages/jest-jasmine2/build/jasmineAsyncInstall.js:195:11) at Suite.failing (__tests__/worksWithConcurrentOnlyMode.test.js:13:22) at Object.describe (__tests__/worksWithConcurrentOnlyMode.test.js:8:1) diff --git a/packages/expect/src/index.ts b/packages/expect/src/index.ts index 202c02d5c185..e6420bd138c9 100644 --- a/packages/expect/src/index.ts +++ b/packages/expect/src/index.ts @@ -10,6 +10,7 @@ import {equals, iterableEquality, subsetEquality} from '@jest/expect-utils'; import * as matcherUtils from 'jest-matcher-utils'; +import {isPromise} from 'jest-util'; import { any, anything, @@ -69,12 +70,6 @@ export class JestAssertionError extends Error { matcherResult?: Omit & {message: string}; } -// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint -const isPromise = (obj: any): obj is PromiseLike => - !!obj && - (typeof obj === 'object' || typeof obj === 'function') && - typeof obj.then === 'function'; - const createToThrowErrorMatchingSnapshotMatcher = function ( matcher: RawMatcherFn, ) { diff --git a/packages/jest-circus/src/utils.ts b/packages/jest-circus/src/utils.ts index 4d787f83fa90..7eeb2dfb2f3a 100644 --- a/packages/jest-circus/src/utils.ts +++ b/packages/jest-circus/src/utils.ts @@ -13,7 +13,12 @@ import slash = require('slash'); import StackUtils = require('stack-utils'); import type {AssertionResult, Status} from '@jest/test-result'; import type {Circus, Global} from '@jest/types'; -import {ErrorWithStack, convertDescriptorToString, formatTime} from 'jest-util'; +import { + ErrorWithStack, + convertDescriptorToString, + formatTime, + isPromise, +} from 'jest-util'; import {format as prettyFormat} from 'pretty-format'; import {ROOT_DESCRIBE_BLOCK_NAME, getState} from './state'; @@ -266,13 +271,7 @@ export const callAsyncCircusFn = ( } } - // 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' - ) { + if (isPromise(returnedValue)) { returnedValue.then(() => resolve(), reject); return; } diff --git a/packages/jest-jasmine2/src/jasmineAsyncInstall.ts b/packages/jest-jasmine2/src/jasmineAsyncInstall.ts index 2971d361f068..4f0847c29f6d 100644 --- a/packages/jest-jasmine2/src/jasmineAsyncInstall.ts +++ b/packages/jest-jasmine2/src/jasmineAsyncInstall.ts @@ -14,15 +14,12 @@ import co from 'co'; import isGeneratorFn from 'is-generator-fn'; import pLimit = require('p-limit'); import type {Config, Global} from '@jest/types'; +import {isPromise} from 'jest-util'; import isError from './isError'; import type Spec from './jasmine/Spec'; import type {DoneFn, QueueableFn} from './queueRunner'; import type {Jasmine} from './types'; -function isPromise(obj: any): obj is PromiseLike { - return obj && typeof obj.then === 'function'; -} - // eslint-disable-next-line @typescript-eslint/no-empty-function const doneFnNoop = () => {}; diff --git a/packages/jest-util/src/__tests__/isPromise.test.ts b/packages/jest-util/src/__tests__/isPromise.test.ts index 41d9a758a70b..5b2e4854ab7d 100644 --- a/packages/jest-util/src/__tests__/isPromise.test.ts +++ b/packages/jest-util/src/__tests__/isPromise.test.ts @@ -23,3 +23,12 @@ test('a resolved Promise', () => { test('a rejected Promise', () => { expect(isPromise(Promise.reject().catch(() => {}))).toBe(true); }); + +test('a thenable', () => { + expect(isPromise({then: () => 'hello'})).toBe(true); +}); + +test('an async function', () => { + async function asyncFn() {} + expect(isPromise(asyncFn())).toBe(true); +}); diff --git a/packages/jest-util/src/isPromise.ts b/packages/jest-util/src/isPromise.ts index 28d4dc7f56c1..84d999fbeb60 100644 --- a/packages/jest-util/src/isPromise.ts +++ b/packages/jest-util/src/isPromise.ts @@ -5,10 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -// capture globalThis.Promise before it may potentially be overwritten -const Promise = globalThis.Promise; - -// see ES2015 spec 25.4.4.5, https://stackoverflow.com/a/38339199 -const isPromise = (candidate: unknown): candidate is Promise => - Promise.resolve(candidate) === candidate; -export default isPromise; +export default function isPromise( + candidate: unknown, +): candidate is PromiseLike { + return ( + candidate != null && + (typeof candidate === 'object' || typeof candidate === 'function') && + typeof (candidate as any).then === 'function' + ); +} diff --git a/packages/jest-worker/src/workers/processChild.ts b/packages/jest-worker/src/workers/processChild.ts index 3302843cf2f4..e1ed99cd7e94 100644 --- a/packages/jest-worker/src/workers/processChild.ts +++ b/packages/jest-worker/src/workers/processChild.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import {isPromise} from 'jest-util'; import { CHILD_MESSAGE_CALL, CHILD_MESSAGE_END, @@ -158,11 +159,6 @@ function execMethod(method: string, args: Array): void { execFunction(main.setup, main, setupArgs, execHelper, reportInitializeError); } -const isPromise = (obj: any): obj is PromiseLike => - !!obj && - (typeof obj === 'object' || typeof obj === 'function') && - typeof obj.then === 'function'; - function execFunction( fn: UnknownFunction, ctx: unknown, diff --git a/packages/jest-worker/src/workers/threadChild.ts b/packages/jest-worker/src/workers/threadChild.ts index 8abfbde77382..c948c7441778 100644 --- a/packages/jest-worker/src/workers/threadChild.ts +++ b/packages/jest-worker/src/workers/threadChild.ts @@ -6,6 +6,7 @@ */ import {isMainThread, parentPort} from 'worker_threads'; +import {isPromise} from 'jest-util'; import { CHILD_MESSAGE_CALL, CHILD_MESSAGE_END, @@ -160,11 +161,6 @@ function execMethod(method: string, args: Array): void { execFunction(main.setup, main, setupArgs, execHelper, reportInitializeError); } -const isPromise = (obj: any): obj is PromiseLike => - !!obj && - (typeof obj === 'object' || typeof obj === 'function') && - typeof obj.then === 'function'; - function execFunction( fn: UnknownFunction, ctx: unknown,