msw icon indicating copy to clipboard operation
msw copied to clipboard

Using a mocked request multiple times in a test suite fails

Open charklewis opened this issue 2 years ago • 12 comments

Environment

Name Version
msw 0.38.2
node 16.14.0
OS macOS 12.2.1

Request handlers


import { graphql } from "msw"
import { loader, action } from "~/routes/sign-in"

const createSignInHandler = ({ acceptRegistrations = true } = {}) => {
  return graphql.query("SignIn", (req, res, ctx) => {
    return res(ctx.data({ newUser: { acceptRegistrations } }))
  })
}

const server = setupServer(createSignInHandler())
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())


test("the loader returns data to determine if we are accepting registrations", async () => {
  const response = await loader({ request: new Request("/sign-in", { method: "GET" }), params: {}, context: {} })
  const data = await response.json()
  expect(data.acceptRegistrations).toBe(true)
})

Actual request


import { GraphQLClient,  gql as graphql } from "graphql-request"
import { SignInDocument, SignInQuery } from "~/graphql"

const client = new GraphQLClient(`${process.env.GRAPHQL_ENDPOINT}`, {})
const fetchQuery = async (query, variables, user, secret) => {
  try {
    const response = await client.request(query, variables || {}, getHeaders(user, secret))
   return response
  } catch (error: any) {
    return {}
  }
}

const loader = async () => {
  graphql`
    query SignIn {
      newUser {
        acceptRegistrations
      }
    }
  `
  const { newUser } = await fetchQuery<SignInQuery>(SignInDocument)
  return json({ acceptRegistrations: newUser?.acceptRegistrations || false })
}

Current behavior

If I have less than 4 tests in one suite the test pass. If I have four or more than I get the following error.

UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason ""

I have ensure MSW is causing the error by hard coding the response and tested that the tests never fail (no matter how many tests are in the suite). If I have 3 tests or less the tests always pass, its only when four or more are added it fails.

I am using vitest (v0.5.9) with the following config:

/// <reference types="vitest" />
/// <reference types="vite/client" />

import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import tsconfigPaths from "vite-tsconfig-paths"

export default defineConfig({
  plugins: [react(), tsconfigPaths()],
  test: {
    globals: true,
    environment: "jsdom",
    setupFiles: "./app/test-utils.ts",
    testTimeout: 20000
  }
})

Expected behavior

I can have as many tests as possible in a test suite.

charklewis avatar Mar 05 '22 03:03 charklewis

I have been able to reproduce very similar behaviour by using the example in this demo on vitest repo - https://github.com/vitest-dev/vitest/tree/main/examples/react-testing-lib-msw.

If you duplicate the GraphQL test and run the tests, the second one fails. If you change what the duplicated test is doing, but ensure the mocked request is used, the second one will still fail.

Is it expected to only be able to use a mocked request once per test suite?

charklewis avatar Mar 05 '22 04:03 charklewis

Hey, @charklewis.

First things first, the exception you're getting is a sign that one of the assertions in your tests is not written correctly (does not await asynchronous code, doesn't handle its rejections). I highly advise you to double-check all your relevant tests.

The common cause for this kind of behavior is enabled caching during tests. Could you look into your GraphQLClient and make sure you disable/reset the cache for tests? You never want to cache when testing. Let me know if it helps.

The test you've mentioned features exactly the issue I'm talking about: the app is using @apollo/client but does not reset its cache after each test here. This will lead to Apollo using cached responses across tests, producing false positive/negatives.

kettanaito avatar Mar 06 '22 17:03 kettanaito

@kettanaito thanks for the reply, hope you had a great weekend! I'll review my tests again to see if there is any async calls that are not being met. If I copy that test I mentioned above more than 3 times it'll error, anything less it passes. I had a look at it appears the library I am using has no cache. This week I'll create an example sandbox and comment it here.

charklewis avatar Mar 06 '22 23:03 charklewis

@kettanaito I have created a very basic sample repo. It uses the basic-example from the Remix team and only contains one test that tests the loader for the index route.

To start the tests, install the packages first (yarn) then run yarn test. You will see in this file index.test.ts I have commented out the duplicate tests. If you uncomment them (I think even just one) the test suite starts to fail.

Hopefully there is a bug in my code somewhere that is causing this weird behaviour. The team for graphql-request have said there is no cache in their client.

If there is anything else I can do to help test/troubleshoot please let me know 🙂

Note: The GraphQL endpoint and query used in the repo are fake so trying to run the app without MSW won't work.

charklewis avatar Mar 11 '22 01:03 charklewis

