How to properly test React Native Modals using Jest and Native Testing Library
Asked Answered
W

3

6

I'm having a bit of a hard time understanding how to test my modal component. I'm using the react-native-modals package and @testing-library/react-native with Jest. My component is a modal that pops up when a GraphQL error is passed to it.

./ErrorMessage.js

import React from 'react';
import PropTypes from 'prop-types';
import { Dimensions, Text } from 'react-native';
import Modal, { ModalContent, ScaleAnimation } from 'react-native-modals';
import { theme } from '../styles/theme.styles';

const ModalError = ({ error, onClose }) => {
  if (!error || !error.message) {
    return (
      <Modal visible={false}>
        <Text />
      </Modal>
    );
  }

  return (
    <Modal
      visible
      modalAnimation={
        new ScaleAnimation({
          initialValue: 0,
          useNativeDriver: true,
        })
      }
      onTouchOutside={onClose}
      swipeDirection={['up', 'down', 'left', 'right']}
      swipeThreshold={200}
      onSwipeOut={onClose}
      modalStyle={modalStyle}
      overlayOpacity={0.7}
    >
      <ModalContent>
        <Text testID="graphql-error">{error.message}</Text>
      </ModalContent>
    </Modal>
  );
};

ModalError.defaultProps = {
  error: {},
};

ModalError.propTypes = {
  error: PropTypes.object,
  onClose: PropTypes.func.isRequired,
};

export default ModalError;

const window = Dimensions.get('window');

const modalStyle = {
  backgroundColor: theme.lightRed,
  borderLeftWidth: 5,
  borderLeftColor: theme.red,
  width: window.width / 1.12,
};

My test is pretty simple so far. I just want to make sure it's rendering the modal. I'm not exactly sure what needs to be mocked out here or how to do it.

./__tests__/ErrorMessage.test.js

import React from 'react';
import { MockedProvider } from '@apollo/react-testing';
import { GraphQLError } from 'graphql';
import { render } from '@testing-library/react-native';
import Error from '../ErrorMessage';

jest.mock('react-native-modals', () => 'react-native-modals');

const error = new GraphQLError('This is a test error message.');
const handleOnCloseError = jest.fn();

describe('<ErrorMessage>', () => {
  it('should render an ErrorMessage modal component', () => {
    const { container } = render(
      <MockedProvider>
        <Error error={error} onClose={handleOnCloseError} />
      </MockedProvider>
    );
    expect(container).toMatchSnapshot();
  });
});

The error that I'm getting is...

TypeError: _reactNativeModals.ScaleAnimation is not a constructor

      18 |       visible
      19 |       modalAnimation={
    > 20 |         new ScaleAnimation({
         |         ^
      21 |           initialValue: 0,
      22 |           useNativeDriver: true,
      23 |         })

And the snapshot is only printing...

./__tests__/__snapshots__/ErrorMessage.test.js.snap

// Jest Snapshot v1, 

exports[`<ErrorMessage> should render an ErrorMessage modal component 1`] = `
<View
  collapsable={true}
  pointerEvents="box-none"
  style={
    Object {
      "flex": 1,
    }
  }
/>
`;

How can I get past this error and make a proper snapshot?

Withrow answered 13/4, 2020 at 19:23 Comment(0)
C
1

you can use this -> https://github.com/testing-library/jest-native

In react native component,
...
<Modal
        testID="test-modal"
        deviceWidth={deviceWidth}
        deviceHeight={deviceHeight}
        isVisible={isModalVisible}.  // isModalVisible = useState(true or false)
        onBackdropPress={toggleModal}
        backdropOpacity={0.5}
 >
...

In test component,
...
const test = getByTestId("test-modal");
expect(test).toHaveProp("visible", true);   // test success ! 
...
Chengteh answered 16/4, 2021 at 10:54 Comment(0)
S
1
// components/Example/index.tsx
import React, { useState } from 'react';
import { Pressable, Text } from 'react-native';
import Modal from 'react-native-modal';

const Example: React.FC = () => {
  const [isPrivacyPolicyVisible, setIsPrivacyPolicyVisible] = useState(false);

  return (
    <>
      <Pressable onPress={() => setIsPrivacyPolicyVisible(true)}>
        <Text>Privacy Policy</Text>
      </Pressable>

      <Modal
        accessibilityLabel="privacy-policy-modal"
        isVisible={isPrivacyPolicyVisible}>
        <Text>Content</Text>
      </Modal>
    </>
  );
};

export default Example;
// components/Example/index.test.tsx
import React from 'react';
import { fireEvent, render, waitFor } from '@testing-library/react-native';

import { Example } from 'components';

describe('Example Component', () => {
  it('should render privacy policy.', async () => {
    // Arrange
    const { queryByText, queryByA11yLabel } = render(<Example />);

    const button = queryByText(/privacy policy/i);
    const modal = queryByA11yLabel('privacy-policy-modal');

    // Act and Assert
    expect(button).toBeTruthy();
    expect(modal).toBeTruthy();
    expect(modal.props).toMatchObject({
      visible: false,
    });
  });

  it('should show privacy policy modal.', async () => {
    // Arrange
    const { queryByText, queryByA11yLabel } = render(<Example />);

    const button = queryByText(/privacy policy/i);
    const modal = queryByA11yLabel('privacy-policy-modal');

    // Act
    await waitFor(() => {
      fireEvent.press(button);
    });

    // Assert
    expect(modal.props).toMatchObject({
      visible: true,
    });
  });
});
Saint answered 6/1, 2022 at 17:50 Comment(0)
R
0

when you do jest.mock('react-native-modals', () => 'react-native-modals'); you're replacing the whole library with the string 'react-native-modals' thus when you use it in your component it fails. You need to return a full mocked implementation from your mock function (second argument to jest.mock). It's also possible auto-mocking may work for you which would be done by simply doing: jest.mock('react-native-modals');

Here's the docks for jest.mock() with some examples of the various ways to use it: https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options.

Redd answered 16/9, 2020 at 16:20 Comment(1)
Thank you for answering this. I apologize for such a delay to respond. I haven't been able to get back to this part of the code for quite some time. I'm really looking forward to trying your solutions. I've learned a lot about testing and mocking since I originally posted this question, so I'm sure I'll get it to work. It may still be a bit before I have a chance to come back to this. Hopefully I can mark your answer as the solution.Stonyhearted

© 2022 - 2024 — McMap. All rights reserved.