Tauri + Svelte 5 + Rust application for orchestrating multiple AI coding agents. Includes Claude, Aider, Codex, and Ollama provider support, multi-agent communication (btmsg/bttask), session anchors, plugin sandbox, FTS5 search, Landlock sandboxing, and 507 vitest + 110 cargo tests. |
||
|---|---|---|
| .. | ||
| specs | ||
| fixtures.ts | ||
| llm-judge.ts | ||
| README.md | ||
| results-db.ts | ||
| tsconfig.json | ||
| wdio.conf.js | ||
E2E Tests (WebDriver)
Tauri apps use the WebDriver protocol for E2E testing (not Playwright directly). The app runs inside WebKit2GTK on Linux, so tests interact with the real WebView.
Prerequisites
- Rust toolchain (for building the Tauri app)
- Display server (X11 or Wayland) — headless Xvfb works for CI
tauri-driverinstalled:cargo install tauri-driverwebkit2gtk-driversystem package:sudo apt install webkit2gtk-driver- npm devDeps already in package.json (
@wdio/cli,@wdio/local-runner,@wdio/mocha-framework,@wdio/spec-reporter)
Running
# From v2/ directory — builds debug binary automatically, spawns tauri-driver
npm run test:e2e
# Skip rebuild (use existing binary)
SKIP_BUILD=1 npm run test:e2e
# With test isolation (custom data/config dirs)
BTERMINAL_TEST_DATA_DIR=/tmp/bt-test/data BTERMINAL_TEST_CONFIG_DIR=/tmp/bt-test/config npm run test:e2e
The wdio.conf.js handles:
- Building the debug binary (
cargo tauri build --debug --no-bundle) inonPrepare - Spawning
tauri-driverbefore each session (TCP readiness probe, 10s deadline) - Killing
tauri-driverafter each session - Passing
BTERMINAL_TEST=1env var to the app for test mode isolation
Test Mode (BTERMINAL_TEST=1)
When BTERMINAL_TEST=1 is set:
- File watchers (watcher.rs, fs_watcher.rs) are disabled to avoid inotify noise
- Wake scheduler is disabled (no auto-wake timers)
- Data/config directories can be overridden via
BTERMINAL_TEST_DATA_DIR/BTERMINAL_TEST_CONFIG_DIR
CI setup (headless)
# Install virtual framebuffer + WebKit driver
sudo apt install xvfb webkit2gtk-driver
# Run with Xvfb wrapper
xvfb-run npm run test:e2e
Writing tests
Tests use WebdriverIO with Mocha. Specs go in specs/:
import { browser, expect } from '@wdio/globals';
describe('BTerminal', () => {
it('should show the status bar', async () => {
const statusBar = await browser.$('[data-testid="status-bar"]');
await expect(statusBar).toBeDisplayed();
});
});
Stable selectors
Prefer data-testid attributes over CSS class selectors:
| Element | Selector |
|---|---|
| Status bar | [data-testid="status-bar"] |
| Sidebar rail | [data-testid="sidebar-rail"] |
| Settings button | [data-testid="settings-btn"] |
| Project box | [data-testid="project-box"] |
| Project ID | [data-project-id="..."] |
| Project tabs | [data-testid="project-tabs"] |
| Agent session | [data-testid="agent-session"] |
| Agent pane | [data-testid="agent-pane"] |
| Agent status | [data-agent-status="idle|running|..."] |
| Agent messages | [data-testid="agent-messages"] |
| Agent prompt | [data-testid="agent-prompt"] |
| Agent submit | [data-testid="agent-submit"] |
| Agent stop | [data-testid="agent-stop"] |
| Terminal tabs | [data-testid="terminal-tabs"] |
| Add tab button | [data-testid="tab-add"] |
| Terminal toggle | [data-testid="terminal-toggle"] |
| Command palette | [data-testid="command-palette"] |
| Palette input | [data-testid="palette-input"] |
Key constraints
maxInstances: 1— Tauri doesn't support parallel WebDriver sessions- Mocha timeout is 60s — the app needs time to initialize
- Tests interact with the real WebKit2GTK WebView, not a browser
- Use
browser.execute()for JS clicks when WebDriver clicks don't trigger Svelte handlers - Agent tests (Scenario 7) require a real Claude CLI install + API key — they skip gracefully if unavailable
Test infrastructure
Fixtures (fixtures.ts)
Creates isolated test environments with temp data/config dirs and git repos:
import { createTestFixture, destroyTestFixture } from '../fixtures';
const fixture = createTestFixture('my-test');
// fixture.dataDir, fixture.configDir, fixture.projectDir, fixture.env
destroyTestFixture(fixture);
Results DB (results-db.ts)
JSON-based test results store for tracking runs and steps:
import { ResultsDb } from '../results-db';
const db = new ResultsDb();
db.startRun('run-001', 'v2-mission-control', 'abc123');
db.recordStep({ run_id: 'run-001', scenario_name: 'Smoke', step_name: 'renders', status: 'passed', ... });
db.finishRun('run-001', 'passed', 5000);
File structure
tests/e2e/
├── README.md # This file
├── wdio.conf.js # WebdriverIO config with tauri-driver lifecycle
├── tsconfig.json # TypeScript config for test specs
├── fixtures.ts # Test fixture generator (isolated environments)
├── results-db.ts # JSON test results store
└── specs/
├── bterminal.test.ts # Smoke tests (CSS class selectors, 50+ tests)
└── agent-scenarios.test.ts # Phase A scenarios (data-testid selectors, 22 tests)
References
- Tauri WebDriver docs: https://v2.tauri.app/develop/tests/webdriver/
- WebdriverIO docs: https://webdriver.io/
- tauri-driver: https://crates.io/crates/tauri-driver