-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
add support for object property selector #110
Conversation
@@ -95,6 +96,10 @@ export function nodeHasType(node, type) { | |||
return node.type.name === type || node.type.displayName === type; | |||
} | |||
|
|||
export function nodeHasProps(node, props) { |
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 function name clashes quite a lot with the nodeHasProperty
function. I think it should probably be more explicit so it's known that it's meant for the object selector.
Something like nodeMatchesObjectProps
?
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 like that name a lot more!
Updated with suggested edits. |
export function instMatchesObjectProps(inst, props) { | ||
if (!isDOMComponent(inst)) return false; | ||
const node = getNode(inst); | ||
return !isEmpty(props) && isMatch(propsOfNode(node), props); |
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 !isEmpty
check seems redudant. On L318, you are ensuring that props
can't be null.
So it seems like the isEmpty
call is just a perf concern to make sure users aren't doing find({})
, but that should be the minority case.
So you are actually just adding complexity for the typical case of find({foo: bar})
.
Ultimately, I think the isEmpty
call is superflous and negatively affecting performance in a very small 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.
I don't see a line 318 in this diff.
what does isMatch(propsOfNode(node), {})
do?
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.
L218. My bad.
isMatch compares keys and values of two objects to be equal
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.
right, so, if propsOfNode(node)
are empty, and props
are empty, then as-is, it returns false - with your change, it returns true.
Which is desired?
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.
That is true. But how often is somebody going to look for something with an empty object? That just seems like a design against the wrong use-case. And I guess maybe somebody does want to search for nodes that don't have any props? which find({})
could support? feels awkward though. I guess I could see either way here.
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.
That seems reasonable (to throw an error). @lelandrichardson, thoughts?
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.
if they are actually passing in an empty object, then matching all nodes actually seems like the reasonable response to me?
I agree this doesn't seem like a normal use case, although I can imagine some code that builds up some object dynamically, and in some cases might build up to nothing...
I don't feel strongly about this, but I don't see the harm in having {}
match all nodes. It seems like the expected behavior, despite being a "corner-case" type input?
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 agree this doesn't seem like a normal use case, although I can imagine some code that builds up some object dynamically, and in some cases might build up to nothing...
Exactly. But I would say that's a bug in their code. If they are dynamically building an object, and it gets to an empty state. An error should be thrown, so they can make sure they are testing what they want.
I don't feel strongly about this, but I don't see the harm in having {} match all nodes. It seems like the expected behavior, despite being a "corner-case" type input?
Would you also want to see find('[]')
to match every node? Because that is the same API effectively.
Comparing it to the '[]'
api, I would think it should only find components with props that match the specified key/values.
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.
It seems its 3-against-1, so I'm happy to go with the approach of throwing an error for this case.
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'll update this after we discuss the best solution to the nested objects below.
This looks great. Quick question, though:
const wrapper = shallow(<div style={{ height: 20 }} />);
expect(wrapper.is({ style: { height: 20 } })).to.equal(true); Do we potentially want to implement our own matcher? We may even want to have special flattening for the |
@ljharb Just took a look at Let me know how you guys feel about this possible solution. |
expect(wrapper.find({ more: { a: 1 } })).to.have.length(0); | ||
expect(wrapper.find({ more: [{ id: 1 }] })).to.have.length(2); | ||
expect(wrapper.find({ more: { item: { id: 1 } } })).to.have.length(1); | ||
expect(wrapper.find({ style: { height: 20 } })).to.have.length(1); |
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.
@lelandrichardson This works now with is-subset
, what do you think?
I was trying to think about what the most sensible API for this is... I think there are 2 questions:
Question 2 is sort of a special case of Question 1... it just goes a bit further. At the moment I feel like the answer to 2 is "yes", and if 2 is "yes" I think 1 has to be yes or else the API becomes inconsistent. As a result, I think the following API would be ideal:
Let me know if any of you guys disagree, but I feel like that last rule is probably the right behavior. Basically, I'm thinking we will only "flatten" styles when the node type is a string (ie, it's a DOM node). This could end up kind of annoying though, because some people could have a test set up where their render method somewhere along the lines has something like: <input style={[styles.input, error && styles.inputError]} ... /> Then they have a test somewhere that asserts that the inputError styles are applied to the input. With the proposed API above, this works swimmingly BUT... at some point there might be a refactor like the following: <Input style={[styles.input, error && styles.inputError]} ... /> At this point, this test will end up failing (assuming a shallow render), but its still behaving properly. Now writing the same test becomes a lot more difficult. I guess the question comes down to whether or not we want to treat the |
If it matches subsets all the way down, how would I go about not matching a subset, ie, only doing strict matching? I think that the only special cases should ever be on DOM components - |
I agree with this. @ljharb. I also haven't thought about strict matching. Maybe hold off till users are asking for it? Then maybe an @lelandrichardson I'm having trouble understanding But the array for example fiddle https://jsfiddle.net/n38pwsdc/ So i could see this:
but even this is weird because the top one truly only works for react native. If we just keep it as is just looking for subsets of props, then the user has to know what to expect since they are testing. No flattening needed on our end. |
LOL. @hartzis you're completely right. I've just been working way too much in react native recently... we don't need to do anything specific for As far as strict matching goes... I agree that maybe we just wait on this. As a result, I think the current state of this PR is what we all agree on? |
no worries. let me know about those errors and i'll get it updated asap to get this merged in |
LGTM. |
1 similar comment
LGTM. |
[semver-minor] add support for object property selector
@blainekasten, thanks for quoting me in #105, gave me the drive to attempt to add the support. Let me know what you guys think.
I also just happened to notice while I tried to follow code patterns adding functionality that there seems to be some opportunity to reuse some code between
MountedTraversal
andShallowTraversal
. There was definitely somecopy & paste
I did :)