oblador / react-native-vector-icons

Customizable Icons for React Native with support for image source and full styling.
https://oblador.github.io/react-native-vector-icons/
MIT License
17.4k stars 2.12k forks source link

Failing Jest test with a wrapped `<Icon.ToolbarAndroid />` #351

Closed danbruegge closed 7 years ago

danbruegge commented 7 years ago

Hi, i got an error when i try to run a test with Jest and a wrapped <Icon.ToolbarAndroid />.

<Toolbar />

import React, { PropTypes } from 'react';
import Icon from 'react-native-vector-icons/MaterialIcons';

import { STYLE, TOOLBAR } from '../styles';

export default class Toolbar extends React.Component {
    static propTypes = {
        visible: PropTypes.bool.isRequired,
        navigator: PropTypes.object.isRequired,
        page: PropTypes.object.isRequired,
        pages: PropTypes.array.isRequired,
        title: PropTypes.string,
    }

    static defaultProps = {
        visible: true,
        style: [ STYLE.base, TOOLBAR.base ],
        title: 'Pinja',
        navIconName: 'face',
    }

    constructor (props) {
        super(props);

        let pageIndex = 1;

        this.state = {
            toolbarPages: props.pages
                .filter(route => route.toolbar.display)
                .map(route => ({
                    id: pageIndex++,
                    title: route.title,
                    show: route.toolbar.show,
                    iconName: route.toolbar.icon
                })),
        };
    }

    render () {
        let props = this.props;

        return !props.visible ? null : <Icon.ToolbarAndroid
            title={props.title || props.page.title}
            style={props.style}
            navIconName={props.navIconName}
            onIconClicked={() => props.navigator.replace(props.pages[0])}
            actions={this.state.toolbarPages}
            onActionSelected={position => this.onActionSelected(position)}
        />;
    }

    onActionSelected (position) {
        let route = this.props.pages[position+1];

        if (typeof route.onAction === 'function') {
            route.onAction();
        } else {
            this.props.navigator.replace(route);
        }
    }
}

Jest test:

import React from 'react';

import renderer from 'react-test-renderer';

import Toolbar from '../toolbar';

describe('renders correctly', () => {
    it('on visible=true', () => {
        const navigator = { replace: jest.fn() };
        const page = { title: 'Toolbar Test #1' };
        const pages = [{
            id: 0,
            title: 'Toolbar Test #2',
            tagName: 'ToolbarTest',
            toolbar: {
                show: 'never',
                icon: 'check',
                display: true,
            },
        }];

        const toolbar = (
            <Toolbar
                visible={true}
                navigator={navigator}
                page={page}
                pages={pages}
            />
        );

        const tree = renderer.create(toolbar).toJSON();

        expect(tree).toMatchSnapshot();
    });
});

The error:

x FAIL  src/components/__tests__/toolbar.js
  ● renders correctly › on visible=true

    RNVectorIconsManager not available, did you add the library to your project and link with libRNVectorIcons.a?

      at getImageSource (node_modules/react-native-vector-icons/lib/create-icon-set.js:93:7)
      at IconToolbarAndroid.updateIconSources (node_modules/react-native-vector-icons/lib/toolbar-android.js:38:1)
      at IconToolbarAndroid.componentWillMount (node_modules/react-native-vector-icons/lib/toolbar-android.js:56:6)
      at node_modules/react/lib/ReactCompositeComponent.js:347:23
      at measureLifeCyclePerf (node_modules/react/lib/ReactCompositeComponent.js:74:12)
      at ReactCompositeComponentWrapper.performInitialMount (node_modules/react/lib/ReactCompositeComponent.js:346:9)
      at ReactCompositeComponentWrapper.mountComponent (node_modules/react/lib/ReactCompositeComponent.js:257:21)
      at Object.mountComponent (node_modules/react/lib/ReactReconciler.js:47:35)
      at ReactCompositeComponentWrapper.performInitialMount (node_modules/react/lib/ReactCompositeComponent.js:370:34)
      at ReactCompositeComponentWrapper.mountComponent (node_modules/react/lib/ReactCompositeComponent.js:257:21)

  renders correctly
    ✕ on visible=true (7ms)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        0.815s, estimated 1s
Ran all test suites matching "toolbar".

Somone has an Idea and can help?

// Edit If i remove the navIconName the test will pass.

danbruegge commented 7 years ago

After a bit of research, looks more like an ios problem. But. I use android and write the code on a linux machine.

oblador commented 7 years ago

You need to mock the native module.

danbruegge commented 7 years ago

Thanks for the fast answer. But how should i mock it?

oblador commented 7 years ago

Refer to the jest docs: https://facebook.github.io/jest/docs/tutorial-react-native.html#mock-native-modules-using-jestmock

danbruegge commented 7 years ago

Ok, i will have a look. :) Thanks.

danbruegge commented 7 years ago

@oblador, for me, it's unclear how i should mock the native module. Because i don't know which. Please can you explain it with more details?

dbertella commented 7 years ago

I had the same problem and at the end on android it works just mocking the library like that:

jest.mock('react-native-vector-icons/Ionicons', () => ({ ToolbarAndroid: 'ToolbarAndroid' }));

On the other hand, pretty much the same code on ios, with the TabBarItemIOS will complain with: Invariant Violation: There is no registered component for the tag TabBarItemIOS This is the ios mock:

jest.mock('react-native-vector-icons/Ionicons', () => ({ TabBarItemIOS: 'TabBarItemIOS' }));

Do you know why?

danbruegge commented 7 years ago

@dbertella awesome, thanks. Looks good now. :)

You're problem looks very common. Maybe you can find help there: https://github.com/facebook/react-native/issues/2066 ?

dbertella commented 7 years ago

@danbruegge Actually I found out that it was the TabBarIOS component that cause the problem, I had to mock it jest.mock('TabBarIOS', () => 'TabBarIOS') and then the snapshot went through. Still concern about it but it worked.

jsbranco commented 7 years ago

I was just having this issue and @dbertella suggestion worked for me : jest.mock('react-native-vector-icons/MaterialIcons', () => { return {TabBarItemIOS:""}}) resolved my problem

chinloongtan commented 7 years ago

Fixed it with:

jest.mock('react-native-vector-icons', () => {
  const ActualTabBarIOS = require.requireActual('TabBarIOS');
  const React = require('react');

  return {
    createIconSet: () => {
      const Icon = class extends React.Component {
        render() {
          return jest.fn();
        }
      }

      Icon.TabBarItem = ActualTabBarIOS.Item;
      return Icon;
    },

    createIconSetFromFontello: () => {
      return {
        TabBarItem: ActualTabBarIOS.Item
      }
    },

    createIconSetFromIcoMoon: jest.fn(),
  }
});

Though I am working on TabBarIOS, but getting the same error RNVectorIconsManager not available, did you add the library to your project and link with libRNVectorIcons.a? before the mock.

This code is just enough for my use case. In case if this helps anyone in the future.

ParAnton commented 6 years ago

I'm a littlebit lost here where do you put the jest.mock part if I place it inside the test file it doesn't seem to have any effect

abumostafa commented 5 years ago

@ParAnton you need to create a file with the mocks and add it to jest config

"jest": {
    ...
    "setupFiles": [
      "./path/to/mocks.js"
    ]
    ...
  }