Formik: [v2] testing form with validationSchema

Created on 23 May 2019  路  11Comments  路  Source: formium/formik

馃悰 Bug report

Current Behavior

I have a test that basically screenshots a form after removing focus from field that is required (while not filling in any value). For validations, I'm using Yup.

During fireEvent.blur(textField);, I get warning:

Warning: An update to Formik inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

I'm also trying to screenshot page after blur, expecting to see validation message. From my experiments, I need to do: await waitForDomChange({ input }) to get correct screenshot.

I've also noticed there's no warning if I don't have validationSchema.

I assume this is connected to https://github.com/jaredpalmer/formik/issues/1524 ?

Expected behavior

No warning.
No need for waitForDomChange.

Reproducible example

https://codesandbox.io/s/agitated-lalande-5nhh0

Your environment

| Software | Version(s) |
| ---------------- | ---------- |
| Formik | 2.0.1-rc.0 |
| React | 16.8.6 |
| TypeScript | 3.4.5 |
| Browser | Chrome |
| npm/Yarn | Yarn |
| Operating System | Linux |

stale v2

Most helpful comment

Have you tried this?

await act(async() => {
    fireEvent.change(getByLabelText(/email/i), {target: {value: fakeUsername}});
});

All 11 comments

I'd like to add this also happens with validate (instead of validationSchema), e.g.:

const validate = (values) => {
  const errors = {};

  if (!values.test) {
    errors.test = "required";
  }

  return errors;
};

And I forgot to mention that, even surrounding fireEvent.blur with act doesn't help, e.g.:

act(() => {
  fireEvent.blue(input);
});

I'm seeing the same issue when using a validation schema.

Warning: An update to Formik inside a test was not wrapped in act(...).

https://github.com/cmelion/react-cra-demo/blob/cfulnecky/formik/src/components/input-form/.test.js

I am having the same issue when trying to change an input value:
fireEvent.change(getByLabelText(/email/i), {target: {value: fakeUsername}});

Obviously, wrapping this in act(() => {}) doesn't do anything to mitigate the problem.

Have you tried this?

await act(async() => {
    fireEvent.change(getByLabelText(/email/i), {target: {value: fakeUsername}});
});

@2Steaks Thanks for that snippet. Works for me.

Looks like in https://github.com/jaredpalmer/formik/blob/master/packages/formik/src/Formik.tsx#L534-L553
setValues and setFieldValue both use the hook useEventCallback so you need to wait on the the promise.

const setValues = useEventCallback((values: Values) => {
    dispatch({ type: 'SET_VALUES', payload: values });
    return validateOnChange
      ? validateFormWithLowPriority(state.values)
      : Promise.resolve();

Including this information in the documentation would be very helpful.

Edit: Wouldn't using wait utility fix the issue? https://testing-library.com/docs/dom-testing-library/api-async#wait

Running into this while attempting to upgrade from v1 to v2, and having it cause warnings in hundreds of tests. I'd like to avoid having to wrap all of these with async act calls if I can help it... Did anyone find any other workaround? Using @testing-library/react and @testing-library/user-event fwiw.

@pleunv I use the same libraries, and I have written my test like this. I hope this will help.

const onSubmit = jest.fn();

render(<Form onSubmit={onSubmit} />);

// without act()
userEvent.click(screen.getByRole('button', { name: 'foo' });

await waitFor(() => expect(onSubmit).toHaveBeenCalledWith({ /** XXX */ }));

Did anyone was able to fix it using Enzyme?
Does that warning indicate that there is an issue in the website? or is it a warning saying you are doing something wrong in your test?

After numerous attempts this is what worked for me:

import { render, fireEvent, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('validates the email field', async () => {
  const { getByText, getByPlaceholderText } = render(<Form />)
  const emailInput = getByPlaceholderText('Email')

  userEvent.type(emailInput, 'invalidemail.com')
  fireEvent.blur(emailInput)

  await waitFor(() => {
    expect(getByText(/Please enter a valid email/i)).toBeInTheDocument()
  })
})
  • Use userEvent.type to type into the input.
  • Don't wrap anything in act.
  • Wrap the assertions in await waitFor block (waitFor is the new API replacing the previous wait).
Was this page helpful?
0 / 5 - 0 ratings

Related issues

pmonty picture pmonty  路  3Comments

jaredpalmer picture jaredpalmer  路  3Comments

PeerHartmann picture PeerHartmann  路  3Comments

sibelius picture sibelius  路  3Comments

jaredpalmer picture jaredpalmer  路  3Comments