Guides
Vitest Setup
Configure Vitest for Open Harness testing
Vitest Setup
Configure Vitest to use Open Harness signal matchers.
Installation
bun add -D @open-harness/vitest vitestConfiguration
Option 1: Setup File (Recommended)
Add the setup file to your Vitest config:
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
setupFiles: ["@open-harness/vitest/setup"],
},
});This automatically registers all matchers globally.
Option 2: Manual Registration
Register matchers in individual test files:
import { expect } from "vitest";
import { toContainSignal, toHaveSignalsInOrder } from "@open-harness/vitest";
expect.extend({ toContainSignal, toHaveSignalsInOrder });Or create your own setup file:
import { expect } from "vitest";
import {
toContainSignal,
toHaveSignalsInOrder,
toHaveSignalWithPayload,
} from "@open-harness/vitest";
expect.extend({
toContainSignal,
toHaveSignalsInOrder,
toHaveSignalWithPayload,
});import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
setupFiles: ["./test/setup.ts"],
},
});TypeScript Support
For TypeScript autocomplete, add the matcher types:
import "@open-harness/vitest";This extends Vitest's expect with Open Harness matchers.
Test Timeouts
Agent tests can be slow. Configure appropriate timeouts:
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
setupFiles: ["@open-harness/vitest/setup"],
testTimeout: 30000, // 30 seconds for agent tests
},
});Or per-test:
it("slow agent test", async () => {
// ...
}, 60000); // 60 second timeoutEnvironment Variables
For live tests, ensure Claude Code authentication is available:
# Run tests from Claude Code terminal
bun testFor CI environments, you may need to set up authentication differently or use recording/replay.
Example Test Structure
my-project/
├── src/
│ └── agents/
│ ├── analyzer.ts
│ └── reviewer.ts
├── test/
│ ├── setup.ts
│ ├── analyzer.test.ts
│ └── reviewer.test.ts
├── vitest.config.ts
└── package.jsonExample Test File
import { describe, it, expect } from "vitest";
import { createWorkflow, ClaudeHarness } from "@open-harness/core";
type State = {
input: string;
analysis: string | null;
};
describe("Analyzer Agent", () => {
const { agent, runReactive } = createWorkflow<State>();
const analyzer = agent({
prompt: "Analyze: {{ state.input }}",
activateOn: ["workflow:start"],
emits: ["analysis:complete"],
updates: "analysis",
});
it("activates on workflow:start", async () => {
const result = await runReactive({
agents: { analyzer },
state: { input: "Test input", analysis: null },
harness: new ClaudeHarness(),
endWhen: (s) => s.analysis !== null,
});
expect(result.signals).toContainSignal("agent:activated");
});
it("emits analysis:complete signal", async () => {
const result = await runReactive({
agents: { analyzer },
state: { input: "Test input", analysis: null },
harness: new ClaudeHarness(),
endWhen: (s) => s.analysis !== null,
});
expect(result.signals).toContainSignal("analysis:complete");
});
it("updates state.analysis", async () => {
const result = await runReactive({
agents: { analyzer },
state: { input: "Test input", analysis: null },
harness: new ClaudeHarness(),
endWhen: (s) => s.analysis !== null,
});
expect(result.state.analysis).toBeDefined();
expect(result.state.analysis).not.toBeNull();
});
});Running Tests
# Run all tests
bun test
# Run specific test file
bun test analyzer.test.ts
# Run in watch mode
bun test --watch
# Run with coverage
bun test --coverage