UNPKG

vitest-e2e

Version:
242 lines (239 loc) 6.94 kB
import { workspaceRoot, isBuild } from './chunk-JXTCPVDR.js'; import fs from 'fs-extra'; import os from 'node:os'; import { join, dirname, resolve } from 'node:path'; import { chromium } from 'playwright-chromium'; import { mergeConfig, createServer, build, preview, loadConfigFromFile } from 'vite'; import { beforeAll, afterEach } from 'vitest'; var server; var viteServer; var rootDir; var testPath; var testDir; var testName; var viteConfig; var serverLogs = []; var browserLogs = []; var browserErrors = []; var resolvedConfig = void 0; var page = void 0; var browser = void 0; var viteTestUrl = ""; var watcher = void 0; function setViteUrl(url) { viteTestUrl = url; } var DIR = join(os.tmpdir(), "vitest_playwright_global_setup"); beforeAll(async (s) => { const suite = s; if (!suite.filepath.includes("playground")) { return; } const wsEndpoint = fs.readFileSync(join(DIR, "wsEndpoint"), "utf-8"); if (!wsEndpoint) { throw new Error("wsEndpoint not found"); } browser = await chromium.connect(wsEndpoint); page = await browser.newPage(); const globalConsole = global.console; const warn = globalConsole.warn; globalConsole.warn = (msg, ...args) => { if (msg.includes("Generated an empty chunk")) return; warn.call(globalConsole, msg, ...args); }; try { page.on("console", (msg) => { if (process.env.VITE_DEBUG_SERVE && msg.text().includes("Failed to load resource:") && msg.location().url.includes("favicon.ico")) { return; } if (msg.text().includes("React DevTools")) return; browserLogs.push(msg.text()); }); page.on("pageerror", (error) => { browserErrors.push(error); }); testPath = suite.filepath; testName = slash(testPath).match(/playground\/([\w-]+)\//)?.[1]; testDir = dirname(testPath); if (testName) { testDir = resolve(workspaceRoot, "playground-temp", testName); const testCustomRoot = resolve(testDir, "root"); rootDir = fs.existsSync(testCustomRoot) ? testCustomRoot : testDir; const testCustomServe = [resolve(dirname(testPath), "serve.ts"), resolve(dirname(testPath), "serve.js")].find( (i) => fs.existsSync(i) ); if (testCustomServe) { const mod = await import(testCustomServe); const serve = mod.serve || mod.default?.serve; const preServe = mod.preServe || mod.default?.preServe; if (preServe) { await preServe(); } if (serve) { server = await serve(); viteServer = mod.viteServer; return; } } else { await startDefaultServe(); } } } catch (e) { await page.close(); await server?.close(); throw e; } return async () => { serverLogs = []; await page?.close(); await server?.close(); await watcher?.close(); if (browser) { await browser.close(); } }; }); afterEach(() => { browserLogs = []; browserErrors = []; }); function loadConfigFromDir(dir) { return loadConfigFromFile( { command: isBuild ? "build" : "serve", mode: isBuild ? "production" : "development" }, void 0, dir ); } async function startDefaultServe() { let config = null; const res = await loadConfigFromDir(dirname(testPath)); if (res) { config = res.config; } if (!config) { const res2 = await loadConfigFromDir(rootDir); if (res2) { config = res2.config; } } const options = { root: rootDir, logLevel: "silent", configFile: false, 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", // tests are flaky when `emptyOutDir` is `true` emptyOutDir: false }, customLogger: createInMemoryLogger(serverLogs) }; setupConsoleWarnCollector(); if (!isBuild) { process.env.VITE_INLINE = "inline-serve"; const testConfig = mergeConfig(options, config || {}); viteConfig = testConfig; viteServer = server = await (await createServer(testConfig)).listen(); const devBase = server.config.base; viteTestUrl = `http://localhost:${server.config.server.port}${devBase === "/" ? "" : devBase}`; await page.goto(viteTestUrl); } else { process.env.VITE_INLINE = "inline-build"; const resolvedPlugin = () => ({ name: "vite-plugin-watcher", configResolved(config2) { resolvedConfig = config2; } }); options.plugins = [resolvedPlugin()]; const testConfig = mergeConfig(options, config || {}); viteConfig = testConfig; const rollupOutput = await build(testConfig); const isWatch = !!resolvedConfig.build.watch; if (isWatch) { watcher = rollupOutput; await notifyRebuildComplete(watcher); } if (config && config.__test__) { config.__test__(); } const _nodeEnv = process.env.NODE_ENV; const previewServer = await preview(testConfig); process.env.NODE_ENV = _nodeEnv; viteTestUrl = previewServer.resolvedUrls.local[0]; await page.goto(viteTestUrl); } } async function notifyRebuildComplete(watcher2) { let resolveFn; const callback = (event) => { if (event.code === "END") { resolveFn?.(); } }; watcher2.on("event", callback); await new Promise((resolve2) => { resolveFn = resolve2; }); return watcher2.off("event", callback); } function createInMemoryLogger(logs) { const loggedErrors = /* @__PURE__ */ new WeakSet(); const warnedMessages = /* @__PURE__ */ new Set(); const logger = { hasWarned: false, hasErrorLogged: (err) => loggedErrors.has(err), clearScreen: () => { }, info(msg) { logs.push(msg); }, warn(msg) { logs.push(msg); logger.hasWarned = true; }, warnOnce(msg) { if (warnedMessages.has(msg)) return; logs.push(msg); logger.hasWarned = true; warnedMessages.add(msg); }, error(msg, opts) { logs.push(msg); if (opts?.error) { loggedErrors.add(opts.error); } } }; return logger; } function setupConsoleWarnCollector(_logs) { const warn = console.warn; console.warn = (...args) => { serverLogs.push(args.join(" ")); return warn.call(console, ...args); }; } function slash(p) { return p.replace(/\\/g, "/"); } export { browser, browserErrors, browserLogs, notifyRebuildComplete, page, resolvedConfig, rootDir, serverLogs, setViteUrl, slash, startDefaultServe, testDir, testName, testPath, viteConfig, viteServer, viteTestUrl, watcher };