diff --git a/README.md b/README.md index 687b43d57..6909fc02b 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ By now, you might be thinking, "Why didn't you just use [Redux-Form](https://git 1. According to our prophet Dan Abramov, [**form state is inherently emphemeral and local**, so tracking it in Redux is unecessary](https://github.com/reactjs/redux/issues/1287#issuecomment-175351978) 2. Redux-Form calls your entire top-level reducer multiple times ON EVERY KEYSTROKE. This is fine for small apps, but as your Redux app grows, input latency will continue increase if you use Redux-Form 3. I no longer use [Redux](https://github.com/reactjs/redux) or [MobX](https://mobx.js.org/), just React's setState. - 4. Redux-Form is 22.5 kB minified gzipped (Formik is 8.9 kB) + 4. Redux-Form is 22.5 kB minified gzipped (Formik is 9.2 kB) My goal with Formik was to create a scalable, performant, form helper with a minimal API that does the really really annoying stuff, and leaves the rest up to you. @@ -277,10 +277,6 @@ npm install yup --save - [React Native](#react-native) - [Why use `setFieldValue` instead of `handleChange`?](#why-use-setfieldvalue-instead-of-handlechange) - [Avoiding a Render Callback](#avoiding-a-render-callback) - - [Testing Formik](#testing-formik) - - [Dummy Form](#dummy-form) - - [Simulating input](#simulating-input) - - [Simulating form submission](#simulating-form-submission) - [Organizations and projects using Formik](#organizations-and-projects-using-formik) - [Authors](#authors) - [Contributors](#contributors) @@ -1069,182 +1065,6 @@ const MyReactNativeForm = props => ( export default MyReactNativeForm ``` -### Testing Formik - -_This section is a work in progress._ - -The suggested approach to testing Formik forms is with Airbnb's [Enzyme](https://github.com/airbnb/enzyme) test utility library. - -The documentation and examples in this guide use Facebook's [Jest](https://facebook.github.io/jest) test runner. However, feel free to use [mocha](https://mochajs.org/) and [chai](http://chaijs.com/) if you prefer that. - -To get started with Enzyme, you can simply install it with npm: - -```bash -npm i enzyme --save-dev -``` - -If you are using React >=15.5, in addition to enzyme, you will have to ensure that you also have the following npm modules installed if they were not already: - -```bash -npm i react-test-renderer react-dom --save-dev -``` - -#### Dummy Form -Imagine we have a basic form with one field `name`. - -```js -// MyForm.js -import { Formik } from 'formik'; -import Yup from 'yup'; - -export const validationSchema = Yup.object().shape({ - name: Yup.string() - .min(2, 'Must be longer than 2 characters') - .max(30, "No one's name is that long") - .required('Required'), -}); - -export const handleSubmit = (values, { setSubmitting }) => { - setTimeout(() => { - setSubmitting(false); - }, 1000); -}; - -export const mapPropsToValues = props => ({ name: '' }); - -export const MyFormInner = ({ - values, - handleSubmit, - handleChange, - handleBlur, - setStatus, - status, - errors, - isSubmitting, -}) => { - return ( -
- - {errors.name && -
- {errors.name} -
} - {isSubmitting &&
Submitting
} - {status && - !!status.myStatusMessage && -
- {status.myStatusMessage} -
} - -
- ); -}; - -export default Formik({ - mapPropsToValues, - validationSchema, - handleSubmit, -})(MyFormInner); -``` - -#### Simulating input - -We can test that our UI is updating properly by using Enzyme's `shallow` renderer in addition to its `dive()` and `simulate()` methods. This lets us render the Formik-enhanced form, but then jump down and run simulations and assertions from the perspective of your inner form. - -```js -// MyForm.test.js -import MyForm, { MyInnerForm } from './MyForm'; - -describe('MyForm', () => { - test('should update an input when it is changed', () => { - const tree = shallow(); - - tree.find(MyInnerForm).dive().find('input').simulate('change', { - // you must add this next line as (Formik calls e.persist() internally) - persist: () => {}, - // simulate changing e.target.name and e.target.value - target: { - name: 'name', - value: 'ian', - }, - }); - - const newValue = tree.find(MyInnerForm).dive().find('input').props().value; - - expect(newValue).toEqual('ian'); - }); -}); - -``` - -#### Simulating form submission - -```js -// MyForm.test.js -import MyForm, { MyInnerForm, validationSchema } from './MyForm'; - -describe('MyForm', () => { - test('submits the form', () => { - const tree = shallow(); - expect(tree.find(MyInnerForm).dive().find('#submitting')).toHaveLength(0); - - // simulate submit event. this is always sync! async calls to setState are swallowed. - // be careful of false positives - tree.find(MyInnerForm).dive().find('form').simulate('submit', { - preventDefault: () => {}, // no op - }); - - // Because the simulated event is 100% sync, we can use it to test the synchronous changes - // here. Any async stuff you do inside handleSubmit will be swallowed. Thus our UI - // will see the following changes: - // - isSubmitting -> true (even if you set it to false asynchronously in your handleSubmit) - // - touched: all fields - expect(tree.find(Form).dive().find('#submitting')).toHaveLength(1); - expect( - tree.find(Form).dive().find('button[type="submit"]').props().disabled - ).toBe(true); - }); - - test('what happens when the form is submitted', async () => { - const tree = shallow(); - - expect(tree.find(MyInnerForm).dive().find('#submitting')).toHaveLength(0); - - await mockCallsToMyApi(); - await tree.find(MyInnerForm).props().submitForm(); - - // check that ui has completely updated - expect( - tree.find(MyInnerForm).update().dive().find('#submitting') - ).toHaveLength(0); - expect(tree.find(MyInnerForm).update().dive().find('#status').text).toEqual( - 'Success!' - ); - expect( - tree - .find(MyInnerForm) - .update() - .dive() - .find('button[type="submit"]') - .props().disabled - ).toBe(false); - - // check that props have updated - expect(tree.find(MyInnerForm).props().status).toEqual({ - myStatusMessage: 'Success!', - }); - expect(tree.find(MyInnerForm).props().errors).toEqual({}); - expect(tree.find(MyInnerForm).props().touched).toEqual({ name: true }); // submit will touch all fields - }); -}); -``` - ## Organizations and projects using Formik [List of organizations and projects using Formik](https://github.com/jaredpalmer/formik/issues/87)