Infrastructure: - adapters/: base, tauri (port 9750), electrobun (port 9761 + PTY daemon) - helpers/: 120+ centralized selectors, reusable actions, custom assertions - wdio.shared.conf.js + stack-specific configs 18 unified specs (205 tests): splash(6) smoke(15) settings(19) terminal(14) agent(15) search(12) files(15) comms(10) tasks(10) theme(12) groups(12) keyboard(8) notifications(10) diagnostics(8) status-bar(12) context(9) worktree(8) llm-judged(10) Daemon: --stack tauri|electrobun|both flag Scripts: test:e2e:tauri, test:e2e:electrobun, test:e2e:both
96 lines
2.8 KiB
TypeScript
96 lines
2.8 KiB
TypeScript
/**
|
|
* Tauri stack adapter — spawns tauri-driver, TCP readiness probe,
|
|
* routes through tauri:options capabilities.
|
|
*/
|
|
|
|
import { spawn, type ChildProcess } from 'node:child_process';
|
|
import { createConnection } from 'node:net';
|
|
import { resolve, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { existsSync } from 'node:fs';
|
|
import { StackAdapter, type StackCapabilities } from './base-adapter.ts';
|
|
import type { TestFixture } from '../infra/fixtures.ts';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const PROJECT_ROOT = resolve(__dirname, '../../..');
|
|
|
|
export class TauriAdapter extends StackAdapter {
|
|
readonly name = 'tauri';
|
|
readonly port = 9750;
|
|
|
|
getBinaryPath(): string {
|
|
return resolve(PROJECT_ROOT, 'target/debug/agent-orchestrator');
|
|
}
|
|
|
|
getDataDir(fixture: TestFixture): string {
|
|
return fixture.dataDir;
|
|
}
|
|
|
|
getCapabilities(fixture: TestFixture): StackCapabilities {
|
|
return {
|
|
capabilities: {
|
|
'wdio:enforceWebDriverClassic': true,
|
|
'tauri:options': {
|
|
application: this.getBinaryPath(),
|
|
env: fixture.env,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
setupDriver(): Promise<ChildProcess> {
|
|
return new Promise((res, reject) => {
|
|
// Check port is free first
|
|
const preCheck = createConnection({ port: this.port, host: 'localhost' }, () => {
|
|
preCheck.destroy();
|
|
reject(new Error(
|
|
`Port ${this.port} already in use. Kill: lsof -ti:${this.port} | xargs kill`
|
|
));
|
|
});
|
|
|
|
preCheck.on('error', () => {
|
|
preCheck.destroy();
|
|
const driver = spawn('tauri-driver', ['--port', String(this.port)], {
|
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
});
|
|
|
|
driver.on('error', (err) => {
|
|
reject(new Error(
|
|
`Failed to start tauri-driver: ${err.message}. Install: cargo install tauri-driver`
|
|
));
|
|
});
|
|
|
|
// TCP readiness probe
|
|
const deadline = Date.now() + 10_000;
|
|
const probe = () => {
|
|
if (Date.now() > deadline) {
|
|
reject(new Error(`tauri-driver not ready on port ${this.port} within 10s`));
|
|
return;
|
|
}
|
|
const sock = createConnection({ port: this.port, host: 'localhost' }, () => {
|
|
sock.destroy();
|
|
res(driver);
|
|
});
|
|
sock.on('error', () => {
|
|
sock.destroy();
|
|
setTimeout(probe, 200);
|
|
});
|
|
};
|
|
setTimeout(probe, 300);
|
|
});
|
|
});
|
|
}
|
|
|
|
teardownDriver(driver: ChildProcess): void {
|
|
driver.kill();
|
|
}
|
|
|
|
verifyBinary(): void {
|
|
if (!existsSync(this.getBinaryPath())) {
|
|
throw new Error(
|
|
`Tauri binary not found at ${this.getBinaryPath()}. ` +
|
|
'Build with: npm run tauri build --debug --no-bundle'
|
|
);
|
|
}
|
|
}
|
|
}
|