mercredi 7 avril 2021

How to test custom hooks with event listener inside useEffect?

I'm using react native and jest to create my tests. I'm facing problems to test an event listener that listens to url events from expo-linking. This event listenner is inside an useEffect hook.

Below is the code from my custom hook with my useEffect and an event listener inside:

useEffect(() => {
    isMounted.current = true;
    Linking.addEventListener('url', async () => {
      try {
        if (!navigation.isFocused() || !isMounted.current) return;

        setIsLoading(true);

        const response = await api.get('sessions/auth/success');

        if (!response.data) return;

        console.log('aqui');

        const { notRegisteredUser, token } = response.data;

        api.defaults.headers.authorization = `Bearer ${token}`;

        if (notRegisteredUser && token) {
          setIsLoading(false);
          navigation.navigate('BirthDateScreen');

          dispatch(
            updateUser({
              ...notRegisteredUser,
            }),
          );
        }
      } catch (err) {
        Alert.alert('Error', `${translate('loginRegisterError')}: `, err);
      }
    });
    return () => {
      isMounted.current = false;
    };
  }, [dispatch, navigation]);

In my test file I have the following mocks:

jest.mock('expo-linking', () => {
  return {
    addEventListener: (event: string, callback: () => void) => callback(),
  };
});

jest.mock('@react-navigation/native', () => {
  return {
    useNavigation: () => ({
      isFocused: mockedNavigationFocus,
      navigate: mockedNavigation,
    }),
  };
});

jest.mock('react-redux', () => {
  return {
    useDispatch: jest.fn(),
  };
});

jest.mock('../../../store/modules/user/actions', () => {
  return {
    updateUser: jest.fn(),
  };
});

jest.mock('i18n-js', () => {
  return {
    locale: 'en',
    t: () => 'any key',
  };
});

Finally this is how my test looks in my first try:

it('should pass the test', async done => {
  mockedNavigationFocus.mockImplementation(() => true);

  apiMock.onGet('sessions/auth/success').reply(200, {
    notRegisteredUser: { name: 'Logan' },
    token: '123',
  });

  render(<LoginScreen />);

  
  await waitFor(() =>
    expect(mockedNavigation).toHaveBeenCalledWith('BirthDateScreen'),
  );

  done();
});

In my second try this is how my test looked (I used renderHooks from @testing-library/react-hooks):

it('should pass the test', async ()  => {
  mockedNavigationFocus.mockImplementation(() => true);

  apiMock.onGet('sessions/auth/success').reply(200, {
    notRegisteredUser: { name: 'Logan' },
    token: '123',
  });


  const { result, waitForValueToChange } = renderHook(() => useLoginButton());

  const { isLoading } = result.current;

 
  await waitForValueToChange(() => isLoading);
  await waitForValueToChange(() => isLoading);

  expect(mockedNavigation).toHaveBeenCalledWith('BirthDateScreen');

});

With both tests I get the following error: test error

Another error I get is that my callback function inside useEffect runs many times before it stops and this does not happen when I am not testing.

Does anyone knows how can I write this test?

Aucun commentaire:

Enregistrer un commentaire