@umituz/react-native-settings 4.20.62 → 4.21.2
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/package.json +6 -61
- package/src/domains/feedback/domain/entities/FeedbackEntity.ts +8 -8
- package/src/domains/gamification/components/AchievementCard.tsx +142 -0
- package/src/domains/gamification/components/AchievementItem.tsx +182 -0
- package/src/domains/gamification/components/AchievementToast.tsx +122 -0
- package/src/domains/gamification/components/GamificationScreen/AchievementsList.tsx +84 -0
- package/src/domains/gamification/components/GamificationScreen/Header.tsx +29 -0
- package/src/domains/gamification/components/GamificationScreen/StatsGrid.tsx +51 -0
- package/src/domains/gamification/components/GamificationScreen/index.tsx +111 -0
- package/src/domains/gamification/components/GamificationScreen/styles.ts +43 -0
- package/src/domains/gamification/components/GamificationScreen/types.ts +77 -0
- package/src/domains/gamification/components/GamificationScreenWrapper.tsx +4 -4
- package/src/domains/gamification/components/GamificationSettingsItem.tsx +1 -1
- package/src/domains/gamification/components/LevelProgress.tsx +129 -0
- package/src/domains/gamification/components/PointsBadge.tsx +60 -0
- package/src/domains/gamification/components/StatsCard.tsx +89 -0
- package/src/domains/gamification/components/StreakDisplay.tsx +119 -0
- package/src/domains/gamification/components/index.ts +13 -0
- package/src/domains/gamification/examples/gamification.config.example.ts +1 -1
- package/src/domains/gamification/hooks/useGamification.ts +91 -0
- package/src/domains/gamification/index.ts +46 -19
- package/src/domains/gamification/store/gamificationStore.ts +162 -0
- package/src/domains/gamification/types/index.ts +95 -23
- package/src/domains/gamification/types/settings.ts +28 -0
- package/src/domains/gamification/utils/calculations.ts +85 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -51
- package/.github/ISSUE_TEMPLATE/documentation.md +0 -52
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -63
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -84
- package/AI_AGENT_GUIDELINES.md +0 -367
- package/ARCHITECTURE.md +0 -246
- package/CHANGELOG.md +0 -67
- package/CODE_OF_CONDUCT.md +0 -75
- package/CONTRIBUTING.md +0 -107
- package/DOCUMENTATION_MIGRATION.md +0 -319
- package/DOCUMENTATION_TEMPLATE.md +0 -155
- package/SECURITY.md +0 -98
- package/SETTINGS_SCREEN_GUIDE.md +0 -185
- package/TESTING.md +0 -358
- package/src/__tests__/integration.test.tsx +0 -371
- package/src/__tests__/performance.test.tsx +0 -369
- package/src/__tests__/setup.test.tsx +0 -20
- package/src/__tests__/setup.ts +0 -154
- package/src/domains/about/__tests__/integration.test.tsx +0 -328
- package/src/domains/about/__tests__/types.d.ts +0 -5
- package/src/domains/about/domain/entities/__tests__/AppInfo.test.ts +0 -93
- package/src/domains/about/infrastructure/repositories/__tests__/AboutRepository.test.ts +0 -153
- package/src/domains/about/presentation/components/__tests__/AboutContent.simple.test.tsx +0 -178
- package/src/domains/about/presentation/components/__tests__/AboutContent.test.tsx +0 -293
- package/src/domains/about/presentation/components/__tests__/AboutHeader.test.tsx +0 -201
- package/src/domains/about/presentation/components/__tests__/AboutSettingItem.test.tsx +0 -71
- package/src/domains/about/presentation/hooks/__tests__/useAboutInfo.simple.test.tsx +0 -229
- package/src/domains/about/presentation/hooks/__tests__/useAboutInfo.test.tsx +0 -240
- package/src/domains/about/presentation/screens/__tests__/AboutScreen.simple.test.tsx +0 -199
- package/src/domains/about/presentation/screens/__tests__/AboutScreen.test.tsx +0 -366
- package/src/domains/about/utils/__tests__/index.test.ts +0 -408
- package/src/domains/appearance/__tests__/components/AppearanceScreen.test.tsx +0 -195
- package/src/domains/appearance/__tests__/hooks/index.test.tsx +0 -232
- package/src/domains/appearance/__tests__/integration/index.test.tsx +0 -207
- package/src/domains/appearance/__tests__/services/appearanceService.test.ts +0 -299
- package/src/domains/appearance/__tests__/setup.ts +0 -88
- package/src/domains/appearance/__tests__/stores/appearanceStore.test.tsx +0 -175
- package/src/domains/cloud-sync/presentation/components/__tests__/CloudSyncSetting.test.tsx +0 -78
- package/src/domains/legal/__tests__/ContentValidationService.test.ts +0 -195
- package/src/domains/legal/__tests__/StyleCacheService.test.ts +0 -110
- package/src/domains/legal/__tests__/UrlHandlerService.test.ts +0 -71
- package/src/domains/legal/__tests__/setup.ts +0 -82
- package/src/presentation/components/__tests__/SettingsErrorBoundary.test.tsx +0 -186
- package/src/presentation/screens/__tests__/SettingsScreen.test.tsx +0 -322
- package/src/presentation/screens/hooks/__tests__/useFeatureDetection.test.tsx +0 -261
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for AboutSettingItem component
|
|
3
|
-
*/
|
|
4
|
-
import React from 'react';
|
|
5
|
-
import renderer from 'react-test-renderer';
|
|
6
|
-
import { AboutSettingItem } from '../AboutSettingItem';
|
|
7
|
-
|
|
8
|
-
describe('AboutSettingItem', () => {
|
|
9
|
-
const defaultProps = {
|
|
10
|
-
title: 'Test Title',
|
|
11
|
-
testID: 'test-item',
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
it('should render correctly with minimal props', () => {
|
|
15
|
-
const component = renderer.create(<AboutSettingItem {...defaultProps} />);
|
|
16
|
-
const root = component.root;
|
|
17
|
-
|
|
18
|
-
// Check if title is rendered
|
|
19
|
-
// Note: implementation details might vary, finding by text content is safer
|
|
20
|
-
const textNodes = root.findAllByType('span'); // Text mocks to span in setup
|
|
21
|
-
const titleNode = textNodes.find(n => n.props.children === 'Test Title');
|
|
22
|
-
expect(titleNode).toBeDefined();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should render with description', () => {
|
|
26
|
-
const component = renderer.create(
|
|
27
|
-
<AboutSettingItem {...defaultProps} description="Test Description" />
|
|
28
|
-
);
|
|
29
|
-
const root = component.root;
|
|
30
|
-
const textNodes = root.findAllByType('span');
|
|
31
|
-
const descNode = textNodes.find(n => n.props.children === 'Test Description');
|
|
32
|
-
expect(descNode).toBeDefined();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should render with value', () => {
|
|
36
|
-
const component = renderer.create(
|
|
37
|
-
<AboutSettingItem {...defaultProps} value="Test Value" />
|
|
38
|
-
);
|
|
39
|
-
const root = component.root;
|
|
40
|
-
const textNodes = root.findAllByType('span');
|
|
41
|
-
const valueNode = textNodes.find(n => n.props.children === 'Test Value');
|
|
42
|
-
expect(valueNode).toBeDefined();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should render chevron when onPress is provided', () => {
|
|
46
|
-
const component = renderer.create(
|
|
47
|
-
<AboutSettingItem {...defaultProps} onPress={jest.fn()} />
|
|
48
|
-
);
|
|
49
|
-
const root = component.root;
|
|
50
|
-
const textNodes = root.findAllByType('span');
|
|
51
|
-
// Chevron is rendered as text '›'
|
|
52
|
-
const chevronNode = textNodes.find(n => n.props.children === '›');
|
|
53
|
-
expect(chevronNode).toBeDefined();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('should call onPress when pressed', () => {
|
|
57
|
-
const mockOnPress = jest.fn();
|
|
58
|
-
const component = renderer.create(
|
|
59
|
-
<AboutSettingItem {...defaultProps} onPress={mockOnPress} />
|
|
60
|
-
);
|
|
61
|
-
const root = component.root;
|
|
62
|
-
|
|
63
|
-
// Find the touchable element (div in mock)
|
|
64
|
-
const touchable = root.findByProps({ 'data-testid': 'test-item' });
|
|
65
|
-
|
|
66
|
-
// Trigger onPress
|
|
67
|
-
touchable.props.onClick();
|
|
68
|
-
|
|
69
|
-
expect(mockOnPress).toHaveBeenCalledTimes(1);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simple test for useAboutInfo hook
|
|
3
|
-
*/
|
|
4
|
-
import { renderHook, act, waitFor } from '@testing-library/react';
|
|
5
|
-
import { useAboutInfo } from '../useAboutInfo';
|
|
6
|
-
import { AppInfo, AboutConfig } from '../../domain/entities/AppInfo';
|
|
7
|
-
|
|
8
|
-
// Mock repository
|
|
9
|
-
let mockAppInfoData: AppInfo | null = null;
|
|
10
|
-
|
|
11
|
-
jest.mock('../../../infrastructure/repositories/AboutRepository', () => ({
|
|
12
|
-
AboutRepository: jest.fn().mockImplementation(() => ({
|
|
13
|
-
getAppInfo: jest.fn(() => mockAppInfoData),
|
|
14
|
-
saveAppInfo: jest.fn((info: AppInfo) => {
|
|
15
|
-
mockAppInfoData = info;
|
|
16
|
-
return Promise.resolve();
|
|
17
|
-
}),
|
|
18
|
-
updateAppInfo: jest.fn((updates: Partial<AppInfo>) => {
|
|
19
|
-
if (mockAppInfoData) {
|
|
20
|
-
mockAppInfoData = { ...mockAppInfoData, ...updates };
|
|
21
|
-
}
|
|
22
|
-
return Promise.resolve();
|
|
23
|
-
}),
|
|
24
|
-
destroy: jest.fn(),
|
|
25
|
-
})),
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
|
-
describe('useAboutInfo', () => {
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
mockAppInfoData = null;
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const mockAppInfo: AppInfo = {
|
|
34
|
-
name: 'Test App',
|
|
35
|
-
version: '1.0.0',
|
|
36
|
-
description: 'Test Description',
|
|
37
|
-
developer: 'Test Developer',
|
|
38
|
-
contactEmail: 'test@example.com',
|
|
39
|
-
websiteUrl: 'https://example.com',
|
|
40
|
-
websiteDisplay: 'example.com',
|
|
41
|
-
moreAppsUrl: 'https://apps.example.com',
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const mockConfig: AboutConfig = {
|
|
45
|
-
appInfo: mockAppInfo,
|
|
46
|
-
actions: {
|
|
47
|
-
onEmailPress: jest.fn(),
|
|
48
|
-
onWebsitePress: jest.fn(),
|
|
49
|
-
onMoreAppsPress: jest.fn(),
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
it('should initialize with default state', () => {
|
|
54
|
-
const { result } = renderHook(() => useAboutInfo());
|
|
55
|
-
|
|
56
|
-
expect(result.current.appInfo).toBeNull();
|
|
57
|
-
expect(result.current.loading).toBe(false);
|
|
58
|
-
expect(result.current.error).toBeNull();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should initialize with config', async () => {
|
|
62
|
-
const { result } = renderHook(() => useAboutInfo({ initialConfig: mockConfig, autoInit: undefined }));
|
|
63
|
-
|
|
64
|
-
// Wait for useEffect to run
|
|
65
|
-
await act(async () => {
|
|
66
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
expect(result.current.appInfo).toEqual(mockAppInfo);
|
|
70
|
-
expect(result.current.loading).toBe(false);
|
|
71
|
-
expect(result.current.error).toBeNull();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('should handle auto initialization', async () => {
|
|
75
|
-
const { result } = renderHook(() =>
|
|
76
|
-
useAboutInfo({ autoInit: true, initialConfig: mockConfig })
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
expect(result.current.loading).toBe(true);
|
|
80
|
-
|
|
81
|
-
await act(async () => {
|
|
82
|
-
// Wait for initialization to complete
|
|
83
|
-
await new Promise(resolve => setTimeout(resolve, 10));
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
expect(result.current.appInfo).toEqual(mockAppInfo);
|
|
87
|
-
expect(result.current.loading).toBe(false);
|
|
88
|
-
expect(result.current.error).toBeNull();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('should handle manual initialization', async () => {
|
|
92
|
-
const { result } = renderHook(() => useAboutInfo());
|
|
93
|
-
|
|
94
|
-
await act(async () => {
|
|
95
|
-
await result.current.initialize(mockConfig);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
expect(result.current.appInfo).toEqual(mockAppInfo);
|
|
99
|
-
expect(result.current.loading).toBe(false);
|
|
100
|
-
expect(result.current.error).toBeNull();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should handle update', async () => {
|
|
104
|
-
const { result } = renderHook(() => useAboutInfo({ initialConfig: mockConfig }));
|
|
105
|
-
|
|
106
|
-
await act(async () => {
|
|
107
|
-
// Wait for initialization to complete
|
|
108
|
-
await new Promise(resolve => setTimeout(resolve, 10));
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
const updatedConfig = {
|
|
112
|
-
...mockConfig,
|
|
113
|
-
appInfo: { ...mockAppInfo, name: 'Updated App' }
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
await act(async () => {
|
|
117
|
-
await result.current.update(updatedConfig);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
expect(result.current.appInfo?.name).toBe('Updated App');
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('should handle reset', async () => {
|
|
124
|
-
const { result } = renderHook(() => useAboutInfo({ initialConfig: mockConfig, autoInit: undefined }));
|
|
125
|
-
|
|
126
|
-
await waitFor(() => {
|
|
127
|
-
expect(result.current.appInfo).toEqual(mockAppInfo);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
act(() => {
|
|
131
|
-
result.current.reset();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
expect(result.current.appInfo).toBeNull();
|
|
135
|
-
expect(result.current.loading).toBe(false);
|
|
136
|
-
expect(result.current.error).toBeNull();
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('should handle refresh', async () => {
|
|
140
|
-
const { result } = renderHook(() =>
|
|
141
|
-
useAboutInfo({ autoInit: true, initialConfig: mockConfig })
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
await act(async () => {
|
|
145
|
-
// Wait for initialization to complete
|
|
146
|
-
await new Promise(resolve => setTimeout(resolve, 10));
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
// Refresh should not throw
|
|
150
|
-
await act(async () => {
|
|
151
|
-
await result.current.refresh();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Should not crash and should have some app info state
|
|
155
|
-
expect(result.current).toBeDefined();
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('should refresh app info', async () => {
|
|
159
|
-
const { result } = renderHook(() => useAboutInfo());
|
|
160
|
-
|
|
161
|
-
await act(async () => {
|
|
162
|
-
await result.current.initialize(mockConfig);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
await act(async () => {
|
|
166
|
-
await result.current.refresh();
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// Should still have app info after refresh
|
|
170
|
-
expect(result.current.appInfo).toBeTruthy();
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('should handle errors during initialization', async () => {
|
|
174
|
-
const { result } = renderHook(() =>
|
|
175
|
-
useAboutInfo({ autoInit: true, initialConfig: null as unknown })
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
await act(async () => {
|
|
179
|
-
// Wait for initialization to complete
|
|
180
|
-
await new Promise(resolve => setTimeout(resolve, 10));
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// With null config, no initialization occurs, so no error is set
|
|
184
|
-
expect(result.current.error).toBeNull();
|
|
185
|
-
expect(result.current.loading).toBe(false);
|
|
186
|
-
expect(result.current.appInfo).toBeNull();
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('should handle errors during update', async () => {
|
|
190
|
-
const { result } = renderHook(() => useAboutInfo({ initialConfig: mockConfig }));
|
|
191
|
-
|
|
192
|
-
await act(async () => {
|
|
193
|
-
await result.current.update(null as unknown);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
expect(result.current.error).toBeTruthy();
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should memoize values', () => {
|
|
200
|
-
const { result, rerender } = renderHook(() => useAboutInfo({ initialConfig: mockConfig }));
|
|
201
|
-
|
|
202
|
-
const initialAppInfo = result.current.appInfo;
|
|
203
|
-
const initialLoading = result.current.loading;
|
|
204
|
-
const initialError = result.current.error;
|
|
205
|
-
|
|
206
|
-
rerender();
|
|
207
|
-
|
|
208
|
-
expect(result.current.appInfo).toBe(initialAppInfo);
|
|
209
|
-
expect(result.current.loading).toBe(initialLoading);
|
|
210
|
-
expect(result.current.error).toBe(initialError);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it('should handle update on unmounted component', async () => {
|
|
214
|
-
const { result, unmount } = renderHook(() =>
|
|
215
|
-
useAboutInfo({ autoInit: true, initialConfig: mockConfig })
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
unmount();
|
|
219
|
-
|
|
220
|
-
// Should not crash when updating after unmount
|
|
221
|
-
await act(async () => {
|
|
222
|
-
try {
|
|
223
|
-
await result.current.update(mockConfig);
|
|
224
|
-
} catch (error) {
|
|
225
|
-
// Expected to not crash
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
});
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for useAboutInfo hook
|
|
3
|
-
*/
|
|
4
|
-
import '../../../types/global.d.ts';
|
|
5
|
-
import { renderHook, act, waitFor } from '@testing-library/react';
|
|
6
|
-
import { useAboutInfo } from '../useAboutInfo';
|
|
7
|
-
import { AboutConfig } from '../../../domain/entities/AppInfo';
|
|
8
|
-
|
|
9
|
-
describe('useAboutInfo', () => {
|
|
10
|
-
const mockConfig: AboutConfig = {
|
|
11
|
-
appInfo: {
|
|
12
|
-
name: 'Test App',
|
|
13
|
-
version: '1.0.0',
|
|
14
|
-
description: 'Test Description',
|
|
15
|
-
developer: 'Test Developer',
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
jest.clearAllMocks();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe('Initial State', () => {
|
|
24
|
-
it('should return initial state', () => {
|
|
25
|
-
const { result } = renderHook(() => useAboutInfo());
|
|
26
|
-
|
|
27
|
-
expect(result.current.appInfo).toBeNull();
|
|
28
|
-
expect(result.current.loading).toBe(false);
|
|
29
|
-
expect(result.current.error).toBeNull();
|
|
30
|
-
expect(typeof result.current.initialize).toBe('function');
|
|
31
|
-
expect(typeof result.current.updateAppInfo).toBe('function');
|
|
32
|
-
expect(typeof result.current.reset).toBe('function');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should auto-initialize when autoInit is true', async () => {
|
|
36
|
-
const { result } = renderHook(() =>
|
|
37
|
-
useAboutInfo({ autoInit: true, initialConfig: mockConfig })
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
expect(result.current.loading).toBe(true);
|
|
41
|
-
|
|
42
|
-
await waitFor(() => {
|
|
43
|
-
expect(result.current.loading).toBe(false);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
expect(result.current.appInfo).toEqual({
|
|
47
|
-
name: 'Test App',
|
|
48
|
-
version: '1.0.0',
|
|
49
|
-
description: 'Test Description',
|
|
50
|
-
developer: 'Test Developer',
|
|
51
|
-
contactEmail: undefined,
|
|
52
|
-
websiteUrl: undefined,
|
|
53
|
-
websiteDisplay: undefined,
|
|
54
|
-
moreAppsUrl: undefined,
|
|
55
|
-
privacyPolicyUrl: undefined,
|
|
56
|
-
termsOfServiceUrl: undefined,
|
|
57
|
-
});
|
|
58
|
-
expect(result.current.loading).toBe(false);
|
|
59
|
-
expect(result.current.error).toBeNull();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should not auto-initialize when autoInit is false', () => {
|
|
63
|
-
const { result } = renderHook(() =>
|
|
64
|
-
useAboutInfo({ autoInit: false, initialConfig: mockConfig })
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
expect(result.current.appInfo).toBeNull();
|
|
68
|
-
expect(result.current.loading).toBe(false);
|
|
69
|
-
expect(result.current.error).toBeNull();
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
describe('initialize', () => {
|
|
74
|
-
it('should initialize with config', async () => {
|
|
75
|
-
const { result } = renderHook(() => useAboutInfo());
|
|
76
|
-
|
|
77
|
-
await act(async () => {
|
|
78
|
-
await result.current.initialize(mockConfig);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
expect(result.current.appInfo).toEqual({
|
|
82
|
-
name: 'Test App',
|
|
83
|
-
version: '1.0.0',
|
|
84
|
-
description: 'Test Description',
|
|
85
|
-
developer: 'Test Developer',
|
|
86
|
-
contactEmail: undefined,
|
|
87
|
-
websiteUrl: undefined,
|
|
88
|
-
websiteDisplay: undefined,
|
|
89
|
-
moreAppsUrl: undefined,
|
|
90
|
-
privacyPolicyUrl: undefined,
|
|
91
|
-
termsOfServiceUrl: undefined,
|
|
92
|
-
});
|
|
93
|
-
expect(result.current.loading).toBe(false);
|
|
94
|
-
expect(result.current.error).toBeNull();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should not initialize multiple times', async () => {
|
|
98
|
-
const { result } = renderHook(() => useAboutInfo());
|
|
99
|
-
|
|
100
|
-
await act(async () => {
|
|
101
|
-
await result.current.initialize(mockConfig);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
const firstAppInfo = result.current.appInfo;
|
|
105
|
-
|
|
106
|
-
await act(async () => {
|
|
107
|
-
await result.current.initialize({
|
|
108
|
-
appInfo: { name: 'Different App', version: '2.0.0' }
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
expect(result.current.appInfo).toEqual(firstAppInfo);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should handle initialization errors', async () => {
|
|
116
|
-
const { result } = renderHook(() => useAboutInfo());
|
|
117
|
-
|
|
118
|
-
await act(async () => {
|
|
119
|
-
await result.current.initialize(null as unknown);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
expect(result.current.appInfo).toBeNull();
|
|
123
|
-
expect(result.current.loading).toBe(false);
|
|
124
|
-
expect(result.current.error).toBeTruthy();
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
describe('updateAppInfo', () => {
|
|
129
|
-
beforeEach(async () => {
|
|
130
|
-
const { result } = renderHook(() =>
|
|
131
|
-
useAboutInfo({ autoInit: true, initialConfig: mockConfig })
|
|
132
|
-
);
|
|
133
|
-
await waitFor(() => {
|
|
134
|
-
expect(result.current.loading).toBe(false);
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('should update app info', async () => {
|
|
139
|
-
const { result } = renderHook(() =>
|
|
140
|
-
useAboutInfo({ autoInit: true, initialConfig: mockConfig })
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
// Wait for initialization
|
|
144
|
-
await act(async () => {
|
|
145
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
await act(async () => {
|
|
149
|
-
await result.current.updateAppInfo({ name: 'Updated App', version: '2.0.0' });
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
expect(result.current.appInfo?.name).toBe('Updated App');
|
|
153
|
-
expect(result.current.appInfo?.version).toBe('2.0.0');
|
|
154
|
-
expect(result.current.appInfo?.description).toBe('Test Description'); // Unchanged
|
|
155
|
-
expect(result.current.loading).toBe(false);
|
|
156
|
-
expect(result.current.error).toBeNull();
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('should handle update when not initialized', async () => {
|
|
160
|
-
const { result } = renderHook(() => useAboutInfo());
|
|
161
|
-
|
|
162
|
-
await act(async () => {
|
|
163
|
-
await result.current.updateAppInfo({ name: 'Updated App' });
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
expect(result.current.appInfo).toBeNull();
|
|
167
|
-
expect(result.current.error).toBe('App info not initialized');
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('should handle update errors', async () => {
|
|
171
|
-
const { result } = renderHook(() =>
|
|
172
|
-
useAboutInfo({ autoInit: true, initialConfig: mockConfig })
|
|
173
|
-
);
|
|
174
|
-
await waitFor(() => {
|
|
175
|
-
expect(result.current.loading).toBe(false);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
await act(async () => {
|
|
179
|
-
await result.current.updateAppInfo(null as unknown);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
expect(result.current.error).toBeTruthy();
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
describe('reset', () => {
|
|
187
|
-
it('should reset state', async () => {
|
|
188
|
-
const { result } = renderHook(() =>
|
|
189
|
-
useAboutInfo({ autoInit: true, initialConfig: mockConfig })
|
|
190
|
-
);
|
|
191
|
-
await waitFor(() => {
|
|
192
|
-
expect(result.current.loading).toBe(false);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
expect(result.current.appInfo).toBeTruthy();
|
|
196
|
-
|
|
197
|
-
act(() => {
|
|
198
|
-
result.current.reset();
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
expect(result.current.appInfo).toBeNull();
|
|
202
|
-
expect(result.current.loading).toBe(false);
|
|
203
|
-
expect(result.current.error).toBeNull();
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
describe('Memory Leak Prevention', () => {
|
|
208
|
-
it('should handle unmounted component', async () => {
|
|
209
|
-
const { result, unmount } = renderHook(() => useAboutInfo());
|
|
210
|
-
|
|
211
|
-
// Start initialization
|
|
212
|
-
const initPromise = act(async () => {
|
|
213
|
-
await result.current.initialize(mockConfig);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
// Unmount before initialization completes
|
|
217
|
-
unmount();
|
|
218
|
-
|
|
219
|
-
// Should not throw error
|
|
220
|
-
await initPromise;
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('should handle update on unmounted component', async () => {
|
|
224
|
-
const { result, unmount } = renderHook(() =>
|
|
225
|
-
useAboutInfo({ autoInit: true, initialConfig: mockConfig })
|
|
226
|
-
);
|
|
227
|
-
await waitFor(() => {
|
|
228
|
-
expect(result.current.loading).toBe(false);
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
unmount();
|
|
232
|
-
|
|
233
|
-
// Should not throw error
|
|
234
|
-
await act(async () => {
|
|
235
|
-
await result.current.updateAppInfo({ name: 'Updated App' });
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
});
|