vatsal chauhan
vatsal chauhan

Reputation: 99

How to test a loading screen in vitest

In my <App /> component I want to show a loading screen until I've fetched my data and after fetching my data all the images(whose source's I got from my data) are loaded.

I am having difficulty to test that.

I want in essence something like this

const { products } = await vi.hoisted(
  async () => await import('./mock-products.js')
)

vi.mock('../../ShoppingCart-Core/api.js', () => {
  return {
    fetchData: vi.fn(
      async () =>
        new Promise((resolve) => {
          resolve(products)
        })
    ),
  }
})

  it('Loading Screen', () => {
    const {queryAllByTitle} = render(<App />)
    let products = queryAllByTitle(/category/i)
    expect(products.length).toBe(0)

//  do something that fetchs your mocked data and images are also loaded

    products = queryAllByTitle(/category/i)
    expect(products.length).toBe(0)
  })

I know how to implement it but don't know how to test it.

I've tried to do this

vi.mock('../../ShoppingCart-Core/api.js', () => {
  return {
    fetchData: vi.fn(
      async () =>
        new Promise((resolve) => {
          setTimeout(() => {
            resolve(products)
          }, 500)
        })
    ),
  }

it('Loading Screen', async () => {
    vi.useFakeTimers()
    render(<App />)

    await waitFor(() => {
      if (fetchData.mock.calls.length > 0) {
        vi.advanceTimersByTime(800)
      } else {
        throw new Error('Fetch not called yet')
      }
})

I am waiting for fetchData to be called and then advance timer so that the promise is resolved but. it doesn't work. Advancing timers doesn't do anything and just says waitFor timed out, and also the promise is not resolved.

My implementation in useEffect

  const [products, setProducts] = useState([])

  useEffect(() => {
    const getProducts = async () => {
      const products = await fetchData()
      setProducts(products)
    }

    getProducts()
  }, [])

Upvotes: 0

Views: 31

Answers (1)

vatsal chauhan
vatsal chauhan

Reputation: 99

You can use fireEvent to load images and then test if the loading state before and after load event.

   it('Loading Screen', async () => {
    const { getAllByAltText, findAllByTitle, getByTestId } = render(<App />)

    const loadingScreen = getByTestId('loading-screen')
    expect(loadingScreen).toBeInTheDocument()

    await findAllByTitle(/category/i)
    expect(loadingScreen).toBeInTheDocument()

    const images = getAllByAltText('product image')
    expect(loadingScreen).toBeInTheDocument()
    images.forEach((image) => fireEvent.load(image))
    expect(loadingScreen).not.toBeInTheDocument()
  })


Upvotes: 0

Related Questions