We use a lot of higher-order components, which leads to long and brittle chains of dive().dive()... in our shallow component testing code. It was previously suggested to add a depth parameter to dive (https://github.com/airbnb/enzyme/issues/1138), which would make the code shorter but equally brittle.
I'm wondering if a new method diveTo that is called with a selector, such as the non-enhanced component, could be helpful. That way, even if we reorganize the HOC composition, our testing code stays stable.
We have an alternative API in mind; but diveTo is a reasonable API as well.
Hello guys,
I use enzyme everyday and would love to help, can I try to do this feature ?
I was thinking of looping using the same logic of dive聽but adding information about the depth on error messages and finally returning the ReactWrapper after n iteration. What do you think ?
@lucmerceron I don't think we're ready to add this feature yet; thanks for your interest! https://github.com/airbnb/enzyme/labels/help%20wanted is the best place to look for things that are ready for contributors to try their hand at.
@ljharb I'm curious about the alternative API you have in mind. Could you share your idea?
I'v been looking forward to this feature for a very long time. Diving down manually is tedious and brittle. Mount renders unwanted deep children. DiveTo is perfect because it doesn't care how many HOC's are between it which means it's not brittle.
@ljharb I'm also interested in this other API you are thinking of.
One issue with the proposed diveTo is that string selectors are always brittle, and always a code smell. The only way to avoid this is .diveTo(Component), but this requires exporting the wrapped component solely for testing, which is also a code smell.
@ljharb I agree with exporting a component from production code just for testing is a code smell because other devs might be confused as to why we are exporting it, i.e we are making a private component a public one.
Why are string selectors bad though? I can't imagine a components name is going to change very often in the future. Or is it because we could have another component higher up the chain with the same name (seems unlikely unless generic).
The best solution perhaps might be to use something like rewire to get the Component. I don't love doing this though because it makes tests messy and can lead to abuse where people rewire everything to test.
Perhaps if enzyme provided a function which internally rewired this it would be nice? This way we could use Component selector but keep the component private and hide the messy API underneath in enzyme. Maybe that's what you were thinking.
Multiple components can have the same name, names can and indeed do change, etc.
I share your concerns, @ljharb. That's why I'm dying to know what could be a better approach. :)
.diveTo() + extra export or displayName selector, while not optimal, at least seems to be an improvement over the current situation with .dive().dive()... chains.
Another problem I see with diveTo(selector) is that it is not obvious, whether diving stops at the element matching the selector or whether it also dives into that element. The latter option is probably preferable as the most common use case is testing the exported, enhanced component as one unit. If you want to test only your HOC composition as a unit, you'd still have to add an extra export, though. So maybe we should rather name it diveInto(selector) then? Anyways, I guess I'm getting ahead of the discussion...
this sample code is run on my project if anyone needs it can ref.
export function diveTo(shallow, displayName, options) {
let component = shallow;
while (component.name() !== displayName) {
component = component.dive(options);
}
return component.dive();
}
I've used a diveFor function, which dives until a given selector is found. A slightly different solution to the same problem.
function diveFor(wrapper, selector) {
let result;
let current = wrapper;
do {
result = current.find(selector);
if (result.length > 0 || !current.dive) {
break;
}
try {
current = current.dive();
} catch (error) {
break;
}
} while (current.dive);
return result;
}
Most helpful comment
We have an alternative API in mind; but
diveTois a reasonable API as well.