Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support msw #2297

Closed
xxleyi opened this issue Mar 4, 2023 · 23 comments · Fixed by #11082
Closed

Support msw #2297

xxleyi opened this issue Mar 4, 2023 · 23 comments · Fixed by #11082
Assignees
Labels
bug Something isn't working node.js Compatibility with Node.js APIs

Comments

@xxleyi
Copy link

xxleyi commented Mar 4, 2023

What is the problem this feature would solve?

use msw in bun has some problem, after some exploration, find node:http is not fully implemented in Bun.

with this code

import axios from 'axios';

import { setupServer } from 'msw/node'

const server = setupServer(...[])
server.listen({
  onUnhandledRequest: 'warn',
});

axios.get('https://swapi.dev/api/people/?page=2')
  .then(function (response) {
    // handle success
    console.log(response.data.results.length);
  })
  .catch(function (error) {
    // handle error
    console.log(error?.message);
  });

and dependencies:

{
  "dependencies": {
    "axios": "^1.4.0",
    "msw": "^1.2.3"
  }
}

output in Node:

[MSW] Warning: captured a request without a matching request handler:

  • GET https://swapi.dev:8001/api/people/

If you still wish to intercept this unhandled request, please create a request handler for it.
Read more: https://mswjs.io/docs/getting-started/mocks
Request failed with status code 400

output in Bun

Cannot call a class constructor without |new|

and first two of error stack is

TypeError: Cannot call a class constructor without |new|
      at ClientRequest (node:http:884:26)

What is the feature you are proposing to solve the problem?

fully implement node:http to support msw

What alternatives have you considered?

No response

@xxleyi xxleyi added the enhancement New feature or request label Mar 4, 2023
@ThatOneBro ThatOneBro added bug Something isn't working node.js Compatibility with Node.js APIs and removed enhancement New feature or request labels Mar 6, 2023
@ThatOneBro
Copy link
Contributor

ThatOneBro commented Mar 6, 2023

Thank you for reporting this.

Our node:http module is basically complete but it looks like some exports are missing (but implemented) and aside from that we have been getting a few bug reports with it lately.

I'm currently working on porting all of the node:http tests from Node and working through the bugs now and hopefully we can get full compatibility with node:http and node:https (or close to it) within the next few days or so.

@xxleyi
Copy link
Author

xxleyi commented May 22, 2023

Thank you for reporting this.

Our node:http module is basically complete but it looks like some exports are missing (but implemented) and aside from that we have been getting a few bug reports with it lately.

I'm currently working on porting all of the node:http tests from Node and working through the bugs now and hopefully we can get full compatibility with node:http and node:https (or close to it) within the next few days or so.

Sorry to bother, do we have trouble in porting node:http?

@xxleyi
Copy link
Author

xxleyi commented Jun 2, 2023

@Jarred-Sumner sorry to bother, can you have a look at this problem, which broke msw in bun:test

@birkskyum
Copy link
Collaborator

birkskyum commented Aug 27, 2023

Current output in bun:

bun index.ts
🚀 ~ http: {
  Agent: [class Agent],
  Server: [class Server],
  createServer: [Function],
  ServerResponse: [class ServerResponse],
  IncomingMessage: [class IncomingMessage],
  request: [Function],
  get: [Function],
  maxHeaderSize: 16384,
  validateHeaderName: [Function],
  validateHeaderValue: [Function],
  setMaxIdleHTTPParsers: [Function: setMaxIdleHTTPParsers],
  globalAgent: { /* Removed for comparison to Node */ },
  ClientRequest: [class ClientRequest],
  OutgoingMessage: [class OutgoingMessage]
}

Node is here

{
  _connectionListener: [Function: connectionListener],
  Agent: [Function: Agent] { defaultMaxSockets: Infinity },
  ClientRequest: [Function: ClientRequest],
  IncomingMessage: [Function: IncomingMessage],
  OutgoingMessage: [Function: OutgoingMessage],
  Server: [Function: Server],
  ServerResponse: [Function: ServerResponse],
  createServer: [Function: createServer],
  validateHeaderName: [Function: __node_internal_],
  validateHeaderValue: [Function: __node_internal_],
  get: [Function: get],
  request: [Function: request],
  maxHeaderSize: [Getter],
  globalAgent: [Getter/Setter]
}

So the discrepancy is now that only Bun has: setMaxIdleHTTPParsers and only Node has _connectionListener, and that the types are somewhat different like maxHeaderSize is a 16384 in bun and a [Getter] in Node.

@birkskyum
Copy link
Collaborator

birkskyum commented Aug 27, 2023

The ticket here is specifically about ClientRequest and msw, which now is in Bun, so @xxleyi, can you verify that this works now? You can change the title to something more specific, like Support msw or ClientRequest not in node:http, and close it if it is working.

@xxleyi
Copy link
Author

xxleyi commented Aug 28, 2023

The ticket here is specifically about ClientRequest and msw, which now is in Bun, so @xxleyi, can you verify that this works now? You can change the title to something more specific, like Support msw or ClientRequest not in node:http, and close it if it is working.

Original issue has been fixed, but Support msw still not works, I will change title to something more specific

@xxleyi xxleyi changed the title fully implemented node:http fully implemented node:http compatible with node to support msw Aug 28, 2023
@birkskyum
Copy link
Collaborator

Issue in MSW repo:

@OleksandrKucherenko
Copy link

Some additional information about node.http module compatibility in BUN mswjs/interceptors#418 (comment)

@Electroid Electroid changed the title fully implemented node:http compatible with node to support msw Support msw Oct 25, 2023
@kettanaito
Copy link

Hey, folks. So from the report in mswjs/interceptors#418 (comment), it appears that this is no longer an issue with MSW 2.0. I highly encourage you updating and letting me know whether the same cannot call without new error persists on that version. Thanks.

@Nisgrak
Copy link

Nisgrak commented Nov 13, 2023

Hey, I try with a minimal repo like this

import { HttpResponse, http } from 'msw';
import { setupServer } from 'msw/node'

const handlers = [
    http.get("https://bun.sh", () => {
        return HttpResponse.json({ "test": "a" })
    })
]

export const server = setupServer(...handlers)

server.listen({
    onUnhandledRequest: "warn"
})

const response = await fetch("https://bun.sh");
const html = await response.text(); // HTML string

And give this error:

61 | }, getEventListeners = function(emitter, type) {
62 |   if (emitter instanceof EventTarget)
63 |     throwNotImplemented("getEventListeners with an EventTarget", 2678);
64 |   return emitter.listeners(type);
65 | 
66 | }, setMaxListeners = function(n, ...eventTargets) {
                                                             ^
TypeError: undefined is not a function
      at node:events:66:58
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/msw/lib/node/index.mjs:60:10
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/msw/lib/node/index.mjs:18:4
      at new Promise (:1:20)
      at __async (/Users/enekorg/Proyectos/test/test-bun-msw/node_modules/msw/lib/node/index.mjs:2:9)
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@mswjs/interceptors/lib/node/chunk-YQGTMMOZ.mjs:50:10
      at emitAsync (/Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@mswjs/interceptors/lib/node/chunk-YQGTMMOZ.mjs:44:25)
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@mswjs/interceptors/lib/node/interceptors/fetch/index.mjs:62:34
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@mswjs/interceptors/lib/node/interceptors/fetch/index.mjs:61:53
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@open-draft/until/lib/index.mjs:4:23

139 |   }
140 | };
141 | var FetchInterceptor = _FetchInterceptor;
142 | FetchInterceptor.symbol = Symbol("fetch");
143 | function createNetworkError(cause) {
144 |   return Object.assign(new TypeError("Failed to fetch"), {
                          ^
TypeError: Failed to fetch
      at createNetworkError (/Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@mswjs/interceptors/lib/node/interceptors/fetch/index.mjs:144:23)
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@mswjs/interceptors/lib/node/interceptors/fetch/index.mjs:84:30

The undefined is not a function is a console log of the error in the
/node_modules/@mswjs/interceptors/lib/node/interceptors/fetch/index.mjs:84:30

The error is inside the bun implementation of setMaxListeners.

If I comment that function the library works like a charm

@rhyzx
Copy link
Contributor

rhyzx commented Dec 19, 2023

Thanks to

@Nisgrak's tips

Hey, I try with a minimal repo like this

import { HttpResponse, http } from 'msw';
import { setupServer } from 'msw/node'

const handlers = [
    http.get("https://bun.sh", () => {
        return HttpResponse.json({ "test": "a" })
    })
]

export const server = setupServer(...handlers)

server.listen({
    onUnhandledRequest: "warn"
})

const response = await fetch("https://bun.sh");
const html = await response.text(); // HTML string

And give this error:

61 | }, getEventListeners = function(emitter, type) {
62 |   if (emitter instanceof EventTarget)
63 |     throwNotImplemented("getEventListeners with an EventTarget", 2678);
64 |   return emitter.listeners(type);
65 | 
66 | }, setMaxListeners = function(n, ...eventTargets) {
                                                             ^
TypeError: undefined is not a function
      at node:events:66:58
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/msw/lib/node/index.mjs:60:10
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/msw/lib/node/index.mjs:18:4
      at new Promise (:1:20)
      at __async (/Users/enekorg/Proyectos/test/test-bun-msw/node_modules/msw/lib/node/index.mjs:2:9)
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@mswjs/interceptors/lib/node/chunk-YQGTMMOZ.mjs:50:10
      at emitAsync (/Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@mswjs/interceptors/lib/node/chunk-YQGTMMOZ.mjs:44:25)
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@mswjs/interceptors/lib/node/interceptors/fetch/index.mjs:62:34
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@mswjs/interceptors/lib/node/interceptors/fetch/index.mjs:61:53
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@open-draft/until/lib/index.mjs:4:23

139 |   }
140 | };
141 | var FetchInterceptor = _FetchInterceptor;
142 | FetchInterceptor.symbol = Symbol("fetch");
143 | function createNetworkError(cause) {
144 |   return Object.assign(new TypeError("Failed to fetch"), {
                          ^
TypeError: Failed to fetch
      at createNetworkError (/Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@mswjs/interceptors/lib/node/interceptors/fetch/index.mjs:144:23)
      at /Users/enekorg/Proyectos/test/test-bun-msw/node_modules/@mswjs/interceptors/lib/node/interceptors/fetch/index.mjs:84:30

The undefined is not a function is a console log of the error in the /node_modules/@mswjs/interceptors/lib/node/interceptors/fetch/index.mjs:84:30

The error is inside the bun implementation of setMaxListeners.

If I comment that function the library works like a charm

Here is the workaround(currently in Bun v1.0.18):

import { expect, test, describe, mock } from "bun:test"
// fix
mock.module("events", () => {
  return {
    setMaxListeners: () => {},
  }
})

import { HttpResponse, http } from 'msw';
import { setupServer } from 'msw/node'

const handlers = [
    http.get("https://bun.sh", () => {
        return HttpResponse.json({ "test": "a" })
    })
]

export const server = setupServer(...handlers)

server.listen({
    onUnhandledRequest: "warn"
})

const response = await fetch("https://bun.sh");
const html = await response.text(); // HTML string

@joel-daros
Copy link

Does it only work with fetch()? I have the same example bellow, using Axios for the request, and it’s throwing an error:

import axios from "axios";
import { HttpResponse, http } from "msw";
import { setupServer } from "msw/node";

const axiosInstance = axios.create();

const handlers = [
  http.get("https://bun.sh", () => {
    return HttpResponse.json({ test: "a" });
  }),
];

export const server = setupServer(...handlers);

server.listen({
  onUnhandledRequest: "warn",
});

// this works fine
// const response = await fetch("https://bun.sh");
// const html = response.json();

// but with axios, it doesn't
const response = await axiosInstance.get("https://bun.sh");
const html = response.data;
bun test v1.0.26 (c75e768a)

src/Sample.test.ts:
432 |         total: totalResponseBodyLength
433 |       });
434 |     };
435 |     if (response.body) {
436 |       this.logger.info("mocked response has body, streaming...");
437 |       const reader = response.body.getReader();
                           ^
TypeError: response.body.getReader is not a function. (In 'response.body.getReader()', 'response.body.getReader' is undefined)
      at respondWith (/Users/joeldaros/corteva/msw-2-issue/node_modules/@mswjs/interceptors/lib/node/chunk-IZG42TWK.mjs:437:22)

@kishores05
Copy link

Does it only work with fetch()? I have the same example bellow, using Axios for the request, and it’s throwing an error:

import axios from "axios";
import { HttpResponse, http } from "msw";
import { setupServer } from "msw/node";

const axiosInstance = axios.create();

const handlers = [
  http.get("https://bun.sh", () => {
    return HttpResponse.json({ test: "a" });
  }),
];

export const server = setupServer(...handlers);

server.listen({
  onUnhandledRequest: "warn",
});

// this works fine
// const response = await fetch("https://bun.sh");
// const html = response.json();

// but with axios, it doesn't
const response = await axiosInstance.get("https://bun.sh");
const html = response.data;
bun test v1.0.26 (c75e768a)

src/Sample.test.ts:
432 |         total: totalResponseBodyLength
433 |       });
434 |     };
435 |     if (response.body) {
436 |       this.logger.info("mocked response has body, streaming...");
437 |       const reader = response.body.getReader();
                           ^
TypeError: response.body.getReader is not a function. (In 'response.body.getReader()', 'response.body.getReader' is undefined)
      at respondWith (/Users/joeldaros/corteva/msw-2-issue/node_modules/@mswjs/interceptors/lib/node/chunk-IZG42TWK.mjs:437:22)

I too facing the same issue. @joel-daros were you able to fix this issue?

@joel-daros
Copy link

too facing the same issue. @joel-daros were you able to fix this issue?

Nope. Maybe @kettanaito can help to identify if it’s a MSW or Bun issue.

@kishores05
Copy link

kishores05 commented Feb 14, 2024

too facing the same issue. @joel-daros were you able to fix this issue?

Nope. Maybe @kettanaito can help to identify if it’s a MSW or Bun issue.

Adding below snippet in jest configuration worked for me.

const { Blob, File } = require('node:buffer')
const { fetch, Headers, FormData, Request, Response } = require('undici')
 
Object.defineProperties(globalThis, {
  fetch: { value: fetch, writable: true },
  Blob: { value: Blob },
  File: { value: File },
  Headers: { value: Headers },
  FormData: { value: FormData },
  Request: { value: Request },
  Response: { value: Response },
})

@kettanaito
Copy link

@joel-daros, I can help indeed, with this issue any other issues: MSW works without problems in Node.js. Every exception you encounter while using it with Bun is Bun's issue related to Node.js compatibility (it can also be your misconfigured environment, like @kishores05 has pointed out, so first ensure MSW works in your environment).

@boldurean
Copy link

Is there any progress on this?

@kettanaito
Copy link

A heads-up on this, in the near future MSW will migrate to a new architecture for request interception in Node.js (see mswjs/interceptors#515). That architecture will rely on net.Socket and HttpAgent instead of http.ClientRequest. If anyone from the Bun team is looking into this issue, consider looking at the compatibility with net.Socket in Node.js as the first step.

The new algorithm also relies on HTTPParser which is not a public API in Node.js but is distributed regardless in the node:_http_common built-in module. Bun has to implement both that module and the HTTPParser class in order for MSW to work in Bun.

@nektro
Copy link
Member

nektro commented May 10, 2024

Does this still reproduce for you? using Bun 1.1.8 this works for me.

@xxleyi
Copy link
Author

xxleyi commented May 11, 2024

Does this still reproduce for you? using Bun 1.1.8 this works for me.

yeah, still reproduce for me. did you try with the code demo i provide?

update: with msw v2 lastest version(2.3.0), the code demo works, however, if i put a real handler, still has issues:

import axios from 'axios';
import { http, passthrough, HttpResponse } from 'msw'

import { setupServer } from 'msw/node'

const server = setupServer(...[
  http.get('https://swapi.dev/api/people/', () => {
    // return passthrough()
    return HttpResponse.json({ results: [{}, {}] })
  }),
])
server.listen({
  onUnhandledRequest: 'warn',
});

axios.get('https://swapi.dev/api/people/?page=2')
  .then(function (response) {
    // handle success
    console.log(response.data.results.length);
  })
  .catch(function (error) {
    // handle error
    console.log(error?.message);
  });

with output:

485 |       writableFinished: { value: true },
486 |       writableEnded: { value: true }
487 |     });
488 |     this.emit("finish");
489 |     const { status, statusText, headers, body } = mockedResponse;
490 |     this.response.statusCode = status;
          ^
TypeError: Attempted to assign to readonly property.

@silverAndroid
Copy link

A heads-up on this, in the near future MSW will migrate to a new architecture for request interception in Node.js (see mswjs/interceptors#515). That architecture will rely on net.Socket and HttpAgent instead of http.ClientRequest. If anyone from the Bun team is looking into this issue, consider looking at the compatibility with net.Socket in Node.js as the first step.

The new algorithm also relies on HTTPParser which is not a public API in Node.js but is distributed regardless in the node:_http_common built-in module. Bun has to implement both that module and the HTTPParser class in order for MSW to work in Bun.

Looks like msw 2.4.4 is now using the HTTPParser which Bun doesn't currently seem to support (I'm on 1.1.27). Is there a Bun version that does, or should this issue be reopened?

@nektro
Copy link
Member

nektro commented Sep 8, 2024

looks like #13072 is already open

@kettanaito
Copy link

Can confirm we've migrated to use built-in HTTP parser in Node.js since v2.4.4. If you wish to run MSW in Bun, Bun has to implement that parser. It may be tricky since it's technically an internal built-in _http_common.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working node.js Compatibility with Node.js APIs
Projects
None yet