Files
OverlayScrollbars/packages/overlayscrollbars/tests/jest/support/utils/function.test.ts
T
2022-06-14 16:30:06 +02:00

322 lines
7.4 KiB
TypeScript

import { noop, debounce } from 'support/utils/function';
import { rAF } from 'support/compatibility/apis';
jest.mock('support/compatibility/apis', () => {
const originalModule = jest.requireActual('support/compatibility/apis');
return {
...originalModule,
rAF: jest.fn().mockImplementation((...args) => originalModule.rAF(...args)),
};
});
const mockSetTimeout = () => {
const original = window.setTimeout;
// @ts-ignore
const setT = (window.setTimeout = jest.fn((...args) => original(...args)));
return [
setT,
() => {
window.setTimeout = original;
},
];
};
// eslint-disable-next-line no-return-await
const timeout = async (timeout = 100) => await new Promise((r) => setTimeout(r, timeout));
describe('function', () => {
test('noop', () => {
expect(typeof noop).toBe('function');
expect(noop()).toBe(undefined);
});
describe('debounce', () => {
describe('timeout', () => {
test('without timeout', () => {
let i = 0;
const [setT, unmockSetTimeout] = mockSetTimeout();
const debouncedFn = debounce(() => {
i += 1;
});
expect(rAF).not.toHaveBeenCalled();
expect(setT).not.toHaveBeenCalled();
debouncedFn();
expect(rAF).not.toHaveBeenCalled();
expect(setT).not.toHaveBeenCalled();
expect(i).toBe(1);
unmockSetTimeout();
});
test('with timeout 0', async () => {
let i = 0;
const [setT, unmockSetTimeout] = mockSetTimeout();
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 0 }
);
expect(rAF).not.toHaveBeenCalled();
expect(setT).not.toHaveBeenCalled();
debouncedFn();
expect(rAF).toHaveBeenCalledTimes(1);
expect(setT).not.toHaveBeenCalled();
expect(i).toBe(0);
await timeout();
expect(i).toBe(1);
unmockSetTimeout();
});
test('with timeout > 0', async () => {
let i = 0;
const [setT, unmockSetTimeout] = mockSetTimeout();
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 1 }
);
expect(rAF).not.toHaveBeenCalled();
expect(setT).not.toHaveBeenCalled();
debouncedFn();
expect(rAF).not.toHaveBeenCalled();
expect(setT).toHaveBeenCalledTimes(1);
expect(i).toBe(0);
await timeout();
expect(i).toBe(1);
unmockSetTimeout();
});
test('with timeout > 0 and multiple calls', async () => {
let i = 0;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 200 }
);
debouncedFn();
await timeout();
expect(i).toBe(0);
debouncedFn();
await timeout();
expect(i).toBe(0);
debouncedFn();
await timeout();
expect(i).toBe(0);
debouncedFn();
await timeout();
expect(i).toBe(0);
debouncedFn();
await timeout(300);
expect(i).toBe(1);
});
test('with timeout function', async () => {
let i = 0;
let timeoutMs = 200;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: () => timeoutMs }
);
debouncedFn();
await timeout();
expect(i).toBe(0);
timeoutMs = 1000;
debouncedFn();
await timeout(500);
expect(i).toBe(0);
await timeout(500);
expect(i).toBe(1);
});
});
describe('maxDelay', () => {
test('without maxDelay', async () => {
let i = 0;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 100 }
);
debouncedFn();
expect(i).toBe(0);
await timeout(150);
expect(i).toBe(1);
});
test('with maxDelay and longer timeout', async () => {
let i = 0;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 10000, _maxDelay: 100 }
);
debouncedFn();
expect(i).toBe(0);
await timeout(150);
expect(i).toBe(1);
});
test('with maxDelay and shorter timeout with multiple calls', async () => {
let i = 0;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 200, _maxDelay: 500 }
);
debouncedFn();
await timeout(150);
expect(i).toBe(0);
debouncedFn();
await timeout(150);
debouncedFn();
await timeout(150);
expect(i).toBe(0);
debouncedFn();
await timeout(150);
expect(i).toBe(1);
});
test('with maxDelay function', async () => {
let i = 0;
let maxDelayMs = 300;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 400, _maxDelay: () => maxDelayMs }
);
debouncedFn();
expect(i).toBe(0);
await timeout();
debouncedFn();
expect(i).toBe(0);
await timeout();
maxDelayMs = 800; // this delay will be applied in the next cycle, not instantly
debouncedFn();
expect(i).toBe(0);
await timeout();
debouncedFn();
expect(i).toBe(1); // max delay 300 invoked here
await timeout(300);
debouncedFn();
expect(i).toBe(1);
await timeout(300);
debouncedFn();
expect(i).toBe(1);
await timeout(300);
debouncedFn();
expect(i).toBe(2); // max delay 800 invoked here
});
});
describe('mergeParams', () => {
test('with correct mergeParams function', async () => {
let i = 0;
const _mergeParams = jest.fn((prev: [number, number], curr: [number, number]) => {
const [prevA, prevB] = prev;
const [currA, currB] = curr;
return [prevA + currA, prevB + currB] as [number, number];
});
const debouncedFn = debounce(
(a: number, b: number) => {
i += a * b;
},
{ _timeout: 200, _mergeParams }
);
debouncedFn(1, 1);
expect(i).toBe(0);
expect(_mergeParams).not.toHaveBeenCalled();
await timeout();
debouncedFn(4, 4);
expect(i).toBe(0);
expect(_mergeParams).toHaveBeenLastCalledWith([1, 1], [4, 4]);
await timeout();
debouncedFn(10, 10);
expect(i).toBe(0);
expect(_mergeParams).toHaveBeenLastCalledWith([5, 5], [10, 10]);
await timeout(250);
expect(i).toBe(15 * 15);
});
test('without correct mergeParams function', async () => {
let i = 0;
const _mergeParams = jest.fn(() => null);
const debouncedFn = debounce(
(a, b) => {
i += a * b;
},
{ _timeout: 200, _mergeParams }
);
debouncedFn(1, 1);
expect(i).toBe(0);
expect(_mergeParams).not.toHaveBeenCalled();
await timeout();
debouncedFn(2, 2);
expect(i).toBe(0);
expect(_mergeParams).toHaveBeenLastCalledWith([1, 1], [2, 2]);
await timeout();
debouncedFn(3, 3);
expect(i).toBe(0);
expect(_mergeParams).toHaveBeenLastCalledWith([2, 2], [3, 3]);
await timeout(250);
expect(i).toBe(3 * 3);
});
});
});
});