vite/scripts/jestPerTestSetup.ts

219 lines
6.1 KiB
TypeScript
Raw Normal View History

2020-12-20 03:33:13 +00:00
import fs from 'fs-extra'
import * as http from 'http'
2021-01-25 22:07:57 +00:00
import { resolve, dirname } from 'path'
2020-12-20 03:33:13 +00:00
import sirv from 'sirv'
import {
createServer,
build,
ViteDevServer,
UserConfig,
PluginOption,
ResolvedConfig
} from 'vite'
2020-12-20 03:33:13 +00:00
import { Page } from 'playwright-chromium'
// eslint-disable-next-line node/no-extraneous-import
import { RollupWatcher, RollupWatcherEvent } from 'rollup'
2020-12-20 03:33:13 +00:00
const isBuildTest = !!process.env.VITE_TEST_BUILD
export function slash(p: string): string {
return p.replace(/\\/g, '/')
}
2020-12-20 03:33:13 +00:00
// injected by the test env
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace NodeJS {
interface Global {
page?: Page
viteTestUrl?: string
watcher?: RollupWatcher
}
}
}
2020-12-20 03:33:13 +00:00
let server: ViteDevServer | http.Server
let tempDir: string
let rootDir: string
2020-12-20 03:33:13 +00:00
let err: Error
const logs = ((global as any).browserLogs = [])
2020-12-23 23:15:09 +00:00
const onConsole = (msg) => {
logs.push(msg.text())
}
2020-12-23 04:07:57 +00:00
2020-12-20 03:33:13 +00:00
beforeAll(async () => {
const page = global.page
if (!page) {
return
}
2020-12-20 03:33:13 +00:00
try {
2020-12-23 23:15:09 +00:00
page.on('console', onConsole)
2020-12-20 03:33:13 +00:00
const testPath = expect.getState().testPath
2020-12-24 17:09:38 +00:00
const testName = slash(testPath).match(/playground\/([\w-]+)\//)?.[1]
2020-12-20 03:33:13 +00:00
// if this is a test placed under playground/xxx/__tests__
// start a vite server in that directory.
if (testName) {
const playgroundRoot = resolve(__dirname, '../packages/playground')
const srcDir = resolve(playgroundRoot, testName)
tempDir = resolve(__dirname, '../temp', testName)
await fs.copy(srcDir, tempDir, {
2020-12-21 23:37:04 +00:00
dereference: true,
2020-12-20 03:33:13 +00:00
filter(file) {
2020-12-23 23:15:09 +00:00
file = slash(file)
return (
!file.includes('__tests__') &&
!file.includes('node_modules') &&
!file.match(/dist(\/|$)/)
)
2020-12-20 03:33:13 +00:00
}
})
// when `root` dir is present, use it as vite's root
let testCustomRoot = resolve(tempDir, 'root')
rootDir = fs.existsSync(testCustomRoot) ? testCustomRoot : tempDir
2021-01-25 22:07:57 +00:00
const testCustomServe = resolve(dirname(testPath), 'serve.js')
if (fs.existsSync(testCustomServe)) {
// test has custom server configuration.
const { serve } = require(testCustomServe)
server = await serve(rootDir, isBuildTest)
2021-01-25 22:07:57 +00:00
return
}
2020-12-22 03:00:54 +00:00
const options: UserConfig = {
root: rootDir,
logLevel: 'silent',
2020-12-22 04:15:38 +00:00
server: {
watch: {
2020-12-22 20:34:02 +00:00
// 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
}
2021-01-08 23:33:15 +00:00
},
build: {
// skip transpilation during tests to make it faster
2021-01-08 23:33:15 +00:00
target: 'esnext'
2020-12-22 04:15:38 +00:00
}
}
2020-12-20 03:33:13 +00:00
if (!isBuildTest) {
process.env.VITE_INLINE = 'inline-serve'
2020-12-22 03:00:54 +00:00
server = await (await createServer(options)).listen()
// use resolved port/base from server
const base = server.config.base === '/' ? '' : server.config.base
const url =
(global.viteTestUrl = `http://localhost:${server.config.server.port}${base}`)
2020-12-20 03:33:13 +00:00
await page.goto(url)
} 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(options)
const isWatch = !!resolvedConfig!.build.watch
// in build watch,call startStaticServer after the build is complete
if (isWatch) {
global.watcher = rollupOutput as RollupWatcher
await notifyRebuildComplete(global.watcher)
}
const url = (global.viteTestUrl = await startStaticServer())
2020-12-20 03:33:13 +00:00
await page.goto(url)
}
}
} catch (e) {
// jest doesn't exit if our setup has error here
// https://github.com/facebook/jest/issues/2713
err = 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()
2020-12-20 03:33:13 +00:00
}
}, 30000)
2020-12-20 03:33:13 +00:00
afterAll(async () => {
2021-04-22 19:26:40 +00:00
global.page?.off('console', onConsole)
await global.page?.close()
await server?.close()
2020-12-20 03:33:13 +00:00
if (err) {
throw err
}
})
function startStaticServer(): Promise<string> {
// check if the test project has base config
const configFile = resolve(rootDir, 'vite.config.js')
let config: UserConfig
try {
config = require(configFile)
} catch (e) {}
const base = (config?.base || '/') === '/' ? '' : config.base
// @ts-ignore
if (config && config.__test__) {
// @ts-ignore
config.__test__()
}
2020-12-20 03:33:13 +00:00
// start static file server
const serve = sirv(resolve(rootDir, 'dist'))
2020-12-27 05:47:18 +00:00
const httpServer = (server = http.createServer((req, res) => {
if (req.url === '/ping') {
res.statusCode = 200
res.end('pong')
} else {
serve(req, res)
}
}))
2020-12-20 03:33:13 +00:00
let port = 5000
return new Promise((resolve, reject) => {
const onError = (e: any) => {
if (e.code === 'EADDRINUSE') {
httpServer.close()
httpServer.listen(++port)
} else {
reject(e)
}
}
httpServer.on('error', onError)
httpServer.listen(port, () => {
httpServer.removeListener('error', onError)
resolve(`http://localhost:${port}${base}`)
2020-12-20 03:33:13 +00:00
})
})
}
/**
* Send the rebuild complete message in build watch
*/
export async function notifyRebuildComplete(
watcher: RollupWatcher
): Promise<RollupWatcher> {
let callback: (event: RollupWatcherEvent) => void
await new Promise((resolve, reject) => {
callback = (event) => {
if (event.code === 'END') {
resolve(true)
}
}
watcher.on('event', callback)
})
return watcher.removeListener('event', callback)
}