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