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>
This commit is contained in:
44
packages/core-testing/src/react/mock-trpc.test.ts
Normal file
44
packages/core-testing/src/react/mock-trpc.test.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { initTRPC } from "@trpc/server";
|
||||
import { z } from "zod";
|
||||
import { createMockTrpcClient } from "@/react/mock-trpc";
|
||||
|
||||
const t = initTRPC.create();
|
||||
const _router = t.router({
|
||||
blog: t.router({
|
||||
articles: t.procedure.output(z.string()).query(() => "articles"),
|
||||
articleBySlug: t.procedure.output(z.string()).query(() => "article"),
|
||||
}),
|
||||
});
|
||||
type Router = typeof _router;
|
||||
|
||||
describe("createMockTrpcClient", () => {
|
||||
it("resolves a single call from its mock", async () => {
|
||||
const client = createMockTrpcClient<Router>({
|
||||
"blog.articles": "mock-articles",
|
||||
});
|
||||
await expect(client.blog.articles.query()).resolves.toBe("mock-articles");
|
||||
});
|
||||
|
||||
it("resolves two same-tick (batched) calls, one result per procedure", async () => {
|
||||
const client = createMockTrpcClient<Router>({
|
||||
"blog.articles": "mock-articles",
|
||||
"blog.articleBySlug": "mock-article",
|
||||
});
|
||||
// httpBatchLink joins these into ONE request with a comma-separated
|
||||
// procedure path — the stub must answer per procedure, in order.
|
||||
const [articles, article] = await Promise.all([
|
||||
client.blog.articles.query(),
|
||||
client.blog.articleBySlug.query(),
|
||||
]);
|
||||
expect(articles).toBe("mock-articles");
|
||||
expect(article).toBe("mock-article");
|
||||
});
|
||||
|
||||
it("rejects a call whose procedure has no mock", async () => {
|
||||
const client = createMockTrpcClient<Router>({});
|
||||
await expect(client.blog.articles.query()).rejects.toThrow(
|
||||
/No mock for blog\.articles/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -10,12 +10,27 @@ export function createMockTrpcClient<TRouter extends AnyTRPCRouter>(
|
||||
) {
|
||||
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\//, "");
|
||||
const result = mocks[path];
|
||||
if (result === undefined) {
|
||||
return new Response(JSON.stringify([{ error: { code: -32603, message: `No mock for ${path}` } }]), { status: 200 });
|
||||
}
|
||||
return new Response(JSON.stringify([{ result: { data: superjson.serialize(result) } }]), { status: 200 });
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user