diff --git a/src/environment-hooks.ts b/src/environment-hooks.ts index 80148f0..ccaaebc 100644 --- a/src/environment-hooks.ts +++ b/src/environment-hooks.ts @@ -2,7 +2,7 @@ import { inspect } from 'util'; import type { EnvironmentContext, JestEnvironment, JestEnvironmentConfig } from '@jest/environment'; import type { Circus } from '@jest/types'; import { JestMetadataError } from './errors'; -import { realm, injectRealmIntoSandbox } from './realms'; +import { injectRealmIntoSandbox, realm, detectDuplicateRealms } from './realms'; import { jestUtils, SemiAsyncEmitter } from './utils'; const emitterMap: WeakMap> = new WeakMap(); @@ -12,6 +12,7 @@ export function onTestEnvironmentCreate( jestEnvironmentConfig: JestEnvironmentConfig, environmentContext: EnvironmentContext, ): void { + detectDuplicateRealms(true); injectRealmIntoSandbox(jestEnvironment.global, realm); const testFilePath = environmentContext.testPath; realm.environmentHandler.handleEnvironmentCreated(testFilePath); @@ -103,6 +104,8 @@ export async function onTestEnvironmentSetup(_env: JestEnvironment): Promise { + detectDuplicateRealms(false); + if (realm.type === 'child_process') { await realm.ipc.stop(); } diff --git a/src/realms/detect.ts b/src/realms/detect.ts index 4f87ba3..8cf3753 100644 --- a/src/realms/detect.ts +++ b/src/realms/detect.ts @@ -28,10 +28,27 @@ export function injectRealmIntoSandbox(sandbox: any, realm: ProcessRealm): Proce return realm; } +/** + * Workaround for the fallback mode, when Jest uses jest-environment-node. + * Jest blindly copies `globalThis` into the sandbox, so it is not enough to + * simply check that __JEST_METADATA_SANDBOX__ is not truthy. + * + * This is especially bad in Jest's single worker mode, because + * reporter's globalThis === testEnvironment's globalThis == sandbox. + * + * This hook is enabled after the copying happens, and disabled at later stages + * when all potentially conflicting packages are loaded. It is not easy to + * grasp, but it works. + */ +export function detectDuplicateRealms(enabled: boolean): void { + const globalAny = globalThis as any; + globalAny.__JEST_METADATA_SANDBOX__ = enabled ? false : undefined; +} + export function getSandboxedRealm(): ProcessRealm | undefined { const globalAny = globalThis as any; const realm = globalAny.__JEST_METADATA__; - if (realm && !globalAny.__JEST_METADATA_SANDBOX__) { + if (realm && globalAny.__JEST_METADATA_SANDBOX__ === false) { console.warn( '[jest-metadata] Detected duplicate jest-metadata package in the same process. This may lead to unexpected behavior.', ); diff --git a/src/realms/index.ts b/src/realms/index.ts index 56007a1..33e3d47 100644 --- a/src/realms/index.ts +++ b/src/realms/index.ts @@ -1,4 +1,4 @@ -export { injectRealmIntoSandbox } from './detect'; +export { injectRealmIntoSandbox, detectDuplicateRealms } from './detect'; export { default as realm } from './realm'; export type { ParentProcessRealm } from './ParentProcessRealm'; export type { ChildProcessRealm } from './ChildProcessRealm'; diff --git a/src/reporter.ts b/src/reporter.ts index 896af7a..280f7a4 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -10,11 +10,13 @@ import type { TestResult, } from '@jest/reporters'; import { JestMetadataError } from './errors'; -import { realm as unknownRealm } from './realms'; +import { detectDuplicateRealms, realm as unknownRealm } from './realms'; import type { ParentProcessRealm } from './realms'; const realm = unknownRealm as ParentProcessRealm; +detectDuplicateRealms(true); + export const query = realm.query; /** @@ -32,6 +34,7 @@ export class JestMetadataReporter implements Reporter { } onRunStart(_results: AggregatedResult, _options: ReporterOnStartOptions): Promise { + detectDuplicateRealms(false); return realm.reporterServer.onRunStart(); }