Skip to content

Commit 5b58b39

Browse files
authored
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

Lines changed: 2 additions & 14 deletions
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

Lines changed: 2 additions & 13 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 23 additions & 9 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 25 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 11 additions & 0 deletions
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

Lines changed: 11 additions & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy