/** * 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 { 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' ); } } }