From da6dc9d4f77e2237c2f04d327f40d2098e364535 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 12 May 2022 13:12:43 +0800 Subject: [PATCH] test: refactor test utils and setup (#8135) --- .eslintrc.cjs | 3 +- playground/cli-module/__tests__/serve.ts | 34 ++- playground/cli/__tests__/serve.ts | 34 ++- playground/legacy/__tests__/ssr/serve.ts | 8 +- playground/lib/__tests__/lib.spec.ts | 6 +- playground/lib/__tests__/serve.ts | 24 +- .../optimize-missing-deps/__test__/serve.ts | 8 +- .../__tests__/resolve-config.spec.ts | 2 +- playground/resolve-config/__tests__/serve.ts | 7 +- playground/ssr-deps/__tests__/serve.ts | 8 +- playground/ssr-html/__tests__/serve.ts | 8 +- playground/ssr-pug/__tests__/serve.ts | 8 +- playground/ssr-react/__tests__/serve.ts | 14 +- playground/ssr-vue/__tests__/serve.ts | 14 +- playground/ssr-webworker/__tests__/serve.ts | 10 +- playground/test-utils.ts | 62 +++-- .../__tests__/serve.ts | 21 ++ .../tsconfig-json-load-error.spec.ts | 10 +- playground/vitestSetup.ts | 212 ++++++++++-------- .../worker/__tests__/es/es-worker.spec.ts | 6 +- .../worker/__tests__/iife/worker.spec.ts | 2 +- .../sourcemap-hidden-worker.spec.ts | 5 +- .../sourcemap-inline-worker.spec.ts | 5 +- .../sourcemap/sourcemap-worker.spec.ts | 2 +- playground/worker/index.html | 8 +- playground/worker/worker/main-format-es.js | 4 +- 26 files changed, 274 insertions(+), 251 deletions(-) create mode 100644 playground/tsconfig-json-load-error/__tests__/serve.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 9f6bf0deb86303..d5050bd4853167 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -120,7 +120,8 @@ module.exports = defineConfig({ 'node/no-extraneous-import': 'off', 'node/no-extraneous-require': 'off', 'node/no-missing-import': 'off', - 'node/no-missing-require': 'off' + 'node/no-missing-require': 'off', + 'no-undef': 'off' } }, { diff --git a/playground/cli-module/__tests__/serve.ts b/playground/cli-module/__tests__/serve.ts index 1085c5e924a1c3..87d036600f77e6 100644 --- a/playground/cli-module/__tests__/serve.ts +++ b/playground/cli-module/__tests__/serve.ts @@ -3,11 +3,18 @@ import execa from 'execa' import kill from 'kill-port' -import { isWindows, ports, viteBinPath } from '~utils' +import { + isBuild, + isWindows, + killProcess, + ports, + rootDir, + viteBinPath +} from '~utils' export const port = ports['cli-module'] -export async function serve(root: string, isProd: boolean) { +export async function serve() { // collect stdout and stderr streams from child processes here to avoid interfering with regular vitest output const streams = { build: { out: [], err: [] }, @@ -35,11 +42,11 @@ export async function serve(root: string, isProd: boolean) { } // only run `vite build` when needed - if (isProd) { + if (isBuild) { const buildCommand = `${viteBinPath} build` try { const buildProcess = execa.command(buildCommand, { - cwd: root, + cwd: rootDir, stdio: 'pipe' }) collectStreams('build', buildProcess) @@ -56,12 +63,12 @@ export async function serve(root: string, isProd: boolean) { // run `vite --port x` or `vite preview --port x` to start server const viteServerArgs = ['--port', `${port}`, '--strict-port'] - if (isProd) { + if (isBuild) { viteServerArgs.unshift('preview') } const serverCommand = `${viteBinPath} ${viteServerArgs.join(' ')}` const serverProcess = execa.command(serverCommand, { - cwd: root, + cwd: rootDir, stdio: 'pipe' }) collectStreams('server', serverProcess) @@ -71,7 +78,7 @@ export async function serve(root: string, isProd: boolean) { if (serverProcess) { const timeoutError = `server process still alive after 3s` try { - killProcess(serverProcess) + await killProcess(serverProcess) await resolvedOrTimeout(serverProcess, 10000, timeoutError) } catch (e) { if (e === timeoutError || (!serverProcess.killed && !isWindows)) { @@ -132,19 +139,6 @@ async function startedOnPort(serverProcess, port, timeout) { ).finally(() => serverProcess.stdout.off('data', checkPort)) } -// helper function to kill process, uses taskkill on windows to ensure child process is killed too -function killProcess(serverProcess) { - if (isWindows) { - try { - execa.commandSync(`taskkill /pid ${serverProcess.pid} /T /F`) - } catch (e) { - console.error('failed to taskkill:', e) - } - } else { - serverProcess.kill('SIGTERM', { forceKillAfterTimeout: 2000 }) - } -} - // helper function that rejects with errorMessage if promise isn't settled within ms async function resolvedOrTimeout(promise, ms, errorMessage) { let timer diff --git a/playground/cli/__tests__/serve.ts b/playground/cli/__tests__/serve.ts index 8e23db9d38686b..6f80726e309082 100644 --- a/playground/cli/__tests__/serve.ts +++ b/playground/cli/__tests__/serve.ts @@ -3,11 +3,18 @@ import execa from 'execa' import kill from 'kill-port' -import { isWindows, ports, viteBinPath } from '~utils' +import { + isBuild, + isWindows, + killProcess, + ports, + rootDir, + viteBinPath +} from '~utils' export const port = ports.cli -export async function serve(root: string, isProd: boolean) { +export async function serve() { // collect stdout and stderr streams from child processes here to avoid interfering with regular vitest output const streams = { build: { out: [], err: [] }, @@ -35,11 +42,11 @@ export async function serve(root: string, isProd: boolean) { } // only run `vite build` when needed - if (isProd) { + if (isBuild) { const buildCommand = `${viteBinPath} build` try { const buildProcess = execa.command(buildCommand, { - cwd: root, + cwd: rootDir, stdio: 'pipe' }) collectStreams('build', buildProcess) @@ -56,12 +63,12 @@ export async function serve(root: string, isProd: boolean) { // run `vite --port x` or `vite preview --port x` to start server const viteServerArgs = ['--port', `${port}`, '--strict-port'] - if (isProd) { + if (isBuild) { viteServerArgs.unshift('preview') } const serverCommand = `${viteBinPath} ${viteServerArgs.join(' ')}` const serverProcess = execa.command(serverCommand, { - cwd: root, + cwd: rootDir, stdio: 'pipe' }) collectStreams('server', serverProcess) @@ -71,7 +78,7 @@ export async function serve(root: string, isProd: boolean) { if (serverProcess) { const timeoutError = `server process still alive after 3s` try { - killProcess(serverProcess) + await killProcess(serverProcess) await resolvedOrTimeout(serverProcess, 3000, timeoutError) } catch (e) { if (e === timeoutError || (!serverProcess.killed && !isWindows)) { @@ -132,19 +139,6 @@ async function startedOnPort(serverProcess, port, timeout) { ).finally(() => serverProcess.stdout.off('data', checkPort)) } -// helper function to kill process, uses taskkill on windows to ensure child process is killed too -function killProcess(serverProcess) { - if (isWindows) { - try { - execa.commandSync(`taskkill /pid ${serverProcess.pid} /T /F`) - } catch (e) { - console.error('failed to taskkill:', e) - } - } else { - serverProcess.kill('SIGTERM', { forceKillAfterTimeout: 2000 }) - } -} - // helper function that rejects with errorMessage if promise isn't settled within ms async function resolvedOrTimeout(promise, ms, errorMessage) { let timer diff --git a/playground/legacy/__tests__/ssr/serve.ts b/playground/legacy/__tests__/ssr/serve.ts index 314d0e729d9590..fc0fba377160ac 100644 --- a/playground/legacy/__tests__/ssr/serve.ts +++ b/playground/legacy/__tests__/ssr/serve.ts @@ -1,14 +1,14 @@ // this is automatically detected by playground/vitestSetup.ts and will replace // the default e2e test serve behavior import path from 'path' -import { ports } from '~utils' +import { ports, rootDir } from '~utils' export const port = ports['legacy/ssr'] -export async function serve(root: string, _isProd: boolean) { +export async function serve() { const { build } = await import('vite') await build({ - root, + root: rootDir, logLevel: 'silent', build: { target: 'esnext', @@ -22,7 +22,7 @@ export async function serve(root: string, _isProd: boolean) { app.use('/', async (_req, res) => { const { render } = await import( - path.resolve(root, './dist/server/entry-server.js') + path.resolve(rootDir, './dist/server/entry-server.js') ) const html = await render() res.status(200).set({ 'Content-Type': 'text/html' }).end(html) diff --git a/playground/lib/__tests__/lib.spec.ts b/playground/lib/__tests__/lib.spec.ts index 470c124ed388dc..c64ccfa075d0e4 100644 --- a/playground/lib/__tests__/lib.spec.ts +++ b/playground/lib/__tests__/lib.spec.ts @@ -17,7 +17,7 @@ describe.runIf(isBuild)('build', () => { test('umd', async () => { expect(await page.textContent('.umd')).toBe('It works') const code = fs.readFileSync( - path.join(testDir(), 'dist/my-lib-custom-filename.umd.js'), + path.join(testDir, 'dist/my-lib-custom-filename.umd.js'), 'utf-8' ) // esbuild helpers are injected inside of the UMD wrapper @@ -27,7 +27,7 @@ describe.runIf(isBuild)('build', () => { test('iife', async () => { expect(await page.textContent('.iife')).toBe('It works') const code = fs.readFileSync( - path.join(testDir(), 'dist/my-lib-custom-filename.iife.js'), + path.join(testDir, 'dist/my-lib-custom-filename.iife.js'), 'utf-8' ) // esbuild helpers are injected inside of the IIFE wrapper @@ -40,7 +40,7 @@ describe.runIf(isBuild)('build', () => { 'hello vite' ) const code = fs.readFileSync( - path.join(testDir(), 'dist/lib/dynamic-import-message.es.js'), + path.join(testDir, 'dist/lib/dynamic-import-message.es.js'), 'utf-8' ) expect(code).not.toMatch('__vitePreload') diff --git a/playground/lib/__tests__/serve.ts b/playground/lib/__tests__/serve.ts index 863df82ef570d7..070b1073837913 100644 --- a/playground/lib/__tests__/serve.ts +++ b/playground/lib/__tests__/serve.ts @@ -4,19 +4,27 @@ import path from 'path' import http from 'http' import sirv from 'sirv' -import { page, ports, serverLogs, setViteUrl, viteTestUrl } from '~utils' +import { + isBuild, + page, + ports, + rootDir, + serverLogs, + setViteUrl, + viteTestUrl +} from '~utils' export const port = ports.lib -export async function serve(root, isBuildTest) { +export async function serve() { setupConsoleWarnCollector() - if (!isBuildTest) { + if (!isBuild) { const { createServer } = await import('vite') process.env.VITE_INLINE = 'inline-serve' const viteServer = await ( await createServer({ - root: root, + root: rootDir, logLevel: 'silent', server: { watch: { @@ -25,7 +33,7 @@ export async function serve(root, isBuildTest) { }, host: true, fs: { - strict: !isBuildTest + strict: !isBuild } }, build: { @@ -42,19 +50,19 @@ export async function serve(root, isBuildTest) { } else { const { build } = await import('vite') await build({ - root, + root: rootDir, logLevel: 'silent', configFile: path.resolve(__dirname, '../vite.config.js') }) await build({ - root, + root: rootDir, logLevel: 'warn', // output esbuild warns configFile: path.resolve(__dirname, '../vite.dyimport.config.js') }) // start static file server - const serve = sirv(path.resolve(root, 'dist')) + const serve = sirv(path.resolve(rootDir, 'dist')) const httpServer = http.createServer((req, res) => { if (req.url === '/ping') { res.statusCode = 200 diff --git a/playground/optimize-missing-deps/__test__/serve.ts b/playground/optimize-missing-deps/__test__/serve.ts index 7ceddad85dd19e..0548ea6fa0c427 100644 --- a/playground/optimize-missing-deps/__test__/serve.ts +++ b/playground/optimize-missing-deps/__test__/serve.ts @@ -2,13 +2,13 @@ // the default e2e test serve behavior import path from 'path' -import { ports } from '~utils' +import { isBuild, ports, rootDir } from '~utils' export const port = ports['optimize-missing-deps'] -export async function serve(root: string, isProd: boolean) { - const { createServer } = require(path.resolve(root, 'server.js')) - const { app, vite } = await createServer(root, isProd) +export async function serve() { + const { createServer } = require(path.resolve(rootDir, 'server.js')) + const { app, vite } = await createServer(rootDir, isBuild) return new Promise((resolve, reject) => { try { diff --git a/playground/resolve-config/__tests__/resolve-config.spec.ts b/playground/resolve-config/__tests__/resolve-config.spec.ts index 9b3588cafcc47d..d6c637ca2d07a4 100644 --- a/playground/resolve-config/__tests__/resolve-config.spec.ts +++ b/playground/resolve-config/__tests__/resolve-config.spec.ts @@ -3,7 +3,7 @@ import path from 'path' import { commandSync } from 'execa' import { isBuild, testDir, viteBinPath } from '~utils' -const fromTestDir = (...p: string[]) => path.resolve(testDir(), ...p) +const fromTestDir = (...p: string[]) => path.resolve(testDir, ...p) const build = (configName: string) => { commandSync(`${viteBinPath} build`, { cwd: fromTestDir(configName) }) diff --git a/playground/resolve-config/__tests__/serve.ts b/playground/resolve-config/__tests__/serve.ts index 97d68d8f7fb54f..e715deff5b1bfb 100644 --- a/playground/resolve-config/__tests__/serve.ts +++ b/playground/resolve-config/__tests__/serve.ts @@ -3,13 +3,14 @@ import path from 'path' import fs from 'fs-extra' +import { isBuild, rootDir } from '~utils' const configNames = ['js', 'cjs', 'mjs', 'ts'] -export async function serve(root: string, isProd: boolean) { - if (!isProd) return +export async function serve() { + if (!isBuild) return - const fromTestDir = (...p: string[]) => path.resolve(root, '..', ...p) + const fromTestDir = (...p: string[]) => path.resolve(rootDir, '..', ...p) // create separate directories for all config types: // ./{js,cjs,mjs,ts} and ./{js,cjs,mjs,ts}-module (with package#type) diff --git a/playground/ssr-deps/__tests__/serve.ts b/playground/ssr-deps/__tests__/serve.ts index 939d9bb297e4b2..979ed64782dd9b 100644 --- a/playground/ssr-deps/__tests__/serve.ts +++ b/playground/ssr-deps/__tests__/serve.ts @@ -3,15 +3,15 @@ import path from 'path' import kill from 'kill-port' -import { ports } from '~utils' +import { isBuild, ports, rootDir } from '~utils' export const port = ports['ssr-deps'] -export async function serve(root, isProd) { +export async function serve() { await kill(port) - const { createServer } = require(path.resolve(root, 'server.js')) - const { app, vite } = await createServer(root, isProd) + const { createServer } = require(path.resolve(rootDir, 'server.js')) + const { app, vite } = await createServer(rootDir, isBuild) return new Promise((resolve, reject) => { try { diff --git a/playground/ssr-html/__tests__/serve.ts b/playground/ssr-html/__tests__/serve.ts index 9cfebc53fcabb3..0740cce6cb456b 100644 --- a/playground/ssr-html/__tests__/serve.ts +++ b/playground/ssr-html/__tests__/serve.ts @@ -3,15 +3,15 @@ import path from 'path' import kill from 'kill-port' -import { ports } from '~utils' +import { isBuild, ports, rootDir } from '~utils' export const port = ports['ssr-html'] -export async function serve(root, isProd) { +export async function serve() { await kill(port) - const { createServer } = require(path.resolve(root, 'server.js')) - const { app, vite } = await createServer(root, isProd) + const { createServer } = require(path.resolve(rootDir, 'server.js')) + const { app, vite } = await createServer(rootDir, isBuild) return new Promise((resolve, reject) => { try { diff --git a/playground/ssr-pug/__tests__/serve.ts b/playground/ssr-pug/__tests__/serve.ts index b7b7ff0f8cd75f..bfaba4ce89d47c 100644 --- a/playground/ssr-pug/__tests__/serve.ts +++ b/playground/ssr-pug/__tests__/serve.ts @@ -3,15 +3,15 @@ import path from 'path' import kill from 'kill-port' -import { ports } from '~utils' +import { isBuild, ports, rootDir } from '~utils' export const port = ports['ssr-pug'] -export async function serve(root, isProd) { +export async function serve() { await kill(port) - const { createServer } = require(path.resolve(root, 'server.js')) - const { app, vite } = await createServer(root, isProd) + const { createServer } = require(path.resolve(rootDir, 'server.js')) + const { app, vite } = await createServer(rootDir, isBuild) return new Promise((resolve, reject) => { try { diff --git a/playground/ssr-react/__tests__/serve.ts b/playground/ssr-react/__tests__/serve.ts index 13a458db1d73c7..b022cb84755089 100644 --- a/playground/ssr-react/__tests__/serve.ts +++ b/playground/ssr-react/__tests__/serve.ts @@ -3,17 +3,17 @@ import path from 'path' import kill from 'kill-port' -import { ports } from '~utils' +import { isBuild, ports, rootDir } from '~utils' export const port = ports['ssr-react'] -export async function serve(root: string, isProd: boolean) { - if (isProd) { +export async function serve() { + if (isBuild) { // build first const { build } = await import('vite') // client build await build({ - root, + root: rootDir, logLevel: 'silent', // exceptions are logged by Vitest build: { target: 'esnext', @@ -24,7 +24,7 @@ export async function serve(root: string, isProd: boolean) { }) // server build await build({ - root, + root: rootDir, logLevel: 'silent', build: { target: 'esnext', @@ -36,8 +36,8 @@ export async function serve(root: string, isProd: boolean) { await kill(port) - const { createServer } = require(path.resolve(root, 'server.js')) - const { app, vite } = await createServer(root, isProd) + const { createServer } = require(path.resolve(rootDir, 'server.js')) + const { app, vite } = await createServer(rootDir, isBuild) return new Promise((resolve, reject) => { try { diff --git a/playground/ssr-vue/__tests__/serve.ts b/playground/ssr-vue/__tests__/serve.ts index 5b678b85ab280c..301d62adda9a42 100644 --- a/playground/ssr-vue/__tests__/serve.ts +++ b/playground/ssr-vue/__tests__/serve.ts @@ -3,17 +3,17 @@ import path from 'path' import kill from 'kill-port' -import { ports } from '~utils' +import { isBuild, ports, rootDir } from '~utils' export const port = ports['ssr-vue'] -export async function serve(root, isProd) { - if (isProd) { +export async function serve() { + if (isBuild) { // build first const { build } = await import('vite') // client build await build({ - root, + root: rootDir, logLevel: 'silent', // exceptions are logged by Vitest build: { target: 'esnext', @@ -24,7 +24,7 @@ export async function serve(root, isProd) { }) // server build await build({ - root, + root: rootDir, logLevel: 'silent', build: { target: 'esnext', @@ -36,8 +36,8 @@ export async function serve(root, isProd) { await kill(port) - const { createServer } = require(path.resolve(root, 'server.js')) - const { app, vite } = await createServer(root, isProd) + const { createServer } = require(path.resolve(rootDir, 'server.js')) + const { app, vite } = await createServer(rootDir, isBuild) return new Promise((resolve, reject) => { try { diff --git a/playground/ssr-webworker/__tests__/serve.ts b/playground/ssr-webworker/__tests__/serve.ts index c3b3306d55758d..09578b58499f6c 100644 --- a/playground/ssr-webworker/__tests__/serve.ts +++ b/playground/ssr-webworker/__tests__/serve.ts @@ -3,11 +3,11 @@ import path from 'path' import kill from 'kill-port' -import { ports } from '~utils' +import { isBuild, ports, rootDir } from '~utils' export const port = ports['ssr-webworker'] -export async function serve(root: string, isProd: boolean) { +export async function serve() { await kill(port) // we build first, regardless of whether it's prod/build mode @@ -16,7 +16,7 @@ export async function serve(root: string, isProd: boolean) { // worker build await build({ - root, + root: rootDir, logLevel: 'silent', build: { target: 'esnext', @@ -25,8 +25,8 @@ export async function serve(root: string, isProd: boolean) { } }) - const { createServer } = require(path.resolve(root, 'worker.js')) - const { app } = await createServer(root, isProd) + const { createServer } = require(path.resolve(rootDir, 'worker.js')) + const { app } = await createServer(rootDir, isBuild) return new Promise((resolve, reject) => { try { diff --git a/playground/test-utils.ts b/playground/test-utils.ts index 47bab08b698336..987ecb5e950a23 100644 --- a/playground/test-utils.ts +++ b/playground/test-utils.ts @@ -13,24 +13,11 @@ import type { Manifest } from 'vite' import { normalizePath } from 'vite' import { fromComment } from 'convert-source-map' import { expect } from 'vitest' -import { page } from './vitestSetup' +import type { ExecaChildProcess } from 'execa' +import { isBuild, isWindows, page, testDir } from './vitestSetup' export * from './vitestSetup' -export const workspaceRoot = path.resolve(__dirname, '../') - -export const isBuild = !!process.env.VITE_TEST_BUILD -export const isServe = !isBuild - -export const isWindows = process.platform === 'win32' -export const viteBinPath = path.join( - workspaceRoot, - 'packages', - 'vite', - 'bin', - 'vite.js' -) - // make sure these ports are unique export const ports = { cli: 9510, @@ -48,16 +35,6 @@ export const ports = { 'css/postcss-plugins-different-dir': 5006 } -export function slash(p: string): string { - return p.replace(/\\/g, '/') -} - -export const testDir = () => { - const testPath = expect.getState().testPath - const testName = slash(testPath).match(/playground\/([\w-]+)\//)?.[1] - return path.resolve(__dirname, '../playground-temp', testName) -} - const hexToNameMap: Record = {} Object.keys(colors).forEach((color) => { hexToNameMap[colors[color]] = color @@ -109,7 +86,7 @@ export async function getBgColor(el: string | ElementHandle): Promise { } export function readFile(filename: string): string { - return fs.readFileSync(path.resolve(testDir(), filename), 'utf-8') + return fs.readFileSync(path.resolve(testDir, filename), 'utf-8') } export function editFile( @@ -118,27 +95,27 @@ export function editFile( runInBuild: boolean = false ): void { if (isBuild && !runInBuild) return - filename = path.resolve(testDir(), filename) + filename = path.resolve(testDir, filename) const content = fs.readFileSync(filename, 'utf-8') const modified = replacer(content) fs.writeFileSync(filename, modified) } export function addFile(filename: string, content: string): void { - fs.writeFileSync(path.resolve(testDir(), filename), content) + fs.writeFileSync(path.resolve(testDir, filename), content) } export function removeFile(filename: string): void { - fs.unlinkSync(path.resolve(testDir(), filename)) + fs.unlinkSync(path.resolve(testDir, filename)) } export function listAssets(base = ''): string[] { - const assetsDir = path.join(testDir(), 'dist', base, 'assets') + const assetsDir = path.join(testDir, 'dist', base, 'assets') return fs.readdirSync(assetsDir) } export function findAssetFile(match: string | RegExp, base = ''): string { - const assetsDir = path.join(testDir(), 'dist', base, 'assets') + const assetsDir = path.join(testDir, 'dist', base, 'assets') const files = fs.readdirSync(assetsDir) const file = files.find((file) => { return file.match(match) @@ -148,10 +125,7 @@ export function findAssetFile(match: string | RegExp, base = ''): string { export function readManifest(base = ''): Manifest { return JSON.parse( - fs.readFileSync( - path.join(testDir(), 'dist', base, 'manifest.json'), - 'utf-8' - ) + fs.readFileSync(path.join(testDir, 'dist', base, 'manifest.json'), 'utf-8') ) } @@ -182,10 +156,26 @@ export const extractSourcemap = (content: string) => { } export const formatSourcemapForSnapshot = (map: any) => { - const root = normalizePath(testDir()) + const root = normalizePath(testDir) const m = { ...map } delete m.file delete m.names m.sources = m.sources.map((source) => source.replace(root, '/root')) return m } + +// helper function to kill process, uses taskkill on windows to ensure child process is killed too +export async function killProcess( + serverProcess: ExecaChildProcess +): Promise { + if (isWindows) { + try { + const { default: execa } = await import('execa') + execa.commandSync(`taskkill /pid ${serverProcess.pid} /T /F`) + } catch (e) { + console.error('failed to taskkill:', e) + } + } else { + serverProcess.kill('SIGTERM', { forceKillAfterTimeout: 2000 }) + } +} diff --git a/playground/tsconfig-json-load-error/__tests__/serve.ts b/playground/tsconfig-json-load-error/__tests__/serve.ts new file mode 100644 index 00000000000000..b2d63428db630b --- /dev/null +++ b/playground/tsconfig-json-load-error/__tests__/serve.ts @@ -0,0 +1,21 @@ +import { startDefaultServe } from '~utils' + +export let serveError: Error | undefined + +export async function serve() { + try { + await startDefaultServe() + } catch (e) { + serveError = e + } +} + +export function clearServeError() { + serveError = undefined +} + +afterAll(() => { + if (serveError) { + throw serveError + } +}) diff --git a/playground/tsconfig-json-load-error/__tests__/tsconfig-json-load-error.spec.ts b/playground/tsconfig-json-load-error/__tests__/tsconfig-json-load-error.spec.ts index 52a02683b79c00..9235913e1ce496 100644 --- a/playground/tsconfig-json-load-error/__tests__/tsconfig-json-load-error.spec.ts +++ b/playground/tsconfig-json-load-error/__tests__/tsconfig-json-load-error.spec.ts @@ -1,7 +1,6 @@ +import { clearServeError, serveError } from './serve' import { - beforeAllError, browserLogs, - clearBeforeAllError, editFile, isBuild, isServe, @@ -12,12 +11,11 @@ import { describe.runIf(isBuild)('build', () => { test('should throw an error on build', () => { - const buildError = beforeAllError - expect(buildError).toBeTruthy() - expect(buildError.message).toMatch( + expect(serveError).toBeTruthy() + expect(serveError.message).toMatch( /^parsing .* failed: SyntaxError: Unexpected token } in JSON at position \d+$/ ) - clearBeforeAllError() // got expected error, null it here so testsuite does not fail from rethrow in afterAll + clearServeError() // got expected error, null it here so testsuite does not fail from rethrow in afterAll }) test('should not output files to dist', () => { diff --git a/playground/vitestSetup.ts b/playground/vitestSetup.ts index 071d078a954455..9c1840b68ffd0a 100644 --- a/playground/vitestSetup.ts +++ b/playground/vitestSetup.ts @@ -18,52 +18,67 @@ import type { RollupError, RollupWatcher, RollupWatcherEvent } from 'rollup' import type { File } from 'vitest' import { beforeAll } from 'vitest' -const isBuildTest = !!process.env.VITE_TEST_BUILD +// #region env + +export const workspaceRoot = path.resolve(__dirname, '../') + +export const isBuild = !!process.env.VITE_TEST_BUILD +export const isServe = !isBuild +export const isWindows = process.platform === 'win32' +export const viteBinPath = path.join(workspaceRoot, 'packages/vite/bin/vite.js') + +// #endregion + +// #region context let server: ViteDevServer | http.Server -let tempDir: string -let rootDir: string + +/** + * Root of the Vite fixture + */ +export let rootDir: string +/** + * Path to the current test file + */ +export let testPath: string +/** + * Path to the test folder + */ +export let testDir: string +/** + * Test folder name + */ +export let testName: string export const serverLogs: string[] = [] export const browserLogs: string[] = [] export const browserErrors: Error[] = [] -/** - * Error caught in beforeAll, useful if you want to test error scenarios on build - */ -export let beforeAllError: Error | null = null - export let page: Page = undefined! export let browser: Browser = undefined! export let viteTestUrl: string = '' export let watcher: RollupWatcher | undefined = undefined -export function clearBeforeAllError() { - beforeAllError = null -} - export function setViteUrl(url: string) { viteTestUrl = url } -export function slash(p: string): string { - return p.replace(/\\/g, '/') -} +// #endregion const DIR = path.join(os.tmpdir(), 'vitest_playwright_global_setup') beforeAll(async (s) => { const suite = s as File - const wsEndpoint = fs.readFileSync(path.join(DIR, 'wsEndpoint'), 'utf-8') - if (!wsEndpoint) { - throw new Error('wsEndpoint not found') - } - // skip browser setup for non-playground tests if (!suite.filepath.includes('playground')) { return } + const wsEndpoint = fs.readFileSync(path.join(DIR, 'wsEndpoint'), 'utf-8') + if (!wsEndpoint) { + throw new Error('wsEndpoint not found') + } + browser = await chromium.connect(wsEndpoint) page = await browser.newPage() @@ -84,107 +99,48 @@ beforeAll(async (s) => { browserErrors.push(error) }) - const testPath = suite.filepath! - const testName = slash(testPath).match(/playground\/([\w-]+)\//)?.[1] + testPath = suite.filepath! + testName = slash(testPath).match(/playground\/([\w-]+)\//)?.[1] + testDir = dirname(testPath) // if this is a test placed under playground/xxx/__tests__ // start a vite server in that directory. if (testName) { - tempDir = resolve(__dirname, '../playground-temp/', testName) + testDir = resolve(workspaceRoot, 'playground-temp', testName) // when `root` dir is present, use it as vite's root - const testCustomRoot = resolve(tempDir, 'root') - rootDir = fs.existsSync(testCustomRoot) ? testCustomRoot : tempDir + const testCustomRoot = resolve(testDir, 'root') + rootDir = fs.existsSync(testCustomRoot) ? testCustomRoot : testDir const testCustomServe = [ resolve(dirname(testPath), 'serve.ts'), - resolve(dirname(testPath), 'serve.cjs'), resolve(dirname(testPath), 'serve.js') ].find((i) => fs.existsSync(i)) + if (testCustomServe) { // test has custom server configuration. const mod = await import(testCustomServe) const serve = mod.serve || mod.default?.serve const preServe = mod.preServe || mod.default?.preServe if (preServe) { - await preServe(rootDir, isBuildTest) + await preServe() } if (serve) { - server = await serve(rootDir, isBuildTest) + server = await serve() return } - } - - const testCustomConfig = resolve(dirname(testPath), 'vite.config.js') - let config: InlineConfig | undefined - if (fs.existsSync(testCustomConfig)) { - // test has custom server configuration. - config = require(testCustomConfig) - } - - const options: InlineConfig = { - root: rootDir, - logLevel: 'silent', - server: { - watch: { - // During tests we edit the files too fast and sometimes chokidar - // misses change events, so enforce polling for consistency - usePolling: true, - interval: 100 - }, - host: true, - fs: { - strict: !isBuildTest - } - }, - build: { - // esbuild do not minify ES lib output since that would remove pure annotations and break tree-shaking - // skip transpilation during tests to make it faster - target: 'esnext' - }, - customLogger: createInMemoryLogger(serverLogs) - } - - setupConsoleWarnCollector(serverLogs) - - if (!isBuildTest) { - process.env.VITE_INLINE = 'inline-serve' - server = await ( - await createServer(mergeConfig(options, config || {})) - ).listen() - // use resolved port/base from server - const base = server.config.base === '/' ? '' : server.config.base - viteTestUrl = `http://localhost:${server.config.server.port}${base}` - await page.goto(viteTestUrl) } else { - process.env.VITE_INLINE = 'inline-build' - // determine build watch - let resolvedConfig: ResolvedConfig - const resolvedPlugin: () => PluginOption = () => ({ - name: 'vite-plugin-watcher', - configResolved(config) { - resolvedConfig = config - } - }) - options.plugins = [resolvedPlugin()] - const rollupOutput = await build(mergeConfig(options, config || {})) - const isWatch = !!resolvedConfig!.build.watch - // in build watch,call startStaticServer after the build is complete - if (isWatch) { - watcher = rollupOutput as RollupWatcher - await notifyRebuildComplete(watcher) - } - viteTestUrl = await startStaticServer(config) - await page.goto(viteTestUrl) + await startDefaultServe() } } - } catch (e: any) { + } catch (e) { // Closing the page since an error in the setup, for example a runtime error // when building the playground should skip further tests. // If the page remains open, a command like `await page.click(...)` produces // a timeout with an exception that hides the real error in the console. await page.close() - beforeAllError = e + await server?.close() + throw e } return async () => { @@ -195,12 +151,74 @@ beforeAll(async (s) => { if (browser) { await browser.close() } - if (beforeAllError) { - throw beforeAllError - } } }) +export async function startDefaultServe() { + const testCustomConfig = resolve(dirname(testPath), 'vite.config.js') + let config: InlineConfig | undefined + if (fs.existsSync(testCustomConfig)) { + // test has custom server configuration. + config = require(testCustomConfig) + } + + const options: InlineConfig = { + root: rootDir, + logLevel: 'silent', + server: { + watch: { + // During tests we edit the files too fast and sometimes chokidar + // misses change events, so enforce polling for consistency + usePolling: true, + interval: 100 + }, + host: true, + fs: { + strict: !isBuild + } + }, + build: { + // esbuild do not minify ES lib output since that would remove pure annotations and break tree-shaking + // skip transpilation during tests to make it faster + target: 'esnext' + }, + customLogger: createInMemoryLogger(serverLogs) + } + + setupConsoleWarnCollector(serverLogs) + + if (!isBuild) { + process.env.VITE_INLINE = 'inline-serve' + server = await ( + await createServer(mergeConfig(options, config || {})) + ).listen() + // use resolved port/base from server + const base = server.config.base === '/' ? '' : server.config.base + viteTestUrl = `http://localhost:${server.config.server.port}${base}` + await page.goto(viteTestUrl) + } else { + process.env.VITE_INLINE = 'inline-build' + // determine build watch + let resolvedConfig: ResolvedConfig + const resolvedPlugin: () => PluginOption = () => ({ + name: 'vite-plugin-watcher', + configResolved(config) { + resolvedConfig = config + } + }) + options.plugins = [resolvedPlugin()] + const rollupOutput = await build(mergeConfig(options, config || {})) + const isWatch = !!resolvedConfig!.build.watch + // in build watch,call startStaticServer after the build is complete + if (isWatch) { + watcher = rollupOutput as RollupWatcher + await notifyRebuildComplete(watcher) + } + viteTestUrl = await startStaticServer(config) + await page.goto(viteTestUrl) + } +} + function startStaticServer(config?: InlineConfig): Promise { if (!config) { // check if the test project has base config @@ -305,3 +323,7 @@ function setupConsoleWarnCollector(logs: string[]) { return warn.call(console, ...args) } } + +export function slash(p: string): string { + return p.replace(/\\/g, '/') +} diff --git a/playground/worker/__tests__/es/es-worker.spec.ts b/playground/worker/__tests__/es/es-worker.spec.ts index 6d8cdaf9767d11..3ca94faac2e7d0 100644 --- a/playground/worker/__tests__/es/es-worker.spec.ts +++ b/playground/worker/__tests__/es/es-worker.spec.ts @@ -62,7 +62,7 @@ test('worker emitted and import.meta.url in nested worker (serve)', async () => describe.runIf(isBuild)('build', () => { // assert correct files test('inlined code generation', async () => { - const assetsDir = path.resolve(testDir(), 'dist/es/assets') + const assetsDir = path.resolve(testDir, 'dist/es/assets') const files = fs.readdirSync(assetsDir) expect(files.length).toBe(26) const index = files.find((f) => f.includes('main-module')) @@ -106,10 +106,10 @@ test('classic worker', async () => { }) test('emit chunk', async () => { - expect(await page.textContent('.emti-chunk-worker')).toMatch( + expect(await page.textContent('.emit-chunk-worker')).toMatch( '["A string",{"type":"emit-chunk-sub-worker","data":"A string"},{"type":"module-and-worker:worker","data":"A string"},{"type":"module-and-worker:module","data":"module and worker"},{"type":"emit-chunk-sub-worker","data":{"module":"module and worker","msg1":"module1","msg2":"module2","msg3":"module3"}}]' ) - expect(await page.textContent('.emti-chunk-dynamic-import-worker')).toMatch( + expect(await page.textContent('.emit-chunk-dynamic-import-worker')).toMatch( '"A string/es/"' ) }) diff --git a/playground/worker/__tests__/iife/worker.spec.ts b/playground/worker/__tests__/iife/worker.spec.ts index 0842d42e3562cc..3e5458bbe9a1a2 100644 --- a/playground/worker/__tests__/iife/worker.spec.ts +++ b/playground/worker/__tests__/iife/worker.spec.ts @@ -63,7 +63,7 @@ test('worker emitted and import.meta.url in nested worker (serve)', async () => describe.runIf(isBuild)('build', () => { // assert correct files test('inlined code generation', async () => { - const assetsDir = path.resolve(testDir(), 'dist/iife/assets') + const assetsDir = path.resolve(testDir, 'dist/iife/assets') const files = fs.readdirSync(assetsDir) expect(files.length).toBe(13) const index = files.find((f) => f.includes('main-module')) diff --git a/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts b/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts index 3b1f153ef1f19a..c60be7016d6477 100644 --- a/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts +++ b/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts @@ -5,10 +5,7 @@ import { isBuild, testDir } from '~utils' describe.runIf(isBuild)('build', () => { // assert correct files test('sourcemap generation for web workers', async () => { - const assetsDir = path.resolve( - testDir(), - 'dist/iife-sourcemap-hidden/assets' - ) + const assetsDir = path.resolve(testDir, 'dist/iife-sourcemap-hidden/assets') const files = fs.readdirSync(assetsDir) // should have 2 worker chunk diff --git a/playground/worker/__tests__/sourcemap-inline/sourcemap-inline-worker.spec.ts b/playground/worker/__tests__/sourcemap-inline/sourcemap-inline-worker.spec.ts index eaf62b25dde214..d82185a3f3f26b 100644 --- a/playground/worker/__tests__/sourcemap-inline/sourcemap-inline-worker.spec.ts +++ b/playground/worker/__tests__/sourcemap-inline/sourcemap-inline-worker.spec.ts @@ -5,10 +5,7 @@ import { isBuild, testDir } from '~utils' describe.runIf(isBuild)('build', () => { // assert correct files test('sourcemap generation for web workers', async () => { - const assetsDir = path.resolve( - testDir(), - 'dist/iife-sourcemap-inline/assets' - ) + const assetsDir = path.resolve(testDir, 'dist/iife-sourcemap-inline/assets') const files = fs.readdirSync(assetsDir) // should have 2 worker chunk diff --git a/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts b/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts index 10dcfcb5fba048..3b0a300c5e10f0 100644 --- a/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts +++ b/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts @@ -5,7 +5,7 @@ import { isBuild, testDir } from '~utils' describe.runIf(isBuild)('build', () => { // assert correct files test('sourcemap generation for web workers', async () => { - const assetsDir = path.resolve(testDir(), 'dist/iife-sourcemap/assets') + const assetsDir = path.resolve(testDir, 'dist/iife-sourcemap/assets') const files = fs.readdirSync(assetsDir) // should have 2 worker chunk expect(files.length).toBe(26) diff --git a/playground/worker/index.html b/playground/worker/index.html index eae8c44119e3a6..243f3657ff0161 100644 --- a/playground/worker/index.html +++ b/playground/worker/index.html @@ -92,15 +92,15 @@

worker emit chunk
module and worker:worker in worker file
module and worker:module in worker file
- .emti-chunk-worker + .emit-chunk-worker

- +

worker dynamic import to emit chunk - .emti-chunk-dynamic-import-worker + .emit-chunk-dynamic-import-worker

- +

module and worker:worker in simple file diff --git a/playground/worker/worker/main-format-es.js b/playground/worker/worker/main-format-es.js index e418c82a136927..2a385556c58684 100644 --- a/playground/worker/worker/main-format-es.js +++ b/playground/worker/worker/main-format-es.js @@ -13,7 +13,7 @@ const dataList = [] nestedWorker.addEventListener('message', (ev) => { dataList.push(ev.data) text( - '.emti-chunk-worker', + '.emit-chunk-worker', JSON.stringify( dataList.sort( (a, b) => JSON.stringify(a).length - JSON.stringify(b).length @@ -29,7 +29,7 @@ const dynamicImportWorker = new Worker( } ) dynamicImportWorker.addEventListener('message', (ev) => { - text('.emti-chunk-dynamic-import-worker', JSON.stringify(ev.data)) + text('.emit-chunk-dynamic-import-worker', JSON.stringify(ev.data)) }) const moduleWorker = new Worker(