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
Update simulate() to return a promise #898
Conversation
* 'master' of https://github.com/airbnb/enzyme: (278 commits) 2.8.2 Make enzyme work for react ^15.4.x. [Docs] Update docs to use `prop-types` v2.8.1 [Tests] `create-react-class` should be a static dev dependency. [Tests] move helpers in to `test/_helpers` dir address final comments Update error message in react-compat Change condition in `performBatchedUpdates` to a version check REACT155 constant is now true for react 15.5 or above Update ReactWrapperComponent to use prop-types package Update karma cofig to be compatible with react@15.5 Lint Remove unnecessary tests see PR #878 for explanation Fix import Remove dependency on create-react-class - remove createClass export from src/react-compat - add test/react-compat with createClass export - change spec files to import createClass from test/react-compat - undo changes to src/ReactWrapperComponent.jsx (use of createClass there removed in #877 - update README with correct dependencies for react@15.5 Extract batchedUpdates to a function `react-test-renderer` needs to be v15.5.4 or later. add missing env entry test both react 15.4 and react 15 ...
Simulate is for events. Events are inappropriate for promises because a promise can only resolve once, whereas events can fire multiple times. See #823. |
Note that if there's a way to make simulate take a callback that works for every event type, that'd be great, but since simulate invokes user code, there's no way to ensure we can ever get notified of its completion. |
Good point. Forgot about that use case. |
so how do people test this type of code? |
If the implementation provides no way to do so (takes a callback or returns a promise) then it has successfully denied you the ability to correctly wait on the results. |
this is out of the scope of If you don't have time, np, thanks for answering prior ?'s |
It's not about having time, that would be incorrect. If the implementation does not provide an async hook, none can exist. |
That's a bit irrelevant. We need to trigger I'll also respond to your previous comment:
While that's true for the DOM, we're testing against React by simply executing prop functions. At most, once per test. Furthermore, each
We only care about the next tick, not whether the event is successful or not. Leave it up to the consumer (e.g., us), to return values correctly. If you're still worried about the DOM, then we can only support |
All of your criteria/circumstances make perfect sense; but since you (the app developer) have that context, and not enzyme itself, you're the one who should be making that decision - ie, you should advance to the next tick yourself, either by returning a new Promise, or using the test's In other words, "at most, once per test" is absolutely not necessarily true for all enzyme users, so it's not an assumption enzyme can/should make. |
The The only solution we found so far is the following (which is far too much annoying bootstrap). const promise = Promise.resolve();
const wrapper = shallow(<Wrapper onClick={jest.fn(() => promise)}) />);
wrapper.find(Form).simulate('click');
return Promise.resolve(promise).then(() => {
expect(wrapper.state('success')).toBe(true); // Works
}); There comes a time when developer productivity and efficiency should be taken into account. |
(you don't need to rewrap While you can guarantee that If, however, Form's In short, I think that |
I've actually proposed calling the prop directly ( I've also tinkered with extending |
@ljharb To follow-up on Mile's point, I think the issue with calling the prop directly is that you loose the semantic meaning of However, what if const wrapper = shallow(<Component />);
const promise = wrapper.find('CloseButton').simulate('press');
return promise.then(() => {
expect(wrapper.state('isClosed')).toBeTruthy();
}); |
simulate()
works great for handlers that are synchronous, but have timing issues for any handler that is async (like promises). Take the following for example:Although the example is quite crude, the
expect
call may fail in specific conditions as the inner promise is firing off in a different tick. To resolve this issue, one must wrap theexpect
in asetTimeout
, which is quite hacky.This change will update
simulate()
to return a promise, allowing this to easily work (plus promises are better).