From 5a49b928ce96c84f7fb713ea2989c9f8cb8d8285 Mon Sep 17 00:00:00 2001 From: Ruslan Strazhnyk Date: Wed, 13 May 2026 22:09:35 +0200 Subject: [PATCH] Add example: live Playwright browser feed via noVNC Run a headed Playwright test inside an E2B sandbox and watch the browser drive itself in real time from any tab. Boots Xvfb + x11vnc + websockify + noVNC inside the sandbox, exposes port 6080 via sandbox.getHost(6080), returns a https:// URL the operator opens in a browser tab. Two runnable scripts: - examples/01-watch-live-test.ts -- passing test against playwright.dev, prints URL, holds sandbox after the run so the final state stays visible in the noVNC tab. - examples/02-hold-on-failure.ts -- pause-on-failure pattern. Deliberately failing test, sandbox kept alive long enough for the operator to reload the noVNC URL and visually diagnose what went wrong. Same VNC stack that powers the qualitymax.io execution-view "Live" tab, trimmed to a self-contained cookbook example. Complements the self-healing example (PR #93): the snapshot pattern tells the LLM what the DOM was at failure, the live feed tells the human watching what the page looked like. typecheck clean. --- README.md | 1 + .../playwright-live-vnc-feed/.env.template | 8 + examples/playwright-live-vnc-feed/README.md | 160 ++++ .../examples/01-watch-live-test.ts | 82 +++ .../examples/02-hold-on-failure.ts | 72 ++ .../package-lock.json | 691 ++++++++++++++++++ .../playwright-live-vnc-feed/package.json | 36 + .../playwright-live-vnc-feed/src/index.ts | 9 + .../playwright-live-vnc-feed/src/runner.ts | 102 +++ .../playwright-live-vnc-feed/src/vnc-setup.ts | 123 ++++ .../playwright-live-vnc-feed/tsconfig.json | 17 + 11 files changed, 1301 insertions(+) create mode 100644 examples/playwright-live-vnc-feed/.env.template create mode 100644 examples/playwright-live-vnc-feed/README.md create mode 100644 examples/playwright-live-vnc-feed/examples/01-watch-live-test.ts create mode 100644 examples/playwright-live-vnc-feed/examples/02-hold-on-failure.ts create mode 100644 examples/playwright-live-vnc-feed/package-lock.json create mode 100644 examples/playwright-live-vnc-feed/package.json create mode 100644 examples/playwright-live-vnc-feed/src/index.ts create mode 100644 examples/playwright-live-vnc-feed/src/runner.ts create mode 100644 examples/playwright-live-vnc-feed/src/vnc-setup.ts create mode 100644 examples/playwright-live-vnc-feed/tsconfig.json diff --git a/README.md b/README.md index 5751550b..a4e9afb6 100644 --- a/README.md +++ b/README.md @@ -235,3 +235,4 @@ Read more about E2B on the [E2B website](https://e2b.dev) and the official [E2B - Next.js app with LLM + Code Interpreter and streaming - [TypeScript](./examples/nextjs-code-interpreter) - How to run a Docker container in E2B - [Python/TypeScript](./examples/docker-in-e2b) - How to run Playwright in E2B - [TypeScript](./examples/playwright-in-e2b) +- Watch a Playwright test live in your browser via noVNC - [TypeScript](./examples/playwright-live-vnc-feed) diff --git a/examples/playwright-live-vnc-feed/.env.template b/examples/playwright-live-vnc-feed/.env.template new file mode 100644 index 00000000..8efb6eb1 --- /dev/null +++ b/examples/playwright-live-vnc-feed/.env.template @@ -0,0 +1,8 @@ +# E2B sandbox API key — get one at https://e2b.dev +E2B_API_KEY= + +# How long to keep the sandbox alive after the test finishes, in seconds. +# A short hold lets you re-load the noVNC tab and inspect the final page +# state (or DevTools, if you opened them) before the sandbox is killed. +# Default: 60. Set to 0 to kill immediately. +POST_RUN_HOLD_SECONDS=60 diff --git a/examples/playwright-live-vnc-feed/README.md b/examples/playwright-live-vnc-feed/README.md new file mode 100644 index 00000000..11c54c5e --- /dev/null +++ b/examples/playwright-live-vnc-feed/README.md @@ -0,0 +1,160 @@ +# Live Browser Feed for Playwright Tests in E2B + +Run a headed Playwright test inside an E2B sandbox and watch the browser +drive itself in real time from any browser tab. No screen-sharing +software, no separate desktop, no recording — just a `https://` URL the +sandbox publishes that streams the test as it runs. + +Battle-tested at [qualitymax.io](https://qualitymax.io): this is the +same stack that powers the platform's "Live" tab in the execution view, +trimmed to the smallest reproducible cookbook example. + +## What it shows + +- Boot `Xvfb + x11vnc + websockify + noVNC` inside a fresh + `playwright-chromium` sandbox. +- Get the publicly-reachable noVNC URL from `sandbox.getHost(6080)`. +- Run a headed Playwright test against `DISPLAY=:99` so x11vnc captures + the rendered surface. +- Optionally hold the sandbox alive *after* the run so the browser tab + can still display the final page state (or the broken state when a + test fails). + +## Architecture + +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ E2B sandbox │ +│ │ +│ Xvfb :99 (1280x720x24) ◀─ DISPLAY=:99 ─ Playwright + Chromium │ +│ │ │ +│ ▼ │ +│ x11vnc → RFB on :5900 │ +│ │ │ +│ ▼ │ +│ websockify → :6080 (also serves noVNC client from /usr/share/novnc)│ +│ │ +└──────────────────────────────────────────────────────────────────────┘ + ▲ + │ https:///vnc.html + │ + Your browser tab +``` + +A single port (`6080`) does two jobs: it serves the noVNC HTML/JS client +*and* it's the WebSocket endpoint the client connects to for the VNC +stream. E2B routes `https:///*` to it transparently. TLS +is terminated at E2B's edge, so the URL is always `https://` — plain +`http://` to a `getHost` URL produces a connection error. + +## Setup + +```bash +npm install +cp .env.template .env # then fill in E2B_API_KEY +npm run typecheck # sanity check +npm run example:watch # watch a passing test live +npm run example:hold # see the pause-on-failure pattern +``` + +## Examples + +| Script | What it shows | +| --- | --- | +| `examples/01-watch-live-test.ts` | Run a passing test against `playwright.dev` while you watch in the noVNC tab. Holds the sandbox for 60s after the run so you can confirm the final state. | +| `examples/02-hold-on-failure.ts` | Deliberately failing test that demonstrates the pause-on-failure pattern: when the test errors out, the sandbox stays alive long enough for you to reload the noVNC URL and see what the page actually looked like at failure. | + +## How it works + +### 1. Boot the VNC stack + +`src/vnc-setup.ts` runs a single bash blob inside the freshly-created +sandbox that: + +- `apt-get install -y x11vnc novnc websockify` (only if missing — the + `playwright-chromium` template doesn't ship them by default). +- `Xvfb :99 -screen 0 1280x720x24 &` — a virtual X server for Chromium + to render into. +- `x11vnc -display :99 -rfbport 5900 -bg` — captures the framebuffer + and serves it as RFB on port 5900. +- `websockify --web=/usr/share/novnc 6080 localhost:5900 &` — wraps the + RFB stream as WebSocket on port 6080 *and* serves the noVNC HTML + client from the same port. + +All four daemons inherit the sandbox's lifetime; killing the sandbox +tears them down. Logs go to `/tmp/vnc-*.log` for debugging if the +noVNC tab refuses to connect. + +### 2. Drive a headed Playwright test + +`src/runner.ts` writes the test file and a Playwright config into +`/home/user/work`, then runs: + +``` +DISPLAY=:99 PLAYWRIGHT_BROWSERS_PATH=... npx playwright test +``` + +The config sets `headless: false` and `slowMo: 250` so the human +watching the noVNC tab actually has time to see the actions land. Two +non-obvious settings: + +- `viewport: { width: 1280, height: 720 }` matches the Xvfb screen + size. Mismatched sizes cause Chromium to scroll the rendered surface + inside the framebuffer. +- `launchOptions.env = { DISPLAY: ':99' }` is belt-and-braces — the + shell env already exports DISPLAY, but Playwright reads `process.env` + at browser launch and the SDK's command runner doesn't always + propagate inherited env. Without this, Chromium can launch against + the wrong display and you see a blank noVNC tab. + +### 3. Hold the sandbox after the run + +`POST_RUN_HOLD_SECONDS` keeps the sandbox alive after the test finishes +or fails. This matters because the moment you call `sandbox.kill()`, +websockify dies and the noVNC tab goes 502. Without a hold, the live +view disappears at exactly the moment a human would want to inspect it. + +`examples/02-hold-on-failure.ts` shows the failure variant: when the +test fails, keep the sandbox up so the operator can reload the noVNC +URL and visually diagnose what went wrong with the page. + +## Adapting it + +- **Pre-bake the VNC stack into your own template** to drop the cold-start + cost of the `apt-get install` step (saves ~5-10s on first run per + fresh sandbox). Build an E2B template based on `playwright-chromium` + with `x11vnc`, `novnc`, `websockify` already installed and the + `Xvfb/x11vnc/websockify` daemons supervised by systemd or `tini`. +- **Plug it into the self-healing example** (`../self-healing-playwright-tests`). + The healer's failure-snapshot loop and the live VNC feed are + complementary: the snapshot tells the LLM what the DOM was at + failure, the VNC feed tells the human watching what the *page* + looked like. +- **Embed the noVNC URL in your UI.** It's just a `