fix: rebrowser uses vanilla Chromium binary, headful skips without display

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ValueOn AG 2026-02-16 21:54:28 +01:00
parent 2265cf425d
commit cc17f638cd
3 changed files with 31 additions and 3 deletions

2
package-lock.json generated
View file

@ -13,7 +13,7 @@
"playwright": "1.50.0",
"playwright-extra": "^4.3.6",
"puppeteer-extra-plugin-stealth": "^2.11.2",
"rebrowser-playwright": "^1.50.0",
"rebrowser-playwright": "^1.49.0",
"uuid": "^9.0.1",
"winston": "^3.11.0",
"ws": "^8.16.0"

View file

@ -12,7 +12,7 @@
},
"dependencies": {
"playwright": "1.50.0",
"rebrowser-playwright": "^1.50.0",
"rebrowser-playwright": "^1.49.0",
"playwright-extra": "^4.3.6",
"puppeteer-extra-plugin-stealth": "^2.11.2",
"ws": "^8.16.0",

View file

@ -261,9 +261,11 @@ async function _launchBrowserForVariant(variant: AuthTestVariant): Promise<Launc
return _launchPlaywrightExtra();
case 'headful':
_requireDisplay();
return _launchVanilla(false, 'enhanced');
case 'headfulAuth':
_requireDisplay();
return _launchVanilla(false, 'enhanced');
default:
@ -271,6 +273,22 @@ async function _launchBrowserForVariant(variant: AuthTestVariant): Promise<Launc
}
}
/**
* Check if a display server is available for headful mode.
* On Linux without DISPLAY env var, headful Chromium will crash immediately.
* On Windows, a display is always available.
*/
function _requireDisplay(): void {
const isLinux = process.platform === 'linux';
if (isLinux && !process.env.DISPLAY) {
throw new Error(
'Kein Display verfuegbar (DISPLAY nicht gesetzt). ' +
'Headful-Modus benoetigt entweder einen X-Server, Xvfb, oder eine Windows/macOS-Umgebung. ' +
'Tipp: Auf dem Server "apt-get install xvfb" und "Xvfb :99 &" + "DISPLAY=:99" setzen.'
);
}
}
/**
* Launch vanilla Playwright with configurable headless mode and stealth level.
*/
@ -300,11 +318,15 @@ async function _launchVanilla(headless: boolean, stealthLevel: 'basic' | 'enhanc
/**
* Launch rebrowser-playwright (patched Playwright with CDP leak fixes).
* Uses dynamic require to avoid import errors if package is not installed.
*
* IMPORTANT: rebrowser-playwright ships its own playwright-core which may
* expect a different Chromium version than what's installed in the Docker image.
* We solve this by passing `executablePath` from vanilla Playwright, so
* rebrowser uses its patched CDP handling with the available Chromium binary.
*/
async function _launchRebrowser(): Promise<LaunchResult> {
let rbChromium: typeof chromium;
try {
// Dynamic require for the rebrowser-playwright package
const rb = require('rebrowser-playwright');
rbChromium = rb.chromium;
} catch {
@ -312,8 +334,14 @@ async function _launchRebrowser(): Promise<LaunchResult> {
return _launchVanilla(true, 'enhanced');
}
// Use the Chromium binary from vanilla Playwright (matches Docker image)
// to avoid "Executable doesn't exist" errors from version mismatch
const execPath = chromium.executablePath();
logger.info(`[AuthTest:rebrowser] Using Chromium binary: ${execPath}`);
const browser = await rbChromium.launch({
headless: true,
executablePath: execPath,
args: _BROWSER_ARGS,
});