@musecat/functionkit 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/index.ts +1 -1
  2. package/package.json +37 -2
  3. package/packages/components/ScrolltoTop.tsx +1 -1
  4. package/packages/components/ViewportPortal.tsx +1 -2
  5. package/packages/cookie/cookie.shared.ts +17 -35
  6. package/packages/cookie/cookieNames.shared.ts +9 -0
  7. package/packages/datetime/dateTime.client.ts +1 -3
  8. package/packages/datetime/dateTime.server.ts +1 -3
  9. package/packages/datetime/dateTime.shared.ts +9 -47
  10. package/packages/hooks/useCheckInvisible.ts +1 -1
  11. package/packages/hooks/useClientDateTime.ts +3 -16
  12. package/packages/hooks/useDebounce.ts +2 -7
  13. package/packages/hooks/useGeolocation.ts +1 -5
  14. package/packages/hooks/useInterval.ts +1 -2
  15. package/packages/hooks/useKeyboardListNavigation.ts +10 -38
  16. package/packages/hooks/useLongPress.ts +13 -24
  17. package/packages/hooks/useRelativeDateTime.ts +1 -4
  18. package/packages/hooks/useViewportHeight.ts +1 -2
  19. package/packages/hooks/useViewportMatch.ts +0 -4
  20. package/packages/utils/buildContext.tsx +2 -3
  21. package/packages/utils/floatingMotion.ts +15 -23
  22. package/packages/utils/subscribeKeyboardHeight.ts +3 -0
  23. package/.agents/references/components/ScrolltoTop.md +0 -36
  24. package/.agents/references/components/SwitchCase.md +0 -47
  25. package/.agents/references/components/ViewportPortal.md +0 -34
  26. package/.agents/references/cookie/cookie.md +0 -47
  27. package/.agents/references/datetime/dateTime.client.md +0 -42
  28. package/.agents/references/datetime/dateTime.server.md +0 -42
  29. package/.agents/references/datetime/dateTime.shared.md +0 -86
  30. package/.agents/references/hooks/useAvoidKeyboard.md +0 -29
  31. package/.agents/references/hooks/useCheckInvisible.md +0 -36
  32. package/.agents/references/hooks/useCheckScroll.md +0 -36
  33. package/.agents/references/hooks/useClientDateTime.md +0 -39
  34. package/.agents/references/hooks/useDebounce.md +0 -46
  35. package/.agents/references/hooks/useDebouncedCallback.md +0 -34
  36. package/.agents/references/hooks/useDoubleClick.md +0 -40
  37. package/.agents/references/hooks/useGeolocation.md +0 -37
  38. package/.agents/references/hooks/useHasMounted.md +0 -27
  39. package/.agents/references/hooks/useIntersectionObserver.md +0 -37
  40. package/.agents/references/hooks/useInterval.md +0 -40
  41. package/.agents/references/hooks/useKeyboardHeight.md +0 -33
  42. package/.agents/references/hooks/useKeyboardListNavigation.md +0 -58
  43. package/.agents/references/hooks/useLongPress.md +0 -48
  44. package/.agents/references/hooks/usePreservedCallback.md +0 -33
  45. package/.agents/references/hooks/usePreservedReference.md +0 -34
  46. package/.agents/references/hooks/useRefEffect.md +0 -35
  47. package/.agents/references/hooks/useRelativeDateTime.md +0 -35
  48. package/.agents/references/hooks/useTimeout.md +0 -42
  49. package/.agents/references/hooks/useToggleState.md +0 -39
  50. package/.agents/references/hooks/useViewportHeight.md +0 -26
  51. package/.agents/references/hooks/useViewportMatch.md +0 -32
  52. package/.agents/references/utils/browserStorage.md +0 -41
  53. package/.agents/references/utils/buildContext.md +0 -47
  54. package/.agents/references/utils/clipboardShare.md +0 -59
  55. package/.agents/references/utils/floatingMotion.md +0 -67
  56. package/.agents/references/utils/getDeviceInfo.md +0 -43
  57. package/.agents/references/utils/isEditableKeyboardTarget.md +0 -35
  58. package/.agents/references/utils/mergeRefs.md +0 -29
  59. package/.agents/references/utils/seen.md +0 -37
  60. package/.agents/references/utils/subscribeKeyboardHeight.md +0 -35
  61. package/.gitattributes +0 -2
  62. package/AGENTS.md +0 -105
  63. package/tests/components/ScrolltoTop.test.tsx +0 -15
  64. package/tests/components/SwitchCase.test.tsx +0 -34
  65. package/tests/components/ViewportPortal.test.tsx +0 -42
  66. package/tests/cookie/cookie.test.ts +0 -156
  67. package/tests/datetime/datetime.test.ts +0 -250
  68. package/tests/hooks/useAvoidKeyboard.test.ts +0 -12
  69. package/tests/hooks/useCheckInvisible.test.ts +0 -28
  70. package/tests/hooks/useCheckScroll.test.ts +0 -23
  71. package/tests/hooks/useClientDateTime.test.ts +0 -14
  72. package/tests/hooks/useDebounce.test.ts +0 -71
  73. package/tests/hooks/useDebouncedCallback.test.ts +0 -34
  74. package/tests/hooks/useDoubleClick.test.ts +0 -31
  75. package/tests/hooks/useGeolocation.test.ts +0 -50
  76. package/tests/hooks/useHasMounted.test.ts +0 -10
  77. package/tests/hooks/useIntersectionObserver.test.ts +0 -13
  78. package/tests/hooks/useInterval.test.ts +0 -31
  79. package/tests/hooks/useKeyboardHeight.test.ts +0 -10
  80. package/tests/hooks/useKeyboardListNavigation.test.ts +0 -82
  81. package/tests/hooks/useLongPress.test.ts +0 -52
  82. package/tests/hooks/usePreservedCallback.test.ts +0 -35
  83. package/tests/hooks/usePreservedReference.test.ts +0 -35
  84. package/tests/hooks/useRefEffect.test.ts +0 -30
  85. package/tests/hooks/useRelativeDateTime.test.ts +0 -19
  86. package/tests/hooks/useTimeout.test.ts +0 -34
  87. package/tests/hooks/useToggleState.test.ts +0 -35
  88. package/tests/hooks/useViewportHeight.test.ts +0 -10
  89. package/tests/hooks/useViewportMatch.test.ts +0 -35
  90. package/tests/setup.ts +0 -9
  91. package/tests/utils/browserStorage.test.ts +0 -54
  92. package/tests/utils/buildContext.test.tsx +0 -27
  93. package/tests/utils/checkDevice.test.ts +0 -84
  94. package/tests/utils/clipboardShare.test.ts +0 -78
  95. package/tests/utils/floatingMotion.test.ts +0 -84
  96. package/tests/utils/keyboardTarget.test.ts +0 -44
  97. package/tests/utils/mergeRefs.test.ts +0 -18
  98. package/tests/utils/seen.test.ts +0 -31
  99. package/tests/utils/subscribeKeyboardHeight.test.ts +0 -50
  100. package/tsconfig.json +0 -18
  101. package/vitest.config.ts +0 -16
