agent-orchestrator/tests/e2e/daemon/index.ts
Hibryda d7dd7722ab feat(e2e): add test daemon CLI with ANSI dashboard and Agent SDK bridge
- index.ts: CLI entry point (--full, --spec, --watch, --agent flags)
- runner.ts: programmatic WDIO launcher with result streaming
- dashboard.ts: ANSI terminal UI (pass/fail/skip/running icons, summary)
- agent-bridge.ts: NDJSON stdin/stdout for Agent SDK queries
  (status, rerun, failures, reset-cache)
- Standalone package at tests/e2e/daemon/
2026-03-18 05:17:17 +01:00

95 lines
2.8 KiB
TypeScript

#!/usr/bin/env tsx
// Agent Orchestrator E2E Test Daemon — CLI entry point
// Usage: tsx index.ts [--full] [--spec <pattern>] [--watch] [--agent]
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { watch } from 'node:fs';
import { Dashboard } from './dashboard.ts';
import { runSpecs, discoverSpecs, specDisplayName, clearCache, type RunOptions } from './runner.ts';
import { AgentBridge } from './agent-bridge.ts';
const __dirname = dirname(fileURLToPath(import.meta.url));
const SPECS_DIR = resolve(__dirname, '../specs');
// ── CLI args ──
const args = process.argv.slice(2);
const fullMode = args.includes('--full');
const watchMode = args.includes('--watch');
const agentMode = args.includes('--agent');
const specIdx = args.indexOf('--spec');
const specPattern = specIdx !== -1 ? args[specIdx + 1] : undefined;
// ── Init ──
const dashboard = new Dashboard();
let bridge: AgentBridge | null = null;
let pendingRerun: RunOptions | null = null;
if (agentMode) {
bridge = new AgentBridge(dashboard);
bridge.onRerunRequest((opts) => { pendingRerun = opts; });
bridge.start();
}
// ── Run cycle ──
async function runCycle(opts: RunOptions = {}): Promise<void> {
const specs = discoverSpecs(opts.pattern ?? specPattern);
dashboard.setTests(specs.map((s) => ({ name: specDisplayName(s), specFile: s })));
dashboard.startRefresh();
bridge?.setRunning(true);
await runSpecs({
pattern: opts.pattern ?? specPattern,
full: opts.full ?? fullMode,
onResult: (r) => dashboard.updateTest(r.name, r.status, r.durationMs, r.error),
});
dashboard.markComplete();
dashboard.stopRefresh();
bridge?.setRunning(false);
}
function shutdown(poll?: ReturnType<typeof setInterval>, watcher?: ReturnType<typeof watch>): void {
watcher?.close();
if (poll) clearInterval(poll);
dashboard.stop();
bridge?.stop();
process.exit(0);
}
// ── Main ──
async function main(): Promise<void> {
if (fullMode) clearCache();
await runCycle();
if (watchMode || agentMode) {
const watcher = watchMode
? watch(SPECS_DIR, { recursive: false }, (_ev, f) => {
if (f?.endsWith('.test.ts')) pendingRerun = { pattern: specPattern, full: false };
})
: undefined;
const poll = setInterval(async () => {
if (pendingRerun) {
const opts = pendingRerun;
pendingRerun = null;
await runCycle(opts);
}
}, 1000);
process.on('SIGINT', () => shutdown(poll, watcher));
process.on('SIGTERM', () => shutdown(poll, watcher));
} else {
dashboard.stop();
bridge?.stop();
const hasFailed = dashboard.getTests().some((t) => t.status === 'failed');
process.exit(hasFailed ? 1 : 0);
}
}
main().catch((err) => {
console.error('Fatal error:', err);
dashboard.stop();
bridge?.stop();
process.exit(1);
});