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?

3 Answers 3

2

you can use Jest Native matchers -> https://github.com/testing-library/jest-native (built-in to testing-library since v12.4)

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

// In your test component,
...
const test = getByTestId("test-modal");
expect(test).toHaveProp("visible", true);   // test success ! 
...
Sign up to request clarification or add additional context in comments.

1 Comment

This works - but just using expect(screen.getByTestId('my-modal').toBeVisible() is semantically preferable, because, if the modal does not have visible={true}, then Jest just won't find it at all - so you can't actually assert against the false case. (In other words, expect(screen.getByTestId('my-modal').toHaveProp("visible", false) will fail when the modal is not visible.)
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,
    });
  });
});

Comments

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.

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.