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

React Native support #203

Closed
hsavit1 opened this issue Jun 7, 2020 · 104 comments
Closed

React Native support #203

hsavit1 opened this issue Jun 7, 2020 · 104 comments
Assignees
Labels
feature help wanted Extra attention is needed
Milestone

Comments

@hsavit1
Copy link

hsavit1 commented Jun 7, 2020

Is your feature request related to a problem? Please describe.
Does React Native have the ability to support this?

Describe the solution you'd like
Some guide to do something similar with react native

Describe alternatives you've considered
Can something custom be set up with https://github.com/Blitz-Mobile-Apps/react-native-bg-thread#readme ?

Additional context
Add any other context or screenshots about the feature request here.

@hsavit1 hsavit1 added the feature label Jun 7, 2020
@kettanaito kettanaito changed the title React Native solution React Native support Jun 8, 2020
@kettanaito kettanaito pinned this issue Jun 8, 2020
@kettanaito
Copy link
Member

I wonder if using setupServer in React Native would be enough. I'd appreciate if somebody could do a proof of concept with that.

@kettanaito kettanaito added the help wanted Extra attention is needed label Jun 14, 2020
@hsavit1
Copy link
Author

hsavit1 commented Jun 14, 2020

do you think, for react native, that it could work for grahphql requests (via apollo client) as well?

@kettanaito
Copy link
Member