@@ -1,156 +0,0 @@
1
- import { describe, test, expect, beforeEach, vi, afterEach } from "vitest";
2
- import {
3
- parseClientCookieNames,
4
- getClientCookie,
5
- setClientCookie,
6
- clearClientCookie,
7
- clearAllClientCookies,
8
- } from "@/packages/cookie/cookie.shared";
9
-
10
- describe("cookie module", () => {
11
- beforeEach(() => {
12
- // Reset cookies
13
- Object.defineProperty(document, "cookie", {
14
- writable: true,
15
- value: "",
16
- });
17
-
18
- // Mock window location
19
- Object.defineProperty(window, "location", {
20
- writable: true,
21
- value: { hostname: "example.com", pathname: "/test/path" },
22
- });
23
- });
24
-
25
- afterEach(() => {
26
- vi.restoreAllMocks();
27
- });
28
-
29
- describe("parseClientCookieNames", () => {
30
- test("parses cookie string correctly", () => {
31
- const result = parseClientCookieNames("foo=bar; baz=qux; ; empty=");
32
- expect(result).toEqual(["foo", "baz", "empty"]);
33
- });
34
-
35
- test("returns empty array for empty string", () => {
36
- expect(parseClientCookieNames("")).toEqual([]);
37
- });
38
- });
39
-
40
- describe("getClientCookie", () => {
41
- test("returns cookie value if it exists", () => {
42
- document.cookie = "foo=bar; baz=qux";
43
- expect(getClientCookie("foo")).toBe("bar");
44
- expect(getClientCookie("baz")).toBe("qux");
45
- });
46
-
47
- test("returns undefined if cookie does not exist", () => {
48
- document.cookie = "foo=bar";
49
- expect(getClientCookie("missing")).toBeUndefined();
50
- });
51
-
52
- test("handles undefined document", () => {
53
- const originalDocument = global.document;
54
- // @ts-ignore
55
- delete global.document;
56
-
57
- expect(getClientCookie("foo")).toBeUndefined();
58
-
59
- global.document = originalDocument;
60
- });
61
- });
62
-
63
- describe("setClientCookie", () => {
64
- test("sets cookie with name and value", () => {
65
- setClientCookie("foo", "bar");
66
- expect(document.cookie).toBe("foo=bar; path=/");
67
- });
68
-
69
- test("sets cookie with days", () => {
70
- vi.useFakeTimers();
71
- vi.setSystemTime(new Date("2020-01-01T00:00:00Z"));
72
-
73
- setClientCookie("foo", "bar", 7);
74
-
75
- const expectedDate = new Date("2020-01-08T00:00:00Z").toUTCString();
76
- expect(document.cookie).toBe(`foo=bar; expires=${expectedDate}; path=/`);
77
-
78
- vi.useRealTimers();
79
- });
80
-
81
- test("encodes cookie value", () => {
82
- setClientCookie("foo", "val ue;");
83
- expect(document.cookie).toBe("foo=val%20ue%3B; path=/");
84
- });
85
- });
86
-
87
- describe("clearClientCookie", () => {
88
- test("clears cookie with default options", () => {
89
- document.cookie = "foo=bar";
90
- clearClientCookie("foo");
91
- expect(document.cookie).toBe(
92
- "foo=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain=example.com;",
93
- );
94
- });
95
-
96
- test("clears cookie with custom options", () => {
97
- const mockDoc = { cookie: "" };
98
- clearClientCookie("foo", {
99
- hostname: "custom.com",
100
- path: "/custom",
101
- documentRef: mockDoc as any,
102
- });
103
- expect(mockDoc.cookie).toBe(
104
- "foo=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/custom; domain=custom.com;",
105
- );
106
- });
107
- });
108
-
109
- describe("clearAllClientCookies", () => {
110
- test("clears all cookies based on documentRef.cookie", () => {
111
- const mockDoc = { cookie: "a=1; b=2" };
112
- const entries = clearAllClientCookies({ documentRef: mockDoc as any });
113
-
114
- expect(entries).toEqual(["a", "b"]);
115
- expect(mockDoc.cookie).toContain(
116
- "b=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/test/path; domain=com;",
117
- );
118
- });
119
-
120
- test("clears cookies using cookieStore if provided", async () => {
121
- const cookieStore = { delete: vi.fn().mockResolvedValue(undefined) };
122
- const mockDoc = { cookie: "a=1; b=2" };
123
-
124
- const entries = clearAllClientCookies({
125
- documentRef: mockDoc as any,
126
- cookieStore: cookieStore as any,
127
- });
128
-
129
- expect(entries).toEqual(["a", "b"]);
130
- expect(cookieStore.delete).toHaveBeenCalledWith("a");
131
- expect(cookieStore.delete).toHaveBeenCalledWith("b");
132
- });
133
-
134
- test("clears cookies from root path when includeRoot is true", () => {
135
- const mockDoc = { cookie: "a=1" };
136
- clearAllClientCookies({ documentRef: mockDoc as any, includeRoot: true });
137
-
138
- // The function iterates and updates document.cookie, so it will contain the last assignment.
139
- // With paths ["/", "/test/path"] and domain "example.com"
140
- expect(mockDoc.cookie).toContain(
141
- "a=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/test/path; domain=com;",
142
- );
143
- });
144
-
145
- test("uses provided cookieString instead of documentRef.cookie", () => {
146
- const mockDoc = { cookie: "a=1" };
147
- const entries = clearAllClientCookies({
148
- documentRef: mockDoc as any,
149
- cookieString: "x=1; y=2",
150
- });
151
-
152
- expect(entries).toEqual(["x", "y"]);
153
- expect(mockDoc.cookie).toContain("y=;");
154
- });
155
- });
156
- });
@@ -1,250 +0,0 @@
1
- import { describe, test, expect } from "vitest";
2
- import {
3
- normalizeAppLocale,
4
- toIntlLocale,
5
- toDate,
6
- toUtcMidnight,
7
- parseUtcDateInput,
8
- formatUtcDateKey,
9
- getUtcWeekdayIndex,
10
- addUtcDays,
11
- formatLongDate,
12
- formatDotDate,
13
- format24HourTime,
14
- formatKoreanTime,
15
- formatTwelveHourTime,
16
- formatRelativeText,
17
- formatRemainingText,
18
- } from "@/packages/datetime/dateTime.shared";
19
- import {
20
- formatClientDate,
21
- formatClientTime,
22
- formatClientDateTime,
23
- formatClientRelative,
24
- } from "@/packages/datetime/dateTime.client";
25
- import {
26
- formatServerDate,
27
- formatServerTime,
28
- formatServerDateTime,
29
- formatServerRelative,
30
- } from "@/packages/datetime/dateTime.server";
31
-
32
- describe("datetime module", () => {
33
- describe("shared", () => {
34
- describe("normalizeAppLocale", () => {
35
- test("normalizes kr/ko", () => {
36
- expect(normalizeAppLocale("kr")).toBe("kr");
37
- expect(normalizeAppLocale("ko")).toBe("kr");
38
- });
39
- test("normalizes jp/ja", () => {
40
- expect(normalizeAppLocale("jp")).toBe("jp");
41
- expect(normalizeAppLocale("ja")).toBe("jp");
42
- });
43
- test("defaults to en", () => {
44
- expect(normalizeAppLocale("fr")).toBe("en");
45
- expect(normalizeAppLocale(undefined)).toBe("en");
46
- });
47
- });
48
-
49
- describe("toIntlLocale", () => {
50
- test("maps to correct intl locales", () => {
51
- expect(toIntlLocale("kr")).toBe("ko-KR");
52
- expect(toIntlLocale("jp")).toBe("ja-JP");
53
- expect(toIntlLocale("en")).toBe("en-US");
54
- });
55
- });
56
-
57
- describe("toDate", () => {
58
- test("returns Date object for valid inputs", () => {
59
- expect(toDate(new Date("2020-01-01"))).toBeInstanceOf(Date);
60
- expect(toDate("2020-01-01")).toBeInstanceOf(Date);
61
- expect(toDate(1577836800000)).toBeInstanceOf(Date);
62
- });
63
- test("returns null for invalid inputs", () => {
64
- expect(toDate("invalid")).toBeNull();
65
- expect(toDate(new Date("invalid"))).toBeNull();
66
- });
67
- });
68
-
69
- describe("toUtcMidnight", () => {
70
- test("converts to UTC midnight", () => {
71
- const date = new Date("2020-01-01T15:30:00Z");
72
- const utcDate = toUtcMidnight(date);
73
- expect(utcDate.toISOString()).toBe("2020-01-01T00:00:00.000Z");
74
- });
75
- });
76
-
77
- describe("parseUtcDateInput", () => {
78
- test("parses YYYY-MM-DD string to UTC midnight", () => {
79
- const result = parseUtcDateInput("2020-05-10");
80
- expect(result?.toISOString()).toBe("2020-05-10T00:00:00.000Z");
81
- });
82
- test("parses Date object to UTC midnight", () => {
83
- const result = parseUtcDateInput(new Date("2020-05-10T15:00:00Z"));
84
- expect(result?.toISOString()).toBe("2020-05-10T00:00:00.000Z");
85
- });
86
- test("uses fallback", () => {
87
- const result = parseUtcDateInput("invalid", "2020-01-01T00:00:00Z");
88
- expect(result?.toISOString()).toBe("2020-01-01T00:00:00.000Z");
89
- });
90
- test("returns null if both invalid", () => {
91
- expect(parseUtcDateInput("invalid", "invalid")).toBeNull();
92
- });
93
- });
94
-
95
- describe("formatUtcDateKey", () => {
96
- test("formats date to YYYY-MM-DD", () => {
97
- expect(formatUtcDateKey(new Date("2020-05-05T00:00:00Z"))).toBe(
98
- "2020-05-05",
99
- );
100
- });
101
- });
102
-
103
- describe("getUtcWeekdayIndex", () => {
104
- test("returns correct weekday index", () => {
105
- expect(getUtcWeekdayIndex(new Date("2020-01-05T00:00:00Z"))).toBe(0); // Sunday
106
- expect(getUtcWeekdayIndex(new Date("2020-01-06T00:00:00Z"))).toBe(1); // Monday
107
- });
108
- });
109
-
110
- describe("addUtcDays", () => {
111
- test("adds days to date", () => {
112
- const result = addUtcDays(new Date("2020-01-01T00:00:00Z"), 5);
113
- expect(result.toISOString()).toBe("2020-01-06T00:00:00.000Z");
114
- });
115
- test("handles negative days", () => {
116
- const result = addUtcDays(new Date("2020-01-05T00:00:00Z"), -2);
117
- expect(result.toISOString()).toBe("2020-01-03T00:00:00.000Z");
118
- });
119
- });
120
-
121
- describe("formatters", () => {
122
- const testDate = new Date("2020-05-10T15:30:45Z"); // UTC time
123
-
124
- test("formatLongDate", () => {
125
- expect(formatLongDate(testDate, "kr", "UTC")).toBe("2020년 5월 10일");
126
- expect(formatLongDate(testDate, "jp", "UTC")).toBe("2020年5月10日");
127
- expect(formatLongDate(testDate, "en", "UTC")).toContain("2020");
128
- });
129
-
130
- test("formatDotDate", () => {
131
- expect(formatDotDate(testDate, "UTC")).toBe("2020. 5. 10.");
132
- });
133
-
134
- test("format24HourTime", () => {
135
- expect(format24HourTime(testDate, { timeZone: "UTC" })).toBe("15:30");
136
- expect(
137
- format24HourTime(testDate, { includeSeconds: true, timeZone: "UTC" }),
138
- ).toBe("15:30:45");
139
- });
140
-
141
- test("formatKoreanTime", () => {
142
- expect(formatKoreanTime(testDate, "UTC")).toBe("오후 3시 30분");
143
- expect(formatKoreanTime(new Date("2020-05-10T09:15:00Z"), "UTC")).toBe(
144
- "오전 9시 15분",
145
- );
146
- });
147
-
148
- test("formatTwelveHourTime", () => {
149
- expect(formatTwelveHourTime(testDate, "kr", "UTC")).toBe("오후 3:30");
150
- expect(formatTwelveHourTime(testDate, "en", "UTC")).toMatch(
151
- /3:30 (PM|pm)/,
152
- );
153
- });
154
- });
155
-
156
- describe("formatRelativeText", () => {
157
- test("formats seconds", () => {
158
- expect(formatRelativeText(30000, "en")).toEqual({
159
- text: "30 seconds ago",
160
- isRelative: true,
161
- });
162
- expect(formatRelativeText(30000, "kr")).toEqual({
163
- text: "30초 전",
164
- isRelative: true,
165
- });
166
- });
167
- test("formats minutes", () => {
168
- expect(formatRelativeText(120000, "en")).toEqual({
169
- text: "2 minutes ago",
170
- isRelative: true,
171
- });
172
- });
173
- test("formats hours", () => {
174
- expect(formatRelativeText(7200000, "en")).toEqual({
175
- text: "2 hours ago",
176
- isRelative: true,
177
- });
178
- });
179
- test("formats days", () => {
180
- expect(formatRelativeText(172800000, "en")).toEqual({
181
- text: "2 days ago",
182
- isRelative: true,
183
- });
184
- });
185
- });
186
-
187
- describe("formatRemainingText", () => {
188
- test("formats remaining time", () => {
189
- expect(formatRemainingText(0)).toBe("Closed");
190
- expect(formatRemainingText(0, "kr")).toBe("마감됨");
191
- expect(formatRemainingText(0, "jp")).toBe("締切終了");
192
-
193
- expect(formatRemainingText(1000, "en")).toBe("1 second left");
194
- expect(formatRemainingText(65000, "en")).toBe(
195
- "1 minute 5 seconds left",
196
- );
197
- expect(formatRemainingText(3665000, "kr")).toBe("1시간 1분 남음");
198
- expect(formatRemainingText(90065000, "jp")).toBe("あと1日1時間");
199
- });
200
- test("without suffix", () => {
201
- expect(formatRemainingText(1000, "en", { includeSuffix: false })).toBe(
202
- "1 second",
203
- );
204
- });
205
- });
206
- });
207
-
208
- describe("client/server wrappers", () => {
209
- const testDate = new Date("2020-05-10T15:30:45Z");
210
-
211
- test("formatClientDateTime / formatServerDateTime", () => {
212
- expect(formatClientDateTime(testDate, { timeZone: "UTC" })).toBe(
213
- "May 10, 2020 15:30",
214
- );
215
- expect(
216
- formatClientDateTime(testDate, { locale: "kr", timeZone: "UTC" }),
217
- ).toBe("2020년 5월 10일 15:30");
218
- expect(
219
- formatServerDateTime(testDate, { locale: "kr", timeZone: "UTC" }),
220
- ).toBe("2020년 5월 10일 15:30");
221
- });
222
-
223
- test("formatClientRelative / formatServerRelative", () => {
224
- const now = new Date("2020-05-10T15:35:45Z"); // 5 mins later
225
- expect(formatClientRelative(testDate, { now, locale: "kr" })).toEqual({
226
- text: "5분 전",
227
- isRelative: true,
228
- });
229
- expect(formatServerRelative(testDate, { now, locale: "kr" })).toEqual({
230
- text: "5분 전",
231
- isRelative: true,
232
- });
233
-
234
- const farFutureNow = new Date("2020-05-20T15:30:45Z"); // 10 days later
235
- expect(
236
- formatClientRelative(testDate, {
237
- now: farFutureNow,
238
- locale: "kr",
239
- fallbackDatePreset: "dot",
240
- maxRelativeDays: 7,
241
- }),
242
- ).toEqual({ text: formatDotDate(testDate), isRelative: false });
243
- });
244
-
245
- test("handles invalid inputs gracefully", () => {
246
- expect(formatClientDate("invalid", { preset: "long" })).toBe("");
247
- expect(formatServerTime("invalid", { preset: "ko" })).toBe("");
248
- });
249
- });
250
- });
@@ -1,12 +0,0 @@
1
- import { describe, test, expect } from "vitest";
2
- import { renderHook } from "@testing-library/react";
3
- import { useAvoidKeyboard } from "@/packages/hooks/useAvoidKeyboard";
4
-
5
- describe("useAvoidKeyboard", () => {
6
- test("returns style object", () => {
7
- const { result } = renderHook(() => useAvoidKeyboard());
8
- expect(result.current.style).toBeDefined();
9
- expect(result.current.style.transform).toBeUndefined();
10
- expect(result.current.style.transition).toContain("transform");
11
- });
12
- });
@@ -1,28 +0,0 @@
1
- import { describe, test, expect, vi } from "vitest";
2
- import { renderHook, act } from "@testing-library/react";
3
- import { useCheckInvisible } from "@/packages/hooks/useCheckInvisible";
4
-
5
- describe("useCheckInvisible", () => {
6
- test("returns isInvisible false when element is visible", () => {
7
- const el = document.createElement("div");
8
- el.className = "test-section";
9
- el.getBoundingClientRect = vi.fn(() => ({ top: 100, bottom: 300, left: 0, right: 100, width: 100, height: 200, x: 0, y: 100 }) as DOMRect);
10
- document.body.appendChild(el);
11
-
12
- const { result } = renderHook(() => useCheckInvisible("test-section"));
13
- expect(result.current.isInvisible).toBe(false);
14
- document.body.removeChild(el);
15
- });
16
-
17
- test("returns isInvisible true when element is scrolled above viewport", () => {
18
- const el = document.createElement("div");
19
- el.className = "test-section";
20
- el.getBoundingClientRect = vi.fn(() => ({ top: -50, bottom: 150, left: 0, right: 100, width: 100, height: 200, x: 0, y: -50 }) as DOMRect);
21
- document.body.appendChild(el);
22
-
23
- const { result } = renderHook(() => useCheckInvisible("test-section"));
24
- act(() => { window.dispatchEvent(new Event("scroll")); });
25
- expect(result.current.isInvisible).toBe(true);
26
- document.body.removeChild(el);
27
- });
28
- });
@@ -1,23 +0,0 @@
1
- import { describe, test, expect } from "vitest";
2
- import { renderHook, act } from "@testing-library/react";
3
- import { useCheckScroll } from "@/packages/hooks/useCheckScroll";
4
-
5
- describe("useCheckScroll", () => {
6
- test("initializes with scrollY state", () => {
7
- Object.defineProperty(window, "scrollY", { value: 100, writable: true });
8
- const { result } = renderHook(() => useCheckScroll());
9
- expect(result.current.isScrolled).toBe(true);
10
- });
11
-
12
- test("updates state on scroll", () => {
13
- Object.defineProperty(window, "scrollY", { value: 0, writable: true });
14
- const { result } = renderHook(() => useCheckScroll());
15
- expect(result.current.isScrolled).toBe(false);
16
-
17
- act(() => {
18
- window.scrollY = 150;
19
- window.dispatchEvent(new Event("scroll"));
20
- });
21
- expect(result.current.isScrolled).toBe(true);
22
- });
23
- });
@@ -1,14 +0,0 @@
1
- import { describe, test, expect } from "vitest";
2
- import { renderHook } from "@testing-library/react";
3
- import { useClientDateTime } from "@/packages/hooks/useClientDateTime";
4
-
5
- describe("useClientDateTime", () => {
6
- test("returns ready state and formatted text", () => {
7
- const { result } = renderHook(() =>
8
- useClientDateTime("2024-06-15", { locale: "ko" }),
9
- );
10
- expect(result.current.ready).toBe(true);
11
- expect(result.current.text).toBeTruthy();
12
- expect(result.current.date).toBeInstanceOf(Date);
13
- });
14
- });
@@ -1,71 +0,0 @@
1
- import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { renderHook } from "@testing-library/react";
3
- import { debounce, useDebounce } from "@/packages/hooks/useDebounce";
4
-
5
- describe("debounce (standalone)", () => {
6
- beforeEach(() => { vi.useFakeTimers(); });
7
- afterEach(() => { vi.useRealTimers(); });
8
-
9
- test("trailing: calls function after delay", () => {
10
- const fn = vi.fn();
11
- const d = debounce(fn, 100, { edges: ["trailing"] });
12
- d();
13
- expect(fn).not.toHaveBeenCalled();
14
- vi.advanceTimersByTime(100);
15
- expect(fn).toHaveBeenCalledOnce();
16
- });
17
-
18
- test("leading + trailing: calls immediately and debounces trailing", () => {
19
- const fn = vi.fn();
20
- const d = debounce(fn, 100, { edges: ["leading", "trailing"] });
21
- d();
22
- expect(fn).toHaveBeenCalledTimes(1);
23
- d();
24
- vi.advanceTimersByTime(100);
25
- expect(fn).toHaveBeenCalledTimes(2);
26
- });
27
-
28
- test("leading only: calls immediately, no trailing", () => {
29
- const fn = vi.fn();
30
- const d = debounce(fn, 100, { edges: ["leading"] });
31
- d();
32
- expect(fn).toHaveBeenCalledTimes(1);
33
- vi.advanceTimersByTime(100);
34
- expect(fn).toHaveBeenCalledTimes(1);
35
- });
36
-
37
- test("cancel aborts pending call", () => {
38
- const fn = vi.fn();
39
- const d = debounce(fn, 100, { edges: ["trailing"] });
40
- d();
41
- d.cancel();
42
- vi.advanceTimersByTime(100);
43
- expect(fn).not.toHaveBeenCalled();
44
- });
45
-
46
- test("multiple calls reset the timer in trailing mode", () => {
47
- const fn = vi.fn();
48
- const d = debounce(fn, 100, { edges: ["trailing"] });
49
- d();
50
- vi.advanceTimersByTime(50);
51
- d();
52
- vi.advanceTimersByTime(50);
53
- expect(fn).not.toHaveBeenCalled();
54
- vi.advanceTimersByTime(50);
55
- expect(fn).toHaveBeenCalledOnce();
56
- });
57
- });
58
-
59
- describe("useDebounce", () => {
60
- beforeEach(() => { vi.useFakeTimers(); });
61
- afterEach(() => { vi.useRealTimers(); });
62
-
63
- test("returns a debounced function", () => {
64
- const fn = vi.fn();
65
- const { result } = renderHook(() => useDebounce(fn, 100));
66
- result.current();
67
- expect(fn).not.toHaveBeenCalled();
68
- vi.advanceTimersByTime(100);
69
- expect(fn).toHaveBeenCalledOnce();
70
- });
71
- });
@@ -1,34 +0,0 @@
1
- import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { renderHook, act } from "@testing-library/react";
3
- import { useDebouncedCallback } from "@/packages/hooks/useDebouncedCallback";
4
-
5
- describe("useDebouncedCallback", () => {
6
- beforeEach(() => { vi.useFakeTimers(); });
7
- afterEach(() => { vi.useRealTimers(); });
8
-
9
- test("debounces onChange calls", () => {
10
- const onChange = vi.fn();
11
- const { result } = renderHook(() =>
12
- useDebouncedCallback({ onChange, timeThreshold: 100 }),
13
- );
14
-
15
- act(() => { result.current(true); });
16
- vi.advanceTimersByTime(100);
17
- expect(onChange).toHaveBeenCalledWith(true);
18
- });
19
-
20
- test("skips duplicate value", () => {
21
- const onChange = vi.fn();
22
- const { result } = renderHook(() =>
23
- useDebouncedCallback({ onChange, timeThreshold: 100 }),
24
- );
25
-
26
- act(() => { result.current(true); });
27
- vi.advanceTimersByTime(100);
28
- expect(onChange).toHaveBeenCalledTimes(1);
29
-
30
- act(() => { result.current(true); });
31
- vi.advanceTimersByTime(100);
32
- expect(onChange).toHaveBeenCalledTimes(1);
33
- });
34
- });
@@ -1,31 +0,0 @@
1
- import { describe, test, expect, vi, afterEach } from "vitest";
2
- import { renderHook, act } from "@testing-library/react";
3
- import { useDoubleClick } from "@/packages/hooks/useDoubleClick";
4
-
5
- describe("useDoubleClick", () => {
6
- afterEach(() => { vi.useRealTimers(); });
7
-
8
- test("fires click on single click (event.detail === 1)", () => {
9
- vi.useFakeTimers();
10
- const click = vi.fn();
11
- const doubleClick = vi.fn();
12
- const { result } = renderHook(() => useDoubleClick({ click, doubleClick }));
13
-
14
- const event = new MouseEvent("click", { detail: 1 }) as unknown as React.MouseEvent<HTMLElement>;
15
- act(() => { result.current(event); });
16
- expect(doubleClick).not.toHaveBeenCalled();
17
- vi.advanceTimersByTime(300);
18
- expect(click).toHaveBeenCalledOnce();
19
- });
20
-
21
- test("fires doubleClick on double click (event.detail === 2)", () => {
22
- const click = vi.fn();
23
- const doubleClick = vi.fn();
24
- const { result } = renderHook(() => useDoubleClick({ click, doubleClick }));
25
-
26
- const event = new MouseEvent("click", { detail: 2 }) as unknown as React.MouseEvent<HTMLElement>;
27
- act(() => { result.current(event); });
28
- expect(doubleClick).toHaveBeenCalledOnce();
29
- expect(click).not.toHaveBeenCalled();
30
- });
31
- });
@@ -1,50 +0,0 @@
1
- import { describe, test, expect, vi, beforeEach, type Mock } from "vitest";
2
- import { renderHook, act } from "@testing-library/react";
3
- import { useGeolocation } from "@/packages/hooks/useGeolocation";
4
-
5
- describe("useGeolocation", () => {
6
- let mockGeolocation: {
7
- getCurrentPosition: Mock;
8
- watchPosition: Mock;
9
- clearWatch: Mock;
10
- };
11
-
12
- beforeEach(() => {
13
- mockGeolocation = {
14
- getCurrentPosition: vi.fn((success) =>
15
- success({
16
- coords: {
17
- latitude: 37.5665,
18
- longitude: 126.978,
19
- accuracy: 10,
20
- altitude: null,
21
- altitudeAccuracy: null,
22
- heading: null,
23
- speed: null,
24
- },
25
- timestamp: Date.now(),
26
- }),
27
- ),
28
- watchPosition: vi.fn(),
29
- clearWatch: vi.fn(),
30
- };
31
- Object.defineProperty(navigator, "geolocation", {
32
- value: mockGeolocation,
33
- writable: true,
34
- configurable: true,
35
- });
36
- });
37
-
38
- test("returns default state", () => {
39
- const { result } = renderHook(() => useGeolocation());
40
- expect(result.current.loading).toBe(false);
41
- expect(result.current.error).toBeNull();
42
- expect(result.current.data).toBeNull();
43
- });
44
-
45
- test("getCurrentPosition fetches location", () => {
46
- const { result } = renderHook(() => useGeolocation());
47
- act(() => { result.current.getCurrentPosition(); });
48
- expect(mockGeolocation.getCurrentPosition).toHaveBeenCalledOnce();
49
- });
50
- });
@@ -1,10 +0,0 @@
1
- import { describe, test, expect } from "vitest";
2
- import { renderHook } from "@testing-library/react";
3
- import { useHasMounted } from "@/packages/hooks/useHasMounted";
4
-
5
- describe("useHasMounted", () => {
6
- test("returns true after mount", () => {
7
- const { result } = renderHook(() => useHasMounted());
8
- expect(result.current).toBe(true);
9
- });
10
- });