shared-timer.test.ts 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
  2. import { _getListenerCount, _isRunning, _reset, subscribeToTick } from "@/lib/shared-timer";
  3. beforeEach(() => {
  4. vi.useFakeTimers();
  5. _reset();
  6. });
  7. afterEach(() => {
  8. _reset();
  9. vi.useRealTimers();
  10. });
  11. describe("shared-timer", () => {
  12. test("timer starts on first subscriber and stops when last unsubscribes", () => {
  13. expect(_isRunning()).toBe(false);
  14. expect(_getListenerCount()).toBe(0);
  15. const unsub1 = subscribeToTick(() => {});
  16. expect(_isRunning()).toBe(true);
  17. expect(_getListenerCount()).toBe(1);
  18. const unsub2 = subscribeToTick(() => {});
  19. expect(_getListenerCount()).toBe(2);
  20. unsub1();
  21. expect(_isRunning()).toBe(true);
  22. expect(_getListenerCount()).toBe(1);
  23. unsub2();
  24. expect(_isRunning()).toBe(false);
  25. expect(_getListenerCount()).toBe(0);
  26. });
  27. test("tick fires every 10 seconds and calls all listeners", () => {
  28. const fn1 = vi.fn();
  29. const fn2 = vi.fn();
  30. subscribeToTick(fn1);
  31. subscribeToTick(fn2);
  32. expect(fn1).not.toHaveBeenCalled();
  33. expect(fn2).not.toHaveBeenCalled();
  34. vi.advanceTimersByTime(10_000);
  35. expect(fn1).toHaveBeenCalledTimes(1);
  36. expect(fn2).toHaveBeenCalledTimes(1);
  37. vi.advanceTimersByTime(10_000);
  38. expect(fn1).toHaveBeenCalledTimes(2);
  39. expect(fn2).toHaveBeenCalledTimes(2);
  40. });
  41. test("unsubscribed listener is no longer called", () => {
  42. const fn1 = vi.fn();
  43. const fn2 = vi.fn();
  44. const unsub1 = subscribeToTick(fn1);
  45. subscribeToTick(fn2);
  46. vi.advanceTimersByTime(10_000);
  47. expect(fn1).toHaveBeenCalledTimes(1);
  48. expect(fn2).toHaveBeenCalledTimes(1);
  49. unsub1();
  50. vi.advanceTimersByTime(10_000);
  51. expect(fn1).toHaveBeenCalledTimes(1);
  52. expect(fn2).toHaveBeenCalledTimes(2);
  53. });
  54. test("_reset clears all state", () => {
  55. subscribeToTick(() => {});
  56. subscribeToTick(() => {});
  57. expect(_isRunning()).toBe(true);
  58. expect(_getListenerCount()).toBe(2);
  59. _reset();
  60. expect(_isRunning()).toBe(false);
  61. expect(_getListenerCount()).toBe(0);
  62. });
  63. test("double unsubscribe is safe", () => {
  64. const unsub = subscribeToTick(() => {});
  65. unsub();
  66. expect(() => unsub()).not.toThrow();
  67. expect(_getListenerCount()).toBe(0);
  68. });
  69. test("a throwing listener does not prevent other listeners from firing", () => {
  70. const fn1 = vi.fn(() => {
  71. throw new Error("boom");
  72. });
  73. const fn2 = vi.fn();
  74. subscribeToTick(fn1);
  75. subscribeToTick(fn2);
  76. vi.advanceTimersByTime(10_000);
  77. expect(fn1).toHaveBeenCalledTimes(1);
  78. expect(fn2).toHaveBeenCalledTimes(1);
  79. });
  80. });