Skip to content
Advertisement

Test clearing of search input field on submit (after fetching)

I want to test that a search box does calls a handler (passed as prop) with the fetched results and resets the input field afterwards.

import React, { useState } from 'react'
import Axios from 'axios'

import './style.css'

function SearchBox({ setPhotos }) {
  const [searchTerm, setSearchTerm] = useState('')

  const handleTyping = (event) => {
    event.preventDefault()
    setSearchTerm(event.currentTarget.value)
  }

  const handleSubmit = async (event) => {
    event.preventDefault()

    try {
      const restURL = `https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=${
        process.env.REACT_APP_API_KEY
      }&per_page=10&format=json&nojsoncallback=1'&text=${encodeURIComponent(
        searchTerm
      )}`
      const { data } = await Axios.get(restURL)
      const fetchedPhotos = data.photos.photo
      setPhotos(fetchedPhotos)
      setSearchTerm('') // 👈 This is giving trouble
    } catch (error) {
      if (!Axios.isCancel(error)) {
        throw error
      }
    }
  }

  return (
    <section>
      <form action="none">
        <input
          aria-label="Search Flickr"
          placeholder="Search Flickr"
          value={searchTerm}
          onChange={handleTyping}
        />
        <button type="submit" aria-label="Submit search" onClick={handleSubmit}>
          <span aria-label="search icon" role="img">
            🔍
          </span>
        </button>
      </form>
    </section>
  )
}

export default SearchBox
import React from 'react'
import { render, fireEvent, waitFor, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { rest } from 'msw'
import { setupServer } from 'msw/node'

import SearchBox from '.'
import { act } from 'react-dom/test-utils'

const fakeServer = setupServer(
  rest.get(
    'https://api.flickr.com/services/rest/?method=flickr.photos.search',
    (req, res, ctx) =>
      res(ctx.status(200), ctx.json({ photos: { photo: [1, 2, 3] } }))
  )
)

beforeAll(() => fakeServer.listen())
afterEach(() => fakeServer.resetHandlers())
afterAll(() => fakeServer.close())

...

test('it calls Flickr REST request when submitting search term', async () => {
  const fakeSetPhotos = jest.fn(() => {})
  const { getByRole } = render(<SearchBox setPhotos={fakeSetPhotos} />)

  const inputField = getByRole('textbox', { name: /search flickr/i })
  const submitButton = getByRole('button', { name: /submit search/i })

  userEvent.type(inputField, 'Finding Walley')
  fireEvent.click(submitButton)

  waitFor(() => {
    expect(fakeSetPhotos).toHaveBeenCalledWith([1, 2, 3])
    waitFor(() => {
      expect(inputField.value).toBe('')
    })
  })
})

This is the error:

Watch Usage: Press w to show more.
  ●  Cannot log after tests are done. Did you forget to wait for something async in your test?
    Attempted to log "Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
        in SearchBox (at SearchBox/index.test.js:38)".

      38 |           aria-label="Search Flickr"
      39 |           placeholder="Search Flickr"
    > 40 |           value={searchTerm}
         |       ^
      41 |           onChange={handleTyping}
      42 |         />
      43 |         <button type="submit" aria-label="Submit search" onClick={handleSubmit}>

Advertisement

Answer

waitFor returns a Promise so you need to use await:

await waitFor(() => { 
  expect(fakeSetPhotos).toHaveBeenCalledWith([1, 2, 3]); 
  expect(inputField.value).toBe(''); 
});
Advertisement