@umituz/react-native-design-system 2.3.14 → 2.3.16

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 (93) hide show
  1. package/package.json +19 -2
  2. package/src/index.ts +105 -0
  3. package/src/layouts/ScreenLayout/ScreenLayout.example.tsx +2 -2
  4. package/src/layouts/ScreenLayout/ScreenLayout.tsx +1 -1
  5. package/src/molecules/animation/core/AnimationCore.ts +29 -0
  6. package/src/molecules/animation/domain/entities/Animation.ts +81 -0
  7. package/src/molecules/animation/domain/entities/Fireworks.ts +44 -0
  8. package/src/molecules/animation/domain/entities/Theme.ts +76 -0
  9. package/src/molecules/animation/index.ts +146 -0
  10. package/src/molecules/animation/infrastructure/services/AnimationConfigService.ts +35 -0
  11. package/src/molecules/animation/infrastructure/services/SpringAnimationConfigService.ts +67 -0
  12. package/src/molecules/animation/infrastructure/services/TimingAnimationConfigService.ts +57 -0
  13. package/src/molecules/animation/infrastructure/services/__tests__/SpringAnimationConfigService.test.ts +114 -0
  14. package/src/molecules/animation/infrastructure/services/__tests__/TimingAnimationConfigService.test.ts +105 -0
  15. package/src/molecules/animation/presentation/components/Fireworks.tsx +126 -0
  16. package/src/molecules/animation/presentation/components/__tests__/Fireworks.test.tsx +189 -0
  17. package/src/molecules/animation/presentation/hooks/__tests__/useAnimation.integration.test.ts +216 -0
  18. package/src/molecules/animation/presentation/hooks/__tests__/useFireworks.test.ts +242 -0
  19. package/src/molecules/animation/presentation/hooks/__tests__/useGesture.test.ts +111 -0
  20. package/src/molecules/animation/presentation/hooks/__tests__/useSpringAnimation.test.ts +131 -0
  21. package/src/molecules/animation/presentation/hooks/__tests__/useTimingAnimation.test.ts +175 -0
  22. package/src/molecules/animation/presentation/hooks/__tests__/useTransformAnimation.test.ts +137 -0
  23. package/src/molecules/animation/presentation/hooks/useAnimation.ts +77 -0
  24. package/src/molecules/animation/presentation/hooks/useFireworks.ts +141 -0
  25. package/src/molecules/animation/presentation/hooks/useGesture.ts +61 -0
  26. package/src/molecules/animation/presentation/hooks/useGestureCreators.ts +163 -0
  27. package/src/molecules/animation/presentation/hooks/useGestureState.ts +53 -0
  28. package/src/molecules/animation/presentation/hooks/useIconAnimations.ts +119 -0
  29. package/src/molecules/animation/presentation/hooks/useModalAnimations.ts +124 -0
  30. package/src/molecules/animation/presentation/hooks/useReanimatedReady.ts +60 -0
  31. package/src/molecules/animation/presentation/hooks/useSpringAnimation.ts +69 -0
  32. package/src/molecules/animation/presentation/hooks/useTimingAnimation.ts +111 -0
  33. package/src/molecules/animation/presentation/hooks/useTransformAnimation.ts +57 -0
  34. package/src/molecules/animation/presentation/providers/AnimationThemeProvider.tsx +62 -0
  35. package/src/molecules/animation/presentation/providers/__tests__/AnimationThemeProvider.test.tsx +165 -0
  36. package/src/molecules/animation/types/global.d.ts +97 -0
  37. package/src/molecules/celebration/domain/entities/CelebrationConfig.ts +17 -0
  38. package/src/molecules/celebration/domain/entities/FireworksConfig.ts +32 -0
  39. package/src/molecules/celebration/index.ts +93 -0
  40. package/src/molecules/celebration/infrastructure/services/FireworksConfigService.ts +49 -0
  41. package/src/molecules/celebration/presentation/components/CelebrationFireworksOverlay.tsx +33 -0
  42. package/src/molecules/celebration/presentation/components/CelebrationModal.tsx +78 -0
  43. package/src/molecules/celebration/presentation/components/CelebrationModalContent.tsx +90 -0
  44. package/src/molecules/celebration/presentation/hooks/useCelebrationModalAnimation.ts +49 -0
  45. package/src/molecules/celebration/presentation/hooks/useCelebrationState.ts +45 -0
  46. package/src/molecules/celebration/presentation/styles/CelebrationModalStyles.ts +65 -0
  47. package/src/molecules/countdown/components/Countdown.tsx +128 -0
  48. package/src/molecules/countdown/components/CountdownHeader.tsx +84 -0
  49. package/src/molecules/countdown/components/TimeUnit.tsx +73 -0
  50. package/src/molecules/countdown/hooks/useCountdown.ts +107 -0
  51. package/src/molecules/countdown/index.ts +25 -0
  52. package/src/molecules/countdown/types/CountdownTypes.ts +31 -0
  53. package/src/molecules/countdown/utils/TimeCalculator.ts +46 -0
  54. package/src/molecules/emoji/domain/entities/Emoji.ts +129 -0
  55. package/src/molecules/emoji/index.ts +177 -0
  56. package/src/molecules/emoji/presentation/components/EmojiPicker.tsx +102 -0
  57. package/src/molecules/emoji/presentation/hooks/useEmojiPicker.ts +171 -0
  58. package/src/molecules/index.ts +21 -0
  59. package/src/molecules/long-press-menu/domain/entities/MenuAction.ts +37 -0
  60. package/src/molecules/long-press-menu/index.ts +16 -0
  61. package/src/molecules/navigation/StackNavigator.tsx +75 -0
  62. package/src/molecules/navigation/TabsNavigator.tsx +94 -0
  63. package/src/molecules/navigation/components/FabButton.tsx +45 -0
  64. package/src/molecules/navigation/components/TabLabel.tsx +47 -0
  65. package/src/molecules/navigation/createStackNavigator.ts +20 -0
  66. package/src/molecules/navigation/createTabNavigator.ts +20 -0
  67. package/src/molecules/navigation/hooks/useTabBarStyles.ts +54 -0
  68. package/src/molecules/navigation/index.ts +37 -0
  69. package/src/molecules/navigation/types.ts +118 -0
  70. package/src/molecules/navigation/utils/AppNavigation.ts +101 -0
  71. package/src/molecules/navigation/utils/IconRenderer.ts +50 -0
  72. package/src/molecules/navigation/utils/LabelProcessor.ts +70 -0
  73. package/src/molecules/navigation/utils/NavigationCleanup.ts +62 -0
  74. package/src/molecules/navigation/utils/NavigationTheme.ts +21 -0
  75. package/src/molecules/navigation/utils/NavigationValidator.ts +61 -0
  76. package/src/molecules/navigation/utils/ScreenFactory.ts +115 -0
  77. package/src/molecules/navigation/utils/__tests__/IconRenderer.getIconName.test.ts +109 -0
  78. package/src/molecules/navigation/utils/__tests__/IconRenderer.renderIcon.test.ts +116 -0
  79. package/src/molecules/navigation/utils/__tests__/LabelProcessor.processLabel.test.ts +116 -0
  80. package/src/molecules/navigation/utils/__tests__/LabelProcessor.processTitle.test.ts +59 -0
  81. package/src/molecules/navigation/utils/__tests__/NavigationCleanup.test.ts +271 -0
  82. package/src/molecules/navigation/utils/__tests__/NavigationValidator.test.ts +252 -0
  83. package/src/molecules/swipe-actions/domain/entities/SwipeAction.ts +194 -0
  84. package/src/molecules/swipe-actions/index.ts +6 -0
  85. package/src/molecules/swipe-actions/presentation/components/SwipeActionButton.tsx +131 -0
  86. package/src/theme/hooks/useResponsiveDesignTokens.ts +1 -1
  87. package/src/utilities/clipboard/ClipboardUtils.ts +71 -0
  88. package/src/utilities/clipboard/index.ts +5 -0
  89. package/src/utilities/index.ts +6 -0
  90. package/src/utilities/sharing/domain/entities/Share.ts +210 -0
  91. package/src/utilities/sharing/index.ts +205 -0
  92. package/src/utilities/sharing/infrastructure/services/SharingService.ts +165 -0
  93. package/src/utilities/sharing/presentation/hooks/useSharing.ts +154 -0
@@ -0,0 +1,216 @@
1
+ /**
2
+ * useAnimation Hook Integration Tests
3
+ *
4
+ * Integration tests for combined animation functionality.
5
+ */
6
+
7
+ import { renderHook, act } from '@testing-library/react';
8
+ import { useAnimation } from '../useAnimation';
9
+
10
+ // Mock react-native-reanimated
11
+ jest.mock('react-native-reanimated', () => ({
12
+ useSharedValue: jest.fn((initialValue) => ({
13
+ value: initialValue,
14
+ })),
15
+ useAnimatedStyle: jest.fn((styleFactory) => styleFactory()),
16
+ withTiming: jest.fn((toValue, config) => ({ toValue, config })),
17
+ withSpring: jest.fn((toValue, config) => ({ toValue, config })),
18
+ withSequence: jest.fn((...animations) => animations),
19
+ withRepeat: jest.fn((animation, count, reverse) => ({ animation, count, reverse })),
20
+ Easing: {
21
+ ease: jest.fn(),
22
+ out: jest.fn((easing) => easing),
23
+ bezier: jest.fn(() => jest.fn()),
24
+ linear: jest.fn(),
25
+ },
26
+ }));
27
+
28
+ // Mock all sub-hooks
29
+ jest.mock('../useTimingAnimation', () => ({
30
+ useTimingAnimation: jest.fn(() => ({
31
+ fadeIn: jest.fn(),
32
+ fadeOut: jest.fn(),
33
+ slideInUp: jest.fn(),
34
+ slideInDown: jest.fn(),
35
+ slideInLeft: jest.fn(),
36
+ slideInRight: jest.fn(),
37
+ opacity: { value: 1 },
38
+ translateY: { value: 0 },
39
+ translateX: { value: 0 },
40
+ })),
41
+ }));
42
+
43
+ jest.mock('../useSpringAnimation', () => ({
44
+ useSpringAnimation: jest.fn(() => ({
45
+ scaleIn: jest.fn(),
46
+ scaleOut: jest.fn(),
47
+ bounce: jest.fn(),
48
+ scale: { value: 1 },
49
+ })),
50
+ }));
51
+
52
+ jest.mock('../useTransformAnimation', () => ({
53
+ useTransformAnimation: jest.fn(() => ({
54
+ shake: jest.fn(),
55
+ pulse: jest.fn(),
56
+ spin: jest.fn(),
57
+ translateX: { value: 0 },
58
+ scale: { value: 1 },
59
+ rotate: { value: 0 },
60
+ })),
61
+ }));
62
+
63
+ describe('useAnimation Integration', () => {
64
+ beforeEach(() => {
65
+ jest.clearAllMocks();
66
+ });
67
+
68
+ it('should integrate all animation hooks', () => {
69
+ const { useTimingAnimation } = require('../useTimingAnimation');
70
+ const { useSpringAnimation } = require('../useSpringAnimation');
71
+ const { useTransformAnimation } = require('../useTransformAnimation');
72
+
73
+ renderHook(() => useAnimation());
74
+
75
+ expect(useTimingAnimation).toHaveBeenCalled();
76
+ expect(useSpringAnimation).toHaveBeenCalled();
77
+ expect(useTransformAnimation).toHaveBeenCalled();
78
+ });
79
+
80
+ it('should provide all timing animations', () => {
81
+ const { result } = renderHook(() => useAnimation());
82
+
83
+ expect(typeof result.current.fadeIn).toBe('function');
84
+ expect(typeof result.current.fadeOut).toBe('function');
85
+ expect(typeof result.current.slideInUp).toBe('function');
86
+ expect(typeof result.current.slideInDown).toBe('function');
87
+ expect(typeof result.current.slideInLeft).toBe('function');
88
+ expect(typeof result.current.slideInRight).toBe('function');
89
+ });
90
+
91
+ it('should provide all spring animations', () => {
92
+ const { result } = renderHook(() => useAnimation());
93
+
94
+ expect(typeof result.current.scaleIn).toBe('function');
95
+ expect(typeof result.current.scaleOut).toBe('function');
96
+ expect(typeof result.current.bounce).toBe('function');
97
+ });
98
+
99
+ it('should provide all transform animations', () => {
100
+ const { result } = renderHook(() => useAnimation());
101
+
102
+ expect(typeof result.current.shake).toBe('function');
103
+ expect(typeof result.current.pulse).toBe('function');
104
+ expect(typeof result.current.spin).toBe('function');
105
+ });
106
+
107
+ it('should provide all shared values', () => {
108
+ const { result } = renderHook(() => useAnimation());
109
+
110
+ expect(result.current.opacity).toBeDefined();
111
+ expect(result.current.translateY).toBeDefined();
112
+ expect(result.current.translateX).toBeDefined();
113
+ expect(result.current.scale).toBeDefined();
114
+ expect(result.current.rotate).toBeDefined();
115
+ });
116
+
117
+ it('should provide animated style', () => {
118
+ const { result } = renderHook(() => useAnimation());
119
+ const { useAnimatedStyle } = require('react-native-reanimated');
120
+
121
+ expect(typeof result.current.animatedStyle).toBe('function');
122
+ expect(useAnimatedStyle).toHaveBeenCalled();
123
+ });
124
+
125
+ describe('animation combinations', () => {
126
+ it('should allow combining fade and scale animations', () => {
127
+ const { result } = renderHook(() => useAnimation());
128
+ const mockTiming = require('../useTimingAnimation').useTimingAnimation();
129
+ const mockSpring = require('../useSpringAnimation').useSpringAnimation();
130
+
131
+ act(() => {
132
+ result.current.fadeIn();
133
+ result.current.scaleIn();
134
+ });
135
+
136
+ expect(mockTiming.fadeIn).toHaveBeenCalled();
137
+ expect(mockSpring.scaleIn).toHaveBeenCalled();
138
+ });
139
+
140
+ it('should allow combining slide and rotate animations', () => {
141
+ const { result } = renderHook(() => useAnimation());
142
+ const mockTiming = require('../useTimingAnimation').useTimingAnimation();
143
+ const mockTransform = require('../useTransformAnimation').useTransformAnimation();
144
+
145
+ act(() => {
146
+ result.current.slideInUp();
147
+ result.current.spin();
148
+ });
149
+
150
+ expect(mockTiming.slideInUp).toHaveBeenCalled();
151
+ expect(mockTransform.spin).toHaveBeenCalled();
152
+ });
153
+
154
+ it('should allow complex animation sequences', () => {
155
+ const { result } = renderHook(() => useAnimation());
156
+ const mockTiming = require('../useTimingAnimation').useTimingAnimation();
157
+ const mockSpring = require('../useSpringAnimation').useSpringAnimation();
158
+ const mockTransform = require('../useTransformAnimation').useTransformAnimation();
159
+
160
+ act(() => {
161
+ result.current.fadeIn();
162
+ result.current.slideInUp();
163
+ result.current.scaleIn();
164
+ result.current.bounce();
165
+ result.current.shake();
166
+ result.current.pulse();
167
+ result.current.spin();
168
+ });
169
+
170
+ expect(mockTiming.fadeIn).toHaveBeenCalled();
171
+ expect(mockTiming.slideInUp).toHaveBeenCalled();
172
+ expect(mockSpring.scaleIn).toHaveBeenCalled();
173
+ expect(mockSpring.bounce).toHaveBeenCalled();
174
+ expect(mockTransform.shake).toHaveBeenCalled();
175
+ expect(mockTransform.pulse).toHaveBeenCalled();
176
+ expect(mockTransform.spin).toHaveBeenCalled();
177
+ });
178
+ });
179
+
180
+ describe('animated style composition', () => {
181
+ it('should compose styles from all animation types', () => {
182
+ const { useAnimatedStyle } = require('react-native-reanimated');
183
+ const mockStyle = {
184
+ opacity: 1,
185
+ transform: [
186
+ { translateY: 0 },
187
+ { translateX: 0 },
188
+ { scale: 1 },
189
+ { rotate: '0deg' },
190
+ ],
191
+ };
192
+
193
+ useAnimatedStyle.mockReturnValue(mockStyle);
194
+
195
+ const { result } = renderHook(() => useAnimation());
196
+
197
+ expect(result.current.animatedStyle).toBe(mockStyle);
198
+ });
199
+
200
+ it('should prioritize transform values over timing/spring values', () => {
201
+ const { useAnimatedStyle } = require('react-native-reanimated');
202
+ const mockTiming = require('../useTimingAnimation').useTimingAnimation();
203
+ const mockTransform = require('../useTransformAnimation').useTransformAnimation();
204
+
205
+ // Set transform values to non-default
206
+ mockTransform.translateX.value = 50;
207
+ mockTransform.scale.value = 1.5;
208
+
209
+ renderHook(() => useAnimation());
210
+
211
+ expect(useAnimatedStyle).toHaveBeenCalledWith(
212
+ expect.any(Function)
213
+ );
214
+ });
215
+ });
216
+ });
@@ -0,0 +1,242 @@
1
+ /**
2
+ * useFireworks Hook Tests
3
+ *
4
+ * Unit tests for fireworks particle system.
5
+ */
6
+
7
+ import { renderHook, act } from '@testing-library/react';
8
+ import { useFireworks } from '../useFireworks';
9
+ import { FIREWORKS_CONSTANTS } from '../../../domain/entities/Animation';
10
+
11
+ // Mock requestAnimationFrame
12
+ global.requestAnimationFrame = jest.fn((cb) => {
13
+ setTimeout(cb, 16);
14
+ return 1;
15
+ });
16
+
17
+ global.cancelAnimationFrame = jest.fn();
18
+
19
+ describe('useFireworks', () => {
20
+ const mockConfig = {
21
+ colors: ['#FF0000', '#00FF00', '#0000FF'],
22
+ particleCount: 10,
23
+ duration: 1000,
24
+ particleSize: 4,
25
+ spread: 50,
26
+ };
27
+
28
+ beforeEach(() => {
29
+ jest.clearAllMocks();
30
+ jest.useFakeTimers();
31
+ });
32
+
33
+ afterEach(() => {
34
+ jest.useRealTimers();
35
+ });
36
+
37
+ it('should initialize with empty particles and inactive state', () => {
38
+ const { result } = renderHook(() => useFireworks(mockConfig));
39
+
40
+ expect(result.current.particles).toEqual([]);
41
+ expect(result.current.isActive).toBe(false);
42
+ expect(typeof result.current.trigger).toBe('function');
43
+ });
44
+
45
+ it('should require colors array', () => {
46
+ const { result } = renderHook(() => useFireworks({ colors: [] }));
47
+
48
+ expect(result.current.particles).toEqual([]);
49
+ expect(result.current.trigger).toEqual(expect.any(Function));
50
+ expect(result.current.isActive).toBe(false);
51
+ });
52
+
53
+ describe('trigger', () => {
54
+ it('should create particles with correct count', () => {
55
+ const { result } = renderHook(() => useFireworks(mockConfig));
56
+
57
+ act(() => {
58
+ result.current.trigger(100, 100);
59
+ });
60
+
61
+ expect(result.current.particles).toHaveLength(mockConfig.particleCount);
62
+ });
63
+
64
+ it('should create particles with correct properties', () => {
65
+ const { result } = renderHook(() => useFireworks(mockConfig));
66
+
67
+ act(() => {
68
+ result.current.trigger(100, 100);
69
+ });
70
+
71
+ const particles = result.current.particles;
72
+ particles.forEach(particle => {
73
+ expect(particle).toHaveProperty('x');
74
+ expect(particle).toHaveProperty('y');
75
+ expect(particle).toHaveProperty('color');
76
+ expect(particle).toHaveProperty('size');
77
+ expect(particle).toHaveProperty('velocityX');
78
+ expect(particle).toHaveProperty('velocityY');
79
+ expect(particle).toHaveProperty('life');
80
+ expect(particle).toHaveProperty('decay');
81
+ });
82
+ });
83
+
84
+ it('should use provided colors', () => {
85
+ const { result } = renderHook(() => useFireworks(mockConfig));
86
+
87
+ act(() => {
88
+ result.current.trigger(100, 100);
89
+ });
90
+
91
+ const particles = result.current.particles;
92
+ const particleColors = particles.map(p => p.color);
93
+
94
+ particleColors.forEach(color => {
95
+ expect(mockConfig.colors).toContain(color);
96
+ });
97
+ });
98
+
99
+ it('should set particles at trigger position', () => {
100
+ const { result } = renderHook(() => useFireworks(mockConfig));
101
+ const triggerX = 150;
102
+ const triggerY = 200;
103
+
104
+ act(() => {
105
+ result.current.trigger(triggerX, triggerY);
106
+ });
107
+
108
+ const particles = result.current.particles;
109
+ particles.forEach(particle => {
110
+ expect(particle.x).toBe(triggerX);
111
+ expect(particle.y).toBe(triggerY);
112
+ });
113
+ });
114
+
115
+ it('should use default position when none provided', () => {
116
+ const { result } = renderHook(() => useFireworks(mockConfig));
117
+
118
+ act(() => {
119
+ result.current.trigger();
120
+ });
121
+
122
+ const particles = result.current.particles;
123
+ particles.forEach(particle => {
124
+ expect(particle.x).toBe(0);
125
+ expect(particle.y).toBe(0);
126
+ });
127
+ });
128
+
129
+ it('should set active state to true', () => {
130
+ const { result } = renderHook(() => useFireworks(mockConfig));
131
+
132
+ act(() => {
133
+ result.current.trigger(100, 100);
134
+ });
135
+
136
+ expect(result.current.isActive).toBe(true);
137
+ });
138
+
139
+ it('should start animation loop', () => {
140
+ const { result } = renderHook(() => useFireworks(mockConfig));
141
+
142
+ act(() => {
143
+ result.current.trigger(100, 100);
144
+ });
145
+
146
+ expect(global.requestAnimationFrame).toHaveBeenCalled();
147
+ });
148
+
149
+ it('should auto-stop after duration', () => {
150
+ const { result } = renderHook(() => useFireworks(mockConfig));
151
+
152
+ act(() => {
153
+ result.current.trigger(100, 100);
154
+ });
155
+
156
+ expect(result.current.isActive).toBe(true);
157
+
158
+ act(() => {
159
+ jest.advanceTimersByTime(mockConfig.duration);
160
+ });
161
+
162
+ expect(result.current.isActive).toBe(false);
163
+ });
164
+ });
165
+
166
+ describe('particle physics', () => {
167
+ it('should update particle positions over time', () => {
168
+ const { result } = renderHook(() => useFireworks(mockConfig));
169
+
170
+ act(() => {
171
+ result.current.trigger(100, 100);
172
+ });
173
+
174
+ const initialParticles = [...result.current.particles];
175
+
176
+ act(() => {
177
+ jest.advanceTimersByTime(16); // One frame
178
+ });
179
+
180
+ const updatedParticles = result.current.particles;
181
+
182
+ updatedParticles.forEach((particle, index) => {
183
+ const initial = initialParticles[index];
184
+ expect(particle.x).not.toBe(initial.x);
185
+ expect(particle.y).not.toBe(initial.y);
186
+ expect(particle.life).toBeLessThan(initial.life);
187
+ });
188
+ });
189
+
190
+ it('should apply gravity to particles', () => {
191
+ const { result } = renderHook(() => useFireworks(mockConfig));
192
+
193
+ act(() => {
194
+ result.current.trigger(100, 100);
195
+ });
196
+
197
+ const initialParticles = [...result.current.particles];
198
+
199
+ act(() => {
200
+ jest.advanceTimersByTime(16);
201
+ });
202
+
203
+ const updatedParticles = result.current.particles;
204
+
205
+ updatedParticles.forEach((particle, index) => {
206
+ const initial = initialParticles[index];
207
+ const expectedY = initial.y + initial.velocityY * 0.1 + FIREWORKS_CONSTANTS.GRAVITY;
208
+ expect(particle.y).toBeCloseTo(expectedY, 1);
209
+ });
210
+ });
211
+
212
+ it('should remove dead particles', () => {
213
+ const { result } = renderHook(() => useFireworks(mockConfig));
214
+
215
+ act(() => {
216
+ result.current.trigger(100, 100);
217
+ });
218
+
219
+ // Simulate particles dying
220
+ act(() => {
221
+ jest.advanceTimersByTime(2000); // Long enough for particles to die
222
+ });
223
+
224
+ expect(result.current.particles).toHaveLength(0);
225
+ expect(result.current.isActive).toBe(false);
226
+ });
227
+ });
228
+
229
+ describe('cleanup', () => {
230
+ it('should cancel animation frame on unmount', () => {
231
+ const { result, unmount } = renderHook(() => useFireworks(mockConfig));
232
+
233
+ act(() => {
234
+ result.current.trigger(100, 100);
235
+ });
236
+
237
+ unmount();
238
+
239
+ expect(global.cancelAnimationFrame).toHaveBeenCalled();
240
+ });
241
+ });
242
+ });
@@ -0,0 +1,111 @@
1
+ /**
2
+ * useGesture Hook Tests
3
+ *
4
+ * Unit tests for gesture handling functionality.
5
+ */
6
+
7
+ import { renderHook } from '@testing-library/react';
8
+ import { useGesture } from '../useGesture';
9
+
10
+ // Mock react-native-gesture-handler
11
+ jest.mock('react-native-gesture-handler', () => ({
12
+ GestureDetector: jest.fn(({ children }) => children),
13
+ Gesture: {
14
+ Tap: jest.fn(() => ({
15
+ numberOfTaps: jest.fn().mockReturnThis(),
16
+ maxDuration: jest.fn().mockReturnThis(),
17
+ onStart: jest.fn().mockReturnThis(),
18
+ })),
19
+ Pan: jest.fn(() => ({
20
+ onStart: jest.fn().mockReturnThis(),
21
+ onUpdate: jest.fn().mockReturnThis(),
22
+ onEnd: jest.fn().mockReturnThis(),
23
+ })),
24
+ Pinch: jest.fn(() => ({
25
+ onStart: jest.fn().mockReturnThis(),
26
+ onUpdate: jest.fn().mockReturnThis(),
27
+ onEnd: jest.fn().mockReturnThis(),
28
+ })),
29
+ LongPress: jest.fn(() => ({
30
+ minDuration: jest.fn().mockReturnThis(),
31
+ onStart: jest.fn().mockReturnThis(),
32
+ })),
33
+ },
34
+ }));
35
+
36
+ // Mock useGestureState
37
+ jest.mock('../useGestureState', () => ({
38
+ useGestureState: jest.fn(() => ({
39
+ translateX: { value: 0 },
40
+ translateY: { value: 0 },
41
+ scale: { value: 1 },
42
+ savedTranslateX: { value: 0 },
43
+ savedTranslateY: { value: 0 },
44
+ savedScale: { value: 1 },
45
+ reset: jest.fn(),
46
+ animatedStyle: {},
47
+ })),
48
+ }));
49
+
50
+ // Mock useGestureCreators
51
+ jest.mock('../useGestureCreators', () => ({
52
+ useGestureCreators: jest.fn(() => ({
53
+ createTapGesture: jest.fn(),
54
+ createPanGesture: jest.fn(),
55
+ createPinchGesture: jest.fn(),
56
+ createLongPressGesture: jest.fn(),
57
+ })),
58
+ }));
59
+
60
+ describe('useGesture', () => {
61
+ beforeEach(() => {
62
+ jest.clearAllMocks();
63
+ });
64
+
65
+ it('should provide gesture creators', () => {
66
+ const { result } = renderHook(() => useGesture());
67
+
68
+ expect(typeof result.current.createTapGesture).toBe('function');
69
+ expect(typeof result.current.createPanGesture).toBe('function');
70
+ expect(typeof result.current.createPinchGesture).toBe('function');
71
+ expect(typeof result.current.createLongPressGesture).toBe('function');
72
+ });
73
+
74
+ it('should provide shared values', () => {
75
+ const { result } = renderHook(() => useGesture());
76
+
77
+ expect(result.current.translateX).toBeDefined();
78
+ expect(result.current.translateY).toBeDefined();
79
+ expect(result.current.scale).toBeDefined();
80
+ });
81
+
82
+ it('should provide utilities', () => {
83
+ const { result } = renderHook(() => useGesture());
84
+
85
+ expect(typeof result.current.reset).toBe('function');
86
+ expect(result.current.animatedStyle).toBeDefined();
87
+ });
88
+
89
+ it('should export GestureDetector', () => {
90
+ const { result } = renderHook(() => useGesture());
91
+ const { GestureDetector } = require('react-native-gesture-handler');
92
+
93
+ expect(result.current.GestureDetector).toBe(GestureDetector);
94
+ });
95
+
96
+ describe('gesture creators integration', () => {
97
+ it('should integrate with useGestureCreators', () => {
98
+ const { useGestureCreators } = require('../useGestureCreators');
99
+ renderHook(() => useGesture());
100
+
101
+ expect(useGestureCreators).toHaveBeenCalled();
102
+ });
103
+
104
+ it('should integrate with useGestureState', () => {
105
+ const { useGestureState } = require('../useGestureState');
106
+ renderHook(() => useGesture());
107
+
108
+ expect(useGestureState).toHaveBeenCalled();
109
+ });
110
+ });
111
+ });
@@ -0,0 +1,131 @@
1
+ /**
2
+ * useSpringAnimation Hook Tests
3
+ *
4
+ * Unit tests for spring-based animations (scale, bounce).
5
+ */
6
+
7
+ import { renderHook, act } from '@testing-library/react';
8
+ import { useSpringAnimation } from '../useSpringAnimation';
9
+ import { AnimationPreset, ANIMATION_CONSTANTS } from '../../../domain/entities/Animation';
10
+
11
+ // Mock react-native-reanimated
12
+ jest.mock('react-native-reanimated', () => ({
13
+ useSharedValue: jest.fn((initialValue) => ({
14
+ value: initialValue,
15
+ })),
16
+ useAnimatedStyle: jest.fn((styleFactory) => styleFactory()),
17
+ withSpring: jest.fn((toValue, config) => ({ toValue, config })),
18
+ withSequence: jest.fn((...animations) => animations),
19
+ }));
20
+
21
+ // Mock SpringAnimationConfigService
22
+ jest.mock('../../../infrastructure/services/SpringAnimationConfigService', () => ({
23
+ SpringAnimationConfigService: {
24
+ getSpringConfig: jest.fn((preset) => ({
25
+ damping: ANIMATION_CONSTANTS.SPRING.DAMPING,
26
+ stiffness: ANIMATION_CONSTANTS.SPRING.STIFFNESS,
27
+ })),
28
+ },
29
+ }));
30
+
31
+ describe('useSpringAnimation', () => {
32
+ beforeEach(() => {
33
+ jest.clearAllMocks();
34
+ });
35
+
36
+ it('should initialize with default values', () => {
37
+ const { result } = renderHook(() => useSpringAnimation());
38
+
39
+ expect(result.current.scale.value).toBe(1);
40
+ });
41
+
42
+ it('should provide animation functions', () => {
43
+ const { result } = renderHook(() => useSpringAnimation());
44
+
45
+ expect(typeof result.current.scaleIn).toBe('function');
46
+ expect(typeof result.current.scaleOut).toBe('function');
47
+ expect(typeof result.current.bounce).toBe('function');
48
+ });
49
+
50
+ describe('scaleIn', () => {
51
+ it('should animate scale from 0 to 1 with default config', () => {
52
+ const { result } = renderHook(() => useSpringAnimation());
53
+ const { withSpring } = require('react-native-reanimated');
54
+
55
+ act(() => {
56
+ result.current.scaleIn();
57
+ });
58
+
59
+ expect(withSpring).toHaveBeenCalledWith(1, {
60
+ damping: ANIMATION_CONSTANTS.SPRING.DAMPING,
61
+ stiffness: ANIMATION_CONSTANTS.SPRING.STIFFNESS,
62
+ });
63
+ });
64
+
65
+ it('should use custom config when provided', () => {
66
+ const { result } = renderHook(() => useSpringAnimation());
67
+ const { withSpring } = require('react-native-reanimated');
68
+ const customConfig = { damping: 20, stiffness: 200 };
69
+
70
+ act(() => {
71
+ result.current.scaleIn(customConfig);
72
+ });
73
+
74
+ expect(withSpring).toHaveBeenCalledWith(1, customConfig);
75
+ });
76
+ });
77
+
78
+ describe('scaleOut', () => {
79
+ it('should animate scale to 0 with default config', () => {
80
+ const { result } = renderHook(() => useSpringAnimation());
81
+ const { withSpring } = require('react-native-reanimated');
82
+
83
+ act(() => {
84
+ result.current.scaleOut();
85
+ });
86
+
87
+ expect(withSpring).toHaveBeenCalledWith(0, {
88
+ damping: ANIMATION_CONSTANTS.SPRING.DAMPING,
89
+ stiffness: ANIMATION_CONSTANTS.SPRING.STIFFNESS,
90
+ });
91
+ });
92
+ });
93
+
94
+ describe('bounce', () => {
95
+ it('should create bounce animation sequence', () => {
96
+ const { result } = renderHook(() => useSpringAnimation());
97
+ const { withSpring, withSequence } = require('react-native-reanimated');
98
+ const mockConfig = { damping: 5, stiffness: 120 };
99
+
100
+ act(() => {
101
+ result.current.bounce(mockConfig);
102
+ });
103
+
104
+ expect(withSequence).toHaveBeenCalledWith(
105
+ withSpring(0.8, mockConfig),
106
+ withSpring(1.2, mockConfig),
107
+ withSpring(1, mockConfig)
108
+ );
109
+ });
110
+
111
+ it('should use default config when none provided', () => {
112
+ const { result } = renderHook(() => useSpringAnimation());
113
+ const { withSpring, withSequence } = require('react-native-reanimated');
114
+
115
+ act(() => {
116
+ result.current.bounce();
117
+ });
118
+
119
+ expect(withSequence).toHaveBeenCalled();
120
+ });
121
+ });
122
+
123
+ describe('shared values', () => {
124
+ it('should expose scale shared value for custom animations', () => {
125
+ const { result } = renderHook(() => useSpringAnimation());
126
+
127
+ expect(result.current.scale).toBeDefined();
128
+ expect(result.current.scale.value).toBe(1);
129
+ });
130
+ });
131
+ });