Closed DamianPereira closed 3 years ago
Hey, @DamianPereira. Thanks for reaching out with this.
You can respond with mocked images/videos/audio and other media types with MSW. To do that, one needs to compose a proper binary response, as aforementioned media types are not strictly textual responses.
Composing such mocked response comes down to these crucial points:
Content-Type
and Content-Length
response headers.res.body
.For example, to respond with a mocked image use the following handler:
import base64Image from 'url-loader!../fixtures/image.jpg'
rest.get('/api/files/:id/content', async (_, res, ctx) => {
// Convert "base64" image to "ArrayBuffer".
const imageBuffer = await fetch(base64Image).then((res) =>
res.arrayBuffer(),
)
return res(
ctx.set('Content-Length', imageBuffer.byteLength.toString()),
ctx.set('Content-Type', 'image/jpeg'),
// Respond with the "ArrayBuffer".
ctx.body(imageBuffer),
)
})
Learn more in the Binary response types in the documentation.
Great thanks! Sorry I missed it in the docs. It still might be nice to have something like ctx.jpg that abstracts this further, but this will do for now.
That's the beauty of a functional API the team works hard on designing: instead of handling each individual use case, or bloating things with options and configurations, you are given a function primitives to abstract, reuse, and create.
Here's how you'd implement a custom response transformer to make your mock definitions more concise:
// mocks/ctx/jpeg.js
import { context } from 'msw'
export const jpeg = async (base64Image) => {
const imageBuffer = await fetch(base64Image)
.then(res => res.arrayBuffer())
return (res) => {
context.set('Content-Length', imageBuffer.byteLength.toStrong())(res)
context.set('Content-Type', 'image/jpeg')(res)
context.body(imageBuffer)(res)
}
}
// mocks/handlers.js
import base64Image from 'url-loader!../fixtures/image.jpg'
import { jpeg } from './ctx/jpeg'
rest.get('...', async (req, res, ctx) => {
// Use your custom response transformer
const respondWithImage = await jpeg(base64Image)
return res(respondWithImage)
})
Currently MSW doesn't support asynchronous response transformers, that's why the
jpeg
transformer needs to be awaited first, before providing it to theres()
function.
Oh, I like that, yeah that solves the problem nicely when having to serve multiple images, thanks!
I have the problem: [MSW] Warning: captured a request without a matching request handler:
• GET http://localhost/iVBORw0KGgoAAAANS....
export const setupFetchProviderConversationBlobHandler = () => { server.use( rest.get(/(.*)attachments\/\d+$/, async (_, res, ctx) => { const imageBuffer = await fetch(base64Image).then((res) => res.arrayBuffer()); return res( ctx.status(200), ctx.set('Content-Length', imageBuffer.byteLength.toString()), ctx.set('Content-Type', 'image/png'), ctx.body(imageBuffer) ); }) ); };
@mordvic, as the name suggests, the actual request URL didn't match the route handler you've defined for MSW. Take a closer look at the structure of your URL and reflect it in the request handler. I'm rather confident in our path matching (since we depend on third-party) so the error is likely on your end.
Right now I'm trying to test a component in storybook, and I'm using msw to mock the neccesary routes for this component to render. One thing this component needs is a user profile image, this user is generated at random in the tests, including the id of the profile image. This url changes everytime storybook runs since the users are auto-generated for the tests, so the url of the profile image changes too.
Ideally I want to do something like this in the handlers:
Or even importing the image like storybook allows https://storybook.js.org/docs/react/configure/images-and-assets
I have tried using the ctx.body method to handle this, but it does not work:
The mocked request generates a 500 error with this message:
DOMException: Failed to execute 'postMessage' on 'MessagePort': Response object could not be cloned. (see more detailed error stack trace in the mocked response body)