Jest React Native, testing / mocking? onLayout

Viewed 332

I currently have this relatively simply screen in React, that I need to test in Jest, however, I'm not terribly familiar with the library and this is what I've got so far.

Things I'd like to test?

The onLayoutEvent. Does this need mocked? Showing / hiding of the spinner on page. At the moment, it finds it here: (which is fine)

expect(spinner).not.toBeNull();

But still finds it after the event call, whereas in actual fact it is hidden by state after the event fires.

Loading for this component occurs from a call to setLoading in it's children. I've also zero clue on how to test this. Any assistance on what / how to test this component appreciated.

describe('Login', () => {
  test('it should render', () => {
    renderWithStore(<LoginStarter />);
  });

  test('it should fire a layout event', async () => {
    const { getByTestId } = renderWithStore(<LoginStarter />);
    const view = await getByTestId('loginId');
    const spinner = await getByTestId('loginSpinner');
    expect(spinner).not.toBeNull();

    act(() =>
      fireEvent(view, 'onLayout', {
        nativeEvent: { layout: { width: 500 } },
      }),
    );
  });
});

The core component

 const [widthLoading, setWidthLoading] = useState(true);
  const [loading, setLoading] = useState(false);
  const theme = useTheme();
  const [containerWidth, setContainerWidth] = useState<number>();

  const onLayout = (event: LayoutChangeEvent) => {
    const { width } = event.nativeEvent.layout;
    setContainerWidth(width);
    setWidthLoading(false);
  };

  const tabs = [
    {
      title: 'Login',
      component: () => LoginScreen({ componentId: props.componentId, setLoading }),
      testID: 'tab1',
    },
    {
      title: 'Sign up',
      component: () => RegisterScreen({ componentId: props.componentId, setLoading }),
      testID: 'tab2',
    },
  ];

  const wrapperStyle = {
    flex: 1,
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    opacity: loading ? 0.5 : 1,
  };

  const loadingStyle = {
    alignItems: 'center',
    justifyContent: 'center',
    flex: 1,
    position: 'absolute',
  };

  return (
    <View testID="loginId" onLayout={onLayout} style={wrapperStyle as StyleProp<ViewStyle>}>
      {loading && (
        <View style={loadingStyle as StyleProp<ViewStyle>}>
          <ActivityIndicator size="large" color={theme.spinner} />
        </View>
      )}
      <View>
        {!widthLoading && containerWidth ? (
          <Tabs style="light" tabWidth={Math.round(containerWidth / 2)} tabs={tabs} tabsScrollEnabled />
        ) : (
          <ActivityIndicator testID="loginSpinner" size="large" color={theme.spinner} />
        )}
      </View>
    </View>
  );
1 Answers

I'm struggling with testing onLayout as well. One thing I will point out is that in your code

act(() =>
  fireEvent(view, 'onLayout', {
    nativeEvent: { layout: { width: 500 } },
  }),
);

onLayout should actually just be layout. I dug into the library code and found this:

const toEventHandlerName = eventName => `on${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`;

Also, you don't need to wrap fireEvent with act as it is wrapped by act by default. As per the docs:

Act: Useful function to help testing components that use hooks API. By default any render, update, fireEvent, and waitFor calls are wrapped by this function, so there is no need to wrap it manually.

All that said, I still can't get onLayout to fire.

Edit:
The above actually was triggering the onLayout for me. It was the was I was spying on useState that was my issue.

Related