Matt Thompson
Matt Thompson2y ago

Mocking tRPC call w/ Playwright (Transform Error)

I have a tRPC call that I would like to mock out for a Playwright E2E test. I've followed their docs as shown below and used the same transformer (superjson) from our tRPC setup.
When inspected via network tab the Call seems to succeed as expected. (image) However, the client errors w/
TRPCClientError: Unable to transform response from server
at transformResult (transformResult-6fb67924.mjs:74:1)
at eval (httpBatchLink.mjs:188:56)
TRPCClientError: Unable to transform response from server
at transformResult (transformResult-6fb67924.mjs:74:1)
at eval (httpBatchLink.mjs:188:56)
https://playwright.dev/docs/mock#mock-api-requests
import { transformer } from '@/utils/trpc';
...
await page.route('**/api/trpc/onboarding.verifyPrimaryPractice?batch=1', (route) => {
route.fulfill({
status: 200,
body: transformer.stringify({ success: true }),
headers: { 'Content-Type': 'application/json' },
});
});
import { transformer } from '@/utils/trpc';
...
await page.route('**/api/trpc/onboarding.verifyPrimaryPractice?batch=1', (route) => {
route.fulfill({
status: 200,
body: transformer.stringify({ success: true }),
headers: { 'Content-Type': 'application/json' },
});
});
8 Replies
Nick
Nick2y ago
Are you writing code to interact with the API directly?
Nick
Nick2y ago
Maybe best to use the tRPC Client for this? https://trpc.io/docs/client/introduction
tRPC Client | tRPC
The "vanilla" tRPC client can be used to call your API procedures as if they are local functions, enabling a seamless development experience.
Nick
Nick2y ago
OH sorry, I see you're intercepting and stubbing it out? Not familiar with playwright We would generally recommend you bring your API under test with tRPC. It's easier to stub out external services and control DB state than to replace the API, and makes for more representative e2e tests If you're really keen on it though, it looks like your approach is on the right track
Matt Thompson
Matt ThompsonOP2y ago
Correct, we are using trpc client below on the form submit. I'm trying to stub that call out. Outside of the transform piece - it seems to be working as expected. I'm just unsure why the transform fails.
const verifyPractice = trpc.onboarding.verifyPrimaryPractice.useMutation({
onSuccess: () => {...}
onError: () => {...}
})

...

const handleOnSubmit = async (data) => await verifyPractice.mutateAsync(data);
const verifyPractice = trpc.onboarding.verifyPrimaryPractice.useMutation({
onSuccess: () => {...}
onError: () => {...}
})

...

const handleOnSubmit = async (data) => await verifyPractice.mutateAsync(data);
Nick
Nick2y ago
You have batching enabled on the client (batch=1), but it looks like your transformed response is in the unbatched response form 2 recommendations: 1. return your response in an array like you'll see when you run your app for real 2. don't match the whole URL, just match the onboarding.verifyPrimaryPractice bit, as a batched up call will have several paths comma separated and an encoded array of inputs to send to each one respectively - shouldn't be too hard to parse the inputs and respond in the correct form Probably will save pain all round if you just disable batching during testing though. I bet responding to a batched request with 2 calls in would be a PITA
Matt Thompson
Matt ThompsonOP2y ago
Looking at the above. Do you mind expanding on this a bit? " We would generally recommend you bring your API under test with tRPC.
Nick
Nick2y ago
Run the API for real Emulate any 3rd party services, run local DBs with real/mock data in.
Matt Thompson
Matt ThompsonOP2y ago
Ah, Yea For the most part we are doing that with Factories, etc. This call has a 3rd party call where I don't have a Sandbox to run against. I was originally looking for the equiv of Jest.mock in Playwright E2E The closest thing they support is Network mocking Sadly the 3rd party call couldn't be mocked in this scenario as the tRPC call is calling it within Fun edge case I guess. I'm looking at the real result like you mentioned above and trying to mock the array. I didn't take that into account earlier. 🤞 Worked 🙂 End Result for me:
await page.route('**/api/trpc/onboarding.verifyPrimaryPractice?batch=1', (route) => {
route.fulfill({
status: 200,
body: JSON.stringify([
{ result: { data: { json: { success: true } }, meta: { values: {} } } },
]),
headers: { 'Content-Type': 'application/json' },
});
});
await page.route('**/api/trpc/onboarding.verifyPrimaryPractice?batch=1', (route) => {
route.fulfill({
status: 200,
body: JSON.stringify([
{ result: { data: { json: { success: true } }, meta: { values: {} } } },
]),
headers: { 'Content-Type': 'application/json' },
});
});
In this case I don't really care about the return - I simply want it to keep going But you could mock out the full return under data.json here. The way the tests run, I shouldn't have more than one for batch Thanks for the 👀 and help @Nick Lucas