The infamous "a test was not wrapped in act(...)"
Asked Answered
M

1

6

I have a test that generates the warning An update to DropDownMultiPick inside a test was not wrapped in act(...)

And the warning points to this code :

  10 |   const toggleDropdown = () => {
> 11 |     setIsOpen(!isOpen);
     |     ^
  12 |   };

my component

const DropDownMultiPick = ({ title, options, selectedOptions, handleOptionsChange }) => {
  const [isOpen, setIsOpen] = useState(false);
  const [tempSelectedOptions, setTempSelectedOptions] = useState([...selectedOptions]);

  const toggleDropdown = () => {
    setIsOpen(!isOpen);
  };
  ...

My test

const title = "Test Options";
const options = ["Option 1", "Option 2", "Option 3"];
const selectedOptions = ["Option 1"];
const handleOptionsChange = jest.fn();
  it("should open and close the dropdown menu on toggle button click", async () => {
    render(
      <DropDownMultiPick
        title={title}
        options={options}
        selectedOptions={selectedOptions}
        handleOptionsChange={handleOptionsChange}
      />
    );

    const user = userEvent.setup();

    const toggleButton = screen.getByRole("button", { name: "Option 1" });

    await user.click(toggleButton);
    // expect(await screen.findByRole("button", { name: /done/i })).toBeInTheDocument();

    await waitFor(() => {
      expect(screen.getByRole("button", { name: /done/i })).toBeInTheDocument();
    });
  });

What I tried

I added an act in my component. The warning is gone, but I don't think it is the best solution to add the act at this place!

const DropDownMultiPick = ({ title, options, selectedOptions, handleOptionsChange }) => {
  const [isOpen, setIsOpen] = useState(false);
  const [tempSelectedOptions, setTempSelectedOptions] = useState([...selectedOptions]);

  const toggleDropdown = async () => {
    await act(async () => {
      setIsOpen(!isOpen);
    });
  };

Question

What is the best solution to get rid of this warning? I looked on this website, but it dit not help : https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning

EDIT

The whole component :

// src/components/batchrec/dropdown-menu-multipick.jsx
import React, { useState } from "react";
import "./dropdown-menu-multipick.styles.scss";

const DropDownMultiPick = ({ title, options, selectedOptions, handleOptionsChange }) => {
  const [isOpen, setIsOpen] = useState(false);
  const [tempSelectedOptions, setTempSelectedOptions] = useState([...selectedOptions]);

  const toggleDropdown = () => {
    setIsOpen(!isOpen);
  };

  const handleItemClick = (option) => {
    if (tempSelectedOptions.includes(option)) {
      setTempSelectedOptions(tempSelectedOptions.filter((item) => item !== option));
    } else {
      setTempSelectedOptions([...tempSelectedOptions, option]);
    }
  };

  const handleClose = () => {
    setIsOpen(false);
  };

  const handleDone = () => {
    handleOptionsChange(tempSelectedOptions);
    handleClose();
  };

  return (
    <div className="dropdown-multipick__container">
      <span className="label-title-style-5">
        <span className="dropdown-multipick__text label-title-style-1">{title}: </span>
        <button className="dropdown-multipick__toggle" onClick={toggleDropdown}>
          {selectedOptions.join(", ") || "Select..."}
        </button>
      </span>
      {isOpen && (
        <div className="dropdown-multipick__menu">
          {options.map((option, index) => (
            <div
              key={index}
              className={`dropdown-multipick__item ${
                tempSelectedOptions.includes(option) ? "dropdown-multipick__item--selected" : ""
              }`}
              onClick={() => handleItemClick(option)}
            >
              {option}
            </div>
          ))}
          <button className="dropdown-multipick__done" onClick={handleDone}>
            Done
          </button>
        </div>
      )}
    </div>
  );
};

export default DropDownMultiPick;
Misgiving answered 28/4, 2023 at 2:38 Comment(5)
Can you share a bit more code on how toggleDropdown is called? Is there any asynchronous events?Mainsheet
act is supposed to be put in the test, not in your components. You can try to wrap the user.click event into an actLilah
@Mainsheet Added the whole component. I don't see any async event in there! I don't get it.Misgiving
@Lilah Yes, this will silence the warning, but I'll get another warning from visual studio code. This rule aims to avoid the usage of act to wrap Testing Library utils just to silence "not wrapped in act(...)" warnings.. github.com/testing-library/eslint-plugin-testing-library/blob/…Misgiving
Hi @peterphonic, it seems like this can be caused by mismatch between your @testing-library/react version and @testing-library/user-event. Can you try upgrading both of them to latest version? (@testing-library/[email protected] and @testing-library/[email protected])Mainsheet
M
6

This seems to be a bug on react-testing-library itself when used with React 18. The bug fix has already been merged in this issue and released in @testing-library/react version 14.0.0.

You can update your @testing-library/react to the latest version and it should solve the issue.

Mainsheet answered 28/4, 2023 at 16:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.