@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,13 +0,0 @@
1
- import { describe, test, expect, vi } from "vitest";
2
- import { renderHook } from "@testing-library/react";
3
- import { useIntersectionObserver } from "@/packages/hooks/useIntersectionObserver";
4
-
5
- describe("useIntersectionObserver", () => {
6
- test("returns a ref callback", () => {
7
- const callback = vi.fn();
8
- const { result } = renderHook(() =>
9
- useIntersectionObserver(callback, { threshold: 0 }),
10
- );
11
- expect(typeof result.current).toBe("function");
12
- });
13
- });
@@ -1,31 +0,0 @@
1
- import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { renderHook } from "@testing-library/react";
3
- import { useInterval } from "@/packages/hooks/useInterval";
4
-
5
- describe("useInterval", () => {
6
- beforeEach(() => { vi.useFakeTimers(); });
7
- afterEach(() => { vi.useRealTimers(); });
8
-
9
- test("calls callback repeatedly at interval", () => {
10
- const fn = vi.fn();
11
- renderHook(() => useInterval(fn, 100));
12
- expect(fn).not.toHaveBeenCalled();
13
- vi.advanceTimersByTime(100);
14
- expect(fn).toHaveBeenCalledTimes(1);
15
- vi.advanceTimersByTime(100);
16
- expect(fn).toHaveBeenCalledTimes(2);
17
- });
18
-
19
- test("calls immediately with immediate option", () => {
20
- const fn = vi.fn();
21
- renderHook(() => useInterval(fn, { delay: 100, immediate: true }));
22
- expect(fn).toHaveBeenCalledOnce();
23
- });
24
-
25
- test("does not call when disabled", () => {
26
- const fn = vi.fn();
27
- renderHook(() => useInterval(fn, { delay: 100, enabled: false }));
28
- vi.advanceTimersByTime(200);
29
- expect(fn).not.toHaveBeenCalled();
30
- });
31
- });
@@ -1,10 +0,0 @@
1
- import { describe, test, expect } from "vitest";
2
- import { renderHook } from "@testing-library/react";
3
- import { useKeyboardHeight } from "@/packages/hooks/useKeyboardHeight";
4
-
5
- describe("useKeyboardHeight", () => {
6
- test("returns default keyboard height of 0", () => {
7
- const { result } = renderHook(() => useKeyboardHeight());
8
- expect(result.current.keyboardHeight).toBe(0);
9
- });
10
- });
@@ -1,82 +0,0 @@
1
- import { describe, test, expect, vi } from "vitest";
2
- import { renderHook, act } from "@testing-library/react";
3
- import { useKeyboardListNavigation } from "@/packages/hooks/useKeyboardListNavigation";
4
-
5
- describe("useKeyboardListNavigation", () => {
6
- test("initializes with activeIndex -1", () => {
7
- const { result } = renderHook(() =>
8
- useKeyboardListNavigation({ itemCount: 3 }),
9
- );
10
- expect(result.current.activeIndex).toBe(-1);
11
- });
12
-
13
- test("handleListKeyDown with ArrowDown moves to first item when none active", () => {
14
- const { result } = renderHook(() =>
15
- useKeyboardListNavigation({ itemCount: 3 }),
16
- );
17
-
18
- const container = document.createElement("div");
19
- const items = [0, 1, 2].map(() => {
20
- const el = document.createElement("div");
21
- el.setAttribute("tabindex", "-1");
22
- el.scrollIntoView = vi.fn();
23
- container.appendChild(el);
24
- return el;
25
- });
26
- items.forEach((el, i) => result.current.setItemRef(i, el));
27
-
28
- const event = {
29
- key: "ArrowDown",
30
- defaultPrevented: false,
31
- preventDefault: vi.fn(),
32
- target: container,
33
- } as unknown as React.KeyboardEvent<HTMLElement>;
34
-
35
- act(() => { result.current.handleListKeyDown(event); });
36
- expect(result.current.activeIndex).toBe(0);
37
- });
38
-
39
- test("handleListKeyDown with ArrowUp moves to previous item", () => {
40
- const { result } = renderHook(() =>
41
- useKeyboardListNavigation({ itemCount: 3 }),
42
- );
43
-
44
- const container = document.createElement("div");
45
- const items = [0, 1, 2].map(() => {
46
- const el = document.createElement("div");
47
- el.setAttribute("tabindex", "-1");
48
- el.scrollIntoView = vi.fn();
49
- container.appendChild(el);
50
- return el;
51
- });
52
- items.forEach((el, i) => result.current.setItemRef(i, el));
53
- act(() => { result.current.setActiveIndex(1); });
54
-
55
- const event = {
56
- key: "ArrowUp",
57
- defaultPrevented: false,
58
- preventDefault: vi.fn(),
59
- target: container,
60
- } as unknown as React.KeyboardEvent<HTMLElement>;
61
-
62
- act(() => { result.current.handleListKeyDown(event); });
63
- expect(result.current.activeIndex).toBe(0);
64
- });
65
-
66
- test("Escape resets activeIndex to -1", () => {
67
- const { result } = renderHook(() =>
68
- useKeyboardListNavigation({ itemCount: 3 }),
69
- );
70
-
71
- const event = {
72
- key: "Escape",
73
- defaultPrevented: false,
74
- preventDefault: vi.fn(),
75
- target: document.createElement("div"),
76
- } as unknown as React.KeyboardEvent<HTMLElement>;
77
-
78
- result.current.setActiveIndex(1);
79
- act(() => { result.current.handleListKeyDown(event); });
80
- expect(result.current.activeIndex).toBe(-1);
81
- });
82
- });
@@ -1,52 +0,0 @@
1
- import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { renderHook, act } from "@testing-library/react";
3
- import type React from "react";
4
- import { useLongPress } from "@/packages/hooks/useLongPress";
5
-
6
- function createMouseEvent(type: string): React.MouseEvent<HTMLElement> {
7
- return {
8
- type,
9
- nativeEvent: new MouseEvent(type, { clientX: 0, clientY: 0 }),
10
- clientX: 0,
11
- clientY: 0,
12
- } as unknown as React.MouseEvent<HTMLElement>;
13
- }
14
-
15
- describe("useLongPress", () => {
16
- beforeEach(() => { vi.useFakeTimers(); });
17
- afterEach(() => { vi.useRealTimers(); });
18
-
19
- test("fires onLongPress after delay", () => {
20
- const onLongPress = vi.fn();
21
- const { result } = renderHook(() => useLongPress(onLongPress, { delay: 200 }));
22
-
23
- act(() => { result.current.onMouseDown(createMouseEvent("mousedown")); });
24
- expect(onLongPress).not.toHaveBeenCalled();
25
- vi.advanceTimersByTime(200);
26
- expect(onLongPress).toHaveBeenCalledOnce();
27
- });
28
-
29
- test("fires onClick when released before delay", () => {
30
- const onLongPress = vi.fn();
31
- const onClick = vi.fn();
32
- const { result } = renderHook(() =>
33
- useLongPress(onLongPress, { delay: 200, onClick }),
34
- );
35
-
36
- act(() => { result.current.onMouseDown(createMouseEvent("mousedown")); });
37
- act(() => { result.current.onMouseUp(createMouseEvent("mouseup")); });
38
- vi.advanceTimersByTime(200);
39
- expect(onLongPress).not.toHaveBeenCalled();
40
- expect(onClick).toHaveBeenCalledOnce();
41
- });
42
-
43
- test("cancels on mouse leave", () => {
44
- const onLongPress = vi.fn();
45
- const { result } = renderHook(() => useLongPress(onLongPress, { delay: 200 }));
46
-
47
- act(() => { result.current.onMouseDown(createMouseEvent("mousedown")); });
48
- act(() => { result.current.onMouseLeave(createMouseEvent("mouseleave")); });
49
- vi.advanceTimersByTime(200);
50
- expect(onLongPress).not.toHaveBeenCalled();
51
- });
52
- });
@@ -1,35 +0,0 @@
1
- import { describe, test, expect, vi } from "vitest";
2
- import { renderHook } from "@testing-library/react";
3
- import { usePreservedCallback } from "@/packages/hooks/usePreservedCallback";
4
-
5
- describe("usePreservedCallback", () => {
6
- test("returns a stable function reference across renders", () => {
7
- const { result, rerender } = renderHook(
8
- ({ cb }) => usePreservedCallback(cb),
9
- { initialProps: { cb: vi.fn() } },
10
- );
11
- const firstRef = result.current;
12
- rerender({ cb: vi.fn() });
13
- expect(result.current).toBe(firstRef);
14
- });
15
-
16
- test("invokes the latest callback", () => {
17
- const fn1 = vi.fn();
18
- const fn2 = vi.fn();
19
- const { result, rerender } = renderHook(
20
- ({ cb }) => usePreservedCallback(cb),
21
- { initialProps: { cb: fn1 } },
22
- );
23
- rerender({ cb: fn2 });
24
- result.current();
25
- expect(fn2).toHaveBeenCalledOnce();
26
- expect(fn1).not.toHaveBeenCalled();
27
- });
28
-
29
- test("passes arguments through", () => {
30
- const fn = vi.fn();
31
- const { result } = renderHook(() => usePreservedCallback(fn));
32
- result.current("a", 1);
33
- expect(fn).toHaveBeenCalledWith("a", 1);
34
- });
35
- });
@@ -1,35 +0,0 @@
1
- import { describe, test, expect, vi } from "vitest";
2
- import { renderHook } from "@testing-library/react";
3
- import { usePreservedReference } from "@/packages/hooks/usePreservedReference";
4
-
5
- describe("usePreservedReference", () => {
6
- test("returns same reference for deeply equal values", () => {
7
- const { result, rerender } = renderHook(
8
- ({ val }) => usePreservedReference(val),
9
- { initialProps: { val: { a: 1, b: { c: [2, 3] } } } },
10
- );
11
- const first = result.current;
12
- rerender({ val: { a: 1, b: { c: [2, 3] } } });
13
- expect(result.current).toBe(first);
14
- });
15
-
16
- test("returns new reference for different values", () => {
17
- const { result, rerender } = renderHook(
18
- ({ val }) => usePreservedReference(val),
19
- { initialProps: { val: { a: 1 } } },
20
- );
21
- const first = result.current;
22
- rerender({ val: { a: 2 } });
23
- expect(result.current).not.toBe(first);
24
- });
25
-
26
- test("accepts custom comparator", () => {
27
- const { result, rerender } = renderHook(
28
- ({ val }) => usePreservedReference(val, () => true),
29
- { initialProps: { val: { a: 1 } } },
30
- );
31
- const first = result.current;
32
- rerender({ val: { a: 999 } });
33
- expect(result.current).toBe(first);
34
- });
35
- });
@@ -1,30 +0,0 @@
1
- import { describe, test, expect, vi } from "vitest";
2
- import { renderHook, act } from "@testing-library/react";
3
- import { useRefEffect } from "@/packages/hooks/useRefEffect";
4
-
5
- describe("useRefEffect", () => {
6
- test("calls callback with element and cleanup on unmount", () => {
7
- const cleanup = vi.fn();
8
- const callback = vi.fn(() => cleanup);
9
- const { result } = renderHook(() => useRefEffect(callback, []));
10
-
11
- const element = document.createElement("div");
12
- act(() => { (result.current as (el: HTMLElement | null) => void)(element); });
13
- expect(callback).toHaveBeenCalledWith(element);
14
-
15
- act(() => { (result.current as (el: HTMLElement | null) => void)(null); });
16
- expect(cleanup).toHaveBeenCalledOnce();
17
- });
18
-
19
- test("calls cleanup before setting new element", () => {
20
- const cleanup = vi.fn();
21
- const callback = vi.fn(() => cleanup);
22
- const { result } = renderHook(() => useRefEffect(callback, []));
23
-
24
- const el1 = document.createElement("div");
25
- const el2 = document.createElement("span");
26
- act(() => { (result.current as (el: HTMLElement | null) => void)(el1); });
27
- act(() => { (result.current as (el: HTMLElement | null) => void)(el2); });
28
- expect(cleanup).toHaveBeenCalledOnce();
29
- });
30
- });
@@ -1,19 +0,0 @@
1
- import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { renderHook } from "@testing-library/react";
3
- import { useRelativeDateTime } from "@/packages/hooks/useRelativeDateTime";
4
-
5
- describe("useRelativeDateTime", () => {
6
- beforeEach(() => { vi.useFakeTimers(); });
7
- afterEach(() => { vi.useRealTimers(); });
8
-
9
- test("returns relative time text", () => {
10
- const now = Date.now();
11
- const fiveMinAgo = new Date(now - 5 * 60 * 1000).toISOString();
12
- const { result } = renderHook(() =>
13
- useRelativeDateTime(fiveMinAgo, { locale: "ko" }),
14
- );
15
- expect(result.current.ready).toBe(true);
16
- expect(result.current.isRelative).toBe(true);
17
- expect(result.current.text).toBeTruthy();
18
- });
19
- });
@@ -1,34 +0,0 @@
1
- import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { renderHook, act } from "@testing-library/react";
3
- import { useTimeout } from "@/packages/hooks/useTimeout";
4
-
5
- describe("useTimeout", () => {
6
- beforeEach(() => { vi.useFakeTimers(); });
7
- afterEach(() => { vi.useRealTimers(); });
8
-
9
- test("calls callback after delay", () => {
10
- const fn = vi.fn();
11
- renderHook(() => useTimeout(fn, 100));
12
- expect(fn).not.toHaveBeenCalled();
13
- vi.advanceTimersByTime(100);
14
- expect(fn).toHaveBeenCalledOnce();
15
- });
16
-
17
- test("does not call when disabled", () => {
18
- const fn = vi.fn();
19
- renderHook(() => useTimeout(fn, 100, { enabled: false }));
20
- vi.advanceTimersByTime(200);
21
- expect(fn).not.toHaveBeenCalled();
22
- });
23
-
24
- test("start and clear controls", () => {
25
- const fn = vi.fn();
26
- const { result } = renderHook(() => useTimeout(fn, 1000, { enabled: false }));
27
- expect(result.current.isPending()).toBe(false);
28
- act(() => { result.current.start(50); });
29
- expect(result.current.isPending()).toBe(true);
30
- vi.advanceTimersByTime(50);
31
- expect(fn).toHaveBeenCalledOnce();
32
- expect(result.current.isPending()).toBe(false);
33
- });
34
- });
@@ -1,35 +0,0 @@
1
- import { describe, test, expect } from "vitest";
2
- import { renderHook, act } from "@testing-library/react";
3
- import { useToggleState } from "@/packages/hooks/useToggleState";
4
-
5
- describe("useToggleState", () => {
6
- test("initializes with false by default", () => {
7
- const { result } = renderHook(() => useToggleState());
8
- expect(result.current.value).toBe(false);
9
- });
10
-
11
- test("initializes with provided value", () => {
12
- const { result } = renderHook(() => useToggleState(true));
13
- expect(result.current.value).toBe(true);
14
- });
15
-
16
- test("setTrue sets value to true", () => {
17
- const { result } = renderHook(() => useToggleState(false));
18
- act(() => { result.current.setTrue(); });
19
- expect(result.current.value).toBe(true);
20
- });
21
-
22
- test("setFalse sets value to false", () => {
23
- const { result } = renderHook(() => useToggleState(true));
24
- act(() => { result.current.setFalse(); });
25
- expect(result.current.value).toBe(false);
26
- });
27
-
28
- test("toggle switches value", () => {
29
- const { result } = renderHook(() => useToggleState(false));
30
- act(() => { result.current.toggle(); });
31
- expect(result.current.value).toBe(true);
32
- act(() => { result.current.toggle(); });
33
- expect(result.current.value).toBe(false);
34
- });
35
- });
@@ -1,10 +0,0 @@
1
- import { describe, test, expect } from "vitest";
2
- import { renderHook } from "@testing-library/react";
3
- import { useViewportHeight } from "@/packages/hooks/useViewportHeight";
4
-
5
- describe("useViewportHeight", () => {
6
- test("returns a height value", () => {
7
- const { result } = renderHook(() => useViewportHeight());
8
- expect(result.current.height).toBeGreaterThanOrEqual(0);
9
- });
10
- });
@@ -1,35 +0,0 @@
1
- import { describe, test, expect, vi, beforeEach } from "vitest";
2
- import { renderHook } from "@testing-library/react";
3
- import { useViewportMatch } from "@/packages/hooks/useViewportMatch";
4
-
5
- describe("useViewportMatch", () => {
6
- beforeEach(() => {
7
- vi.stubGlobal(
8
- "matchMedia",
9
- vi.fn().mockImplementation((query: string) => ({
10
- matches: false,
11
- media: query,
12
- addEventListener: vi.fn(),
13
- removeEventListener: vi.fn(),
14
- })),
15
- );
16
- });
17
-
18
- test("returns false initially", () => {
19
- const { result } = renderHook(() => useViewportMatch("(min-width: 768px)"));
20
- expect(result.current).toBe(false);
21
- });
22
-
23
- test("returns true when matched", () => {
24
- vi.stubGlobal(
25
- "matchMedia",
26
- vi.fn().mockImplementation(() => ({
27
- matches: true,
28
- addEventListener: vi.fn(),
29
- removeEventListener: vi.fn(),
30
- })),
31
- );
32
- const { result } = renderHook(() => useViewportMatch("(min-width: 768px)"));
33
- expect(result.current).toBe(true);
34
- });
35
- });
package/tests/setup.ts DELETED
@@ -1,9 +0,0 @@
1
- import "@testing-library/jest-dom";
2
-
3
- class ResizeObserverMock {
4
- observe() {}
5
- unobserve() {}
6
- disconnect() {}
7
- }
8
-
9
- window.ResizeObserver = ResizeObserverMock as unknown as typeof ResizeObserver;
@@ -1,54 +0,0 @@
1
- import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
2
- import {
3
- getLocalStorage,
4
- updateLocalStorage,
5
- removeLocalStorage,
6
- getSessionStorage,
7
- updateSessionStorage,
8
- removeSessionStorage,
9
- } from "@/packages/utils/browserStorage";
10
-
11
- describe("browserStorage", () => {
12
- let mockStorage: Record<string, string>;
13
- let mockStorage2: Record<string, string>;
14
-
15
- beforeEach(() => {
16
- mockStorage = {};
17
- mockStorage2 = {};
18
-
19
- const createMockStorage = (store: Record<string, string>) => ({
20
- getItem: vi.fn((key) => store[key] ?? null),
21
- setItem: vi.fn((key, value) => { store[key] = value.toString(); }),
22
- removeItem: vi.fn((key) => { delete store[key]; }),
23
- clear: vi.fn(() => { Object.keys(store).forEach((k) => delete store[k]); }),
24
- });
25
-
26
- vi.stubGlobal("localStorage", createMockStorage(mockStorage));
27
- vi.stubGlobal("sessionStorage", createMockStorage(mockStorage2));
28
- });
29
-
30
- afterEach(() => { vi.unstubAllGlobals(); });
31
-
32
- test("localStorage operations", () => {
33
- updateLocalStorage("foo", { bar: "baz" });
34
- expect(localStorage.getItem("foo")).toBe('{"bar":"baz"}');
35
- expect(getLocalStorage("foo")).toEqual({ bar: "baz" });
36
-
37
- removeLocalStorage("foo");
38
- expect(getLocalStorage("foo")).toBeNull();
39
- });
40
-
41
- test("sessionStorage operations", () => {
42
- updateSessionStorage("foo", { bar: "baz" });
43
- expect(sessionStorage.getItem("foo")).toBe('{"bar":"baz"}');
44
- expect(getSessionStorage("foo")).toEqual({ bar: "baz" });
45
-
46
- removeSessionStorage("foo");
47
- expect(getSessionStorage("foo")).toBeNull();
48
- });
49
-
50
- test("handles invalid json gracefully", () => {
51
- localStorage.setItem("invalid", "not-json");
52
- expect(getLocalStorage("invalid")).toBeNull();
53
- });
54
- });
@@ -1,27 +0,0 @@
1
- import { describe, test, expect } from "vitest";
2
- import { render, screen } from "@testing-library/react";
3
- import React from "react";
4
- import { buildContext } from "@/packages/utils/buildContext";
5
-
6
- describe("buildContext", () => {
7
- const [Provider, useValue] = buildContext<string>("default");
8
-
9
- function TestComponent() {
10
- const val = useValue();
11
- return <div data-testid="val">{val}</div>;
12
- }
13
-
14
- test("returns default value when not wrapped in provider", () => {
15
- render(<TestComponent />);
16
- expect(screen.getByTestId("val").textContent).toBe("default");
17
- });
18
-
19
- test("returns provided value", () => {
20
- render(
21
- <Provider value="provided">
22
- <TestComponent />
23
- </Provider>,
24
- );
25
- expect(screen.getByTestId("val").textContent).toBe("provided");
26
- });
27
- });
@@ -1,84 +0,0 @@
1
- import { describe, test, expect, afterEach } from "vitest";
2
- import { getDeviceInfo } from "@/packages/utils/checkDevice";
3
-
4
- describe("getDeviceInfo", () => {
5
- const originalUserAgent = navigator.userAgent;
6
-
7
- afterEach(() => {
8
- Object.defineProperty(navigator, "userAgent", {
9
- value: originalUserAgent,
10
- writable: true,
11
- configurable: true,
12
- });
13
- });
14
-
15
- test("returns default values in SSR (no window)", () => {
16
- const win = global.window;
17
- (global as any).window = undefined;
18
- const info = getDeviceInfo();
19
- expect(info.isMobile).toBe(false);
20
- expect(info.browser).toBe("unknown");
21
- global.window = win;
22
- });
23
-
24
- test("detects mobile Chrome", () => {
25
- Object.defineProperty(navigator, "userAgent", {
26
- value: "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
27
- writable: true,
28
- configurable: true,
29
- });
30
- const info = getDeviceInfo();
31
- expect(info.isMobile).toBe(true);
32
- expect(info.isAndroid).toBe(true);
33
- expect(info.browser).toBe("chrome");
34
- });
35
-
36
- test("detects iOS Safari", () => {
37
- Object.defineProperty(navigator, "userAgent", {
38
- value: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
39
- writable: true,
40
- configurable: true,
41
- });
42
- const info = getDeviceInfo();
43
- expect(info.isMobile).toBe(true);
44
- expect(info.isIOS).toBe(true);
45
- expect(info.isSafari).toBe(true);
46
- expect(info.isIOSSafari).toBe(true);
47
- expect(info.browser).toBe("safari");
48
- });
49
-
50
- test("detects desktop Chrome", () => {
51
- Object.defineProperty(navigator, "userAgent", {
52
- value: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
53
- writable: true,
54
- configurable: true,
55
- });
56
- const info = getDeviceInfo();
57
- expect(info.isMobile).toBe(false);
58
- expect(info.browser).toBe("chrome");
59
- });
60
-
61
- test("detects Mac Safari", () => {
62
- Object.defineProperty(navigator, "userAgent", {
63
- value: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/604.1",
64
- writable: true,
65
- configurable: true,
66
- });
67
- const info = getDeviceInfo();
68
- expect(info.isMobile).toBe(false);
69
- expect(info.isMacSafari).toBe(true);
70
- expect(info.isSafari).toBe(true);
71
- expect(info.browser).toBe("safari");
72
- });
73
-
74
- test("detects Samsung Browser", () => {
75
- Object.defineProperty(navigator, "userAgent", {
76
- value: "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/24.0 Chrome/120.0.6099.230 Mobile Safari/537.36",
77
- writable: true,
78
- configurable: true,
79
- });
80
- const info = getDeviceInfo();
81
- expect(info.isSamsungBrowser).toBe(true);
82
- expect(info.browser).toBe("samsung");
83
- });
84
- });