mardi 22 décembre 2020

how to test a hook with async state update in useEffect?

i have a simple hook that fetches the value and sets it to option as follows:

import Fuse from 'fuse.js'
import React from 'react'

// prefetches options and uses fuzzy search to search on that option
// instead of fetching on each keystroke
export function usePrefetchedOptions<T extends {}>(fetcher: () => Promise<T[]>) {
  const [options, setOptions] = React.useState<T[]>([])
  React.useEffect(() => {
    // fetch options initially
    const optionsFetcher = async () => {
      try {
        const data = await fetcher()
        setOptions(data)
      } catch (err) {
        errorSnack(err)
      }
    }
    optionsFetcher()
  }, [])
  // const fuseOptions = {
  //   isCaseSensitive: false,
  //   keys: ['name'],
  // }

  // const fuse = new Fuse(options, fuseOptions)

  // const dataServiceProxy = (options) => (pattern: string) => {
  //   // console.error('options inside proxy call', { options })
  //   const optionsFromSearch = fuse.search(pattern).map((fuzzyResult) => fuzzyResult.item)
  //   return new Promise((resolve) => resolve(pattern === '' ? options : optionsFromSearch))
  // }

  return options
}

i am trying to test it with following code:

import { act, renderHook, waitFor } from '@testing-library/react-hooks'
import { Wrappers } from './test-utils'
import { usePrefetchedOptions } from './usePrefetchedOptions'
import React from 'react'

const setup = ({ fetcher }) => {
  const {
    result: { current },
    waitForNextUpdate,
    ...rest
  } = renderHook(() => usePrefetchedOptions(fetcher), { wrapper: Wrappers })
  return { current, waitForNextUpdate, ...rest }
}

describe('usePrefetchedOptions', () => {
  const mockOptions = [
    {
      value: 'value1',
      text: 'Value one',
    },
    {
      value: 'value2',
      text: 'Value two',
    },
    {
      value: 'value3',
      text: 'Value three',
    },
  ]
  test('searches for appropriate option', async () => {
    const fetcher = jest.fn(() => new Promise((resolve) => resolve(mockOptions)))
    const { rerender, current: options, waitForNextUpdate } = setup({ fetcher })
    await waitFor(() => {
      expect(fetcher).toHaveBeenCalled()
    })
    // async waitForNextUpdate()
    expect(options).toHaveLength(3) // returns initial value of empty options = []
  })
})

the problem is when i am trying to assert the options at the end of the test, it still has the initial value of []. However if I log the value inside the hook, it returns the mockOptions. How do I update the hook after it is update by useEffect but in async manner.

I have also tried using using waitForNextUpdate where it is commented in the code. it times out with following error: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error:

Aucun commentaire:

Enregistrer un commentaire