agent-orchestrator/tests/e2e/wdio.shared.conf.js
Hibryda 75bb96cfed fix(e2e): switch Electrobun to ChromeDriver + debuggerAddress attach
- devtools protocol kills Electrobun's CEF browser on attach (navigates)
- ChromeDriver with debuggerAddress connects WITHOUT navigating
- Added Vite dev server auto-start (Electrobun dev mode requires it)
- Fixed this.skip() → test.pending for cross-protocol compat
- chromedriver@145 matches CEF Chrome 145
2026-03-22 07:19:57 +01:00

116 lines
3.9 KiB
JavaScript

/**
* Shared WebDriverIO configuration — common settings for both Tauri and Electrobun.
*
* Stack-specific configs (wdio.tauri.conf.js, wdio.electrobun.conf.js)
* import and extend this with their own lifecycle hooks and capabilities.
*/
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { getResultsDb } from './infra/results-db.ts';
const __dirname = dirname(fileURLToPath(import.meta.url));
const projectRoot = resolve(__dirname, '../..');
export const sharedConfig = {
// ── Runner ──
runner: 'local',
maxInstances: 1,
// ── Connection defaults (overridden per-stack) ──
hostname: 'localhost',
path: '/',
// ── Specs — unified set, all shared ──
specs: [
resolve(projectRoot, 'tests/e2e/specs/splash.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/smoke.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/groups.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/settings.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/theme.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/terminal.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/agent.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/keyboard.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/search.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/notifications.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/files.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/comms.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/tasks.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/status-bar.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/context.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/diagnostics.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/worktree.test.ts'),
resolve(projectRoot, 'tests/e2e/specs/llm-judged.test.ts'),
],
// ── Framework ──
framework: 'mocha',
mochaOpts: {
ui: 'bdd',
timeout: 180_000,
},
// ── Reporter ──
reporters: ['spec'],
// ── Logging ──
logLevel: 'warn',
// ── Timeouts ──
waitforTimeout: 10_000,
connectionRetryTimeout: 30_000,
connectionRetryCount: 3,
// ── Hooks ──
/** Smart test caching: skip tests with 3+ consecutive passes */
beforeTest(test) {
browser.__expectedErrors = [];
if (process.env.FULL_RESCAN) return;
const db = getResultsDb();
const specFile = test.file?.replace(/.*specs\//, '') ?? '';
if (db.shouldSkip(specFile, test.title)) {
const stats = db.getCacheStats();
console.log(`Skipping (3+ consecutive passes): ${test.title} [${stats.skippable}/${stats.total} skippable]`);
test.pending = true;
}
},
/** After each test: check for error toasts, record in pass cache */
async afterTest(test, _context, { passed }) {
let unexpected = [];
try {
// Use string script for cross-protocol compatibility (devtools + webdriver)
const errors = await browser.execute(
'return (function() {' +
' var toasts = Array.from(document.querySelectorAll(".toast-error, .load-error"));' +
' return toasts.map(function(t) { return (t.textContent || "").trim(); }).filter(Boolean);' +
'})()'
);
const expected = browser.__expectedErrors || [];
unexpected = errors.filter(e => !expected.some(exp => e.includes(exp)));
if (unexpected.length > 0 && passed) {
throw new Error(
`Unexpected error toast(s) during "${test.title}": ${unexpected.join('; ')}`
);
}
} catch (e) {
if (e.message?.includes('Unexpected error toast')) throw e;
}
const db = getResultsDb();
const specFile = test.file?.replace(/.*specs\//, '') ?? '';
db.recordTestResult(specFile, test.title, passed && unexpected.length === 0);
},
// ── TypeScript ──
autoCompileOpts: {
tsNodeOpts: {
project: resolve(projectRoot, 'tests/e2e/tsconfig.json'),
},
},
};