Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

await userEvent.type causes setState in async effect warns not wrapped in act(...) #424

Closed
levinqdl opened this issue Aug 5, 2020 · 5 comments
Labels

Comments

@levinqdl
Copy link

levinqdl commented Aug 5, 2020

  • @testing-library/user-event version: 12.1.0
  • Testing Framework and version: jest
  • DOM Environment: jsdom

Relevant code or config

// App.js
import React, {useState, useEffect} from "react";
import "./styles.css";

export default function App() {
  const [value, setValue] = useState('')
  const [content, setContent] = useState('')
  useEffect(() => {
    Promise.resolve(value).then(setContent)
  }, [value])
  return (
    <div className="App">
      <div>{content}</div>
      <input placeholder="input" value={value} onChange={({target: {value}}) => setValue(value)}/>
    </div>
  );
}
// App.test.js
import React from 'react'
import {render, screen} from '@testing-library/react'
import App from './App'
import userEvent from '@testing-library/user-event'

test('waining', async () => {
    render(<App/>)
    await userEvent.type(screen.getByPlaceholderText('input'), 'abc')
    expect(screen.getByText('abc')).toBeTruthy()
})

What you did:

testing a compoent with setState in async useEffect

What happened:

test passes with a waning of not wrapped in act(...)

Warning: An update to App 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 */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act
    in App (at App.test.js:15)

Reproduction repository:

codesandbox demo: https://codesandbox.io/s/heuristic-chaum-v50nn?file=/src/App.test.js

Problem description:

When await userEvent.type which triggers an async effect to setState another state, the test passes, but a warning of not wrapped in act(...) happens.

If not await userEvent.type, but use waitFor to assert, no warning happens:

// this test passes
test('pass', async () => {
    render(<App/>)
    userEvent.type(screen.getByPlaceholderText('input'), "abc")
    await waitFor(() => {
        expect(screen.getByText('abc')).toBeTruthy()
    })
})

Suggested solution:

I'm not very understand what act is doing, and not sure if this is a bug or expected behavior.

If it's a bug, the warning should not happen.

If it's expected, this case should be documented. It's very confusing to see the warning.

@kentcdodds
Copy link
Member

kentcdodds commented Aug 5, 2020

Hi @levinqdl,

This is expected behavior and has nothing to do with user event and everything to do with what your application is doing as a result of the events that are being fired. To learn more about the act warning read this: https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning

So what you have with the waitFor solution works well, though you might prefer this:

// this test passes
test('pass', async () => {
    render(<App/>)
    userEvent.type(screen.getByPlaceholderText('input'), "abc")
    // this assertion is available if you're using @testing-library/jest-dom
    expect(await screen.findByText('abc')).toBeInTheDocument()
})

I can't think of how we would document it any differently. If your app does something asynchronous, then you should use the async utilities (like waitFor or the find* queries). If your app isn't doing anything async, then you won't get the error.

@levinqdl
Copy link
Author

levinqdl commented Aug 5, 2020

Hi, @kentcdodds

What confused me was since userEvent.type return a promise, so I should always await it. But now it seems await is not good for all cases. So I think it should be documented to remind users about it.

@marcosvega91
Copy link
Member

Just for info type's promise is always resolved immediately except when you pass delay parameter

@kentcdodds
Copy link
Member

I'll add a better note in the README about this.

@kentcdodds kentcdodds reopened this Aug 5, 2020
@kentcdodds
Copy link
Member

🎉 This issue has been resolved in version 12.1.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants