Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Timed out in waitForElementToBeRemoved error in RTL

I have a basic login form made using Formik, just trying to write an RTL and jest code to fill out the details, submit the form, and navigate to the Home page. Therefore, wrote the following:

 it('Fill the form and login', async () => {
    render(<Login />)

    await userEvent.type(screen.getByTestId('emailInput'), '[email protected]')
    await userEvent.type(screen.getByTestId('passwordInput'), 'neeraj')
    await userEvent.click(screen.getByTestId('submitBtn'))

    expect(window.location.pathname).toBe('/')
  })

The above test is getting passed but getting the classic act error.

When testing, code that causes React state updates should be wrapped into act(...):
    
    act(() => {
      /* fire events that update state */
    });
    /* assert on the output */

So, referred to this blog post and used waitForElementToBeRemoved function as suggested but now the test is getting failed with a timeout error.

Updated test case with waitForElementToBeRemoved

it('Fill the form and login', async () => {
    render(<Login />)

    await userEvent.type(screen.getByTestId('emailInput'), '[email protected]')
    await userEvent.type(screen.getByTestId('passwordInput'), 'neeraj')
    await userEvent.click(screen.getByTestId('submitBtn'))

    expect(window.location.pathname).toBe('/')
    await waitForElementToBeRemoved(screen.getByTestId('emailInput'))
  })

Error:

 Timed out in waitForElementToBeRemoved.

    Ignored nodes: comments, <script />, <style />
    <html>
      <head />
      <body>
        <div>
          <div>
            <div
              data-testid="loginCard"
            >
              <form
                data-testid="loginForm"
              >
                <div>
                  <label
                    for="email-input"
                  >
                    Email
                  </label>
                  <input
                    data-testid="emailInput"
                    id="email-input"
                    name="email"
                    type="email"
                    value="[email protected]"
                  />
                </div>
                <div>
                  <label
                    for="pass-input"
                  >
                    Password
                  </label>
                  <input
                    data-testid="passwordInput"
                    id="pass-input"
                    name="password"
                    type="password"
                    value="neeraj"
                  />
                </div>
                <button
                  data-testid="submitBtn"
                  type="submit"
                >
                  Submit
                </button>
              </form>
            </div>
          </div>
        </div>
      </body>
    </html>

      19 |
      20 |     expect(window.location.pathname).toBe('/')
    > 21 |     await waitForElementToBeRemoved(screen.getByTestId('emailInput'))
         |           ^
      22 |   })
      23 | })
      24 |

      at waitForElementToBeRemoved (node_modules/@testing-library/dom/dist/wait-for-element-to-be-removed.js:22:24)
      at Object.<anonymous> (tests/login.test.js:21:11)

Tried to wrap those three userEvent s in act function but getting the same timeout error, couldn't figure out where I am going wrong.

Code Sandbox link

like image 786
Neeraj Sewani Avatar asked Jun 30 '21 14:06

Neeraj Sewani


1 Answers

I managed to pass the test case with no act warnings.

Since you are calling an setTimeout which is an asynchronous function when the user clicks on the submitBtn button, therefore you have to wait for the promise to resolve.

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

import Login from './../pages/login'

describe('Login Page', () => {
  it('renders without crashing', () => {
    render(<Login />)
    expect(screen.getByTestId('loginCard')).toBeInTheDocument()
  })
  
  it('Fill the form and login', async () => {
    render(<Login />)

    userEvent.type(screen.getByLabelText(/email/i), '[email protected]')
    userEvent.type(screen.getByLabelText(/password/i), 'neeraj')
    userEvent.click(screen.getByTestId('submitBtn'))

    await waitFor(() => expect(window.location.pathname).toBe('/'))
  })
})

You could also do it using async act on the click event which will going to be wait for the promise to resolve and state updates to complete.

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

import Login from './../pages/login'

describe('Login Page', () => {
  it('renders without crashing', () => {
    render(<Login />)
    expect(screen.getByTestId('loginCard')).toBeInTheDocument()
  })
  
  it('Fill the form and login', async () => {
    render(<Login />)

    userEvent.type(screen.getByLabelText(/email/i), '[email protected]')
    userEvent.type(screen.getByLabelText(/password/i), 'neeraj')
    await act(async () => userEvent.click(screen.getByTestId('submitBtn')))

    expect(window.location.pathname).toBe('/')
  })
})
like image 108
Subrato Pattanaik Avatar answered Nov 01 '22 09:11

Subrato Pattanaik