Testing utilities

Helpers for unit and integration testing. Import from @arlopass/react/testing.

import {
  createMockTransport,
  MockArlopassProvider,
  mockWindowArlopass,
  cleanupWindowArlopass,
  simulateExternalDisconnect,
  waitForSnapshot,
  waitForChat,
  waitForStream,
  waitForState,
} from "@arlopass/react/testing";

createMockTransport

Creates a ArlopassTransport that responds to protocol capabilities without a real extension. Supports configurable responses, errors, latency, and streaming.

<ApiTable props={[
{
name: "capabilities",
type: "readonly string[]",
default:
'[ "session.create", "provider.list", "chat.completions", "chat.stream" ]',
description: "Capabilities returned on session.create.",
},
{
name: "providers",
type: "readonly { providerId: string; providerName: string; models: readonly string[] }[]",
default:
'[{ providerId: "mock", providerName: "Mock Provider", models: ["mock-model"] }]'} ,
description: "Providers returned on provider.list.",
},
{
name: "chatResponse",
type: "string | (() => string)",
default: '"Hello from mock!"',
description:
"Response for chat.completions. Can be a string or a factory function.",
},
{
name: "failOn",
type: "string",
description:
'Capability name to throw an error on (e.g. "chat.completions").',
},
{
name: "chatError",
type: "Error",
description: "Specific error to throw on chat.completions.",
},
{
name: "latency",
type: "number",
default: "0",
description: "Simulated latency in milliseconds before responding.",
},
{
name: "streamChunks",
type: "readonly string[]",
description:
"Individual chunks for chat.stream. Each string becomes a chunk payload.",
},
{
name: "streamResponse",
type: "string",
description: "Full stream response — overrides streamChunks when set.",
},
]}
/>

const transport = createMockTransport({
  chatResponse: "Hello!",
  latency: 100,
});

MockArlopassProvider

Test wrapper that injects a mock transport into window.arlopass and wraps children with <ArlopassProvider>. Drop-in replacement for the real provider in test renders.

<ApiTable props={[
{
name: "transport",
type: "ArlopassTransport",
required: true,
description: "Mock transport to inject as window.arlopass.",
},
{
name: "appId",
type: "string",
default: '"test"',
description: "App ID for the provider.",
},
{
name: "children",
type: "ReactNode",
required: true,
description: "Components under test.",
},
{
name: "...rest",
type: "ArlopassProviderProps",
description:
"All other ArlopassProvider props (defaultProvider, autoConnect, etc.).",
},
]}
/>

const transport = createMockTransport();

render(
  <MockArlopassProvider transport={transport}>
    <MyComponent />
  </MockArlopassProvider>,
);

Window mocks

Low-level functions for controlling window.arlopass in tests.

<ApiTable props={[
{
name: "mockWindowArlopass",
type: "(transport: ArlopassTransport) => void",
description:
"Injects a transport as window.arlopass. Use before rendering ArlopassProvider in tests.",
},
{
name: "cleanupWindowArlopass",
type: "() => void",
description:
"Removes window.arlopass to simulate extension not installed.",
},
{
name: "simulateExternalDisconnect",
type: "(transport: ArlopassTransport) => Promise",
description:
"Removes window.arlopass and calls transport.disconnect() if available.",
},
]}
/>

// Setup
mockWindowArlopass(transport);

// Teardown
cleanupWindowArlopass();

// Simulate disconnect
await simulateExternalDisconnect(transport);

Wait helpers

Polling utilities for async test assertions. All accept an optional timeout (default 3000ms, poll interval 50ms).

<ApiTable props={[
{
name: "waitForSnapshot",
type: "(store: ClientStore, predicate: (snapshot: ClientSnapshot) => boolean, options?: { timeout?: number }) => Promise",
description:
"Polls the store until the predicate returns true. Default timeout: 3000ms.",
},
{
name: "waitForChat",
type: "(screen: Screen, testId?: string) => Promise",
description:
'Waits for an element with the given data-testid (default: "chat-ready") to appear.',
},
{
name: "waitForStream",
type: "(screen: Screen, options?: { timeout?: number }) => Promise",
description:
'Waits until the streaming indicator disappears (data-testid="streaming" is removed).',
},
{
name: "waitForState",
type: "(screen: Screen, state: string, options?: { timeout?: number }) => Promise",
description:
'Waits until data-testid="state" has the given text content.',
},
]}
/>

// Wait for connected state
await waitForState(screen, "connected");

// Wait for streaming to finish
await waitForStream(screen, { timeout: 5000 });