Skip to content

Commit 5b58b39

Browse files
authoredFeb 27, 2024··
fix(forks): wrap defines to support undefined values (#5284)
1 parent 9abef3d commit 5b58b39

File tree

10 files changed

+83
-41
lines changed

10 files changed

+83
-41
lines changed
 

‎packages/vitest/src/node/pools/forks.ts

+2-14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { ContextRPC, ContextTestEnvironment, ResolvedConfig, RunnerRPC, Run
88
import type { PoolProcessOptions, ProcessPool, RunWithFiles } from '../pool'
99
import type { WorkspaceProject } from '../workspace'
1010
import { envsOrder, groupFilesByEnv } from '../../utils/test-helpers'
11+
import { wrapSerializableConfig } from '../../utils/config-helpers'
1112
import { groupBy, resolve } from '../../utils'
1213
import { createMethodsRPC } from './rpc'
1314

@@ -44,12 +45,6 @@ function createChildProcessChannel(project: WorkspaceProject) {
4445
return { channel, cleanup }
4546
}
4647

47-
function stringifyRegex(input: RegExp | string): string {
48-
if (typeof input === 'string')
49-
return input
50-
return `$$vitest:${input.toString()}`
51-
}
52-
5348
export function createForksPool(ctx: Vitest, { execArgv, env }: PoolProcessOptions): ProcessPool {
5449
const numCpus
5550
= typeof nodeos.availableParallelism === 'function'
@@ -144,14 +139,7 @@ export function createForksPool(ctx: Vitest, { execArgv, env }: PoolProcessOptio
144139
return configs.get(project)!
145140

146141
const _config = project.getSerializableConfig()
147-
148-
const config = {
149-
..._config,
150-
// v8 serialize does not support regex
151-
testNamePattern: _config.testNamePattern
152-
? stringifyRegex(_config.testNamePattern)
153-
: undefined,
154-
} as ResolvedConfig
142+
const config = wrapSerializableConfig(_config)
155143

156144
configs.set(project, config)
157145
return config

‎packages/vitest/src/node/pools/vmForks.ts

+2-13
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { groupFilesByEnv } from '../../utils/test-helpers'
1212
import { AggregateError } from '../../utils/base'
1313
import type { WorkspaceProject } from '../workspace'
1414
import { getWorkerMemoryLimit, stringToBytes } from '../../utils/memory-limit'
15+
import { wrapSerializableConfig } from '../../utils/config-helpers'
1516
import { createMethodsRPC } from './rpc'
1617

1718
const suppressWarningsPath = resolve(rootDir, './suppress-warnings.cjs')
@@ -49,12 +50,6 @@ function createChildProcessChannel(project: WorkspaceProject) {
4950
return { channel, cleanup }
5051
}
5152

52-
function stringifyRegex(input: RegExp | string): string {
53-
if (typeof input === 'string')
54-
return input
55-
return `$$vitest:${input.toString()}`
56-
}
57-
5853
export function createVmForksPool(ctx: Vitest, { execArgv, env }: PoolProcessOptions): ProcessPool {
5954
const numCpus
6055
= typeof nodeos.availableParallelism === 'function'
@@ -149,14 +144,8 @@ export function createVmForksPool(ctx: Vitest, { execArgv, env }: PoolProcessOpt
149144
return configs.get(project)!
150145

151146
const _config = project.getSerializableConfig()
147+
const config = wrapSerializableConfig(_config)
152148

153-
const config = {
154-
..._config,
155-
// v8 serialize does not support regex
156-
testNamePattern: _config.testNamePattern
157-
? stringifyRegex(_config.testNamePattern)
158-
: undefined,
159-
} as ResolvedConfig
160149
configs.set(project, config)
161150
return config
162151
}

‎packages/vitest/src/runtime/workers/forks.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import v8 from 'node:v8'
22
import type { WorkerGlobalState } from '../../types/worker'
3-
import { createForksRpcOptions, unwrapForksConfig } from './utils'
3+
import { createForksRpcOptions, unwrapSerializableConfig } from './utils'
44
import { runBaseTests } from './base'
55
import type { VitestWorker } from './types'
66

@@ -13,7 +13,7 @@ class ForksBaseWorker implements VitestWorker {
1313
// TODO: don't rely on reassigning process.exit
1414
// https://github.com/vitest-dev/vitest/pull/4441#discussion_r1443771486
1515
const exit = process.exit
16-
state.ctx.config = unwrapForksConfig(state.ctx.config)
16+
state.ctx.config = unwrapSerializableConfig(state.ctx.config)
1717

1818
try {
1919
await runBaseTests(state)

‎packages/vitest/src/runtime/workers/utils.ts

+23-9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import type { WorkerContext } from '../../types/worker'
44
import type { ResolvedConfig } from '../../types/config'
55
import type { WorkerRpcOptions } from './types'
66

7+
const REGEXP_WRAP_PREFIX = '$$vitest:'
8+
79
export function createThreadsRpcOptions({ port }: WorkerContext): WorkerRpcOptions {
810
return {
911
post: (v) => { port.postMessage(v) },
@@ -28,15 +30,27 @@ export function createForksRpcOptions(nodeV8: typeof import('v8')): WorkerRpcOpt
2830
}
2931
}
3032

31-
function parsePossibleRegexp(str: string | RegExp) {
32-
const prefix = '$$vitest:'
33-
if (typeof str === 'string' && str.startsWith(prefix))
34-
return parseRegexp(str.slice(prefix.length))
35-
return str
36-
}
33+
/**
34+
* Reverts the wrapping done by `utils/config-helpers.ts`'s `wrapSerializableConfig`
35+
*/
36+
export function unwrapSerializableConfig(config: ResolvedConfig) {
37+
if (config.testNamePattern && typeof config.testNamePattern === 'string') {
38+
const testNamePattern = config.testNamePattern as string
39+
40+
if (testNamePattern.startsWith(REGEXP_WRAP_PREFIX))
41+
config.testNamePattern = parseRegexp(testNamePattern.slice(REGEXP_WRAP_PREFIX.length))
42+
}
43+
44+
if (config.defines && Array.isArray(config.defines.keys) && config.defines.original) {
45+
const { keys, original } = config.defines
46+
const defines: ResolvedConfig['defines'] = {}
47+
48+
// Apply all keys from the original. Entries which had undefined value are missing from original now
49+
for (const key of keys)
50+
defines[key] = original[key]
51+
52+
config.defines = defines
53+
}
3754

38-
export function unwrapForksConfig(config: ResolvedConfig) {
39-
if (config.testNamePattern)
40-
config.testNamePattern = parsePossibleRegexp(config.testNamePattern) as RegExp
4155
return config
4256
}

‎packages/vitest/src/runtime/workers/vmForks.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import v8 from 'node:v8'
22
import type { WorkerGlobalState } from '../../types/worker'
3-
import { createForksRpcOptions, unwrapForksConfig } from './utils'
3+
import { createForksRpcOptions, unwrapSerializableConfig } from './utils'
44
import type { VitestWorker } from './types'
55
import { runVmTests } from './vm'
66

@@ -11,7 +11,7 @@ class ForksVmWorker implements VitestWorker {
1111

1212
async runTests(state: WorkerGlobalState) {
1313
const exit = process.exit
14-
state.ctx.config = unwrapForksConfig(state.ctx.config)
14+
state.ctx.config = unwrapSerializableConfig(state.ctx.config)
1515

1616
try {
1717
await runVmTests(state)

‎packages/vitest/src/utils/config-helpers.ts

+25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import type { ResolvedConfig } from '../types/config'
12
import type { BenchmarkBuiltinReporters, BuiltinReporters } from '../node/reporters'
23

4+
const REGEXP_WRAP_PREFIX = '$$vitest:'
5+
36
interface PotentialConfig {
47
outputFile?: string | Partial<Record<string, string>>
58
}
@@ -13,3 +16,25 @@ export function getOutputFile(config: PotentialConfig | undefined, reporter: Bui
1316

1417
return config.outputFile[reporter]
1518
}
19+
20+
/**
21+
* Prepares `ResolvedConfig` for serialization, e.g. `node:v8.serialize`
22+
*/
23+
export function wrapSerializableConfig(config: ResolvedConfig) {
24+
let testNamePattern = config.testNamePattern
25+
let defines = config.defines
26+
27+
// v8 serialize does not support regex
28+
if (testNamePattern && typeof testNamePattern !== 'string')
29+
testNamePattern = `${REGEXP_WRAP_PREFIX}${testNamePattern.toString()}` as unknown as RegExp
30+
31+
// v8 serialize drops properties with undefined value
32+
if (defines)
33+
defines = { keys: Object.keys(defines), original: defines }
34+
35+
return {
36+
...config,
37+
testNamePattern,
38+
defines,
39+
} as ResolvedConfig
40+
}

‎packages/vitest/src/workers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { createForksRpcOptions, createThreadsRpcOptions, unwrapForksConfig } from './runtime/workers/utils'
1+
export { createForksRpcOptions, createThreadsRpcOptions, unwrapSerializableConfig } from './runtime/workers/utils'
22
export { provideWorkerState } from './utils/global'
33
export { run as runVitestWorker } from './runtime/worker'
44
export { runVmTests } from './runtime/workers/vm'

‎test/core/test/define-ssr.test.ts

+11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { afterAll, expect, test } from 'vitest'
33
declare let __DEFINE__: string
44
declare let __JSON__: any
55
declare let __MODE__: string
6+
declare let __UNDEFINED__: undefined
7+
declare let __NULL__: null
8+
declare let __ZERO__: 0
9+
declare let __FALSE__: false
610
declare let SOME: {
711
VARIABLE: string
812
SOME: {
@@ -64,3 +68,10 @@ test('dotted defines are processed by Vite, but cannot be reassigned', () => {
6468
SOME.VARIABLE = 'new variable'
6569
expect(SOME.VARIABLE).not.toBe('new variable')
6670
})
71+
72+
test('falsy defines are passed', () => {
73+
expect(__UNDEFINED__).toBe(undefined)
74+
expect(__NULL__).toBe(null)
75+
expect(__ZERO__).toBe(0)
76+
expect(__FALSE__).toBe(false)
77+
})

‎test/core/test/define-web.test.ts

+11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import { afterAll, expect, test } from 'vitest'
55
declare let __DEFINE__: string
66
declare let __JSON__: any
77
declare let __MODE__: string
8+
declare let __UNDEFINED__: undefined
9+
declare let __NULL__: null
10+
declare let __ZERO__: 0
11+
declare let __FALSE__: false
812
declare let SOME: {
913
VARIABLE: string
1014
SOME: {
@@ -61,3 +65,10 @@ test('dotted defines can be reassigned', () => {
6165
SOME.VARIABLE = 'new variable'
6266
expect(SOME.VARIABLE).toBe('new variable')
6367
})
68+
69+
test('falsy defines are passed', () => {
70+
expect(__UNDEFINED__).toBe(undefined)
71+
expect(__NULL__).toBe(null)
72+
expect(__ZERO__).toBe(0)
73+
expect(__FALSE__).toBe(false)
74+
})

‎test/core/vite.config.ts

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ export default defineConfig({
3232
'__MODE__': 'process.env.MODE',
3333
'SOME.VARIABLE': '"variable"',
3434
'SOME.SOME.VARIABLE': '"nested variable"',
35+
'__UNDEFINED__': undefined,
36+
'__NULL__': null,
37+
'__ZERO__': 0,
38+
'__FALSE__': false,
3539
},
3640
resolve: {
3741
alias: [

0 commit comments

Comments
 (0)
Please sign in to comment.