react-testing-library version: 9.3.1react version: 16.12.0node version: 10.16.0yarn version: 1.19.1https://github.com/donaldpipowitch/testing-library-react-act-bug-demo
Updated @testing-library/react from 9.3.0 to 9.3.1. Also see https://github.com/testing-library/react-testing-library/pull/514 (afaik the only change in that version bump).
A new warning appeared and I don't know, if this is a bug or not.
https://github.com/donaldpipowitch/testing-library-react-act-bug-demo
Note that using an "await" somewhere can make the error go away, so this might be some race condition thingy with the event loop.
A new warning appeared and I don't know, if this is a bug or not.
If it's not a bug, but intended - everything is fine. If it's a bug, the warning should disappear after the bug fix.
I'll have a look at this next week.
@donaldpipowitch @threepointone I don't have much background info here but I think it's possible that something within formik may be causing this issue?
I encountered the same (or similar?) error (below) when I initially built my form with the formik useFormik hook. As a test I swapped out useFormik with a useState hook and the test stopped throwing the error.
As an additional test I started checking previous versions of @testing-library/react and it appears the issue began w/ 9.3.1. The test did not throw the error while using formik w/ react-testing-library 9.3.0.
Hope that's helpful.
@testing-library/react version 9.3.2@apollo/react-testing version 3.1.3react version 16.12.0formik version 2.0.6node version 12.13.0yarn version 1.19.1console.error node_modules/react-dom/cjs/react-dom.development.js:530
Warning: An update to Login 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 Login
in ApolloProvider (created by MockedProvider)
in MockedProvider
but I think it's possible that something within formik may be causing this issue?
Yeah, it's likely. I personally don't know if the warning is correct or a "false negative".
@donaldpipowitch My previous comment may not be accurate about it involving formik. After hacking a bit more I was able to get my tests to pass w/o the error being thrown.
My first problem was that I was incorrectly using the apollo mock provider and had the result returning wrong.
Secondly I wasn't calling any of the wait* variations to allow the mocked api request to resolve.
Here was my particular test after I got it fixed. Hope it helps.
import React from 'react';
import { MockedProvider } from '@apollo/react-testing';
import '@testing-library/jest-dom/extend-expect';
import { fireEvent, render, waitForElement } from '@testing-library/react';
import LoginPage, { loginMutation } from './Login';
it('renders a message if the login is not successful', async () => {
const email = '[email protected]';
const password = 'badpassword';
const mocks = [
{
request: {
query: loginMutation,
variables: {
email,
password
}
},
result: { data: { loginUser: null } }
}
]
const { getByLabelText, getByTestId, getByText } = render(
<MockedProvider mocks={mocks} addTypename={false}>
<LoginPage />
</MockedProvider>
);
fireEvent.change(getByLabelText('Email:'), {
target: { value: email },
});
fireEvent.change(getByLabelText('Password:'), {
target: { value: password },
});
fireEvent.click(getByText('Login', { selector: 'button' }));
const messageEl = await waitForElement(() => getByTestId('message'));
expect(messageEl).toHaveTextContent(
'Invalid email and/or password'
);
});
@donaldpipowitch as a follow up to my comment above. I added an await to your example repo w/ "@testing-library/react": "^9.3.2", and was able to get the tests to pass
import React from 'react'
import {render, fireEvent, waitForElement} from '@testing-library/react'
import { Formik, Form, useField } from 'formik';
const Input = ({ label, name }) => {
const [field] = useField(name);
return (
<label>
{label}
<input {...field} />
</label>
);
};
test('formik example', () => {
const { getByLabelText } = render(
<Formik
initialValues={{
email: ''
}}
onSubmit={() => {}}
>
<Form>
<Input name="email" label="Email address" />
</Form>
</Formik>
);
fireEvent.change(getByLabelText('Email address'), {
target: { value: '[email protected]' }
});
waitForElement(() => getByLabelText('Email address')).then(el => {
expect(el).toBeDefined();
expect(el.value).toEqual('[email protected]');
})
})
@ghargrove Sorry that I haven't pointed it out in the description that using an "await" will make this warning go away. I noticed it as well when I initially found the error here: https://github.com/testing-library/react-testing-library/pull/514#issuecomment-551418182. I'll update the description.
I'm fairly confident the issue I was encountering was https://github.com/testing-library/react-testing-library/issues/224.
After my updates in my previous comment I continued to have tests that would sometimes run without the errors and other times would throw them. I couldn't shake the feeling it was related to formik as they'd stop every time I pulled it out.
I had to tweak my tests a bit but now I've got them running without issues.
One of the biggest things that took me a minute to realize was that my component implementation was doing <div data-testid="message">{error && 'Invalid login'}</div> instead of {error && <div data-testid="message">Invalid login</div>}. Therefore await findByTestId('message') was always resolving before all of the async actions has a chance to take place
I'm getting the same (red) warning on v9.3.2 and even 9.3.0 if I downgrade. Not using formik at all here. The issue appears to be 100% related to a dispatch call within a useEffect.
const Foo: React.FC = () => {
const [, api] = someContext.use();
useEffect(() => {
let didCancel = false;
api.fetchSomething(() => didCancel);
return () => {
didCancel = true;
};
}, [api]);
return null;
};
I even simplified the fetch call to this simple Promise.resolve:
export default async function fetchSomething() {
return Promise.resolve([]);
}
Still, the same (red) warning.
+1, seeing this error on 9.40 and 9.32. Downgrading to 9.3.0 fixes the issue. My setup is just react without external dependencies.
I can confirm that sticking to 9.3.0 makes me not have the warning, while 9.3.1 does.
Things I did:
I'm 99% certain that in this case y'all are suffering from the thing I discuss in the appendix of this blog post. TL;DR: act is working properly (as is React Testing Library).
Basically something is happening in some of your component code (or the code of your dependencies) after the test completes. You need to wait for those things to finish before your test completes (or your test is incomplete... no pun intended).
So in the case of @donaldpipowitch's demo, I added a single await wait() as the last line of the test and the error went away. Normally you'll want to have some kind of assertion for what actually happened there, but thanks to the fact that wait is wrapped in act we get the error to go away and if there is a problem, it will be more likely to surface.
Good luck!
Am I the only one that keeps forgetting that and get confused about the messaging? And then read a message like from @kentcdodds and think 'Yes of course!'
I don't think you're alone and I'm not sure how to help people understand this when they bump into it 馃槵
I have bumped into this warning many times and now I finally solved it seems the problem was not await for the act function so no when I used it as in following code it works:
import { cleanup, fireEvent, render } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import React from "react";
import RecaptchaSample from "../../../../../../src/pages/en/devtools/recaptcha-sample";
import { act } from "react-dom/test-utils";
jest.mock("../../../../../../src/utils/recaptcha-service", () => {
return {
__esModule: true,
default: {
getRecaptchaToken: async () => "TOKEN_EXAMPLE",
},
};
});
describe("RecaptchaSample", () => {
afterEach(cleanup);
it("it should get recaptcha", async () => {
const recaptchaSample = render(<RecaptchaSample></RecaptchaSample>);
const textArea = recaptchaSample.container.querySelector("textarea");
const getRecaptchaButton = recaptchaSample.container.querySelector("button");
expect(getRecaptchaButton).toHaveTextContent("Test Recaptcha");
await act(async () => { . /// notice await
fireEvent.click(getRecaptchaButton);
});
expect(textArea.value).toBe("TOKEN_EXAMPLE");
});
});
I believe this happens because my component has an async clickHandler:
const RecaptchaSample: NextPage<IProps> = () => {
const [token, setToken] = useState("");
const clickHandler = async () => { // notice click handler is async
const recaptchatoken = await RecaptchaService.getRecaptchaToken();
setToken(recaptchatoken);
};
return (
<div>
<h1>Google Recaptcha Example</h1>
<Button onClick={clickHandler}>Test Recaptcha</Button>
<textarea css={styles.textAreaToken} value={token} readOnly={true}></textarea>
</div>
);
};
I have bumped into this warning many times and now I finally solved it seems the problem was not await for the act function so no when I used it as in following code it works:
import { cleanup, fireEvent, render } from "@testing-library/react"; import "@testing-library/jest-dom/extend-expect"; import React from "react"; import RecaptchaSample from "../../../../../../src/pages/en/devtools/recaptcha-sample"; import { act } from "react-dom/test-utils"; jest.mock("../../../../../../src/utils/recaptcha-service", () => { return { __esModule: true, default: { getRecaptchaToken: async () => "TOKEN_EXAMPLE", }, }; }); describe("RecaptchaSample", () => { afterEach(cleanup); it("it should get recaptcha", async () => { const recaptchaSample = render(<RecaptchaSample></RecaptchaSample>); const textArea = recaptchaSample.container.querySelector("textarea"); const getRecaptchaButton = recaptchaSample.container.querySelector("button"); expect(getRecaptchaButton).toHaveTextContent("Test Recaptcha"); await act(async () => { . /// notice await fireEvent.click(getRecaptchaButton); }); expect(textArea.value).toBe("TOKEN_EXAMPLE"); }); });I believe this happens because my component has an async clickHandler:
const RecaptchaSample: NextPage<IProps> = () => { const [token, setToken] = useState(""); const clickHandler = async () => { // notice click handler is async const recaptchatoken = await RecaptchaService.getRecaptchaToken(); setToken(recaptchatoken); }; return ( <div> <h1>Google Recaptcha Example</h1> <Button onClick={clickHandler}>Test Recaptcha</Button> <textarea css={styles.textAreaToken} value={token} readOnly={true}></textarea> </div> ); };
Thanks for that. I also have async action on click and it was really hard to find this solution.
act is not a promise, so you should not await it:
console.error node_modules/react-dom/cjs/react-dom-test-utils.development.js:87
Warning: Do not await the result of calling act(...) with sync logic, it is not a Promise.
Act is a promise that you can await if you pass it an async function.
However, in this case you shouldn't need to use act directly at all. 99% of the time, you shouldn't need to use act. All of the utilities exposed by @testing-library/react are automatically wrapped in act for you.
got into a situation that I want to use document.querySelector for a complex query and not one the methods provider by react-testing-library, but I can't get rid of that act warning, any ideas? @kentcdodds
btw if I add a data-testid and then switch to findByTestId it works, just wondered how to remove the warning myself.
I've been able to fix this by either wrapping the document.querySelector call in waitFor (I feel like this only makes sense if it appears asyncronously) or by putting waitFor(() => {}) at the end of the test (similarly to what @kentcdodds suggested with await wait()).
In my case, it seems like my whole test can be synchronous, but React just complains about using Formik without act (unless I use waitFor). Is waitFor(() => {}) at the end still a good way to flush these warnings as with await wait()? If so, why is the callback required in waitFor? I would think it should be optional to encourage fixing the act warning. If not, what is the migration path for await wait()?
For those having problem with act give this article a complete read it will clear things up:
https://kentcdodds.com/blog/common-mistakes-with-react-testing-library
Most helpful comment
I have bumped into this warning many times and now I finally solved it seems the problem was not await for the act function so no when I used it as in following code it works:
I believe this happens because my component has an async clickHandler: