From 9e886352a188d06ce9d2b5fe123e39f3cbd94865 Mon Sep 17 00:00:00 2001
From: Cheng Shao <terrorjack@type.dance>
Date: Mon, 17 Mar 2025 22:36:37 +0000
Subject: [PATCH] wasm: add puppeteer/playwright support for ghci browser mode

This commit adds support for using puppeteer/playwright for
automatically launching a headless browser that backs the ghci browser
mode. This is useful for testing the ghci browser mode as a part of
GHC testsuite, and it's also convenient for local development since
the step to start iserv can be automated away.

(cherry picked from commit fc57679876b660e1d78a63ee2d84d76f498b550c)
(cherry picked from commit 760cb5f97f74db3114e58c423b955a44928ef4eb)
---
 utils/jsffi/dyld.mjs | 78 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/utils/jsffi/dyld.mjs b/utils/jsffi/dyld.mjs
index d41b411ba8c..dd84a579aad 100755
--- a/utils/jsffi/dyld.mjs
+++ b/utils/jsffi/dyld.mjs
@@ -1127,6 +1127,84 @@ export async function main({ rpc, libdir, ghciSoPath, args }) {
   });
   const origin = originFromServerAddress(await server.listening);
 
+  // https://pptr.dev/api/puppeteer.consolemessage
+  // https://playwright.dev/docs/api/class-consolemessage
+  const on_console_msg = (msg) => {
+    switch (msg.type()) {
+      case "error":
+      case "warn":
+      case "warning":
+      case "trace":
+      case "assert": {
+        console.error(msg.text());
+        break;
+      }
+      default: {
+        console.log(msg.text());
+        break;
+      }
+    }
+  };
+
+  if (process.env.GHCI_BROWSER_PUPPETEER_LAUNCH_OPTS) {
+    let puppeteer;
+    try {
+      puppeteer = require("puppeteer");
+    } catch {
+      puppeteer = require("puppeteer-core");
+    }
+
+    // https://pptr.dev/api/puppeteer.puppeteernode.launch
+    const browser = await puppeteer.launch(
+      JSON.parse(process.env.GHCI_BROWSER_PUPPETEER_LAUNCH_OPTS)
+    );
+    try {
+      const page = await browser.newPage();
+
+      // https://pptr.dev/api/puppeteer.pageevent
+      page.on("console", on_console_msg);
+      page.on("error", (err) => console.error(err));
+      page.on("pageerror", (err) => console.error(err));
+
+      await page.goto(`${origin}/main.html`);
+      await server.closed;
+      return;
+    } finally {
+      await browser.close();
+    }
+  }
+
+  if (process.env.GHCI_BROWSER_PLAYWRIGHT_BROWSER_TYPE) {
+    let playwright;
+    try {
+      playwright = require("playwright");
+    } catch {
+      playwright = require("playwright-core");
+    }
+
+    // https://playwright.dev/docs/api/class-browsertype#browser-type-launch
+    const browser = await playwright[
+      process.env.GHCI_BROWSER_PLAYWRIGHT_BROWSER_TYPE
+    ].launch(
+      process.env.GHCI_BROWSER_PLAYWRIGHT_LAUNCH_OPTS
+        ? JSON.parse(process.env.GHCI_BROWSER_PLAYWRIGHT_LAUNCH_OPTS)
+        : {}
+    );
+    try {
+      const page = await browser.newPage();
+
+      // https://playwright.dev/docs/api/class-page#events
+      page.on("console", on_console_msg);
+      page.on("pageerror", (err) => console.error(err));
+
+      await page.goto(`${origin}/main.html`);
+      await server.closed;
+      return;
+    } finally {
+      await browser.close();
+    }
+  }
+
   console.log(
     `Open ${origin}/main.html or import ${origin}/main.js to boot ghci`
   );
-- 
GitLab