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

MSW loading in react native is not happening #389

Open
turjoy-real opened this issue Apr 23, 2024 · 8 comments
Open

MSW loading in react native is not happening #389

turjoy-real opened this issue Apr 23, 2024 · 8 comments

Comments

@turjoy-real
Copy link

turjoy-real commented Apr 23, 2024

Preloading MSW fails to detect if App is registered.

Extra line needs to be added which loads data without MSW first, then recognises MSW.

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';

import Config from 'react-native-config';

async function enableMocking() {
  if (Config.APP_ENV !== 'test') {
    return;
  }

  await import('./msw.polyfills');
  const {server} = await import('./src/__mock__/server');
  server.listen();
}

AppRegistry.registerComponent(appName, () => App); // <----

enableMocking().then(() => {
  AppRegistry.registerComponent(appName, () => App);
});
@kettanaito
Copy link
Member

Hi, @turjoy-real.

Not sure I fully understand the issue, could you describe it a bit more for me?

The reason AppRegistry.registerComponent() is deferred untul the enableMocking() promise resolves is so your app wouldn't render a component that relies on an HTTP request while MSW hasn't activated yet. In RN case, it mostly comes to the dynamic import promise, which still takes time (the interception itself is instantaneous).

If we call .registerComponent() straight away, wouldn't it start rendering the app?

@turjoy-real
Copy link
Author

Yes it starts rendering the app with default APIs. After MSW loads, it then uses MSW mocks.

MSW is supposed to load before AppRegistry. But react native doesn't wait for the promise to resolve loading MSW and throws error saying there's no AppRegistry.

@kettanaito
Copy link
Member

kettanaito commented Apr 24, 2024

But react native doesn't wait for the promise to resolve loading MSW and throws error saying there's no AppRegistry.

Yeah, this is the culprit. RN expects you to call .registerComponent on the same tick.

Do you know if we can defer the promise await to the () => App closure? Does RN support something like this?

App.registerComponent(async () => {
  await enableMocking()
  return App
})

I suppose not but I have zero clue about how RN behaves.

@turjoy-real
Copy link
Author

No this is not working too. React Native doesn't expect a async callback function.

This is the new error:

Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

This is happening even if I remove the await step.

It's taking the unresolved promise object.

Used this:

AppRegistry.registerComponent(appName, async () => {
await enableMocking();
return App;
});

Instead of:

AppRegistry.registerComponent(appName, () => App);

@kettanaito
Copy link
Member

Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

This error suggests the second argument is expected to be a React component. This means we can create a wrapper component and delegate the promise resolution to the state.

App.registerComponent(appName, () => {
  const [isMockingEnabled, setMockingEnabled] = React.useState(false)

  React.useEffect(() => {
    enableMocking().then(() => setMockingEnabled(true))
  }, [])

  if (!isMockingEnabled) return null
  return <App />
})

Can you please give this a try, @turjoy-real? Thanks.

@turjoy-real
Copy link
Author

turjoy-real commented Apr 26, 2024

Hi @kettanaito,

Thanks for the idea.

Successfully made MSW APIs the default for all E2E testing API calls.

The changes had to be made in the App.tsx(or App.js) to be able to use hooks.

function App() {
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    enableMocking().then(() => setLoading(false));
  }, []);

  return (
    !loading && <Component/>
  );
}

And for the dynamic imports of msw.polyfills and mock server in typescript,

updated tsconfig.json to use 'ES2022':

{
  "extends": "@react-native/typescript-config/tsconfig.json",
  "compilerOptions": {
    "typeRoots": ["./types"],
    "module": "ES2022"
  }
}

Leaving index.js as it was:

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => App);

Here is the App commit with the changes.

Thank you for your support.

It was exciting to see it resolved finally with such a simple solution.

Let me know if I can contribute with you in any other issues.

@kettanaito
Copy link
Member

That's awesome! 🎉 We need to update the React Native integration docs with this so it's clear to everyone.

@turjoy-real
Copy link
Author

turjoy-real commented Apr 26, 2024

Yes, that'll be helpful for everyone!

Let me know if and how I can help.

Should I prepare a draft and post in this thread?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants