React Jest test - How to wrap state into an act(...)

Viewed 214

My tests are passing fine - but I still get the (in)famous *Warning: An update to Movie 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 */* 

here:

13 | 14 | const handleSelect = () => {

15 | setSelected(!selected); | ^ 16 | }; 17 | 18 | return (

I can't figure how to solve it.

My code below

Movies.tsx

    import { useState } from "react";
import { MovieSearchResults } from "../../model/movie";
import Movie from "../Movie/Movie";
import Search from "../Search/Search";
import Pager from "../Pager/Pager";

import "./Movies.css";

const Movies = () => {
    const [search, setSearch] = useState<string>("");
    const [movies, setMovies] = useState<MovieSearchResults>({
        results: [],
        total_pages: 1,
        page: 1,
    });

    const handleSearch = () => {
        fetch(
            `https://api.themoviedb.org/3/search/movie?api_key=${process.env.REACT_APP_API_KEY}&query=${search}`
        )
            .then((response) => response.json())
            .then((json) => setMovies(json));
    };

    const handlePager = (page: number) => {
        fetch(
            `https://api.themoviedb.org/3/search/movie?api_key=${process.env.REACT_APP_API_KEY}&query=${search}&page=${page}`
        )
            .then((response) => response.json())
            .then((json) => setMovies(json));
    };

    return (
        <div className="Movies">
            <Search search={search} setSearch={setSearch} onSearch={handleSearch} />
            {movies && movies.total_pages > 1 ? (
                <Pager
                    {...movies}
                    total_pages={movies.total_pages}
                    onPageChange={handlePager}
                />
            ) : (
                <></>
            )}
            {movies &&
                movies.results.map((movie) => (
                    <Movie
                        {...movie} /*here I am destructuring movie properties because I don't need to specify all*/
                        id={movie.id}
                        poster_path={movie.poster_path}
                        title={movie.title}
                        key={movie.id}
                        overview={movie.overview.substring(0, 250) + "..."}
                    />
                ))}

            {movies && movies.total_pages > 1 ? (
                <Pager
                    {...movies}
                    total_pages={movies.total_pages}
                    onPageChange={handlePager}
                />
            ) : (
                <div className="message--noResulst">
                    Sorry. There's no results available for your search...
                </div>
            )}
        </div>
    );
};

export default Movies;

Movie.tsx

import { useState } from "react";
import { MovieSearchResult } from "../../model/movie";
import classnames from "classnames";

import "./Movie.css";

const Movie = (movie: MovieSearchResult, key: number) => {
    //select Movie on click:
    const [selected, setSelected] = useState<boolean>(false);

    // use classnames to modify className on select:
    const classNames = classnames("Movie", { Movie__selected: selected });

    const handleSelect = () => {
        setSelected(!selected);
    };

    return (
        <div className={classNames} onClick={handleSelect}>
            <div className="Movie__poster-wrap">
                <img
                    className="Movie__poster"
                    src={`https://image.tmdb.org/t/p/w92${movie.poster_path}`}
                    alt="Movie poster"
                />
            </div>
            <div className="Movie__details">
                <h2>{movie.title}</h2>
                <p>{movie.overview}</p>
            </div>
        </div>
    );
};

export default Movie;

Movie.test.js

import React from "react";

import renderer from "react-test-renderer";
import { act, render, fireEvent } from "@testing-library/react";
import Movie from "./Movie";

describe("Movie", () => {
    it(" changes the class of Movie when clicked i.e. selected", async () => {
        const movie = {
            adult: false,
            backdrop_path: "/eM9MZ1dhDCbRzwQg0FWpWWtLPMn.jpg",
            genre_ids: [10752, 28, 18],
            id: 390054,
            original_language: "en",
            original_title: "Sand Castle",
            overview:
                "Set during the occupation of Iraq, a squad of U.S. soldiers try to protect a small village....",
            popularity: 38.305,
            poster_path: "/c9buG2jVRgAu68E4D4jpwlgqhO1.jpg",
            release_date: "2017-04-21",
            title: "Sand Castle",
            video: false,
            vote_average: 6.5,
            vote_count: 613,
        };

        const component = renderer.create(
            <Movie
                {...movie}
                id={movie.id}
                poster_path={movie.poster_path}
                title={movie.title}
                key={movie.id}
                overview={movie.overview.substring(0, 250) + "..."}
            />
        );

        let tree = component.toJSON();
        expect(tree).toMatchSnapshot();

        //trigger the click
        tree.props.onClick();
        // re-rendering
        tree = component.toJSON();
        expect(tree).toMatchSnapshot();
    });
});

I've looked into the act(...) functionality on Jets but couldn't figure it out.

Thanks in advance for the help and explanations

0 Answers
Related