Skip to content

Commit

Permalink
fix: added support for FormData bodies in jsdom to MockedRequest (msw…
Browse files Browse the repository at this point in the history
  • Loading branch information
ddolcimascolo committed Oct 10, 2022
1 parent 3e7c4c1 commit 631d0a6
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/handlers/RequestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export type DefaultRequestMultipartBody = Record<
export type DefaultBodyType =
| Record<string, any>
| DefaultRequestMultipartBody
| FormData
| string
| number
| boolean
Expand Down
12 changes: 12 additions & 0 deletions src/utils/request/MockedRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ export class MockedRequest<
* to read the request body as a plain text, JSON, or ArrayBuffer.
*/
public get body(): RequestBody {
/**
* If an XHR sends a FormData body, as per https://developer.mozilla.org/fr/docs/Web/API/XMLHttpRequest/send the interceptor
* will pass the raw FormData instance directly, not an ArrayBuffer. Short-circuit here in this case
*/
if (this['_body'] instanceof FormData) {
return this['_body'] as RequestBody
}

const text = decodeBuffer(this['_body'])

/**
Expand All @@ -125,6 +133,10 @@ export class MockedRequest<
return body as RequestBody
}

public formData(): FormData {
return this['_body']
}

/**
* Bypass the intercepted request.
* This will make a call to the actual endpoint requested.
Expand Down
86 changes: 83 additions & 3 deletions test/rest-api/request/body/body-form-data.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,41 @@ import { rest } from 'msw'
import { setupServer } from 'msw/node'

const server = setupServer(
rest.post('http://localhost/deprecated', (req, res, ctx) => {
rest.post('http://localhost/deprecated/json', (req, res, ctx) => {
return res(ctx.json(req.body))
}),
rest.post('http://localhost/deprecated/formData', (req, res, ctx) => {
const body = req.body as FormData
const file = body.get('file') as File

return res(
ctx.json({
username: body.get('username'),
password: body.get('password'),
file: {
name: file.name,
size: file.size,
type: file.type,
},
}),
)
}),
rest.post('http://localhost/formData', (req, res, ctx) => {
const body = req.formData()
const file = body.get('file') as File

return res(
ctx.json({
username: body.get('username'),
password: body.get('password'),
file: {
name: file.name,
size: file.size,
type: file.type,
},
}),
)
}),
)

beforeAll(() => {
Expand All @@ -17,15 +49,15 @@ afterAll(() => {
server.close()
})

test('handles "FormData" as a request body', async () => {
test('handles "FormData" from the "form-data" library as a request body', async () => {
// Note that creating a `FormData` instance in Node/JSDOM differs
// from the same instance in a real browser. Follow the instructions
// of your `fetch` polyfill to learn more.
const formData = new FormDataPolyfill()
formData.append('username', 'john.maverick')
formData.append('password', 'secret123')

const res = await fetch('http://localhost/deprecated', {
const res = await fetch('http://localhost/deprecated/json', {
method: 'POST',
headers: formData.getHeaders(),
body: formData,
Expand All @@ -38,3 +70,51 @@ test('handles "FormData" as a request body', async () => {
password: 'secret123',
})
})

test.each([
'http://localhost/formData',
'http://localhost/deprecated/formData',
])(
'handles "FormData" native object as a request body when sent as an XHR request to %s',
async (url) => {
const formData = new FormData()

formData.append('username', 'john.maverick')
formData.append('password', 'secret123')
formData.append(
'file',
new Blob(['file content'], { type: 'text/plain' }),
'file.txt',
)

await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()

xhr.open('POST', url, true)

xhr.onerror = reject
xhr.onload = () => {
try {
const jsonResponse = JSON.parse(xhr.response)

expect(xhr.status).toBe(200)
expect(jsonResponse).toEqual({
username: 'john.maverick',
password: 'secret123',
file: {
name: 'file.txt',
size: 12, // Can't match file content exactly, no support for File.text() in jsdom
type: 'text/plain',
},
})

resolve(void 0)
} catch (e) {
reject(e)
}
}

xhr.send(formData)
})
},
)

0 comments on commit 631d0a6

Please sign in to comment.