Skip to content

Commit 05b4178

Browse files
authored
fix(jsdom): override globals that Fetch API relies on (#8390)
1 parent 202d5d6 commit 05b4178

File tree

3 files changed

+47
-13
lines changed

3 files changed

+47
-13
lines changed

packages/vitest/src/integrations/env/jsdom-keys.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
const LIVING_KEYS = [
44
'DOMException',
5-
'URL',
6-
'URLSearchParams',
75
'EventTarget',
86
'NamedNodeMap',
97
'Node',
@@ -160,9 +158,6 @@ const LIVING_KEYS = [
160158
'ShadowRoot',
161159
'MutationObserver',
162160
'MutationRecord',
163-
'Headers',
164-
'AbortController',
165-
'AbortSignal',
166161

167162
'Uint8Array',
168163
'Uint16Array',
@@ -183,6 +178,13 @@ const LIVING_KEYS = [
183178
'Option',
184179

185180
'CSS',
181+
182+
// Conflict with Node.js values
183+
// 'Headers',
184+
// 'AbortController',
185+
// 'AbortSignal',
186+
// 'URL',
187+
// 'URLSearchParams',
186188
]
187189

188190
const OTHER_KEYS = [

packages/vitest/src/integrations/env/jsdom.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,6 @@ export default <Environment>{
8080
// https://nodejs.org/dist/latest/docs/api/globals.html
8181
const globalNames = [
8282
'structuredClone',
83-
'fetch',
84-
'Request',
85-
'Response',
8683
'BroadcastChannel',
8784
'MessageChannel',
8885
'MessagePort',
@@ -99,6 +96,25 @@ export default <Environment>{
9996
}
10097
}
10198

99+
// since we are providing Node.js's Fetch API,
100+
// we also should override other APIs they use
101+
const overrideGlobals = [
102+
'fetch',
103+
'Request',
104+
'Response',
105+
'Headers',
106+
'AbortController',
107+
'AbortSignal',
108+
'URL',
109+
'URLSearchParams',
110+
] as const
111+
for (const name of overrideGlobals) {
112+
const value = globalThis[name]
113+
if (typeof value !== 'undefined') {
114+
dom.window[name] = value as any
115+
}
116+
}
117+
102118
return {
103119
getVmContext() {
104120
return dom.getInternalVMContext()

test/core/test/environments/jsdom.spec.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,16 @@ import { stripVTControlCharacters } from 'node:util'
44
import { processError } from '@vitest/utils/error'
55
import { expect, test } from 'vitest'
66

7-
const nodeMajor = Number(process.version.slice(1).split('.')[0])
8-
9-
test.runIf(nodeMajor >= 15)('MessageChannel and MessagePort are available', () => {
7+
test('MessageChannel and MessagePort are available', () => {
108
expect(MessageChannel).toBeDefined()
119
expect(MessagePort).toBeDefined()
1210
})
1311

14-
test.runIf(nodeMajor >= 17)('structuredClone is available', () => {
12+
test('structuredClone is available', () => {
1513
expect(structuredClone).toBeDefined()
1614
})
1715

18-
test.runIf(nodeMajor >= 18)('fetch, Request, Response, and BroadcastChannel are available', () => {
16+
test('fetch, Request, Response, and BroadcastChannel are available', () => {
1917
expect(fetch).toBeDefined()
2018
expect(Request).toBeDefined()
2119
expect(Response).toBeDefined()
@@ -24,6 +22,24 @@ test.runIf(nodeMajor >= 18)('fetch, Request, Response, and BroadcastChannel are
2422
expect(BroadcastChannel).toBeDefined()
2523
})
2624

25+
test('Fetch API accepts other APIs', () => {
26+
expect.soft(() => new Request('http://localhost', { signal: new AbortController().signal })).not.toThrowError()
27+
expect.soft(() => new Request('http://localhost', { method: 'POST', body: new FormData() })).not.toThrowError()
28+
expect.soft(() => new Request('http://localhost', { method: 'POST', body: new Blob() })).not.toThrowError()
29+
expect.soft(() => new Request(new URL('https://localhost'))).not.toThrowError()
30+
31+
const request = new Request('http://localhost')
32+
expect.soft(request.headers).toBeInstanceOf(Headers)
33+
34+
expect.soft(
35+
() => new Request('http://localhost', { method: 'POST', body: new URLSearchParams([['key', 'value']]) }),
36+
).not.toThrowError()
37+
38+
const searchParams = new URLSearchParams()
39+
searchParams.set('key', 'value')
40+
expect.soft(() => new Request('http://localhost', { method: 'POST', body: searchParams })).not.toThrowError()
41+
})
42+
2743
test('atob and btoa are available', () => {
2844
expect(atob('aGVsbG8gd29ybGQ=')).toBe('hello world')
2945
expect(btoa('hello world')).toBe('aGVsbG8gd29ybGQ=')

0 commit comments

Comments
 (0)