@umituz/react-native-settings 4.20.62 → 4.21.1

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 (69) hide show
  1. package/package.json +6 -61
  2. package/src/domains/gamification/components/AchievementCard.tsx +142 -0
  3. package/src/domains/gamification/components/AchievementItem.tsx +182 -0
  4. package/src/domains/gamification/components/AchievementToast.tsx +122 -0
  5. package/src/domains/gamification/components/GamificationScreen/AchievementsList.tsx +84 -0
  6. package/src/domains/gamification/components/GamificationScreen/Header.tsx +29 -0
  7. package/src/domains/gamification/components/GamificationScreen/StatsGrid.tsx +51 -0
  8. package/src/domains/gamification/components/GamificationScreen/index.tsx +111 -0
  9. package/src/domains/gamification/components/GamificationScreen/styles.ts +43 -0
  10. package/src/domains/gamification/components/GamificationScreen/types.ts +77 -0
  11. package/src/domains/gamification/components/GamificationScreenWrapper.tsx +4 -4
  12. package/src/domains/gamification/components/GamificationSettingsItem.tsx +1 -1
  13. package/src/domains/gamification/components/LevelProgress.tsx +129 -0
  14. package/src/domains/gamification/components/PointsBadge.tsx +60 -0
  15. package/src/domains/gamification/components/StatsCard.tsx +89 -0
  16. package/src/domains/gamification/components/StreakDisplay.tsx +119 -0
  17. package/src/domains/gamification/components/index.ts +13 -0
  18. package/src/domains/gamification/examples/gamification.config.example.ts +1 -1
  19. package/src/domains/gamification/hooks/useGamification.ts +91 -0
  20. package/src/domains/gamification/index.ts +46 -19
  21. package/src/domains/gamification/store/gamificationStore.ts +162 -0
  22. package/src/domains/gamification/types/index.ts +95 -23
  23. package/src/domains/gamification/types/settings.ts +28 -0
  24. package/src/domains/gamification/utils/calculations.ts +85 -0
  25. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -51
  26. package/.github/ISSUE_TEMPLATE/documentation.md +0 -52
  27. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -63
  28. package/.github/PULL_REQUEST_TEMPLATE.md +0 -84
  29. package/AI_AGENT_GUIDELINES.md +0 -367
  30. package/ARCHITECTURE.md +0 -246
  31. package/CHANGELOG.md +0 -67
  32. package/CODE_OF_CONDUCT.md +0 -75
  33. package/CONTRIBUTING.md +0 -107
  34. package/DOCUMENTATION_MIGRATION.md +0 -319
  35. package/DOCUMENTATION_TEMPLATE.md +0 -155
  36. package/SECURITY.md +0 -98
  37. package/SETTINGS_SCREEN_GUIDE.md +0 -185
  38. package/TESTING.md +0 -358
  39. package/src/__tests__/integration.test.tsx +0 -371
  40. package/src/__tests__/performance.test.tsx +0 -369
  41. package/src/__tests__/setup.test.tsx +0 -20
  42. package/src/__tests__/setup.ts +0 -154
  43. package/src/domains/about/__tests__/integration.test.tsx +0 -328
  44. package/src/domains/about/__tests__/types.d.ts +0 -5
  45. package/src/domains/about/domain/entities/__tests__/AppInfo.test.ts +0 -93
  46. package/src/domains/about/infrastructure/repositories/__tests__/AboutRepository.test.ts +0 -153
  47. package/src/domains/about/presentation/components/__tests__/AboutContent.simple.test.tsx +0 -178
  48. package/src/domains/about/presentation/components/__tests__/AboutContent.test.tsx +0 -293
  49. package/src/domains/about/presentation/components/__tests__/AboutHeader.test.tsx +0 -201
  50. package/src/domains/about/presentation/components/__tests__/AboutSettingItem.test.tsx +0 -71
  51. package/src/domains/about/presentation/hooks/__tests__/useAboutInfo.simple.test.tsx +0 -229
  52. package/src/domains/about/presentation/hooks/__tests__/useAboutInfo.test.tsx +0 -240
  53. package/src/domains/about/presentation/screens/__tests__/AboutScreen.simple.test.tsx +0 -199
  54. package/src/domains/about/presentation/screens/__tests__/AboutScreen.test.tsx +0 -366
  55. package/src/domains/about/utils/__tests__/index.test.ts +0 -408
  56. package/src/domains/appearance/__tests__/components/AppearanceScreen.test.tsx +0 -195
  57. package/src/domains/appearance/__tests__/hooks/index.test.tsx +0 -232
  58. package/src/domains/appearance/__tests__/integration/index.test.tsx +0 -207
  59. package/src/domains/appearance/__tests__/services/appearanceService.test.ts +0 -299
  60. package/src/domains/appearance/__tests__/setup.ts +0 -88
  61. package/src/domains/appearance/__tests__/stores/appearanceStore.test.tsx +0 -175
  62. package/src/domains/cloud-sync/presentation/components/__tests__/CloudSyncSetting.test.tsx +0 -78
  63. package/src/domains/legal/__tests__/ContentValidationService.test.ts +0 -195
  64. package/src/domains/legal/__tests__/StyleCacheService.test.ts +0 -110
  65. package/src/domains/legal/__tests__/UrlHandlerService.test.ts +0 -71
  66. package/src/domains/legal/__tests__/setup.ts +0 -82
  67. package/src/presentation/components/__tests__/SettingsErrorBoundary.test.tsx +0 -186
  68. package/src/presentation/screens/__tests__/SettingsScreen.test.tsx +0 -322
  69. package/src/presentation/screens/hooks/__tests__/useFeatureDetection.test.tsx +0 -261
