React-testing-library: react testing library with formik & yup validation not erroring as expected

Created on 23 Nov 2018  路  6Comments  路  Source: testing-library/react-testing-library

  • react-testing-library version: 5.2.3
  • react version: 16.7.0-alpha.2
  • node version: unk
  • npm (or yarn) version: unk

Relevant code or config:

https://codesandbox.io/s/lll3rl5q0q

What you did:

Attempted to create a test for a basic form with a disabled submit button and yup for validation

What happened:

Disabled submit button remains disabled in the ui until all fields are valid, but submit button becomes enabled after a single field becomes valid when tests are run

Reproduction:

https://codesandbox.io/s/lll3rl5q0q

Problem description:

As seen in the debug output of formik in the web after a single change has been made compared to the debug output from the test; the state of the validation seems very different.
codesandbox can be a little iffy with this so suggest it might be better running localy when examining.

help wanted needs investigation question

Most helpful comment

If I remember correctly. The problem here is that you forgot that the Yup validation schema in Formik is an asynchronous call. To solve this problem, I wrapped it around a wait-statement like this:

        fireEvent.click(getByText('Post donation'))
        await wait(() => {
            expect(queryByText('You need to give the order a title')).not.toBeNull()
            expect(queryByText('You need to select a collection date')).not.toBeNull()
            expect(queryByText('You need to select a collection time')).not.toBeNull()
       })

All 6 comments

If I remember correctly. The problem here is that you forgot that the Yup validation schema in Formik is an asynchronous call. To solve this problem, I wrapped it around a wait-statement like this:

        fireEvent.click(getByText('Post donation'))
        await wait(() => {
            expect(queryByText('You need to give the order a title')).not.toBeNull()
            expect(queryByText('You need to select a collection date')).not.toBeNull()
            expect(queryByText('You need to select a collection time')).not.toBeNull()
       })

Thanks for the investigation @weyert

Awesome, I've got this all working now, thanks @weyert and @kentcdodds

it('Form with valid entry', async () => {
  ...
  const givenNameInput = getByPlaceholderText('Name');
  fireEvent.change(givenNameInput, { target: { name: 'givenName', value: 'Foo Bar' } });
  expect(givenNameInput.value).toBe('Foo Bar');
  await wait(() => {
    expect(submitButton).toBeDisabled();
  });
...
});

Out of curiosity.

How awful is it to simply do

describe('Input', () => {
it('displays an error message when a required field is touched', async () => {
    const validate = () => ({ test: 'Required' });

    const { container, getByText, getByLabelText } = render(
      <Formik validate={validate}>
        <form>
          <Field id="test" name="test" label={requiredProps.label} component={Input} />,
        </form>
      </Formik>
    );

    // blur past input
    const input = getByLabelText(requiredProps.label);
    fireEvent.blur(input);

    // Formik validations are async
    await wait();

    // Ensure error message shows
    expect(getByText('Required')).not.toBeNull();
    expect(container.querySelector('.ncss-error-msg')).not.toBeNull();
  });
});

namely, simply doing: await wait(); (without waiting for something specific/using a callback)

That's not a big deal honestly, but you could improve things a bit:

describe('Input', () => {
  it('displays an error message when a required field is touched', async () => {
    const validate = () => ({ test: 'Required' });

    const { container, getByText, getByLabelText } = render(
      <Formik validate={validate}>
        <form>
          <Field id="test" name="test" label={requiredProps.label} component={Input} />,
        </form>
      </Formik>
    );

    // blur past input
    const input = getByLabelText(requiredProps.label);
    fireEvent.blur(input);

    // Ensure error message shows
    expect(await findByText(/required/i)).not.toBeNull();
    expect(container.querySelector('.ncss-error-msg')).not.toBeNull();
  });
});

I receive a typescript error with this:

await wait(() => {
    expect(wrapper.find('TagSelect').prop('itemId')).to.eq(1);
});

It reads

Argument of type '() => void' is not assignable to parameter of type 'number'
Was this page helpful?
0 / 5 - 0 ratings

Related issues

alejandronanez picture alejandronanez  路  4Comments

good-idea picture good-idea  路  4Comments

joshvillahermosa picture joshvillahermosa  路  3Comments

kangweichan picture kangweichan  路  3Comments

jalvarado91 picture jalvarado91  路  3Comments