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

cy.request is not intercepted by msw #389

Closed
hakankaraduman opened this issue Sep 18, 2020 · 4 comments
Closed

cy.request is not intercepted by msw #389

hakankaraduman opened this issue Sep 18, 2020 · 4 comments
Labels
bug Something isn't working potentially solved scope:browser Related to MSW running in a browser

Comments

@hakankaraduman
Copy link

Environment

Name Version
msw ^0.20.5
browser Chrome 85.0.4183.102 (Official Build) (64-bit) (cohort: Stable)
OS Windows 10

Request handlers

I have request handlers set up correct because they work both in regular browser, in cypress with http reques triggered from browser and in the jest tests

Current behavior

I use mobx store for my apps global state, I have some util methods to apply some snapshot to test specific cases. I added corresponding endpoints to apply those snapshots.

They works when they are triggered by the app, but not triggered when I call them with cy.request, I made sure it's called after the msw is initialized.

Expected behavior

To msw to give back response as if it would be created from the app itself.

@hakankaraduman hakankaraduman added bug Something isn't working scope:browser Related to MSW running in a browser labels Sep 18, 2020
@kettanaito
Copy link
Member

kettanaito commented Sep 18, 2020

Hi, @hakankaraduman 👋

Let’s take a look how cy.request() works under the hood. The investigation we are about to perform is relevant for cypress@5.2.0.

When cy.request() is called, it returns a Promise of Cypress.backend() call:

https://github.com/cypress-io/cypress/blob/2c8c15fa41796c269da6818a8bbfc01a25f5fcec/packages/driver/src/cy/commands/request.js#L285

Fast-forwarding to where .backend() method is defined:

https://github.com/cypress-io/cypress/blob/2c8c15fa41796c269da6818a8bbfc01a25f5fcec/packages/driver/src/cypress.js#L544

We can see that it creates a Promise (returned from cy.request()) and emits the ”backend:request" event. By searching the Cypress repository for that event we find this reference in the event manager:

https://github.com/cypress-io/cypress/blob/2c8c15fa41796c269da6818a8bbfc01a25f5fcec/packages/runner/src/lib/event-manager.js#L25

That event is listed as the one to be transported to the socket. I don’t have any knowledge on Cypress internals, so if this is unknown to you, that’s unknown to me as well. However, let’s see what driverToSocketEvents affects.

Here we see that they iterate over each such event and create an event listener for each. In the listener they propagate that event to the ws instance via ws.emit().

https://github.com/cypress-io/cypress/blob/2c8c15fa41796c269da6818a8bbfc01a25f5fcec/packages/runner/src/lib/event-manager.js#L290

And here’s the declaration for the ws instance:

https://github.com/cypress-io/cypress/blob/2c8c15fa41796c269da6818a8bbfc01a25f5fcec/packages/runner/src/lib/event-manager.js#L13-L16

Diving further down, that’s how the “backend:request” event is handled by the web socket:

https://github.com/cypress-io/cypress/blob/2c8c15fa41796c269da6818a8bbfc01a25f5fcec/packages/server/lib/socket.js#L367

I can assume that in case of HTTP request this switch case is what gets executed:

https://github.com/cypress-io/cypress/blob/2c8c15fa41796c269da6818a8bbfc01a25f5fcec/packages/server/lib/socket.js#L384-L385

Conclusions

There are two conclusions we can draw from this lengthy codebase journey:

  1. Cypress doesn’t perform an actual request in cy.request(), but rather treats it as an event emitter, signaling to the web socket server to handle that event.
  2. Cypress uses WebSockets to handle requests performed via cy.request().

Based on those conclusions I’m afraid there’s nothing MSW could (or should) do with this behavior. The request you make with cy.request() doesn’t leave your app as an HTTP request, but rather as a WebSocket event. We do not support Web Sockets yet, however, the implementation is in works (see #156). Bear in mind that even when we do support sockets, that won’t help in this case, as handling an HTTP request via Web Socket API is misleading at best. You shouldn’t go that deep into Cypress implementation detail to mock a request.

I highly recommend to perform the request as a usual request in your app. That means either via fetch, XMLHttpRequest, or any third-party request issuing libraries to your liking (axios, react-query, etc.). That would result into an actual HTTP request, and such request can be intercepted and mocked by MSW.

Hope this helps.

@hakankaraduman
Copy link
Author

Hi @kettanaito

Thank you so much for this detailed explanation. I learned a lot and understood how to structure my tests for cypress. You're great.

@edouard-lopez
Copy link

This also happen with cy.intercept.

As I had end-to-end tests prior to using MSW, I decided to use it only for Jest and manual usage of the application. Thus, I restricted MSW start to:

  1. process.env.NODE_ENV === 'development'
  2. not being in Cypress: !window.Cypress

So my MSW start function look like:

const startMockServiceWorker = async () => {
  if (process.env.NODE_ENV === 'development' && !window.Cypress) {
    const { worker } = await import(
      /* webpackChunkName: "mock-service-worker" */
      './mocks/browser'
    );
    worker.start();
  }
  return Promise.resolve();
};

Alternate solutions

There is some libs that might help on this gap

@silent-tan
Copy link

is this being solved in msw v2?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working potentially solved scope:browser Related to MSW running in a browser
Projects
None yet
Development

No branches or pull requests

4 participants