@umituz/react-native-settings 4.17.14 → 4.17.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 (103) hide show
  1. package/package.json +16 -15
  2. package/src/domains/about/__tests__/integration.test.tsx +328 -0
  3. package/src/domains/about/__tests__/types.d.ts +5 -0
  4. package/src/domains/about/domain/entities/AppInfo.ts +74 -0
  5. package/src/domains/about/domain/entities/__tests__/AppInfo.test.ts +93 -0
  6. package/src/domains/about/domain/repositories/IAboutRepository.ts +22 -0
  7. package/src/domains/about/index.ts +10 -0
  8. package/src/domains/about/infrastructure/repositories/AboutRepository.ts +68 -0
  9. package/src/domains/about/infrastructure/repositories/__tests__/AboutRepository.test.ts +153 -0
  10. package/src/domains/about/presentation/components/AboutContent.tsx +104 -0
  11. package/src/domains/about/presentation/components/AboutHeader.tsx +79 -0
  12. package/src/domains/about/presentation/components/AboutSection.tsx +134 -0
  13. package/src/domains/about/presentation/components/AboutSettingItem.tsx +208 -0
  14. package/src/domains/about/presentation/components/__tests__/AboutContent.simple.test.tsx +178 -0
  15. package/src/domains/about/presentation/components/__tests__/AboutContent.test.tsx +293 -0
  16. package/src/domains/about/presentation/components/__tests__/AboutHeader.test.tsx +201 -0
  17. package/src/domains/about/presentation/components/__tests__/AboutSettingItem.test.tsx +71 -0
  18. package/src/domains/about/presentation/hooks/__tests__/useAboutInfo.simple.test.tsx +229 -0
  19. package/src/domains/about/presentation/hooks/__tests__/useAboutInfo.test.tsx +240 -0
  20. package/src/domains/about/presentation/hooks/useAboutInfo.ts +262 -0
  21. package/src/domains/about/presentation/screens/AboutScreen.tsx +195 -0
  22. package/src/domains/about/presentation/screens/__tests__/AboutScreen.simple.test.tsx +199 -0
  23. package/src/domains/about/presentation/screens/__tests__/AboutScreen.test.tsx +366 -0
  24. package/src/domains/about/types/global.d.ts +15 -0
  25. package/src/domains/about/utils/__tests__/index.test.ts +408 -0
  26. package/src/domains/about/utils/index.ts +160 -0
  27. package/src/domains/appearance/__tests__/components/AppearanceScreen.test.tsx +195 -0
  28. package/src/domains/appearance/__tests__/hooks/index.test.tsx +232 -0
  29. package/src/domains/appearance/__tests__/integration/index.test.tsx +207 -0
  30. package/src/domains/appearance/__tests__/services/appearanceService.test.ts +299 -0
  31. package/src/domains/appearance/__tests__/setup.ts +96 -0
  32. package/src/domains/appearance/__tests__/stores/appearanceStore.test.tsx +175 -0
  33. package/src/domains/appearance/data/colorPalettes.ts +94 -0
  34. package/src/domains/appearance/hooks/index.ts +6 -0
  35. package/src/domains/appearance/hooks/useAppearance.ts +61 -0
  36. package/src/domains/appearance/hooks/useAppearanceActions.ts +144 -0
  37. package/src/domains/appearance/index.ts +7 -0
  38. package/src/domains/appearance/infrastructure/services/appearanceService.ts +301 -0
  39. package/src/domains/appearance/infrastructure/services/systemThemeDetection.ts +79 -0
  40. package/src/domains/appearance/infrastructure/services/validation.ts +91 -0
  41. package/src/domains/appearance/infrastructure/storage/appearanceStorage.ts +120 -0
  42. package/src/domains/appearance/infrastructure/stores/appearanceStore.ts +132 -0
  43. package/src/domains/appearance/presentation/components/AppearanceHeader.tsx +67 -0
  44. package/src/domains/appearance/presentation/components/AppearancePreview.tsx +141 -0
  45. package/src/domains/appearance/presentation/components/AppearanceSection.tsx +139 -0
  46. package/src/domains/appearance/presentation/components/ColorPicker.tsx +113 -0
  47. package/src/domains/appearance/presentation/components/CustomColorsSection.tsx +186 -0
  48. package/src/domains/appearance/presentation/components/ThemeModeSection.tsx +110 -0
  49. package/src/domains/appearance/presentation/components/ThemeOption.tsx +138 -0
  50. package/src/domains/appearance/presentation/components/index.ts +6 -0
  51. package/src/domains/appearance/presentation/screens/AppearanceScreen.tsx +226 -0
  52. package/src/domains/appearance/presentation/screens/index.ts +2 -0
  53. package/src/domains/appearance/types/index.ts +54 -0
  54. package/src/domains/faqs/domain/entities/FAQEntity.ts +16 -0
  55. package/src/domains/faqs/domain/services/FAQSearchService.ts +36 -0
  56. package/src/domains/faqs/domain/services/index.ts +1 -0
  57. package/src/domains/faqs/index.ts +7 -0
  58. package/src/domains/faqs/presentation/components/FAQCategory.tsx +71 -0
  59. package/src/domains/faqs/presentation/components/FAQEmptyState.tsx +75 -0
  60. package/src/domains/faqs/presentation/components/FAQItem.tsx +103 -0
  61. package/src/domains/faqs/presentation/components/FAQSearchBar.tsx +70 -0
  62. package/src/domains/faqs/presentation/components/FAQSection.tsx +50 -0
  63. package/src/domains/faqs/presentation/components/index.ts +18 -0
  64. package/src/domains/faqs/presentation/hooks/index.ts +6 -0
  65. package/src/domains/faqs/presentation/hooks/useFAQExpansion.ts +51 -0
  66. package/src/domains/faqs/presentation/hooks/useFAQSearch.ts +33 -0
  67. package/src/domains/faqs/presentation/screens/FAQScreen.tsx +129 -0
  68. package/src/domains/faqs/presentation/screens/index.ts +2 -0
  69. package/src/domains/feedback/domain/entities/FeedbackEntity.ts +92 -0
  70. package/src/domains/feedback/domain/repositories/IFeedbackRepository.ts +28 -0
  71. package/src/domains/feedback/index.ts +6 -0
  72. package/src/domains/feedback/presentation/components/FeedbackForm.tsx +189 -0
  73. package/src/domains/feedback/presentation/components/FeedbackModal.tsx +111 -0
  74. package/src/domains/feedback/presentation/components/SupportSection.tsx +160 -0
  75. package/src/domains/feedback/presentation/hooks/useDeleteFeedback.ts +25 -0
  76. package/src/domains/feedback/presentation/hooks/useFeedbackForm.ts +59 -0
  77. package/src/domains/feedback/presentation/hooks/useSubmitFeedback.ts +55 -0
  78. package/src/domains/feedback/presentation/hooks/useUserFeedback.ts +29 -0
  79. package/src/domains/legal/__tests__/ContentValidationService.test.ts +195 -0
  80. package/src/domains/legal/__tests__/StyleCacheService.test.ts +110 -0
  81. package/src/domains/legal/__tests__/UrlHandlerService.test.ts +71 -0
  82. package/src/domains/legal/__tests__/setup.ts +82 -0
  83. package/src/domains/legal/domain/entities/LegalConfig.ts +26 -0
  84. package/src/domains/legal/domain/services/ContentValidationService.ts +89 -0
  85. package/src/domains/legal/domain/services/StyleCacheService.ts +97 -0
  86. package/src/domains/legal/domain/services/UrlHandlerService.ts +128 -0
  87. package/src/domains/legal/index.ts +8 -0
  88. package/src/domains/legal/presentation/components/LegalItem.tsx +177 -0
  89. package/src/domains/legal/presentation/components/LegalLinks.tsx +154 -0
  90. package/src/domains/legal/presentation/components/LegalSection.tsx +134 -0
  91. package/src/domains/legal/presentation/screens/LegalScreen.tsx +237 -0
  92. package/src/domains/legal/presentation/screens/PrivacyPolicyScreen.tsx +214 -0
  93. package/src/domains/legal/presentation/screens/TermsOfServiceScreen.tsx +214 -0
  94. package/src/index.ts +19 -0
  95. package/src/presentation/components/DevSettingsSection.tsx +2 -2
  96. package/src/presentation/components/SettingItem.tsx +2 -2
  97. package/src/presentation/components/SettingsErrorBoundary.tsx +2 -2
  98. package/src/presentation/components/SettingsFooter.tsx +2 -2
  99. package/src/presentation/components/SettingsSection.tsx +2 -2
  100. package/src/presentation/navigation/SettingsStackNavigator.tsx +2 -2
  101. package/src/presentation/screens/SettingsScreen.tsx +2 -2
  102. package/src/presentation/screens/components/SettingsContent.tsx +2 -2
  103. package/src/presentation/screens/components/SettingsHeader.tsx +2 -2
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Component Tests
3
+ */
4
+
5
+ import React from 'react';
6
+ import { render, fireEvent, act } from '@testing-library/react-native';
7
+ import { AppearanceScreen } from '../../presentation/screens/AppearanceScreen';
8
+ import { ThemeOption } from '../../presentation/components/ThemeOption';
9
+ import { ColorPicker } from '../../presentation/components/ColorPicker';
10
+ import type { ThemeMode, CustomThemeColors } from '../../types';
11
+
12
+ // Mock hooks
13
+ jest.mock('../../hooks', () => ({
14
+ useAppearance: jest.fn(() => ({
15
+ themeMode: 'dark',
16
+ customColors: {},
17
+ isInitialized: true,
18
+ setThemeMode: jest.fn(),
19
+ toggleTheme: jest.fn(),
20
+ setCustomColors: jest.fn(),
21
+ resetCustomColors: jest.fn(),
22
+ reset: jest.fn(),
23
+ })),
24
+ useAppearanceActions: jest.fn(() => ({
25
+ localCustomColors: {},
26
+ handleThemeSelect: jest.fn(),
27
+ handleColorChange: jest.fn(),
28
+ handleResetColors: jest.fn(),
29
+ })),
30
+ }));
31
+
32
+ const mockUseAppearance = require('../../hooks').useAppearance;
33
+ const mockUseAppearanceActions = require('../../hooks').useAppearanceActions;
34
+
35
+ describe('AppearanceScreen', () => {
36
+ beforeEach(() => {
37
+ jest.clearAllMocks();
38
+ });
39
+
40
+ afterEach(() => {
41
+ jest.restoreAllMocks();
42
+ });
43
+
44
+ it('should render correctly with default props', () => {
45
+ const { getByTestId } = render(<AppearanceScreen />);
46
+
47
+ expect(getByTestId('appearance-screen')).toBeTruthy();
48
+ });
49
+
50
+ it('should render custom header when provided', () => {
51
+ const CustomHeader = () => <div>Custom Header</div>;
52
+ const { getByText } = render(
53
+ <AppearanceScreen headerComponent={<CustomHeader />} />
54
+ );
55
+
56
+ expect(getByText('Custom Header')).toBeTruthy();
57
+ });
58
+
59
+ it('should hide theme section when showThemeSection is false', () => {
60
+ const { queryByTestId } = render(
61
+ <AppearanceScreen showThemeSection={false} />
62
+ );
63
+
64
+ expect(queryByTestId('theme-mode-section')).toBeFalsy();
65
+ });
66
+
67
+ it('should hide colors section when showColorsSection is false', () => {
68
+ const { queryByTestId } = render(
69
+ <AppearanceScreen showColorsSection={false} />
70
+ );
71
+
72
+ expect(queryByTestId('custom-colors-section')).toBeFalsy();
73
+ });
74
+
75
+ it('should hide preview section when showPreviewSection is false', () => {
76
+ const { queryByTestId } = render(
77
+ <AppearanceScreen showPreviewSection={false} />
78
+ );
79
+
80
+ expect(queryByTestId('appearance-preview')).toBeFalsy();
81
+ });
82
+
83
+ it('should apply custom container styles', () => {
84
+ const customStyle = { backgroundColor: 'red' };
85
+ const { getByTestId } = render(
86
+ <AppearanceScreen containerStyle={customStyle} />
87
+ );
88
+
89
+ const container = getByTestId('appearance-screen');
90
+ expect(container.props.style).toEqual(
91
+ expect.objectContaining(customStyle)
92
+ );
93
+ });
94
+
95
+ it('should apply custom content container styles', () => {
96
+ const customStyle = { padding: 20 };
97
+ const { getByTestId } = render(
98
+ <AppearanceScreen contentContainerStyle={customStyle} />
99
+ );
100
+
101
+ const container = getByTestId('appearance-screen');
102
+ const scrollView = container.findByType('ScrollView');
103
+ expect(scrollView.props.contentContainerStyle).toEqual(
104
+ expect.objectContaining(customStyle)
105
+ );
106
+ });
107
+
108
+ it('should handle theme selection', async () => {
109
+ const mockHandleThemeSelect = jest.fn();
110
+ mockUseAppearanceActions.mockReturnValue({
111
+ localCustomColors: {},
112
+ handleThemeSelect: mockHandleThemeSelect,
113
+ handleColorChange: jest.fn(),
114
+ handleResetColors: jest.fn(),
115
+ });
116
+
117
+ const { getByTestId } = render(<AppearanceScreen />);
118
+
119
+ // Find and click theme option
120
+ const themeOption = getByTestId('theme-option-light');
121
+ await act(async () => {
122
+ fireEvent.press(themeOption);
123
+ });
124
+
125
+ expect(mockHandleThemeSelect).toHaveBeenCalledWith('light');
126
+ });
127
+
128
+ it('should handle color change', async () => {
129
+ const mockHandleColorChange = jest.fn();
130
+ mockUseAppearanceActions.mockReturnValue({
131
+ localCustomColors: {},
132
+ handleThemeSelect: jest.fn(),
133
+ handleColorChange: mockHandleColorChange,
134
+ handleResetColors: jest.fn(),
135
+ });
136
+
137
+ const { getByTestId } = render(<AppearanceScreen />);
138
+
139
+ // Find and click color option
140
+ const colorOption = getByTestId('color-option-primary');
141
+ await act(async () => {
142
+ fireEvent.press(colorOption);
143
+ });
144
+
145
+ expect(mockHandleColorChange).toHaveBeenCalledWith('primary', expect.any(String));
146
+ });
147
+
148
+ it('should handle reset colors', async () => {
149
+ const mockHandleResetColors = jest.fn();
150
+ mockUseAppearanceActions.mockReturnValue({
151
+ localCustomColors: { primary: '#FF0000' },
152
+ handleThemeSelect: jest.fn(),
153
+ handleColorChange: jest.fn(),
154
+ handleResetColors: mockHandleResetColors,
155
+ });
156
+
157
+ const { getByTestId } = render(<AppearanceScreen />);
158
+
159
+ // Find and click reset button
160
+ const resetButton = getByTestId('reset-colors-button');
161
+ await act(async () => {
162
+ fireEvent.press(resetButton);
163
+ });
164
+
165
+ expect(mockHandleResetColors).toHaveBeenCalled();
166
+ });
167
+
168
+ it('should not re-render unnecessarily', () => {
169
+ const { rerender } = render(<AppearanceScreen />);
170
+
171
+ // Rerender with same props
172
+ rerender(<AppearanceScreen />);
173
+
174
+ // Hooks should not be called again for same props
175
+ expect(mockUseAppearance).toHaveBeenCalledTimes(1);
176
+ expect(mockUseAppearanceActions).toHaveBeenCalledTimes(1);
177
+ });
178
+
179
+ it('should handle performance optimizations', () => {
180
+ const startTime = performance.now();
181
+
182
+ const { rerender } = render(<AppearanceScreen />);
183
+
184
+ // Multiple rerenders
185
+ for (let i = 0; i < 10; i++) {
186
+ rerender(<AppearanceScreen />);
187
+ }
188
+
189
+ const endTime = performance.now();
190
+ const renderTime = endTime - startTime;
191
+
192
+ // Should render quickly even with multiple updates
193
+ expect(renderTime).toBeLessThan(100); // 100ms for 10 renders
194
+ });
195
+ });
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Hooks Tests
3
+ */
4
+
5
+ import { renderHook, act } from '@testing-library/react-native';
6
+ import { useAppearance, useAppearanceActions } from '../../hooks';
7
+ import type { ThemeMode, CustomThemeColors } from '../../types';
8
+
9
+ // Mock design system theme
10
+ jest.mock('@umituz/react-native-design-system', () => ({
11
+ useTheme: jest.fn(() => ({
12
+ getState: jest.fn(() => ({
13
+ setThemeMode: jest.fn(),
14
+ })),
15
+ })),
16
+ useDesignSystemTheme: jest.fn(() => ({
17
+ getState: jest.fn(() => ({
18
+ setThemeMode: jest.fn(),
19
+ setCustomColors: jest.fn(),
20
+ })),
21
+ })),
22
+ }));
23
+
24
+ const mockUseTheme = require('@umituz/react-native-design-system').useTheme;
25
+ const mockUseDesignSystemTheme = require('@umituz/react-native-design-system').useDesignSystemTheme;
26
+
27
+ describe('useAppearance', () => {
28
+ beforeEach(() => {
29
+ jest.clearAllMocks();
30
+
31
+ mockUseTheme.getState.mockReturnValue({
32
+ setThemeMode: jest.fn(),
33
+ });
34
+
35
+ mockUseDesignSystemTheme.getState.mockReturnValue({
36
+ setThemeMode: jest.fn(),
37
+ setCustomColors: jest.fn(),
38
+ });
39
+ });
40
+
41
+ afterEach(() => {
42
+ jest.restoreAllMocks();
43
+ });
44
+
45
+ it('should return appearance state and actions', () => {
46
+ const { result } = renderHook(() => useAppearance());
47
+
48
+ expect(result.current).toHaveProperty('themeMode');
49
+ expect(result.current).toHaveProperty('customColors');
50
+ expect(result.current).toHaveProperty('isInitialized');
51
+ expect(result.current).toHaveProperty('setThemeMode');
52
+ expect(result.current).toHaveProperty('toggleTheme');
53
+ expect(result.current).toHaveProperty('setCustomColors');
54
+ expect(result.current).toHaveProperty('resetCustomColors');
55
+ expect(result.current).toHaveProperty('reset');
56
+
57
+ expect(typeof result.current.setThemeMode).toBe('function');
58
+ expect(typeof result.current.toggleTheme).toBe('function');
59
+ expect(typeof result.current.setCustomColors).toBe('function');
60
+ expect(typeof result.current.resetCustomColors).toBe('function');
61
+ expect(typeof result.current.reset).toBe('function');
62
+ });
63
+
64
+ it('should have stable function references', () => {
65
+ const { result: result1 } = renderHook(() => useAppearance());
66
+ const { result: result2 } = renderHook(() => useAppearance());
67
+
68
+ // Functions should be stable across renders
69
+ expect(result1.current.setThemeMode).toBe(result2.current.setThemeMode);
70
+ expect(result1.current.toggleTheme).toBe(result2.current.toggleTheme);
71
+ expect(result1.current.setCustomColors).toBe(result2.current.setCustomColors);
72
+ expect(result1.current.resetCustomColors).toBe(result2.current.resetCustomColors);
73
+ expect(result1.current.reset).toBe(result2.current.reset);
74
+ });
75
+
76
+ it('should call service methods correctly', async () => {
77
+ const { result } = renderHook(() => useAppearance());
78
+
79
+ const mockSetThemeMode = mockUseTheme.getState().setThemeMode;
80
+ const mockSetCustomColors = mockUseDesignSystemTheme.getState().setCustomColors;
81
+
82
+ await act(async () => {
83
+ await result.current.setThemeMode('light');
84
+ });
85
+
86
+ expect(mockSetThemeMode).toHaveBeenCalledWith('light');
87
+ });
88
+
89
+ it('should handle errors gracefully', async () => {
90
+ const { result } = renderHook(() => useAppearance());
91
+
92
+ const mockSetThemeMode = mockUseTheme.getState().setThemeMode;
93
+ const error = new Error('Service error');
94
+ mockSetThemeMode.mockImplementation(() => {
95
+ throw error;
96
+ });
97
+
98
+ await expect(result.current.setThemeMode('light')).rejects.toThrow(error);
99
+ });
100
+ });
101
+
102
+ describe('useAppearanceActions', () => {
103
+ beforeEach(() => {
104
+ jest.clearAllMocks();
105
+ });
106
+
107
+ afterEach(() => {
108
+ jest.restoreAllMocks();
109
+ });
110
+
111
+ it('should return actions and local state', () => {
112
+ const { result } = renderHook(() => useAppearanceActions());
113
+
114
+ expect(result.current).toHaveProperty('localCustomColors');
115
+ expect(result.current).toHaveProperty('handleThemeSelect');
116
+ expect(result.current).toHaveProperty('handleColorChange');
117
+ expect(result.current).toHaveProperty('handleResetColors');
118
+
119
+ expect(typeof result.current.handleThemeSelect).toBe('function');
120
+ expect(typeof result.current.handleColorChange).toBe('function');
121
+ expect(typeof result.current.handleResetColors).toBe('function');
122
+ });
123
+
124
+ it('should initialize with empty custom colors', () => {
125
+ const { result } = renderHook(() => useAppearanceActions());
126
+
127
+ expect(result.current.localCustomColors).toEqual({});
128
+ });
129
+
130
+ it('should handle theme selection', async () => {
131
+ const { result } = renderHook(() => useAppearanceActions());
132
+
133
+ const mockSetThemeMode = mockUseTheme.getState().setThemeMode;
134
+
135
+ await act(async () => {
136
+ await result.current.handleThemeSelect('light');
137
+ });
138
+
139
+ expect(mockSetThemeMode).toHaveBeenCalledWith('light');
140
+ });
141
+
142
+ it('should handle color change', () => {
143
+ const { result } = renderHook(() => useAppearanceActions());
144
+
145
+ const mockSetCustomColors = mockUseDesignSystemTheme.getState().setCustomColors;
146
+
147
+ act(() => {
148
+ result.current.handleColorChange('primary', '#FF0000');
149
+ });
150
+
151
+ expect(mockSetCustomColors).toHaveBeenCalledWith({
152
+ primary: '#FF0000',
153
+ });
154
+ });
155
+
156
+ it('should not update if color is same', () => {
157
+ const { result } = renderHook(() => useAppearanceActions());
158
+
159
+ const mockSetCustomColors = mockUseDesignSystemTheme.getState().setCustomColors;
160
+
161
+ act(() => {
162
+ result.current.handleColorChange('primary', '#FF0000');
163
+ result.current.handleColorChange('primary', '#FF0000'); // Same color
164
+ });
165
+
166
+ // Should only be called once due to optimization
167
+ expect(mockSetCustomColors).toHaveBeenCalledTimes(1);
168
+ });
169
+
170
+ it('should handle reset colors', () => {
171
+ const { result } = renderHook(() => useAppearanceActions());
172
+
173
+ const mockResetCustomColors = mockUseDesignSystemTheme.getState().setCustomColors;
174
+
175
+ act(() => {
176
+ result.current.handleResetColors();
177
+ });
178
+
179
+ expect(mockResetCustomColors).toHaveBeenCalled();
180
+ });
181
+
182
+ it('should handle reset colors with config', () => {
183
+ const onConfirm = jest.fn();
184
+ const config = { onResetConfirm };
185
+
186
+ const { result } = renderHook(() => useAppearanceActions(config));
187
+
188
+ const mockResetCustomColors = mockUseDesignSystemTheme.getState().setCustomColors;
189
+
190
+ act(() => {
191
+ result.current.handleResetColors();
192
+ });
193
+
194
+ expect(mockResetCustomColors).toHaveBeenCalled();
195
+ expect(onConfirm).toHaveBeenCalled();
196
+ });
197
+
198
+ it('should sync local colors with store changes', () => {
199
+ const { result, rerender } = renderHook(
200
+ () => useAppearanceActions(),
201
+ {
202
+ initialProps: { customColors: { primary: '#FF0000' } }
203
+ }
204
+ );
205
+
206
+ expect(result.current.localCustomColors).toEqual({ primary: '#FF0000' });
207
+
208
+ // Simulate store change
209
+ rerender({ customColors: { primary: '#00FF00' } });
210
+
211
+ expect(result.current.localCustomColors).toEqual({ primary: '#00FF00' });
212
+ });
213
+
214
+ it('should have stable function references', () => {
215
+ const { result: result1 } = renderHook(() => useAppearanceActions());
216
+ const { result: result2 } = renderHook(() => useAppearanceActions());
217
+
218
+ // Functions should be stable across renders
219
+ expect(result1.current.handleThemeSelect).toBe(result2.current.handleThemeSelect);
220
+ expect(result1.current.handleColorChange).toBe(result2.current.handleColorChange);
221
+ expect(result1.current.handleResetColors).toBe(result2.current.handleResetColors);
222
+ });
223
+
224
+ it('should cleanup on unmount', () => {
225
+ const { unmount } = renderHook(() => useAppearanceActions());
226
+
227
+ unmount();
228
+
229
+ // Should not throw any errors during cleanup
230
+ expect(true).toBe(true);
231
+ });
232
+ });
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Integration Tests
3
+ */
4
+
5
+ import React from 'react';
6
+ import { render, fireEvent, act } from '@testing-library/react-native';
7
+ import { AppearanceService, appearanceService } from '../../infrastructure/services/appearanceService';
8
+ import { useAppearanceStore } from '../../infrastructure/stores/appearanceStore';
9
+ import { AppearanceScreen } from '../../presentation/screens/AppearanceScreen';
10
+ import type { ThemeMode, CustomThemeColors } from '../../types';
11
+
12
+ // Mock design system theme
13
+ jest.mock('@umituz/react-native-design-system', () => ({
14
+ useTheme: {
15
+ getState: jest.fn(() => ({
16
+ setThemeMode: jest.fn(),
17
+ })),
18
+ },
19
+ useDesignSystemTheme: {
20
+ getState: jest.fn(() => ({
21
+ setThemeMode: jest.fn(),
22
+ setCustomColors: jest.fn(),
23
+ })),
24
+ },
25
+ }));
26
+
27
+ // Mock storage
28
+ jest.mock('../../infrastructure/storage/appearanceStorage', () => ({
29
+ AppearanceStorage: {
30
+ getSettings: jest.fn(() => Promise.resolve(null)),
31
+ setSettings: jest.fn(() => Promise.resolve()),
32
+ clear: jest.fn(() => Promise.resolve()),
33
+ },
34
+ }));
35
+
36
+ const mockUseTheme = require('@umituz/react-native-design-system').useTheme;
37
+ const mockUseDesignSystemTheme = require('@umituz/react-native-design-system').useDesignSystemTheme;
38
+ const mockAppearanceStorage = require('../../infrastructure/storage/appearanceStorage').AppearanceStorage;
39
+
40
+ describe('Appearance Integration Tests', () => {
41
+ beforeEach(() => {
42
+ jest.clearAllMocks();
43
+ });
44
+
45
+ afterEach(() => {
46
+ jest.restoreAllMocks();
47
+ });
48
+
49
+ it('should initialize and render correctly', async () => {
50
+ const { getByTestId } = render(<AppearanceScreen />);
51
+
52
+ // Should render without crashing
53
+ expect(getByTestId('appearance-screen')).toBeTruthy();
54
+
55
+ // Should initialize service
56
+ await act(async () => {
57
+ await appearanceService.initialize();
58
+ });
59
+
60
+ expect(mockAppearanceStorage.getSettings).toHaveBeenCalled();
61
+ expect(mockUseTheme.getState().setThemeMode).toHaveBeenCalled();
62
+ expect(mockUseDesignSystemTheme.getState().setThemeMode).toHaveBeenCalled();
63
+ });
64
+
65
+ it('should handle theme mode changes end-to-end', async () => {
66
+ const { getByTestId } = render(<AppearanceScreen />);
67
+
68
+ // Initialize service
69
+ await act(async () => {
70
+ await appearanceService.initialize();
71
+ });
72
+
73
+ // Change theme mode
74
+ await act(async () => {
75
+ await appearanceService.setThemeMode('light');
76
+ });
77
+
78
+ // Verify all systems are updated
79
+ expect(mockUseTheme.getState().setThemeMode).toHaveBeenCalledWith('light');
80
+ expect(mockUseDesignSystemTheme.getState().setThemeMode).toHaveBeenCalledWith('light');
81
+ expect(mockAppearanceStorage.setSettings).toHaveBeenCalledWith({
82
+ themeMode: 'light',
83
+ });
84
+ });
85
+
86
+ it('should handle custom color changes end-to-end', async () => {
87
+ const { getByTestId } = render(<AppearanceScreen />);
88
+
89
+ // Initialize service
90
+ await act(async () => {
91
+ await appearanceService.initialize();
92
+ });
93
+
94
+ // Change custom colors
95
+ const newColors: CustomThemeColors = {
96
+ primary: '#FF0000',
97
+ secondary: '#00FF00',
98
+ };
99
+
100
+ await act(async () => {
101
+ await appearanceService.setCustomColors(newColors);
102
+ });
103
+
104
+ // Verify all systems are updated
105
+ expect(mockUseDesignSystemTheme.getState().setCustomColors).toHaveBeenCalledWith(newColors);
106
+ expect(mockAppearanceStorage.setSettings).toHaveBeenCalledWith({
107
+ themeMode: 'dark', // Default theme
108
+ customColors: newColors,
109
+ });
110
+ });
111
+
112
+ it('should handle reset end-to-end', async () => {
113
+ const { getByTestId } = render(<AppearanceScreen />);
114
+
115
+ // Initialize with custom settings
116
+ await act(async () => {
117
+ await appearanceService.setThemeMode('light');
118
+ await appearanceService.setCustomColors({
119
+ primary: '#FF0000',
120
+ secondary: '#00FF00',
121
+ });
122
+ });
123
+
124
+ // Reset everything
125
+ await act(async () => {
126
+ await appearanceService.reset();
127
+ });
128
+
129
+ // Verify reset
130
+ expect(mockAppearanceStorage.clear).toHaveBeenCalled();
131
+ expect(mockUseTheme.getState().setThemeMode).toHaveBeenCalledWith('dark');
132
+ expect(mockUseDesignSystemTheme.getState().setThemeMode).toHaveBeenCalledWith('dark');
133
+ expect(mockUseDesignSystemTheme.getState().setCustomColors).toHaveBeenCalledWith(undefined);
134
+ });
135
+
136
+ it('should handle errors gracefully', async () => {
137
+ const { getByTestId } = render(<AppearanceScreen />);
138
+
139
+ // Mock storage to throw error
140
+ mockAppearanceStorage.setSettings.mockRejectedValue(new Error('Storage failed'));
141
+
142
+ // Should still render without crashing
143
+ expect(getByTestId('appearance-screen')).toBeTruthy();
144
+
145
+ // Should handle error gracefully
146
+ await act(async () => {
147
+ await appearanceService.setThemeMode('light');
148
+ });
149
+
150
+ // Should still attempt to update despite storage error
151
+ expect(mockUseTheme.getState().setThemeMode).toHaveBeenCalled();
152
+ });
153
+
154
+ it('should prevent memory leaks during rapid updates', async () => {
155
+ const { getByTestId } = render(<AppearanceScreen />);
156
+
157
+ // Initialize service
158
+ await act(async () => {
159
+ await appearanceService.initialize();
160
+ });
161
+
162
+ // Rapid theme changes
163
+ const promises = [];
164
+ for (let i = 0; i < 10; i++) {
165
+ promises.push(
166
+ act(async () => {
167
+ await appearanceService.setThemeMode(i % 2 === 0 ? 'light' : 'dark');
168
+ })
169
+ );
170
+ }
171
+
172
+ await Promise.all(promises);
173
+
174
+ // Should handle rapid changes without memory leaks
175
+ expect(getByTestId('appearance-screen')).toBeTruthy();
176
+
177
+ // Verify final state
178
+ const finalTheme = i % 2 === 0 ? 'dark' : 'light';
179
+ expect(mockUseTheme.getState().setThemeMode).toHaveBeenLastCalledWith(finalTheme);
180
+ });
181
+
182
+ it('should maintain performance during heavy usage', async () => {
183
+ const { getByTestId } = render(<AppearanceScreen />);
184
+
185
+ const startTime = performance.now();
186
+
187
+ // Heavy usage simulation
188
+ await act(async () => {
189
+ await appearanceService.initialize();
190
+
191
+ // Rapid color changes
192
+ for (let i = 0; i < 20; i++) {
193
+ const colors: CustomThemeColors = {
194
+ primary: `#${i.toString(16).padStart(6, '0')}`,
195
+ };
196
+ await appearanceService.setCustomColors(colors);
197
+ }
198
+ });
199
+
200
+ const endTime = performance.now();
201
+ const duration = endTime - startTime;
202
+
203
+ // Should complete within reasonable time
204
+ expect(duration).toBeLessThan(1000); // 1 second for 20 color changes
205
+ expect(getByTestId('appearance-screen')).toBeTruthy();
206
+ });
207
+ });