1import { describe, expect, it } from "vitest"
2
3import {
4 createHoldDetector,
5 type HoldEvent,
6} from "@/features/gesture/holdDetector"
7import {
8 CAPTURE_COOLDOWN_MS,
9 HOLD_DURATION_MS,
10} from "@/features/gesture/thresholds"
11
12const victory = { category: "Victory", score: 0.7 }
13const openPalm = { category: "Open_Palm", score: 0.8 }
14const none = { category: "None", score: 1 }
15
16function types(events: HoldEvent[]): string[] {
17 return events.map((event) => event.type)
18}
19
20describe("holdDetector", () => {
21 it("menahan sampai durasi penuh: start, progres, lalu complete", () => {
22 const detector = createHoldDetector()
23
24 expect(detector.push(victory, 0)).toEqual([
25 { type: "hold-start", action: "snap" },
26 ])
27 expect(detector.push(victory, 200)).toEqual([
28 { type: "hold-progress", action: "snap", progress: 0.25 },
29 ])
30 expect(detector.push(victory, 400)).toEqual([
31 { type: "hold-progress", action: "snap", progress: 0.5 },
32 ])
33 expect(detector.push(victory, 600)).toEqual([
34 { type: "hold-progress", action: "snap", progress: 0.75 },
35 ])
36 expect(detector.push(victory, HOLD_DURATION_MS)).toEqual([
37 { type: "hold-complete", action: "snap" },
38 ])
39 })
40
41 it("melepas gesture sebelum penuh: hold-cancel dan siklus mulai lagi", () => {
42 const detector = createHoldDetector()
43
44 detector.push(victory, 0)
45 expect(detector.push(victory, 400)).toEqual([
46 { type: "hold-progress", action: "snap", progress: 0.5 },
47 ])
48 expect(detector.push(null, 450)).toEqual([{ type: "hold-cancel" }])
49 expect(detector.push(victory, 500)).toEqual([
50 { type: "hold-start", action: "snap" },
51 ])
52 expect(detector.push(victory, 700)).toEqual([
53 { type: "hold-progress", action: "snap", progress: 0.25 },
54 ])
55 })
56
57 it("jeda setelah foto mengabaikan gesture sampai cooldown habis", () => {
58 const detector = createHoldDetector()
59
60 detector.notifyCapture(1000)
61 expect(detector.push(victory, 1000)).toEqual([])
62 expect(detector.push(victory, 2000)).toEqual([])
63 expect(detector.push(victory, 1000 + CAPTURE_COOLDOWN_MS - 1)).toEqual([])
64 expect(detector.push(victory, 1000 + CAPTURE_COOLDOWN_MS)).toEqual([
65 { type: "hold-start", action: "snap" },
66 ])
67 })
68
69 it("mengabaikan skor di bawah ambang", () => {
70 const detector = createHoldDetector()
71
72 expect(detector.push({ category: "Victory", score: 0.59 }, 0)).toEqual([])
73 expect(detector.push({ category: "Victory", score: 0.59 }, 200)).toEqual([])
74 })
75
76 it("membatalkan tahanan saat sampel turun ke bawah ambang", () => {
77 const detector = createHoldDetector()
78
79 detector.push(victory, 0)
80 expect(
81 detector.push({ category: "Victory", score: 0.2 }, 200)
82 ).toEqual([{ type: "hold-cancel" }])
83 })
84
85 it("mengganti kategori di tengah menahan membatalkan tahanan", () => {
86 const detector = createHoldDetector()
87
88 detector.push(victory, 0)
89 expect(detector.push(openPalm, 200)).toEqual([{ type: "hold-cancel" }])
90 expect(detector.push(openPalm, 260)).toEqual([
91 { type: "hold-start", action: "retake" },
92 ])
93 })
94
95 it("tidak memicu dua kali untuk satu pose", () => {
96 const detector = createHoldDetector()
97
98 expect(types(detector.push(victory, 0))).toEqual(["hold-start"])
99 expect(types(detector.push(victory, HOLD_DURATION_MS))).toEqual([
100 "hold-complete",
101 ])
102 expect(detector.push(victory, HOLD_DURATION_MS + 200)).toEqual([])
103 expect(detector.push(victory, HOLD_DURATION_MS + 400)).toEqual([])
104 expect(detector.push(none, HOLD_DURATION_MS + 600)).toEqual([])
105 expect(types(detector.push(victory, HOLD_DURATION_MS + 800))).toEqual([
106 "hold-start",
107 ])
108 })
109
110 it("aksi lain memakai ambang yang sama", () => {
111 const detector = createHoldDetector()
112
113 expect(types(detector.push(openPalm, 0))).toEqual(["hold-start"])
114 expect(types(detector.push(openPalm, HOLD_DURATION_MS))).toEqual([
115 "hold-complete",
116 ])
117
118 const other = createHoldDetector()
119 const thumbUp = { category: "Thumb_Up", score: 0.9 }
120 expect(types(other.push(thumbUp, 0))).toEqual(["hold-start"])
121 expect(types(other.push(thumbUp, HOLD_DURATION_MS))).toEqual([
122 "hold-complete",
123 ])
124 })
125})
126