test: run E2E under a virtual display so it never steals focus - #402
Conversation
Both scripts now route through scripts/e2e.ts, which wraps the run in xvfb-run on Linux; the virtual display alone is inert on a Wayland session, where Electron ignores DISPLAY and connects to the compositor, so launch.ts pins --ozone-platform=x11 behind the E2E_VIRTUAL_DISPLAY flag the wrapper sets. CI drops its own xvfb-run prefix and invokes the identical command. New lessons-learned entry covers the verification trap: Xvfb running proves nothing about where the window rendered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughChangesHeadless E2E execution
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant DeveloperOrCI
participant E2EScript
participant Xvfb
participant Playwright
participant ElectronHarness
participant Electron
DeveloperOrCI->>E2EScript: run test:e2e or test:e2e:packaged
E2EScript->>Xvfb: start virtual display on Linux
E2EScript->>Playwright: spawn tests with E2E_VIRTUAL_DISPLAY=1
Playwright->>ElectronHarness: launch Electron
ElectronHarness->>Electron: select X11 ozone backend
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/e2e.ts`:
- Around line 52-58: Update the Linux headless guard in the e2e startup flow to
exit with a nonzero status when xvfb-run is unavailable and neither headed mode
nor virtual display is enabled. Preserve the existing warning, and allow
execution to continue only when E2E_HEADED=1 explicitly opts out of the virtual
display.
- Around line 39-45: Update the headed detection expression in scripts/e2e.ts to
treat PWDEBUG=console as headed alongside the existing PWDEBUG=1 case. Preserve
the current forwarded-flag checks and virtualDisplay behavior so console debug
runs are not wrapped in xvfb-run.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: a882584b-2c85-47cc-8c90-80d948fc4eac
⛔ Files ignored due to path filters (3)
docs/implementation/lessons-learned/README.mdis excluded by!docs/**docs/implementation/lessons-learned/xvfb-does-not-hide-electron-on-wayland.mdis excluded by!docs/**docs/testing.mdis excluded by!docs/**
📒 Files selected for processing (5)
.claude/rules/testing.md.github/workflows/ci.ymle2e/harness/launch.tspackage.jsonscripts/e2e.ts
| const headed = | ||
| process.env.E2E_HEADED === '1' || | ||
| process.env.PWDEBUG === '1' || | ||
| forwarded.some((arg) => | ||
| INTERACTIVE_FLAGS.some((flag) => arg === flag || arg.startsWith(`${flag}=`)), | ||
| ) | ||
| const virtualDisplay = process.platform === 'linux' && !headed && onPath('xvfb-run') |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== candidate files =="
fd -a 'e2e\.ts$' . || true
echo "== scripts/e2e.ts outline =="
ast-grep outline scripts/e2e.ts 2>/dev/null || true
echo "== relevant lines =="
sed -n '1,120p' scripts/e2e.ts
echo "== tests around e2e wrappers/headed/debug =="
rg -n "e2e\.ts|PWDEBUG|E2E_HEADED|INTERACTIVE|headed|virtualDisplay|xvfb" -S . --glob '!node_modules' --glob '!dist' --glob '!build' || trueRepository: AventurasTeam/Aventuras
Length of output: 7734
🌐 Web query:
Playwright PWDEBUG=console launches browser debugger window documentation
💡 Result:
Setting the environment variable PWDEBUG=console enables a specialized debug mode in Playwright that facilitates using the browser's native developer tools for debugging [1][2]. When this mode is active, Playwright automatically runs in headed mode and disables timeouts [3]. To use this feature, follow these steps: 1. Insert a breakpoint into your test code using the pause method (e.g., page.pause in JavaScript/TypeScript, Page.PauseAsync in.NET, or page.pause in Java/Python) [1][4][5][2]. 2. Run your tests with the environment variable set to console. For example, in a terminal: - For Playwright Test (Node.js): PWDEBUG=console npx playwright test [1] - For Python (pytest): PWDEBUG=console pytest -s [2] - For.NET: PWDEBUG=console dotnet test [4] - For Java: PWDEBUG=console mvn test [5] 3. When the execution hits the breakpoint and pauses, the browser will remain open [1][2]. 4. Open the browser's developer tools (F12 or right-click > Inspect) [1][2]. 5. Within the console panel of the developer tools, you will have access to a playwright object, which allows you to inspect elements, check network activity, and interact with the browser state [1][2]. This mode is distinct from the default PWDEBUG=1, which launches the Playwright Inspector GUI instead [3].
Citations:
- 1: https://playwright.dev/docs/debug
- 2: https://playwright.dev/python/docs/debug
- 3: feat(debug): PWDEBUG=console vs PWDEBUG=1 microsoft/playwright#6213
- 4: https://playwright.dev/dotnet/docs/debug
- 5: https://playwright.dev/java/docs/debug
Handle PWDEBUG=console as an interactive run.
PWDEBUG=console runs Playwright in headed debug mode, but scripts/e2e.ts only treats PWDEBUG=1 as interactive. On Linux with xvfb-run available, this route can hide the developer-focused browser run behind a virtual display; add it to the headed check.
🧰 Tools
🪛 ast-grep (0.45.0)
[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from 'node:child_process'
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
(detect-child-process-typescript)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/e2e.ts` around lines 39 - 45, Update the headed detection expression
in scripts/e2e.ts to treat PWDEBUG=console as headed alongside the existing
PWDEBUG=1 case. Preserve the current forwarded-flag checks and virtualDisplay
behavior so console debug runs are not wrapped in xvfb-run.
| if (process.platform === 'linux' && !headed && !virtualDisplay) { | ||
| console.warn( | ||
| '[e2e] xvfb-run not found — Electron will open real windows and take focus.\n' + | ||
| '[e2e] Install it (Arch: sudo pacman -S xorg-server-xvfb, Debian/Ubuntu: apt install xvfb),\n' + | ||
| '[e2e] or set E2E_HEADED=1 to opt out of the virtual display deliberately.', | ||
| ) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Fail closed when xvfb-run is unavailable.
The default Linux path only warns and then runs Playwright directly, breaking the headless-by-default contract and potentially stealing focus or failing on hosts without a usable DISPLAY. The documented opt-out is E2E_HEADED=1; exit nonzero here unless that explicit mode is selected.
🧰 Tools
🪛 ast-grep (0.45.0)
[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from 'node:child_process'
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
(detect-child-process-typescript)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/e2e.ts` around lines 52 - 58, Update the Linux headless guard in the
e2e startup flow to exit with a nonzero status when xvfb-run is unavailable and
neither headed mode nor virtual display is enabled. Preserve the existing
warning, and allow execution to continue only when E2E_HEADED=1 explicitly opts
out of the virtual display.
Both scripts now route through scripts/e2e.ts, which wraps the run in xvfb-run on Linux; the virtual display alone is inert on a Wayland session, where Electron ignores DISPLAY and connects to the compositor, so launch.ts pins --ozone-platform=x11 behind the E2E_VIRTUAL_DISPLAY flag the wrapper sets. CI drops its own xvfb-run prefix and invokes the identical command. New lessons-learned entry covers the verification trap: Xvfb running proves nothing about where the window rendered.
Summary by CodeRabbit
Bug Fixes
Documentation