-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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/RTTR: find manually managed three components #1977
Fix/RTTR: find manually managed three components #1977
Conversation
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit 4e4eebb:
|
58a9b45
to
e12511a
Compare
…ponents that are not mounted via r3f
e12511a
to
90f834d
Compare
|
||
it('should also find three components which are not mounted via r3f and not throw', async () => { | ||
const { scene } = await ReactThreeTestRenderer.create(<WithManuallyManagedComponent />) | ||
expect(() => scene.findByProps({ name: MANUALLY_MOUNTED_COMPONENT_NAME })).not.toThrow() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is important because the test below just tests a specific error but not if no error is thrown at all.
return this._fiber.__r3f.memoizedProps | ||
return this._fiber.__r3f?.memoizedProps ?? this._fiber |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. I'd do the same for the parent getter below. External parts of the threejs scene don't have __r3f
appended to them. Helpers are one case of this but also <primitive />
or results of loaders.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this should be returning the node if memoized props is not there, you're confusing props with the instance properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you suggest then @joshuaellis ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well you're not passing props to the object cause it's not rendered by react. So it should either return an empty object or not be accessible in the tree which would be even harder to do. I don't mind the former.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you cannot use the .findByProps(), .findAll() etc. methods at all anymore because they throw.
This isn't necessarily true:
expect(() => scene.find((node) => node.instance.name === MANUALLY_MOUNTED_COMPONENT_NAME)).not.toThrow()
will not throw currently.
as you said, in this particular scenario the return-value of .props is equal to .instance for "manually" mounted components.
Which makes the public facing API confusing. To someone new to an existing codebase with implemented tests, they might start to ask "why can I access some props but not all of them on certain components". It should act differently for two different nodes.
I'm not opposed to extending the API, we could have a new methods called findByInstanceProperties
which could look like this:
public findByInstanceProperties = (properties: Obj): ReactThreeTestInstance =>
expectOne(this.findAllByInstanceProperties(properties), `with instance properties: ${JSON.stringify(properties)}`)
public findAllByInstanceProperties = (properties: Obj): ReactThreeTestInstance[] =>
findAll(this, (node: ReactThreeTestInstance) => Boolean(node.instance && matchProps(node.instance, properties)))
This would create a clear mental differentiation between the two APIs whilst including the feature you're requesting (which I have no problem with adding, it's just about how we add it).
What do you think? Also for more opinions cc @CodyJasonBennett
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The argument you made regarding the inconsistent API is true: returning a subset when the component is mounted via r3f and returning all props when it isn't is not good design. Your idea of adding a .findByInstanceProps()
method is great!
But then you'd write .findByProps({name: "BOX-A"})
when you only have a "BOX-A" which is mounted via r3f and .findByInstanceProps({name: "BOX-B"})
when you have "BOX-B" which isn't mounted via r3f. This doesn't seem quite elegant from the client-code perspective to me because you'd have to use different testing methods based on implementation details, which you actually want to ignore in black box tests that focus on user interaction. What do you say? Maybe there is a third even better way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Besides: I just tested your approach with the .find()
method and it throws too because it uses .getChildren()
which acesses fiber.__r3f.objects
.
Cannot read properties of undefined (reading 'objects')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just noticed that .findByProps()
is actually not User oriented as it is recommended in react-testing-library since it tests for things which the user cannot see (e.g. the name
prop). It is comparable to .getByTestId()
which is not recommended, not to .getByText()
e.g. in react-testing-lib.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to discuss this in an issue. There's a clear separation between react and three, and I don't want design to hold back fixes if we can avoid it.
…ror now it returns the standard three parent as a fallback
Can you help me with fixing the CI testing error @CodyJasonBennett? |
Looking into it. Strange that this will pass locally. |
I was surprised by that either... |
I think we'll want to revisit this with a query API since there is a meaningful difference between props known to JSX and the underlying three.js instance, but the semantics of RTTR are wrong here to begin with. I'll look to resolve this with #2338, since props should be clearly communicated as a narrower search. |
We came across a missing feature/bug(?) in test renderer where it would throw when using r3f together with manually instantiated and managed three components:
Feedback required: