React testing library's waitFor() returns null

Viewed 5646

I'm testing the rejection of the submit event of my login form. If the user just submitted the form without filling up the username and password, the two error messages must show up and it should pass the test. But the result of the test shows the opposite: it shows that the username and password error messages are null. I tried using setTimeout() since the onSubmit event is asynchronous because of axios, but it still didn't pass the test. Is there anything wrong about the way I use the waitFor() utility for an asynchronous submit event?

it('Should render username and password error messages when both inputs are blank', () => {
    const { getByTestId, queryByTestId } = render(<Index />)

    fireEvent.submit(getByTestId('form'))

    expect(getByTestId('submit-button').disabled).toBeTruthy()

    setTimeout(async () => {
        expect(getByTestId('submit-button').disabled).toBeFalsy()

        const usernameError = await waitFor(() => queryByTestId('username-error'))
        expect(usernameError).toBeInTheDocument()

        const passwordError = await waitFor(() => queryByTestId('password-error'))
        expect(passwordError).toBeInTheDocument()
    }, 0)
})

Test result

1 Answers

There are a couple of changes to the test that might fix this problem. Please find them in the following code as comments

// The first is adding async to the callback of your `it` so you don't have to use a timeout
it('Should render username and password error messages when both inputs are blank', async () => {
    const { getByTestId, findByTestId, queryByTestId } = render(<Index />)

    expect(getByTestId('submit-button').disabled).toBeTruthy()

    fireEvent.submit(getByTestId('form'))

    // Here we can use waitFor which waits until the promise triggered by the last fireEvent finishes.
    await waitFor(() => {
        expect(getByTestId('submit-button').disabled).toBeFalsy()
    })

    // Finally, you should be able to just use getByTestId to locate the elements you need, and given that the promises are resolved, they should be in the document
    expect(getByTestId('username-error')).toBeInTheDocument()
    expect(getByTestId('password-error')).toBeInTheDocument()
})

Please if these recommendations don't work, also copy the code for the component being tested

Related