import { mount } from 'enzyme';
import React from 'react';
import { useDebouncedValue } from './useDebouncedValue.hook';
describe('useDebouncedValue hook', () => {
beforeEach(() => jasmine.clock().install());
afterEach(() => jasmine.clock().uninstall());
const timeoutMillis = 1000;
function Component(props: any) {
const { value, onChange, millis } = props;
const [debounced, isDebouncing] = useDebouncedValue(value, millis);
React.useEffect(() => onChange(value, debounced, isDebouncing), [value, debounced, isDebouncing]);
return <>>;
}
it('initially, debounced value is the same as the initial value', () => {
const spy = jasmine.createSpy();
mount();
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith('a', 'a', jasmine.anything());
});
it('initially, isDebouncing is false', () => {
const spy = jasmine.createSpy();
mount();
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(jasmine.anything(), jasmine.anything(), false);
});
it('isDebounced is true during the time where the value is different than the debounced value', () => {
const spy = jasmine.createSpy();
const component = mount();
component.setProps({ value: 'b' });
expect(spy).toHaveBeenCalledTimes(2);
const [value, debouncedValue, isDebouncing] = spy.calls.mostRecent().args;
expect([value, debouncedValue, isDebouncing]).toEqual(['b', 'a', true]);
});
it('after the timeout, debounced should equal value and isDebouncing is false', () => {
const spy = jasmine.createSpy();
const component = mount();
component.setProps({ value: 'b' });
expect(spy).toHaveBeenCalledTimes(2);
expect(spy.calls.mostRecent().args).toEqual(['b', 'a', true]);
jasmine.clock().tick(timeoutMillis);
component.setProps({}); // rerender
expect(spy).toHaveBeenCalledTimes(3);
expect(spy.calls.mostRecent().args).toEqual(['b', 'b', false]);
});
it('does not update debounced value until after the timeout', () => {
const spy = jasmine.createSpy();
const component = mount();
component.setProps({ value: 'b' });
expect(spy).toHaveBeenCalledTimes(2);
expect(spy.calls.mostRecent().args).toEqual(['b', 'a', true]);
jasmine.clock().tick(timeoutMillis - 1);
component.setProps({}); // rerender
expect(spy).toHaveBeenCalledTimes(2);
jasmine.clock().tick(1);
component.setProps({}); // rerender
expect(spy).toHaveBeenCalledTimes(3);
expect(spy.calls.mostRecent().args).toEqual(['b', 'b', false]);
});
it('coalesces multiple values into a single debounced value', () => {
const spy = jasmine.createSpy();
const component = mount();
component.setProps({ value: 'b' });
component.setProps({ value: 'c' });
component.setProps({ value: 'd' });
component.setProps({ value: 'e' });
expect(spy).toHaveBeenCalledTimes(5);
expect(spy.calls.allArgs()).toEqual([
['a', 'a', false],
['b', 'a', true],
['c', 'a', true],
['d', 'a', true],
['e', 'a', true],
]);
jasmine.clock().tick(timeoutMillis);
component.setProps({}); // rerender
expect(spy).toHaveBeenCalledTimes(6);
expect(spy.calls.mostRecent().args).toEqual(['e', 'e', false]);
});
it('resets the timeout when a new value is seen but the previous value hasnt been debounced yet', () => {
const spy = jasmine.createSpy();
const component = mount();
component.setProps({ value: 'b' });
expect(spy).toHaveBeenCalledTimes(2);
expect(spy.calls.mostRecent().args).toEqual(['b', 'a', true]);
const halfTimeoutMillis = timeoutMillis / 2;
// Wait 500ms -- change the value to 'c' before 'b' is debounced
jasmine.clock().tick(halfTimeoutMillis); // clock is now 500ms
component.setProps({ value: 'c' });
expect(spy).toHaveBeenCalledTimes(3);
expect(spy.calls.mostRecent().args).toEqual(['c', 'a', true]);
// Wait 500ms more. Debounced should still be 'a'
jasmine.clock().tick(halfTimeoutMillis); // clock is now 1000ms
component.setProps({ value: 'c' });
expect(spy).toHaveBeenCalledTimes(3);
// Wait 500ms more. Debounced should now be 'c'
jasmine.clock().tick(halfTimeoutMillis); // clock is now 1500ms
component.setProps({ value: 'c' });
expect(spy).toHaveBeenCalledTimes(4);
expect(spy.calls.mostRecent().args).toEqual(['c', 'c', false]);
});
});