Hello all,
Before I start, I have asked for help in the Discord chat but it's a bit on the quiet side today.
Anyway to my question. I've put together a pretty basic contact form that works just fine. However I now need to start writing my unit tests and I've run into a load of problems (like I literally have only managed to get a snapshot test to pass so far).
So to start with I'm trying to test that the form should render my validation messages when you click the submit button if you have not filled out all the required sections.
I thought I could achieve this by calling the handleSubmit() function e.g:
componentRender.find('Formik').instance().props.handleSubmit(badFormValues, { resetForm });
However when I run componentRender.debug() , my validation messages aren't being rendered. It's like the validationSchema function isn't being called?
Is there something special that needs to be done? I feel like the mapPropsToValues() function is working, from looking at the state object it's being populated with the values I'm passing the form. I just can't see why validation is seemingly being skipped?
I've been at this for 2 days now and can't find any good examples through google (probably my fault) so any help would be massively appreciated.
For reference here is the test file so far:
import React from 'react';
import { shallow, mount } from 'enzyme';
import { BrowserRouter as Router } from 'react-router-dom';
import PartnerRegistrationForm from 'Components/partner-registration-form/PartnerRegistrationForm';
describe('PartnerRegistrationForm component', () => {
const formValues = {
companyName: 'some company',
countryCode: 'GB +44',
telNumber: 12345678,
selectCountry: 'United Kingdom',
postcode: 'EC3M 5DJ',
addressSelect: '123 street',
siteName: 'blah',
siteURL: 'https://www.blah.com',
contactName: 'Me',
email: '[email protected]',
};
const componentShallow = shallow(<PartnerRegistrationForm {...formValues} />);
describe('Component Snapshot', () => {
it('should match stored snapshot', () => {
expect(componentShallow).toMatchSnapshot();
});
});
describe('Component functionality', () => {
it('should not submit if required fields are empty', () => {
const badFormValues = {
companyName: 'some company',
countryCode: 'GB +44',
telNumber: 12345678,
};
const resetForm = jest.fn();
const componentRender = mount(
<Router>
<PartnerRegistrationForm {...badFormValues} />
</Router>,
);
componentRender.find('Formik').instance().props.handleSubmit(badFormValues, { resetForm });
// console.log(componentRender.update().find('.validation-error'));
// console.log(componentRender.find('Formik').instance());
// expect(componentRender.find('.validation-error').text()).toEqual('Company Name is required');
});
});
});
And here is my withFormik() function:
const WrappedFormWithFormik = withFormik({
mapPropsToValues({
companyName,
countryCode,
telNumber,
selectCountry,
postcode,
addressSelect,
siteName,
siteURL,
contactName,
email,
}) {
return {
companyName: companyName || '',
countryCode: countryCode || '',
telNumber: telNumber || '',
selectCountry: selectCountry || '',
postcode: postcode || '',
addressSelect: addressSelect || '',
siteName: siteName || '',
siteURL: siteURL || '',
contactName: contactName || '',
email: email || '',
};
},
validationSchema, // This is a standard Yup.object(), just importing it from a separate file
handleSubmit: (values, { resetForm }) => {
console.log('submitting');
const {
companyName,
countryCode,
telNumber,
selectCountry,
postcode,
addressSelect,
siteName,
siteURL,
contactName,
email,
} = values;
const emailBody = `Name: ${contactName},`
+ `Email: ${email},`
+ `Company Name: ${companyName},`
+ `Country Code: ${countryCode},`
+ `Telephone Number: ${telNumber},`
+ `Country: ${selectCountry},`
+ `Postcode: ${postcode},`
+ `Address: ${addressSelect},`
+ `Website Name: ${siteName},`
+ `Website URL: ${siteURL}`;
// TODO set up actual contact submit logic
window.location.href = `mailto:[email protected]?subject=New partner request&body=${emailBody}`;
resetForm();
},
})(PartnerRegistrationForm);
So you seem to be testing internals of Formik here. Did you figure things out, or do you still need guidance?
So you seem to be testing internals of Formik here. Did you figure things out, or do you still need guidance?
@jaredpalmer thanks for the reply. Due to time constraints of this project, I haven't been able to come back to this test for now. Basically just added a snapshot until I can figure out what's going on.
I could definitely use some help!! :)
Basically I just want to be able to test wether the form doesn't submit if validation does not pass, and, if it does pass, that it returns the correct values.
I've refactored my handleSubmit function slightly, it now uses window.location.assign rather than .href (which should be better for mocking purposes if I understand things correctly).
I'm unsure how to do unit tests integrated with Formik as well.
For example, the handleSubmit that we pass as props... should we test this in a mounted component wrapped with withFormik??
Is enough to just check if the only submit in a shallow rendering?
What if Formik is replaced in this component and, somehow, we are receiving another handleSubmit function via props...?
Hi, we are having similar issues with testing Formik since upgrading from 0.11.11 to 1.3.1. We previously used a strategy of testing reactions to form submissions (i.e. router change or fetch call). Here is an example:
Formik 0.11.11
test('Submitting form navigates to new route', () => {
const computeMatch = jest.spyOn(Router.prototype, 'computeMatch');
const wrapper = mount(<RouterWrapper location='/old-route'><ConnectedFormikComponent /></RouterWrapper>);
// Do the thing
wrapper.find('form').simulate('submit');
expect(computeMatch).toBeCalledWith('/new-route');
expect(wrapper.find('.new-route-page')).toExist();
});
The latest version requires us to chain async callbacks for testing behavior that we previously got for free. In our example below it is only a minor inconvenience; however, we have other tests that require multiple callbacks chained together. Also, this strategy has made us very dependent on mocking the implementation of jest spies, which we feel has undermined the value of these tests.
Fromik 1.3.1
test('Submitting form navigates to new route', (done) => {
const computeMatch = jest.spyOn(Router.prototype, 'computeMatch')
.mockImplementation((route) => makeAssertions(route));
const wrapper = mount(<RouterWrapper location='/old-route'><ConnectedFormikComponent /></RouterWrapper>);
// Do the thing
wrapper.find('form').simulate('submit');
const makeAssertions = (route) => {
expect(route).toEqual('/new-route');
expect(wrapper.find('.new-route-page')).toExist();
done();
};
});
Is there a way that we can configure Formik to handle onSubmit synchronously like in previous versions? Alternatively, is there a better strategy to test the collaboration between our form and submit handlers?
Hola! So here's the deal, between open source and my day job and life and what not, I have a lot to manage, so I use a GitHub bot to automate a few things here and there. This particular GitHub bot is going to mark this as stale because it has not had recent activity for a while. It will be closed if no further activity occurs in a few days. Do not take this personally--seriously--this is a completely automated action. If this is a mistake, just make a comment, DM me, send a carrier pidgeon, or a smoke signal.
Is it possible that we have an example of how to do unit testing with Formik using Enzyme, please? I think that this would clear all of our doubts.
@robbporto , found a small writeup here: https://github.com/jaredpalmer/formik/blob/f5d76609b222ce583663add279ac76e807e2d0ba/README.md#testing-formik
This helps, @cagmz. Thanks for pointing this out.
@cagmz Do you know anything about the submitForm method they're using in the submission example. I don't understand where they got that prop from.
Also is MyInnerForm supposed to be the MyFormInner component from the Dummy Form?
@borisyordanov I think you're right; MyInnerForm is supposed to be MyFormInner.
The submitForm is part of the injected props to MyInnerForm:
https://github.com/jaredpalmer/formik/blob/master/docs/api/formik.md#submitform---void
In order to use submitForm in MyInnerForm, it would need to accept a submitForm prop.
Edit; join us on https://www.reactiflux.com/ ! There's a #formik channel.
I don't understand how to access the submitForm prop using the <Formik /> HOC, if I just have <Formik ... render={(handleSubmit} => <BootstrapForm onSubmit={handleSubmit}>...</BootstrapForm>} /> which component would I target with find to access submitForm?
@Anahkiasen I believe you should not access props directly, rather find Submit button and trigger click in order to execute submitForm handler.
But then I cannot await it, which means assertions on whether onSubmit was called and with what fail at the moment :/
// The component
const MyComponent = ({ myOnSubmit }) => (
<Formik
initialValues={initialValues}
onSubmit={data => {
myOnSubmit(data.foo, data.bar).then(() => {
this.closeFormModal();
});
}}
render={({ handleSubmit }) => (
<BootstrapForm onSubmit={handleSubmit}>
<Field name="foo" />
<Field name="bar" />
</BootstrapForm>
)}
/>
);
// Test
const myOnSubmit = jest.fn().mockResolvedValueOnce({});
const wrapper = mount(<MyComponent myOnSubmit={myOnSubmit} />);
// Fill form inputs
wrapper.find("input").blabla();
// Submit form
wrapper.find("form").simulate("submit");
expect(myOnSubmit).toHaveBeenCalledWith("foo", "bar"); // <-- This fails
It works if I wrap the expect in a setTimeout of like 1/2s but I'd rather wait the exact time to not make my test suite slower
Using the done callback should work:
// since Formik handler is evaluated asynchronously we have to delay checking the assertion
window.setTimeout(() => {
expect(myOnSubmit).toHaveBeenCalledWith("foo", "bar");
done();
}, 0);
See also:
https://jestjs.io/docs/en/asynchronous
@robbporto , found a small writeup here: https://github.com/jaredpalmer/formik/blob/f5d76609b222ce583663add279ac76e807e2d0ba/README.md#testing-formik
Cool man, this is very clear.
Hey there!
How is it possible to set values for form in the tests?
I tried like:
const wrapper = shallowWithTheme(<SignUpFormComponent />);
const signUpForm = wrapper.find(SignUpForm);
signUpForm
.props()
.setValues({
name: 'name',
email: '[email protected]',
company: 'company',
userType: 'userType',
password: 'password',
});
I console values like this: console.log(signUpForm.props().values);
And got: { name: '', email: '', company: '', password: '', userType: '' }
Also, I tried to find the input component, there I'm using the Field like:
<Field
icon="passwordIcon"
type="password"
name="password"
value={values.password}
onChange={handleChange}
isInvalid={!!errors.password}
isTouched={touched.password}
component={AuthInputComponent}
placeholder="Create Password"
/>
Find it by: signUpForm.dive().find(Field)
And got all fields:
<FormikConnect(FieldInner) icon="userIcon" type="text" name="name" value="" onChange={[Function]} isInvalid={false} isTouched={[undefined]} component={{...}} placeholder="Name" />
<FormikConnect(FieldInner) icon="emailIcon" type="email" name="email" value="" onChange={[Function]} isInvalid={false} isTouched={[undefined]} component={{...}} placeholder="Email" />
<FormikConnect(FieldInner) name="userType" options={{...}} component={[Function: UserTypeComponent]} />
<FormikConnect(FieldInner) icon="companyIcon" type="text" name="company" value="" onChange={[Function]} isInvalid={false} isTouched={[undefined]} component={{...}} placeholder="Company" />
<FormikConnect(FieldInner) icon="passwordIcon" type="password" name="password" value="" onChange={[Function]} isInvalid={false} isTouched={[undefined]} component={{...}} placeholder="Create Password" />
How it's possible to get Field by name to change Field value?
Thx a lot!
How we solved it with React Testing Library is like this, if it can help other people:
export const fillField = (field: HTMLElement, value: any) => {
fireEvent.change(field, {
persist: () => {},
target: { value },
});
};
export const waitForSubmission = (
submitButton: HTMLElement,
spy: () => void,
) => {
fireEvent.click(submitButton);
return wait(() => expect(spy).toHaveBeenCalled());
};
it("can update a user", async () => {
const result = await render(
<User
updateOrganisationUser={jest.fn().mockResolvedValueOnce(response)}
/>,
);
fillField(result.getByPlaceholderText("Name"), "New Name");
// Fill more fields
await waitForSubmission(
result.getByText("Save"),
props.updateOrganisationUser,
);
expect(props.updateOrganisationUser).toHaveBeenCalledWith(
...theCorrectArguments,
);
});
How we solved it with React Testing Library is like this, if it can help other people:
export const fillField = (field: HTMLElement, value: any) => { fireEvent.change(field, { persist: () => {}, target: { value }, }); }; export const waitForSubmission = ( submitButton: HTMLElement, spy: () => void, ) => { fireEvent.click(submitButton); return wait(() => expect(spy).toHaveBeenCalled()); }; it("can update a user", async () => { const result = await render( <User updateOrganisationUser={jest.fn().mockResolvedValueOnce(response)} />, ); fillField(result.getByPlaceholderText("Name"), "New Name"); // Fill more fields await waitForSubmission( result.getByText("Save"), props.updateOrganisationUser, ); expect(props.updateOrganisationUser).toHaveBeenCalledWith( ...theCorrectArguments, ); });
Nice!
I did like that:
const fillField = (field: ShallowWrapper, name: string, value: any) => {
field.simulate('change', {
persist: () => {},
target: {
name,
value,
},
});
};
it('should invoke submit function cause form is invalid', () => {
const wrapper = shallowWithTheme(<SignUpFormComponent />);
const signUpForm = wrapper.find(SignUpForm);
const nameField = signUpForm.dive().find({ name: 'name' });
const emailField = signUpForm.dive().find({ name: 'email' });
const passwordField = signUpForm.dive().find({ name: 'password' });
fillField(nameField, 'name', 'value');
fillField(emailField, 'email', '[email protected]');
fillField(passwordField, 'password', 'password');
console.log(nameField.props().value);
console.log(signUpForm.props().values);
});
But on consoles, I still have empty values =|
@KonstantinKudelko I believe you need to do a few things to make this work. One is wait for the change events to actually run. To do that, add async to your function and add this after your fillField calls:
await new Promise(resolve => { setTimeout(resolve); });
After that you need to then update your wrapper, so call wrapper.update().
I think that since you stored nameField, emailField, and passwordField, you will have references to the old, non-updated wrapper. You'll want to refetch all of these.
All together now, of course there are optimizations to be made, this is just to illustrate what's going on _(disclaimer: untested)_:
const fillField = (field: ShallowWrapper, name: string, value: any) => {
field.simulate('change', {
persist: () => {},
target: {
name,
value,
},
});
};
it('should invoke submit function cause form is invalid', async () => {
const wrapper = shallowWithTheme(<SignUpFormComponent />);
fillField(wrapper.find(SignUpForm).dive().find({ name: 'name' }), 'name', 'value');
fillField(wrapper.find(SignUpForm).dive().find({ name: 'email' }), 'email', '[email protected]');
fillField(wrapper.find(SignUpForm).dive().find({ name: 'password' }), 'password', 'password');
await new Promise(resolve => { setTimeout(resolve); });
wrapper.update();
console.log(wrapper.find(SignUpForm).dive().find({ name: 'name' }).props().value);
console.log(wrapper.find(SignUpForm).props().values);
});
@evankennedy Something like that works for me. However, I would prefer to use built-in jest solutions so that it feels less hacky -- unfortunately, any combination of jest.runAllTicks() and jest.runAllTimers() do not seem to work.
Wondering if anyone else has had luck fixing this without awaiting promises as in the example above...
Also use simulate('blur') on input to make it touched
@z0d14c any working snippet of code for this? @evankennedy's solution looked promising but actually didn't work for me. value is still empty after waiting and wrapper.update()
It would be _really_ helpful to have a page with working examples of testing validation and submission with both React Testing Library and Enzyme.
@TrueWill and everyone
Hello, I finally managed to tests formik input changes, on blur event, form submit button with Jest/Enzyme/Formik after wasting couple hours searching solutions.
Here's my code, note that I use Flow, MaterialUI, HOC pattern and recompose intensively and Jest as the testing framework.
Form component to be tested:
// @flow strict
import React from 'react'
import { compose } from 'react-apollo'
import { withStyles } from '@material-ui/core/styles'
import TextField from '@material-ui/core/TextField'
import { withFormik } from 'formik'
import type { Formik } from 'formik'
import { object, string } from 'yup'
import { Typography } from '@material-ui/core'
import SendIcon from '@material-ui/icons/Send'
import Button from '@material-ui/core/Button'
import { withTranslation } from '../utils/i18n'
const styles = (theme) => ({
root: {
},
form: {
width: '50%',
},
creator: {
marginTop: '10px',
},
icon: {
marginLeft: theme.spacing(1),
},
link: {
color: theme.palette.primary.main,
},
})
type Props = {
email: String,
onSendAccessRequest: Function,
t: Function,
classes: Object,
// Formik
// ...Formik
values: Object,
errors: Object,
touched: Object,
handleBlur: Function,
handleChange: Function,
handleSubmit: Function,
isSubmitting: boolean,
setFieldValue: Function
}
class DatasetContact extends React.PureComponent<Props> {
render() {
const {
email,
classes,
t,
values,
errors,
touched,
handleBlur,
handleChange,
handleSubmit,
isSubmitting,
} = this.props
return (
<div className={classes.root}>
{/* <Typography className={classes.creator} variant="subtitle1">todo</Typography> */}
<form className={classes.form}>
<TextField
disabled
fullWidth
error={Boolean(touched.email && errors.email)}
helperText={(touched.email && errors.email) ? errors.email : ''}
required
label={t('common:email')}
value={values.email}
onChange={handleChange('email')}
onBlur={handleBlur('email')}
className={classes.textField}
margin="normal"
name="email"
/>
<TextField
fullWidth
error={Boolean(touched.subject && errors.subject)}
helperText={(touched.subject && errors.subject) ? errors.subject : ''}
required
label={t('subject')}
value={values.subject}
onChange={handleChange('subject')}
onBlur={handleBlur('subject')}
className={classes.textField}
margin="normal"
name="subject"
/>
<TextField
fullWidth
error={Boolean(touched.message && errors.message)}
helperText={(touched.message && errors.message) ? errors.message : ''}
required
label={t('message')}
multiline
rows="4"
value={values.message}
onChange={handleChange('message')}
onBlur={handleBlur('message')}
className={classes.textField}
margin="normal"
name="message"
/>
<Button onClick={handleSubmit} type="submit" disabled={isSubmitting} variant="contained" color="primary" className={classes.button}>
{t('common:send')}
{' '}
<SendIcon className={classes.icon} />
</Button>
</form>
</div>
)
}
}
export default (compose(
withTranslation('dataset-details'),
withStyles(styles, { withTheme: true }),
withFormik({
mapPropsToValues({
email, subject, message,
}) {
return {
email: email || '',
subject: subject || '',
message: message || '',
}
},
validateOnBlur: true,
validationSchema: (props) => {
const { t } = props
return object().shape({
email: string().email(t('emailNotValid')).required(t('common:fieldRequired')),
subject: string().required(t('common:fieldRequired')),
message: string().required(t('common:fieldRequired')), /* .min(9, 'Message must be 9 characters or longer') */
})
},
handleSubmit: (values, { props, resetForm, setSubmitting }) => {
setSubmitting(true)
props.onSendAccessRequest(values, () => {
setSubmitting(false)
resetForm()
})
},
}),
)(DatasetContact))
Test input changes, on blur events and callback after form submit:
// @flow strict
import { mount, type ReactWrapper } from 'enzyme'
import React from 'react'
import expect from 'expect'
import { Formik } from 'formik'
import Button from '@material-ui/core/Button'
import Typography from '@material-ui/core/Typography'
import wait from '../../utils/tests/wait'
import DatasetContact from '../DatasetContact'
const validEmailValue = '[email protected]'
const invalidEmailValue = 'test'
const validValue = 'test'
const testProps = {
t: (value) => value,
authors: 'test author1, test author2',
onSendAccessRequest: () => {},
}
const updateField = (wrapper: ReactWrapper<any>, name: string, value: any) => {
wrapper.simulate('change', {
persist: () => {},
target: {
name,
value,
},
})
}
describe('DatasetContact with all expected props', () => {
let wrapper
let onSubmitCallback
beforeEach(async () => {
onSubmitCallback = jest.fn() // .mockResolvedValueOnce({})
wrapper = mount(<DatasetContact {...testProps} onSendAccessRequest={onSubmitCallback} />)
})
afterEach(() => {
wrapper.unmount()
})
it('Should display provided authors string', () => {
expect(wrapper.find(Typography).at(0).text()).toContain(testProps.authors)
})
it('Should display three form fields', () => {
expect(wrapper.find('input[name="email"]')).toBeTruthy()
expect(wrapper.find('input[name="subject"]')).toBeTruthy()
expect(wrapper.find('input[name="message"]')).toBeTruthy()
})
it('Should update email input field on change', () => {
updateField(wrapper.find('input[name="email"]'), 'email', validEmailValue)
expect(wrapper.find('input[name="email"]').props().value).toEqual(validEmailValue)
})
it('Should show error with invalid email input field on blur', async () => {
const field = wrapper.find('input[name="email"]')
field.simulate('focus')
updateField(field, 'email', invalidEmailValue)
field.simulate('blur', { target: { name: 'email', value: invalidEmailValue } })
await wait(0)
wrapper.update()
const updatedField = wrapper.find('input[name="email"]')
expect(updatedField.props().value).toEqual(invalidEmailValue)
expect(updatedField.props()['aria-invalid']).toEqual(true)
})
it('Should show error with empty subject input field on blur', async () => {
const field = wrapper.find('input[name="subject"]')
field.simulate('focus')
updateField(field, 'email', '')
field.simulate('blur', { target: { name: 'subject', value: '' } })
await wait(0)
wrapper.update()
const updatedField = wrapper.find('input[name="subject"]')
expect(updatedField.props().value).toEqual('')
expect(updatedField.props()['aria-invalid']).toEqual(true)
})
it('Should update subject input field on change', () => {
updateField(wrapper.find('input[name="subject"]'), 'subject', validValue)
expect(wrapper.find('input[name="subject"]').props().value).toEqual(validValue)
})
it('Should update message textarea field on change', () => {
updateField(wrapper.find('textarea[name="message"]'), 'message', validValue)
expect(wrapper.find('textarea[name="message"]').props().value).toEqual(validValue)
})
it('Should trigger submit on submit clicked with valid form', async () => {
updateField(wrapper.find('input[name="email"]'), 'email', validEmailValue)
updateField(wrapper.find('input[name="subject"]'), 'subject', validValue)
updateField(wrapper.find('textarea[name="message"]'), 'message', validValue)
const button = wrapper.find(Button)
expect(button.props().type).toEqual('submit') // Making sure that is the submit button
button.simulate('click', { preventDefault: () => {} }) // this launches field validation, thats for sure !!
await wait(0)
wrapper.update()
expect(onSubmitCallback).toHaveBeenCalledWith({
email: validEmailValue,
message: validValue,
subject: validValue,
}, expect.any(Function))
})
it('Should not trigger submit on submit clicked with invalid form', async () => {
updateField(wrapper.find('input[name="email"]'), 'email', invalidEmailValue)
updateField(wrapper.find('input[name="subject"]'), 'subject', validValue)
updateField(wrapper.find('textarea[name="message"]'), 'message', validValue)
const button = wrapper.find(Button)
expect(button.props().type).toEqual('submit') // Making sure that is the submit button
button.simulate('click', { preventDefault: () => {} }) // this lauches field validation, thats for sure !!
await wait(0)
wrapper.update()
expect(onSubmitCallback).not.toHaveBeenCalled()
})
})
wait.js file is only a timeout wrapped in a promise
// @flow strict
const wait: (number) => Promise<void> = (timeout: number) => new Promise((resolve) => setTimeout(resolve, timeout))
export default wait
Jest/Enzyme with no promise/setTimeout or wait()
I was able to take things a step further with help from @TrueWill and @Renaud009's post.
<Field/>sFirstly I am using act() from react-dom/test-utils _every_ time that I simulate an event. I then created a function that simulates multiple events on the developer's behalf, updateFormikField
export const updateFormikField = async (
nativeFieldWrapper: ReactWrapper<HTMLAttributes, any, React.Component<{}, {}, any>>,
targetName: string,
value: any,
) => {
// updates values and errors
await act(async () => {
nativeFieldWrapper.simulate(
'change',
{ target: { name: targetName, value } }
);
});
// updates touched
await act(async () => {
nativeFieldWrapper.simulate(
'blur',
{ target: { name: targetName } }
);
});
}
When using this approach I _did not need_ to call wrapper.update() or wait() or use the promise/setTimeout pattern! 馃帀
An example where one field (newPasswordConfirmation) is disabled until a prerequisite field is valid (newPassword)
it('Should disable the newPasswordConfirmation field if the newPassword field is not valid', async () => {
const wrapper = getWrapper();
const newPasswordField = wrapper.find('input[name="newPassword"]').first();
await updateFormikField(newPasswordField, 'newPassword', '');
const newPasswordConfirmationField = wrapper.find('input[name="newPasswordConfirmation"]').first();
expect(newPasswordConfirmationField.prop('disabled')).toBe(true);
});
it('Should enable the newPasswordConfirmation field if the newPassword field is valid', async () => {
const wrapper = getWrapper();
const newPasswordField = wrapper.find('input[name="newPassword"]').first();
await updateFormikField(newPasswordField, 'newPassword', mockPassword);
const newPasswordConfirmationField = wrapper.find('input[name="newPasswordConfirmation"]').first();
expect(newPasswordConfirmationField.prop('disabled')).toBe(false);
});
<Form/>sSimilarly I created a function that simulates a form submission event, submitFormikForm.
export const submitFormikForm = async (
nativeFormWrapper: ReactWrapper<HTMLAttributes, any, React.Component<{}, {}, any>>,
) => {
await act(async () => {
nativeFormWrapper.simulate(
'submit',
{ preventDefault: () => {} }
);
});
}
Just an update, none of the above solutions worked for me, I had to follow https://dev.to/dannypule/updating-formik-fields-when-testing-using-jest-and-enzyme-or-react-testing-library-4hb1 this posts suggestions and use actImmediate to ensure the form callback was called
await act(async () => {
cmp
.find('[name="user"]')
.simulate('change', { target: { name: 'user', value: 'Test123' } })
cmp
.find('[name="password"]')
.simulate('change', { target: { name: 'password', value: 'testpass' } })
})
cmp.find('form').simulate('submit', { preventDefault: () => {} })
await actImmediate(cmp)
// callback has been called
actImmediate is defined as suggested in the blog post
export const actImmediate = (wrapper) =>
act(
() =>
new Promise<void>((resolve) => {
setImmediate(() => {
wrapper.update()
resolve()
})
}),
)
Most helpful comment
Is it possible that we have an example of how to do unit testing with Formik using Enzyme, please? I think that this would clear all of our doubts.