const puppeteer = require("puppeteer"); const http = require("http"); const path = require("path"); const url = require("url"); const fs = require("fs"); const child_process = require("child_process"); const snippetsDir = path.resolve(__dirname, "../snippets"); const serverURL = process.env.TEST_SERVER_URL || "http://localhost:8080"; const USE_EXISTING_PROCESS = process.env.USE_EXISTING_PROCESS || false; const DISABLE_HMR = !!process.env.DISABLE_HMR; const bunFlags = [ `--origin=${serverURL}`, DISABLE_HMR && "--disable-hmr", ].filter(Boolean); const bunExec = process.env.BUN_BIN || "bun"; var bunProcess; var waitSpawn; if (!USE_EXISTING_PROCESS) { bunProcess = child_process.spawn(bunExec, bunFlags, { cwd: snippetsDir, stdio: "pipe", env: { ...process.env, DISABLE_BUN_ANALYTICS: "1", }, shell: false, }); console.log("$", bunExec, bunFlags.join(" ")); bunProcess.stderr.pipe(process.stderr); bunProcess.stdout.pipe(process.stdout); var rejecter; bunProcess.once("error", (err) => { console.error("❌ bun error", err); process.exit(1); }); waitSpawn = new Promise((resolve, reject) => { bunProcess.once("spawn", (code) => { console.log("Spawned"); resolve(); }); }); process.on("beforeExit", () => { bunProcess?.kill(0); }); } const isDebug = bunExec.endsWith("-debug"); function writeSnapshot(name, code) { let file = path.join(__dirname, "../snapshots", name); if (!DISABLE_HMR) { file = file.substring(0, file.length - path.extname(file).length) + ".hmr" + path.extname(file); } if (!fs.existsSync(path.dirname(file))) { fs.mkdirSync(path.dirname(file), { recursive: true }); } fs.writeFileSync( isDebug ? file.substring(0, file.length - path.extname(file).length) + ".debug" + path.extname(file) : file, code ); } async function main() { const launchOptions = USE_EXISTING_PROCESS ? { devtools: true } : undefined; const browser = await puppeteer.launch(launchOptions); const promises = []; let allTestsPassed = true; if (waitSpawn) await waitSpawn; var canRetry = true; async function runPage(key) { var page; try { page = await browser.newPage(); if (USE_EXISTING_PROCESS) { await page.evaluate(` globalThis.BUN_DEBUG_MODE = true; `); } var shouldClose = true; page.on("console", (obj) => console.log(`[console.${obj.type()}] ${obj.text()}`) ); page.exposeFunction("testFail", (error) => { console.log(`❌ ${error}`); allTestsPassed = false; }); let testDone = new Promise((resolve) => { page.exposeFunction("testDone", resolve); }); try { await page.goto(`${serverURL}/`, { waitUntil: "domcontentloaded", }); await page.evaluate(` globalThis.runTest("${key}"); `); await testDone; } catch (err) { if (canRetry) { console.log( `❌ ${key} failed once (incase it's still booting on universal binary for the first time). Retrying...` ); canRetry = false; return await runPage(key); } throw err; } console.log(`✅ ${key}`); } catch (e) { if (USE_EXISTING_PROCESS) shouldClose = false; allTestsPassed = false; console.log(`❌ ${key}: ${(e && e.message) || e}`); } finally { try { const code = await page.evaluate(` globalThis.getModuleScriptSrc("${key}"); `); writeSnapshot(key, code); } catch (exception) { console.warn(`Failed to update snapshot: ${key}`, exception); } } canRetry = false; if (shouldClose) await page.close(); } const tests = require("./snippets.json"); tests.reverse(); for (let test of tests) { await runPage(test); } if (!USE_EXISTING_PROCESS || (USE_EXISTING_PROCESS && allTestsPassed)) { bunProcess && bunProcess.kill(0); if (!allTestsPassed) { console.error(`❌ browser test failed`); process.exit(1); } else { console.log(`✅ browser test passed`); bunProcess && bunProcess.kill(0); process.exit(0); } await browser.close(); } } main().catch((error) => setTimeout(() => { throw error; }) );