Request bodies of URLSearchParams can not be read
See original GitHub issuePrerequisites
- I confirm my issue is not in the opened issues
- I confirm the Frequently Asked Questions didn’t contain the answer to my issue
Environment check
- I’m using the latest
msw
version - I’m using Node.js version 14 or higher
Node.js version
v16.13.0
Reproduction repository
https://github.com/markdon/cra-msw
Reproduction steps
clone repo
npm i
DEBUG=* npm run test -- --watchAll=false
Current behavior
I think this is related to https://github.com/mswjs/msw/issues/1318
When handling a fetch request where the request body is a URLSearchParameters object, the body can not be read in the handler. This was previously available through request.body
(v0.27.1). request.body
is now deprecated.
I would expect to be able to just use the newer request.text()
, however this seems to throw an error parsing the body.
Debug logs. See ERR_INVALID_ARG_TYPE
➜ cra-msw git:(main) DEBUG=* npm run test -- --watchAll=falsecra-msw@0.1.0 test react-scripts test “–watchAll=false”
babel:config:loading:files:plugins Loaded preset ‘/Users/mark/projects/cra-msw/node_modules/babel-preset-react-app/index.js’ from ‘/Users/mark/projects/cra-msw’. +0ms babel:config:loading:files:plugins Loaded preset ‘/Users/mark/projects/cra-msw/node_modules/babel-preset-jest/index.js’ from ‘/Users/mark/projects/cra-msw’. +2ms http constructing the interceptor… +0ms xhr constructing the interceptor… +0ms setup-server constructing the interceptor… +0ms http:on adding “request” event listener: setupServerListener +0ms async-event-emitter:on adding “request” listener… +0ms xhr:on adding “request” event listener: setupServerListener +0ms async-event-emitter:on adding “request” listener… +0ms http:on adding “response” event listener: +0ms async-event-emitter:on adding “response” listener… +0ms xhr:on adding “response” event listener: +0ms async-event-emitter:on adding “response” listener… +0ms setup-server:apply applying the interceptor… +0ms async-event-emitter:activate set state to: ACTIVE +0ms setup-server:apply activated the emiter! ACTIVE +0ms setup-server retrieved global instance: undefined +4ms setup-server:apply no running instance found, setting up a new instance… +1ms setup-server:setup applying all 2 interceptors… +0ms setup-server:setup applying “ClientRequestInterceptor” interceptor… +0ms http:apply applying the interceptor… +0ms async-event-emitter:activate set state to: ACTIVE +0ms http:apply activated the emiter! ACTIVE +0ms http retrieved global instance: undefined +4ms http:apply no running instance found, setting up a new instance… +0ms http:setup native “http” module patched! +0ms http:setup native “https” module patched! +0ms http set global instance! http +0ms setup-server:setup adding interceptor dispose subscription +1ms setup-server:setup applying “XMLHttpRequestInterceptor” interceptor… +0ms xhr:apply applying the interceptor… +0ms async-event-emitter:activate set state to: ACTIVE +0ms xhr:apply activated the emiter! ACTIVE +0ms xhr retrieved global instance: undefined +5ms xhr:apply no running instance found, setting up a new instance… +0ms xhr:setup patching “XMLHttpRequest” module… +0ms xhr:setup native “XMLHttpRequest” module patched! XMLHttpRequestOverride +0ms xhr set global instance! xhr +0ms setup-server:setup adding interceptor dispose subscription +0ms setup-server set global instance! setup-server +1ms xhr:request POST /login open { method: ‘POST’, url: ‘/login’, async: true, user: undefined, password: undefined } +0ms xhr:request POST /login reset +1ms xhr:request POST /login readyState change 0 -> 1 +1ms xhr:request POST /login triggering readystate change… +0ms xhr:request POST /login trigger “readystatechange” (1) +0ms xhr:request POST /login resolve listener for event “readystatechange” +0ms xhr:request POST /login set request header “content-type” to “application/x-www-form-urlencoded;charset=UTF-8” +0ms xhr:request POST /login send POST /login +0ms xhr:request POST /login request headers HeadersPolyfill { [Symbol(normalizedHeaders)]: { ‘content-type’: ‘application/x-www-form-urlencoded;charset=UTF-8’ }, [Symbol(rawHeaderNames)]: Map(1) { ‘content-type’ => ‘content-type’ } } +1ms xhr:request POST /login emitting the “request” event for 1 listener(s)… +0ms async-event-emitter:emit emitting “request” event… +0ms async-event-emitter:openListenerQueue opening “request” listeners queue… +0ms async-event-emitter:openListenerQueue no queue found, creating one… +0ms async-event-emitter:emit appending a one-time cleanup “request” listener… +0ms async-event-emitter:openListenerQueue opening “request” listeners queue… +0ms async-event-emitter:openListenerQueue returning an exising queue: [] +0ms async-event-emitter:on awaiting the “request” listener… +11ms xhr:request POST /login awaiting mocked response… +2ms async-event-emitter:on “TypeError [ERR_INVALID_ARG_TYPE]: The “input” argument must be an instance of ArrayBuffer or ArrayBufferView. Received an instance of URLSearchParams” listener has rejected! +13ms xhr:request POST /login middleware function threw an exception! TypeError: The “input” argument must be an instance of ArrayBuffer or ArrayBufferView. Received an instance of URLSearchParams at new NodeError (node:internal/errors:371:5) at TextDecoder.decode (node:internal/encoding:413:15) at Object.decodeBuffer (/Users/mark/projects/cra-msw/node_modules/@mswjs/interceptors/src/utils/bufferUtils.ts:11:18) at RestRequest.<anonymous> (/Users/mark/projects/cra-msw/node_modules/@mswjs/interceptors/src/IsomorphicRequest.ts:59:12) at step (/Users/mark/projects/cra-msw/node_modules/@mswjs/interceptors/lib/IsomorphicRequest.js:33:23) at Object.next (/Users/mark/projects/cra-msw/node_modules/@mswjs/interceptors/lib/IsomorphicRequest.js:14:53) at /Users/mark/projects/cra-msw/node_modules/@mswjs/interceptors/lib/IsomorphicRequest.js:8:71 at new Promise (<anonymous>) at Object.<anonymous>.__awaiter (/Users/mark/projects/cra-msw/node_modules/@mswjs/interceptors/lib/IsomorphicRequest.js:4:12) at RestRequest.Object.<anonymous>.IsomorphicRequest.text (/Users/mark/projects/cra-msw/node_modules/@mswjs/interceptors/lib/IsomorphicRequest.js:73:16) { code: ‘ERR_INVALID_ARG_TYPE’ } +12ms xhr:request POST /login trigger “error” (1) +8ms xhr:request POST /login resolve listener for event “error” +1ms xhr:request POST /login abort +0ms xhr:request POST /login readyState change 1 -> 0 +0ms xhr:request POST /login trigger “abort” (0) +0ms xhr:request POST /login resolve listener for event “abort” +0ms async-event-emitter:emit cleaned up “request” listeners queue! +27ms setup-server:dispose disposing the interceptor… +0ms setup-server retrieved global instance: BatchInterceptor +49ms setup-server cleared global instance! setup-server +0ms setup-server:dispose global symbol deleted: undefined +0ms setup-server:dispose disposing of 2 subscriptions… +0ms http:dispose disposing the interceptor… +0ms http retrieved global instance: ClientRequestInterceptor +51ms http cleared global instance! http +0ms http:dispose global symbol deleted: undefined +0ms http:dispose disposing of 2 subscriptions… +0ms http:setup native “http” module restored! +51ms http:setup native “https” module restored! +0ms http:dispose disposed of all subscriptions! 0 +0ms async-event-emitter:deactivate removing all listeners… +0ms async-event-emitter:removeAllListeners event: undefined +0ms async-event-emitter:removeAllListeners cleared the listeners queue! Map(0) {} +0ms async-event-emitter:deactivate set state to: DEACTIVATED +0ms http:dispose destroyed the listener! +0ms xhr:dispose disposing the interceptor… +0ms xhr retrieved global instance: XMLHttpRequestInterceptor +50ms xhr cleared global instance! xhr +0ms xhr:dispose global symbol deleted: undefined +0ms xhr:dispose disposing of 1 subscriptions… +0ms xhr:setup native “XMLHttpRequest” module restored! XMLHttpRequest +51ms xhr:dispose disposed of all subscriptions! 0 +1ms async-event-emitter:deactivate removing all listeners… +0ms async-event-emitter:removeAllListeners event: undefined +0ms async-event-emitter:removeAllListeners cleared the listeners queue! Map(0) {} +0ms async-event-emitter:deactivate set state to: DEACTIVATED +0ms xhr:dispose destroyed the listener! +0ms setup-server:dispose disposed of all subscriptions! 0 +2ms async-event-emitter:deactivate removing all listeners… +0ms async-event-emitter:removeAllListeners event: undefined +0ms async-event-emitter:removeAllListeners cleared the listeners queue! Map(0) {} +0ms async-event-emitter:deactivate set state to: DEACTIVATED +0ms setup-server:dispose destroyed the listener! +0ms FAIL src/Login.test.ts fetch ✕ can login (35 ms)
● fetch › can login
expect(received).resolves.toBeInstanceOf() Received promise rejected instead of resolved Rejected to value: [TypeError: Network request failed] 2 | describe('fetch', ()=>{ 3 | it('can login', async ()=>{ > 4 | await expect(fetch('/login', { | ^ 5 | method: 'POST', 6 | body: new URLSearchParams('username=admin&password=admin') 7 | })).resolves.toBeInstanceOf(Response); at expect (node_modules/expect/build/index.js:178:15) at Object.<anonymous> (src/Login.test.ts:4:11) at TestScheduler.scheduleTests (node_modules/@jest/core/build/TestScheduler.js:333:13) at runJest (node_modules/@jest/core/build/runJest.js:404:19) at _run10000 (node_modules/@jest/core/build/cli/index.js:320:7) at runCLI (node_modules/@jest/core/build/cli/index.js:173:3)
Test Suites: 1 failed, 1 total Tests: 1 failed, 1 total Snapshots: 0 total Time: 0.834 s, estimated 1 s Ran all test suites.
Expected behavior
A request body of type URLSearchParameters should be able to be read in a handler. I suggest via request.text()
, which would return the value of URLSearchParameters.toString().
Issue Analytics
- State:
- Created a year ago
- Reactions:1
- Comments:12 (3 by maintainers)
Top GitHub Comments
Same issue as mine in #1327 but with
FormData
bodies.msw
just plain broke reading request bodies in v0.44.0.New features keep getting added in the 0.x line so that it’s totally OK to break stuff from a semver perspective. And bugs are not fixed.
#sadness
It looks like a lot of progress has been made towards the Fetch API update that replaces the related code and fixes this issue. There’s a beta available to try.
I had a quick look at making a fix for the current release of msw but @mswjs/interceptors has already moved on from the related IsomorphicRequest class and the Fetch API update looks pretty close to done anyway.