- phase-a-structure.test.ts (156 lines, 14 tests): structural integrity, settings panel, sidebar gear, accent colors, project name/icon, grid layout - phase-a-agent.test.ts (210 lines, 14 tests): agent pane, prompts, provider badge, cost display, context meter, status transitions - phase-a-navigation.test.ts (297 lines, 19 tests): terminal tabs, command palette, focus switching, palette categories, shortcut hints - Original agent-scenarios.test.ts (429 lines) deleted - 25 new exhaustive tests added
186 lines
6 KiB
JavaScript
186 lines
6 KiB
JavaScript
import { spawn } from 'node:child_process';
|
|
import { createConnection } from 'node:net';
|
|
import { resolve, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { rmSync } from 'node:fs';
|
|
import { createTestFixture } from './infra/fixtures.ts';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const projectRoot = resolve(__dirname, '../..');
|
|
|
|
// Debug binary path (Cargo workspace target at repo root)
|
|
const tauriBinary = resolve(projectRoot, 'target/debug/agent-orchestrator');
|
|
|
|
let tauriDriver;
|
|
|
|
// ── Test Fixture ──
|
|
// IMPORTANT: Must be created at module top-level (synchronously) because the
|
|
// capabilities object below references fixtureDataDir/fixtureConfigDir at eval time.
|
|
// tauri:options.env may not reliably set process-level env vars, so we also
|
|
// inject into process.env for tauri-driver inheritance.
|
|
const fixture = createTestFixture('agor-e2e');
|
|
|
|
process.env.AGOR_TEST = '1';
|
|
process.env.AGOR_TEST_DATA_DIR = fixture.dataDir;
|
|
process.env.AGOR_TEST_CONFIG_DIR = fixture.configDir;
|
|
|
|
console.log(`Test fixture created at ${fixture.rootDir}`);
|
|
|
|
export const config = {
|
|
// ── Runner ──
|
|
runner: 'local',
|
|
maxInstances: 1, // Tauri doesn't support parallel sessions
|
|
|
|
// ── Connection (external tauri-driver on port 4444) ──
|
|
hostname: 'localhost',
|
|
port: 4444,
|
|
path: '/',
|
|
|
|
// ── Specs ──
|
|
// All specs run in a single Tauri app session — state persists between files.
|
|
// Stateful describe blocks include reset-to-home-state in their before() hooks.
|
|
specs: [
|
|
resolve(projectRoot, 'tests/e2e/specs/smoke.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/workspace.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/settings.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/features.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/terminal-theme.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-a-structure.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-a-agent.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-a-navigation.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-b-grid.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-b-llm.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-c-ui.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-c-tabs.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-c-llm.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-d-settings.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-d-errors.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-e-agents.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-e-health.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-f-search.test.ts'),
|
|
resolve(projectRoot, 'tests/e2e/specs/phase-f-llm.test.ts'),
|
|
],
|
|
|
|
// ── Capabilities ──
|
|
capabilities: [{
|
|
// Disable BiDi negotiation — tauri-driver doesn't support webSocketUrl
|
|
'wdio:enforceWebDriverClassic': true,
|
|
'tauri:options': {
|
|
application: tauriBinary,
|
|
// Test isolation: fixture-created data/config dirs, disable watchers/telemetry
|
|
env: fixture.env,
|
|
},
|
|
}],
|
|
|
|
// ── 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 ──
|
|
|
|
/**
|
|
* Build the debug binary before the test run.
|
|
* Uses --debug --no-bundle for fastest build time.
|
|
*/
|
|
onPrepare() {
|
|
if (process.env.SKIP_BUILD) {
|
|
console.log('SKIP_BUILD set — using existing debug binary.');
|
|
return Promise.resolve();
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
console.log('Building Tauri debug binary...');
|
|
const build = spawn('cargo', ['tauri', 'build', '--debug', '--no-bundle'], {
|
|
cwd: projectRoot,
|
|
stdio: 'inherit',
|
|
});
|
|
build.on('close', (code) => {
|
|
if (code === 0) {
|
|
console.log('Debug binary ready.');
|
|
resolve();
|
|
} else {
|
|
reject(new Error(`Tauri build failed with exit code ${code}`));
|
|
}
|
|
});
|
|
build.on('error', reject);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Spawn tauri-driver before the session.
|
|
* tauri-driver bridges WebDriver protocol to WebKit2GTK's inspector.
|
|
* Uses TCP probe to confirm port 4444 is accepting connections.
|
|
*/
|
|
beforeSession() {
|
|
return new Promise((res, reject) => {
|
|
tauriDriver = spawn('tauri-driver', [], {
|
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
});
|
|
|
|
tauriDriver.on('error', (err) => {
|
|
reject(new Error(
|
|
`Failed to start tauri-driver: ${err.message}. ` +
|
|
'Install it with: cargo install tauri-driver'
|
|
));
|
|
});
|
|
|
|
// TCP readiness probe — poll port 4444 until it accepts a connection
|
|
const maxWaitMs = 10_000;
|
|
const intervalMs = 200;
|
|
const deadline = Date.now() + maxWaitMs;
|
|
|
|
function probe() {
|
|
if (Date.now() > deadline) {
|
|
reject(new Error('tauri-driver did not become ready within 10s'));
|
|
return;
|
|
}
|
|
const sock = createConnection({ port: 4444, host: 'localhost' }, () => {
|
|
sock.destroy();
|
|
res();
|
|
});
|
|
sock.on('error', () => {
|
|
sock.destroy();
|
|
setTimeout(probe, intervalMs);
|
|
});
|
|
}
|
|
|
|
// Give it a moment before first probe
|
|
setTimeout(probe, 300);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Kill tauri-driver after the test run.
|
|
*/
|
|
afterSession() {
|
|
if (tauriDriver) {
|
|
tauriDriver.kill();
|
|
tauriDriver = null;
|
|
}
|
|
// Clean up test fixture
|
|
try {
|
|
rmSync(fixture.rootDir, { recursive: true, force: true });
|
|
console.log('Test fixture cleaned up.');
|
|
} catch { /* best-effort cleanup */ }
|
|
},
|
|
|
|
// ── TypeScript (auto-compile via tsx) ──
|
|
autoCompileOpts: {
|
|
tsNodeOpts: {
|
|
project: resolve(projectRoot, 'tests/e2e/tsconfig.json'),
|
|
},
|
|
},
|
|
};
|