Files
agentic-dev/packages/core-testing/src/react/mock-trpc.ts
Danijel Martinek 0234e18425 fix(core-testing): handle batched paths + transformed errors in mock tRPC
httpBatchLink joins same-tick calls into one request whose path is a
comma-separated list of procedure paths; the stub matched the joined
string against a single mock key, so any batched pair failed with 'No
mock for a.one,a.two'. It now answers one element per procedure, in
order. Error bodies are also superjson-serialized — raw error JSON made
the client throw 'Unable to transform response' instead of surfacing
the intended error. Regression tests added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:32:03 +02:00

48 lines
1.8 KiB
TypeScript

import { createTRPCClient, httpBatchLink } from "@trpc/client";
import superjson from "superjson";
import type { AnyTRPCRouter } from "@trpc/server";
// Returns a tRPC client whose fetch is a stub honouring the provided mocks.
// Mocks are keyed by procedure path ("blog.articleBySlug") returning the
// raw response body.
export function createMockTrpcClient<TRouter extends AnyTRPCRouter>(
mocks: Record<string, unknown> = {},
) {
const fetchStub: typeof fetch = async (input) => {
const url = typeof input === "string" ? input : (input as Request).url;
const path = new URL(url, "http://mock").pathname.replace(
/^\/api\/trpc\//,
"",
);
// httpBatchLink joins same-tick calls into one request whose path is the
// comma-separated list of procedure paths; respond with one element per
// call, in order.
const paths = path.split(",");
const body = paths.map((p) =>
Object.hasOwn(mocks, p)
? { result: { data: superjson.serialize(mocks[p]) } }
: {
// Error payloads pass through the transformer too — serialize
// them or the client throws "Unable to transform response".
error: superjson.serialize({
code: -32603,
message: `No mock for ${p}`,
}),
},
);
return new Response(JSON.stringify(body), { status: 200 });
};
// httpBatchLink<TRouter> requires a conditional TransformerOptions<TRouter> that
// depends on whether the router uses a transformer; making this generic-friendly
// without parameterizing twice requires <any> here.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const link = httpBatchLink<any>({
url: "http://mock/api/trpc",
transformer: superjson,
fetch: fetchStub,
});
return createTRPCClient<TRouter>({ links: [link] });
}