react library testing are not updating my screen

Viewed 3212

I recently bumped into this weird problem. I am using react-testing-library and am trying to make a simple update. Whenever the user types in the correct name, they will be awarded 10 points and it will be logged on the screen. However, currently the new score doesn't get logged (I remain at the default score which is 0) and I also get the error saying: Cannot log after tests are done. Did you forget to wait for something async in your test? Attempted to log "Warning: An update to Pokemon inside a test was not wrapped in act(...).

This is how my test code looks like

//PokemonPage.test.js
test.only("should have their score updated if they guess the name correctly", async () => {
    const guessedPokemon = "Pikachu";
    jest.spyOn(global, "fetch").mockResolvedValue({
      json: () =>
        Promise.resolve({
          name: "Pikachu",
          sprites: {
            other: {
              "official-artwork": {
                front_default:
                  "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/25.png",
              },
            },
          },
        }),
    });
    render(<Pokemon pokemonTrainer={pokemonTrainer} />);
    expect(screen.getByText(/Score: 0/)).toBeInTheDocument();
    await waitFor(() => screen.findByRole("img"));
    userEvent.type(await screen.findByRole("textbox"), guessedPokemon);
    await waitFor(() => userEvent.click(screen.getByRole("button")))
    expect(screen.getByText(/Score: 10/)).toBeInTheDocument()
  });

This is the code it is supposed to call:

//PokemonPage.js
const handleChange = (e) => setValue(e.target.value);

  const handleSubmit = async (e) => {
    e.preventDefault();
    pokemonRef.current = await getPokemon();
    setPokemonList((prev) => [
      ...prev,
      { name: pokemonRef.current.name, image: pokemonRef.current.image },
    ]);
    updateScore(value)
    setValue('')
  };

  const updateScore = async (guessedPokemonName) => {
    if (guessedPokemonName === pokemonList[pokemonList.length - 1].name) {
       setPokemonTrainerObject(prev => ({...prev, score: pokemonTrainerObject['score'] + 10 || 10 }))
    } 
  };

Basically I am submitting the user input, and if it is corrent guessedPokemonName === pokemonList[pokemonList.length - 1].name then the user object will update the score. This is what I am trying to emulate with my test.

I have tried to use the waitFor to hope that the code understands that the components needs to be updated but to no avail.

Is there anyone out there that have encountered something similar?

1 Answers

The warning from React, "Warning: An update to Pokemon inside a test was not wrapped in act(...)" means that it has detected a change to your state that is not accounted for in your testing.

In this case, this warning is wrapped inside of a more explanatory message saying that this problem occurred after your test.

In your code, you have await waitFor(() => userEvent.click(screen.getByRole("button"))). However, waitFor is meant to be used with an assertion. In other words it is meant to wait until an expectation is true -- not until an event is fired, because the firing of an event like this isn't asynchronous (the processing of it may be, but not the firing of it.)

Instead, you should just fire the event, and then waitFor something you can test that shows the process is finished. For instance, you said "they will be awarded 10 points and it will be logged on the screen". So after your event, do a waitFor that looks for the message showing that they answered it correctly, which you are already doing in the next line.

So try:

userEvent.click(screen.getByRole("button"));
await waitFor(() => {
   expect(screen.getByText(/Score: 10/)).toBeInTheDocument();
});

Or, even simpler, since findBy* methods use waitFor inside:

userEvent.click(screen.getByRole("button"));
await screen.findByText(/Score: 10/);

PS You can learn more about that error message and some different approaches to fix it by reading Kent Dodds article on it, Fix the "not wrapped in act() warning

Related