@hsavit1, I believe so. It's a matter of understanding a request flow in React Native. I assume React Native app runs on Node, so that's why I've suggested to check if using setupServer doesn't intercept its requests already (that's the API that powers API mocking in Node).

As of GraphQL, it's not much different than a regular REST request. In fact, it is a regular REST request, just compliant with the GraphQL specification (has a query, optional variables, and so on).

@hsavit1
Copy link
Author

hsavit1 commented Jun 14, 2020

I believe React Native has some access to Node.js APIs via the node-js mobile library. See this for more.

Read more about the javascript core here: https://reactnative.dev/docs/0.60/javascript-environment

@donovanhiland
Copy link

donovanhiland commented Jun 29, 2020

Stumbled across this issue while trying to set up a react native app to use msw for integration tests. I would love to help get this working for react-native outside of tests if someone could point me in the right direction.

@kettanaito
Copy link
Member

Hey, @donovanhiland! We'd really appreciate your help, if you have a spare minute.

Could you please try using setupServer (regular integration with Node) in your React Native integration tests? Here's the documentation on the process. Since tests run in Node environment, that integration should work. But due to the difference of the platform, it may have some unexpected behavior.

Would love to hear how it goes from you.

@donovanhiland
Copy link

Absolutely! @kettanaito I was able to get setupServer working in my react-native apollo jest integration test environment. There was a small caveat for react-native tests where I had to set a global fetch implementation so msw could intercept. Otherwise fetch could not be found.

In trying to implement msw in the actual react native app it looks like it does not have access to core NodeJS modules without some extra work. I toyed with https://github.com/parshap/node-libs-react-native which got me past the missing module warnings, but am now running into:

Error: [MSW] Failed to execute `setupServer` in the environment that is not NodeJS (i.e. a browser). Consider using `setupWorker` instead.

There is no global process variable in the react-native environment

@hsavit1
Copy link
Author

hsavit1 commented Jun 30, 2020

@donovanhiland @kettanaito is there a way to shim the node env using this?

@kettanaito
Copy link
Member

kettanaito commented Jun 30, 2020

Thanks for trying that out so quickly. Excited to hear that you got it to the working state!

There was a small caveat for react-native tests where I had to set a global fetch implementation so msw could intercept.

I think this is the gotcha for any Node tests that rely on fetch. Fetch implementation needs to be polyfilled.

There is no global process variable in the react-native environment

This is an interesting detail. Looks like we need to adjust our isNodeProcess function that validates your API usage against the environment. The question is: should we consider React Native as Node? It should definitely error on attemtpint to use setupWorker in React Native (no Service Worker API).

@hsavit1, that's a nice find! I'm concerned about the amount of modules this single import pulls in. It may have a high cost, which may affect the tests' performance. Perhaps, if we could also check for React Native environment by some distinct flag, we may omit relying on global.

@hsavit1
Copy link
Author

hsavit1 commented Jun 30, 2020

@kettanaito this is a project that seems to be a bit more focused

@hsavit1
Copy link
Author

hsavit1 commented Jun 30, 2020

There was a small caveat for react-native tests where I had to set a global fetch implementation so msw could intercept

@donovanhiland How did you do this?

@hsavit1
Copy link
Author

hsavit1 commented Jun 30, 2020

I can confirm that I was able to get this working for my jest test suite using ApolloProvider and the cross-fetch library , but was not able to get it working for my application

@donovanhiland
Copy link

There was a small caveat for react-native tests where I had to set a global fetch implementation so msw could intercept

@donovanhiland How did you do this?

I set global.fetch = require('node-fetch') in a jest setup file

@hsavit1
Copy link
Author

hsavit1 commented Jul 5, 2020

Thanks for trying that out so quickly. Excited to hear that you got it to the working state!

There was a small caveat for react-native tests where I had to set a global fetch implementation so msw could intercept.

I think this is the gotcha for any Node tests that rely on fetch. Fetch implementation needs to be polyfilled.

There is no global process variable in the react-native environment

This is an interesting detail. Looks like we need to adjust our isNodeProcess function that validates your API usage against the environment. The question is: should we consider React Native as Node? It should definitely error on attemtpint to use setupWorker in React Native (no Service Worker API).

@hsavit1, that's a nice find! I'm concerned about the amount of modules this single import pulls in. It may have a high cost, which may affect the tests' performance. Perhaps, if we could also check for React Native environment by some distinct flag, we may omit relying on global.

does the PR #262 address these issues?

@hsavit1
Copy link
Author

hsavit1 commented Jul 5, 2020

strangely, when trying to run my tests on master, they now fail with error Cannot find module 'msw/node'

@kettanaito
Copy link
Member

kettanaito commented Jul 5, 2020

Yes, #262 addresses proper environment check for React Native, so it's treated as any other NodeJS process by MSW. The changes are not published yet, however.

Looks like you have msw package missing. What does npm ls msw says?

@hsavit1
Copy link
Author

hsavit1 commented Jul 5, 2020

Yes, @262 addresses proper environment check for React Native, so it's treated as any other NodeJS process by MSW. The changes are not published yet, however.

Looks like you have msw package missing. What does npm ls msw says?

That's great news!

In my package.json, I have:

"msw": "github:mswjs/msw#master",

When I run npm ls I see

[email protected]  (github:mswjs/msw#d6e1f0aea1ec5fbe09db574722247a5b16d3da54)

@hsavit1
Copy link
Author

hsavit1 commented Jul 6, 2020

Even when I added a postinstall script it fails with the "cannot find config.rollup.ts" error. For some reason my directory tree looks like (?):

Screen Shot 2020-07-06 at 9 54 30 AM

@kettanaito
Copy link
Member

It appears you are installing the library from its Git branch. While that would link the library to your dependencies, it won't build the library. Since build artifacts, such as lib/**, are not committed to VCS, you'd have to build the library manually:

$ cd node_modules/msw
$ yarn build

The screenshot above illustrates that the msw directory does not contain the built library (the lib directory).

@hsavit1
Copy link
Author

hsavit1 commented Jul 7, 2020

It appears you are installing the library from its Git branch. While that would link the library to your dependencies, it won't build the library. Since build artifacts, such as lib/**, are not committed to VCS, you'd have to build the library manually:

$ cd node_modules/msw
$ yarn build

The screenshot above illustrates that the msw directory does not contain the built library (the lib directory).

the yarn build is what I added to the postinstall script. I tried doing it manually by running it in the directory, again it failed with the error

[!] Error: ENOENT: no such file or directory, lstat '/Users/xxx/yyy/zzz-app/node_modules/msw/rollup.config.ts'
Error: ENOENT: no such file or directory,

@Federkun
Copy link
Contributor

Federkun commented Jul 8, 2020

I was interested to see if it was possible to start msw's server at runtime in react native ( as is possible with the browser's worker ). I was glad to find that it's possible to make it work.

My finding:

I had to patch msw like this:

diff --git a/node_modules/msw/node/index.js b/node_modules/msw/node/index.js
index 0e16e2e..e06d680 100644
--- a/node_modules/msw/node/index.js
+++ b/node_modules/msw/node/index.js
@@ -4,7 +4,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
 
 function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
 
-var timers = _interopDefault(require('timers'));
 var nodeRequestInterceptor = require('node-request-interceptor');
 
 /*! *****************************************************************************
@@ -590,10 +589,6 @@ function resetHandlers(initialHandlers, ...nextHandlers) {
  */
 const setupServer = (...requestHandlers) => {
     const interceptor = new nodeRequestInterceptor.RequestInterceptor();
-    // Error when attempting to run this function in a browser environment.
-    if (!isNodeProcess()) {
-        throw new Error('[MSW] Failed to execute `setupServer` in the environment that is not NodeJS (i.e. a browser). Consider using `setupWorker` instead.');
-    }
     // Store the list of request handlers for the current server instance,
     // so it could be modified at a runtime.
     let currentHandlers = [...requestHandlers];
@@ -632,7 +627,7 @@ const setupServer = (...requestHandlers) => {
                     var _a;
                     // using the timers module to ensure @sinon/fake-timers or jest fake timers
                     // don't affect this timeout.
-                    timers.setTimeout(() => {
+                    setTimeout(() => {
                         resolve({
                             status: response.status,
                             statusText: response.statusText,

and node-request-interceptor like this:

diff --git a/node_modules/node-request-interceptor/lib/RequestInterceptor.js b/node_modules/node-request-interceptor/lib/RequestInterceptor.js
index e24d5df..abfb4cf 100644
--- a/node_modules/node-request-interceptor/lib/RequestInterceptor.js
+++ b/node_modules/node-request-interceptor/lib/RequestInterceptor.js
@@ -36,7 +36,6 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
     }
 };
 Object.defineProperty(exports, "__esModule", { value: true });
-var override_1 = require("./http/override");
 var override_2 = require("./XMLHttpRequest/override");
 var debug = require('debug')('interceptor');
 var RequestInterceptor = /** @class */ (function () {
@@ -69,7 +68,7 @@ var RequestInterceptor = /** @class */ (function () {
         }); };
         this.middleware = [];
         debug('created new RequestInterceptor');
-        this.overrides = [override_1.overrideHttpModule, override_2.overrideXhrModule].map(function (override) {
+        this.overrides = [override_2.overrideXhrModule].map(function (override) {
             return override(_this.applyMiddleware);
         });
     }
diff --git a/node_modules/node-request-interceptor/lib/XMLHttpRequest/XMLHttpRequest/createEvent.js b/node_modules/node-request-interceptor/lib/XMLHttpRequest/XMLHttpRequest/createEvent.js
index 8d9479a..81d2e69 100644
--- a/node_modules/node-request-interceptor/lib/XMLHttpRequest/XMLHttpRequest/createEvent.js
+++ b/node_modules/node-request-interceptor/lib/XMLHttpRequest/XMLHttpRequest/createEvent.js
@@ -1,6 +1,16 @@
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
 var EventOverride_1 = require("./EventOverride");
+var ProgressEvent = function(type, eventInit) {
+    this.type = type
+    this.lengthComputable = eventInit.lengthComputable
+    this.loaded = eventInit.loaded
+    this.total = eventInit.total
+  }
+
+  //ProgressEvent.prototype = Object.create(Event.prototype)
+  ProgressEvent.prototype.constructor = ProgressEvent
+
 exports.createEvent = function (options, target, type) {
     var progressEvents = [
         'error',

and include a polyfill, import 'react-native-url-polyfill/auto'.

That was enough to start using a local dev server in RN, at runtime.

@hsavit1
Copy link
Author

hsavit1 commented Jul 8, 2020

I was interested to see if it was possible to start msw's server at runtime in react native ( as is possible with the browser's worker ). I was glad to find that it's possible to make it work.

My finding:

I had to patch msw like this:

Nice work @Federkun - did you do this against master or against 0.19.5 ?

@Federkun
Copy link
Contributor

Federkun commented Jul 8, 2020

Against 0.19.5 ( 7a72ff7 ), so it doesn't have those few useful last commits

@kettanaito
Copy link
Member

kettanaito commented Jul 8, 2020

Hey, @Federkun. Thank you for those insights!

-var timers = _interopDefault(require('timers'));

I suppose timers is not a built-in in React Native?

History: timers are used because they are decoupled from setTimeout. When you mock timers in testing frameworks they stub setTimeout, which results into mocked requests never resolving.

-if (!isNodeProcess()) {

Could you please share your findings on why this line was causing troubles?

I suppose it's because 0.19.5 doesn't contain important improvements of the isNodeProcess utility that unexpectedly failed in React Native. Should be fixed in the next release by #255 and #262.

React Native should be treated as a NodeJS environment. This condition should never resolve in React Native.

-var override_1 = require("./http/override");

This is interesting. I'd expect http to be present in RN, while XHR to be not. Is it the opposite? I wonder if we have to check for http module availability in node-request-interceptor before patching it.

@Federkun
Copy link
Contributor

Federkun commented Jul 8, 2020

Keep in mind that RN's js runtime is not the same as node, most of those core modules are missing there. That unfortunately includes timers and http.

About, isNodeProcess, I wouldn't worry about it. I did delete it because the latest released version (0.19.5) didn't include 32b7545 , which I can confirm fix the issue

@marcosvega91
Copy link
Member

Happy to hear that @hsavit1 , I think that we can consider this issue as resolved. Happy coding to everyone 🎉

@ilkerceng
Copy link

ilkerceng commented Sep 26, 2020

hi @marcosvega91 @hsavit1 ,
1- I have tried the given example with Rest Api not Graphql. Tt works fine with react-native debugger if network inspect disabled. But when i enable network inspection, it gives 404.

2- When msw js enabled with running server.listen(), the service calls having no mocks in any handlers give network error

Do you have any idea about these situations?

@brookemitchell
Copy link

brookemitchell commented Oct 14, 2020

This is some great work, sadly like @hsavit1 I couldn't get msw running against my jest tests?

msw/native didn't seem to intercept the response in my jest testing. msw/node did manage to intercept (could log the query), but very strangely I couldn't return my res(ctx.data({ ... graphql response.

I get it now, msw/native for running live against app, msw/node for jest testing. In business now 👏 🙏

@kettanaito
Copy link
Member

Please try the onUnhandledRequest option if you're experiencing any issues. It will help you figure out if there's a mistake in your request handlers. For example, you can set it to "error" which will raise an exception when an unhandled request occurs.

server.listen({ onUnhandledRequest: 'error' })

@varunrayen
Copy link

@ilkerceng I am also trying to implement REST Api. But i get this following error

Error: While trying to resolve module `tty` from file `/Users/varunrayen/Development/tmsss/freshcatch-mobile-app/node_modules/msw/native/index.js`, the package `/Users/varunrayen/Development/tmsss/freshcatch-mobile-app/node_modules/tty/package.json` was successfully found. However, this package itself specifies a `main` module field that could not be resolved (`/Users/varunrayen/Development/tmsss/freshcatch-mobile-app/node_modules/tty/index.js`. Indeed, none of these files exist:

MSW Version: 0.24.1

@kettanaito
Copy link
Member

That's odd. We have tty marked as an external dependency:

'tty',

This means the native.js module will try to resolve it against your project. In this case, it's a NodeJS dependency, so it should be installed globally in your system. What version of NodeJS do you have, @varunrayen?

@varunrayen
Copy link

@kettanaito My Node version is v12.13.1

@wwdrew
Copy link
Contributor

wwdrew commented Jan 13, 2021

I'm having issues getting this to work too. I've tried on an existing project and a brand new project with the same error:

error: Error: Unable to resolve module `os` from `node_modules/msw/native/index.js`: os could not be found within the project.

I'm using node version v12.20.1 and I've tried it using RN 0.63.4 and 0.64-rc2. I've followed the example project from the PR above (mswjs/examples#40), but every time I run I get that error.

Edit: After going back through the published versions of msw, the last version I'm able to get working is 0.22.2. I've had a quick look at the differences made between 0.22.2 and 0.22.3 to see what's changed that might be causing it but I can't really see what it might be.

However, while with that version running I'm able to intercept a rest API call (which is great), it crashes when I try to return a json response.

Using react-query to call the API (with fetch), If I use this as my handler array:

export const handlers = [
  rest.get(
    'https://api/api,
    (req, res, ctx) => {
      return res(
        ctx.status(200),
        ctx.json({
          please: 'work',
        }),
      );
    },
  ),
];

I get this error:

Possible Unhandled Promise Rejection (id: 0):
ReferenceError: Can't find variable: Buffer

If I remove the ctx.json call and just leave the ctx.status it doesn't crash, and the app does end up failing due to there being no json value (which is to be expected as there is none).

Has anyone else run into these problems?

@kettanaito
Copy link
Member

It seems that the bundled version for React Native (msw/native) isn't bundled correctly, as it relies on some modules RN fails to resolve. It was also raised in #622, let's track it there.

@kettanaito
Copy link
Member

The React Native support should be back in 0.31.0. Thanks to @wwdrew.

@taylorkline
Copy link

I've opened mswjs/examples#60 to get in a Rest React Native example for reference and smoke testing future MSW versions.

@chazmuzz
Copy link

Love this. Great job guys!

@kettanaito
Copy link
Member

Thank you, @taylorkline! I'll take a look at the example and help you with what I can.

@astriskit
Copy link

Hi. Any progress on this? this library is sort-of a last-mile towards getting integration (visual-testing) done using the storybook+expo setup for a current project. But still unsure - if this would work with it; just before trying it out - has anyone got any feedback for the same?

Some relevant info - expo^41.0.0 -> rn-0.63; storybook^5.3;

Thanks.

@kettanaito
Copy link
Member

Hey, @astriskit. There's no time capacity to look into this issue. Please feel free to pursue the fix and share the results of the investigation with us. We'd love to have a stable representative of the React Native community to help us with the respective issues.

@wwdrew
Copy link
Contributor

wwdrew commented Sep 14, 2021

@astriskit I'm not sure what progress is being waited on, React Native support should be working fine now. If you're looking for an example, @taylorkline has referenced one above, and there's mine at https://github.com/wwdrew/examples/tree/react-native

@astriskit
Copy link

Hi @kettanaito - the {msw, storybook, expo} setup went successfully. Was able to render a network-ed component from the example provided by @wwdrew.

And @wwdrew - thanks for the example link - the react-native example was helpful in making the setup work.

And for those - who might be reading to get the same setup working -

  1. Try following the example by @wwdrew.
  2. And once the src-tree is in shape and the setup seem to fail (after running storybook-app, crashes the expo app, atleast for me it did) - try adding {onUnhandledRequest: 'bypass'} to listen callback in setupServer(...handlers).listen.

@blwinters
Copy link

blwinters commented Oct 13, 2022

Should we update this FAQ about React Native? I can create a PR on the mswjs.io repo, but I'm not sure what the official stance is on RN support.

@ShivamJoker
Copy link

ShivamJoker commented Oct 14, 2022

I agree with @blwinters we should update the website
@kettanaito if you could let us know it would be great.

@kettanaito
Copy link
Member

MSW should work fine in React Native since that's just a Node.js process. You may require to provide your test environment with all the necessary polyfills to run tests.

@blwinters, so yes, by all means, pull requests are welcome. What would you like to add?

@blwinters
Copy link

@kettanaito Cool, I'll create a PR with text that briefly explains the nature of RN support and use of polyfills and we can refine the text from there.

@dani-pereira-dev
Copy link

I can't get it to work to perform integration tests with jest, is there anything new regarding that?

@github-actions github-actions bot locked and limited conversation to collaborators Oct 29, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests