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

How to mock this library with jest? (Fails when used normally)

Open princefishthrower opened this issue 4 years ago • 7 comments

Environment

Relevant versions:

"react": "17.0.2",
"react-native": "0.66.0",
"react-native-pager-view": "^5.4.9",

Description

Calling the setPage method on a viewPager ref causes jest to throw this error:

TypeError: Cannot read properties of undefined (reading 'Commands')

      87 |     if (viewPager.current) {
    > 88 |       viewPager.current.setPage(page);
         |                         ^
      89 |     }
      90 | 

Reproducible Demo

  1. Create an integration test with any component using ViewPager and call setPage on it's ref. This error will throw.

princefishthrower avatar Nov 16 '21 09:11 princefishthrower

Anybody? This is like the last thing I need to mock / stub to get my integration tests to start fully working...

princefishthrower avatar Nov 22 '21 08:11 princefishthrower

jest.mock('react-native-pager-view', () => {
  const React = require('react');
  const View = require('react-native').View;

  return class ViewPager extends React.Component {
    render() {
      const {
        children,
        initialPage,
        onPageScroll,
        onPageScrollStateChanged,
        onPageSelected,
        style,
        scrollEnabled,
        accessibilityLabel,
      } = this.props;
      console.log({
        children,
        initialPage,
        onPageScroll,
        onPageScrollStateChanged,
        onPageSelected,
        style,
        scrollEnabled,
        accessibilityLabel,
      });
      return (
        <View
          testID={this.props.testID}
          initialPage={initialPage}
          onPageScroll={onPageScroll}
          onPageScrollStateChanged={onPageScrollStateChanged}
          onPageSelected={onPageSelected}
          style={style}
          scrollEnabled={scrollEnabled}
          accessibilityLabel={accessibilityLabel}
        >
          {children}
        </View>
      );
    }
  };
});

troZee avatar Dec 02 '21 15:12 troZee

I extended the example with the public class methods and now it is working:

jest.mock("react-native-pager-view", () => {
  const React = require("react");
  const View = require("react-native").View;

  return class ViewPager extends React.Component {
    // *********************
    // THIS WAS MISSING
    setPage() {}
    setPageWithoutAnimation() {}
    setScrollEnabled() {}
    // *********************

    render() {
      const {
        children,
        initialPage,
        onPageScroll,
        onPageScrollStateChanged,
        onPageSelected,
        style,
        scrollEnabled,
        accessibilityLabel,
      } = this.props;

      console.log({
        children,
        initialPage,
        onPageScroll,
        onPageScrollStateChanged,
        onPageSelected,
        style,
        scrollEnabled,
        accessibilityLabel,
      });

      return (
        <View
          testID={this.props.testID}
          initialPage={initialPage}
          onPageScroll={onPageScroll}
          onPageScrollStateChanged={onPageScrollStateChanged}
          onPageSelected={onPageSelected}
          style={style}
          scrollEnabled={scrollEnabled}
          accessibilityLabel={accessibilityLabel}>
          {children}
        </View>
      );
    }
  };
});

madflanderz avatar Jan 18 '22 09:01 madflanderz

You can complement setPage callback mock and allow page view switching with: setPage(selectedPage) { this.props.onPageSelected({ nativeEvent: { position: selectedPage } }); } It allows rendering of content when a tab is pressed

mowbell avatar Jul 14 '22 14:07 mowbell

I had a use-case where I wanted to test screen change using setPage. Made some changes to get it working for me:

jest.mock('react-native-pager-view', () => {
  const React = require('react');
  const PropTypes = require('prop-types');
  const View = require('react-native').View;

  class ViewPager extends React.Component {
    constructor(props) {
      super();
      this.state = {
        page: props.initialPage,
      };
    }

    setPage(selectedPage) {
      this.setState({ page: selectedPage });
    }
    setPageWithoutAnimation() {}
    setScrollEnabled() {}

    render() {
      const {
        children,
        initialPage,
        onPageScroll,
        onPageScrollStateChanged,
        onPageSelected,
        style,
        scrollEnabled,
        accessibilityLabel,
      } = this.props;

      return (
        <View
          testID={this.props.testID}
          initialPage={initialPage}
          onPageScroll={onPageScroll}
          onPageScrollStateChanged={onPageScrollStateChanged}
          onPageSelected={onPageSelected}
          style={style}
          scrollEnabled={scrollEnabled}
          accessibilityLabel={accessibilityLabel}
        >
          {children[this.state.page]}
        </View>
      );
    }
  }
  ViewPager.propTypes = {
    children: PropTypes.element,
    initialPage: PropTypes.number,
    onPageScroll: PropTypes.func,
    onPageScrollStateChanged: PropTypes.func,
    onPageSelected: PropTypes.func,
    style: PropTypes.object,
    scrollEnabled: PropTypes.bool,
    accessibilityLabel: PropTypes.string,
    testID: PropTypes.string,
  };

  return ViewPager;
});

shubhnik avatar Aug 09 '22 11:08 shubhnik

Thank you very much, @shubhnik . I needed to change setPage to

    setPage(selectedPage: number) {
      this.setState({ page: selectedPage });
      this.props.onPageSelected({ nativeEvent: { position: selectedPage } } as Partial<PagerViewOnPageSelectedEvent>);
    }

to make it working :)

helferleinsoftware avatar Nov 16 '22 20:11 helferleinsoftware

Thanks @shubhnik and @helferleinsoftware.

In my case it was useful to define setPageWithoutAnimation as well:

  setPageWithoutAnimation(selectedPage) {
    this.setState({ page: selectedPage });
    this.props.onPageSelected({ nativeEvent: { position: selectedPage } });
  }

I also got this warning:

 Warning: Failed prop type: Invalid prop `children` of type `array` supplied to `ViewPager`, expected a single ReactElement.

which can be avoided by adjusting the prop types for children:

-- children: PropTypes.element,
++ children: PropTypes.node,

MarianPalkus avatar Jun 08 '23 09:06 MarianPalkus