logancyang_obsidian-copilot/src/utils/debounce.test.ts
Zero Liu d6255374cb
chore(deps): swap unmaintained/legacy deps per e18e module-replacements (#2447)
- npm-run-all → npm-run-all2 (dev script uses run-p)
- lint-staged → nano-staged (config block + husky hook)
- lodash.debounce → local src/utils/debounce.ts with cancel/flush/leading/trailing
- dotenv → process.loadEnvFile in integration test bootstrap
- eslint-plugin-react → @eslint-react/eslint-plugin (flat-config migration)
- js-yaml → yaml (mock + jest CJS moduleNameMapper for jsdom)
- crypto-js → pure-JS md5/sha256 in src/utils/hash.ts producing byte-identical
  digests so all on-disk cache keys (PDF, file, project, search index)
  remain valid across the upgrade — no migration or re-parse cost

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 01:12:52 -07:00

86 lines
2.2 KiB
TypeScript

import { debounce } from "./debounce";
jest.useFakeTimers();
describe("debounce", () => {
it("invokes once after the wait period with the latest args (trailing default)", () => {
const fn = jest.fn();
const d = debounce(fn, 100);
d("a");
d("b");
d("c");
expect(fn).not.toHaveBeenCalled();
jest.advanceTimersByTime(100);
expect(fn).toHaveBeenCalledTimes(1);
expect(fn).toHaveBeenCalledWith("c");
});
it("supports leading-only mode", () => {
const fn = jest.fn();
const d = debounce(fn, 100, { leading: true, trailing: false });
d("a");
expect(fn).toHaveBeenCalledTimes(1);
expect(fn).toHaveBeenCalledWith("a");
d("b");
d("c");
jest.advanceTimersByTime(100);
expect(fn).toHaveBeenCalledTimes(1);
});
it("fires both edges when leading and trailing are true and there are multiple calls", () => {
const fn = jest.fn();
const d = debounce(fn, 100, { leading: true, trailing: true });
d("a");
expect(fn).toHaveBeenCalledTimes(1);
expect(fn).toHaveBeenLastCalledWith("a");
d("b");
jest.advanceTimersByTime(100);
expect(fn).toHaveBeenCalledTimes(2);
expect(fn).toHaveBeenLastCalledWith("b");
});
it("does not fire trailing if leading already covered the single call", () => {
const fn = jest.fn();
const d = debounce(fn, 100, { leading: true, trailing: true });
d("only");
expect(fn).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(100);
expect(fn).toHaveBeenCalledTimes(1);
});
it("cancel prevents pending trailing invocation", () => {
const fn = jest.fn();
const d = debounce(fn, 100);
d("a");
d.cancel();
jest.advanceTimersByTime(100);
expect(fn).not.toHaveBeenCalled();
});
it("flush invokes the pending call synchronously", () => {
const fn = jest.fn();
const d = debounce(fn, 100);
d("a");
d.flush();
expect(fn).toHaveBeenCalledTimes(1);
expect(fn).toHaveBeenCalledWith("a");
jest.advanceTimersByTime(100);
expect(fn).toHaveBeenCalledTimes(1);
});
it("flush is a no-op when nothing is pending", () => {
const fn = jest.fn();
const d = debounce(fn, 100);
expect(d.flush()).toBeUndefined();
expect(fn).not.toHaveBeenCalled();
});
});