Testing debounced function React - React-testing-library

Viewed 3793

I have the following component

import React, { useState, useEffect } from 'react';
import { FiSearch } from 'react-icons/fi';
import { useProducts } from '../../hooks';

export default function SearchBar() {
  const [query, setQuery] = useState('');
  const [debounced, setDebounced] = useState('');

  useEffect(() => {
    const timeout = setTimeout(() => {
      setDebounced(query);
    }, 300);
    return () => {
      clearTimeout(timeout);
    };
  }, [query]);

  const handleChange = (e) => {
    e.preventDefault();
    setQuery(e.target.value);
  };

  useProducts(debounced);

  return (
    <div className="search-form">
      <FiSearch className="search-form__icon" />
      <input
        type="text"
        className="search-form__input"
        placeholder="Search for brands or shoes..."
        onChange={handleChange}
        value={query}
      />
    </div>
  );
}

I want to test if useProducts(debounced); is actually called 300ms later after typing. I sadly have no Idea where to begin and was hoping someone could help.

1 Answers

I'd recommend using @testing-library/user-event for typing on the <input> element, as it more closely simulates a user's triggered events.

As for the test, you should mock useProducts implementation to assert that it's called properly.

import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import SearchBar from '<path-to-search-bar-component>'; // Update this accordingly
import * as hooks from '<path-to-hooks-file>'; // Update this accordingly

describe('Test <SearchBar />', () => {
    it('should call useProducts after 300ms after typing', async () => {
        const mockHook = jest.fn();
        jest.spyOn(hooks, 'useProducts').mockImplementation(mockHook);
        render(<SearchBar />);
        const input = screen.getByPlaceholderText('Search for brands or shoes...');
        userEvent.type(input, 'A');
        expect(mockHook).not.toHaveBeenCalledWith('A'); // It won't be called immediately
        await waitFor(() => expect(mockHook).toHaveBeenCalledWith('A'), { timeout: 350 }); // But will get called within 350ms
        jest.clearAllMocks();
    });
});
Related