Headless Electron Testing
July 6, 2026 ยท View on GitHub
This document explains how headless mode works for Electron testing in this project.
Overview
Unlike regular browsers, Electron doesn't support true "headless" mode. However, we've implemented a workaround that prevents windows from showing during testing, effectively simulating headless behavior.
How It Works
1. Environment Variable Control
The Electron app checks for headless mode using these environment variables (in order of precedence):
HEADLESS=true- Explicit headless modeCI=true- Automatically enables headless in CI environments--headlesscommand line argument--test-headlesscommand line argument
2. Window Show Prevention
When headless mode is detected, the WindowService skips calling window.show() in the ready-to-show event handler. The window is still created and functional (for testing DOM interactions), but remains hidden.
3. Automatic Configuration
Playwright tests automatically enable headless mode via:
- Global Setup: Sets
HEADLESS=trueenvironment variable - Electron Helper: Passes
HEADLESS=trueto launched Electron processes - Configuration: Documents the headless behavior in playwright.config.ts
Usage
Running Tests Headless (Default)
npx playwright test
All Playwright tests run in headless mode by default.
Running Tests with Visible Windows (Debug)
To see windows during testing (useful for debugging):
node scripts/run-node-cli.mjs HEADLESS=false -- npx playwright test
# or
npx playwright test --headed # For browser portions only
Manual Electron Launch (Headless)
node scripts/run-node-cli.mjs HEADLESS=true -- npm run electron-dev
# or (run Vite separately, then launch Electron headless)
npm run dev
npm run electron -- --headless
CI Environment
In CI environments, headless mode is automatically enabled when CI=true is set.
Benefits
- Faster Execution: No window rendering overhead
- CI Compatibility: Works in headless CI environments without Xvfb on Linux
- Reliable Testing: Prevents window focus issues and race conditions
- Resource Efficiency: Lower memory and CPU usage during tests
Implementation Details
WindowService Changes
private readonly handleReadyToShow = (): void => {
logger.info("[WindowService] Main window ready to show");
// Check for headless mode (for testing environments)
const isHeadless = process.env["HEADLESS"] === "true" ||
process.env["CI"] === "true" ||
process.argv.includes("--headless") ||
process.argv.includes("--test-headless");
if (isHeadless) {
logger.info("[WindowService] Running in headless mode - window will not be shown");
return;
}
this.mainWindow?.show();
};
Playwright Configuration
The playwright.config.ts includes documentation explaining that the headless setting affects browser tests but NOT Electron tests, since Electron headless is controlled at the application level.
Platform Support
- Windows: Works natively
- macOS: Works natively
- Linux: Works natively (no Xvfb required with this implementation)