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>
48 lines
1.8 KiB
TypeScript
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] });
|
|
}
|