lucasferreira / react-native-flash-message

React Native flashbar and top notification alert utility
MIT License
1.42k stars 154 forks source link

How to test FlashMessage and show message using the react native testing library #165

Open Lois-Varghese opened 3 years ago

Lois-Varghese commented 3 years ago

"@testing-library/react-native": "^7.2.0",

import React, {useEffect} from 'react';
import RNBootSplash from 'react-native-bootsplash';
import {StatusBar} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import FlashMessage from 'react-native-flash-message';
import Navigation from '@navigation/navigation';
import theme from '@config/themes';
import {Provider} from 'react-redux';
import Store from '@src/redux/store';

export default function App() {
  useEffect(() => {
    RNBootSplash.hide({fade: true});
    StatusBar.setHidden(true);
  }, []);

  const textStyle = {
    fontFamily: theme.fonts[0],
    fontSize: 16,
  };

  return (
    <Provider store={Store}>
      <NavigationContainer>
        <SafeAreaProvider>
          <Navigation />
        </SafeAreaProvider>
      </NavigationContainer>
      <FlashMessage position="bottom" animated={true} titleStyle={textStyle} />
    </Provider>
  );
}

In the snapshot test I get Flash Message as an invalid element

lucasferreira commented 3 years ago

Hi @Lois-Varghese

I never had the opportunity to do that on my own, but some people relate in other issues:

https://github.com/lucasferreira/react-native-flash-message/issues/147 https://github.com/lucasferreira/react-native-flash-message/issues/113

I guess that you need to do something with testID prop.

Lois-Varghese commented 3 years ago

Hi @lucasferreira

I solved my issue by mocking the showMessage and other functions I was using from the library, using Jest and I also had to mock FlashMessage component from react-native-flash-message library and then my tests were passing. I wrote the tests using testing-library/react-native.

moiseshilario commented 3 years ago

Just for reference how to make a basic mock, that worked for me:

jest.mock('react-native-flash-message', () => ({
  hideMessage: () => jest.fn(),
  showMessage: () => jest.fn(),
  __esModule: true,
  default: jest.fn().mockReturnValue(() => <></>),
}));
rodrigodiasf1984 commented 1 year ago

Hello @lucasferreira can you please reopen this issue? I'm facing the same problem, I've got the two functions inside the showMessage that inst covered:

this is my jest.setup:

jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');

jest.mock('@react-native-community/netinfo', () => mockRNCNetInfo);
jest.mock('rn-swipe-button', () => 'SwipeButton');
jest.mock('@react-native-async-storage/async-storage', () =>
  require('@react-native-async-storage/async-storage/jest/async-storage-mock'),
);

jest.mock('aws-amplify');

jest.mock('react-native-maps', () => {
  const { View } = require('react-native');
  const MockMapView = props => {
    return <View>{props.children}</View>;
  };
  const MockMarker = props => {
    return <View>{props.children}</View>;
  };
  return {
    __esModule: true,
    default: MockMapView,
    Marker: MockMarker,
  };
});

jest.mock('react-native-flash-message', () => ({
  hideMessage: () => jest.fn(),
  showMessage: () => jest.fn(),
  __esModule: true,
  default: jest.fn().mockReturnValue(() => <></>),
}));

this is my component:

import React, { useCallback, useEffect, useState } from 'react';
import { CustomToastProps } from '../../@types';
import { showMessage } from 'react-native-flash-message';
import { CheckCircle, Info, WarningCircle } from 'phosphor-react-native';
import { theme } from '../../../tailwind.config';
import { ThemeConfig } from 'tailwindcss/types/config';
import { View } from 'react-native';

export const CustomToast = ({
  description,
  setIsToastOpen,
  title,
  type,
}: CustomToastProps) => {
  const { toast_colors, colors } = theme?.extend as ThemeConfig;

  const handleToastStatus = useCallback(() => {
    switch (type) {
      case 'success':
        return {
          icon: (
            <View testID="success-icon">
              <CheckCircle
                size={25}
                color={colors['text_black']}
                weight="bold"
                style={{ marginRight: 10 }}
              />
            </View>
          ),
          borderColor: toast_colors['border_success'],
          backgroundColor: toast_colors['background_success'],
        };
      case 'danger':
        return {
          icon: (
            <View testID="warning-icon">
              <WarningCircle
                size={25}
                color={colors['text_black']}
                weight="bold"
                style={{ marginRight: 10 }}
              />
            </View>
          ),
          borderColor: toast_colors['border_danger'],
          backgroundColor: toast_colors['background_danger'],
        };
      case 'info':
        return {
          icon: (
            <View testID="info-icon">
              <Info
                size={25}
                color={colors['text_black']}
                weight="bold"
                style={{ marginRight: 10 }}
              />
            </View>
          ),
          borderColor: toast_colors['border_info'],
          backgroundColor: toast_colors['background_info'],
        };
    }
  }, [type]);

  useEffect(() => {
    const showToast = () => {
      showMessage({
        message: title,
        description: description,
        icon: 'auto',
        style: {
          alignSelf: 'center',
          marginBottom: 80,
          width: '94%',
          borderRadius: 5,
          borderLeftWidth: 5,
          borderLeftColor: handleToastStatus()?.borderColor,
          backgroundColor: handleToastStatus()?.backgroundColor,
          alignItems: 'center',
        },
        color: colors['text_black'],
        renderFlashMessageIcon: () => handleToastStatus()?.icon ?? null,
        onHide: () => setIsToastOpen(false),
      });
    };
    showToast();
  }, []);

  return <View testID="custom-toast" />;
};

this is my test file:

import { render } from '@testing-library/react-native';
import { CustomToast } from '..';

describe('CustomToast', () => {
  const setIsToastOpen = jest.fn();

  it('should render the component with type success', async () => {
    const { container } = render(
      <CustomToast
        setIsToastOpen={setIsToastOpen}
        title="Test toast"
        description=""
        type="success"
      />,
    );
    expect(container).toBeDefined();
  });

  it('should render the component with type danger', () => {
    const { container } = render(
      <CustomToast
        setIsToastOpen={setIsToastOpen}
        title="Test toast"
        description=""
        type="danger"
      />,
    );

    expect(container).toBeDefined();
  });

  it('should render the component with type info', () => {
    const { container } = render(
      <CustomToast
        setIsToastOpen={setIsToastOpen}
        title="Test toast"
        description=""
        type="info"
      />,
    );
    expect(container).toBeDefined();
  });
});

this is the result of the running tests:

image

I've tried to do a spyOn but I'm getting this error: image

import { render, waitFor } from '@testing-library/react-native';
import { CustomToast } from '..';
import { showMessage } from 'react-native-flash-message';
import flashMessage from 'react-native-flash-message';

const renderFlashMessageIcon = jest.fn();
const onHide = jest.fn();

const mockShowMessage = jest.spyOn(flashMessage, 'showMessage');

describe('CustomToast', () => {
  const setIsToastOpen = jest.fn();

  jest.mock('react-native-flash-message', () => {
    return {
      showMessage: () => ({
        renderFlashMessageIcon: () => jest.fn(),
        onHide: () => jest.fn(),
      }),
    };
  });

  it('should render the component with type success', async () => {
    const { container } = render(
      <CustomToast
        setIsToastOpen={setIsToastOpen}
        title="Test toast"
        description=""
        type="success"
      />,
    );
    expect(mockShowMessage).toHaveBeenCalled();

    expect(container).toBeDefined();
  });

  it('should render the component with type danger', () => {
    const { container } = render(
      <CustomToast
        setIsToastOpen={setIsToastOpen}
        title="Test toast"
        description=""
        type="danger"
      />,
    );

    expect(container).toBeDefined();
  });

  it('should render the component with type info', () => {
    const { container } = render(
      <CustomToast
        setIsToastOpen={setIsToastOpen}
        title="Test toast"
        description=""
        type="info"
      />,
    );

    expect(container).toBeDefined();
  });
});

@Lois-Varghese @moiseshilario any idea how to do it? Thanks

lucasferreira commented 1 year ago

Reopened @rodrigodiasf1984,

But I can't help too much here, never done this jest kind of tests in RN.