feat: CEF mode for Electrobun E2E — CDP automation, WebGL unlocked
- electrobun.config.ts: AGOR_CEF=1 enables bundleCEF + chromiumFlags (remote-debugging-port=9222). Production stays on WebKitGTK. - wdio.electrobun.conf.js: rewritten for CDP via devtools protocol. Spawns app, waits for CDP port, kills on complete. - helpers/actions.ts: waitForPort() polls CDP /json endpoint - docs/testing.md: CEF mode docs, CI setup, troubleshooting - npm script: test:e2e:electrobun prepends AGOR_CEF=1 CEF also enables: WebGL xterm.js addon (unlimited terminals), WebGPU, full Chrome DevTools — all for dev/test only.
This commit is contained in:
parent
fea6e267b0
commit
c79d489e1a
5 changed files with 189 additions and 27 deletions
|
|
@ -1,77 +1,119 @@
|
|||
/**
|
||||
* WebDriverIO config for Electrobun stack E2E tests.
|
||||
*
|
||||
* Extends shared config with WebKitWebDriver lifecycle and optional
|
||||
* PTY daemon management. Port: 9761 (per project convention).
|
||||
* Uses CDP (Chrome DevTools Protocol) via CEF mode for reliable E2E automation.
|
||||
* Electrobun must be built/run with AGOR_CEF=1 to bundle CEF and expose
|
||||
* --remote-debugging-port=9222 (configured in electrobun.config.ts).
|
||||
*
|
||||
* Port conventions:
|
||||
* 9222 — CDP debugging port (CEF)
|
||||
* 9760 — Vite dev server (HMR)
|
||||
* 9761 — (reserved, was WebKitWebDriver)
|
||||
*/
|
||||
|
||||
import { execSync } from 'node:child_process';
|
||||
import { execSync, spawn } from 'node:child_process';
|
||||
import { resolve, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { existsSync, rmSync } from 'node:fs';
|
||||
import { sharedConfig } from './wdio.shared.conf.js';
|
||||
import { waitForPort } from './helpers/actions.ts';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const projectRoot = resolve(__dirname, '../..');
|
||||
const electrobunRoot = resolve(projectRoot, 'ui-electrobun');
|
||||
|
||||
const CDP_PORT = 9222;
|
||||
|
||||
// Use the Electrobun fixture generator (different groups.json format)
|
||||
let fixture;
|
||||
try {
|
||||
const { createTestFixture } = await import('../../ui-electrobun/tests/e2e/fixtures.ts');
|
||||
fixture = createTestFixture('agor-ebun-unified');
|
||||
fixture = createTestFixture('agor-ebun-cdp');
|
||||
} catch {
|
||||
// Fall back to the Tauri fixture generator if Electrobun fixtures not available
|
||||
const { createTestFixture } = await import('./infra/fixtures.ts');
|
||||
fixture = createTestFixture('agor-ebun-unified');
|
||||
fixture = createTestFixture('agor-ebun-cdp');
|
||||
}
|
||||
|
||||
process.env.AGOR_TEST = '1';
|
||||
process.env.AGOR_CEF = '1';
|
||||
process.env.AGOR_TEST_DATA_DIR = fixture.dataDir;
|
||||
process.env.AGOR_TEST_CONFIG_DIR = fixture.configDir;
|
||||
|
||||
const WEBDRIVER_PORT = 9761;
|
||||
console.log(`[electrobun-cdp] Test fixture at ${fixture.rootDir ?? fixture.configDir}`);
|
||||
|
||||
console.log(`[electrobun] Test fixture at ${fixture.rootDir ?? fixture.configDir}`);
|
||||
let appProcess;
|
||||
|
||||
export const config = {
|
||||
...sharedConfig,
|
||||
|
||||
port: WEBDRIVER_PORT,
|
||||
// Use devtools protocol (CDP) instead of WebDriver
|
||||
automationProtocol: 'devtools',
|
||||
|
||||
capabilities: [{
|
||||
'wdio:enforceWebDriverClassic': true,
|
||||
browserName: 'webkit',
|
||||
browserName: 'chromium',
|
||||
'goog:chromeOptions': {
|
||||
debuggerAddress: `localhost:${CDP_PORT}`,
|
||||
},
|
||||
}],
|
||||
|
||||
onPrepare() {
|
||||
// Try multiple binary paths (dev vs canary vs production)
|
||||
// Find existing binary or build
|
||||
const candidates = [
|
||||
resolve(electrobunRoot, 'build/dev-linux-x64/AgentOrchestrator-dev/AgentOrchestrator-dev'),
|
||||
resolve(electrobunRoot, 'build/Agent Orchestrator'),
|
||||
resolve(electrobunRoot, 'build/AgentOrchestrator'),
|
||||
];
|
||||
const electrobunBinary = candidates.find(p => existsSync(p));
|
||||
let electrobunBinary = candidates.find(p => existsSync(p));
|
||||
|
||||
if (!electrobunBinary && !process.env.SKIP_BUILD) {
|
||||
console.log('Building Electrobun...');
|
||||
console.log('[electrobun-cdp] Building with CEF...');
|
||||
try {
|
||||
execSync('npx vite build', { cwd: electrobunRoot, stdio: 'inherit' });
|
||||
// electrobun build may not be available — skip if missing
|
||||
try { execSync('electrobun build --env=dev', { cwd: electrobunRoot, stdio: 'inherit' }); } catch {}
|
||||
execSync('electrobun build --env=dev', { cwd: electrobunRoot, stdio: 'inherit' });
|
||||
} catch (e) {
|
||||
console.warn('Build failed:', e.message);
|
||||
console.warn('[electrobun-cdp] Build failed:', e.message);
|
||||
}
|
||||
electrobunBinary = candidates.find(p => existsSync(p));
|
||||
}
|
||||
|
||||
const finalBinary = candidates.find(p => existsSync(p));
|
||||
if (!finalBinary) {
|
||||
console.warn('Electrobun binary not found — tests will use WebKitWebDriver with dev server');
|
||||
if (!electrobunBinary) {
|
||||
// Fall back to `electrobun dev` which builds + launches in one step
|
||||
console.log('[electrobun-cdp] No binary found, launching via electrobun dev...');
|
||||
appProcess = spawn('electrobun', ['dev'], {
|
||||
cwd: electrobunRoot,
|
||||
env: {
|
||||
...process.env,
|
||||
AGOR_CEF: '1',
|
||||
AGOR_TEST: '1',
|
||||
AGOR_TEST_DATA_DIR: fixture.dataDir,
|
||||
AGOR_TEST_CONFIG_DIR: fixture.configDir,
|
||||
},
|
||||
stdio: 'pipe',
|
||||
});
|
||||
} else {
|
||||
console.log(`[electrobun-cdp] Launching binary: ${electrobunBinary}`);
|
||||
appProcess = spawn(electrobunBinary, [], {
|
||||
env: {
|
||||
...process.env,
|
||||
AGOR_CEF: '1',
|
||||
AGOR_TEST: '1',
|
||||
AGOR_TEST_DATA_DIR: fixture.dataDir,
|
||||
AGOR_TEST_CONFIG_DIR: fixture.configDir,
|
||||
},
|
||||
stdio: 'pipe',
|
||||
});
|
||||
}
|
||||
|
||||
appProcess.stdout?.on('data', (d) => process.stdout.write(`[app] ${d}`));
|
||||
appProcess.stderr?.on('data', (d) => process.stderr.write(`[app] ${d}`));
|
||||
appProcess.on('exit', (code) => console.log(`[electrobun-cdp] App exited with code ${code}`));
|
||||
|
||||
// Wait for CDP port to become available
|
||||
return waitForPort(CDP_PORT, 30_000);
|
||||
},
|
||||
|
||||
async before() {
|
||||
// Wait for Electrobun app to load
|
||||
// Wait for Electrobun app to render
|
||||
await browser.waitUntil(
|
||||
async () => {
|
||||
const hasEl = await browser.execute(() =>
|
||||
|
|
@ -83,10 +125,16 @@ export const config = {
|
|||
},
|
||||
{ timeout: 20_000, interval: 500, timeoutMsg: 'Electrobun app did not load in 20s' },
|
||||
);
|
||||
console.log('[electrobun] App loaded.');
|
||||
console.log('[electrobun-cdp] App loaded.');
|
||||
},
|
||||
|
||||
afterSession() {
|
||||
onComplete() {
|
||||
if (appProcess) {
|
||||
console.log('[electrobun-cdp] Stopping app...');
|
||||
appProcess.kill('SIGTERM');
|
||||
appProcess = undefined;
|
||||
}
|
||||
|
||||
const cleanup = fixture.cleanup ?? (() => {
|
||||
try {
|
||||
if (fixture.rootDir) rmSync(fixture.rootDir, { recursive: true, force: true });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue