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

Unable to be used alongside superagent #209

Closed
boblauer opened this issue Jun 11, 2020 · 16 comments · Fixed by #211
Closed

Unable to be used alongside superagent #209

boblauer opened this issue Jun 11, 2020 · 16 comments · Fixed by #211
Assignees
Labels
bug Something isn't working scope:node Related to MSW running in Node

Comments

@boblauer
Copy link

Describe the bug

I have an express server that makes calls to third party apis, and I would like to test my express server using supertest and mock the third party api requests using msw.

When starting the msw node server, all supertest requests fail with the following error:

TypeError [ERR_INVALID_URL]: Invalid URL: undefined//undefined
      at onParseError (internal/url.js:241:17)
      at new URL (internal/url.js:319:5)
      at new URL (internal/url.js:316:22)
      at normalizeHttpRequestParams (node_modules/node-request-interceptor/lib/http/ClientRequest/normalizeHttpRequestParams.js:29:57)
      at new ClientRequestOverride (node_modules/node-request-interceptor/lib/http/ClientRequest/ClientRequestOverride.js:65:74)
      at handleRequest (node_modules/node-request-interceptor/lib/http/override.js:28:12)
      at Object.requestOverride [as request] (node_modules/node-request-interceptor/lib/http/override.js:55:20)
      at Test.Request.request (node_modules/superagent/lib/node/index.js:622:31)
      at Test.Request.end (node_modules/superagent/lib/node/index.js:764:8)
      at Test.end (node_modules/supertest/lib/test.js:125:7)

Environment

  • msw: 0.19.0
  • nodejs: 10.16.3
  • npm: 6.9.0

To Reproduce

Steps to reproduce the behavior:

import express from 'express';
import request from 'supertest';
import { setupServer } from 'msw/node';

const app = express();
app.get('/', (req, res) => res.send('hello world'));

setupServer().listen();

request(app)
  .get('/')
  .then((res) => {
    console.log(res);
  });
@boblauer boblauer added the bug Something isn't working label Jun 11, 2020
@kettanaito
Copy link
Member

kettanaito commented Jun 11, 2020

Hey, @boblauer. Thanks for reaching out.

I can see that, although you're calling setupServer(), you are not passing any request handlers to that function. The way MSW works is that you pass what is called a request handler as arguments to the setupServer/setupWorker functions.

For example, if you wish to intercept a / request to your Node app, you should do:

import { rest } from 'msw'
import { setupServer } from 'msw'

setupServer(
  // The `rest.get` call is a request handler
  rest.get('http://localhost:8080/', (req, res, ctx) => {
    return res(ctx.json({ intercepted: 'sure' }))
  }
).listen()

Notice how I'm giving an absolute URL to rest.get(). That is because there cannot be any relative URLs in Node (nothing to be relative to).

If you're wondering how come app.get('/') works with a relative URL, it's actually not relative either. It's just in Express you establish a server on a specific address later on (via app.listen(), so Express router knows what are those routes are relative to).

MSW is agnostic of your server framework, so if you wish it to intercept a request to your app, provide an absolute URL that you wish to intercept. In the example above I've assumed that the app is established on localhost at the port 8080. Those values may be different in your case, so make sure to adjust them.

Also, from your original task I got the impression that you'd like to mock a third-party request, not the request to your app. Perhaps, you should consider:

setupServer(
  // Intercepting a third-party request
  rest.get('https://third-party.address/route', (req, res, ctx) => {...})
).listen()

// Request to a third-party server
request('https://third-party.address/route')

Let me know if that helps.

@boblauer
Copy link
Author

Thanks for the response. My example was more simplistic than my real world code.

In my real world code, I was intercepting the third-party request made from within my express controller, but what I don't want to intercept is the call to my express controller. So my code looked a lot like your last example, except my request to a third-party server is happening with an express controller.

However, regardless of what I pass to setupServer, when supertest tries to call my express controller, I get the error mentioned in the original post. I could try intercepting the call to localhost to see if that works, although as you know that's not really what I'm trying to achieve.

@kettanaito kettanaito added the scope:node Related to MSW running in Node label Jun 11, 2020
@kettanaito
Copy link
Member

I've set up a reproduction repository and was able to get the same issue.

Some of the Socket methods are missing on the Socket instance that is established by node-request-interceptor. I will tackle the issue there and propagate the fix to MSW.

@kettanaito
Copy link
Member

kettanaito commented Jun 12, 2020

Technical details

The issue is caused by the RequestOptions object supertest creates. Unlike regular NodeJS RequestOptions, the one from the mentioned library doesn't have the protocol and hostname property, which resulted into the derived url being undefined//undefined.

I'm going to provide a fix in the node-request-interceptor library (responsible for requests interception and mocking in Node) and propagate it to MSW.

@kettanaito
Copy link
Member

The fix is published in 0.19.2. Could you please update and let me know if it works for you? Thanks.

@bopfer
Copy link

bopfer commented Jul 5, 2020

I am having this issue in version 0.19.5 while trying to test my GraphQL app. The GraphQL resolver within the app makes an external API call to a 3rd party to get a list of websites. The mocking of that call using msw is working properly. But, when I combine it with supertest in my testing, I am getting a 500 response along with the undefined undefined error.

Here is my test file:

import supertest from 'supertest';
import { rest } from 'msw';
import { setupServer } from 'msw/node';

// app is my ApolloServer
import { app } from '../app';
import { getWebsites } from './gql';

const server = setupServer(
  rest.get(`https://the-third-party-api.com/created`, (_req, res, ctx) => {
    return res(ctx.json([`fake-site-1`, `fake-site-2`]));
  })
);

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

test(`Get all websites`, async () => {
  await supertest(app)
    .post(`/`)
    .send({ query: getWebsites })
    .then(({ error }) => {
      console.log(error);
      expect(error).toBeFalsy();
    });
});

Here is the output when I run the test:

  expect(received).toBeFalsy()

  Received: [Error: cannot undefined undefined (500)]

  console.log
    Error: cannot undefined undefined (500)
        at Response.toError (node_modules/superagent/lib/node/response.js:94:15)
        at ResponseBase._setStatusProperties (node_modules/superagent/lib/response-base.js:123:16)
        at new Response (node_modules/superagent/lib/node/response.js:41:8)
        at Test.Request._emitResponse (node_modules/superagent/lib/node/index.js:752:20)
        at IncomingMessage.<anonymous> (node_modules/superagent/lib/node/index.js:916:38)
        at IncomingMessage.emit (events.js:326:22)
        at endReadableNT (_stream_readable.js:1226:12)
        at processTicksAndRejections (internal/process/task_queues.js:80:21) {
      status: 500,
      text: 'POST body missing. Did you forget use body-parser middleware?',
      method: undefined,
      path: undefined
    }

If I remove the msw setupServer code, then the test passes.

Am I doing something wrong?

@kettanaito
Copy link
Member

Hey, @bopfer. Sorry to hear that. Could you please let me know the exact versions of these dependencies that you are using?

$ npm ls msw
$ npm ls node-request-interceptor

@bopfer
Copy link

bopfer commented Jul 5, 2020

@kettanaito
Copy link
Member

The packages seem to be up to date, thanks.

One thing I can notice is that you mock a rest.get() request, while in supertest you perform a .post() request. Could you please align those?

@bopfer
Copy link

bopfer commented Jul 5, 2020

The post is to my GraphQL app via supertest, which I do not want mocked. In there, the resolver code does a get to the 3rd party API. That's the part I would like mocked.

@kettanaito
Copy link
Member

kettanaito commented Jul 5, 2020

Something else must be at place then. Could you please set up a minimum reproduction repository for us to look? That'd be most useful.

@bopfer
Copy link

bopfer commented Jul 6, 2020

Will do! This is sort of a side project. So, it may take me a few days to put something together.

@bopfer
Copy link

bopfer commented Jul 6, 2020

Here is the reproduction repo: https://github.com/bopfer/msw-supertest-issues

I tried to make it as slim as possible to show the issue.

@manishiitg
Copy link

@kettanaito is there a fix for this issue and even i am getting the same problem.

@kettanaito
Copy link
Member

@manishiitg, hey. The fix for this issue has been provided. The issue you are experiencing may be similar, but not the same. Thanks for opening an issue regarding your problem, I'll look into that.

@kettanaito
Copy link
Member

@bopfer, thanks for making a reproduction repository, this is most useful!
I confirm that error you mention in the README is thrown during yarn test. Do you mind creating a separate issue for this?

I understand some of the issue also relate to supertest, but let's treat it this way: if there's a closed ticket then it most likely means it's been resolved. The issue you are experiencing may be similar (even identical), but technically may have a completely different reason. I'd appreciate if those issues are filed separately, that would make them easier to navigate for future generations. Thanks.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working scope:node Related to MSW running in Node
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants