Chat · API Reference

mockAgent()

mockAgent() creates a runtime-neutral Agent mock with writable signals for testing @threadplane/chat components. Use it when you're testing chat UI behavior and don't need LangGraph-specific fields.

Import:

import { mockAgent } from '@threadplane/chat';
import type { MockAgent } from '@threadplane/chat';

#Signature

function mockAgent(opts?: MockAgentOptions): MockAgent

#Options

OptionTypeDefaultDescription
messagesMessage[][]Initial runtime-neutral messages
statusAgentStatus'idle'Initial agent status
isLoadingbooleanfalseWhether the agent is currently running
errorunknownnullInitial error value
toolCallsToolCall[][]Initial tool-call list
stateRecord<string, unknown>{}Initial agent state
withInterruptbooleanfalseInclude a writable interrupt signal
withSubagentsbooleanfalseInclude a writable subagents signal
historyAgentCheckpoint[]undefinedInclude history and return an AgentWithHistory-compatible mock
events$Observable<AgentEvent>EMPTYEvent stream the mock exposes via events$ (defaults to RxJS EMPTY)

#Basic Test Usage

import { TestBed } from '@angular/core/testing';
import { ChatComponent, mockAgent } from '@threadplane/chat';
 
it('renders messages from an agent', async () => {
  const agent = mockAgent({
    messages: [
      { id: 'm1', role: 'user', content: 'Hello' },
      { id: 'm2', role: 'assistant', content: 'Hi there' },
    ],
  });
 
  await TestBed.configureTestingModule({
    imports: [ChatComponent],
  }).compileComponents();
 
  const fixture = TestBed.createComponent(ChatComponent);
  fixture.componentRef.setInput('agent', agent);
  fixture.detectChanges();
 
  expect(fixture.nativeElement.textContent).toContain('Hello');
  expect(fixture.nativeElement.textContent).toContain('Hi there');
});

#Controlling State

The returned mock exposes writable signals, so tests can update state directly:

const agent = mockAgent();
 
agent.isLoading.set(true);
agent.error.set(new Error('Connection failed'));
agent.messages.set([{ id: 'm1', role: 'assistant', content: 'Retry?' }]);

#Spying on Actions

The mock records calls to core actions:

const agent = mockAgent();
 
await agent.submit({ message: 'Hello' });
await agent.stop();
await agent.regenerate(1);
 
expect(agent.submitCalls[0]?.input).toEqual({ message: 'Hello' });
expect(agent.stopCount).toBe(1);
expect(agent.submitCalls[1]?.input).toEqual({
  regenerate: { assistantMessageIndex: 1 },
});

#Lifecycle and History

mockAgent() exposes a readonly lifecycle.streamStartedAt signal plus an _internal.streamStartedAt writable escape hatch. Use the escape hatch to drive stream-start timing in a test without running a full submit/stream cycle:

const agent = mockAgent();
 
// Flip the underlying writable; lifecycle.streamStartedAt() reflects it.
agent._internal.streamStartedAt.set(Date.now());
expect(agent.lifecycle.streamStartedAt()).not.toBeNull();

The withInterrupt, withSubagents, and history options unlock optional signals — interrupt, subagents, and history are undefined unless you opt in, so tests must null-check (or use ?.) before driving them:

const agent = mockAgent({
  withInterrupt: true,
  withSubagents: true,
  history: [], // pass an AgentCheckpoint[] to enable agent.history
});
 
// Each optional signal is writable when its option was set.
agent.interrupt?.set({ id: 'int-1', value: 'Approve this action?', resumable: true });
agent.history?.set([{ id: 'ckpt-1', values: {} }]);
expect(agent.subagents?.()).toBeDefined();

#LangGraph-Specific Tests

Use mockLangGraphAgent() from @threadplane/langgraph only when the test needs LangGraph-specific signals such as raw LangGraph messages, checkpoint state, branches, queued runs, or transport metadata.