React Native testing go back navigation behaviour

Viewed 282

I am trying to test my go back button, but I keep getting errors (check at the bottom). Any tips, ideas would be helpful or other way to test if there isn't something rendered. I already tried doing something along the lines of findByTestId and then expect it to throw but that didn't work for me.

I am using

"react-native": "0.65.1",
"@react-navigation/bottom-tabs": "^6.0.9",
"@react-navigation/devtools": "^6.0.4",
"@react-navigation/native": "^6.0.6",
"@react-navigation/stack": "^6.0.11",

My test:

test("UserProfileScreen navigate to UserProfilePersonalInformation and back", async () =>
{
    const component = render(
        <NavigationContainer>
            <UserProfileStack />
        </NavigationContainer>,
    );

    // Get button that navigates to other screen
    const btnUserProfilePersonalInformationNavigate = await component.findByTestId(
        tiConfig.USER_PROFILE_BUTTON_PERSONAL_INFORMATION);

    // Test if the screen we are about to navigate doesn't exist
    expect(component.queryByTestId(
        tiConfig.USER_PROFILE_PERSONAL_INFORMATION_SCREEN)).toBeFalsy();

    fireEvent.press(btnUserProfilePersonalInformationNavigate);

    // Get the screen we navigated to
    const userProfilePersonalInformationScreen = await component.findByTestId(
        tiConfig.USER_PROFILE_PERSONAL_INFORMATION_SCREEN);

    // Check if it exists, but maybe not necessary as findByTestId would crash?
    expect(userProfilePersonalInformationScreen).not.toBeNull();

    // Get back button
    const btnBack = await component.findByTestId(
        tiConfig.BACK_BUTTON);

    fireEvent.press(btnBack);

    // Test if the screen we were in is still being rendered
    // We can't use findByTestId as that one will crash
    await waitFor(() => expect(component.queryByTestId(
        tiConfig.USER_PROFILE_PERSONAL_INFORMATION_SCREEN)).toBeFalsy());
});

Error I am getting:

    Couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'? See https://reactnavigation.org/docs/getting-started for setup instructions.

      107 |                     fireEvent.press(btnBack);
      108 |
    > 109 |                     await waitFor(() => expect(component.queryByTestId(
          |                           ^
      110 |                             tiConfig.USER_PROFILE_PERSONAL_INFORMATION_SCREEN)).toBeFalsy());
      111 |             });
2 Answers

Not sure for your exact requirements to go back to the previous screen, but the simplest way to do so is either using the navigation prop or by importing;

Method 1:

onPress={() => this.props.navigation.goBack()}

Method 2:

function ProfileScreen({ navigation: { goBack } }) {
  return (
    <View>
      <Button onPress={() => goBack()} title="Go back from ProfileScreen" />
    </View>
  );
}

The navigation in RN, keeps a history as you navigate from one screen to another. If you wish to go back to the screen just before the screen you are on, use the goBack().

If you want to navigate from screen D back to screen A directly, use navigate().

Ref: https://reactnavigation.org/docs/navigation-prop/#goback

I had a similar problem and the approach below resolved the issue. It might not resolve yours directly, but hopefully it can still be helpful.

My test case that got hit with the "Couldn't find a navigation context" error

test('press the "Cancel" button, go back to the prev screen', async () => {
  await reachCurrentScreen();
  fireEvent.press(screen.queryByText('Cancel'));
  expect(screen.queryByText('Some Text on Current Screen')).toBeFalsy();
});

The error was resolved by this

import FakeTimers from '@sinonjs/fake-timers';

const clock = FakeTimers.install();

test('press the "Cancel" button, go back to the prev screen', async () => {
  await reachCurrentScreen();
  fireEvent.press(screen.queryByText('Cancel'));
  await clock.tickAsync(1000);
  expect(screen.queryByText('Some Text on Current Screen')).toBeFalsy();
});

I don't know the exact reason why adding a bit of wait resolves the problem, but my speculation is that immediately after we trigger the test to go to the previous screen, the test is in a limbo state. We need to wait a bit (albeit fake wait) for the test's state to stabilize before making a new query.

Related