aboutsummaryrefslogtreecommitdiff
path: root/test/scripts/browser.js
diff options
context:
space:
mode:
Diffstat (limited to 'test/scripts/browser.js')
-rw-r--r--test/scripts/browser.js190
1 files changed, 190 insertions, 0 deletions
diff --git a/test/scripts/browser.js b/test/scripts/browser.js
new file mode 100644
index 000000000..479a55c9c
--- /dev/null
+++ b/test/scripts/browser.js
@@ -0,0 +1,190 @@
+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);
+ });
+ if (!process.env.CI) {
+ waitSpawn = new Promise((resolve, reject) => {
+ bunProcess.once("spawn", (code) => {
+ console.log("Spawned");
+ resolve();
+ });
+ });
+ }
+ process.on("beforeExit", () => {
+ bunProcess && 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
+ );
+}
+
+const baseOptions = {
+ dumpio: !!process.env.CI_DEBUG,
+
+ args: [
+ "--disable-gpu",
+ "--disable-dev-shm-usage",
+ "--disable-setuid-sandbox",
+ "--no-sandbox",
+ "--ignore-certificate-errors",
+ "--use-fake-ui-for-media-stream",
+ "--use-fake-device-for-media-stream",
+ "--disable-sync",
+ ],
+ executablePath: process.env.BROWSER_EXECUTABLE,
+ headless: true,
+};
+
+async function main() {
+ const launchOptions = USE_EXISTING_PROCESS
+ ? { ...baseOptions, devtools: !process.env.CI }
+ : baseOptions;
+ 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;
+ })
+);