react-native icon indicating copy to clipboard operation
react-native copied to clipboard

Running Jest test returns TypeError: _global.WebSocket is not a constructor

Open sabun123 opened this issue 5 years ago • 3 comments

Describe the bug With storybook setup in React Native, running npm test hits a snag at: TypeError: _global.WebSocket is not a constructor

I cannot find any documentation relating to setting up Storybook for Jest testing the app to work in React Native.

The error in full:

 FAIL  __tests__/App-test.js
  ● Test suite failed to run

    TypeError: _global.WebSocket is not a constructor

      18 | // Refer to https://github.com/storybookjs/storybook/tree/master/app/react-native#start-command-parameters
      19 | // To find allowed options for getStorybookUI
    > 20 | const StorybookUIRoot = getStorybookUI({
         |                         ^
      21 |   port: Number(7007),
      22 | });
      23 | 

      at WebsocketTransport.connect (node_modules/@storybook/channel-websocket/dist/index.js:87:21)
      at new WebsocketTransport (node_modules/@storybook/channel-websocket/dist/index.js:40:10)
      at Object.createChannel [as default] (node_modules/@storybook/channel-websocket/dist/index.js:119:19)
      at Preview.getStorybookUI (node_modules/@storybook/react-native/dist/preview/index.js:134:58)
      at Object.<anonymous> (storybook/index.js:20:25)
      at Object.<anonymous> (src/App.js:14:1)

To Reproduce Steps to reproduce the behavior:

  1. Setup an RN project (not Expo)
  2. Add storybook/react-native and it's dependencies to project
  3. Add <StorybookUI /> to code to render StoryBook on app startup if certain env variable is met
  4. Have initial basic Jest test renderer.create(<App />); setup
  5. Run npm test

To be clear, I have a separate script to run storybook mode: "storybook": "ENVFILE=.env.storybook react-native run-ios --scheme \"DEV\" --configuration \"DEV.Debug\" && start-storybook -p 7007",

When running normally without storybook, a separate App is run instead. Code in code snippet below.

Expected behavior Either that it works with Jest out-of-the-box, or that there's documentation on how to setup for this.

Code snippets My App.js:

...

const App: () => React$Node = () => {
  const theme = getTheme();

  return (
    <NavigationContainer>
      <Provider store={store}>
        <PersistGate loading={null} persistor={persistor}>
          <ThemeProvider theme={theme}>
            <LanguageProvider>
              <Root />
            </LanguageProvider>
          </ThemeProvider>
        </PersistGate>
      </Provider>
    </NavigationContainer>
  );
};

/* istanbul ignore next */
const AppStorybook: () => React$Node = () => {
  useEffect(() => {
    SplashScreen.hide();
  }, []);

  return <StorybookUI />;
};

// Either we run the app normally, or in dev we can choose to run storybook
const AppToExport = Config.APP_TYPE === 'storybook' ? AppStorybook : App;
export default AppToExport;

The basic Jest test that comes with RN out of the box:

/**
 * @format
 */

import 'react-native';
import React from 'react';
import renderer from 'react-test-renderer';
import App from '../src/App';

// Note: test renderer must be required after react-native.

it('renders correctly', () => {
  renderer.create(<App />);
});

My jest.config.js:

module.exports = {
  preset: 'react-native',
  setupFiles: ['<rootDir>/jest/setup.js'],
};

System:

    "@storybook/addon-actions": "5.3",
    "@storybook/addon-knobs": "5.3",
    "@storybook/addon-links": "5.3",
    "@storybook/addon-ondevice-actions": "5.3.23",
    "@storybook/addon-ondevice-knobs": "5.3.23",
    "@storybook/react-native": "5.3.23",
    "@storybook/react-native-server": "5.3.23",

Additional context I've tried ignoring the storybook folder in Jest config with testPathIgnorePatterns: ['<rootDir>/storybook'], but to no avail. I've also tried /* istanbul ignore next */ to ignore the storybook app and it doesn't seem to work.

Any pointers or direction to the appropriate documentation would be wonderful. If the only solution is to mock websocket to move forward with this, would it be possible then to bypass Storybook all together in testing?

sabun123 avatar Oct 22 '20 23:10 sabun123

An option might be to mock the getStorybookUI with a jest mock. Something like jest.mock("@storybook/react-native", ()=>({ getStorybookUI: jest.fn() })). I haven't tried this to check it works yet but something like that would probably work. I will investigate this at some point to find a proper fix and update this comment.

You can also just remove this default test file because I don't think it's really necessary. Of course ideally storybook wouldn't cause this error but if you want to just get past the error removing the app-test.js file should work.

dannyhw avatar Nov 03 '20 21:11 dannyhw

I had the same problem, maybe this will help you.

const StorybookUIRoot = getStorybookUI({
  disableWebsockets: true,
})

alemanza avatar Jan 26 '21 22:01 alemanza

To complete @dannyhw's answer (thanks a lot, by the way), don't forget to include other function like addDecorator or configure depending on your needs.

jest.mock('@storybook/react-native', () => ({
  getStorybookUI: jest.fn(),
  addDecorator: jest.fn(),
  configure: jest.fn(),
}));

GuillaumeHemmen avatar Mar 15 '21 12:03 GuillaumeHemmen

Confirmed the solution by @GuillaumeHemmen works like a charm, just added to my jest.setup.js

@sabun123 If your problem is solved can you please close the issue? It sparks joy for maintainers ✨

flexbox avatar Feb 16 '23 10:02 flexbox

Since it seems like this is resolved I will close the issue.

Let me know if it should be reopened, thanks.

dannyhw avatar Jul 14 '23 10:07 dannyhw