forked from SmartDropLabs/smartdrop-frontend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuseCountdown.test.ts
More file actions
79 lines (58 loc) · 2.48 KB
/
Copy pathuseCountdown.test.ts
File metadata and controls
79 lines (58 loc) · 2.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import { act, renderHook } from "@/test/renderHook";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { useCountdown } from "./useCountdown";
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.useRealTimers();
});
describe("useCountdown", () => {
it("Test 1 — Accuracy over 60 ticks: remainingMs stays within 50 ms of expected", () => {
const now = Date.now();
// Target 2 minutes ahead so the hook is still ticking after 60 ticks
const unlockAtMs = now + 120_000;
const { result } = renderHook(() => useCountdown(unlockAtMs));
// Advance 60 full 1-second ticks; wrap in act so React state updates flush
act(() => {
vi.advanceTimersByTime(60_000);
});
// Wall-clock formula: unlockAtMs - Date.now() after 60 s of fake-timer advance
const expected = Math.max(0, unlockAtMs - Date.now());
const drift = Math.abs(result.current.remainingMs - expected);
expect(drift).toBeLessThanOrEqual(50);
});
it("Test 2 — Catches exact expiry: isElapsed true and remainingMs 0 at target", () => {
const unlockAtMs = Date.now() + 5000;
const { result } = renderHook(() => useCountdown(unlockAtMs));
act(() => {
vi.advanceTimersByTime(5000);
});
expect(result.current.isElapsed).toBe(true);
expect(result.current.remainingMs).toBe(0);
});
it("Test 3 — Already-elapsed target: returns isElapsed true immediately, no interval started", () => {
// unlockAtMs is 1 second in the past
const unlockAtMs = Date.now() - 1000;
const { result } = renderHook(() => useCountdown(unlockAtMs));
expect(result.current.isElapsed).toBe(true);
expect(result.current.remainingMs).toBe(0);
// No interval should have been registered
expect(vi.getTimerCount()).toBe(0);
});
it("Test 4 — No memory leak on unmount: interval is cleared after unmount", () => {
const unlockAtMs = Date.now() + 10_000;
const { unmount } = renderHook(() => useCountdown(unlockAtMs));
// Interval should exist while mounted
expect(vi.getTimerCount()).toBeGreaterThan(0);
unmount();
// Interval should be cleared after unmount
expect(vi.getTimerCount()).toBe(0);
});
it("Test 5 — Label formatting: 90 061 000 ms formats as '1d 01h 01m 01s'", () => {
const remainingMs = 90_061_000;
const unlockAtMs = Date.now() + remainingMs;
const { result } = renderHook(() => useCountdown(unlockAtMs));
expect(result.current.label).toBe("1d 01h 01m 01s");
});
});