I cannot find(Component|".selector") but I can see it via html() and via .contains()
Here are the most important parts of code. (I am using ava, t.log is the test logger)
I am using a styled component here but it is the same when I add a custom className, id or data-attribute to attempt finding the element/Component.
code
const RetryButtonId = RetryButton.styledComponentId;
const retrySelector = `.${RetryButtonId}`;
t.log(retrySelector);
const retry = wrapper.find(RetryButton);
const _retry = wrapper.find(retrySelector);
const html = wrapper.html();
const match = html.match(new RegExp(`<button class="${RetryButtonId}.*?">Retry</button>`));
t.log(`match: ${match && match[0]}`);
t.log(`length "${retrySelector}": ${_retry.length}`);
t.log(`length "RetryButton": ${retry.length}`);
t.log(`exists "${retrySelector}": ${wrapper.exists(retrySelector)}`);
t.log(`contains "RetryButton": ${wrapper.contains(RetryButton)}`);
The logs show inconsistencies. While contains() returns true, exists() returns false.
The html() matches the element but find() and exists() ignore it.
Logs
ℹ .sc-jTzLTM
ℹ match: <button class="sc-jTzLTM dlKhUw sc-bdVaJa kgpquR">Retry</button>
ℹ length ".sc-jTzLTM": 0
ℹ length "RetryButton": 0
ℹ exists ".sc-jTzLTM": false
ℹ contains "RetryButton": true
contains(), find(), exists(), html() should return matching results.
OS X 10.13.6
node: v10.10.0
npm: 6.4.1
yarn 1.12.3
| library | version
| ------------------- | -------
| enzyme | 3.8.0
| react | 16.7.0
| react-dom | 16.7.0
| react-test-renderer |
| adapter (below) |
I get the expected results if I call wrapper.update() beforehand but this shouldn't be needed, right?
What does .debug() print out prior to the update? (.html() should be ignored)
I’ll note that styled components tend to have some quirks which make working with them difficult. Could you provide the full component and test code?
Sorry I can't share the code since it's private. I'd have to set up a MVP for reproduction (probably won't have time).
Inspecting the debug tree is pretty hard. I tried it and wasn't very successful since the output is too cluttered (nested & extended components.). I gave up after a while and don't think I will try this method again in the near future.
I think I did notice that the "conditional section" wasn't in the tree. I'm not fully sure (cluttered output) and I moved on to try other methods.
My main concern was, that contains and html return absolutely different results than exists and find. This should simply not be an issue.
If you suggest not to use html then this should be noted or be removed. What use is a method if it returns the wrong output? especially if several methods aside have conflicting output.
I’ll note that styled components tend to have some quirks which make working with them difficult. Could you provide the full component and test code?
As mentioned I' might not have time to create an MVP. This is the best I can do to explain those componennts.
const Button = styled.button`
background: red;
`
const MyButton = styled(Button)`
background: red;
`
class App extends Component (
state = {
there: false
}
toggle() {
this.setState(prevState => ({foo: !prevState.there}));
}
<ThemeProvider theme={myTheme}>
<React.Fragment>
<Button onClick={this.toggle}>Toggle</Button>
{this.state.there && <MyButton onClick={this.toggle}>Sometimes I'm here</Button>}
</React.Fragment>
</ThemeProvider>
)
const Wrapper = mount <App/>
const button = wrapper.find(Button).at(0);
// >> FAIL
// The next operation fails ("something about not present length... expected 1 but received 0")
// button.simulate(click, {target: {value: "Mock event"}});
// const myButton = wrapper.find(Button).at(1); // ReactWrapper[]
// myButton.simulate(click, {target: {value: "I'm conditional"}});
// >> FAIL
// The next operation fails ("something about not present length... expected 1 but received 0")
// wrapper.setState({there: true}, () => {
// EVEN INSIDE THE CALLBACK
// const myButton = wrapper.find(Button).at(1); // ReactWrapper[]
// myButton.simulate(click, {target: {value: "I'm conditional"}});
//});
// >> WINNER
wrapper.update()
// The next operation works as expected.
myButton.simulate(click, {target: {value: "I'm conditional"}});
I'm sorry it's late at night and This is the best I can do right now. (probably ever)
The purpose of .html is that it uses the render API, and produces HTML output. I agree that it should probably be removed.
contains, exists, and find should definitely agree - although it's worth noting as well that simulate doesn't actually simulate anything, it's just sugar for invoking a prop function - so you might indeed need a wrapper.update() before things are consistent.
Thank you for your time.
I understand how simulate works. rendering is handled async from setting a state in react. I think this is why the issue occurs. I didn't see this in any documentation (which IMHO is too vague anyways). I tried several methods and was in some cases able to get the correct result after a await Promise.resolve() or (await new Promise(r => setTimeout(r, n)) (which greatly helped me understand the issue).
I think better examples considering this topic would help. It seems to be a very common use case.
I’d be happy to accept a PR that made the docs more clear.
If I ever find the time... I am currently writing docs for another big library.
(enquirer) so right now I'm rather busy.
I enjoy writing docs (mostly because I value them so much) so I might get back to this, ... no promises.
I usually write easy to follow guides alongside documentation. I might suggest a format (in January). I've had good responses to that format.
I suppose I hit a similar issue, but perhaps due to different reason as wrapper.update() makes no difference:
it.each(['foo', 'bar'])(
'lorem ipsum',
(value) => {
// Logs 1
console.log(wrapper.find({ value }).length);
// Shows the nodes with matching `value`
// <mockConstructor>
// <WithStyles(MyElem) checked={true} value="foo" />
// <WithStyles(MyElem) checked={true} value="bar" />
// </mockConstructor>
console.log(wrapper.debug());
// Fails with: Method “props” is meant to be run on 1 node. 0 found instead.
expect(wrapper.find({ value }).props()).toMatchObject({ checked: true });
}
);
Any suggestions?
@maciej-gurban hm, that's strange. Could you file a new issue, ideally with a repro repo?
I am on a similar issue here.
the component which is to render conditionally isn't showing up when using .find() but shows up on output of .contains()
// passes
loginWrapper.contains('input[placeholder="NewPassword');
// .props() meant to be run on 1 node , 0 found instead
loginWrapper.find('input[placeholder="New Password"]').props();
@shaleenmundra what's loginWrapper.debug() say?
it doesn't return the "New Password" input component which .html() does
.debug() is in sync with .find()
Most helpful comment
I suppose I hit a similar issue, but perhaps due to different reason as
wrapper.update()makes no difference:Any suggestions?