@kettanaito I have done some more testing and it seems like the behaviour is a little haphazard. I have used the example repo I posted before, and running two duplicate tests. Below is a video of my re-running the test over and over, you can see it randomly failing and succeeding. As you'll see in the code, its a really basic, no frills set up so I am unsure what is going wrong.

If there is any further experiments or troubleshooting you would like me to perform, please let me know. What I might try next is running the graphql query without it going through the loader to see if the loader from remix is causing the issue.

https://user-images.githubusercontent.com/11977093/158714784-b65f8be9-a122-48e3-855a-f6016bf00a30.mov

charklewis avatar Mar 17 '22 00:03 charklewis

I feel this is an issue with the graphql-request library. If I replace the client.request and use the typical fetch the requests never fail. I noticed they are using cross-fetch to perform the requests, but when I tried using that library it also worked without a blip. So there is something in their configuration / library that is causing the bug. I have raised an issue on their repo - https://github.com/prisma-labs/graphql-request/issues/332.

charklewis avatar Mar 17 '22 08:03 charklewis

After some more testing today, the issue only persists when I use request from graphql-request. It does not appear when I use fetch, cross-fetch or node-fetch.

charklewis avatar Mar 18 '22 06:03 charklewis

Thanks for such a detailed investigation, @charklewis!

We need to look into what kind of request graphql-request makes when the test fails. We can do so by using DEBUG:

DEBUG=http* yarn test

In the case of test failure, there will be something to guide us.

It can still be an issue with how the constructed request gets handled in MSW.

kettanaito avatar Mar 18 '22 11:03 kettanaito

I am not too sure what is the helpful bit of info in this log, but this is the output of a failing test using request-graphql.

