@umituz/react-native-settings 1.11.4 → 2.4.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.
- package/LICENSE +0 -0
- package/README.md +129 -3
- package/lib/__tests__/setup.d.ts +5 -0
- package/lib/__tests__/setup.d.ts.map +1 -0
- package/lib/__tests__/setup.js +143 -0
- package/lib/__tests__/setup.js.map +1 -0
- package/lib/domain/repositories/ISettingsRepository.d.ts +51 -0
- package/lib/domain/repositories/ISettingsRepository.d.ts.map +1 -0
- package/lib/domain/repositories/ISettingsRepository.js +8 -0
- package/lib/domain/repositories/ISettingsRepository.js.map +1 -0
- package/lib/index.d.ts +35 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +32 -0
- package/lib/index.js.map +1 -0
- package/lib/infrastructure/storage/SettingsStore.d.ts +36 -0
- package/lib/infrastructure/storage/SettingsStore.d.ts.map +1 -0
- package/lib/infrastructure/storage/SettingsStore.js +144 -0
- package/lib/infrastructure/storage/SettingsStore.js.map +1 -0
- package/lib/presentation/components/CloudSyncSetting.d.ts +16 -0
- package/lib/presentation/components/CloudSyncSetting.d.ts.map +1 -0
- package/lib/presentation/components/CloudSyncSetting.js +30 -0
- package/lib/presentation/components/CloudSyncSetting.js.map +1 -0
- package/lib/presentation/components/DisclaimerCard.d.ts +15 -0
- package/lib/presentation/components/DisclaimerCard.d.ts.map +1 -0
- package/lib/presentation/components/DisclaimerCard.js +73 -0
- package/lib/presentation/components/DisclaimerCard.js.map +1 -0
- package/lib/presentation/components/DisclaimerModal.d.ts +13 -0
- package/lib/presentation/components/DisclaimerModal.d.ts.map +1 -0
- package/lib/presentation/components/DisclaimerModal.js +62 -0
- package/lib/presentation/components/DisclaimerModal.js.map +1 -0
- package/lib/presentation/components/DisclaimerSetting.d.ts +39 -0
- package/lib/presentation/components/DisclaimerSetting.d.ts.map +1 -0
- package/lib/presentation/components/DisclaimerSetting.js +59 -0
- package/lib/presentation/components/DisclaimerSetting.js.map +1 -0
- package/lib/presentation/components/SettingItem.d.ts +45 -0
- package/lib/presentation/components/SettingItem.d.ts.map +1 -0
- package/lib/presentation/components/SettingItem.js +113 -0
- package/lib/presentation/components/SettingItem.js.map +1 -0
- package/lib/presentation/components/SettingsErrorBoundary.d.ts +23 -0
- package/lib/presentation/components/SettingsErrorBoundary.d.ts.map +1 -0
- package/lib/presentation/components/SettingsErrorBoundary.js +73 -0
- package/lib/presentation/components/SettingsErrorBoundary.js.map +1 -0
- package/lib/presentation/components/SettingsFooter.d.ts +11 -0
- package/lib/presentation/components/SettingsFooter.d.ts.map +1 -0
- package/lib/presentation/components/SettingsFooter.js +31 -0
- package/lib/presentation/components/SettingsFooter.js.map +1 -0
- package/lib/presentation/components/SettingsSection.d.ts +13 -0
- package/lib/presentation/components/SettingsSection.d.ts.map +1 -0
- package/lib/presentation/components/SettingsSection.js +37 -0
- package/lib/presentation/components/SettingsSection.js.map +1 -0
- package/lib/presentation/components/StorageClearSetting.d.ts +16 -0
- package/lib/presentation/components/StorageClearSetting.d.ts.map +1 -0
- package/lib/presentation/components/StorageClearSetting.js +21 -0
- package/lib/presentation/components/StorageClearSetting.js.map +1 -0
- package/lib/presentation/components/UserProfileHeader.d.ts +30 -0
- package/lib/presentation/components/UserProfileHeader.d.ts.map +1 -0
- package/lib/presentation/components/UserProfileHeader.js +119 -0
- package/lib/presentation/components/UserProfileHeader.js.map +1 -0
- package/lib/presentation/screens/AppearanceScreen.d.ts +8 -0
- package/lib/presentation/screens/AppearanceScreen.d.ts.map +1 -0
- package/lib/presentation/screens/AppearanceScreen.js +8 -0
- package/lib/presentation/screens/AppearanceScreen.js.map +1 -0
- package/lib/presentation/screens/SettingsScreen.d.ts +38 -0
- package/lib/presentation/screens/SettingsScreen.d.ts.map +1 -0
- package/lib/presentation/screens/SettingsScreen.js +37 -0
- package/lib/presentation/screens/SettingsScreen.js.map +1 -0
- package/lib/presentation/screens/components/AboutLegalSection.d.ts +15 -0
- package/lib/presentation/screens/components/AboutLegalSection.d.ts.map +1 -0
- package/lib/presentation/screens/components/AboutLegalSection.js +28 -0
- package/lib/presentation/screens/components/AboutLegalSection.js.map +1 -0
- package/lib/presentation/screens/components/AppearanceSection.d.ts +12 -0
- package/lib/presentation/screens/components/AppearanceSection.d.ts.map +1 -0
- package/lib/presentation/screens/components/AppearanceSection.js +21 -0
- package/lib/presentation/screens/components/AppearanceSection.js.map +1 -0
- package/lib/presentation/screens/components/LanguageSection.d.ts +12 -0
- package/lib/presentation/screens/components/LanguageSection.d.ts.map +1 -0
- package/lib/presentation/screens/components/LanguageSection.js +26 -0
- package/lib/presentation/screens/components/LanguageSection.js.map +1 -0
- package/lib/presentation/screens/components/NotificationsSection.d.ts +12 -0
- package/lib/presentation/screens/components/NotificationsSection.d.ts.map +1 -0
- package/lib/presentation/screens/components/NotificationsSection.js +58 -0
- package/lib/presentation/screens/components/NotificationsSection.js.map +1 -0
- package/lib/presentation/screens/components/SettingsContent.d.ts +36 -0
- package/lib/presentation/screens/components/SettingsContent.d.ts.map +1 -0
- package/lib/presentation/screens/components/SettingsContent.js +81 -0
- package/lib/presentation/screens/components/SettingsContent.js.map +1 -0
- package/lib/presentation/screens/components/SettingsHeader.d.ts +12 -0
- package/lib/presentation/screens/components/SettingsHeader.d.ts.map +1 -0
- package/lib/presentation/screens/components/SettingsHeader.js +59 -0
- package/lib/presentation/screens/components/SettingsHeader.js.map +1 -0
- package/lib/presentation/screens/components/index.d.ts +9 -0
- package/lib/presentation/screens/components/index.d.ts.map +1 -0
- package/lib/presentation/screens/components/index.js +9 -0
- package/lib/presentation/screens/components/index.js.map +1 -0
- package/lib/presentation/screens/hooks/useFeatureDetection.d.ts +21 -0
- package/lib/presentation/screens/hooks/useFeatureDetection.d.ts.map +1 -0
- package/lib/presentation/screens/hooks/useFeatureDetection.js +82 -0
- package/lib/presentation/screens/hooks/useFeatureDetection.js.map +1 -0
- package/lib/presentation/screens/types/CustomSection.d.ts +19 -0
- package/lib/presentation/screens/types/CustomSection.d.ts.map +1 -0
- package/lib/presentation/screens/types/CustomSection.js +6 -0
- package/lib/presentation/screens/types/CustomSection.js.map +1 -0
- package/lib/presentation/screens/types/ExtendedConfig.d.ts +68 -0
- package/lib/presentation/screens/types/ExtendedConfig.d.ts.map +1 -0
- package/lib/presentation/screens/types/ExtendedConfig.js +6 -0
- package/lib/presentation/screens/types/ExtendedConfig.js.map +1 -0
- package/lib/presentation/screens/types/FeatureConfig.d.ts +95 -0
- package/lib/presentation/screens/types/FeatureConfig.d.ts.map +1 -0
- package/lib/presentation/screens/types/FeatureConfig.js +6 -0
- package/lib/presentation/screens/types/FeatureConfig.js.map +1 -0
- package/lib/presentation/screens/types/SettingsConfig.d.ts +97 -0
- package/lib/presentation/screens/types/SettingsConfig.d.ts.map +1 -0
- package/lib/presentation/screens/types/SettingsConfig.js +6 -0
- package/lib/presentation/screens/types/SettingsConfig.js.map +1 -0
- package/lib/presentation/screens/types/index.d.ts +10 -0
- package/lib/presentation/screens/types/index.d.ts.map +1 -0
- package/lib/presentation/screens/types/index.js +6 -0
- package/lib/presentation/screens/types/index.js.map +1 -0
- package/lib/presentation/screens/utils/normalizeConfig.d.ts +44 -0
- package/lib/presentation/screens/utils/normalizeConfig.d.ts.map +1 -0
- package/lib/presentation/screens/utils/normalizeConfig.js +38 -0
- package/lib/presentation/screens/utils/normalizeConfig.js.map +1 -0
- package/package.json +46 -11
- package/src/__tests__/integration.test.tsx +371 -0
- package/src/__tests__/performance.test.tsx +369 -0
- package/src/__tests__/setup.test.tsx +20 -0
- package/src/__tests__/setup.ts +157 -0
- package/src/domain/repositories/ISettingsRepository.ts +0 -0
- package/src/index.ts +9 -1
- package/src/infrastructure/storage/SettingsStore.ts +90 -45
- package/src/infrastructure/storage/__tests__/SettingsStore.test.tsx +302 -0
- package/src/presentation/components/CloudSyncSetting.tsx +11 -17
- package/src/presentation/components/DisclaimerCard.tsx +115 -0
- package/src/presentation/components/DisclaimerModal.tsx +104 -0
- package/src/presentation/components/DisclaimerSetting.tsx +77 -159
- package/src/presentation/components/SettingItem.tsx +11 -2
- package/src/presentation/components/SettingsErrorBoundary.tsx +126 -0
- package/src/presentation/components/SettingsFooter.tsx +0 -0
- package/src/presentation/components/SettingsSection.tsx +0 -0
- package/src/presentation/components/StorageClearSetting.tsx +13 -8
- package/src/presentation/components/UserProfileHeader.tsx +48 -11
- package/src/presentation/components/__tests__/CloudSyncSetting.test.tsx +78 -0
- package/src/presentation/components/__tests__/DisclaimerCard.test.tsx +208 -0
- package/src/presentation/components/__tests__/DisclaimerModal.test.tsx +236 -0
- package/src/presentation/components/__tests__/DisclaimerSetting.test.tsx +74 -0
- package/src/presentation/components/__tests__/SettingItem.test.tsx +189 -0
- package/src/presentation/components/__tests__/SettingsErrorBoundary.test.tsx +186 -0
- package/src/presentation/screens/AppearanceScreen.tsx +0 -0
- package/src/presentation/screens/SettingsScreen.tsx +29 -159
- package/src/presentation/screens/__tests__/SettingsScreen.test.tsx +322 -0
- package/src/presentation/screens/components/AboutLegalSection.tsx +14 -5
- package/src/presentation/screens/components/AppearanceSection.tsx +1 -1
- package/src/presentation/screens/components/LanguageSection.tsx +2 -1
- package/src/presentation/screens/components/NotificationsSection.tsx +19 -14
- package/src/presentation/screens/components/SettingsContent.tsx +167 -0
- package/src/presentation/screens/components/SettingsHeader.tsx +79 -0
- package/src/presentation/screens/components/index.ts +0 -0
- package/src/presentation/screens/hooks/__tests__/useFeatureDetection.test.tsx +261 -0
- package/src/presentation/screens/hooks/useFeatureDetection.ts +15 -5
- package/src/presentation/screens/types/CustomSection.ts +20 -0
- package/src/presentation/screens/types/ExtendedConfig.ts +68 -0
- package/src/presentation/screens/types/FeatureConfig.ts +102 -0
- package/src/presentation/screens/types/SettingsConfig.ts +116 -0
- package/src/presentation/screens/types/index.ts +20 -0
- package/src/presentation/screens/utils/normalizeConfig.ts +2 -1
- package/src/presentation/screens/LanguageSelectionScreen.tsx +0 -204
- package/src/presentation/screens/types.ts +0 -263
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance and Memory Leak Tests
|
|
3
|
+
* Tests for performance optimization and memory management
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { render, unmountComponentAtNode } from '@testing-library/react-native';
|
|
8
|
+
import { SettingsScreen } from '../../screens/SettingsScreen';
|
|
9
|
+
import { useSettings } from '../../../infrastructure/storage/SettingsStore';
|
|
10
|
+
import { DisclaimerSetting } from '../../components/DisclaimerSetting';
|
|
11
|
+
import { SettingItem } from '../../components/SettingItem';
|
|
12
|
+
|
|
13
|
+
// Mock dependencies
|
|
14
|
+
jest.mock('@umituz/react-native-design-system-theme', () => ({
|
|
15
|
+
useDesignSystemTheme: () => ({
|
|
16
|
+
themeMode: 'light',
|
|
17
|
+
}),
|
|
18
|
+
useAppDesignTokens: () => ({
|
|
19
|
+
colors: {
|
|
20
|
+
backgroundPrimary: '#ffffff',
|
|
21
|
+
},
|
|
22
|
+
}),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
jest.mock('@umituz/react-native-localization', () => ({
|
|
26
|
+
useLocalization: () => ({
|
|
27
|
+
t: (key: string) => key,
|
|
28
|
+
}),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
jest.mock('@umituz/react-native-storage', () => ({
|
|
32
|
+
storageRepository: {
|
|
33
|
+
getItem: jest.fn(),
|
|
34
|
+
setItem: jest.fn(),
|
|
35
|
+
},
|
|
36
|
+
StorageKey: {
|
|
37
|
+
SETTINGS: 'settings',
|
|
38
|
+
},
|
|
39
|
+
createUserKey: (key: string, userId: string) => `${key}_${userId}`,
|
|
40
|
+
unwrap: (result: any, defaultValue: any) => result.success ? result.data : defaultValue,
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
jest.mock('../../components/SettingsHeader', () => 'SettingsHeader');
|
|
44
|
+
jest.mock('../../components/SettingsContent', () => 'SettingsContent');
|
|
45
|
+
jest.mock('../../components/SettingsErrorBoundary', () => 'SettingsErrorBoundary');
|
|
46
|
+
jest.mock('../../utils/normalizeConfig', () => ({
|
|
47
|
+
normalizeSettingsConfig: jest.fn(() => ({})),
|
|
48
|
+
}));
|
|
49
|
+
jest.mock('../../hooks/useFeatureDetection', () => ({
|
|
50
|
+
useFeatureDetection: jest.fn(() => ({})),
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
jest.mock('@react-navigation/native', () => ({
|
|
54
|
+
useNavigation: () => ({
|
|
55
|
+
navigate: jest.fn(),
|
|
56
|
+
goBack: jest.fn(),
|
|
57
|
+
getState: jest.fn(() => ({ routes: [] })),
|
|
58
|
+
}),
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
describe('Performance and Memory Tests', () => {
|
|
62
|
+
let container: any;
|
|
63
|
+
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
container = document.createElement('div');
|
|
66
|
+
document.body.appendChild(container);
|
|
67
|
+
jest.clearAllMocks();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
afterEach(() => {
|
|
71
|
+
if (container) {
|
|
72
|
+
unmountComponentAtNode(container);
|
|
73
|
+
container.remove();
|
|
74
|
+
container = null;
|
|
75
|
+
}
|
|
76
|
+
jest.clearAllTimers();
|
|
77
|
+
jest.useRealTimers();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('Memory Leak Prevention', () => {
|
|
81
|
+
it('should not leak memory on SettingsScreen mount/unmount', () => {
|
|
82
|
+
const mockSetItem = jest.fn();
|
|
83
|
+
require('@umituz/react-native-storage').storageRepository.setItem = mockSetItem;
|
|
84
|
+
|
|
85
|
+
const { unmount } = render(<SettingsScreen />);
|
|
86
|
+
|
|
87
|
+
// Component should mount without issues
|
|
88
|
+
expect(() => unmount()).not.toThrow();
|
|
89
|
+
|
|
90
|
+
// Check if any timers or listeners are left
|
|
91
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should cleanup useEffect hooks properly', () => {
|
|
95
|
+
jest.useFakeTimers();
|
|
96
|
+
|
|
97
|
+
const { unmount } = render(<DisclaimerSetting />);
|
|
98
|
+
|
|
99
|
+
// Fast forward time
|
|
100
|
+
jest.advanceTimersByTime(1000);
|
|
101
|
+
|
|
102
|
+
// Unmount component
|
|
103
|
+
unmount();
|
|
104
|
+
|
|
105
|
+
// Clear any remaining timers
|
|
106
|
+
jest.clearAllTimers();
|
|
107
|
+
|
|
108
|
+
// Should not have any remaining timers
|
|
109
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should handle rapid mount/unmount cycles', () => {
|
|
113
|
+
for (let i = 0; i < 100; i++) {
|
|
114
|
+
const { unmount } = render(<SettingsScreen />);
|
|
115
|
+
unmount();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Should not throw any errors
|
|
119
|
+
expect(true).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should cleanup event listeners', () => {
|
|
123
|
+
const addEventListenerSpy = jest.spyOn(document, 'addEventListener');
|
|
124
|
+
const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener');
|
|
125
|
+
|
|
126
|
+
const { unmount } = render(<SettingsScreen />);
|
|
127
|
+
|
|
128
|
+
unmount();
|
|
129
|
+
|
|
130
|
+
// All added listeners should be removed
|
|
131
|
+
expect(removeEventListenerSpy.mock.calls.length).toBeGreaterThanOrEqual(
|
|
132
|
+
addEventListenerSpy.mock.calls.length
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
addEventListenerSpy.mockRestore();
|
|
136
|
+
removeEventListenerSpy.mockRestore();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('Performance Optimization', () => {
|
|
141
|
+
it('should render within acceptable time limits', () => {
|
|
142
|
+
const startTime = performance.now();
|
|
143
|
+
|
|
144
|
+
render(<SettingsScreen />);
|
|
145
|
+
|
|
146
|
+
const endTime = performance.now();
|
|
147
|
+
const renderTime = endTime - startTime;
|
|
148
|
+
|
|
149
|
+
// Should render within 100ms
|
|
150
|
+
expect(renderTime).toBeLessThan(100);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should handle large datasets efficiently', () => {
|
|
154
|
+
const largeCustomSections = Array.from({ length: 1000 }, (_, i) => ({
|
|
155
|
+
title: `Section ${i}`,
|
|
156
|
+
data: Array.from({ length: 100 }, (_, j) => ({
|
|
157
|
+
id: `item-${i}-${j}`,
|
|
158
|
+
title: `Item ${i}-${j}`,
|
|
159
|
+
})),
|
|
160
|
+
}));
|
|
161
|
+
|
|
162
|
+
const startTime = performance.now();
|
|
163
|
+
|
|
164
|
+
const { unmount } = render(
|
|
165
|
+
<SettingsScreen customSections={largeCustomSections} />
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const endTime = performance.now();
|
|
169
|
+
const renderTime = endTime - startTime;
|
|
170
|
+
|
|
171
|
+
// Should handle large datasets within reasonable time
|
|
172
|
+
expect(renderTime).toBeLessThan(1000);
|
|
173
|
+
|
|
174
|
+
unmount();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should not cause unnecessary re-renders', () => {
|
|
178
|
+
const renderSpy = jest.fn();
|
|
179
|
+
|
|
180
|
+
const TestComponent = () => {
|
|
181
|
+
renderSpy();
|
|
182
|
+
return <SettingsScreen />;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const { rerender } = render(<TestComponent />);
|
|
186
|
+
|
|
187
|
+
expect(renderSpy).toHaveBeenCalledTimes(1);
|
|
188
|
+
|
|
189
|
+
// Rerender with same props
|
|
190
|
+
rerender(<TestComponent />);
|
|
191
|
+
|
|
192
|
+
// Should not cause unnecessary re-renders
|
|
193
|
+
expect(renderSpy).toHaveBeenCalledTimes(2);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should use React.memo effectively', () => {
|
|
197
|
+
const props = {
|
|
198
|
+
icon: 'Settings',
|
|
199
|
+
title: 'Test Setting',
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const { rerender } = render(<SettingItem {...props} />);
|
|
203
|
+
|
|
204
|
+
const startTime = performance.now();
|
|
205
|
+
|
|
206
|
+
// Rerender with same props
|
|
207
|
+
rerender(<SettingItem {...props} />);
|
|
208
|
+
|
|
209
|
+
const endTime = performance.now();
|
|
210
|
+
const rerenderTime = endTime - startTime;
|
|
211
|
+
|
|
212
|
+
// Rerender should be fast due to memoization
|
|
213
|
+
expect(rerenderTime).toBeLessThan(50);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe('Memory Usage', () => {
|
|
218
|
+
it('should not accumulate memory on repeated operations', () => {
|
|
219
|
+
const initialMemory = (performance as any).memory?.usedJSHeapSize || 0;
|
|
220
|
+
|
|
221
|
+
// Perform many operations
|
|
222
|
+
for (let i = 0; i < 1000; i++) {
|
|
223
|
+
const { unmount } = render(<SettingsScreen />);
|
|
224
|
+
unmount();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Force garbage collection if available
|
|
228
|
+
if (global.gc) {
|
|
229
|
+
global.gc();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const finalMemory = (performance as any).memory?.usedJSHeapSize || 0;
|
|
233
|
+
|
|
234
|
+
// Memory usage should not increase significantly
|
|
235
|
+
if (initialMemory > 0 && finalMemory > 0) {
|
|
236
|
+
const memoryIncrease = finalMemory - initialMemory;
|
|
237
|
+
expect(memoryIncrease).toBeLessThan(10 * 1024 * 1024); // Less than 10MB
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should cleanup large objects properly', () => {
|
|
242
|
+
const largeData = {
|
|
243
|
+
config: Array.from({ length: 10000 }, (_, i) => ({
|
|
244
|
+
id: i,
|
|
245
|
+
data: new Array(1000).fill('large data string'),
|
|
246
|
+
})),
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const { unmount } = render(<SettingsScreen {...largeData} />);
|
|
250
|
+
|
|
251
|
+
unmount();
|
|
252
|
+
|
|
253
|
+
// Force garbage collection if available
|
|
254
|
+
if (global.gc) {
|
|
255
|
+
global.gc();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Should not throw errors
|
|
259
|
+
expect(true).toBe(true);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe('Timer and Interval Management', () => {
|
|
264
|
+
it('should cleanup timers on unmount', () => {
|
|
265
|
+
jest.useFakeTimers();
|
|
266
|
+
|
|
267
|
+
const { unmount } = render(<DisclaimerSetting />);
|
|
268
|
+
|
|
269
|
+
// Simulate some timers
|
|
270
|
+
jest.advanceTimersByTime(5000);
|
|
271
|
+
|
|
272
|
+
expect(jest.getTimerCount()).toBeGreaterThan(0);
|
|
273
|
+
|
|
274
|
+
unmount();
|
|
275
|
+
|
|
276
|
+
jest.clearAllTimers();
|
|
277
|
+
|
|
278
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should not have memory leaks from intervals', () => {
|
|
282
|
+
jest.useFakeTimers();
|
|
283
|
+
|
|
284
|
+
const { unmount } = render(<SettingsScreen />);
|
|
285
|
+
|
|
286
|
+
// Simulate time passing
|
|
287
|
+
for (let i = 0; i < 100; i++) {
|
|
288
|
+
jest.advanceTimersByTime(1000);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
unmount();
|
|
292
|
+
|
|
293
|
+
jest.clearAllTimers();
|
|
294
|
+
|
|
295
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
describe('Async Operation Cleanup', () => {
|
|
300
|
+
it('should cleanup async operations on unmount', async () => {
|
|
301
|
+
const mockGetItem = jest.fn(() => new Promise(resolve =>
|
|
302
|
+
setTimeout(() => resolve({ success: true, data: {} }), 1000)
|
|
303
|
+
));
|
|
304
|
+
|
|
305
|
+
require('@umituz/react-native-storage').storageRepository.getItem = mockGetItem;
|
|
306
|
+
|
|
307
|
+
const { unmount } = render(<SettingsScreen />);
|
|
308
|
+
|
|
309
|
+
// Unmount before async operation completes
|
|
310
|
+
unmount();
|
|
311
|
+
|
|
312
|
+
// Should not cause memory leaks
|
|
313
|
+
expect(true).toBe(true);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should handle promise rejection on unmount', async () => {
|
|
317
|
+
const mockGetItem = jest.fn(() => new Promise((_, reject) =>
|
|
318
|
+
setTimeout(() => reject(new Error('Test error')), 1000)
|
|
319
|
+
));
|
|
320
|
+
|
|
321
|
+
require('@umituz/react-native-storage').storageRepository.getItem = mockGetItem;
|
|
322
|
+
|
|
323
|
+
const { unmount } = render(<SettingsScreen />);
|
|
324
|
+
|
|
325
|
+
// Unmount before promise rejects
|
|
326
|
+
unmount();
|
|
327
|
+
|
|
328
|
+
// Should not cause unhandled promise rejection
|
|
329
|
+
await new Promise(resolve => setTimeout(resolve, 1100));
|
|
330
|
+
|
|
331
|
+
expect(true).toBe(true);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
describe('Resource Cleanup', () => {
|
|
336
|
+
it('should cleanup subscriptions', () => {
|
|
337
|
+
const mockSubscribe = jest.fn(() => jest.fn());
|
|
338
|
+
|
|
339
|
+
// Mock subscription
|
|
340
|
+
const originalUseEffect = React.useEffect;
|
|
341
|
+
React.useEffect = (fn, deps) => {
|
|
342
|
+
const cleanup = fn();
|
|
343
|
+
return cleanup;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const { unmount } = render(<SettingsScreen />);
|
|
347
|
+
|
|
348
|
+
unmount();
|
|
349
|
+
|
|
350
|
+
React.useEffect = originalUseEffect;
|
|
351
|
+
|
|
352
|
+
expect(true).toBe(true);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('should cleanup event emitters', () => {
|
|
356
|
+
const mockEmitter = {
|
|
357
|
+
addListener: jest.fn(() => jest.fn()),
|
|
358
|
+
removeListener: jest.fn(),
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// Mock event emitter usage
|
|
362
|
+
const { unmount } = render(<SettingsScreen />);
|
|
363
|
+
|
|
364
|
+
unmount();
|
|
365
|
+
|
|
366
|
+
expect(true).toBe(true);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple Test to Verify Jest Setup
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
describe('Jest Setup', () => {
|
|
6
|
+
it('should run a simple test', () => {
|
|
7
|
+
expect(1 + 1).toBe(2);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should handle async operations', async () => {
|
|
11
|
+
const result = await Promise.resolve(42);
|
|
12
|
+
expect(result).toBe(42);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should handle mocking', () => {
|
|
16
|
+
const mockFn = jest.fn();
|
|
17
|
+
mockFn('test');
|
|
18
|
+
expect(mockFn).toHaveBeenCalledWith('test');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jest Test Setup
|
|
3
|
+
* Mock all external dependencies
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Mock React Native modules
|
|
7
|
+
jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
|
|
8
|
+
|
|
9
|
+
// Mock design system packages
|
|
10
|
+
jest.mock('@umituz/react-native-design-system-theme', () => ({
|
|
11
|
+
useDesignSystemTheme: () => ({
|
|
12
|
+
themeMode: 'light',
|
|
13
|
+
}),
|
|
14
|
+
useAppDesignTokens: () => ({
|
|
15
|
+
colors: {
|
|
16
|
+
backgroundPrimary: '#ffffff',
|
|
17
|
+
primary: '#007AFF',
|
|
18
|
+
textPrimary: '#000000',
|
|
19
|
+
textSecondary: '#666666',
|
|
20
|
+
surface: '#f5f5f5',
|
|
21
|
+
borderLight: '#e0e0e0',
|
|
22
|
+
warning: '#FF9800',
|
|
23
|
+
},
|
|
24
|
+
spacing: {
|
|
25
|
+
md: 16,
|
|
26
|
+
},
|
|
27
|
+
typography: {
|
|
28
|
+
labelLarge: {
|
|
29
|
+
fontWeight: '500',
|
|
30
|
+
fontSize: 14,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
withAlpha: jest.fn((color: string, alpha: number) => `${color}${alpha}`),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
jest.mock('@umituz/react-native-design-system-atoms', () => ({
|
|
38
|
+
AtomicText: ({ children, type, color, style, testID }: any) => {
|
|
39
|
+
const React = require('react');
|
|
40
|
+
const { Text } = require('react-native');
|
|
41
|
+
return React.createElement(Text, {
|
|
42
|
+
style,
|
|
43
|
+
testID: testID || `atomic-text-${type}-${color}`
|
|
44
|
+
}, children);
|
|
45
|
+
},
|
|
46
|
+
AtomicIcon: ({ name, color, size, style, testID }: any) => {
|
|
47
|
+
const React = require('react');
|
|
48
|
+
const { Text } = require('react-native');
|
|
49
|
+
return React.createElement(Text, {
|
|
50
|
+
style,
|
|
51
|
+
testID: testID || `atomic-icon-${name}-${color}-${size}`
|
|
52
|
+
}, `Icon: ${name}`);
|
|
53
|
+
},
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
// Mock localization
|
|
57
|
+
jest.mock('@umituz/react-native-localization', () => ({
|
|
58
|
+
useLocalization: () => ({
|
|
59
|
+
t: (key: string) => key,
|
|
60
|
+
changeLanguage: jest.fn(),
|
|
61
|
+
currentLanguage: 'en-US',
|
|
62
|
+
}),
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
// Mock storage
|
|
66
|
+
jest.mock('@umituz/react-native-storage', () => ({
|
|
67
|
+
storageRepository: {
|
|
68
|
+
getItem: jest.fn(),
|
|
69
|
+
setItem: jest.fn(),
|
|
70
|
+
removeItem: jest.fn(),
|
|
71
|
+
clear: jest.fn(),
|
|
72
|
+
},
|
|
73
|
+
StorageKey: {
|
|
74
|
+
SETTINGS: 'settings',
|
|
75
|
+
},
|
|
76
|
+
createUserKey: (key: string, userId: string) => `${key}_${userId}`,
|
|
77
|
+
unwrap: (result: any, defaultValue: any) => result.success ? result.data : defaultValue,
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
// Mock lucide-react-native
|
|
81
|
+
jest.mock('lucide-react-native', () => ({
|
|
82
|
+
Cloud: 'Cloud',
|
|
83
|
+
Bell: 'Bell',
|
|
84
|
+
Palette: 'Palette',
|
|
85
|
+
ChevronRight: 'ChevronRight',
|
|
86
|
+
AlertTriangle: 'AlertTriangle',
|
|
87
|
+
Info: 'Info',
|
|
88
|
+
X: 'X',
|
|
89
|
+
ArrowRight: 'ArrowRight',
|
|
90
|
+
Settings: 'Settings',
|
|
91
|
+
}));
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
// Mock navigation
|
|
96
|
+
jest.mock('@react-navigation/native', () => ({
|
|
97
|
+
useNavigation: () => ({
|
|
98
|
+
navigate: jest.fn(),
|
|
99
|
+
goBack: jest.fn(),
|
|
100
|
+
getState: jest.fn(() => ({
|
|
101
|
+
routes: [{ name: 'Settings' }],
|
|
102
|
+
})),
|
|
103
|
+
setOptions: jest.fn(),
|
|
104
|
+
isFocused: jest.fn(() => true),
|
|
105
|
+
addListener: jest.fn(() => jest.fn()),
|
|
106
|
+
removeListener: jest.fn(),
|
|
107
|
+
}),
|
|
108
|
+
useFocusEffect: jest.fn(),
|
|
109
|
+
useIsFocused: jest.fn(() => true),
|
|
110
|
+
NavigationContainer: ({ children }: any) => children,
|
|
111
|
+
}));
|
|
112
|
+
|
|
113
|
+
// Mock safe area context
|
|
114
|
+
jest.mock('react-native-safe-area-context', () => ({
|
|
115
|
+
SafeAreaProvider: ({ children }: any) => children,
|
|
116
|
+
SafeAreaView: ({ children }: any) => children,
|
|
117
|
+
useSafeAreaInsets: () => ({
|
|
118
|
+
top: 0,
|
|
119
|
+
bottom: 0,
|
|
120
|
+
left: 0,
|
|
121
|
+
right: 0,
|
|
122
|
+
}),
|
|
123
|
+
}));
|
|
124
|
+
|
|
125
|
+
// Mock linear gradient
|
|
126
|
+
jest.mock('expo-linear-gradient', () => ({
|
|
127
|
+
LinearGradient: ({ children }: any) => children,
|
|
128
|
+
}));
|
|
129
|
+
|
|
130
|
+
// Mock notification service
|
|
131
|
+
jest.mock('@umituz/react-native-notifications', () => ({
|
|
132
|
+
notificationService: {
|
|
133
|
+
hasPermissions: jest.fn(() => Promise.resolve(true)),
|
|
134
|
+
requestPermissions: jest.fn(() => Promise.resolve()),
|
|
135
|
+
},
|
|
136
|
+
}));
|
|
137
|
+
|
|
138
|
+
// Mock appearance
|
|
139
|
+
jest.mock('@umituz/react-native-appearance', () => ({
|
|
140
|
+
useAppearance: () => ({
|
|
141
|
+
colorScheme: 'light',
|
|
142
|
+
setColorScheme: jest.fn(),
|
|
143
|
+
}),
|
|
144
|
+
}));
|
|
145
|
+
|
|
146
|
+
// Mock console methods in tests
|
|
147
|
+
global.console = {
|
|
148
|
+
...console,
|
|
149
|
+
warn: jest.fn(),
|
|
150
|
+
error: jest.fn(),
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Set up __DEV__ mock
|
|
154
|
+
Object.defineProperty(global, '__DEV__', {
|
|
155
|
+
value: true,
|
|
156
|
+
writable: true,
|
|
157
|
+
});
|
|
File without changes
|
package/src/index.ts
CHANGED
|
@@ -35,7 +35,6 @@ export {
|
|
|
35
35
|
export { SettingsScreen } from './presentation/screens/SettingsScreen';
|
|
36
36
|
export type { SettingsScreenProps } from './presentation/screens/SettingsScreen';
|
|
37
37
|
export { AppearanceScreen } from './presentation/screens/AppearanceScreen';
|
|
38
|
-
export { LanguageSelectionScreen } from './presentation/screens/LanguageSelectionScreen';
|
|
39
38
|
|
|
40
39
|
// =============================================================================
|
|
41
40
|
// PRESENTATION LAYER - Types
|
|
@@ -60,6 +59,15 @@ export { UserProfileHeader } from './presentation/components/UserProfileHeader';
|
|
|
60
59
|
export type { UserProfileHeaderProps } from './presentation/components/UserProfileHeader';
|
|
61
60
|
|
|
62
61
|
export { DisclaimerSetting } from './presentation/components/DisclaimerSetting';
|
|
62
|
+
export type { DisclaimerSettingProps } from './presentation/components/DisclaimerSetting';
|
|
63
|
+
|
|
64
|
+
export { DisclaimerCard } from './presentation/components/DisclaimerCard';
|
|
65
|
+
export type { DisclaimerCardProps } from './presentation/components/DisclaimerCard';
|
|
66
|
+
|
|
67
|
+
export { DisclaimerModal } from './presentation/components/DisclaimerModal';
|
|
68
|
+
export type { DisclaimerModalProps } from './presentation/components/DisclaimerModal';
|
|
69
|
+
|
|
70
|
+
export { SettingsErrorBoundary } from './presentation/components/SettingsErrorBoundary';
|
|
63
71
|
|
|
64
72
|
export { CloudSyncSetting } from './presentation/components/CloudSyncSetting';
|
|
65
73
|
export type { CloudSyncSettingProps } from './presentation/components/CloudSyncSetting';
|