I'm trying to test the debounce with Jest in a component I've made that uses URL params for a search. The handleSearch
function uses the useDebounceCallback
hook from usehooks-ts (which uses lodash debounce under the hood), and returns a Carbon component.
export function ParamSearch({
defaultSearchValue,
placeholder = 'Search',
}: {
defaultSearchValue: string;
placeholder: string;
}) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const handleSearch = useDebounceCallback((event: '' | ChangeEvent<HTMLInputElement>) => {
const { value } = event. target;
const params = new URLSearchParams(searchParams);
if (value === '') {
params. delete( 'search');
} else {
params.set('search', value);
}
router.push(${pathname}?${params.toString()});
}, 500);
return (
<TableToolbarSearch
id="search"
labelText={placeholder}
placeholder={placeholder}
defaultValue={defaultSearchValue}
onChange={handleSearch}
/>
);
}
So far, my test looks like this. The first expect
passes, but the second routerMock.push
is never called.
jest-mock('usehooks-ts', () => ({
useDebounceCallback: jest.fn((fn) => {
setTimeout (() => fn, 500)
}),
}));
describe('ParamSearch', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.runOnlyPendingTimers();
jest.useRealTimers();
});
it('calls handleSearch as debounced callback after delay', async () => {
usePathname.mockReturnValue('/test-search');
useSearchParams.mockReturnValue(new Map([['search', '']]));
const { getByPlaceholderText } = render(
<ParamSearch placeholder={'Search'} defaultSearchValue={''} />
);
const searchInput = getByPlaceholderText('Search');
fireEvent.change(searchInput, { target: { value: 'x'} });
expect(routerMock.push).not.toHaveBeenCalled();
jest.advanceTimersByTime(500);
expect(routerMock.push).toHaveBeenCalledWith('/test-search?search=x');
});
});
I feel like I'm doing something wrong with the fake timers, can anybody help?
The mock to the useDebounce hook is unnecessary if you are using jest fake timer, it should work without mocking.
Just remove these line:
jest.mock('usehooks-ts', () => ({
useDebounceCallback: jest.fn((fn) => {
setTimeout (() => fn, 500)
}),
}));
And I doubt that the setTimeout
you provided to the mock function will not be affected by jest fake timer hijack process.