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: toHaveProp func to work with @testing-library/react-native latest version (7.0.2) #36

Merged

Conversation

yonatan-altaraz
Copy link
Contributor

@yonatan-altaraz yonatan-altaraz commented Sep 3, 2020

What:

when using the latest @testing-library/react-native version, the function toHaveProp(attr: string, value?: any) doesn't work properly when getting the element by text, it checks for the prop existence but does not compare to the expected value, when given, and returns 'pass' even if the expected value is wrong.

Why:

to be able to work with this function using the new version of @testing-library/react-native

How:

just changed a couple of row in to-have-props.js

Checklist:

  • Documentation added to the
    docs (N/A)
  • Typescript definitions updated (N/A)
  • Tests
  • Ready to be merged

to recreate the issue, you can checkout this branch

@codecov
Copy link

codecov bot commented Sep 3, 2020

Codecov Report

Merging #36 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff            @@
##            master       #36   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            8         8           
  Lines           95        94    -1     
  Branches        27        27           
=========================================
- Hits            95        94    -1     
Impacted Files Coverage Δ
src/to-have-prop.js 100.00% <ø> (ø)
src/utils.js 100.00% <ø> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update cb02b6a...d3bebde. Read the comment docs.

@yonatan-altaraz yonatan-altaraz changed the title fix fix toHaveProp func to work with @testing-library/react-native latest version (7.0.2) Sep 3, 2020
Copy link
Collaborator

@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.

Not sure about the fact that composite components with specific displayName are allowed matched by this matcher.

@@ -18,7 +18,8 @@ export function toHaveProp(element, name, expectedValue) {
const prop = element.props[name];

const isDefined = expectedValue !== undefined;
const isAllowed = VALID_ELEMENTS.includes(element.type);
const elementType = typeof element.type == 'string' ? element.type : element.type?.displayName;
const isAllowed = VALID_ELEMENTS.includes(elementType);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm wondering whether we should have a list of allowed components to check their props. IMO we should be able to check props of any host component. @thymikee, @brunohkbx, wdyt?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Same. A list of valid elements is a remnant of the previous lib implementation

Copy link
Collaborator

Choose a reason for hiding this comment

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

The current impl. seems a bit weird here, as reading the code bellow:

pass: isDefined && isAllowed ? hasProp && equals(prop, expectedValue) : hasProp,

Is the mere prop existence is checked in case either expected value is not defined (which sound fine) and also when element is not on VALID_ELEMENTS (which sounds kind of strange).

As I see it, the desired behavior is as follows:

  • For all host elements:
    • if provided expectedValue check for prop existence and that it's value matches expectedValue
    • if not provided expectedValue: just check if prop exists.
  • For composite elements:
    • as all RNTL queries already return only host elements, then the dev user had to use some escape hatch like .parent, .children to get hold of that kind of element,
    • IMO I would allow user to use this check on such elements, because: 1) it's hard to write such test by mistake, so they are rather written deliberately, 2) it reduces the "surprise factor" for the user, 3) it simplifies our codebase.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@mdjastrzebski yeah I completely agree.

Comment on lines 33 to 35
expect(() =>
expect(getByText('text')).toHaveProp('allowFontScaling', 'wrongValue'),
).toThrowError();
Copy link
Collaborator

Choose a reason for hiding this comment

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

As far as I don't prefer {get|query}ByTestId queries, it seems that for consistency and readability it would be better to use it here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's just for the example, any reason it should behave this way if using getByText here?

Copy link
Collaborator

Choose a reason for hiding this comment

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

getByText('text') vs queryByTestId('text') is just a stylistic issue here, as for current setup, both elements return exactly the same element.

Optimally, we should be using the same queries, as we encourage our users (ByText, ByHintText, ByRole, etc) everywhere in our tests when possible. But since surrounding code uses queryByTestId, the second best solution seems to be consistent with surrounding code to reduce cognitive load for reading user.

Be aware that this is a bit nitpicking :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i understand, in the current setup it's correct, the returned element of both accessors is the same,
but this fix is for an issue that pops only when using the 7.0.2 version of testing-library/react-native, in which the returned elements are not the same, so only the getByTest func will fail the test (without the fix). that was the logic behind using getByTest.
but no problem to change it to queryByTestId, tell me what you think is best

@@ -18,7 +18,8 @@ export function toHaveProp(element, name, expectedValue) {
const prop = element.props[name];

const isDefined = expectedValue !== undefined;
const isAllowed = VALID_ELEMENTS.includes(element.type);
const elementType = typeof element.type == 'string' ? element.type : element.type?.displayName;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This looks a bit strange, as it will allow any composite component that sets it's displayName to a value from VALID_ELEMENTS list, to have it's props check. AFAIK we want to discourage interaction with composite components in favor of interacting only with host components.
@thymikee, @brunohkbx, wdyt?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yup, I got the same impression, we shouldn't do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

can you explain the logic behind not checking the value of a prop for composite components (or any component)?

Copy link
Collaborator

Choose a reason for hiding this comment

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

So the reason why React Native Testing Library ignores composite components, and is only concerned with host components, is that it tries to resemble what user (or manual tester) would see when interacting with the app. This is similar to React Testing Library, when user interacts with rendered HTML and not composite components.

That said, RNTL already filters out composite components in all queries (getBy, queryBy, findBy), so we probably can ignore checking for host/composite component in toHaveProp matchers, and perform prop check on both types. This should not compromise our goal of making it easy to write good test, and hard to write bad tests.

Additionally, this will reduce the "suprise factor" for users trying to validate props on compisite components that they got from using parent or children props for navigation.

@thymikee, what's your take on that?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm good with not validating that here. It's the library that's responsible for filtering.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

did i understand you correctly that the change should be removing the whole isAllowed and VALID_ELEMENTS
validation?

@yonatan-altaraz yonatan-altaraz changed the title fix toHaveProp func to work with @testing-library/react-native latest version (7.0.2) fix: toHaveProp func to work with @testing-library/react-native latest version (7.0.2) Sep 16, 2020
@yonatan-altaraz
Copy link
Contributor Author

@mdjastrzebski, I'v changed the code according to what i understand from your comments, i hope i got it right.
please have another look.

Copy link
Collaborator

@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.

@yonatan-altaraz I think that the code looks good now. Great thanks for your contribution!

Please updated the docs (README.md) to remove the mention that toHaveProp checks only native elements (= host elements), as after this change this is no longer the case.

README.md Outdated
@@ -215,8 +215,7 @@ expect(parent).not.toContainElement(grandparent);
toHaveProp(prop: string, value?: any);
```

Check that an element has a given prop. Only works for native elements, so this is similar to
checking for attributes in the DOM.
Check that an element has a given prop. this is similar to checking for attributes in the DOM.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Check that an element has a given prop. this is similar to checking for attributes in the DOM.
Check that an element has a given prop.

@thymikee thymikee merged commit 29756b4 into testing-library:master Sep 28, 2020
@bcarroll22
Copy link
Collaborator

🎉 This PR is included in version 3.4.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants