msw: msw in testcafe says its started, but responds with 404

Describe the bug

I am attempted to use msw in conjunction with testcafe (the best e2e testing tool for node, yeah even better than cypress, shamless plug).

testcafe has good docs about running service workers & https features in tests which I am following.

When I run the test, I can see it says [MSW] Mocking enabled but then none of the fetches are working.

image

Environment

  • msw: 0.19.4
  • nodejs: v12.16.1
  • yarn:1.22.4

Please also provide your browser version. chrome 83

To Reproduce

Steps to reproduce the behavior:

  1. Clone https://github.com/benmonro/msw-server/tree/feature/testcafe (note repro for this is on a branch called feature/testcafe)
  2. (cd app && yarn)
  3. yarn
  4. yarn testcafe
  5. testcafe will launch a browser, Once the browser loads you can launch chrome dev tools and see the 404 as shown in the above screenshot.

If you’d like to see this working in the actual app:

  1. (cd app && yarn dev)
  2. you can then add a todo or click a todo to remove one.

that all goes through msw and works fine. it only seems to be problematic over testcafe’s proxy server.

Expected behavior

expecting fetch calls to be mocked.

Screenshots

If applicable, add screenshots to help explain your problem. (see above)

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 18 (18 by maintainers)

Most upvoted comments

@kettanaito I have a fix, but I’m not sure where to put a test to capture the fix, would you mind pointing me in the right place? I just have to add a null check for req.body.query and it seems to fix it.

Hey, I’m glad to see it running!

The usage of asterisk proves that the issue is with the proxy URL that Testcafe prepends. While asterisk would work, it’s generally not recommended, as it literally matches <ANYTHING>/todos, which may lead to unexpected matches.

I recommend to look into the Testcafe API and grab its prepended URL during the test runtime. Then use that URL when resolving a request handler’s route. Here’s an example:

const IS_TEST = process.env.NODE_ENV === 'test'

function withUrl(url) {
  const prefix = IS_TEST ? testCafeApi.getUrl() : ''
  return `${prefix}${url}`
}

rest.get(withUrl('/todo'), (req, res, ctx) => res())

Hey, @benmonro! Thanks for reporting this, and for the detailed reproduction scenario with the repo 🎉 I’ll try to look into it as soon as I can and let you know what I find. Meanwhile, if you discover something, do not hesitate to update this thread.

I’ve debugged the reproduction repository, posting some of the insights below. I’ll sort my findings by the probability to cause the issue.

Service Worker script is inactive

To verify: add any console.log statement within the body of the "fetch" event in the mockServiceWorker.js script. That event should trigger on any request on the page, considering the page’s scope is controlled by the worker. The console is not called, thus, the “fetch” event is not called either.

Why?

  • Hammerhead modifies the mockServiceWorker.js script:
Screen Shot 2020-07-10 at 10 55 46

Although the Service Worker registers successfully, I cannot predict how Hammerhead’s changes may affect its lifecycle.

  • The registration scope is wrong.

By default the worker registers at / (root) scope. In your case it’s http://localhost:1337/. Since the worker has access to all the routes relative to the registered scope, it should cover the URL that TestCafe creates during the test. However, there may be an issue with that, as http://localhost:1337/yghhCXKav/http://localhost:3000/ is a confusing URL.

I’m not familiar with TestCafe/Hammerhead, but I’d strongly recommend you to disable the editing of mockServiceWorker.js file by Hammerhead, and see how it behaves.

Relative routes are invalid

Once your testing framework spawns a URL like http://localhost:1337/yghhCXKav/http://localhost:3000/ your request handler’s URL become relative to the / (root), meaning:

rest.get('/todos')
// http://localhost:1337/todos

However, the actual request is performed relative to the entire URL: http://localhost:1337/yghhCXKav/http://localhost:3000/todos

One of the ways I’ve tried to overcome this is by adding the * (asterisks) before the leading slash of the route:

rest.get('*/todos')

This reads as “any URL ending with /todos”. Nevertheless, this doesn’t get the request URL matched, and I think the previous point about an inactive Service Worker is the reason. Once you can get around that, you’d most likely want to adjust request handlers’ URLs as well, in one way or another.

Overall, I find such URL structure during tests utterly confusing. There may be much more implications than Service Worker not registering, if your apps suddenly renders in such nested URL. I’d consider looking into TestCafe and any ways to make sure your app’s URL structure resembles its actual production structure as close as possible.

msw/node is required in index.ts

I strongly recommend to remove the import of msw/node in src/index.ts. That file is used by browser.ts to get the list of endpoints in a browser’s runtime. However, as it imports msw/node, it includes Node-specific modules into browser’s runtime (doesn’t matter if you use those modules or not).

You should split your browser/node setups of MSW into separate files and require them only in the proper environment. Here’s a good example of that.

Local untrusted SSL

Service Workers are demanding when it comes to SSL. Make sure that your local certificate is trusted by your browser/system. According to spec, Service Worker won’t register on an untrusted host. In your case it does register, but I’d still recommend to either ensure the SSL is always trusted, or use http:// for testing.


At the moment I cannot provide more details, as I lack the experience with TestCafe. I hope the insights above are enough to get you started in the right direction. Let me know in this thread what you find out. Thanks.