@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,328 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for the entire About package
|
|
3
|
-
*/
|
|
4
|
-
import '../types/global.d.ts';
|
|
5
|
-
import './types.d.ts';
|
|
6
|
-
import React from 'react';
|
|
7
|
-
import { View, Text } from 'react-native';
|
|
8
|
-
import { render, waitFor, fireEvent } from '@testing-library/react';
|
|
9
|
-
import { AboutScreen } from '../presentation/screens/AboutScreen';
|
|
10
|
-
import { AboutConfig } from '../domain/entities/AppInfo';
|
|
11
|
-
|
|
12
|
-
// Mock console methods
|
|
13
|
-
const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation();
|
|
14
|
-
const mockConsoleError = jest.spyOn(console, 'error').mockImplementation();
|
|
15
|
-
|
|
16
|
-
describe('About Package Integration', () => {
|
|
17
|
-
const fullConfig: AboutConfig = {
|
|
18
|
-
appInfo: {
|
|
19
|
-
name: 'Integration Test App',
|
|
20
|
-
version: '2.0.0',
|
|
21
|
-
description: 'This is an integration test app',
|
|
22
|
-
developer: 'Test Developer',
|
|
23
|
-
contactEmail: 'integration@test.com',
|
|
24
|
-
websiteUrl: 'https://integration.example.com',
|
|
25
|
-
websiteDisplay: 'integration.example.com',
|
|
26
|
-
moreAppsUrl: 'https://apps.integration.example.com',
|
|
27
|
-
},
|
|
28
|
-
theme: {
|
|
29
|
-
primary: '#FF0000',
|
|
30
|
-
secondary: '#00FF00',
|
|
31
|
-
background: '#0000FF',
|
|
32
|
-
text: '#FFFFFF',
|
|
33
|
-
border: '#CCCCCC',
|
|
34
|
-
},
|
|
35
|
-
style: {
|
|
36
|
-
containerStyle: { backgroundColor: '#f0f0f0' },
|
|
37
|
-
itemStyle: { padding: 20 },
|
|
38
|
-
textStyle: { fontSize: 18 },
|
|
39
|
-
iconStyle: { color: '#333333' },
|
|
40
|
-
},
|
|
41
|
-
actions: {
|
|
42
|
-
onWebsitePress: jest.fn(),
|
|
43
|
-
onEmailPress: jest.fn(),
|
|
44
|
-
onMoreAppsPress: jest.fn(),
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
beforeEach(() => {
|
|
49
|
-
jest.clearAllMocks();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
afterEach(() => {
|
|
53
|
-
mockConsoleLog.mockClear();
|
|
54
|
-
mockConsoleError.mockClear();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe('Complete Flow', () => {
|
|
58
|
-
it('should render complete about screen with all features', async () => {
|
|
59
|
-
const { getByText, queryByText } = render(
|
|
60
|
-
<AboutScreen config={fullConfig} />
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
// Should show loading initially
|
|
64
|
-
expect(getByText((content) => content.includes('Loading'))).toBeTruthy();
|
|
65
|
-
|
|
66
|
-
// Wait for content to load
|
|
67
|
-
await waitFor(() => {
|
|
68
|
-
expect(queryByText((content) => content.includes('Loading'))).toBeFalsy();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// Check header content
|
|
72
|
-
expect(getByText('Integration Test App')).toBeTruthy();
|
|
73
|
-
expect(getByText('Version 2.0.0')).toBeTruthy();
|
|
74
|
-
expect(getByText('This is an integration test app')).toBeTruthy();
|
|
75
|
-
|
|
76
|
-
// Check content items
|
|
77
|
-
expect(getByText('Developer')).toBeTruthy();
|
|
78
|
-
expect(getByText('Test Developer')).toBeTruthy();
|
|
79
|
-
expect(getByText('Contact')).toBeTruthy();
|
|
80
|
-
expect(getByText('integration@test.com')).toBeTruthy();
|
|
81
|
-
expect(getByText('Website')).toBeTruthy();
|
|
82
|
-
expect(getByText('integration.example.com')).toBeTruthy();
|
|
83
|
-
expect(getByText('More Apps')).toBeTruthy();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should handle all user interactions', async () => {
|
|
87
|
-
const { getByTestId } = render(
|
|
88
|
-
<AboutScreen config={fullConfig} />
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
// Wait for content to load
|
|
92
|
-
await waitFor(() => {
|
|
93
|
-
expect(getByTestId('email-item')).toBeTruthy();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// Test all interactions
|
|
97
|
-
fireEvent.click(getByTestId('email-item'));
|
|
98
|
-
fireEvent.click(getByTestId('website-item'));
|
|
99
|
-
fireEvent.click(getByTestId('more-apps-item'));
|
|
100
|
-
|
|
101
|
-
// Verify all actions were called
|
|
102
|
-
expect(fullConfig.actions!.onEmailPress).toHaveBeenCalledTimes(1);
|
|
103
|
-
expect(fullConfig.actions!.onWebsitePress).toHaveBeenCalledTimes(1);
|
|
104
|
-
expect(fullConfig.actions!.onMoreAppsPress).toHaveBeenCalledTimes(1);
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
describe('Minimal Configuration', () => {
|
|
109
|
-
it('should work with minimal config', async () => {
|
|
110
|
-
const minimalConfig: AboutConfig = {
|
|
111
|
-
appInfo: {
|
|
112
|
-
name: 'Minimal App',
|
|
113
|
-
version: '1.0.0',
|
|
114
|
-
},
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const { getByText, queryByText } = render(
|
|
118
|
-
<AboutScreen config={minimalConfig} />
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
await waitFor(() => {
|
|
122
|
-
expect(queryByText((content) => content.includes('Loading'))).toBeFalsy();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// Should show basic info
|
|
126
|
-
expect(getByText('Minimal App')).toBeTruthy();
|
|
127
|
-
expect(getByText('Version 1.0.0')).toBeTruthy();
|
|
128
|
-
|
|
129
|
-
// Should not show optional items
|
|
130
|
-
expect(queryByText('Developer')).toBeFalsy();
|
|
131
|
-
expect(queryByText('Contact')).toBeFalsy();
|
|
132
|
-
expect(queryByText('Website')).toBeFalsy();
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
describe('Custom Components', () => {
|
|
137
|
-
it('should render custom header and footer', async () => {
|
|
138
|
-
const CustomHeader = () => <View testID="custom-header"><Text>Custom Header</Text></View>;
|
|
139
|
-
const CustomFooter = () => <View testID="custom-footer"><Text>Custom Footer</Text></View>;
|
|
140
|
-
|
|
141
|
-
const { getByTestId, queryByText } = render(
|
|
142
|
-
<AboutScreen
|
|
143
|
-
config={fullConfig}
|
|
144
|
-
headerComponent={<CustomHeader />}
|
|
145
|
-
footerComponent={<CustomFooter />}
|
|
146
|
-
showHeader={false}
|
|
147
|
-
/>
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
await waitFor(() => {
|
|
151
|
-
expect(queryByText((content) => content.includes('Loading'))).toBeFalsy();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Should show custom components
|
|
155
|
-
expect(getByTestId('custom-header')).toBeTruthy();
|
|
156
|
-
expect(getByTestId('custom-footer')).toBeTruthy();
|
|
157
|
-
|
|
158
|
-
// Should not show default header
|
|
159
|
-
expect(queryByText('Integration Test App')).toBeFalsy();
|
|
160
|
-
expect(queryByText('Version 2.0.0')).toBeFalsy();
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
describe('Error Handling', () => {
|
|
165
|
-
it('should handle invalid config gracefully', async () => {
|
|
166
|
-
const { queryByText, getByText } = render(
|
|
167
|
-
<AboutScreen config={null as unknown} />
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
await waitFor(() => {
|
|
171
|
-
expect(queryByText((content) => content.includes('Loading'))).toBeFalsy();
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
expect(getByText('No app information available')).toBeTruthy();
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('should handle missing actions gracefully', async () => {
|
|
178
|
-
const configWithoutActions: AboutConfig = {
|
|
179
|
-
appInfo: fullConfig.appInfo,
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const { getByTestId } = render(
|
|
183
|
-
<AboutScreen config={configWithoutActions} />
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
await waitFor(() => {
|
|
187
|
-
expect(getByTestId('email-item')).toBeTruthy();
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
// Should not crash when pressing items without actions
|
|
191
|
-
expect(() => {
|
|
192
|
-
fireEvent.click(getByTestId('email-item'));
|
|
193
|
-
fireEvent.click(getByTestId('website-item'));
|
|
194
|
-
}).not.toThrow();
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
describe('Performance', () => {
|
|
199
|
-
it('should handle rapid re-renders', async () => {
|
|
200
|
-
const { rerender, getByText, queryByText } = render(
|
|
201
|
-
<AboutScreen config={fullConfig} />
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
await waitFor(() => {
|
|
205
|
-
expect(queryByText((content) => content.includes('Loading'))).toBeFalsy();
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
// Rapid re-renders with different configs
|
|
209
|
-
for (let i = 0; i < 5; i++) {
|
|
210
|
-
const newConfig = {
|
|
211
|
-
...fullConfig,
|
|
212
|
-
appInfo: {
|
|
213
|
-
...fullConfig.appInfo,
|
|
214
|
-
name: `App ${i}`,
|
|
215
|
-
},
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
rerender(<AboutScreen config={newConfig} />);
|
|
219
|
-
|
|
220
|
-
await waitFor(() => {
|
|
221
|
-
expect(getByText(`App ${i}`)).toBeTruthy();
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Should not crash and should show final state
|
|
226
|
-
expect(getByText('App 4')).toBeTruthy();
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it('should handle prop changes efficiently', async () => {
|
|
230
|
-
const { rerender, getByText, queryByText } = render(
|
|
231
|
-
<AboutScreen config={fullConfig} />
|
|
232
|
-
);
|
|
233
|
-
|
|
234
|
-
await waitFor(() => {
|
|
235
|
-
expect(queryByText((content) => content.includes('Loading'))).toBeFalsy();
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
// Change showHeader prop
|
|
239
|
-
rerender(<AboutScreen config={fullConfig} showHeader={false} />);
|
|
240
|
-
|
|
241
|
-
await waitFor(() => {
|
|
242
|
-
expect(queryByText('Integration Test App')).toBeFalsy();
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
// Change back
|
|
246
|
-
rerender(<AboutScreen config={fullConfig} showHeader={true} />);
|
|
247
|
-
|
|
248
|
-
await waitFor(() => {
|
|
249
|
-
expect(getByText('Integration Test App')).toBeTruthy();
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
describe('Memory Management', () => {
|
|
255
|
-
it('should cleanup properly on unmount', async () => {
|
|
256
|
-
const { unmount, queryByText } = render(
|
|
257
|
-
<AboutScreen config={fullConfig} />
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
await waitFor(() => {
|
|
261
|
-
expect(queryByText((content) => content.includes('Loading'))).toBeFalsy();
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// Should not throw when unmounting
|
|
265
|
-
expect(() => {
|
|
266
|
-
unmount();
|
|
267
|
-
}).not.toThrow();
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it('should handle unmount during loading', () => {
|
|
271
|
-
const { unmount } = render(
|
|
272
|
-
<AboutScreen config={fullConfig} />
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
// Should not throw when unmounting during loading
|
|
276
|
-
expect(() => {
|
|
277
|
-
unmount();
|
|
278
|
-
}).not.toThrow();
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
describe('Accessibility', () => {
|
|
283
|
-
it('should have proper test IDs', async () => {
|
|
284
|
-
const { getByTestId } = render(
|
|
285
|
-
<AboutScreen config={fullConfig} testID="about-screen" />
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
await waitFor(() => {
|
|
289
|
-
expect(getByTestId('about-screen')).toBeTruthy();
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
// Check for item test IDs
|
|
293
|
-
expect(getByTestId('developer-item')).toBeTruthy();
|
|
294
|
-
expect(getByTestId('email-item')).toBeTruthy();
|
|
295
|
-
expect(getByTestId('website-item')).toBeTruthy();
|
|
296
|
-
expect(getByTestId('more-apps-item')).toBeTruthy();
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
describe('Console Logging', () => {
|
|
301
|
-
it('should log in development mode', async () => {
|
|
302
|
-
const originalDev = global.__DEV__;
|
|
303
|
-
global.__DEV__ = true;
|
|
304
|
-
|
|
305
|
-
render(<AboutScreen config={fullConfig} />);
|
|
306
|
-
|
|
307
|
-
await waitFor(() => {
|
|
308
|
-
expect(mockConsoleLog).toHaveBeenCalled();
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
global.__DEV__ = originalDev;
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
it('should not log in production mode', async () => {
|
|
315
|
-
const originalDev = global.__DEV__;
|
|
316
|
-
global.__DEV__ = false;
|
|
317
|
-
|
|
318
|
-
render(<AboutScreen config={fullConfig} />);
|
|
319
|
-
|
|
320
|
-
await waitFor(() => {
|
|
321
|
-
// Wait a bit to ensure no logging
|
|
322
|
-
expect(mockConsoleLog).not.toHaveBeenCalled();
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
global.__DEV__ = originalDev;
|
|
326
|
-
});
|
|
327
|
-
});
|
|
328
|
-
});
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for AppInfo entity
|
|
3
|
-
*/
|
|
4
|
-
import { AppInfo } from '../AppInfo';
|
|
5
|
-
|
|
6
|
-
describe('AppInfo Entity', () => {
|
|
7
|
-
const validAppInfo: AppInfo = {
|
|
8
|
-
name: 'Test App',
|
|
9
|
-
version: '1.0.0',
|
|
10
|
-
description: 'Test Description',
|
|
11
|
-
developer: 'Test Developer',
|
|
12
|
-
contactEmail: 'test@example.com',
|
|
13
|
-
websiteUrl: 'https://example.com',
|
|
14
|
-
websiteDisplay: 'example.com',
|
|
15
|
-
moreAppsUrl: 'https://apps.example.com',
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
describe('Validation', () => {
|
|
19
|
-
it('should accept valid AppInfo', () => {
|
|
20
|
-
expect(() => {
|
|
21
|
-
const appInfo: AppInfo = validAppInfo;
|
|
22
|
-
expect(appInfo.name).toBe('Test App');
|
|
23
|
-
expect(appInfo.version).toBe('1.0.0');
|
|
24
|
-
}).not.toThrow();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should accept minimal AppInfo', () => {
|
|
28
|
-
const minimalAppInfo: AppInfo = {
|
|
29
|
-
name: 'Minimal App',
|
|
30
|
-
version: '1.0.0',
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
expect(minimalAppInfo.name).toBe('Minimal App');
|
|
34
|
-
expect(minimalAppInfo.version).toBe('1.0.0');
|
|
35
|
-
expect(minimalAppInfo.description).toBeUndefined();
|
|
36
|
-
expect(minimalAppInfo.developer).toBeUndefined();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should accept AppInfo with optional fields', () => {
|
|
40
|
-
const appInfoWithOptionals: AppInfo = {
|
|
41
|
-
name: 'App',
|
|
42
|
-
version: '1.0.0',
|
|
43
|
-
description: 'Description',
|
|
44
|
-
developer: 'Developer',
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
expect(appInfoWithOptionals.description).toBe('Description');
|
|
48
|
-
expect(appInfoWithOptionals.developer).toBe('Developer');
|
|
49
|
-
expect(appInfoWithOptionals.contactEmail).toBeUndefined();
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe('Type Safety', () => {
|
|
54
|
-
it('should have correct types', () => {
|
|
55
|
-
const appInfo: AppInfo = validAppInfo;
|
|
56
|
-
|
|
57
|
-
expect(typeof appInfo.name).toBe('string');
|
|
58
|
-
expect(typeof appInfo.version).toBe('string');
|
|
59
|
-
expect(typeof appInfo.description).toBe('string');
|
|
60
|
-
expect(typeof appInfo.developer).toBe('string');
|
|
61
|
-
expect(typeof appInfo.contactEmail).toBe('string');
|
|
62
|
-
expect(typeof appInfo.websiteUrl).toBe('string');
|
|
63
|
-
expect(typeof appInfo.websiteDisplay).toBe('string');
|
|
64
|
-
expect(typeof appInfo.moreAppsUrl).toBe('string');
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('should allow undefined for optional fields', () => {
|
|
68
|
-
const appInfo: AppInfo = {
|
|
69
|
-
name: 'Test',
|
|
70
|
-
version: '1.0.0',
|
|
71
|
-
description: undefined,
|
|
72
|
-
developer: undefined,
|
|
73
|
-
contactEmail: undefined,
|
|
74
|
-
websiteUrl: undefined,
|
|
75
|
-
websiteDisplay: undefined,
|
|
76
|
-
moreAppsUrl: undefined,
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
expect(appInfo.description).toBeUndefined();
|
|
80
|
-
expect(appInfo.developer).toBeUndefined();
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe('Immutability', () => {
|
|
85
|
-
it('should be assignable but not inherently immutable', () => {
|
|
86
|
-
const appInfo: AppInfo = { ...validAppInfo };
|
|
87
|
-
|
|
88
|
-
// TypeScript allows mutation but we can test the behavior
|
|
89
|
-
appInfo.name = 'Modified App';
|
|
90
|
-
expect(appInfo.name).toBe('Modified App');
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
});
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for AboutRepository
|
|
3
|
-
*/
|
|
4
|
-
import { AboutRepository } from '../AboutRepository';
|
|
5
|
-
import { AppInfo } from '../../../domain/entities/AppInfo';
|
|
6
|
-
|
|
7
|
-
describe('AboutRepository', () => {
|
|
8
|
-
let repository: AboutRepository;
|
|
9
|
-
const mockAppInfo: AppInfo = {
|
|
10
|
-
name: 'Test App',
|
|
11
|
-
version: '1.0.0',
|
|
12
|
-
description: 'Test Description',
|
|
13
|
-
developer: 'Test Developer',
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
repository = new AboutRepository();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
afterEach(() => {
|
|
21
|
-
repository.destroy();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe('saveAppInfo', () => {
|
|
25
|
-
it('should save app info successfully', async () => {
|
|
26
|
-
await expect(repository.saveAppInfo(mockAppInfo)).resolves.not.toThrow();
|
|
27
|
-
|
|
28
|
-
const savedInfo = await repository.getAppInfo();
|
|
29
|
-
expect(savedInfo).toEqual(mockAppInfo);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should throw error for invalid input', async () => {
|
|
33
|
-
await expect(repository.saveAppInfo(null as unknown)).rejects.toThrow('Invalid app info provided');
|
|
34
|
-
await expect(repository.saveAppInfo(undefined as unknown)).rejects.toThrow('Invalid app info provided');
|
|
35
|
-
await expect(repository.saveAppInfo('invalid' as unknown)).rejects.toThrow('Invalid app info provided');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should store a copy of the data', async () => {
|
|
39
|
-
const appInfoCopy = { ...mockAppInfo };
|
|
40
|
-
await repository.saveAppInfo(mockAppInfo);
|
|
41
|
-
|
|
42
|
-
// Modify original object
|
|
43
|
-
appInfoCopy.name = 'Modified';
|
|
44
|
-
|
|
45
|
-
const savedInfo = await repository.getAppInfo();
|
|
46
|
-
expect(savedInfo.name).toBe('Test App'); // Should not be affected
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe('getAppInfo', () => {
|
|
51
|
-
it('should return saved app info', async () => {
|
|
52
|
-
await repository.saveAppInfo(mockAppInfo);
|
|
53
|
-
|
|
54
|
-
const retrievedInfo = await repository.getAppInfo();
|
|
55
|
-
expect(retrievedInfo).toEqual(mockAppInfo);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('should throw error when not initialized', async () => {
|
|
59
|
-
await expect(repository.getAppInfo()).rejects.toThrow('App info not initialized');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should return a copy of the data', async () => {
|
|
63
|
-
await repository.saveAppInfo(mockAppInfo);
|
|
64
|
-
|
|
65
|
-
const retrievedInfo = await repository.getAppInfo();
|
|
66
|
-
|
|
67
|
-
// Modify returned object
|
|
68
|
-
const modifiedInfo = { ...retrievedInfo };
|
|
69
|
-
modifiedInfo.name = 'Modified';
|
|
70
|
-
|
|
71
|
-
// Get it again to verify original is unchanged
|
|
72
|
-
const retrievedInfo2 = await repository.getAppInfo();
|
|
73
|
-
expect(retrievedInfo2.name).toBe('Test App');
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should throw error when destroyed', async () => {
|
|
77
|
-
await repository.saveAppInfo(mockAppInfo);
|
|
78
|
-
repository.destroy();
|
|
79
|
-
|
|
80
|
-
await expect(repository.getAppInfo()).rejects.toThrow('Repository has been destroyed');
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe('updateAppInfo', () => {
|
|
85
|
-
beforeEach(async () => {
|
|
86
|
-
await repository.saveAppInfo(mockAppInfo);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should update app info successfully', async () => {
|
|
90
|
-
const updates = { name: 'Updated App', version: '2.0.0' };
|
|
91
|
-
|
|
92
|
-
const updatedInfo = await repository.updateAppInfo(updates);
|
|
93
|
-
|
|
94
|
-
expect(updatedInfo.name).toBe('Updated App');
|
|
95
|
-
expect(updatedInfo.version).toBe('2.0.0');
|
|
96
|
-
expect(updatedInfo.description).toBe('Test Description'); // Unchanged
|
|
97
|
-
expect(updatedInfo.developer).toBe('Test Developer'); // Unchanged
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('should throw error when not initialized', async () => {
|
|
101
|
-
const newRepository = new AboutRepository();
|
|
102
|
-
|
|
103
|
-
await expect(newRepository.updateAppInfo({ name: 'Test' })).rejects.toThrow('App info not initialized');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should throw error for invalid updates', async () => {
|
|
107
|
-
await expect(repository.updateAppInfo(null as unknown)).rejects.toThrow('Invalid updates provided');
|
|
108
|
-
await expect(repository.updateAppInfo(undefined as unknown)).rejects.toThrow('Invalid updates provided');
|
|
109
|
-
await expect(repository.updateAppInfo('invalid' as unknown)).rejects.toThrow('Invalid updates provided');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('should return a copy of the updated data', async () => {
|
|
113
|
-
const updates = { name: 'Updated App' };
|
|
114
|
-
|
|
115
|
-
const updatedInfo = await repository.updateAppInfo(updates);
|
|
116
|
-
|
|
117
|
-
// Modify returned object
|
|
118
|
-
const modifiedInfo = { ...updatedInfo };
|
|
119
|
-
modifiedInfo.name = 'Modified';
|
|
120
|
-
|
|
121
|
-
// Get it again to verify original is unchanged
|
|
122
|
-
const updatedInfo2 = await repository.getAppInfo();
|
|
123
|
-
expect(updatedInfo2.name).toBe('Updated App');
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should throw error when destroyed', async () => {
|
|
127
|
-
repository.destroy();
|
|
128
|
-
|
|
129
|
-
await expect(repository.updateAppInfo({ name: 'Test' })).rejects.toThrow('Repository has been destroyed');
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
describe('destroy', () => {
|
|
134
|
-
it('should cleanup resources', async () => {
|
|
135
|
-
await repository.saveAppInfo(mockAppInfo);
|
|
136
|
-
|
|
137
|
-
repository.destroy();
|
|
138
|
-
|
|
139
|
-
await expect(repository.getAppInfo()).rejects.toThrow('Repository has been destroyed');
|
|
140
|
-
await expect(repository.updateAppInfo({ name: 'Test' })).rejects.toThrow('Repository has been destroyed');
|
|
141
|
-
await expect(repository.saveAppInfo(mockAppInfo)).rejects.toThrow('Repository has been destroyed');
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('should be safe to call multiple times', async () => {
|
|
145
|
-
await repository.saveAppInfo(mockAppInfo);
|
|
146
|
-
|
|
147
|
-
repository.destroy();
|
|
148
|
-
repository.destroy(); // Should not throw
|
|
149
|
-
|
|
150
|
-
await expect(repository.getAppInfo()).rejects.toThrow('Repository has been destroyed');
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
});
|