diff --git a/packages/vitest/src/integrations/env/happy-dom.ts b/packages/vitest/src/integrations/env/happy-dom.ts index 917fccb47d5b..f425bea46ff4 100644 --- a/packages/vitest/src/integrations/env/happy-dom.ts +++ b/packages/vitest/src/integrations/env/happy-dom.ts @@ -15,9 +15,6 @@ export default ({ }) return { - getGlobal() { - return win.globalThis - }, getVmContext() { return win }, diff --git a/packages/vitest/src/integrations/env/node.ts b/packages/vitest/src/integrations/env/node.ts index bc6807e329d7..c2b68cf254c3 100644 --- a/packages/vitest/src/integrations/env/node.ts +++ b/packages/vitest/src/integrations/env/node.ts @@ -1,17 +1,108 @@ import { Console } from 'node:console' -import { importModule } from 'local-pkg' import type { Environment } from '../../types' +// some globals we do not want, either because deprecated or we set it ourselves +const denyList = new Set([ + 'GLOBAL', + 'root', + 'global', + 'Buffer', + 'ArrayBuffer', + 'Uint8Array', +]) + +const nodeGlobals = new Map( + Object.getOwnPropertyNames(globalThis) + .filter(global => !denyList.has(global)) + .map((nodeGlobalsKey) => { + const descriptor = Object.getOwnPropertyDescriptor( + globalThis, + nodeGlobalsKey, + ) + + if (!descriptor) { + throw new Error( + `No property descriptor for ${nodeGlobalsKey}, this is a bug in Jest.`, + ) + } + + return [nodeGlobalsKey, descriptor] + }), +) + export default ({ name: 'node', + // this is largely copied from jest's node environment async setupVM() { - const vm = await importModule('node:vm') - const global = {} // TODO: copy more globals + const vm = await import('node:vm') const context = vm.createContext() + const global = vm.runInContext( + 'this', + context, + ) + + const contextGlobals = new Set(Object.getOwnPropertyNames(global)) + for (const [nodeGlobalsKey, descriptor] of nodeGlobals) { + if (!contextGlobals.has(nodeGlobalsKey)) { + if (descriptor.configurable) { + Object.defineProperty(global, nodeGlobalsKey, { + configurable: true, + enumerable: descriptor.enumerable, + get() { + // @ts-expect-error: no index signature + const val = globalThis[nodeGlobalsKey] as unknown + + // override lazy getter + Object.defineProperty(global, nodeGlobalsKey, { + configurable: true, + enumerable: descriptor.enumerable, + value: val, + writable: + descriptor.writable === true + // Node 19 makes performance non-readable. This is probably not the correct solution. + || nodeGlobalsKey === 'performance', + }) + return val + }, + set(val: unknown) { + // override lazy getter + Object.defineProperty(global, nodeGlobalsKey, { + configurable: true, + enumerable: descriptor.enumerable, + value: val, + writable: true, + }) + }, + }) + } + else if ('value' in descriptor) { + Object.defineProperty(global, nodeGlobalsKey, { + configurable: false, + enumerable: descriptor.enumerable, + value: descriptor.value, + writable: descriptor.writable, + }) + } + else { + Object.defineProperty(global, nodeGlobalsKey, { + configurable: false, + enumerable: descriptor.enumerable, + get: descriptor.get, + set: descriptor.set, + }) + } + } + } + + global.global = global + global.Buffer = Buffer + global.ArrayBuffer = ArrayBuffer + // TextEncoder (global or via 'util') references a Uint8Array constructor + // different than the global one used by users in tests. This makes sure the + // same constructor is referenced by both. + global.Uint8Array = Uint8Array + return { - getGlobal() { - return global - }, getVmContext() { return context }, diff --git a/packages/vitest/src/runtime/vm.ts b/packages/vitest/src/runtime/vm.ts index f4353900a8a0..54861efbb09a 100644 --- a/packages/vitest/src/runtime/vm.ts +++ b/packages/vitest/src/runtime/vm.ts @@ -1,4 +1,5 @@ -import { URL, pathToFileURL } from 'node:url' +import { pathToFileURL } from 'node:url' +import { performance } from 'node:perf_hooks' import { ModuleCacheMap } from 'vite-node/client' import { workerId as poolId } from 'tinypool' import { createBirpc } from 'birpc' @@ -64,17 +65,9 @@ export async function run(ctx: WorkerContext) { const context = vm.getVmContext() context.__vitest_worker__ = state - // TODO: all globals for test/core to work - // TODO: copy more globals + // this is unfortunately needed for our own dependencies + // we need to find a way to not rely on this by default context.process = process - context.URL ??= URL - context.performance ??= performance - context.setImmediate = setImmediate - context.clearImmediate = clearImmediate - context.setTimeout = setTimeout - context.clearTimeout = clearTimeout - context.setInterval = setInterval - context.clearInterval = clearInterval context.global = context context.console = createCustomConsole(state) diff --git a/packages/vitest/src/types/general.ts b/packages/vitest/src/types/general.ts index 4ab98281798e..471f008729c2 100644 --- a/packages/vitest/src/types/general.ts +++ b/packages/vitest/src/types/general.ts @@ -20,7 +20,7 @@ export interface ModuleCache { } export interface EnvironmentReturn { - teardown: (global: any) => Awaitable + teardown(global: any): Awaitable } export interface VmEnvironmentReturn {