@@ -1,199 +0,0 @@
1
- /**
2
- * Simple test for AboutScreen component
3
- */
4
- import React from 'react';
5
- import { AboutScreen } from '../AboutScreen';
6
- import { AppInfo, AboutConfig } from '../../domain/entities/AppInfo';
7
-
8
- describe('AboutScreen', () => {
9
- const mockAppInfo: AppInfo = {
10
- name: 'Test App',
11
- version: '1.0.0',
12
- description: 'Test Description',
13
- developer: 'Test Developer',
14
- contactEmail: 'test@example.com',
15
- websiteUrl: 'https://example.com',
16
- websiteDisplay: 'example.com',
17
- moreAppsUrl: 'https://apps.example.com',
18
- privacyPolicyUrl: 'https://example.com/privacy',
19
- termsOfServiceUrl: 'https://example.com/terms',
20
- };
21
-
22
- const mockConfig: AboutConfig = {
23
- appInfo: mockAppInfo,
24
- actions: {
25
- onEmailPress: jest.fn(),
26
- onWebsitePress: jest.fn(),
27
- onPrivacyPress: jest.fn(),
28
- onTermsPress: jest.fn(),
29
- onMoreAppsPress: jest.fn(),
30
- },
31
- };
32
-
33
- it('should render without crashing', () => {
34
- const element = React.createElement(AboutScreen, { config: mockConfig });
35
- expect(element).toBeTruthy();
36
- });
37
-
38
- it('should render app info after loading', () => {
39
- const element = React.createElement(AboutScreen, { config: mockConfig });
40
- expect(element).toBeTruthy();
41
- expect(mockConfig.appInfo.name).toBe('Test App');
42
- });
43
-
44
- it('should render error state when initialization fails', () => {
45
- const invalidConfig = null as unknown;
46
-
47
- const element = React.createElement(AboutScreen, { config: invalidConfig });
48
- expect(element).toBeTruthy();
49
- });
50
-
51
- it('should render no app info message when app info is null', () => {
52
- const emptyConfig: AboutConfig = {
53
- appInfo: null as unknown,
54
- actions: {},
55
- };
56
-
57
- const element = React.createElement(AboutScreen, { config: emptyConfig });
58
- expect(element).toBeTruthy();
59
- });
60
-
61
- it('should not render header when showHeader is false', () => {
62
- const element = React.createElement(AboutScreen, { config: mockConfig, showHeader: false });
63
- expect(element).toBeTruthy();
64
- });
65
-
66
- it('should render custom header component when provided', () => {
67
- const CustomHeader = () => React.createElement('div', { testID: 'custom-header' }, 'Custom Header');
68
-
69
- const element = React.createElement(AboutScreen, {
70
- config: mockConfig,
71
- headerComponent: React.createElement(CustomHeader)
72
- });
73
- expect(element).toBeTruthy();
74
- });
75
-
76
- it('should render custom footer component when provided', () => {
77
- const CustomFooter = () => React.createElement('div', { testID: 'custom-footer' }, 'Custom Footer');
78
-
79
- const element = React.createElement(AboutScreen, {
80
- config: mockConfig,
81
- footerComponent: React.createElement(CustomFooter)
82
- });
83
- expect(element).toBeTruthy();
84
- });
85
-
86
- it('should apply custom container style', () => {
87
- const customStyle = { backgroundColor: 'red' };
88
-
89
- const element = React.createElement(AboutScreen, {
90
- config: mockConfig,
91
- containerStyle: customStyle,
92
- testID: 'screen'
93
- });
94
- expect(element).toBeTruthy();
95
- });
96
-
97
- it('should apply custom header style', () => {
98
- const customStyle = { backgroundColor: 'blue' };
99
-
100
- const element = React.createElement(AboutScreen, {
101
- config: mockConfig,
102
- headerStyle: customStyle
103
- });
104
- expect(element).toBeTruthy();
105
- });
106
-
107
- it('should apply custom title style', () => {
108
- const customStyle = { color: 'green' } as React.CSSProperties;
109
-
110
- const element = React.createElement(AboutScreen, {
111
- config: mockConfig,
112
- titleStyle: customStyle
113
- });
114
- expect(element).toBeTruthy();
115
- });
116
-
117
- it('should apply custom version style', () => {
118
- const customStyle = { color: 'purple' };
119
-
120
- const element = React.createElement(AboutScreen, {
121
- config: mockConfig,
122
- versionStyle: customStyle
123
- });
124
- expect(element).toBeTruthy();
125
- });
126
-
127
- it('should handle empty config', () => {
128
- const emptyConfig: AboutConfig = {
129
- appInfo: {
130
- name: '',
131
- version: '',
132
- description: '',
133
- developer: '',
134
- contactEmail: '',
135
- websiteUrl: '',
136
- websiteDisplay: '',
137
- moreAppsUrl: '',
138
- privacyPolicyUrl: '',
139
- termsOfServiceUrl: '',
140
- },
141
- actions: {},
142
- };
143
-
144
- const element = React.createElement(AboutScreen, { config: emptyConfig });
145
- expect(element).toBeTruthy();
146
- });
147
-
148
- it('should handle config with only required fields', () => {
149
- const minimalConfig: AboutConfig = {
150
- appInfo: {
151
- name: 'Minimal App',
152
- version: '1.0.0',
153
- description: '',
154
- developer: '',
155
- contactEmail: '',
156
- websiteUrl: '',
157
- websiteDisplay: '',
158
- moreAppsUrl: '',
159
- privacyPolicyUrl: '',
160
- termsOfServiceUrl: '',
161
- },
162
- actions: {},
163
- };
164
-
165
- const element = React.createElement(AboutScreen, { config: minimalConfig });
166
- expect(element).toBeTruthy();
167
- expect(minimalConfig.appInfo.name).toBe('Minimal App');
168
- });
169
-
170
- it('should handle special characters in text', () => {
171
- const configWithSpecialChars: AboutConfig = {
172
- appInfo: {
173
- ...mockAppInfo,
174
- name: 'App & Special <Chars>',
175
- description: 'Description with "quotes" and \'apostrophes\'',
176
- },
177
- actions: {},
178
- };
179
-
180
- const element = React.createElement(AboutScreen, { config: configWithSpecialChars });
181
- expect(element).toBeTruthy();
182
- expect(configWithSpecialChars.appInfo.name).toBe('App & Special <Chars>');
183
- });
184
-
185
- it('should handle very long text', () => {
186
- const configWithLongText: AboutConfig = {
187
- appInfo: {
188
- ...mockAppInfo,
189
- name: 'A'.repeat(100),
190
- description: 'B'.repeat(500),
191
- },
192
- actions: {},
193
- };
194
-
195
- const element = React.createElement(AboutScreen, { config: configWithLongText });
196
- expect(element).toBeTruthy();
197
- expect(configWithLongText.appInfo.name.length).toBe(100);
198
- });
199
- });
@@ -1,366 +0,0 @@
1
- /**
2
- * Tests for AboutScreen component
3
- */
4
- import React from 'react';
5
- import { View, Text, TextStyle } from 'react-native';
6
- import { render, waitFor, fireEvent } from '@testing-library/react';
7
- import { AboutScreen } from '../AboutScreen';
8
- import { AboutConfig } from '../../../domain/entities/AppInfo';
9
-
10
- // Mock console methods
11
- const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation();
12
- const mockConsoleError = jest.spyOn(console, 'error').mockImplementation();
13
-
14
- describe('AboutScreen', () => {
15
- const mockConfig: AboutConfig = {
16
- appInfo: {
17
- name: 'Test App',
18
- version: '1.0.0',
19
- description: 'Test Description',
20
- developer: 'Test Developer',
21
- contactEmail: 'test@example.com',
22
- websiteUrl: 'https://example.com',
23
- websiteDisplay: 'example.com',
24
- moreAppsUrl: 'https://apps.example.com',
25
- },
26
- actions: {
27
- onEmailPress: jest.fn(),
28
- onWebsitePress: jest.fn(),
29
- onMoreAppsPress: jest.fn(),
30
- },
31
- };
32
-
33
- beforeEach(() => {
34
- jest.clearAllMocks();
35
- });
36
-
37
- afterEach(() => {
38
- mockConsoleLog.mockClear();
39
- mockConsoleError.mockClear();
40
- });
41
-
42
- describe('Rendering', () => {
43
- it('should render loading state initially', () => {
44
- const { getByText } = render(
45
- <AboutScreen config={mockConfig} />
46
- );
47
-
48
- expect(getByText('Loading...')).toBeTruthy();
49
- });
50
-
51
- it('should render app info after loading', async () => {
52
- const { getByText, queryByText } = render(
53
- <AboutScreen config={mockConfig} />
54
- );
55
-
56
- // Wait for loading to complete
57
- await waitFor(() => {
58
- expect(queryByText('Loading...')).toBeFalsy();
59
- });
60
-
61
- // Check for app info
62
- expect(getByText('Test App')).toBeTruthy();
63
- expect(getByText('Version 1.0.0')).toBeTruthy();
64
- expect(getByText('Test Description')).toBeTruthy();
65
- expect(getByText('Test Developer')).toBeTruthy();
66
- expect(getByText('test@example.com')).toBeTruthy();
67
- expect(getByText('example.com')).toBeTruthy();
68
- });
69
-
70
- it('should render error state when initialization fails', async () => {
71
- const invalidConfig = null as unknown;
72
-
73
- const { getByText, queryByText } = render(
74
- <AboutScreen config={invalidConfig} />
75
- );
76
-
77
- await waitFor(() => {
78
- expect(queryByText('Loading...')).toBeFalsy();
79
- });
80
-
81
- expect(getByText('No app information available')).toBeTruthy();
82
- });
83
-
84
- it('should render no app info message when app info is null', async () => {
85
- const emptyConfig: AboutConfig = {
86
- appInfo: {},
87
- };
88
-
89
- const { getByText, queryByText } = render(
90
- <AboutScreen config={emptyConfig} />
91
- );
92
-
93
- await waitFor(() => {
94
- expect(queryByText('Loading...')).toBeFalsy();
95
- });
96
-
97
- // With empty appInfo, it should show empty name and default version
98
- expect(getByText('Version 1.0.0')).toBeTruthy();
99
- });
100
-
101
- it('should not render header when showHeader is false', async () => {
102
- const { queryByText } = render(
103
- <AboutScreen config={mockConfig} showHeader={false} />
104
- );
105
-
106
- await waitFor(() => {
107
- expect(queryByText('Loading...')).toBeFalsy();
108
- });
109
-
110
- expect(queryByText('Test App')).toBeFalsy();
111
- expect(queryByText('Version 1.0.0')).toBeFalsy();
112
- expect(queryByText('Test Description')).toBeFalsy();
113
- });
114
-
115
- it('should render custom header component when provided', async () => {
116
- const CustomHeader = () => <View testID="custom-header"><Text>Custom Header</Text></View>;
117
-
118
- const { getByTestId, queryByText } = render(
119
- <AboutScreen config={mockConfig} headerComponent={<CustomHeader />} />
120
- );
121
-
122
- await waitFor(() => {
123
- expect(queryByText('Loading...')).toBeFalsy();
124
- });
125
-
126
- expect(getByTestId('custom-header')).toBeTruthy();
127
- expect(queryByText('Test App')).toBeFalsy(); // Default header should not render
128
- });
129
-
130
- it('should render custom footer component when provided', async () => {
131
- const CustomFooter = () => <View testID="custom-footer"><Text>Custom Footer</Text></View>;
132
-
133
- const { getByTestId } = render(
134
- <AboutScreen config={mockConfig} footerComponent={<CustomFooter />} />
135
- );
136
-
137
- await waitFor(() => {
138
- expect(getByTestId('custom-footer')).toBeTruthy();
139
- });
140
- });
141
- });
142
-
143
- describe('Custom Styles', () => {
144
- it('should apply custom container style', async () => {
145
- const customStyle = { backgroundColor: 'red' };
146
-
147
- const { queryByText, container } = render(
148
- <AboutScreen config={mockConfig} containerStyle={customStyle} testID="screen" />
149
- );
150
-
151
- await waitFor(() => {
152
- expect(queryByText('Loading...')).toBeFalsy();
153
- });
154
-
155
- // Test that custom container style is applied (component renders without error)
156
- expect(container).toBeTruthy();
157
- });
158
-
159
- it('should apply custom header style', async () => {
160
- const customStyle = { backgroundColor: 'blue' };
161
-
162
- const { getByText, queryByText } = render(
163
- <AboutScreen config={mockConfig} headerStyle={customStyle} />
164
- );
165
-
166
- await waitFor(() => {
167
- expect(queryByText('Loading...')).toBeFalsy();
168
- });
169
-
170
- const header = getByText('Test App');
171
- // Check that header renders with custom style (component renders without error)
172
- expect(header).toBeTruthy();
173
- });
174
-
175
- it('should apply custom title style', async () => {
176
- const customStyle: TextStyle = { color: 'green' };
177
-
178
- const { getByText, queryByText } = render(
179
- <AboutScreen config={mockConfig} titleStyle={customStyle} />
180
- );
181
-
182
- await waitFor(() => {
183
- expect(queryByText('Loading...')).toBeFalsy();
184
- });
185
-
186
- const title = getByText('Test App');
187
- // Check that title renders with custom style (component renders without error)
188
- expect(title).toBeTruthy();
189
- });
190
-
191
- it('should apply custom version style', async () => {
192
- const customStyle = { color: 'purple' };
193
-
194
- const { getByText, queryByText } = render(
195
- <AboutScreen config={mockConfig} versionStyle={customStyle} />
196
- );
197
-
198
- await waitFor(() => {
199
- expect(queryByText('Loading...')).toBeFalsy();
200
- });
201
-
202
- const version = getByText('Version 1.0.0');
203
- // Check that version renders with custom style (component renders without error)
204
- expect(version).toBeTruthy();
205
- });
206
- });
207
-
208
- describe('Interactions', () => {
209
- it('should call action handlers when items are pressed', async () => {
210
- const { getByTestId } = render(
211
- <AboutScreen config={mockConfig} />
212
- );
213
-
214
- await waitFor(() => {
215
- expect(getByTestId('email-item')).toBeTruthy();
216
- expect(getByTestId('website-item')).toBeTruthy();
217
- expect(getByTestId('more-apps-item')).toBeTruthy();
218
- });
219
-
220
- // Test interactions
221
- fireEvent.click(getByTestId('email-item'));
222
- fireEvent.click(getByTestId('website-item'));
223
- fireEvent.click(getByTestId('more-apps-item'));
224
-
225
- expect(mockConfig.actions!.onEmailPress).toHaveBeenCalledTimes(1);
226
- expect(mockConfig.actions!.onWebsitePress).toHaveBeenCalledTimes(1);
227
- expect(mockConfig.actions!.onMoreAppsPress).toHaveBeenCalledTimes(1);
228
- });
229
- });
230
-
231
- describe('Performance', () => {
232
- it('should memoize render functions', async () => {
233
- const { rerender } = render(
234
- <AboutScreen config={mockConfig} />
235
- );
236
-
237
- // Wait for initial render
238
- await waitFor(() => {
239
- expect(() => { }).not.toThrow();
240
- });
241
-
242
- // Re-render with same props
243
- rerender(
244
- <AboutScreen config={mockConfig} />
245
- );
246
-
247
- // Should not throw and should render correctly
248
- await waitFor(() => {
249
- expect(() => {
250
- rerender(
251
- <AboutScreen config={mockConfig} />
252
- );
253
- }).not.toThrow();
254
- });
255
- });
256
-
257
- it('should handle rapid prop changes', async () => {
258
- const { rerender } = render(
259
- <AboutScreen config={mockConfig} />
260
- );
261
-
262
- // Rapid prop changes
263
- for (let i = 0; i < 5; i++) {
264
- const newConfig = {
265
- ...mockConfig,
266
- appInfo: { ...mockConfig.appInfo, name: `App ${i}` }
267
- };
268
- rerender(<AboutScreen config={newConfig} />);
269
-
270
- await waitFor(() => {
271
- expect(() => { }).not.toThrow();
272
- });
273
- }
274
-
275
- expect(() => {
276
- rerender(
277
- <AboutScreen config={{ ...mockConfig, appInfo: { ...mockConfig.appInfo, name: 'Final App' } }} />
278
- );
279
- }).not.toThrow();
280
- });
281
- });
282
-
283
- describe('Edge Cases', () => {
284
- it('should handle empty config', async () => {
285
- const emptyConfig: AboutConfig = {
286
- appInfo: {},
287
- };
288
-
289
- const { getByText, queryByText } = render(
290
- <AboutScreen config={emptyConfig} />
291
- );
292
-
293
- await waitFor(() => {
294
- expect(queryByText('Loading...')).toBeFalsy();
295
- });
296
-
297
- // With empty appInfo, it should show empty name and default version
298
- expect(getByText('Version 1.0.0')).toBeTruthy();
299
- });
300
-
301
- it('should handle config with only required fields', async () => {
302
- const minimalConfig: AboutConfig = {
303
- appInfo: {
304
- name: 'Minimal App',
305
- version: '1.0.0',
306
- },
307
- };
308
-
309
- const { getByText, queryByText } = render(
310
- <AboutScreen config={minimalConfig} />
311
- );
312
-
313
- await waitFor(() => {
314
- expect(queryByText('Loading...')).toBeFalsy();
315
- });
316
-
317
- expect(getByText('Minimal App')).toBeTruthy();
318
- expect(getByText('Version 1.0.0')).toBeTruthy();
319
- expect(queryByText('Test Developer')).toBeFalsy();
320
- expect(queryByText('test@example.com')).toBeFalsy();
321
- });
322
-
323
- it('should handle special characters in text', async () => {
324
- const configWithSpecialChars: AboutConfig = {
325
- appInfo: {
326
- name: 'Test & App <script>',
327
- version: '1.0.0',
328
- description: 'Description with émojis 🎉 and ñ',
329
- developer: 'Developer & Co.',
330
- },
331
- };
332
-
333
- const { getByText } = render(
334
- <AboutScreen config={configWithSpecialChars} />
335
- );
336
-
337
- await waitFor(() => {
338
- expect(getByText('Test & App <script>')).toBeTruthy();
339
- expect(getByText('Description with émojis 🎉 and ñ')).toBeTruthy();
340
- expect(getByText('Developer & Co.')).toBeTruthy();
341
- });
342
- });
343
-
344
- it('should handle very long text', async () => {
345
- const longName = 'A'.repeat(100);
346
- const longDescription = 'B'.repeat(200);
347
-
348
- const configWithLongText: AboutConfig = {
349
- appInfo: {
350
- name: longName,
351
- version: '1.0.0',
352
- description: longDescription,
353
- },
354
- };
355
-
356
- const { getByText } = render(
357
- <AboutScreen config={configWithLongText} />
358
- );
359
-
360
- await waitFor(() => {
361
- expect(getByText(longName)).toBeTruthy();
362
- expect(getByText(longDescription)).toBeTruthy();
363
- });
364
- });
365
- });
366
- });