enzymejs / enzyme

JavaScript Testing utilities for React
https://enzymejs.github.io/enzyme/
MIT License
19.96k stars 2.01k forks source link

find & debug use an outdated render tree. html() doesn't #2412

Open tareqdayya opened 4 years ago

tareqdayya commented 4 years ago

Current behavior

I'm trying to find an element inside my mounted wrapper using the find function [also tried the findWhere and the contain methods] but it doesn't seem to find it because enzyme seems to be looking through an earlier version of the rendered component. What do i mean? Well, in a nutshell, I have a loading spinner that appears until: a thunk is dispatched -> Mocked (sync) API request made -> data stored in redux store -> component gets updated After which, the loading spinner is replaced with the DOM element i'm trying to find. Now, the funny stuff, is if i console log wrapper.html() i get the LATEST version of the component in which the element i'm trying to find exists. But if i console log wrapper.debug(), i get the older render tree which includes the loading spinner. It seems that methods that find elements use something similar to debug() rather than html() to parse the DOM tree? If you're wondering, the snapshot returned by the react-test-renderer behaves correctly (returns the most recent render). My component is a Function Component BTW and not a class-based one. It's not memoized.

Additional things i've tried:

Code: it('should find my element', async () => { expect(wrapper.find('#my-el')).toExist(); // fails expect(wrapper.find('#loading-spinner')).not.toExist(); // fails });

Current workaround: i'm using wrapper.getDOMNode() to get an actual DOM element before successfully finding my elements using vanilla javascript query selectors.

Expected behavior

debug(), html() and find() should all return the latest render tree.

Your environment

"react": "^16.13.1", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.2", "enzyme-to-json": "^3.5.0", "jest-environment-enzyme": "^7.1.2", "jest-enzyme": "^7.1.2", "ts-jest": "^26.0.0", "typescript": "~3.7.2"

API

Version

library version
enzyme ^3.11.0
react ^16.13.1
react-dom ^16.13.1
react-test-renderer ^16.13.1
adapter (below) ^1.15.2

Adapter

ljharb commented 3 years ago

This is because .html() invokes enzyme's render API. Try instead using .debug() - can you share what your component code, test code, and wrapper debug output is?

tareqdayya commented 3 years ago

This is because .html() invokes enzyme's render API. Try instead using .debug() - can you share what your component code, test code, and wrapper debug output is?

Hi @ljharb This actually applies to all of my components that follow the same pattern mentioned above: component mounts -> a thunk is dispatched on mount -> Mocked (sync OR async) API request made -> data stored in redux store -> component gets updated In a nutshell, if i need to get the version of the component after the update, the find method fails. Regarding your suggestion, it's not actually html() or debug() that are irking me, but rather the find() method. I just mentioned them to draw your attention to the fact that their behaviour differs and that the find() method seems to be using the same api as the debug() method instead of the html() one which fetches the component post-update. As you know, the find method is the most used method. TBH, it's a pain to keep using getDOMNode() along with vanilla js since it doesn't always work when i'm trying to update components inside the test using code (e.g. by filling a form or clicking a button) Thanks, Sorry for not including code snippets, it just literally affects all of my components that follow the above pattern; some of them are complex, and some are simple, but the problem remains the same.

ljharb commented 3 years ago

In that case, you need to await on the API request promise, and then wrapper.update().

Yes, nothing uses the html() one, because that's a shortcut to an entirely different API. Only the debug() tree matters.

tareqdayya commented 3 years ago

In that case, you need to await on the API request promise, and then wrapper.update().

Yes, nothing uses the html() one, because that's a shortcut to an entirely different API. Only the debug() tree matters.

Hi @ljharb Thanks 4 answering again as mentioned in the OP, I have actually tried using wrapper.update() while awaiting on the API request (and even converting the network request to a sync method just to see if it's the source of the issue) but to no avail. Any other suggestions would be more than welcome. Out of curiosity, am I stretching it with the way I use enzyme? should i be doing these kinds of tests using e2e/integration tools? or is enzyme's design inclusive of more than just simpler white-box unit tests where props are passed directly to components without all these updates? Thanks again