mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-06 00:22:26 +03:00
423 lines
10 KiB
TypeScript
423 lines
10 KiB
TypeScript
import { noop, debounce, selfClearTimeout } from 'support/utils/function';
|
|
import { rAF, cAF, setT, clearT } from 'support/compatibility/apis';
|
|
|
|
jest.useFakeTimers();
|
|
|
|
jest.mock('support/compatibility/apis', () => {
|
|
const originalModule = jest.requireActual('support/compatibility/apis');
|
|
const mockRAF = (arg: any) => setTimeout(arg, 0);
|
|
return {
|
|
...originalModule,
|
|
// @ts-ignore
|
|
rAF: jest.fn().mockImplementation((...args) => mockRAF(...args)),
|
|
cAF: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
|
// @ts-ignore
|
|
setT: jest.fn().mockImplementation((...args) => setTimeout(...args)),
|
|
clearT: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
|
};
|
|
});
|
|
|
|
describe('function', () => {
|
|
test('noop', () => {
|
|
expect(typeof noop).toBe('function');
|
|
expect(noop()).toBe(undefined);
|
|
});
|
|
|
|
describe('debounce', () => {
|
|
describe('timeout', () => {
|
|
test('without timeout', () => {
|
|
let i = 0;
|
|
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);
|
|
});
|
|
|
|
test('with timeout 0', async () => {
|
|
let i = 0;
|
|
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);
|
|
|
|
jest.advanceTimersByTime(0);
|
|
|
|
expect(i).toBe(1);
|
|
});
|
|
|
|
test('with timeout > 0', async () => {
|
|
let i = 0;
|
|
const debouncedFn = debounce(
|
|
() => {
|
|
i += 1;
|
|
},
|
|
{ _timeout: 100 }
|
|
);
|
|
|
|
expect(rAF).not.toHaveBeenCalled();
|
|
expect(setT).not.toHaveBeenCalled();
|
|
debouncedFn();
|
|
expect(rAF).not.toHaveBeenCalled();
|
|
expect(setT).toHaveBeenCalledTimes(1);
|
|
|
|
expect(i).toBe(0);
|
|
|
|
jest.advanceTimersByTime(100);
|
|
|
|
expect(i).toBe(1);
|
|
});
|
|
|
|
test('with timeout > 0 and multiple calls', async () => {
|
|
let i = 0;
|
|
const debouncedFn = debounce(
|
|
() => {
|
|
i += 1;
|
|
},
|
|
{ _timeout: 200 }
|
|
);
|
|
|
|
debouncedFn();
|
|
jest.advanceTimersByTime(100);
|
|
expect(i).toBe(0);
|
|
|
|
debouncedFn();
|
|
jest.advanceTimersByTime(100);
|
|
expect(i).toBe(0);
|
|
|
|
debouncedFn();
|
|
jest.advanceTimersByTime(100);
|
|
expect(i).toBe(0);
|
|
|
|
debouncedFn();
|
|
jest.advanceTimersByTime(100);
|
|
expect(i).toBe(0);
|
|
|
|
debouncedFn();
|
|
jest.advanceTimersByTime(200);
|
|
expect(i).toBe(1);
|
|
});
|
|
|
|
test('with timeout function', async () => {
|
|
let i = 0;
|
|
let timeoutMs = 200;
|
|
const debouncedFn = debounce(
|
|
() => {
|
|
i += 1;
|
|
},
|
|
{ _timeout: () => timeoutMs }
|
|
);
|
|
|
|
debouncedFn();
|
|
jest.advanceTimersByTime(100);
|
|
expect(i).toBe(0);
|
|
|
|
timeoutMs = 1000;
|
|
|
|
debouncedFn();
|
|
jest.advanceTimersByTime(500);
|
|
expect(i).toBe(0);
|
|
|
|
jest.advanceTimersByTime(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);
|
|
|
|
jest.advanceTimersByTime(99);
|
|
expect(i).toBe(0);
|
|
|
|
jest.advanceTimersByTime(1);
|
|
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);
|
|
|
|
jest.advanceTimersByTime(99);
|
|
expect(i).toBe(0);
|
|
|
|
jest.advanceTimersByTime(1);
|
|
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();
|
|
jest.advanceTimersByTime(150); // 150
|
|
expect(i).toBe(0);
|
|
|
|
debouncedFn();
|
|
jest.advanceTimersByTime(150); // 300
|
|
|
|
debouncedFn();
|
|
jest.advanceTimersByTime(150); // 450
|
|
expect(i).toBe(0);
|
|
|
|
debouncedFn();
|
|
jest.advanceTimersByTime(49); // 499
|
|
expect(i).toBe(0);
|
|
|
|
jest.advanceTimersByTime(1); // 500
|
|
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);
|
|
jest.advanceTimersByTime(100); // 100
|
|
|
|
debouncedFn();
|
|
expect(i).toBe(0);
|
|
jest.advanceTimersByTime(100); // 200
|
|
|
|
maxDelayMs = 800; // this delay will be applied in the next cycle, not instantly
|
|
|
|
debouncedFn();
|
|
expect(i).toBe(0);
|
|
jest.advanceTimersByTime(99); // 299
|
|
expect(i).toBe(0);
|
|
jest.advanceTimersByTime(1); // 300
|
|
expect(i).toBe(1);
|
|
|
|
debouncedFn();
|
|
expect(i).toBe(1);
|
|
jest.advanceTimersByTime(300); // 300
|
|
|
|
debouncedFn();
|
|
expect(i).toBe(1);
|
|
jest.advanceTimersByTime(300); // 600
|
|
|
|
debouncedFn();
|
|
expect(i).toBe(1);
|
|
jest.advanceTimersByTime(199); // 799
|
|
expect(i).toBe(1);
|
|
jest.advanceTimersByTime(1); // 800
|
|
expect(i).toBe(2); // max delay 800 invoked here
|
|
|
|
debouncedFn();
|
|
expect(i).toBe(2);
|
|
jest.advanceTimersByTime(399);
|
|
debouncedFn();
|
|
expect(i).toBe(2);
|
|
jest.advanceTimersByTime(401);
|
|
expect(i).toBe(3);
|
|
});
|
|
});
|
|
|
|
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();
|
|
|
|
jest.advanceTimersByTime(100); // 100
|
|
|
|
debouncedFn(4, 4);
|
|
expect(i).toBe(0);
|
|
expect(_mergeParams).toHaveBeenLastCalledWith([1, 1], [4, 4]);
|
|
|
|
jest.advanceTimersByTime(100); // 200
|
|
|
|
debouncedFn(10, 10);
|
|
expect(i).toBe(0);
|
|
expect(_mergeParams).toHaveBeenLastCalledWith([5, 5], [10, 10]);
|
|
|
|
jest.advanceTimersByTime(250); // 450
|
|
|
|
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();
|
|
|
|
jest.advanceTimersByTime(100); // 100
|
|
|
|
debouncedFn(2, 2);
|
|
expect(i).toBe(0);
|
|
expect(_mergeParams).toHaveBeenLastCalledWith([1, 1], [2, 2]);
|
|
|
|
jest.advanceTimersByTime(100); // 200
|
|
|
|
debouncedFn(3, 3);
|
|
expect(i).toBe(0);
|
|
expect(_mergeParams).toHaveBeenLastCalledWith([2, 2], [3, 3]);
|
|
|
|
jest.advanceTimersByTime(250); // 450
|
|
|
|
expect(i).toBe(3 * 3);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('selfClearTimeout', () => {
|
|
test('without timeout', () => {
|
|
let i = 0;
|
|
const [timeout, clear] = selfClearTimeout();
|
|
|
|
expect(rAF).not.toHaveBeenCalled();
|
|
expect(cAF).not.toHaveBeenCalled();
|
|
expect(setT).not.toHaveBeenCalled();
|
|
expect(clearT).not.toHaveBeenCalled();
|
|
|
|
timeout(() => {
|
|
i += 1;
|
|
});
|
|
clear();
|
|
|
|
expect(rAF).toHaveBeenCalledTimes(1);
|
|
expect(cAF).toHaveBeenCalledTimes(2);
|
|
expect(setT).not.toHaveBeenCalled();
|
|
expect(clearT).not.toHaveBeenCalled();
|
|
|
|
expect(i).toBe(0);
|
|
timeout(() => {
|
|
i += 1;
|
|
});
|
|
timeout(() => {
|
|
i += 1;
|
|
});
|
|
timeout(() => {
|
|
i += 1;
|
|
});
|
|
jest.runAllTimers();
|
|
expect(i).toBe(1);
|
|
});
|
|
|
|
test('with timeout', () => {
|
|
let i = 0;
|
|
const [timeout, clear] = selfClearTimeout(100);
|
|
|
|
expect(rAF).not.toHaveBeenCalled();
|
|
expect(cAF).not.toHaveBeenCalled();
|
|
expect(setT).not.toHaveBeenCalled();
|
|
expect(clearT).not.toHaveBeenCalled();
|
|
|
|
timeout(() => {
|
|
i += 1;
|
|
});
|
|
clear();
|
|
|
|
expect(rAF).not.toHaveBeenCalled();
|
|
expect(cAF).not.toHaveBeenCalled();
|
|
expect(setT).toHaveBeenCalledTimes(1);
|
|
expect(clearT).toHaveBeenCalledTimes(2);
|
|
|
|
expect(i).toBe(0);
|
|
timeout(() => {
|
|
i += 1;
|
|
});
|
|
timeout(() => {
|
|
i += 1;
|
|
});
|
|
timeout(() => {
|
|
i += 1;
|
|
});
|
|
jest.runAllTimers();
|
|
expect(i).toBe(1);
|
|
});
|
|
|
|
test('with timeout function', () => {
|
|
let i = 0;
|
|
const [timeout, clear] = selfClearTimeout(() => 100);
|
|
|
|
expect(rAF).not.toHaveBeenCalled();
|
|
expect(cAF).not.toHaveBeenCalled();
|
|
expect(setT).not.toHaveBeenCalled();
|
|
expect(clearT).not.toHaveBeenCalled();
|
|
|
|
timeout(() => {
|
|
i += 1;
|
|
});
|
|
clear();
|
|
|
|
expect(rAF).not.toHaveBeenCalled();
|
|
expect(cAF).not.toHaveBeenCalled();
|
|
expect(setT).toHaveBeenCalledTimes(1);
|
|
expect(clearT).toHaveBeenCalledTimes(2);
|
|
|
|
expect(i).toBe(0);
|
|
timeout(() => {
|
|
i += 1;
|
|
});
|
|
timeout(() => {
|
|
i += 1;
|
|
});
|
|
timeout(() => {
|
|
i += 1;
|
|
});
|
|
jest.runAllTimers();
|
|
expect(i).toBe(1);
|
|
});
|
|
});
|
|
});
|