Skip to content

test: run E2E under a virtual display so it never steals focus - #402

Merged
failerko merged 1 commit into
mainfrom
test/e2e-virtual-display
Jul 29, 2026
Merged

test: run E2E under a virtual display so it never steals focus#402
failerko merged 1 commit into
mainfrom
test/e2e-virtual-display

Conversation

@failerko

@failerko failerko commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

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

    • Improved end-to-end test reliability in Linux and Wayland environments by automatically using a virtual display and compatible Electron display settings.
    • Reduced the risk of unwanted visible application windows during automated test runs.
  • Documentation

    • Added guidance for running end-to-end tests through the supported project commands, including headless and packaged test scenarios.

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>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Headless E2E execution

Layer / File(s) Summary
Virtual-display E2E launcher
scripts/e2e.ts
Adds a TypeScript launcher that detects headed modes, runs Playwright through Xvfb when available, forwards environment and arguments, and propagates failures.
Electron X11 launch integration
e2e/harness/launch.ts
Adds --ozone-platform=x11 to packaged and development Electron launches when virtual-display execution is enabled.
E2E entrypoint and CI adoption
package.json, .github/workflows/ci.yml, .claude/rules/testing.md
Routes E2E scripts and packaged CI execution through the launcher and documents the headless workflow.

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
Loading

Possibly related PRs

Poem

A bunny hops where headless tests run,
Xvfb hides each window from the sun.
X11 guides Electron’s flight,
Playwright checks the path just right—
Green little carrots for everyone! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: routing E2E runs through a virtual display to avoid stealing focus.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3b23d33 and 3ccef4c.

⛔ Files ignored due to path filters (3)
  • docs/implementation/lessons-learned/README.md is excluded by !docs/**
  • docs/implementation/lessons-learned/xvfb-does-not-hide-electron-on-wayland.md is excluded by !docs/**
  • docs/testing.md is excluded by !docs/**
📒 Files selected for processing (5)
  • .claude/rules/testing.md
  • .github/workflows/ci.yml
  • e2e/harness/launch.ts
  • package.json
  • scripts/e2e.ts

Comment thread scripts/e2e.ts
Comment on lines +39 to +45
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')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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' || true

Repository: 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:


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.

Comment thread scripts/e2e.ts
Comment on lines +52 to +58
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.',
)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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.

@failerko
failerko merged commit 5452859 into main Jul 29, 2026
4 checks passed
@failerko
failerko deleted the test/e2e-virtual-display branch July 29, 2026 01:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant