PAIPAI

React Testing

Test PAI React renderers, chats, hooks, and thread lists with @pai/react-test-utils.

React tests use @pai/react-test-utils. For contract-bound renderer and client harness tests, create one typed helper from your React binding:

import { createReactTestUtils } from "@pai/react-test-utils";
import { Pai, SupportAI } from "./pai";

const t = createReactTestUtils({ Pai, AI: SupportAI });

The examples in this section use React Testing Library and user-event because they are common React testing tools. PAI does not require either one; the React test utilities provide typed fixtures and mock client providers that can be used with your app's existing React test renderer.

There are three normal frontend layers. Pick the smallest layer that includes the boundary you need to prove.

LayerWhat Is RealWhat Is FakeUse For
Renderer testsOne extracted React componentProviders, hooks, client, runtime, agent, model, toolsTool card rendering, visual states, submit button behavior inside one renderer
Client tool testsOne client-defined tool's schemas, executor, renderer, action, and ToolDataAgent, providers, thread, and transportBrowser behavior, live progress UI, manual client-tool output
Client harness testsPAI React providers and hooksThe client transport and client-visible thread stateChat UI, thread lists, pending-action UI, scripted streaming, hook behavior

The Boundary

Renderer tests start with typed props. They do not create a thread.

const compareProposals = t.toolProps("compareProposals");
const tool = compareProposals.outputAvailable({ input, output });

render(<CompareProposalsRenderer tool={tool} />);

Client harness tests mount the real React integration, but there is no runtime behind it. The test scripts the same public thread state a real client would receive from a server. PAI provides the provider harness; your test suite still uses its normal React render helper.

await using h = t.createClientHarness({ threadId: "thread-1" });

await h.driver.responses.queue((r) =>
  r.assistant([r.text("Searching agencies complete.")]),
);

render(<ChatSurface />, { wrapper: h.Provider });

Choosing A Layer

If You Need To ProveUse
One renderer handles native output states or derived running and waiting display statesRenderer test
One client-defined tool executes browser behavior or coordinates its renderer and ToolDataClient tool test
A hook-driven component reacts to messages, runs, pending-action sidecars, or thread list changesClient harness test
A specific stream shape appears over time, but runtime behavior is irrelevantClient harness test
useThreads() refreshes when a thread-created event arrivesClient harness test
The real server agent chooses good tools or answers wellAgent eval, not React testing

Use backend agent/runtime tests to prove how runtime state is produced. Use client harness tests to prove React behavior against that public state shape.

What Not To Do

  • Do not import the real server agent for a renderer unit test.
  • Do not mock React hooks directly when the client harness can mount real providers.
  • Do not import the real server agent into frontend tests just to get a typed contract. Prefer generated contracts.
  • Do not use fireEvent for normal user interactions; prefer userEvent.setup() and await user.click(...).

Next

On this page