@repo/datadata/test
Test helpers: a mock live client, a recording logger, and a collector for write errors.
Functions
collectWriteErrors
export declare function collectWriteErrors(client: {
onWriteError: (cb: (error: WriteError) => void) => () => void;
}): WriteError[];Register an onWriteError listener and return the array it accumulates into. A rejected fire-and-forget write surfaces here (awaited writes reject their promise instead), so tests assert on this rather than a status-doc error.
createMockLiveClient
export declare function createMockLiveClient<Live>(live: Live): MockLiveClient<Live>;Wraps a faked live-session object as a MockLiveClient, so a unit mock can hand the result anywhere a client.live-shaped value is expected without writing the { live: … } wrapper (or the cast) inline. Live is inferred from the argument:
const client = createMockLiveClient({
createDocument: vi.fn(),
updateDocument: vi.fn(),
});
client.live.createDocument(...);
For a full in-memory fake instead of a hand-rolled one, reach for createInMemoryClient from @repo/datadata/in-memory.
createMockLogger
export declare function createMockLogger(): MockLogger;A MockLogger that discards output and records every call. child() returns the same logger, so calls made through a child are recorded on it too.
getMockedLogs
export declare function getMockedLogs(logger: MockLogger): Record<string, unknown[]>;The first argument of every recorded call, grouped by level (info, warn, error, debug), for snapshotting. Levels with no calls are left out.
Types
MockLiveClient
export type MockLiveClient<Live = Partial<LiveSession>> = {
live: Live;
};Shape of a hand-rolled client double for code that only touches the live editing surface.
Since editing/reading moved off the bare client onto the ambient live session, every mock that fakes createDocument / updateDocument / getDocument must nest them under .live, exactly where the real client keeps them. This type captures that one bit of structure so consumers don't re-derive the nesting (and the as unknown as … cast) by hand. Pick the subset you actually call from LiveSession:
import type { LiveSession } from "@repo/datadata/session";
import type { MockLiveClient } from "@repo/datadata/test";
type Bridge = MockLiveClient<Pick<LiveSession, "createDocument" | "updateDocument">>;
MockLogger
export type MockLogger = Logger & {
info: RecordedFn<Parameters<Logger["info"]>, void>;
warn: RecordedFn<Parameters<Logger["warn"]>, void>;
error: RecordedFn<Parameters<Logger["error"]>, void>;
debug: RecordedFn<Parameters<Logger["debug"]>, void>;
child: RecordedFn<Parameters<Logger["child"]>, Logger>;
};A Logger whose methods record their calls; see createMockLogger.
RecordedFn
export type RecordedFn<Args extends unknown[], Return> = ((...args: Args) => Return) & {
calls: Args[];
};A function that records the arguments of every call in calls. Not a vitest mock, so it works outside vitest too.