mswjs / msw

Industry standard API mocking for JavaScript.
https://mswjs.io
MIT License
15.98k stars 519 forks source link

Trying to pull body in POST mocks breaks mocking #2162

Closed reintroducing closed 3 months ago

reintroducing commented 6 months ago

Prerequisites

Environment check

Browsers

Chromium (Chrome, Brave, etc.)

Reproduction repository

https://github.com/reintroducing/vite-msw

Reproduction steps

Current behavior

When using async/await handler for post request and trying to get the body using const body = await request.formData();, the request fails. Note that the method can be async but just trying to get the body using await is what is causing the issue.

Expected behavior

Should be able to retrieve the body and log out foo as the docs outline and the mock should function as expected.

haakonph commented 6 months ago

I have the same issue. Using node v20.13.1 it works with node V20.0.0

reintroducing commented 5 months ago

@kettanaito just wanted to follow up on this and see if there is anything else that would be helpful in getting to the bottom of it. This seems like a rather large issue considering the documented behavior seems to be broken, i'm surprised there are not more reports of this.

I have not tested different node versions but for what its worth i'm on v20.11.1.

kjindal0802 commented 5 months ago

@kettanaito I am also facing the same issue, when I am trying to pull request body or formData by using async await, it breaks the handlers without any error.

http.post(
  `https://mydomain/upload`,
  async ({ request }) => {
  const info = await request.formData();
  console.log('info--------------', info);
  return HttpResponse.json(bulkUploadErrorResponse.FAILED);
  }
)

test('success message should appear on screen when a correct bulk file is uploaded', async () => {
  render(<MyComponent />);

  const file = generateXLSXFile('Sample Content', 'testFile'); // custom method to generate excel file
  const fileInput = screen.getByTestId('file-upload');

  // eslint-disable-next-line
  await act(async () => {
  console.log('Uploading file ......');
  await userEvent.upload(fileInput, file);
  });

  // eslint-disable-next-line
  expect(
  await screen.findByText(
  'Bulk Upload for the inventory has been successfully uploaded!'
  )
  );
});
kettanaito commented 3 months ago

Hi, @reintroducing. Thanks for reporting this.

It's important to stress that FormData handling is different in the browser and Node.js (because the implementations of that class are different there), so let's approach this issue per environment.

I don't think your expected behavior applies here. The click handler for the "Test post" button doesn't send a FormData request body:

https://github.com/reintroducing/vite-msw/blob/21bda295d3cc2e693e5ac54865d81a22d33cfd6a/src/App.tsx#L86-L95

As such, you won't be able to read this request's body as FormData. It will be missing boundaries that the browsers sets for multipart/form-data requests, for once, failing the parsing in request.formData().

You should receive an error in that case. Do you?

kettanaito commented 3 months ago

I also encourage anyone experiencing this issue to update msw and see if it's still there. Your reproduction example if 4 patch versions behind the latest. Perhaps this has already been addressed.

reintroducing commented 3 months ago

@kettanaito Ah, that makes sense. Updating msw did not fix the "issue", however I made the following changes:

in utils.ts I changed: ...(body && {body: JSON.stringify(body)}), to ...(body && {body: createFormData(body)}),.

That method looks like this (for anyone reading along):

function createFormData(object: Record<string, unknown>) {
  const formData = new FormData();

  Object.keys(object).forEach(key =>
    formData.append(key, object[key] as string),
  );

  return formData;
}

This now works as expected and I get no errors. Thank you!