react-native-testing-library: Error: Unable to find an element with text: "My text", while that text exist.

Describe the bug

getByText can find element if text render component with text as not children (See “Steps to Reproduce” below)

-const Trans = p => p.i18nKey; // can find (error)
+const Trans = p => p.children; // everything ok

Expected behavior

I see in debug output (see screenshot below) that text exists! But API can find it( It should work properly!

Steps to Reproduce

    test('test',  () => {
      const { Text } = require('react-native');
      const { render } = require('@testing-library/react-native');
      const Trans = p => p.i18nKey;

      const screen = render(
        <Text>
          <Trans i18nKey="My text" />
        </Text>,
      );

      screen.debug();

      expect(screen.getByText('My text')); // Error: Unable to find an element with text: My text
    });

Screenshots

Output:

image

Versions

  npmPackages:
    @testing-library/react-native: ^7.2.0 => 7.2.0 
    react: 17.0.2 => 16.13.1 
    react-native: 0.66.4 => 0.66.4 
    react-test-renderer: 17.0.2 => 17.0.2 

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 36 (15 by maintainers)

Most upvoted comments

@AugustinLF easy to reproduce but not easy to fix?

@AugustinLF I know that this is a feature request. But is is possible to have the .debug() run automatically when a getBy* matcher fails? Recall react-testing-library had that and it was really useful to see how the component looks when debugging why it can’t get a hold of it.

I (or whoever’s interested in) just need to take the time, but I don’t think it should be very hard. I’ll try to do that later this week, or next week

@pierrezimmermannbam I refactored back into toJSON method on ReactTestInstance and I’ve submitted PR: https://github.com/facebook/react/pull/25329

Let’s see how the review goes…

Not sure this is easy to fix, the type of <Trans i18nKey=“text”/> isn’t string so the getChildrenAsText method is looking for strings in its children but it doesn’t have any. For this to work it would require to be able to tell that it renders as a string but i’m not sure it’s easily doable.

@retyui this can be fixed on your end by mocking the Trans component like this

jest.mock('./Trans', () => {
  const { Text } = require('react-native');

  return {
    Trans: (props) => {
      return <Text>{props.i18nKey}</Text>;
    },
  };
});

However if there is an effective way to fix this it would be very nice, or maybe it could be documented somewhere

@pierrezimmermannbam I’ve started with implementing toJSON as a method on ReactTestInstance, however I’ve encountered two issues that made free function a more natural approach:

  1. testInstanceToJSON is recursive, as it needs to call itself for child nodes, and that requires ability to pass node on which to run the function
  2. testInstanceToJSON accepts either ReactTestInstance | string as sometimes children might be string. Particular there was one existing unit test for renderer.toJSON() method that I could not duplicate as renderer.root.toJSON() because renderer.root was actually a string.

Thinking about this after your comment, I think that point 1 might be solvable by invoking child.toJSON() from testInstance.toJSON() and adding child type checks for string values. I will review my code tomorrow to see which feels like a better API.

@mdjastrzebski The following implementation seems to work

toJSON(): ReactTestRendererNode | null {
    if (typeof this._fiber.type === 'string') {
      return toJSON(this._fiber.stateNode);
    }

     return toJSON(this._fiber.child.stateNode);
  }

You were right it does look like it only works on host component

@nanda-kumar-k the posted errors seems to concern React Testing Library, and not React Native Testing Library, as the stack trace mentions @testing-library/dom and you use HTML elements.

@mdjastrzebski I think it is a very interesting approach, it could event be possible to still use the reactTestInstance.findAll method and then only use the ReactTestRendererJSON type for the getChildrenAsText method which would be a way less significant change. I’ll try it and if it works ask on the React test renderer repo whether it is a change they are willing to make

@AugustinLF @thymikee I have made some progress on this, I opened a draft pr but I’ve hit an issue. The queries by text work fine using the json representation but there are some problems.

First, I still need to get the ReactTestInstance from the renderer.root because the instances in the ReactTestRendererTree type are in fact not of type ReactTestInstance and do not have parents which is problematic for the fireEvent. I was able to fix this by finding the ReactTestInstance matching the result of the search though I’m not entirely sure it is reliable.

The second issue is with within. For within to work, the queries return type and what is used to build the api need to be of the same type and I broke that by building queries with a ReactTestRenderer type and returning the same type as before. It would be doable to have the following type :

type QueryResult  = ReactTestInstance & { tree : ReactTestRendererTree }

However this would require to change not only the byText queries but all of them, which is more complex but shouldn’t be too much of an issue if well refactored. It would change the type of the api but not cause a breaking change as we’d be only adding a new field so it shouldn’t be too problematic.

So before going further I wanted to have your inputs on this and make sure the implementation I started was what you also had in mind