diff --git a/src/node/SetupServerApi.ts b/src/node/SetupServerApi.ts index 557ce47b5..291c66feb 100644 --- a/src/node/SetupServerApi.ts +++ b/src/node/SetupServerApi.ts @@ -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( diff --git a/test/node/msw-api/many-request-handlers-jsdom.test.ts b/test/node/msw-api/many-request-handlers-jsdom.test.ts new file mode 100644 index 000000000..0a1755cdd --- /dev/null +++ b/test/node/msw-api/many-request-handlers-jsdom.test.ts @@ -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 } }) +})