Skip to content

Nested custom Text componentns not working correctly #515

Closed

Description

@mym0404

Describe the bug

If custom Text components are nested and find with text, then the wrong other element is retrieved.

Steps to Reproduce

const CustomText = ({ children, ...props }: PropsWithChildren<TextProps>) => {
  return <Text {...props}>{children}</Text>;
};
CustomText.displayName = 'CustomText';

const NestedText = () => {
  return (
    <View>
      <CustomText testID="outer">
        <CustomText testID="inner1"></CustomText>
        <CustomText testID="inner2">Hello</CustomText>
      </CustomText>
    </View>
  );
};

it('wrong result', () => {
  const { getByText, debug } = render(<NestedText />);

  const element = getByText(/Hello/);
  debug();

  expect(element.props.testID).toBe('inner2');
});
  console.log
    <View>
      <Text
        testID="outer"
      >
        <Text
          testID="inner1"
        />
        <Text
          testID="inner2"
        >
          Hello
        </Text>
      </Text>
    </View>

      at debugDeep (node_modules/@testing-library/react-native/build/helpers/debugDeep.js:19:13)


Error: expect(received).toBe(expected) // Object.is equality

Expected: "inner2"
Received: "outer"
<Click to see difference>


    at Object.<anonymous> (/Users/mj/Desktop/Turing/MathKing/src/components/shared/AiTeacherCommentText.test.tsx:79:32)
    at Object.asyncJestTest (/Users/mj/Desktop/Turing/MathKing/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:100:37)
    at /Users/mj/Desktop/Turing/MathKing/node_modules/jest-jasmine2/build/queueRunner.js:47:12
    at new Promise (<anonymous>)
    at mapper (/Users/mj/Desktop/Turing/MathKing/node_modules/jest-jasmine2/build/queueRunner.js:30:19)
    at /Users/mj/Desktop/Turing/MathKing/node_modules/jest-jasmine2/build/queueRunner.js:77:41
    at processTicksAndRejections (internal/process/task_queues.js:93:5)

Versions

  npmPackages:
    @testing-library/react-native: 7.0.2 => 7.0.2 
    react: 16.13.1 => 16.13.1 
    react-native: 0.63.2 => 0.63.2 
    react-test-renderer: 16.13.1 => 16.13.1 

Activity

  1. mym0404 commented on Aug 19, 2020

    @mym0404
    Author

    One question more, If I set displayName of CustomText like CustomText.displayName = 'customText'; why debug just show it as <Text> ?

  2. thymikee commented on Aug 19, 2020

    @thymikee
    Member

    Yup, this is an edge case we don't handle at the moment. Please feel free to send a PR fixing that :)
    Related work: #489

    One question more, If I set displayName of CustomText like CustomText.displayName = 'customText'; why debug just show it as ?

    It's because debug will only show host (native) components and CustomText is not one of these. You can call debug.shallow() using a shallow renderer to display custom component hierarchy to some extent.

  3. mym0404 commented on Aug 19, 2020

    @mym0404
    Author

    Ok I will try Thank you :)

  4. mym0404 commented on Aug 20, 2020

    @mym0404
    Author

    This bug is related to a recursive way of find text node in this library.
    I can temporary fix like the following.

    const matches = testingLib.getAllByText(text);
    const element = matches[matches.length - 1];
  5. mikeduminy commented on Oct 11, 2020

    @mikeduminy
    Contributor

    Trying to match across nested nodes in an attempt to match what the user sees is an issue for several of our tests too. Most notably when we try to fire an event on an inner Text node.

    Right now I can't think of a way to achieve this nested matching and the specific matching behaviour beforehand. Landing #573 would at least give us a way to implement our own overrides, but long term WDYT of adding a matching option to ByText queries - something like a smart mode (for the current behaviour) and a basic/legacy/simple mode? @thymikee

    We would have to think about how the exact texting matching option in #554 affects this, if at all.

  6. thymikee commented on Oct 12, 2020

    @thymikee
    Member

    We could still support nested Texts by traversing up the tree once the original query fail. If the direct parents are Text nodes, we could go up to the first one, construct the string and match using the specified matcher/normalizer. What do you think?

  7. mikeduminy commented on Oct 12, 2020

    @mikeduminy
    Contributor

    I'm not sure I follow. Is this the kind of algorithm you had in mind?

    1. If current node is a Text node, build string from children (string and number) and run through matcher / normalize. Return node if match.
    2. If parent is a Text node, increment stepUp counter, build string from children of parent (and their children to a depth of stepUp counter) and run through matcher / normalizer. Return if match.
    3. Goto 3 for as long as parent is a Text node and there is no match.
  8. thymikee commented on Oct 12, 2020

    @thymikee
    Member

    Yea, something like this.

  9. mikeduminy commented on Oct 13, 2020

    @mikeduminy
    Contributor

    I tried it and found the following problems:

    It is somewhat complicated by the native view being wrapped with a React.forwardRef, determining if a parent or child is actually a text element is not cleanly possible.

    It matches 2 instances in this case:

    <Text>
      <CustomText>Hello</CustomText> <CustomText>World!</CustomText>
    </Text>

    Reversing the search method like this matches the lowest level element, not its parent, because when searching the instance.find is trying to find elements for which getNodeByText returns true, and using this algorithm it will only return true when evaluating the lowest node.

  10. thymikee commented on Oct 13, 2020

    @thymikee
    Member

    We only care about host (native) components, with type of "string". This will eliminate any ForwardRefs, Functions etc.

    I was rather thinking of building a separate tree of host components while searching for the lowest Text node. If the search fails we have the tree which we can then traverse up for finding the Text that's furthest away from the leaf text node, and then construct the text based on its children.

  11. mikeduminy commented on Oct 13, 2020

    @mikeduminy
    Contributor

    This sounds like some effort needs to go into this feature. I personally don't need it, what I need is parity with v6 for getByText, which is why I suggested bringing back the simple get text node implementation and having the current smarter implementation behind a flag.

  12. AugustinLF commented on Mar 28, 2022

    @AugustinLF
    Collaborator

    Fixed (I believe as part of #489).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions