From a36ba3df2c9cd56b0ed975a373e02e4a7426bd64 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 23 Sep 2022 14:50:40 +0200 Subject: [PATCH 1/7] fix: correct `isPromise` implementation --- packages/jest-util/src/isPromise.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/jest-util/src/isPromise.ts b/packages/jest-util/src/isPromise.ts index 28d4dc7f56c1..cbecabeb355c 100644 --- a/packages/jest-util/src/isPromise.ts +++ b/packages/jest-util/src/isPromise.ts @@ -5,10 +5,8 @@ * 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; + candidate != null && + typeof candidate === 'object' && + typeof (candidate as any).then === 'function'; export default isPromise; From e0a66df1a0ae926f06b694f8b557b50fe39c30df Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 23 Sep 2022 14:51:24 +0200 Subject: [PATCH 2/7] use util in jest-circus --- packages/jest-circus/src/utils.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) 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; } From b8e31a3799a6012d915f3cca69fa2d3d851bf2b5 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 23 Sep 2022 14:54:53 +0200 Subject: [PATCH 3/7] use in more places --- packages/expect/src/index.ts | 7 +------ packages/jest-jasmine2/src/jasmineAsyncInstall.ts | 5 +---- packages/jest-util/src/isPromise.ts | 4 ++-- packages/jest-worker/src/workers/processChild.ts | 6 +----- packages/jest-worker/src/workers/threadChild.ts | 6 +----- 5 files changed, 6 insertions(+), 22 deletions(-) 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-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/isPromise.ts b/packages/jest-util/src/isPromise.ts index cbecabeb355c..0eab6be8c22a 100644 --- a/packages/jest-util/src/isPromise.ts +++ b/packages/jest-util/src/isPromise.ts @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -const isPromise = (candidate: unknown): candidate is Promise => +const isPromise = (candidate: unknown): candidate is PromiseLike => candidate != null && - typeof candidate === 'object' && + (typeof candidate === 'object' || typeof candidate === 'function') && typeof (candidate as any).then === 'function'; export default isPromise; 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, From 9741e7e0f592658c82d130ecaa4226ca0bb9a52e Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 23 Sep 2022 15:01:47 +0200 Subject: [PATCH 4/7] no arrow --- packages/jest-util/src/isPromise.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/jest-util/src/isPromise.ts b/packages/jest-util/src/isPromise.ts index 0eab6be8c22a..84d999fbeb60 100644 --- a/packages/jest-util/src/isPromise.ts +++ b/packages/jest-util/src/isPromise.ts @@ -5,8 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -const isPromise = (candidate: unknown): candidate is PromiseLike => - candidate != null && - (typeof candidate === 'object' || typeof candidate === 'function') && - typeof (candidate as any).then === 'function'; -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' + ); +} From b601db06eeddb31b70ee7b148a8d4e8cf229cab7 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 23 Sep 2022 15:03:43 +0200 Subject: [PATCH 5/7] add test for thenable --- packages/jest-util/src/__tests__/isPromise.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/jest-util/src/__tests__/isPromise.test.ts b/packages/jest-util/src/__tests__/isPromise.test.ts index 41d9a758a70b..d388e872f05e 100644 --- a/packages/jest-util/src/__tests__/isPromise.test.ts +++ b/packages/jest-util/src/__tests__/isPromise.test.ts @@ -23,3 +23,7 @@ test('a resolved Promise', () => { test('a rejected Promise', () => { expect(isPromise(Promise.reject().catch(() => {}))).toBe(true); }); + +test('a thenable', () => { + expect(isPromise({then: () => 'hello'})).toBe(true); +}); From a422e05b59da8c08615702b52934f57518dfcc4c Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 23 Sep 2022 15:05:17 +0200 Subject: [PATCH 6/7] async function --- packages/jest-util/src/__tests__/isPromise.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/jest-util/src/__tests__/isPromise.test.ts b/packages/jest-util/src/__tests__/isPromise.test.ts index d388e872f05e..5b2e4854ab7d 100644 --- a/packages/jest-util/src/__tests__/isPromise.test.ts +++ b/packages/jest-util/src/__tests__/isPromise.test.ts @@ -27,3 +27,8 @@ test('a rejected Promise', () => { test('a thenable', () => { expect(isPromise({then: () => 'hello'})).toBe(true); }); + +test('an async function', () => { + async function asyncFn() {} + expect(isPromise(asyncFn())).toBe(true); +}); From cdec848182b18f479e63f14f9a5556f30937d6d4 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 23 Sep 2022 15:14:29 +0200 Subject: [PATCH 7/7] snap --- e2e/__tests__/__snapshots__/testFailingJasmine.test.ts.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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)