1

I'm trying to implement react native test library with jest to my app.

For now I have a problem with the navigation on my component.

When I'm running the test, I've got an error :

TypeError: navigation.setOptions is not a function

Here is my component:

const initialState: StateTypes = {
  toShowAsGridLayout: false,
  isLoadingMoreContacts: false
};

export const Main: FC<Props> = ({ navigation }) => {
  const dispatch = useDispatch();
  const { data } = useSelector((state: RootState) => state.appReducer);

  const [state, setState] = useState<StateTypes>(initialState);

  useLayoutEffect(() => {
    navigation.setOptions({
      title: state.isLoadingMoreContacts ? strings.LOADING : strings.ALL_CONTACTS + ' - ' + data.length,
      headerRight: () => (
        <TouchableOpacity style={styles.changeLayoutButton} onPress={changeLayout}>
          <Text style={styles.changeLayoutText}>{state.toShowAsGridLayout ? strings.LIST : strings.GRID}</Text>
        </TouchableOpacity>
      )
    });
  }, [state.isLoadingMoreContacts, state.toShowAsGridLayout])

  return (
    <View style={styles.container}>
      {renderLayout()}
    </View>
  );
};

Here is a router:

const SplashStack = createStackNavigator();
const MainStack = createStackNavigator();

export const RootNavigator = () => {
  const { isDataLoading } = useSelector((state: RootState) => state.appReducer);

  return (
    isDataLoading
      ? <SplashStack.Navigator>
        <SplashStack.Screen name={'SplashStack'} component={Splash} />
      </SplashStack.Navigator>
      : <MainStack.Navigator>
        <MainStack.Screen name={'Main'} component={Main} />
        <MainStack.Screen name={'ContactDetails'} component={ContactDetails} />
      </MainStack.Navigator>
  );
};

And a test itself:

import React from 'react';

import { render } from '@testing-library/react-native';
import { Main } from '../Main';
import * as redux from 'react-redux';
import strings from '../../constants/strings';
import mocks from '../../mocks';

describe('dispatch mock', () => {
  it('should dispatch mock', () => {
    const useDispatchSpy = jest.spyOn(redux, 'useDispatch');
    const useSelectorSpy = jest.spyOn(redux, 'useSelector');
    const mockDispatchFn = jest.fn();
    useDispatchSpy.mockReturnValue(mockDispatchFn);
    useSelectorSpy.mockReturnValue({ data: mocks });

    const { getByText } = render(<Main navigation={({})} />);
    getByText(strings.ALL_CONTACTS);
  });
});

How can i fix this error ? What should I pass to navigation props in line :

const { getByText } = render(<Main navigation={({})} />);
Michael
  • 429
  • 5
  • 22

3 Answers3

0

You need to pass object with setOptions method.

const { getByText } = render(<Main navigation={
  {
    setOptions: (props: { title: string, headerRight: React.FC }) => void
  }
} />);

This answer might be relevant

  • 1
    hi, I got the same problem, but your solution, even though it looks good, doesn't work. Namely, I get an error with `setOptions`: `Type '(props: { headerRight: React.FC;}) => any' is not assignable to type ...`. Also, at the last curly brace, I get `Expression expected.`. I would really appreciate if you could solve this. Thanks. – Fotios Tsakiris Feb 16 '22 at 07:15
  • @FotiosTsakiris hi, could you please share your minimum reproducible example – captain-yossarian from Ukraine Feb 16 '22 at 08:28
  • please have a look [here](https://stackoverflow.com/questions/71138351/testing-react-native-app-how-to-fix-navigation-setoptions-is-not-a-function). It's not reproducible, but you get more info. Thanks – Fotios Tsakiris Feb 16 '22 at 08:34
0

For my purpose solution was very easy, I just added ?. at the end of setOptions

useLayoutEffect(() => {
    navigation.setOptions?.({ // <--- CHANGED HERE 

      title: state.isLoadingMoreContacts ? strings.LOADING : strings.ALL_CONTACTS + ' - ' + data.length,
      headerRight: () => (
        <TouchableOpacity style={styles.changeLayoutButton} onPress={changeLayout}>
          <Text style={styles.changeLayoutText}>{state.toShowAsGridLayout ? strings.LIST : strings.GRID}</Text>
        </TouchableOpacity>
      )
    });
  }, [state.isLoadingMoreContacts, state.toShowAsGridLayout])
Mikołaj Wittbrodt
  • 399
  • 1
  • 4
  • 18
0

I am also face the same issue. With below code changes I have resolved my issue.

For more info please check this answer

    const createTestProps = (props: Object) => ({
          navigation: {
            navigate: jest.fn(),
            setOptions: jest.fn()
          },
          ...props
        });
    const props = createTestProps({});    
    const { toJSON } = render(<MainScreen {...props} />); 

enter image description here

You have to mock the properties and function that your are using in your screen has to be mocked like above. For more info please check this answer

Ananth Cool
  • 125
  • 8