Skip to content

Commit

Permalink
fix(setupserver): prevent EventEmitter.setMaxListeners runtime except…
Browse files Browse the repository at this point in the history
…ions in JSDOM
  • Loading branch information
christoph-fricke committed Oct 20, 2023
1 parent f5f31fa commit a2535e6
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 4 deletions.
21 changes: 17 additions & 4 deletions src/node/SetupServerApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,23 @@ export class SetupServerApi
// new "abort" event listener to the parent request's
// "AbortController" so if the parent aborts, all the
// clones are automatically aborted.
setMaxListeners(
Math.max(defaultMaxListeners, this.currentHandlers.length),
request.signal,
)
try {
setMaxListeners(
Math.max(defaultMaxListeners, this.currentHandlers.length),
request.signal,
)
} catch (e: unknown) {
/**
* @note Mock environments (JSDOM, ...) are not able to implement an internal
* "kIsNodeEventTarget" Symbol that Node.js uses to identify Node.js `EventTarget`s.
* `setMaxListeners` throws an error for non-Node.js `EventTarget`s.
* At the same time, mock environments are also not able to implement the
* internal "events.maxEventTargetListenersWarned" Symbol, which results in
* "MaxListenersExceededWarning" not being printed by Node.js for those anyway.
* The main reason for using `setMaxListeners` is to suppress these warnings in Node.js,
* which won't be printed anyway if `setMaxListeners` fails.
*/
}
}

const response = await handleRequest(
Expand Down
58 changes: 58 additions & 0 deletions test/node/msw-api/many-request-handlers-jsdom.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* @jest-environment jsdom
*/
import { graphql, http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'

// Create a large number of request handlers.
const restHandlers = new Array(100).fill(null).map((_, index) => {
return http.post(
`https://example.com/resource/${index}`,
async ({ request }) => {
const text = await request.text()
return HttpResponse.text(text + index.toString())
},
)
})

const graphqlHanlers = new Array(100).fill(null).map((_, index) => {
return graphql.query(`Get${index}`, () => {
return HttpResponse.json({ data: { index } })
})
})

const server = setupServer(...restHandlers, ...graphqlHanlers)

beforeAll(() => {
server.listen()
jest.spyOn(process.stderr, 'write')
})

afterAll(() => {
server.close()
jest.restoreAllMocks()
})

it('does not print a memory leak warning when having many request handlers', async () => {
const httpResponse = await fetch('https://example.com/resource/42', {
method: 'POST',
body: 'request-body-',
}).then((response) => response.text())

const graphqlResponse = await fetch('https://example.com', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `query Get42 { index }`,
}),
}).then((response) => response.json())

// Must not print any memory leak warnings.
expect(process.stderr.write).not.toHaveBeenCalled()

// Must return the mocked response.
expect(httpResponse).toBe('request-body-42')
expect(graphqlResponse).toEqual({ data: { index: 42 } })
})

0 comments on commit a2535e6

Please sign in to comment.