'An update to InfiniteScroll inside a test was not wrapped in act(...)

I'm trying to build a component test for one of my components that actually uses some data that we fetch from the API but I'm getting some errors...

I'm working with React Testing Library and MSW to create a mock server to intercept my post request from reaching my actual server

But I'm getting this error below :

 console.error
    Warning: An update to InfiniteScroll 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 */
    
    This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
        at InfiniteScroll (/home/rosumber/sources/clear-cms/core/frontend/api/src/client/components/results/InfiniteScroll/index.tsx:9:29)

      25 |             setHasMore(res.data.hasMore)
      26 |         }).catch(() => {
    > 27 |             setLoading(false)
         |             ^
      28 |             setFailed(true)
      29 |         })
      30 |

      at printWarning (node_modules/react-dom/cjs/react-dom.development.js:67:30)
      at error (node_modules/react-dom/cjs/react-dom.development.js:43:5)
      at warnIfNotCurrentlyActingUpdatesInDEV (node_modules/react-dom/cjs/react-dom.development.js:24064:9)
      at setLoading (node_modules/react-dom/cjs/react-dom.development.js:16135:9)
      at src/client/components/results/InfiniteScroll/utils/useResutlsHook.ts:27:13

I have wrapped my test renders with act(() => {}) as the error requested but I'm still running on the same error on multiple tests...

I call my API using this hook :

import {useEffect, useState} from "react"
import axios from "axios"

export default function useResutlsHook(page: number){

    const [loading, setLoading] = useState<boolean>(true)
    const [results, setResults] = useState<[]>([])
    const [failed, setFailed] = useState<boolean>(false)
    const [hasMore, setHasMore] = useState<boolean>(false)

    useEffect(() => {
        setLoading(true)
        setFailed(false)

        axios.post(`/results-data`,
            {page: page}
        ).then(res => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            setResults(prev => {
                return [...prev, ...res.data.results[0]]
            })
            setLoading(false)
            setFailed(false)
            setHasMore(res.data.hasMore)
        }).catch(() => {
            setLoading(false)
            setFailed(true)
        })

        // eslint-disable-next-line @typescript-eslint/no-empty-function
        return () => { }
    }, [page])

    return {loading, results, failed, hasMore}
}

And that's the component :

import React, {useCallback, useRef, useState} from "react";
import useResutlsHook from "./utils/useResutlsHook";
import Item from "../Item"

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const InfiniteScroll =  () => {

    const [page, setPage] = useState<number>(1)
    const {loading, results, failed, hasMore} = useResutlsHook(page)

    //unnecessary code

    return (
        <div data-testid="infinite-scroll-container">
            {results.map((item, index) => {
                if (results.length - 1 === index) return <Item key={index} {...item} ref={lastElement}/>
                return <Item key={index} {...item} ref={null}/>
            })}
            {loading && <div>Loading...</div>}
            {failed && <div data-testid="error-test-message">An error has occurred...</div>}
        </div>
    )
}

export default InfiniteScroll

and thats my test file :

import {cleanup, render, screen, act} from '@testing-library/react'
import InfiniteScroll from './index'
import '@testing-library/jest-dom'

import { rest } from "msw"
import {setupServer} from "msw/node"


const server = setupServer(
    rest.post("/results-data",
        (req, res, ctx) => {
            console.log("entered here")
            const dummy = [[
                {
                    itemTitle: "Lorem ipsum - page1",
                    itemDescription: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
                },
                {
                    itemTitle: "Lorem ipsum",
                    itemDescription: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
                },
                {
                    itemTitle: "Lorem ipsum",
                    itemDescription: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
                },
                {
                    itemTitle: "Lorem ipsum",
                    itemDescription: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
                },
                {
                    itemTitle: "Lorem ipsum",
                    itemDescription: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
                }]]
            const ret = {
                "results": dummy,
                "page": 1,
                "hasMore": false,
            }

            return res(
                ctx.status(200),
                ctx.json(ret)
            )
    }
    )
)

beforeAll(() => server.listen())
afterEach(() => {
    cleanup()
    server.resetHandlers()
})
afterAll(() => server.close())

test("display infinite scroll component",async () => {
     await act( async () => {
         render(<InfiniteScroll/>)
    })

    expect(screen.getByTestId(/infinite-scroll-container/)).toBeInTheDocument()
})

test("display Loading state", async () => {

     await act( async () => {
         render(<InfiniteScroll/>)
    })


    expect(screen.getByTestId(/infinite-scroll-container/)).toBeInTheDocument()
    expect(screen.getByText("Loading...")).toBeInTheDocument()
})

test("display results", async () => {

     await act( async () => {
         render(<InfiniteScroll/>)
    })


    expect(screen.getByTestId(/infinite-scroll-container/)).toBeInTheDocument()
    expect(screen.getByText(/Lorem/)).toBeInTheDocument()
})

test("display failed state", async () => {
    server.use(
        rest.post("/results-data", (req, res, ctx) => {
            console.log("here")
            return res(ctx.status(404))
        })
    )

     await act( async () => {
         render(<InfiniteScroll/>)
    })


    expect(screen.getByTestId(/infinite-scroll-container/)).toBeInTheDocument()
    expect(screen.getByTestId(/error-test-message/)).toBeInTheDocument()
})

also, it looks like the msw mock server doesn't intercept my API call from going to the api server...



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source