testing-library / native-testing-library

🐳 Simple and complete React Native testing utilities that encourage good testing practices.
https://native-testing-library.com
MIT License
516 stars 44 forks source link

`Got unexpected null` when using FlatList's ref #108

Closed demchenkoalex closed 4 years ago

demchenkoalex commented 4 years ago

Relevant code or config:

import React, {useRef} from 'react';
import {FlatList, TextInput, Button, SafeAreaView} from 'react-native';

const App = ({onPress}) => {
  const flatList = useRef(null);
  const textInput = useRef(null);

  const handlePress = () => {
    onPress();
    textInput.current && textInput.current.focus();
    flatList.current && flatList.current.scrollToOffset({offset: 0});
  };

  return (
    <SafeAreaView style={{flex: 1}}>
      <FlatList ref={flatList} />
      <TextInput ref={textInput} />
      <Button
        accessibilityLabel="Press Me"
        title="Press Me"
        onPress={handlePress}
      />
    </SafeAreaView>
  );
};

export default App;
import {fireEvent, render} from '@testing-library/react-native';
import React from 'react';
import App from '../App';

test('it handles onPress', () => {
  const onPress = jest.fn();
  const {getByLabelText} = render(<App onPress={onPress} />);

  const button = getByLabelText('Press Me');
  fireEvent.press(button);
  expect(onPress).toHaveBeenCalled();
});

What you did:

My use case was testing onPress prop passed to a component, and this prop was called in a local handlePress function. This function also calling FlatList's scrollToOffset using ref. (I have send button in a chat view, where send button will send a message and scroll messages to this latest one)

What happened:

Got unexpected null

       9 |     onPress();
      10 |     textInput.current && textInput.current.focus();
    > 11 |     flatList.current && flatList.current.scrollToOffset({offset: 0});
         |                                          ^
      12 |   };
      13 | 
      14 |   return (

Note that other refs are working correctly (I tried simple View and TextInput).

Reproduction:

I have added a drop in replacement for the App.js and __tests__/App-test.js files in a new React Native project above. I also did the same in this fork https://github.com/demchenkoalex/ntl-sample but please note I didn't update React Native in a fork because migration to 0.61 is required. In my project I use latest version as specified in this issue.

Problem description:

Tests will pass if I will remove the line which scrolls FlatList, but this is a requirement, can't do that.

Suggested solution:

Tried to modify my code to add null checks, making another function which will call scrollToOffset and other probably stupid stuff, I don't think I can suggest anything :/

Can you help us fix this issue by submitting a pull request?

N/A

WesleyHovick commented 4 years ago

I am experiencing this same issue with FlatList refs and scrolling.

nickthepayne commented 4 years ago

I'm not sure if your issue has the same cause, but I was getting the same error message when using native-base tabs, which also use scrolling. The problematic piece of code was this (inside native-base):

setTimeout(() => this.scrollView.scrollTo({...}))

When I call jest.useFakeTimers() inside my test the error disappears. Might be worth a try.

demchenkoalex commented 4 years ago

@enpayne thanks for the suggestion, however this issue is reproducible in a clean project with no dependencies other than native-testing-library. I did try jest.useFakeTimers() in my project but it didn't help.

kulyk commented 4 years ago

I got the same problem, but with react-native-interactable. I guess the problem here is the same.

When we call a native component's ref method like scrollTo, it actually sends a message to the native component through a bridge. Not sure about native-testing-library internals, but I doubt it mocks all the native stuff, the bridge, etc. The component is present in JS, but not in the native realm, so it fails to access it for sending a message

In my particular case, I've just mocked the whole react-native-interactable like jest.mock('react-native-interactable'). So, you can try to mock the FlatList

murtinha commented 4 years ago

Same problem here. Im using SectionList

Any solutions @demchenkoalex ?

murtinha commented 4 years ago

Got it to work using this mock jest.mock('react-native/Libraries/Components/ScrollResponder');

props to @linzera