-2

I understand that I'm not supposed to add act() around user events because the testing library does it for me, but I get warnings if I don't. Specifically, for example given a MUI component with the ripple effect, here's what I find:

import { Tab as MuiTab } from "@mui/material";

describe("Tab", () => {

    it("MuiTab creates a warning ", async () => {
        const user = userEvent.setup();
        render(<MuiTab label="hello" />);

        // yep.  There's a warning here.
        await user.click(screen.getByRole("tab"));
    });

    it("MuiTab does NOT create a warning with disableTouchRipple", async () => {
        const user = userEvent.setup();

        // disabling the animation fixes the warning
        render(<MuiTab label="hello" disableTouchRipple />);

        await user.click(screen.getByRole("tab"));
    });

    it("MuiTab does NOT create a warning with act", async () => {
        const user = userEvent.setup();
        render(<MuiTab label="hello" />);

        // no warning
        await act(async () => await user.click(screen.getByRole("tab")));
    });
});

These warnings started when I upgraded @testing-library/react to ^14.0.0, which fixed other act warnings.

But now with the upgrade, I have 1.7M lines of warnings in my test suite. It's not "wrong" in that the ripple effect hasn't ended by the end of the test and I also understand that using jest timers can help resolve this.

But it feels wrong to add act throughout the testing suite or to litter my test code with fake timers just because MUI has some animations.

Given all that, what's the recommended way for me to fix these warnings?

Versions:

  • "@mui/material": "^5.12.1"
  • "@testing-library/jest-dom": "^5.16.5",
  • "@testing-library/react": "^14.0.0",
  • "@testing-library/user-event": "^14.4.3",
  • "jest": "^29.5.0"

(Also, please don't tell me that I should be asserting on the expected behavior after the click. I know that. I'm just using the Tab as an example and I get the warnings whether there's an expect afterwards or not.)

emragins
  • 4,607
  • 2
  • 33
  • 48
  • 1
    In tests, React needs to understand that certain actions will cause component updates. React testing library already wraps some of its APIs in `act`. But in some cases, you still need to use `waitFor`, `waitForElementToBeRemoved`, or `act` to tell the test: *"I'm expecting the component to update here"*. Consider reading [this](https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning), [this](https://davidwcai.medium.com/491a5629193b), [this](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library) and [this](https://meta.stackoverflow.com/questions/261592/). – tao Apr 18 '23 at 15:25
  • Thanks. I've actually read all of those articles and this question comes after a lot of research. I'm sorry you didn't like my comments on your answer, and I can only assume that the downvote and the link to the meta question is in response. As said in my question, I have 1.7 million lines of warnings that just showed up with an upgrade, and adding `act` or `waitFor` around every single animation (which I don't control) does not seem like the right solution. – emragins Apr 18 '23 at 15:53
  • You're correct in assuming I downvoted, but you're a bit off about the reasons. First of all, I should have downvoted your question earlier, as it didn't contain any research effort. You're supposed to document all relevant research effort. The other reason is you're not looking for a correct solution, but for one that *"seems right"* (whatever that means). In essence, you're asking for opinionated advice, preferably one which supports your own feelings regarding your own code-base, not for factual based answers. And that's *off-topic*. – tao Apr 18 '23 at 16:14

1 Answers1

0

The root cause ended up being that npm resolved version dependencies of @testing-library/dom incorrectly.

This took a lot of time to figure out. Specifically, I would tweak the package.json and pin specific versions, then run npm install && npm run test &> output##-version-details.log so that I could track what happened with each change. When I started seeing conflicts in my output is when I looked at the package-lock.json changes. By leveraging git I could then start easily compare package-lock.json files.

Where @testing-library/react wanted @testing-library/dom as ^9.0.0, what npm had actually resolved was 8.20.0. I don't know why -- the package.json did not specify a dom version.

Once @testing-library/react was using the correct dom version, the bulk of the warnings went away.

The upgrade also resulted in 7 new test failures out of ~950 tests, but those should be easy to resolve.


Regarding wrapping all user input in act() or waitFor()

While I don't have specific evidence, I read a number of reports about waitFor suppressing error messages. It doesn't make sense to waitFor a click event since there's nothing identifiable for the library to say "yes, okay, it's done". I suspect that it "works" because it's suppressing the warnings and/or just a stand-in for wrapping it in another act() directly.

On that note, wrapping events in act() seemed like it should have been superfluous since testing-library already wraps user events in act. And when I did start adding it through my test suite, I started getting the Warning: The current testing environment is not configured to support act(...) error in some cases.

Hence none of the documentation on react testing library nor my own experiences indicated to me this was the "right" solution.


Regarding configuring MUI

This was a dead-end.

Before I figured out the package issue, I also spent a lot of time trying to disable the TouchRipple effect in MUI and transitions in MUI. This resulted in ~20K warning lines reduced total, but it also resulted in more failed tests, too. I might go back later and disable TouchRipple just because it's not necessary for tests.

source for globally disabling

import { createTheme } from '@mui/material';

const themeOverrides = createTheme({
  transitions: {
    // So we have `transition: none;` everywhere
    create: () => 'none',
  },
  components: {
    // Name of the component ⚛️
    MuiButtonBase: {
      defaultProps: {
        // The props to apply
        disableRipple: true, // No more ripple, on the whole application !
      },
    },
  },
});
## Then use the new these in a ThemeProvider for your tests.  
## You can nest themes.
emragins
  • 4,607
  • 2
  • 33
  • 48