Skip to content

feat(breaking): add text match options a.k.a string precision API #554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
Nov 23, 2021

Conversation

AugustinLF
Copy link
Collaborator

@AugustinLF AugustinLF commented Sep 28, 2020

Continuation of @RafikiTiki's work of #541

Summary

This does two things, adding some options to the queries (see initial PR), and remove matching strings spread in several levels of text. Namely the following example will not be valid anymore.

render(
      <Text>
        Hello <CustomText>World!</CustomText>	
      </Text>	
    ).queryByText('Hello World!')	
  ).toBeTruthy();

This is a breaking change, but it aligns the APIs and behaviour with @testing-library/react.

Closes #541
Closes #514

@AugustinLF
Copy link
Collaborator Author

@thymikee is there anything I can do to help this PR to move forward?

@@ -48,7 +48,7 @@ test('queryByText not found', () => {
).toBeFalsy();
});

test('queryByText nested text across multiple <Text> in <Text>', () => {
test('queryByText does not match nested text across multiple <Text> in <Text>', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we're regressing this functionality to be compatible with Testing Library, which also doesn't support this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I checked, testing library doesn't match in this case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this PR change this functionality? Finding the text in nodes still recurses through children

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I must be missing something but how does the logic added to getNodeByText end up not matching in this case? As I see it the the logic in getChildrenAsText will return ['Hello', ' ', 'World', '!'] which when joined will be "Hello World!", which is an exact match for what you are looking for.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the change is to keep matching component with multiple children, but not <Text>Hello <Text>World</Text></Text, because now getChildrenAsText don't read its children's children

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, so if the test render looked like this:

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

Would this then match the outer text?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This currently match is the current released version, but wouldn't match in my current implementation. It's the topic of discussion in #554 (comment)

Copy link
Member

@thymikee thymikee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before merging, I need to make sure that @RafikiTiki gets attribution for his work. Since there are 2 breaking changes here now, I'm considering releasing it in a major update

@AugustinLF
Copy link
Collaborator Author

Yes, I think this should be a major release. I only see the recursive matching as breaking (don't know which other one you're talking about) though. I can help with the release notes if needed.

await expect(
findByPlaceholderText('Placeholder Text', options)
findByPlaceholderText('Placeholder Text', {}, options)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the second breaking change I mentioned

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could provide an easy migration path by looking for waitFor options (timeout, interval) also in the queryOptions only for findBy queries and only till next major release. While displaying a console warning to inform the developer. Wdyt?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea that I missed, I fixed it in the latest commit d2f9f33

@thymikee
Copy link
Member

thymikee commented Oct 7, 2020

FYI, there's an ongoing discussion in DOM Testing Library regarding matching nested text: testing-library/dom-testing-library#473 (comment). Also, seems like getByLabelText already behaves similar to what we have today.

Hope you understand why I'm so hesitant merging this. We're likely regressing a use case that we'd eventually had to add anyway. I'd like to think of a way to not break matching nested nodes using exact: false

Copy link
Member

@mdjastrzebski mdjastrzebski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow a lot of good code! 👍

I've added some comments as there seems to be some minor errors (e.g. typescript types) and opportunities to improve readability/code structure.

</View>
);

expect(getByText('Text and whitespace')).toBeTruthy();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that one 👍

Comment on lines 107 to 110
expect(queryByTextFirstCase('My text')).toBe(null);
expect(
render(
<Text>
Hello <CustomText>World!</CustomText>
</Text>
).queryByText('Hello World!')
).toBeTruthy();
queryByTextSecondCase('My text', { exact: false })?.props.nativeID
).toBe('2');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does seem strange a bit, that we use one component structure with the default exact: true and other component structure with exact: false. If we were to contast exact true/false IMO we should use the same component structure.

Looks like we should do one of the following (in order of preference):

  1. merge queryByTextFirstCase + queryByTextSecondCase
  2. add queryByTextFirstCase + exact: false & queryByTextSecondCase + exact: true
  3. split the test into two unreleated tests

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do overall believe that tests should be restructured, a lot of cases are tested only because the internal APIs are the same (a lot of edge cases for findByText are only tested in getByText for instance), so I'd like to have more tests, and make them more verbose. But I believe it's out of the scope of this PR.

But this specific case makes sense, I'll follow your suggestion, I got a bit lazy and monkey patched some tests without really reconsidering them.

).toBe(null);
});

test('queryAllByText does not match several times the same text', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

try {
return instance.find((node) => getNodeByText(node, text));
const results = getAllByText(instance)(text, options);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines 134 to 168
findByText: (
text: string | RegExp,
waitForOptions?: WaitForOptions
text: TextMatch,
waitForOptions?: WaitForOptions,
options?: TextMatchOptions,
) => FindReturn;
findByPlaceholderText: (
placeholder: string | RegExp,
waitForOptions?: WaitForOptions
placeholder: TextMatch,
waitForOptions?: WaitForOptions,
options?: TextMatchOptions,
) => FindReturn;
findByDisplayValue: (
value: string | RegExp,
waitForOptions?: WaitForOptions
value: TextMatch,
waitForOptions?: WaitForOptions,
options?: TextMatchOptions,
) => FindReturn;
findByTestId: (testID: string | RegExp, waitForOptions?: WaitForOptions) => FindReturn;
findByTestId: (testID: TextMatch, waitForOptions?: WaitForOptions) => FindReturn;
findAllByText: (
text: string | RegExp,
waitForOptions?: WaitForOptions
text: TextMatch,
waitForOptions?: WaitForOptions,
options?: TextMatchOptions,
) => FindAllReturn;
findAllByPlaceholderText: (
placeholder: string | RegExp,
waitForOptions?: WaitForOptions
placeholder: TextMatch,
waitForOptions?: WaitForOptions,
options?: TextMatchOptions,
) => FindAllReturn;
findAllByDisplayValue: (
value: string | RegExp,
waitForOptions?: WaitForOptions
value: TextMatch,
waitForOptions?: WaitForOptions,
options?: TextMatchOptions,
) => FindAllReturn;
findAllByTestId: (
testID: string | RegExp,
testID: TextMatch,
waitForOptions?: WaitForOptions
) => FindAllReturn;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like typescript types for findBy have incorrect options args order.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops forgot to update, thanks!

@AugustinLF AugustinLF force-pushed the non-recursive-text-match-options branch from 677c78a to d2f9f33 Compare November 1, 2021 08:53
@AugustinLF
Copy link
Collaborator Author

Hey folks, it's never too late to pick up a stale PR! I've addressed one of the comments to reduce the breaking changes, but there's still the main question, which is the issue with matching across multiple nodes and the difference in the API with @testing-library/react. @thymikee you were unsure about this change and raised some concerns. I've written higher in this PR what my opinion is (i.e. same behaviour on both platform), but also discussed some other alternatives, what do you think? Do you see a solution to move forward?

@thymikee
Copy link
Member

thymikee commented Nov 3, 2021

Hey, I'm gonna look at this once again soon. We already release v8, so we're clear to introduce other unrelated breaking changes 💪🏼

@AugustinLF
Copy link
Collaborator Author

Hey hey @thymikee, anything I can do to help? I admit that current status is a bit messy since the PR went over several months and building on top of another contribution. If that helps, I could try to make a recap of the whole discussion/status.

@thymikee
Copy link
Member

@AugustinLF that won't hurt for sure! I'm pretty sure this is the way we want to move forward. It would help to gather all the breaking changes this can cause and ways to migrate.

I'm a bit swamped lately with work and personal stuff and having a really hard time to fit this into my pipeline. But maybe I could only have a brief look and release this as an alpha version for testing. Appreciate you stick with us all this time 🙌🏼

* origin/main: (24 commits)
  v8.0.0
  chore(deps): bump dns-packet from 1.3.1 to 1.3.4 in /website (callstack#742)
  chore(deps-dev): bump @babel/preset-flow from 7.13.13 to 7.14.5 (callstack#765)
  chore(deps-dev): bump typescript from 4.4.2 to 4.4.4 (callstack#845)
  chore(deps-dev): bump release-it from 14.6.1 to 14.11.6 (callstack#830)
  chore(deps-dev): bump @babel/plugin-proposal-class-properties (callstack#846)
  chore(deps-dev): bump @babel/cli from 7.13.16 to 7.15.7 (callstack#833)
  chore(deps-dev): bump @testing-library/jest-native from 3.4.3 to 4.0.2 (callstack#842)
  chore(deps): bump color-string from 1.5.3 to 1.6.0 in /website (callstack#844)
  chore(deps): bump prismjs from 1.23.0 to 1.25.0 in /website (callstack#843)
  Forward extra data to press event (callstack#828)
  chore(deps-dev): bump @babel/preset-react from 7.13.13 to 7.14.5 (callstack#839)
  chore(deps): bump url-parse from 1.5.1 to 1.5.3 in /website (callstack#813)
  chore(deps-dev): bump @types/react from 17.0.2 to 17.0.30 (callstack#838)
  chore(deps): bump postcss from 7.0.29 to 7.0.36 in /website (callstack#756)
  chore(deps): bump prismjs from 1.23.0 to 1.25.0 in /website (callstack#823)
  chore(deps): bump path-parse from 1.0.6 to 1.0.7 in /website (callstack#796)
  chore(deps): bump merge-deep from 3.0.2 to 3.0.3 in /website (callstack#754)
  chore(deps): bump ws from 6.2.1 to 6.2.2 in /website (callstack#752)
  chore(deps): bump path-parse from 1.0.6 to 1.0.7 (callstack#795)
  ...
@AugustinLF
Copy link
Collaborator Author

This PR includes two main changes: it adds TextMatch options, and changes the way nested text is matched:

Support to TextMatch in all text based queries. This moves the current waitForOptions parameter of the findBy* queries from the 2nd place to the 3rd place. For backward compatibility, we still read it from the 2nd parameter but log a deprecation warning.

Nested text matching changes, it will not match text broken across several nodes:

// This examples matches before this PR and will not after
render(
      <Text>
        Hello <CustomText>World!</CustomText>	
      </Text>	
    ).getByText('Hello World!')	

Extra care has been put to make sure that the relevant node gets matched, when several nodes could match (in the previous example, when matching "World", it'll match CustomText and not Text.

These two changes have the benefit of making the API closer to the older implementation of testing-library/react-native (before being transferred to callstack), and to make it closer to the web implementation.

@thymikee
Copy link
Member

@AugustinLF I've pushed a commit with a few updates, please check it. One thing that we're still missing is matching a11y queries, but I'm thinking about introducing it in a separate PR

@AugustinLF
Copy link
Collaborator Author

AugustinLF commented Nov 23, 2021

Great, that looks good.

The a11y queries are very high on my list (to be honest we've reimplemented them in user-land on our side). But I'd love to take a step back and try to get a more holistic view and figure out what we're missing, if anything needs to be improved, I'm planning to try to get some feedback from our devs here at Klarna. I hope to be able to free more time to improve our test infra and @testing-library/react-native is a very important part of it, so looking forward helping.

Any plan to switch to TS? 👀

@thymikee
Copy link
Member

The a11y queries are very high on my list (to be honest we've reimplemented them in user-land on our side). But I'd love to take a step back and try to get a more holistic view and figure out what we're missing

Yes, let's take some time to make it right. We may even want to revisit the API and maybe somehow mimic the getByRole from the web, as accessibility matures on RN? Not sure if that's even viable, so treating it as an idea / open question for now.

Any plan to switch to TS? 👀

Yes please!

@thymikee thymikee merged commit 31ef0b9 into callstack:main Nov 23, 2021
@thymikee thymikee changed the title feat: add text match options a.k.a string precision API feat(breaking): add text match options a.k.a string precision API Nov 23, 2021
@AugustinLF AugustinLF deleted the non-recursive-text-match-options branch November 23, 2021 15:34
@thymikee
Copy link
Member

Published a pre-release: https://github.com/callstack/react-native-testing-library/releases/tag/v9.0.0-alpha.0. Thank you once again @AugustinLF, much appreciated!

@janpe
Copy link

janpe commented Dec 2, 2021

@thymikee any estimate on when 9.0.0 could be released?

@AugustinLF
Copy link
Collaborator Author

We need to publish some announcement around those change, I hope to pick that work today or tomorrow #862 (reply in thread)

@AugustinLF AugustinLF mentioned this pull request Dec 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

The string precision API is missing in v7
7 participants