fix(testing): preserve parallel module evaluation mocks

This commit is contained in:
YeonGyu-Kim
2026-05-30 16:04:59 +09:00
parent 7e1cfbfdc2
commit 475a12a08d
2 changed files with 55 additions and 2 deletions
@@ -113,6 +113,49 @@ describe("installModuleMockLifecycle active-test tracking", () => {
])
})
test("treats module-evaluation mocks from another file as persistent while a different test is active", () => {
// given
const events: string[] = []
let callerStack = "Error\n at file:///repo/tests/first.test.ts:5:1\n at test (native:1:11)"
let callerUrl = "file:///repo/tests/first.test.ts"
const mockApi = {
module: (specifier: string, factory: () => Record<string, unknown>) => {
events.push(`module:${specifier}:${String(factory().named)}`)
},
restore: mock(() => {
events.push("delegate:restore")
}),
}
const { beginTestMockTracking, endTestMockTracking } = installModuleMockLifecycle(mockApi, {
getCallerStack: () => callerStack,
getCallerUrl: () => callerUrl,
trackOnlyDuringActiveTest: true,
isPersistentModuleMockOwner: () => true,
resolveSpecifier: (specifier, ownerUrl) => `resolved:${ownerUrl}:${specifier}`,
loadOriginalModule: () => ({ ok: true, value: { named: "original" } }),
})
beginTestMockTracking()
callerStack = "Error\n at file:///repo/tests/second.test.ts:5:1\n at moduleEvaluation (native:1:11)"
callerUrl = "file:///repo/tests/second.test.ts"
mockApi.module("./dependency", () => ({ named: "top-level second" }))
// when
callerStack = "Error\n at file:///repo/tests/first.test.ts:10:1\n at cleanup (native:1:11)"
callerUrl = "file:///repo/tests/first.test.ts"
mockApi.restore()
endTestMockTracking()
// then
expect(events).toEqual([
"module:./dependency:top-level second",
"delegate:restore",
"module:./dependency:top-level second",
"module:resolved:file:///repo/tests/second.test.ts:./dependency:top-level second",
])
})
test("clears persistent module-evaluation snapshots when restore runs while inactive", () => {
// given
const events: string[] = []
+12 -2
View File
@@ -1,6 +1,6 @@
import { createRequire } from "node:module"
import { fileURLToPath } from "node:url"
import { defaultGetCallerStack, resolveCallerUrlFromStack } from "./module-mock-stack"
import { defaultGetCallerStack, isModuleEvaluationStack, resolveCallerUrlFromStack } from "./module-mock-stack"
import { createRestoreExports } from "./module-mock-restore-exports"
type MockModuleFactory = () => Record<string, unknown>
@@ -98,6 +98,7 @@ export function installModuleMockLifecycle(
let lastRestoredSnapshots: ModuleSnapshot[] = []
let isActiveTest = !options.trackOnlyDuringActiveTest
let hasStartedTest = false
let activeTestOwnerUrl: string | null = null
const delegateModule = mockApi.module.bind(mockApi)
const delegateRestore = mockApi.restore.bind(mockApi)
const getCallerStack = options.getCallerStack ?? defaultGetCallerStack
@@ -200,18 +201,27 @@ export function installModuleMockLifecycle(
function beginTestMockTracking(): void {
hasStartedTest = true
isActiveTest = true
const callerStack = getCallerStack()
activeTestOwnerUrl = getCallerUrl(callerStack)
}
function endTestMockTracking(): void {
isActiveTest = !options.trackOnlyDuringActiveTest
activeTestOwnerUrl = null
}
mockApi.module = (specifier: string, factory: MockModuleFactory): unknown => {
lastRestoredSnapshots = []
const callerStack = getCallerStack()
const callerUrl = getCallerUrl(callerStack)
const isParallelFileEvaluationMock =
isActiveTest &&
options.trackOnlyDuringActiveTest === true &&
activeTestOwnerUrl !== null &&
callerUrl !== activeTestOwnerUrl &&
isModuleEvaluationStack(callerStack)
if (!isActiveTest && isPersistentModuleMockOwner(callerUrl)) {
if ((!isActiveTest || isParallelFileEvaluationMock) && isPersistentModuleMockOwner(callerUrl)) {
const resolvedSpecifier = resolveSpecifier(specifier, callerUrl)
const snapshotsByOwner = persistentSnapshots.get(resolvedSpecifier) ?? new Map<string, PersistentModuleSnapshot>()
const existingSnapshot = snapshotsByOwner.get(callerUrl)