2022-03-18T21:29:04.278Z http override patching the "http" module...
2022-03-18T21:29:04.278Z http override patching the "https" module...
2022-03-18T21:29:04.281Z http.request request call (protocol "http"): [
  {
    protocol: 'http:',
    slashes: true,
    auth: null,
    host: 'some-graphql-api.com',
    port: null,
    hostname: 'some-graphql-api.com',
    hash: null,
    search: null,
    query: null,
    pathname: '/api/graphql',
    path: '/api/graphql',
    href: 'http://some-graphql-api.com/api/graphql',
    method: 'POST',
    headers: [Object: null prototype] {
      'Content-Type': [Array],
      Accept: [Array],
      'Content-Length': [Array],
      'User-Agent': [Array],
      'Accept-Encoding': [Array],
      Connection: [Array]
    },
    agent: undefined
  }
]
2022-03-18T21:29:04.282Z http normalizeClientRequestArgs arguments [
  {
    protocol: 'http:',
    slashes: true,
    auth: null,
    host: 'some-graphql-api.com',
    port: null,
    hostname: 'some-graphql-api.com',
    hash: null,
    search: null,
    query: null,
    pathname: '/api/graphql',
    path: '/api/graphql',
    href: 'http://some-graphql-api.com/api/graphql',
    method: 'POST',
    headers: [Object: null prototype] {
      'Content-Type': [Array],
      Accept: [Array],
      'Content-Length': [Array],
      'User-Agent': [Array],
      'Accept-Encoding': [Array],
      Connection: [Array]
    },
    agent: undefined
  }
]
2022-03-18T21:29:04.282Z http normalizeClientRequestArgs using default protocol: http:
2022-03-18T21:29:04.282Z http normalizeClientRequestArgs first argument is RequestOptions: {
  protocol: 'http:',
  slashes: true,
  auth: null,
  host: 'some-graphql-api.com',
  port: null,
  hostname: 'some-graphql-api.com',
  hash: null,
  search: null,
  query: null,
  pathname: '/api/graphql',
  path: '/api/graphql',
  href: 'http://some-graphql-api.com/api/graphql',
  method: 'POST',
  headers: [Object: null prototype] {
    'Content-Type': [ 'application/json' ],
    Accept: [ '*/*' ],
    'Content-Length': [ '113' ],
    'User-Agent': [ 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)' ],
    'Accept-Encoding': [ 'gzip,deflate' ],
    Connection: [ 'close' ]
  },
  agent: undefined
}
2022-03-18T21:29:04.282Z http normalizeClientRequestArgs normalized request options: {
  protocol: 'http:',
  slashes: true,
  auth: null,
  host: 'some-graphql-api.com',
  port: null,
  hostname: 'some-graphql-api.com',
  hash: null,
  search: null,
  query: null,
  pathname: '/api/graphql',
  path: '/api/graphql',
  href: 'http://some-graphql-api.com/api/graphql',
  method: 'POST',
  headers: [Object: null prototype] {
    'Content-Type': [ 'application/json' ],
    Accept: [ '*/*' ],
    'Content-Length': [ '113' ],
    'User-Agent': [ 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)' ],
    'Accept-Encoding': [ 'gzip,deflate' ],
    Connection: [ 'close' ]
  },
  agent: undefined
}
2022-03-18T21:29:04.282Z http normalizeClientRequestArgs created a URL from RequestOptions: http://some-graphql-api.com/api/graphql
2022-03-18T21:29:04.282Z http normalizeClientRequestArgs resolved fallback agent: Agent {
  _events: [Object: null prototype] {
    free: [Function (anonymous)],
    newListener: [Function: maybeEnableKeylog]
  },
  _eventsCount: 2,
  _maxListeners: undefined,
  defaultPort: 80,
  protocol: 'http:',
  options: [Object: null prototype] { path: null },
  requests: [Object: null prototype] {},
  sockets: [Object: null prototype] {},
  freeSockets: [Object: null prototype] {},
  keepAliveMsecs: 1000,
  keepAlive: false,
  maxSockets: Infinity,
  maxFreeSockets: 256,
  scheduling: 'lifo',
  maxTotalSockets: Infinity,
  totalSocketCount: 0,
  [Symbol(kCapture)]: false
}
2022-03-18T21:29:04.283Z http normalizeClientRequestArgs has no default agent, setting the default agent for "http:"
2022-03-18T21:29:04.283Z http normalizeClientRequestArgs successfully resolved url: http://some-graphql-api.com/api/graphql
2022-03-18T21:29:04.283Z http normalizeClientRequestArgs successfully resolved options: {
  protocol: 'http:',
  slashes: true,
  auth: null,
  host: 'some-graphql-api.com',
  port: null,
  hostname: 'some-graphql-api.com',
  hash: null,
  search: null,
  query: null,
  pathname: '/api/graphql',
  path: '/api/graphql',
  href: 'http://some-graphql-api.com/api/graphql',
  method: 'POST',
  headers: [Object: null prototype] {
    'Content-Type': [ 'application/json' ],
    Accept: [ '*/*' ],
    'Content-Length': [ '113' ],
    'User-Agent': [ 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)' ],
    'Accept-Encoding': [ 'gzip,deflate' ],
    Connection: [ 'close' ]
  },
  agent: Agent {
    _events: [Object: null prototype] {
      free: [Function (anonymous)],
      newListener: [Function: maybeEnableKeylog]
    },
    _eventsCount: 2,
    _maxListeners: undefined,
    defaultPort: 80,
    protocol: 'http:',
    options: [Object: null prototype] { path: null },
    requests: [Object: null prototype] {},
    sockets: [Object: null prototype] {},
    freeSockets: [Object: null prototype] {},
    keepAliveMsecs: 1000,
    keepAlive: false,
    maxSockets: Infinity,
    maxFreeSockets: 256,
    scheduling: 'lifo',
    maxTotalSockets: Infinity,
    totalSocketCount: 0,
    [Symbol(kCapture)]: false
  },
  _defaultAgent: Agent {
    _events: [Object: null prototype] {
      free: [Function (anonymous)],
      newListener: [Function: maybeEnableKeylog]
    },
    _eventsCount: 2,
    _maxListeners: undefined,
    defaultPort: 80,
    protocol: 'http:',
    options: [Object: null prototype] { path: null },
    requests: [Object: null prototype] {},
    sockets: [Object: null prototype] {},
    freeSockets: [Object: null prototype] {},
    keepAliveMsecs: 1000,
    keepAlive: false,
    maxSockets: Infinity,
    maxFreeSockets: 256,
    scheduling: 'lifo',
    maxTotalSockets: Infinity,
    totalSocketCount: 0,
    [Symbol(kCapture)]: false
  }
}
2022-03-18T21:29:04.283Z http normalizeClientRequestArgs successfully resolved callback: undefined
2022-03-18T21:29:04.286Z http POST http://some-graphql-api.com/api/graphql constructing ClientRequest... {
  url: URL {
    href: 'http://some-graphql-api.com/api/graphql',
    origin: 'http://some-graphql-api.com',
    protocol: 'http:',
    username: '',
    password: '',
    host: 'some-graphql-api.com',
    hostname: 'some-graphql-api.com',
    port: '',
    pathname: '/api/graphql',
    search: '',
    searchParams: URLSearchParams {},
    hash: ''
  },
  requestOptions: {
    protocol: 'http:',
    slashes: true,
    auth: null,
    host: 'some-graphql-api.com',
    port: null,
    hostname: 'some-graphql-api.com',
    hash: null,
    search: null,
    query: null,
    pathname: '/api/graphql',
    path: '/api/graphql',
    href: 'http://some-graphql-api.com/api/graphql',
    method: 'POST',
    headers: [Object: null prototype] {
      'Content-Type': [Array],
      Accept: [Array],
      'Content-Length': [Array],
      'User-Agent': [Array],
      'Accept-Encoding': [Array],
      Connection: [Array]
    },
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 80,
      protocol: 'http:',
      options: [Object: null prototype],
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype],
      freeSockets: [Object: null prototype] {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 1,
      [Symbol(kCapture)]: false
    },
    _defaultAgent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 80,
      protocol: 'http:',
      options: [Object: null prototype],
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype] {},
      freeSockets: [Object: null prototype] {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 0,
      [Symbol(kCapture)]: false
    }
  },
  callback: undefined
}
2022-03-18T21:29:04.286Z http normalizeWriteArgs normalizing ClientRequest.write arguments... [
  <Buffer 7b 22 71 75 65 72 79 22 3a 22 5c 6e 20 20 20 20 71 75 65 72 79 20 4d 79 51 75 65 72 79 20 7b 5c 6e 20 20 20 20 20 20 64 65 6d 6f 51 75 65 72 79 20 7b ... 63 more bytes>
]
2022-03-18T21:29:04.287Z http normalizeWriteArgs successfully normalized ClientRequest.write arguments: [
  <Buffer 7b 22 71 75 65 72 79 22 3a 22 5c 6e 20 20 20 20 71 75 65 72 79 20 4d 79 51 75 65 72 79 20 7b 5c 6e 20 20 20 20 20 20 64 65 6d 6f 51 75 65 72 79 20 7b ... 63 more bytes>,
  undefined,
  undefined
]
2022-03-18T21:29:04.287Z http POST http://some-graphql-api.com/api/graphql write: {
  chunk: <Buffer 7b 22 71 75 65 72 79 22 3a 22 5c 6e 20 20 20 20 71 75 65 72 79 20 4d 79 51 75 65 72 79 20 7b 5c 6e 20 20 20 20 20 20 64 65 6d 6f 51 75 65 72 79 20 7b ... 63 more bytes>,
  encoding: undefined,
  callback: undefined
}
2022-03-18T21:29:04.287Z http POST http://some-graphql-api.com/api/graphql chunk successfully stored!
2022-03-18T21:29:04.287Z http POST http://some-graphql-api.com/api/graphql end []
2022-03-18T21:29:04.287Z http normalizeClientRequestEndArgs arguments []
2022-03-18T21:29:04.287Z http normalizeClientRequestEndArgs normalized args [ null, null, null ]
2022-03-18T21:29:04.287Z http POST http://some-graphql-api.com/api/graphql normalized arguments: { chunk: null, encoding: null, callback: null }
2022-03-18T21:29:04.287Z http POST http://some-graphql-api.com/api/graphql written request body: {"query":"\n    query MyQuery {\n      demoQuery {\n        value\n      }\n    }\n  ","operationName":"MyQuery"}
2022-03-18T21:29:04.287Z http POST http://some-graphql-api.com/api/graphql resolved request body: {"query":"\n    query MyQuery {\n      demoQuery {\n        value\n      }\n    }\n  ","operationName":"MyQuery"}
2022-03-18T21:29:04.287Z http POST http://some-graphql-api.com/api/graphql creating isomorphic request object...
2022-03-18T21:29:04.287Z http POST http://some-graphql-api.com/api/graphql request outgoing headers: [Object: null prototype] {
  'content-type': [ 'application/json' ],
  accept: [ '*/*' ],
  'content-length': [ '113' ],
  'user-agent': [ 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)' ],
  'accept-encoding': [ 'gzip,deflate' ],
  connection: [ 'close' ],
  host: 'some-graphql-api.com'
}
2022-03-18T21:29:04.287Z http POST http://some-graphql-api.com/api/graphql successfully created isomorphic request! {
  id: '5da4f479-8b70-4854-86e8-50dcfd4e29f1',
  url: URL {
    href: 'http://some-graphql-api.com/api/graphql',
    origin: 'http://some-graphql-api.com',
    protocol: 'http:',
    username: '',
    password: '',
    host: 'some-graphql-api.com',
    hostname: 'some-graphql-api.com',
    port: '',
    pathname: '/api/graphql',
    search: '',
    searchParams: URLSearchParams {},
    hash: ''
  },
  method: 'POST',
  credentials: 'omit',
  headers: HeadersPolyfill {
    _headers: {
      'content-type': 'application/json',
      accept: '*/*',
      'content-length': '113',
      'user-agent': 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)',
      'accept-encoding': 'gzip,deflate',
      connection: 'close',
      host: 'some-graphql-api.com'
    },
    _names: Map(7) {
      'content-type' => 'content-type',
      'accept' => 'accept',
      'content-length' => 'content-length',
      'user-agent' => 'user-agent',
      'accept-encoding' => 'accept-encoding',
      'connection' => 'connection',
      'host' => 'host'
    }
  },
  body: '{"query":"\\n    query MyQuery {\\n      demoQuery {\\n        value\\n      }\\n    }\\n  ","operationName":"MyQuery"}'
}
2022-03-18T21:29:04.288Z http POST http://some-graphql-api.com/api/graphql executing response resolver...
2022-03-18T21:29:04.291Z http POST http://some-graphql-api.com/api/graphql event:socket

charklewis avatar Mar 18 '22 21:03 charklewis

And if it helps, this is the output of when a test passes.

2022-03-18T21:28:56.817Z http override patching the "http" module...
2022-03-18T21:28:56.817Z http override patching the "https" module...
2022-03-18T21:28:56.821Z http.request request call (protocol "http"): [
  {
    protocol: 'http:',
    slashes: true,
    auth: null,
    host: 'some-graphql-api.com',
    port: null,
    hostname: 'some-graphql-api.com',
    hash: null,
    search: null,
    query: null,
    pathname: '/api/graphql',
    path: '/api/graphql',
    href: 'http://some-graphql-api.com/api/graphql',
    method: 'POST',
    headers: [Object: null prototype] {
      'Content-Type': [Array],
      Accept: [Array],
      'Content-Length': [Array],
      'User-Agent': [Array],
      'Accept-Encoding': [Array],
      Connection: [Array]
    },
    agent: undefined
  }
]
2022-03-18T21:28:56.822Z http normalizeClientRequestArgs arguments [
  {
    protocol: 'http:',
    slashes: true,
    auth: null,
    host: 'some-graphql-api.com',
    port: null,
    hostname: 'some-graphql-api.com',
    hash: null,
    search: null,
    query: null,
    pathname: '/api/graphql',
    path: '/api/graphql',
    href: 'http://some-graphql-api.com/api/graphql',
    method: 'POST',
    headers: [Object: null prototype] {
      'Content-Type': [Array],
      Accept: [Array],
      'Content-Length': [Array],
      'User-Agent': [Array],
      'Accept-Encoding': [Array],
      Connection: [Array]
    },
    agent: undefined
  }
]
2022-03-18T21:28:56.822Z http normalizeClientRequestArgs using default protocol: http:
2022-03-18T21:28:56.822Z http normalizeClientRequestArgs first argument is RequestOptions: {
  protocol: 'http:',
  slashes: true,
  auth: null,
  host: 'some-graphql-api.com',
  port: null,
  hostname: 'some-graphql-api.com',
  hash: null,
  search: null,
  query: null,
  pathname: '/api/graphql',
  path: '/api/graphql',
  href: 'http://some-graphql-api.com/api/graphql',
  method: 'POST',
  headers: [Object: null prototype] {
    'Content-Type': [ 'application/json' ],
    Accept: [ '*/*' ],
    'Content-Length': [ '113' ],
    'User-Agent': [ 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)' ],
    'Accept-Encoding': [ 'gzip,deflate' ],
    Connection: [ 'close' ]
  },
  agent: undefined
}
2022-03-18T21:28:56.822Z http normalizeClientRequestArgs normalized request options: {
  protocol: 'http:',
  slashes: true,
  auth: null,
  host: 'some-graphql-api.com',
  port: null,
  hostname: 'some-graphql-api.com',
  hash: null,
  search: null,
  query: null,
  pathname: '/api/graphql',
  path: '/api/graphql',
  href: 'http://some-graphql-api.com/api/graphql',
  method: 'POST',
  headers: [Object: null prototype] {
    'Content-Type': [ 'application/json' ],
    Accept: [ '*/*' ],
    'Content-Length': [ '113' ],
    'User-Agent': [ 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)' ],
    'Accept-Encoding': [ 'gzip,deflate' ],
    Connection: [ 'close' ]
  },
  agent: undefined
}
2022-03-18T21:28:56.822Z http normalizeClientRequestArgs created a URL from RequestOptions: http://some-graphql-api.com/api/graphql
2022-03-18T21:28:56.822Z http normalizeClientRequestArgs resolved fallback agent: Agent {
  _events: [Object: null prototype] {
    free: [Function (anonymous)],
    newListener: [Function: maybeEnableKeylog]
  },
  _eventsCount: 2,
  _maxListeners: undefined,
  defaultPort: 80,
  protocol: 'http:',
  options: [Object: null prototype] { path: null },
  requests: [Object: null prototype] {},
  sockets: [Object: null prototype] {},
  freeSockets: [Object: null prototype] {},
  keepAliveMsecs: 1000,
  keepAlive: false,
  maxSockets: Infinity,
  maxFreeSockets: 256,
  scheduling: 'lifo',
  maxTotalSockets: Infinity,
  totalSocketCount: 0,
  [Symbol(kCapture)]: false
}
2022-03-18T21:28:56.823Z http normalizeClientRequestArgs has no default agent, setting the default agent for "http:"
2022-03-18T21:28:56.823Z http normalizeClientRequestArgs successfully resolved url: http://some-graphql-api.com/api/graphql
2022-03-18T21:28:56.823Z http normalizeClientRequestArgs successfully resolved options: {
  protocol: 'http:',
  slashes: true,
  auth: null,
  host: 'some-graphql-api.com',
  port: null,
  hostname: 'some-graphql-api.com',
  hash: null,
  search: null,
  query: null,
  pathname: '/api/graphql',
  path: '/api/graphql',
  href: 'http://some-graphql-api.com/api/graphql',
  method: 'POST',
  headers: [Object: null prototype] {
    'Content-Type': [ 'application/json' ],
    Accept: [ '*/*' ],
    'Content-Length': [ '113' ],
    'User-Agent': [ 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)' ],
    'Accept-Encoding': [ 'gzip,deflate' ],
    Connection: [ 'close' ]
  },
  agent: Agent {
    _events: [Object: null prototype] {
      free: [Function (anonymous)],
      newListener: [Function: maybeEnableKeylog]
    },
    _eventsCount: 2,
    _maxListeners: undefined,
    defaultPort: 80,
    protocol: 'http:',
    options: [Object: null prototype] { path: null },
    requests: [Object: null prototype] {},
    sockets: [Object: null prototype] {},
    freeSockets: [Object: null prototype] {},
    keepAliveMsecs: 1000,
    keepAlive: false,
    maxSockets: Infinity,
    maxFreeSockets: 256,
    scheduling: 'lifo',
    maxTotalSockets: Infinity,
    totalSocketCount: 0,
    [Symbol(kCapture)]: false
  },
  _defaultAgent: Agent {
    _events: [Object: null prototype] {
      free: [Function (anonymous)],
      newListener: [Function: maybeEnableKeylog]
    },
    _eventsCount: 2,
    _maxListeners: undefined,
    defaultPort: 80,
    protocol: 'http:',
    options: [Object: null prototype] { path: null },
    requests: [Object: null prototype] {},
    sockets: [Object: null prototype] {},
    freeSockets: [Object: null prototype] {},
    keepAliveMsecs: 1000,
    keepAlive: false,
    maxSockets: Infinity,
    maxFreeSockets: 256,
    scheduling: 'lifo',
    maxTotalSockets: Infinity,
    totalSocketCount: 0,
    [Symbol(kCapture)]: false
  }
}
2022-03-18T21:28:56.823Z http normalizeClientRequestArgs successfully resolved callback: undefined
2022-03-18T21:28:56.826Z http POST http://some-graphql-api.com/api/graphql constructing ClientRequest... {
  url: URL {
    href: 'http://some-graphql-api.com/api/graphql',
    origin: 'http://some-graphql-api.com',
    protocol: 'http:',
    username: '',
    password: '',
    host: 'some-graphql-api.com',
    hostname: 'some-graphql-api.com',
    port: '',
    pathname: '/api/graphql',
    search: '',
    searchParams: URLSearchParams {},
    hash: ''
  },
  requestOptions: {
    protocol: 'http:',
    slashes: true,
    auth: null,
    host: 'some-graphql-api.com',
    port: null,
    hostname: 'some-graphql-api.com',
    hash: null,
    search: null,
    query: null,
    pathname: '/api/graphql',
    path: '/api/graphql',
    href: 'http://some-graphql-api.com/api/graphql',
    method: 'POST',
    headers: [Object: null prototype] {
      'Content-Type': [Array],
      Accept: [Array],
      'Content-Length': [Array],
      'User-Agent': [Array],
      'Accept-Encoding': [Array],
      Connection: [Array]
    },
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 80,
      protocol: 'http:',
      options: [Object: null prototype],
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype],
      freeSockets: [Object: null prototype] {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 1,
      [Symbol(kCapture)]: false
    },
    _defaultAgent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 80,
      protocol: 'http:',
      options: [Object: null prototype],
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype] {},
      freeSockets: [Object: null prototype] {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 0,
      [Symbol(kCapture)]: false
    }
  },
  callback: undefined
}
2022-03-18T21:28:56.826Z http normalizeWriteArgs normalizing ClientRequest.write arguments... [
  <Buffer 7b 22 71 75 65 72 79 22 3a 22 5c 6e 20 20 20 20 71 75 65 72 79 20 4d 79 51 75 65 72 79 20 7b 5c 6e 20 20 20 20 20 20 64 65 6d 6f 51 75 65 72 79 20 7b ... 63 more bytes>
]
2022-03-18T21:28:56.826Z http normalizeWriteArgs successfully normalized ClientRequest.write arguments: [
  <Buffer 7b 22 71 75 65 72 79 22 3a 22 5c 6e 20 20 20 20 71 75 65 72 79 20 4d 79 51 75 65 72 79 20 7b 5c 6e 20 20 20 20 20 20 64 65 6d 6f 51 75 65 72 79 20 7b ... 63 more bytes>,
  undefined,
  undefined
]
2022-03-18T21:28:56.826Z http POST http://some-graphql-api.com/api/graphql write: {
  chunk: <Buffer 7b 22 71 75 65 72 79 22 3a 22 5c 6e 20 20 20 20 71 75 65 72 79 20 4d 79 51 75 65 72 79 20 7b 5c 6e 20 20 20 20 20 20 64 65 6d 6f 51 75 65 72 79 20 7b ... 63 more bytes>,
  encoding: undefined,
  callback: undefined
}
2022-03-18T21:28:56.827Z http POST http://some-graphql-api.com/api/graphql chunk successfully stored!
2022-03-18T21:28:56.827Z http POST http://some-graphql-api.com/api/graphql end []
2022-03-18T21:28:56.827Z http normalizeClientRequestEndArgs arguments []
2022-03-18T21:28:56.827Z http normalizeClientRequestEndArgs normalized args [ null, null, null ]
2022-03-18T21:28:56.827Z http POST http://some-graphql-api.com/api/graphql normalized arguments: { chunk: null, encoding: null, callback: null }
2022-03-18T21:28:56.827Z http POST http://some-graphql-api.com/api/graphql written request body: {"query":"\n    query MyQuery {\n      demoQuery {\n        value\n      }\n    }\n  ","operationName":"MyQuery"}
2022-03-18T21:28:56.827Z http POST http://some-graphql-api.com/api/graphql resolved request body: {"query":"\n    query MyQuery {\n      demoQuery {\n        value\n      }\n    }\n  ","operationName":"MyQuery"}
2022-03-18T21:28:56.827Z http POST http://some-graphql-api.com/api/graphql creating isomorphic request object...
2022-03-18T21:28:56.827Z http POST http://some-graphql-api.com/api/graphql request outgoing headers: [Object: null prototype] {
  'content-type': [ 'application/json' ],
  accept: [ '*/*' ],
  'content-length': [ '113' ],
  'user-agent': [ 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)' ],
  'accept-encoding': [ 'gzip,deflate' ],
  connection: [ 'close' ],
  host: 'some-graphql-api.com'
}
2022-03-18T21:28:56.827Z http POST http://some-graphql-api.com/api/graphql successfully created isomorphic request! {
  id: '5e66b743-5cd7-4b64-bd3d-912e438da0bf',
  url: URL {
    href: 'http://some-graphql-api.com/api/graphql',
    origin: 'http://some-graphql-api.com',
    protocol: 'http:',
    username: '',
    password: '',
    host: 'some-graphql-api.com',
    hostname: 'some-graphql-api.com',
    port: '',
    pathname: '/api/graphql',
    search: '',
    searchParams: URLSearchParams {},
    hash: ''
  },
  method: 'POST',
  credentials: 'omit',
  headers: HeadersPolyfill {
    _headers: {
      'content-type': 'application/json',
      accept: '*/*',
      'content-length': '113',
      'user-agent': 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)',
      'accept-encoding': 'gzip,deflate',
      connection: 'close',
      host: 'some-graphql-api.com'
    },
    _names: Map(7) {
      'content-type' => 'content-type',
      'accept' => 'accept',
      'content-length' => 'content-length',
      'user-agent' => 'user-agent',
      'accept-encoding' => 'accept-encoding',
      'connection' => 'connection',
      'host' => 'host'
    }
  },
  body: '{"query":"\\n    query MyQuery {\\n      demoQuery {\\n        value\\n      }\\n    }\\n  ","operationName":"MyQuery"}'
}
2022-03-18T21:28:56.827Z http POST http://some-graphql-api.com/api/graphql executing response resolver...
2022-03-18T21:28:56.831Z http POST http://some-graphql-api.com/api/graphql event:socket
2022-03-18T21:28:56.832Z http POST http://some-graphql-api.com/api/graphql received mocked response: {
  status: 200,
  statusText: 'OK',
  headers: { 'x-powered-by': 'msw', 'content-type': 'application/json' },
  body: '{"data":{"demoQuery":{"value":true}}}'
}
2022-03-18T21:28:56.832Z http POST http://some-graphql-api.com/api/graphql responding with a mocked response... {
  status: 200,
  statusText: 'OK',
  headers: { 'x-powered-by': 'msw', 'content-type': 'application/json' },
  body: '{"data":{"demoQuery":{"value":true}}}'
}
2022-03-18T21:28:56.833Z http POST http://some-graphql-api.com/api/graphql mocked response headers ready: { 'x-powered-by': 'msw', 'content-type': 'application/json' }
2022-03-18T21:28:56.833Z http POST http://some-graphql-api.com/api/graphql event:finish
2022-03-18T21:28:56.833Z http POST http://some-graphql-api.com/api/graphql event:response
2022-03-18T21:28:56.833Z http POST http://some-graphql-api.com/api/graphql found "response" event, cloning the response...
2022-03-18T21:28:56.833Z http POST http://some-graphql-api.com/api/graphql event:response-internal
2022-03-18T21:28:56.833Z http POST http://some-graphql-api.com/api/graphql response successfuly cloned, emitting "response" event...
2022-03-18T21:28:56.834Z http POST http://some-graphql-api.com/api/graphql 200 OK {"data":{"demoQuery":{"value":true}}} (MOCKED)
2022-03-18T21:28:56.834Z http POST http://some-graphql-api.com/api/graphql emitting the custom "response" event...
2022-03-18T21:28:56.835Z http override done, restoring modules...
2022-03-18T21:28:56.836Z http POST http://some-graphql-api.com/api/graphql event:close

charklewis avatar Mar 18 '22 21:03 charklewis

@kettanaito I will have some time this w/e if you would like me to do anymore specific testing.

charklewis avatar Mar 24 '22 06:03 charklewis

Think this could be an issue that we're also running in with slightly different output. We have a test suite over at https://github.com/backstage/backstage that when upgrading from 0.35 -> 0.39.2 fails on some weird tests, with some DNS failures like it's not registering the mock that is in the beforeAll? https://github.com/backstage/backstage/pull/10589

Some tests pass, but then others that re-use the existing mock seem to fail.

Also strangely, these tests only seem to fail in CI, and not on our super M1 macbooks, so we're thinking that this could be something to do with race conditions and it being slower in CI, but that's just a suspicion.

benjdlambert avatar Apr 08 '22 13:04 benjdlambert

Any updates on this? Have someone found a workaround for this issue? I'm also using graphql-request

frixaco avatar Sep 26 '23 08:09 frixaco

Can someone please confirm this is still an issue on MSW 1.x? There's been a number of improvements shipped since this issue has been originally reported.

I also highly recommend check it on msw@next, which is a significant rewrite of the library. I've got tests with graphql-request that are passing without issues but perhaps there are use cases I haven't thought of. Help would be appreciated here.

kettanaito avatar Sep 26 '23 09:09 kettanaito

Hey @kettanaito!

I don't see this problem anymore. I tried to create reproduction using [email protected], [email protected], [email protected] and [email protected], but everything worked fine without errors, msw was correctly mocking graphql-request requests. So I updated those packages and issue dissappeared from my project.

frixaco avatar Sep 26 '23 13:09 frixaco

Hey @kettanaito!

I don't see this problem anymore. I tried to create reproduction using [email protected], [email protected], [email protected] and [email protected], but everything worked fine without errors, msw was correctly mocking graphql-request requests. So I updated those packages and issue dissappeared from my project.

using "msw": "1.3.2" and "jest": "29.7.0", where issue still appears

Envoy49 avatar Oct 10 '23 01:10 Envoy49

Sorry, I don't have the time to look into this. Please use msw@next where this issue is not reproducible. There's a year-worth of improvements and bug fixes in that version so I highly encourage you migrate and use it. See #1464 for more details on the migration guide.

I'm focused on releasing that version as 2.0 later this month so it's a good time to start updating.

kettanaito avatar Oct 10 '23 12:10 kettanaito

I'm closing this one because it likely has to do with fetch polyfills shenanigans.

How to fix this

  1. Migrate to Node.js v18 or newer.
  2. Migrate to MSW 2.0.
  3. Uninstall any fetch polyfills—you don't need them anymore in Node.js v18.

If the issue still persists, please file a new issue on GitHub and provide a reproduction repository. I will be happy to look into it.

kettanaito avatar Nov 14 '23 09:11 kettanaito