Skip to content

Commit 9752a5b

Browse files
authored
test: simplify support matrix (#1052)
1 parent bc4b293 commit 9752a5b

File tree

1 file changed

+106
-43
lines changed

1 file changed

+106
-43
lines changed

packages/nuxt-cli/test/e2e/runtimes.spec.ts

Lines changed: 106 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,128 @@
11
import type { ChildProcess } from 'node:child_process'
2+
import type { TestOptions } from 'vitest'
23
import { spawn, spawnSync } from 'node:child_process'
34
import { cpSync, rmSync } from 'node:fs'
45
import { rm } from 'node:fs/promises'
56
import { join, resolve } from 'node:path'
6-
import { fileURLToPath } from 'node:url'
77

8+
import { fileURLToPath } from 'node:url'
89
import { getPort, waitForPort } from 'get-port-please'
9-
import { isCI, isLinux, isWindows } from 'std-env'
10+
import { isCI, isLinux, isMacOS, isWindows } from 'std-env'
1011
import { WebSocket } from 'undici'
11-
import { afterAll, describe, expect, it, vi } from 'vitest'
12+
import { it as _it, afterAll, describe, expect, vi } from 'vitest'
1213

1314
const playgroundDir = fileURLToPath(new URL('../../../../playground', import.meta.url))
1415
const nuxiPath = join(fileURLToPath(new URL('../..', import.meta.url)), 'bin/nuxi.mjs')
1516

16-
const hasBun = spawnSync('bun', ['--version'], { stdio: 'ignore' }).status === 0
17-
const hasDeno = spawnSync('deno', ['--version'], { stdio: 'ignore' }).status === 0
17+
const runtimes = ['bun', 'node', 'deno'] as const
1818

19-
describe.sequential.each(['bun', 'node', 'deno'] as const)('dev server (%s)', (runtime) => {
20-
let server: DevServerInstance
19+
const platform = {
20+
windows: isWindows,
21+
linux: isLinux,
22+
macos: isMacOS,
23+
}
2124

22-
if (runtime === 'bun' && !hasBun && !isCI) {
23-
console.warn('Not testing locally with bun as it is not installed.')
24-
it.skip('should pass with bun')
25-
return
25+
const runtime = {
26+
bun: spawnSync('bun', ['--version'], { stdio: 'ignore' }).status === 0,
27+
deno: spawnSync('deno', ['--version'], { stdio: 'ignore' }).status === 0,
28+
node: true,
29+
}
30+
31+
type SupportStatus = boolean | {
32+
start: boolean
33+
fetching: boolean
34+
websockets: boolean
35+
websocketClose: boolean
36+
}
37+
38+
const supports: Record<typeof runtimes[number], SupportStatus> = {
39+
node: true,
40+
bun: {
41+
start: true,
42+
fetching: !platform.windows,
43+
websockets: false,
44+
websocketClose: false,
45+
},
46+
deno: {
47+
start: !platform.windows,
48+
fetching: !platform.windows,
49+
websockets: platform.linux && !platform.windows,
50+
websocketClose: false,
51+
},
52+
}
53+
54+
function createIt(status: SupportStatus) {
55+
function it(description: string, fn: () => Promise<void>): void
56+
function it(description: string, options: TestOptions, fn: () => Promise<void>): void
57+
function it(description: string, _options: TestOptions | (() => Promise<void>), _fn?: () => Promise<void>): void {
58+
const fn = typeof _options === 'function' ? _options : _fn!
59+
const options = typeof _options === 'function' ? {} : _options
60+
61+
if (status === false) {
62+
return _it.fails(description, options, fn)
63+
}
64+
if (status === true) {
65+
return _it(description, options, fn)
66+
}
67+
if (description.includes('should start dev server')) {
68+
if (!status.start) {
69+
return _it.fails(description, options, fn)
70+
}
71+
return _it(description, options, fn)
72+
}
73+
if (!status.start) {
74+
return _it.todo(description)
75+
}
76+
if (description.includes('websocket connection close gracefully')) {
77+
if (!status.websocketClose) {
78+
return _it.fails(description, options, fn)
79+
}
80+
return _it(description, options, fn)
81+
}
82+
if (description.includes('websocket')) {
83+
if (!status.websockets) {
84+
return _it.fails(description, options, fn)
85+
}
86+
return _it(description, options, fn)
87+
}
88+
// Handle fetching tests (all tests that are not websocket or start tests)
89+
if (!status.fetching) {
90+
return _it.fails(description, options, fn)
91+
}
92+
return _it(description, options, fn)
2693
}
2794

28-
if (runtime === 'deno' && !hasDeno && !isCI) {
29-
console.warn('Not testing locally with deno as it is not installed.')
30-
it.skip('should pass with deno')
95+
return it
96+
}
97+
98+
describe.sequential.each(runtimes)('dev server (%s)', (runtimeName) => {
99+
let server: DevServerInstance
100+
101+
if (!isCI && !runtime[runtimeName]) {
102+
console.warn(`Not testing locally with ${runtimeName} as it is not installed.`)
103+
_it.skip(`should pass with ${runtimeName}`)
31104
return
32105
}
33106

34-
const cwd = resolve(playgroundDir, `../playground-${runtime}`)
107+
const cwd = resolve(playgroundDir, `../playground-${runtimeName}`)
35108

36109
afterAll(async () => {
37110
await server?.close()
38111
await rm(cwd, { recursive: true, force: true }).catch(() => null)
39112
})
40113

41-
const isWindowsNonDeno = isWindows && runtime === 'deno'
42-
const assertNonDeno = isWindowsNonDeno ? it.fails : it
43-
assertNonDeno('should start dev server', { timeout: isCI ? 60_000 : 30_000 }, async () => {
114+
const it = createIt(supports[runtimeName])
115+
116+
it('should start dev server', { timeout: isCI ? 60_000 : 30_000 }, async () => {
44117
rmSync(cwd, { recursive: true, force: true })
45118
cpSync(playgroundDir, cwd, {
46119
recursive: true,
47120
filter: src => !src.includes('.nuxt') && !src.includes('.output'),
48121
})
49-
server = await startDevServer({ cwd, runtime })
122+
server = await startDevServer({ cwd, runtime: runtimeName })
50123
})
51124

52-
if (isWindowsNonDeno) {
53-
it.todo('should run rest of tests on windows')
54-
return
55-
}
56-
57-
const failsOnlyWithWindowsBun = runtime === 'bun' && isWindows ? it.fails : it
58-
failsOnlyWithWindowsBun('should serve the main page', async () => {
125+
it('should serve the main page', async () => {
59126
const response = await fetch(server.url)
60127
expect(response.status).toBe(200)
61128

@@ -64,18 +131,18 @@ describe.sequential.each(['bun', 'node', 'deno'] as const)('dev server (%s)', (r
64131
expect(html).toContain('<!DOCTYPE html>')
65132
})
66133

67-
failsOnlyWithWindowsBun('should serve static assets', async () => {
134+
it('should serve static assets', async () => {
68135
const response = await fetch(`${server.url}/favicon.ico`)
69136
expect(response.status).toBe(200)
70137
expect(response.headers.get('content-type')).toContain('image/')
71138
})
72139

73-
failsOnlyWithWindowsBun('should handle API routes', async () => {
140+
it('should handle API routes', async () => {
74141
const response = await fetch(`${server.url}/api/hello`)
75142
expect(response.status).toBe(200)
76143
})
77144

78-
failsOnlyWithWindowsBun('should handle POST requests', async () => {
145+
it('should handle POST requests', async () => {
79146
const response = await fetch(`${server.url}/api/echo`, {
80147
method: 'POST',
81148
headers: { 'Content-Type': 'application/json' },
@@ -85,7 +152,7 @@ describe.sequential.each(['bun', 'node', 'deno'] as const)('dev server (%s)', (r
85152
expect(response.status).toBe(200)
86153
})
87154

88-
failsOnlyWithWindowsBun('should preserve request headers', async () => {
155+
it('should preserve request headers', async () => {
89156
const headers = {
90157
'X-Custom-Header': 'test-value',
91158
'User-Agent': 'vitest',
@@ -102,7 +169,7 @@ describe.sequential.each(['bun', 'node', 'deno'] as const)('dev server (%s)', (r
102169
expect(res.status).toBe(200)
103170
})
104171

105-
failsOnlyWithWindowsBun('should handle concurrent requests', async () => {
172+
it('should handle concurrent requests', async () => {
106173
const requests = Array.from({ length: 5 }, () => fetch(server.url))
107174
const responses = await Promise.all(requests)
108175

@@ -112,7 +179,7 @@ describe.sequential.each(['bun', 'node', 'deno'] as const)('dev server (%s)', (r
112179
}
113180
})
114181

115-
failsOnlyWithWindowsBun('should handle large request payloads', async () => {
182+
it('should handle large request payloads', async () => {
116183
const largePayload = { data: 'x'.repeat(10_000) }
117184
const response = await fetch(`${server.url}/api/echo`, {
118185
method: 'POST',
@@ -125,7 +192,7 @@ describe.sequential.each(['bun', 'node', 'deno'] as const)('dev server (%s)', (r
125192
expect(result.echoed.data).toBe(largePayload.data)
126193
})
127194

128-
failsOnlyWithWindowsBun('should handle different HTTP methods', async () => {
195+
it('should handle different HTTP methods', async () => {
129196
const methods = ['GET', 'POST', 'PUT', 'DELETE']
130197

131198
for (const method of methods) {
@@ -137,9 +204,7 @@ describe.sequential.each(['bun', 'node', 'deno'] as const)('dev server (%s)', (r
137204
}
138205
})
139206

140-
// TODO: fix websockets in bun + deno
141-
const failsWithBunOrNonLinuxDeno = runtime === 'bun' || (runtime === 'deno' && !isLinux) ? it.fails : it
142-
failsWithBunOrNonLinuxDeno('should establish websocket connection and handle ping/pong', async () => {
207+
it('should establish websocket connection and handle ping/pong', { timeout: 20_000 }, async () => {
143208
const wsUrl = `${server.url.replace('http', 'ws')}/_ws`
144209

145210
// Create a promise that resolves when the websocket test is complete
@@ -188,10 +253,9 @@ describe.sequential.each(['bun', 'node', 'deno'] as const)('dev server (%s)', (r
188253
})
189254

190255
await wsTest
191-
}, 20_000)
256+
})
192257

193-
// TODO: fix websockets in bun + deno
194-
failsWithBunOrNonLinuxDeno('should handle multiple concurrent websocket connections', async () => {
258+
it('should handle multiple concurrent websocket connections', { timeout: 20_000 }, async () => {
195259
const wsUrl = `${server.url.replace('http', 'ws')}/_ws`
196260
const connectionCount = 3
197261

@@ -225,10 +289,9 @@ describe.sequential.each(['bun', 'node', 'deno'] as const)('dev server (%s)', (r
225289
})
226290

227291
await Promise.all(connectionPromises)
228-
}, 15000)
292+
})
229293

230-
const failsWithBunOrDeno = runtime === 'bun' || runtime === 'deno' ? it.fails : it
231-
failsWithBunOrDeno('should handle websocket connection close gracefully', async () => {
294+
it('should handle websocket connection close gracefully', { timeout: 10_000 }, async () => {
232295
const wsUrl = `${server.url.replace('http', 'ws')}/_ws`
233296

234297
const wsTest = new Promise<void>((resolve, reject) => {
@@ -266,7 +329,7 @@ describe.sequential.each(['bun', 'node', 'deno'] as const)('dev server (%s)', (r
266329
})
267330

268331
await wsTest
269-
}, 10_000)
332+
})
270333
})
271334

272335
interface DevServerInstance {

0 commit comments

Comments
 (0)