Debouncer.spec.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import { describe, expect, it } from "vitest";
  2. import { debounce, Debouncer } from "./Debouncer.js";
  3. import { pauseFor } from "./misc.js";
  4. describe("Debouncer", () => {
  5. //#region deltaT
  6. it("deltaT test with type \"immediate\"", async () => {
  7. const deb = new Debouncer(200, "immediate");
  8. deb.addListener(debCalled);
  9. const deltaTs: number[] = [];
  10. let lastCall: number | undefined;
  11. function debCalled() {
  12. const n = Date.now(),
  13. deltaT = lastCall ? n - lastCall : undefined;
  14. typeof deltaT === "number" && deltaT > 0 && deltaTs.push(deltaT);
  15. lastCall = n;
  16. }
  17. for(let i = 0; i < 2; i++) {
  18. for(let j = 0; j < 6; j++) {
  19. deb.call(i, j);
  20. expect(deb.isTimeoutActive()).toBe(true);
  21. await pauseFor(50);
  22. }
  23. await pauseFor(300);
  24. }
  25. const avg = deltaTs
  26. .reduce((a, b) => a + b, 0) / deltaTs.length;
  27. expect(avg + 10).toBeLessThanOrEqual(deb.getTimeout() + 50);
  28. });
  29. //#region idle
  30. it("deltaT test with type \"idle\"", async () => {
  31. const deb = new Debouncer(200, "idle");
  32. deb.addListener(debCalled);
  33. const deltaTs: number[] = [];
  34. let callCount = 0;
  35. let lastCall: number | undefined;
  36. function debCalled() {
  37. callCount++;
  38. const n = Date.now(),
  39. deltaT = lastCall ? n - lastCall : undefined;
  40. typeof deltaT === "number" && deltaT > 0 && deltaTs.push(deltaT);
  41. lastCall = n;
  42. }
  43. const jAmt = 6,
  44. iTime = 400,
  45. jTime = 30;
  46. for(let i = 0; i < 2; i++) {
  47. for(let j = 0; j < jAmt; j++) {
  48. deb.call(i, j);
  49. await pauseFor(jTime);
  50. }
  51. await pauseFor(iTime);
  52. }
  53. expect(callCount).toBeLessThanOrEqual(5); // expected 2~3 calls
  54. /** Minimum possible deltaT between calls */
  55. const minDeltaT = jAmt * jTime + iTime;
  56. const avg = deltaTs
  57. .reduce((a, b) => a + b, 0) / deltaTs.length;
  58. expect(avg + 10).toBeGreaterThanOrEqual(minDeltaT);
  59. });
  60. //#region modify props & listeners
  61. it("Modify props and listeners", async () => {
  62. const deb = new Debouncer(200);
  63. expect(deb.getTimeout()).toBe(200);
  64. deb.setTimeout(10);
  65. expect(deb.getTimeout()).toBe(10);
  66. expect(deb.getType()).toBe("immediate");
  67. deb.setType("idle");
  68. expect(deb.getType()).toBe("idle");
  69. const l = () => {};
  70. deb.addListener(l);
  71. deb.addListener(() => {});
  72. expect(deb.getListeners()).toHaveLength(2);
  73. deb.removeListener(l);
  74. expect(deb.getListeners()).toHaveLength(1);
  75. deb.removeAllListeners();
  76. expect(deb.getListeners()).toHaveLength(0);
  77. });
  78. //#region all methods
  79. // TODO:FIXME:
  80. it.skip("All methods", async () => {
  81. const deb = new Debouncer<(v?: number) => void>(200);
  82. let callAmt = 0, evtCallAmt = 0;
  83. const debCalled = (): number => ++callAmt;
  84. const debCalledEvt = (): number => ++evtCallAmt;
  85. // hook debCalled first, then call, then hook debCalledEvt:
  86. deb.addListener(debCalled);
  87. deb.call();
  88. deb.on("call", debCalledEvt);
  89. expect(callAmt).toBe(1);
  90. expect(evtCallAmt).toBe(0);
  91. deb.setTimeout(10);
  92. expect(deb.getTimeout()).toBe(10);
  93. const callPaused = (v?: number): Promise<void> => {
  94. deb.call(v);
  95. return pauseFor(50);
  96. };
  97. let onceAmt = 0;
  98. deb.once("call", () => ++onceAmt);
  99. await callPaused();
  100. await callPaused();
  101. await callPaused();
  102. expect(onceAmt).toBe(1);
  103. let args = 0;
  104. const setArgs = (v?: number) => args = v ?? args;
  105. deb.addListener(setArgs);
  106. await callPaused(1);
  107. expect(args).toBe(1);
  108. deb.removeListener(setArgs);
  109. await callPaused(2);
  110. expect(args).toBe(1);
  111. deb.removeAllListeners();
  112. await callPaused();
  113. expect(callAmt).toEqual(evtCallAmt + 1); // evtCallAmt is always behind by 1
  114. });
  115. //#region errors
  116. it("Errors", () => {
  117. try {
  118. // @ts-expect-error
  119. const deb = new Debouncer(200, "invalid");
  120. deb.call();
  121. }
  122. catch(e) {
  123. expect(e).toBeInstanceOf(TypeError);
  124. }
  125. });
  126. //#region debounce function
  127. it("Debounce function", async () => {
  128. let callAmt = 0;
  129. const callFn = debounce(() => ++callAmt, 200);
  130. for(let i = 0; i < 4; i++) {
  131. callFn();
  132. await pauseFor(25);
  133. }
  134. expect(callAmt).toBe(1);
  135. });
  136. });