enzyme: simulated click on submit button in form does not submit form
Say I have a simple form to test:
<form onSubmit={onSubmit} >
<input />
<button type="submit">Submit</button>
</form>
If I mount
this component with enzyme
, I can confirm onSubmit
is called by simulating a submit
event on the form
element. However, if I simulate a click
on the submit button, the form is not submitted and onSubmit
is not called.
form.simulate('submit')
calls onSubmitbutton.simulate('click')
does not call onSubmit
Is this working as intended? Seems inconsistent, but I could definitely be missing something.
Example repo here. In particular, compare the test cases.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 150
- Comments: 41 (9 by maintainers)
The form submits if you use the submit event on the button.
@BLamy there are tons of ways to test it:
<form>
with an “onSubmit” prop that has a functionlogin
, and then unit-test the function on the prop and assert that it calls your spy with the right arguments.What further testing is needed? In no way should you be trying to write tests that test React’s “onSubmit” functionality, or of browsers’ submit behavior - that’s the job of the React team, or the browser implementors, respectively.
@ljharb I was talking about no way of testing it using
simulate
.If you
.simulate('click')
it doesn’t work. If you.simulate('submit')
thene
is not bound to the right context and therefore doesn’t have a.target
property.So yeah I can stub
e
and directly callonSubmit
with a spy onlogin
but realistically what I wanted to test was to make sure I was referencing my input fields right.For me the solution was to get the DOM element and trigger the click without using simulate.
Hey guys, I was able to get around this by doing:
Looks hacky, but works.
wrapper.find(...).get(...).click is not a function
Enzyme v3 with react 16 adapter also throws this.
Instead of simulating the
submit
event on theform
you could simulate the event from the button itself.wrapper.find('button').simulate('submit');
Especially concerned, that this is mount and should, by design, simulate real DOM behaviour. Or maybe DOM simulation through
mount
has some limitation – and in this case it should be explicitly specified.@fernandopasik “wrapper.find(…).get(…).click is not a function” (This may be due to shallow instead of mount…)
@BLamy does
wrapper.find('button').simulate('submit', { target: wrapper.find('button').get(0) })
work?https://github.com/airbnb/enzyme/blob/master/docs/future.md
“Event propagation is not supported”. I assume that’s what causes this problem.
@Schachte I think I used these instead:
Best I could do:
Which works but leaves this in the console
I’m using tape for my test
This is pretty rough. So if I’m getting this straight there is currently no way to test code which looks like this?
Better would be
expect(callback).to.have.property('callCount', 1)
or something similar; noop getter assertions are very dangerous (becauseexpect(callback).to.have.been.yogurt
will silently pass, and is always a bug)@netrocc’s solution seems to work, however I’m not sure why buttons are able to receive
submit
events. Is this something we can rely upon?@ZephD you can’t call .get() with shallow rendering.
I agree with @colinramsay. If any of you guys want to put up a PR to integrate Event Propagation, i’m sure the maintainers would greatly appreciate it.
Can somebody post a full working example please.
I have tried all the suggested solutions above using mount with a simple type submit button inside a form with onSubmit and it does not work still.
@mrchief it’s totally fine for your tests for a given component, to rely on that component’s instance (and it having one). In general, I’d suggest never using
simulate
, and directly invoking prop functions. One solution is to directly test that invoking those props does the right thing; or you can mock out instance methods, test that the prop functions call them, and unit test the instance methods. Either is fine, and it’ll depend on what your code is doing.@ajc24 I like how your post started but then, you end up testing the browser, not your unit. We all know that a submit button inside a form will fire the submit event and it’s the browser’s job to test that part. You should rather be testing that the submit handler does what it’s supposed to and if you can ensure that, then you can leave testing the browser out of your unit tests and still be ok. Am I missing something?
@cppbit I don’t think this is a solution, because this enzyme approach doesn’t honor the
type
of the rendered<button>
withinmount()
.In my situation, I have a React component that is a wrapper for an HTML
<button>
. And I want the<button>
type
to be dynamic, as chosen by the wrapper component. I was hoping to test the correct rendering and behavior, by seeing of the<form>
onSubmit
callback is called (or not called).I used @sohailykhan94’s solution to test if a form submittal was properly executing a handler function. Here’s what it looks like, and it seems to work well with my Jest/Enzyme testing environment.
Here is my example Form React Component. Of course this component is going to have more features, but I wanted to test if this worked, before building out the rest of it.
@MartinDawson no, it’s not - and your test should be testing that the onSubmit prop does what you expect. Your test should not be verifying that the browser, or react itself, works properly - that’s a job for those projects’ tests.
@vmasto Potentially it’s because submit events bubble.