Test runner throws act() warning on waitForNextTick - discussing better solution for React 16.8.1
After upgrading to React 16.8.1 ([email protected]), tests throw warnings whenever we use waitForNextTick with Promise and setTimeout, which is everywhere when we need to wait for a component to rerender with result data.
act(() => {
/* fire events that update state */
});
Sample test: https://github.com/trojanowski/react-apollo-hooks-sample-test/blob/master/src/tests/Hello-test.js
As this is just a temporary solution to the problem with useEffect hook
render(null);
await waitForNextTick();
I came up with below solution which is far from pretty and elegant, but in simple scenario it works. Since act callback can't return anything, as for now it's not supported, I had to wrap the above code inside another self executed async function.
// Mocking queries and rendering component goes here
// ...
// Asserting for the rendered mocked data
act( () => {
(async () => {
render(null);
await waitForNextTick();
expect(container).toMatchSnapshot();
})()
});
I was wondering if anybody found a better solution to this problem?
@mateusznowosielski it's a known React problem. You can read more about it here: https://github.com/facebook/react/issues/14769. There is a WIP PR with a solution: https://github.com/facebook/react/pull/14853.
I just tried @mateusznowosielski's solution and the expect call within the async function isn't even being called for me. Is there anything super obvious that I'm doing wrong?
it("renders the component", async () => {
const mockQuery = [
{
request: {
query: gql`
query HelloQuery {
hello
}
`,
variables: {},
},
result: { data: { hello: "World" } },
},
];
const waitForNextTick = () => new Promise(resolve => setTimeout(resolve));
const client = createMockApolloClient(mockQuery);
const { container, queryByText } = render(
<ApolloProvider client={client}>
<MemoryRouter>
<MyComponent />
</MemoryRouter>
</ApolloProvider>
);
const loadingStateText = queryByText("Loading!");
expect(loadingStateText).not.toBeNull();
act(() => {
(async () => {
render(null);
await waitForNextTick();
console.log("foo");
expect(container.firstChild).toMatchSnapshot();
})();
});
});
Anything in the async block within act() isn't executed at all. The console doesn't log and the snapshot isn't generated.
EDIT: Nevermind. I gave up on act() until the async act is released. I'm just running jest --silent for the time being. Sucks, but w/e.
I suppose no one came up with some viable workaround for this yet? The --silent sort of works, but it's annoying if I actually need to see console output.
I wonder if we could somehow utilize the actHack being present the code of the library. I am thinking something like check for process.env.NODE_ENV === 'test' and use the real act in that case. That should pretty much solve the problem imo.
Update: Ok, I tried exactly that just by hardcoding following inside the node_modules/react-apollo-hooks/lib/internal/actHack.js and it works just fine. I will make a PR tomorrow.
"use strict";
exports.__esModule = true;
exports.default = actHack;
var _testUtils = require("react-dom/test-utils");
function actHack(callback) {
_testUtils.act(() => {
callback();
})
}
Now in react-dom v16.9.0-alpha.0 act works with async functions:
await act(async () => {
await waitForNextTick();
...
})