Skip to content
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

fix: improve text matching #1222

Merged
merged 5 commits into from
Nov 17, 2022
Merged

Conversation

mdjastrzebski
Copy link
Member

@mdjastrzebski mdjastrzebski commented Nov 11, 2022

Summary

TLDR: Make this pass:

test('byText matches accross multiple texts', () => {
  const { getByText } = render(
    <Text testID="outer">
      <Text testID="inner-1">Hello</Text> <Text testID="inner-2">World</Text>
    </Text>
  );
  expect(getByText('Hello World').props.testID).toBe('outer');
});

while not breaking this:

test('byText matches inner nested text', () => {
  const { getByText } = render(
    <Text testID="outer">
      <Text testID="inner">Hello World</Text>
    </Text>
  );
  expect(getByText('Hello World').props.testID).toBe('inner');
});

Resolves #1221
Resolves #1181

What

Match only the deepest Text element containing given content while supporting nested Text elements.

In the light of #1221, user expectation for handling nested texts is reasonable, this MIGHT be considered as a bug fix rather than a breaking change. Or we might consider this breaking change, since the case is not clear cut.

All in all, as text matching has been complex subject in the past, let's be extra careful with this PR.

How

  1. Improve findAll function to accept deepestOnly option that when enabled prevents from matching given node if any of its descendants is matched.
    a. This option could be used with other queries if useful, but can be breaking change depending on usage
    b. The updated findAll preserves the same tree walk order as initial one so getByAll* unit tests relying on matching order should work the same.
    c. New findAll is based on findAll from React Test Renderer, with necessary tweaks
  2. Create a new getTextContent() function that can work with nested Text elements. Implementation based on toHaveTextContent() matcher from Jest Native
  3. Update *ByText queries to match only the deepest Text using new getTextContent() function.

This should keep only return a single match even in the case of nested Text elements. The matched Text element will be the deepest one that fulfils the predicate.

Test plan

Add some simple test for desired effect. Make all existing test pass as is. Tweak only tests that actually checked that there is no nesting text matching.

@codecov
Copy link

codecov bot commented Nov 11, 2022

Codecov Report

Base: 94.67% // Head: 94.72% // Increases project coverage by +0.05% 🎉

Coverage data is based on head (bd1c0e4) compared to base (c9c0493).
Patch coverage: 100.00% of modified lines in pull request are covered.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1222      +/-   ##
==========================================
+ Coverage   94.67%   94.72%   +0.05%     
==========================================
  Files          42       44       +2     
  Lines        2946     2979      +33     
  Branches      440      442       +2     
==========================================
+ Hits         2789     2822      +33     
  Misses        157      157              
Impacted Files Coverage Δ
src/helpers/findAll.ts 100.00% <100.00%> (ø)
src/helpers/getTextContent.ts 100.00% <100.00%> (ø)
src/helpers/matchers/matchTextContent.ts 100.00% <100.00%> (ø)
src/matches.ts 100.00% <100.00%> (ø)
src/queries/displayValue.ts 100.00% <100.00%> (ø)
src/queries/hintText.ts 100.00% <100.00%> (ø)
src/queries/labelText.ts 100.00% <100.00%> (ø)
src/queries/placeholderText.ts 100.00% <100.00%> (ø)
src/queries/testId.ts 100.00% <100.00%> (ø)
src/queries/text.ts 97.29% <100.00%> (-1.07%) ⬇️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@AugustinLF
Copy link
Collaborator

AugustinLF commented Nov 11, 2022

Should we also considering matching?

    <View>
      <Text>Hello</Text>
      <Text>World</Text>
    </View>

That's an open question, I feel it had some value, but could also lead to weird things. Do screen readers can read this type of text together, or do they treat that as distinct elements?

@mdjastrzebski
Copy link
Member Author

mdjastrzebski commented Nov 11, 2022

@AugustinLF I think in the short term we should not consider supporting that, as the current *ByText queries have an assumption that text can be only put into Text components, hence the return from *ByText should only be Text components. Changes to that behaviour would definitely be breaking.

Afaik how a11y & screen readers work, in your example the Text elements would be treated as separate focusable elements. That might be changed by grouping them under View using <View accessible>, which should make screen reader read them together.

However, we are getting into an "edge case" territory here, I would rather not support returning non-Text views, unless we can build a compelling real world & wide audience use case for such feature.

@AugustinLF
Copy link
Collaborator

Yeah, the breaking change factor is a fair one, you're right. And if we want to allow that, it should probably start behind a flag.

src/helpers/textContent.ts Outdated Show resolved Hide resolved
src/helpers/findAll.ts Outdated Show resolved Hide resolved
src/helpers/findAll.ts Outdated Show resolved Hide resolved
src/helpers/findAll.ts Outdated Show resolved Hide resolved
src/helpers/findAll.ts Outdated Show resolved Hide resolved
@mdjastrzebski mdjastrzebski force-pushed the fix/match-text-accords-text-children branch from 9810bd3 to cbf35e9 Compare November 15, 2022 10:25
Copy link
Collaborator

@AugustinLF AugustinLF left a comment

Choose a reason for hiding this comment

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

Really like the new API/file structure, I feel it makes things much clearer :)

I approve but do agree with the comment about renaming the textContent file to getTextContent.

@@ -45,7 +45,7 @@ test('React Native API assumption: nested <Text> renders single host element', (
</Text>
</Text>
);
expect(getHostSelf(view.getByText('Hello'))).toBe(view.getByTestId('test'));
expect(getHostSelf(view.getByText(/Hello/))).toBe(view.getByTestId('test'));
Copy link
Member

Choose a reason for hiding this comment

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

is this a breaking change?

Copy link
Member Author

Choose a reason for hiding this comment

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

This test relied of nested Text not being matched, so I needed to change whole string match to partial match. This was a limitation on previous text matching and not a feature per se. IMO that is not a breaking change as test required a minor tweak without affecting the essence of the test.

@mdjastrzebski mdjastrzebski merged commit 647ae26 into main Nov 17, 2022
@mdjastrzebski mdjastrzebski deleted the fix/match-text-accords-text-children branch November 17, 2022 12:08
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.

ByText queries don't seem to match across multiple Text elements Feature: improved findAll method
4 participants