@qlover/create-app 0.6.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/react-app/__tests__/__mocks__/I18nService.ts +13 -0
- package/dist/templates/react-app/__tests__/__mocks__/MockAppConfit.ts +48 -0
- package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +16 -0
- package/dist/templates/react-app/__tests__/__mocks__/MockLogger.ts +14 -0
- package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +92 -0
- package/dist/templates/react-app/__tests__/setup/index.ts +51 -0
- package/dist/templates/react-app/__tests__/src/App.test.tsx +139 -0
- package/dist/templates/react-app/__tests__/src/base/cases/AppConfig.test.ts +288 -0
- package/dist/templates/react-app/__tests__/src/base/cases/AppError.test.ts +102 -0
- package/dist/templates/react-app/__tests__/src/base/cases/DialogHandler.test.ts +228 -0
- package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +207 -0
- package/dist/templates/react-app/__tests__/src/base/cases/InversifyContainer.test.ts +181 -0
- package/dist/templates/react-app/__tests__/src/base/cases/PublicAssetsPath.test.ts +61 -0
- package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +199 -0
- package/dist/templates/react-app/__tests__/src/base/cases/RequestStatusCatcher.test.ts +192 -0
- package/dist/templates/react-app/__tests__/src/base/cases/RouterLoader.test.ts +235 -0
- package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +224 -0
- package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +257 -0
- package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +72 -0
- package/dist/templates/react-app/__tests__/src/main.integration.test.tsx +62 -0
- package/dist/templates/react-app/__tests__/src/main.test.tsx +46 -0
- package/dist/templates/react-app/__tests__/src/uikit/components/BaseHeader.test.tsx +88 -0
- package/dist/templates/react-app/config/app.router.ts +155 -0
- package/dist/templates/react-app/config/common.ts +9 -1
- package/dist/templates/react-app/docs/en/test-guide.md +782 -0
- package/dist/templates/react-app/docs/zh/test-guide.md +782 -0
- package/dist/templates/react-app/package.json +8 -19
- package/dist/templates/react-app/src/base/cases/AppConfig.ts +16 -9
- package/dist/templates/react-app/src/base/cases/PublicAssetsPath.ts +7 -1
- package/dist/templates/react-app/src/base/services/I18nService.ts +15 -4
- package/dist/templates/react-app/src/base/services/RouteService.ts +43 -7
- package/dist/templates/react-app/src/core/bootstraps/BootstrapApp.ts +31 -10
- package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +1 -1
- package/dist/templates/react-app/src/core/globals.ts +1 -3
- package/dist/templates/react-app/src/core/registers/RegisterCommon.ts +5 -3
- package/dist/templates/react-app/src/main.tsx +6 -1
- package/dist/templates/react-app/src/pages/404.tsx +0 -1
- package/dist/templates/react-app/src/pages/500.tsx +1 -1
- package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +3 -1
- package/dist/templates/react-app/src/uikit/components/BaseHeader.tsx +9 -2
- package/dist/templates/react-app/src/uikit/components/LocaleLink.tsx +5 -3
- package/dist/templates/react-app/src/uikit/hooks/useI18nGuard.ts +4 -6
- package/dist/templates/react-app/tsconfig.json +2 -1
- package/dist/templates/react-app/tsconfig.test.json +13 -0
- package/dist/templates/react-app/vite.config.ts +3 -2
- package/package.json +1 -1
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AppConfig test suite
|
|
3
|
+
*
|
|
4
|
+
* Coverage:
|
|
5
|
+
* 1. constructor - Constructor initialization
|
|
6
|
+
* 2. basic properties - App name, version, etc.
|
|
7
|
+
* 3. OpenAI configuration
|
|
8
|
+
* 4. Login configuration
|
|
9
|
+
* 5. API endpoints
|
|
10
|
+
* 6. Environment detection
|
|
11
|
+
* 7. Default values
|
|
12
|
+
* 8. Configuration validation
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
16
|
+
import { AppConfig } from '@/base/cases/AppConfig';
|
|
17
|
+
import { InjectEnv } from '@qlover/corekit-bridge';
|
|
18
|
+
import { envBlackList, envPrefix } from '@config/common';
|
|
19
|
+
|
|
20
|
+
// Mock import.meta.env
|
|
21
|
+
const mockImportMetaEnv = {
|
|
22
|
+
VITE_USER_NODE_ENV: 'test',
|
|
23
|
+
VITE_APP_NAME: 'TestApp',
|
|
24
|
+
VITE_APP_VERSION: '1.0.0',
|
|
25
|
+
VITE_USER_TOKEN_STORAGE_KEY: 'test_user_token',
|
|
26
|
+
VITE_USER_INFO_STORAGE_KEY: 'test_user_info',
|
|
27
|
+
VITE_OPEN_AI_BASE_URL: 'https://api.openai.com',
|
|
28
|
+
VITE_OPEN_AI_TOKEN: 'test_openai_token',
|
|
29
|
+
VITE_OPEN_AI_TOKEN_PREFIX: 'Bearer',
|
|
30
|
+
VITE_OPEN_AI_REQUIRE_TOKEN: 'true',
|
|
31
|
+
VITE_LOGIN_USER: 'testuser',
|
|
32
|
+
VITE_LOGIN_PASSWORD: 'testpass',
|
|
33
|
+
VITE_FE_API_BASE_URL: 'https://api.fe.com',
|
|
34
|
+
VITE_USER_API_BASE_URL: 'https://api.user.com',
|
|
35
|
+
VITE_AI_API_BASE_URL: 'https://api.ai.com',
|
|
36
|
+
VITE_AI_API_TOKEN: 'test_ai_token',
|
|
37
|
+
VITE_AI_API_TOKEN_PREFIX: 'Bearer',
|
|
38
|
+
VITE_AI_API_REQUIRE_TOKEN: 'true',
|
|
39
|
+
VITE_BOOT_HREF: 'https://test.com'
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Mock window.location
|
|
43
|
+
const mockLocation = {
|
|
44
|
+
href: 'https://test.com/app'
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Helper function to inject environment variables using InjectEnv plugin
|
|
48
|
+
function injectEnvVariables(
|
|
49
|
+
config: AppConfig,
|
|
50
|
+
envVars: Record<string, string>
|
|
51
|
+
) {
|
|
52
|
+
const injectEnv = new InjectEnv({
|
|
53
|
+
target: config,
|
|
54
|
+
source: envVars,
|
|
55
|
+
prefix: envPrefix,
|
|
56
|
+
blackList: envBlackList
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
injectEnv.onBefore();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
describe('AppConfig', () => {
|
|
63
|
+
let appConfig: AppConfig;
|
|
64
|
+
let originalImportMetaEnv: Record<string, string>;
|
|
65
|
+
let originalLocation: Location;
|
|
66
|
+
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
// Save original values
|
|
69
|
+
originalImportMetaEnv = { ...import.meta.env };
|
|
70
|
+
originalLocation = window.location;
|
|
71
|
+
|
|
72
|
+
// Mock import.meta.env
|
|
73
|
+
Object.defineProperty(import.meta, 'env', {
|
|
74
|
+
value: { ...mockImportMetaEnv },
|
|
75
|
+
writable: true
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Mock window.location
|
|
79
|
+
Object.defineProperty(window, 'location', {
|
|
80
|
+
value: mockLocation,
|
|
81
|
+
writable: true
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
appConfig = new AppConfig();
|
|
85
|
+
|
|
86
|
+
// Simulate environment variable injection
|
|
87
|
+
injectEnvVariables(appConfig, mockImportMetaEnv);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
afterEach(() => {
|
|
91
|
+
// Restore original values
|
|
92
|
+
Object.defineProperty(import.meta, 'env', {
|
|
93
|
+
value: originalImportMetaEnv,
|
|
94
|
+
writable: true
|
|
95
|
+
});
|
|
96
|
+
Object.defineProperty(window, 'location', {
|
|
97
|
+
value: originalLocation,
|
|
98
|
+
writable: true
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('Basic Properties', () => {
|
|
103
|
+
it('should have correct appName', () => {
|
|
104
|
+
expect(appConfig.appName).toBe('TestApp');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should have correct appVersion', () => {
|
|
108
|
+
expect(appConfig.appVersion).toBe('1.0.0');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should have correct env', () => {
|
|
112
|
+
// because blackList
|
|
113
|
+
expect(appConfig.env).toBeUndefined();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should have correct userTokenStorageKey', () => {
|
|
117
|
+
expect(appConfig.userTokenStorageKey).toBe('test_user_token');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should have correct userInfoStorageKey', () => {
|
|
121
|
+
expect(appConfig.userInfoStorageKey).toBe('test_user_info');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('OpenAI Configuration', () => {
|
|
126
|
+
it('should have correct openAiModels', () => {
|
|
127
|
+
const expectedModels = [
|
|
128
|
+
'gpt-4o-mini',
|
|
129
|
+
'gpt-3.5-turbo',
|
|
130
|
+
'gpt-3.5-turbo-2',
|
|
131
|
+
'gpt-4',
|
|
132
|
+
'gpt-4-32k'
|
|
133
|
+
];
|
|
134
|
+
expect(appConfig.openAiModels).toEqual(expectedModels);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should have correct openAiBaseUrl', () => {
|
|
138
|
+
expect(appConfig.openAiBaseUrl).toBe('https://api.openai.com');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should have correct openAiToken', () => {
|
|
142
|
+
expect(appConfig.openAiToken).toBe('test_openai_token');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should have correct openAiTokenPrefix', () => {
|
|
146
|
+
expect(appConfig.openAiTokenPrefix).toBe('Bearer');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should have correct openAiRequireToken', () => {
|
|
150
|
+
expect(appConfig.openAiRequireToken).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('Login Configuration', () => {
|
|
155
|
+
it('should have correct loginUser', () => {
|
|
156
|
+
expect(appConfig.loginUser).toBe('testuser');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should have correct loginPassword', () => {
|
|
160
|
+
expect(appConfig.loginPassword).toBe('testpass');
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('API Configuration', () => {
|
|
165
|
+
it('should have correct feApiBaseUrl', () => {
|
|
166
|
+
expect(appConfig.feApiBaseUrl).toBe('https://api.fe.com');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should have correct userApiBaseUrl', () => {
|
|
170
|
+
expect(appConfig.userApiBaseUrl).toBe('https://api.user.com');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should have correct aiApiBaseUrl', () => {
|
|
174
|
+
expect(appConfig.aiApiBaseUrl).toBe('https://api.ai.com');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should have correct aiApiToken', () => {
|
|
178
|
+
expect(appConfig.aiApiToken).toBe('test_ai_token');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should have correct aiApiTokenPrefix', () => {
|
|
182
|
+
expect(appConfig.aiApiTokenPrefix).toBe('Bearer');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should have correct aiApiRequireToken', () => {
|
|
186
|
+
expect(appConfig.aiApiRequireToken).toBe(true);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('Boot Configuration', () => {
|
|
191
|
+
it('should have correct bootHref', () => {
|
|
192
|
+
expect(appConfig.bootHref).toBe('https://test.com');
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe('Environment Detection', () => {
|
|
197
|
+
it('should correctly identify production environment', () => {
|
|
198
|
+
const productionConfig = new AppConfig('production');
|
|
199
|
+
expect(productionConfig.isProduction).toBe(true);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should correctly identify non-production environment', () => {
|
|
203
|
+
// Mock development environment
|
|
204
|
+
Object.defineProperty(import.meta, 'env', {
|
|
205
|
+
value: { ...mockImportMetaEnv, VITE_USER_NODE_ENV: 'development' },
|
|
206
|
+
writable: true
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const developmentConfig = new AppConfig();
|
|
210
|
+
expect(developmentConfig.isProduction).toBe(false);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should correctly identify test environment', () => {
|
|
214
|
+
expect(appConfig.isProduction).toBe(false);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('Property Types', () => {
|
|
219
|
+
it('should have correct property types', () => {
|
|
220
|
+
expect(typeof appConfig.appName).toBe('string');
|
|
221
|
+
expect(typeof appConfig.appVersion).toBe('string');
|
|
222
|
+
expect(appConfig.env).toBeUndefined(); // because blackList
|
|
223
|
+
expect(typeof appConfig.userInfoStorageKey).toBe('string');
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('Default Values', () => {
|
|
228
|
+
it('should have sensible default values when environment variables are not set', () => {
|
|
229
|
+
// Mock empty environment
|
|
230
|
+
Object.defineProperty(import.meta, 'env', {
|
|
231
|
+
value: {},
|
|
232
|
+
writable: true
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const defaultConfig = new AppConfig();
|
|
236
|
+
// Don't inject environment variables to test defaults
|
|
237
|
+
|
|
238
|
+
expect(defaultConfig.appName).toBe('');
|
|
239
|
+
expect(defaultConfig.appVersion).toBe('');
|
|
240
|
+
expect(defaultConfig.userTokenStorageKey).toBe('__fe_user_token__');
|
|
241
|
+
expect(defaultConfig.userInfoStorageKey).toBe('__fe_user_info__');
|
|
242
|
+
expect(defaultConfig.openAiRequireToken).toBe(true);
|
|
243
|
+
expect(defaultConfig.aiApiBaseUrl).toBe('https://api.openai.com/v1');
|
|
244
|
+
expect(defaultConfig.aiApiTokenPrefix).toBe('Bearer');
|
|
245
|
+
expect(defaultConfig.aiApiRequireToken).toBe(true);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe('Configuration Validation', () => {
|
|
250
|
+
it('should have valid OpenAI models configuration', () => {
|
|
251
|
+
expect(Array.isArray(appConfig.openAiModels)).toBe(true);
|
|
252
|
+
expect(appConfig.openAiModels.length).toBeGreaterThan(0);
|
|
253
|
+
|
|
254
|
+
// Check that all models are strings
|
|
255
|
+
appConfig.openAiModels.forEach((model) => {
|
|
256
|
+
expect(typeof model).toBe('string');
|
|
257
|
+
expect(model.length).toBeGreaterThan(0);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('should have valid storage keys', () => {
|
|
262
|
+
expect(typeof appConfig.userTokenStorageKey).toBe('string');
|
|
263
|
+
expect(typeof appConfig.userInfoStorageKey).toBe('string');
|
|
264
|
+
expect(appConfig.userTokenStorageKey.length).toBeGreaterThan(0);
|
|
265
|
+
expect(appConfig.userInfoStorageKey.length).toBeGreaterThan(0);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should have valid API URLs when provided', () => {
|
|
269
|
+
// Test with valid URLs
|
|
270
|
+
const validUrlConfig = new AppConfig();
|
|
271
|
+
if (validUrlConfig.aiApiBaseUrl) {
|
|
272
|
+
expect(validUrlConfig.aiApiBaseUrl).toMatch(/^https?:\/\//);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe('Instance Creation', () => {
|
|
278
|
+
it('should create new instance with default values', () => {
|
|
279
|
+
const newConfig = new AppConfig();
|
|
280
|
+
expect(newConfig).toBeInstanceOf(AppConfig);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('should create new instance with custom environment', () => {
|
|
284
|
+
const customConfig = new AppConfig('custom');
|
|
285
|
+
expect(customConfig.env).toBe('custom');
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AppError test suite
|
|
3
|
+
*
|
|
4
|
+
* Coverage:
|
|
5
|
+
* 1. constructor - Constructor initialization with different source types
|
|
6
|
+
* 2. inheritance - Verify inheritance from ExecutorError
|
|
7
|
+
* 3. error properties - Verify error properties are set correctly
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect } from 'vitest';
|
|
11
|
+
import { AppError } from '@/base/cases/AppError';
|
|
12
|
+
import { ExecutorError } from '@qlover/fe-corekit';
|
|
13
|
+
|
|
14
|
+
describe('AppError', () => {
|
|
15
|
+
describe('constructor', () => {
|
|
16
|
+
it('should create instance with id only', () => {
|
|
17
|
+
const error = new AppError('TEST_ERROR');
|
|
18
|
+
expect(error).toBeInstanceOf(AppError);
|
|
19
|
+
expect(error).toBeInstanceOf(ExecutorError);
|
|
20
|
+
expect(error).toBeInstanceOf(Error);
|
|
21
|
+
expect(error.id).toBe('TEST_ERROR');
|
|
22
|
+
expect(error.message).toBe('TEST_ERROR');
|
|
23
|
+
expect(error.source).toBeUndefined();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should create instance with string source', () => {
|
|
27
|
+
const source = 'Test error message';
|
|
28
|
+
const error = new AppError('TEST_ERROR', source);
|
|
29
|
+
expect(error.id).toBe('TEST_ERROR');
|
|
30
|
+
expect(error.message).toBe(source);
|
|
31
|
+
expect(error.source).toBe(source);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should create instance with Error source', () => {
|
|
35
|
+
const source = new Error('Original error');
|
|
36
|
+
const error = new AppError('TEST_ERROR', source);
|
|
37
|
+
expect(error.id).toBe('TEST_ERROR');
|
|
38
|
+
expect(error.message).toBe('Original error');
|
|
39
|
+
expect(error.source).toBe(source);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('error properties', () => {
|
|
44
|
+
it('should have correct error type', () => {
|
|
45
|
+
const error = new AppError('TEST_ERROR');
|
|
46
|
+
expect(error.id).toBe('TEST_ERROR');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should maintain id property', () => {
|
|
50
|
+
const error = new AppError('TEST_ERROR');
|
|
51
|
+
Object.defineProperty(error, 'id', { value: 'NEW_ID' });
|
|
52
|
+
expect(error.id).toBe('NEW_ID');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should maintain source property', () => {
|
|
56
|
+
const source = 'source';
|
|
57
|
+
const error = new AppError('TEST_ERROR', source);
|
|
58
|
+
Object.defineProperty(error, 'source', { value: 'new source' });
|
|
59
|
+
expect(error.source).toBe('new source');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('error handling', () => {
|
|
64
|
+
it('should work in try-catch block', () => {
|
|
65
|
+
expect(() => {
|
|
66
|
+
throw new AppError('TEST_ERROR');
|
|
67
|
+
}).toThrow(AppError);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should preserve stack trace', () => {
|
|
71
|
+
const error = new AppError('TEST_ERROR');
|
|
72
|
+
expect(error.stack).toBeDefined();
|
|
73
|
+
expect(error.stack).toContain('AppError.test.ts');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should handle nested errors', () => {
|
|
77
|
+
const originalError = new Error('Original error');
|
|
78
|
+
const wrappedError = new AppError('WRAPPED_ERROR', originalError);
|
|
79
|
+
expect(wrappedError.source).toBe(originalError);
|
|
80
|
+
expect(wrappedError.message).toBe('Original error');
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('error message formatting', () => {
|
|
85
|
+
it('should use id as message when source is undefined', () => {
|
|
86
|
+
const error = new AppError('TEST_ERROR');
|
|
87
|
+
expect(error.message).toBe('TEST_ERROR');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should use source message when source is Error', () => {
|
|
91
|
+
const source = new Error('Source error message');
|
|
92
|
+
const error = new AppError('TEST_ERROR', source);
|
|
93
|
+
expect(error.message).toBe('Source error message');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should use source string when source is string', () => {
|
|
97
|
+
const source = 'Source error message';
|
|
98
|
+
const error = new AppError('TEST_ERROR', source);
|
|
99
|
+
expect(error.message).toBe('Source error message');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/**
|
|
3
|
+
* DialogHandler test suite
|
|
4
|
+
*
|
|
5
|
+
* Coverage:
|
|
6
|
+
* 1. constructor - Constructor initialization
|
|
7
|
+
* 2. setters - Message, Modal, Notification setters
|
|
8
|
+
* 3. formatErrorMessage - Error message formatting
|
|
9
|
+
* 4. notification methods - success, error, info, warn
|
|
10
|
+
* 5. confirm method - Modal confirmation
|
|
11
|
+
* 6. edge cases - Null/undefined handling
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
15
|
+
import { DialogHandler } from '../../../../src/base/cases/DialogHandler';
|
|
16
|
+
import type {
|
|
17
|
+
MessageApi,
|
|
18
|
+
ModalApi,
|
|
19
|
+
NotificationApi
|
|
20
|
+
} from '@brain-toolkit/antd-theme-override/react';
|
|
21
|
+
import type { ConfirmOptions } from '../../../../src/base/port/InteractionHubInterface';
|
|
22
|
+
|
|
23
|
+
describe('DialogHandler', () => {
|
|
24
|
+
let dialogHandler: DialogHandler;
|
|
25
|
+
let mockMessage: MessageApi;
|
|
26
|
+
let mockModal: ModalApi;
|
|
27
|
+
let mockNotification: NotificationApi;
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
// Create mock APIs
|
|
31
|
+
mockMessage = {
|
|
32
|
+
success: vi.fn(),
|
|
33
|
+
error: vi.fn(),
|
|
34
|
+
info: vi.fn(),
|
|
35
|
+
warning: vi.fn()
|
|
36
|
+
} as unknown as MessageApi;
|
|
37
|
+
|
|
38
|
+
mockModal = {
|
|
39
|
+
confirm: vi.fn()
|
|
40
|
+
} as unknown as ModalApi;
|
|
41
|
+
|
|
42
|
+
mockNotification = {
|
|
43
|
+
success: vi.fn(),
|
|
44
|
+
error: vi.fn(),
|
|
45
|
+
info: vi.fn(),
|
|
46
|
+
warning: vi.fn()
|
|
47
|
+
} as unknown as NotificationApi;
|
|
48
|
+
|
|
49
|
+
// Initialize DialogHandler
|
|
50
|
+
dialogHandler = new DialogHandler();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
vi.clearAllMocks();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('constructor', () => {
|
|
58
|
+
it('should create instance with empty antds object', () => {
|
|
59
|
+
expect(dialogHandler).toBeInstanceOf(DialogHandler);
|
|
60
|
+
expect((dialogHandler as any).antds).toEqual({});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('setters', () => {
|
|
65
|
+
it('should set message api', () => {
|
|
66
|
+
dialogHandler.setMessage(mockMessage);
|
|
67
|
+
expect((dialogHandler as any).antds.message).toBe(mockMessage);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should set modal api', () => {
|
|
71
|
+
dialogHandler.setModal(mockModal);
|
|
72
|
+
expect((dialogHandler as any).antds.modal).toBe(mockModal);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should set notification api', () => {
|
|
76
|
+
dialogHandler.setNotification(mockNotification);
|
|
77
|
+
expect((dialogHandler as any).antds.notification).toBe(mockNotification);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('formatErrorMessage', () => {
|
|
82
|
+
it('should format Error object', () => {
|
|
83
|
+
const error = new Error('test error');
|
|
84
|
+
const result = (dialogHandler as any).formatErrorMessage(error);
|
|
85
|
+
expect(result).toBe('test error');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should format string error', () => {
|
|
89
|
+
const result = (dialogHandler as any).formatErrorMessage('test error');
|
|
90
|
+
expect(result).toBe('test error');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should handle unknown error type', () => {
|
|
94
|
+
const result = (dialogHandler as any).formatErrorMessage({});
|
|
95
|
+
expect(result).toBe('An unknown error occurred');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('notification methods', () => {
|
|
100
|
+
beforeEach(() => {
|
|
101
|
+
dialogHandler.setMessage(mockMessage);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('success', () => {
|
|
105
|
+
it('should call message.success with content', () => {
|
|
106
|
+
dialogHandler.success('test success');
|
|
107
|
+
expect(mockMessage.success).toHaveBeenCalledWith({
|
|
108
|
+
content: 'test success'
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should pass through options', () => {
|
|
113
|
+
const options = { duration: 5000 };
|
|
114
|
+
dialogHandler.success('test success', options);
|
|
115
|
+
expect(mockMessage.success).toHaveBeenCalledWith({
|
|
116
|
+
content: 'test success',
|
|
117
|
+
...options
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('error', () => {
|
|
123
|
+
it('should call message.error with content', () => {
|
|
124
|
+
dialogHandler.error('test error');
|
|
125
|
+
expect(mockMessage.error).toHaveBeenCalledWith({
|
|
126
|
+
content: 'test error'
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should format error from options', () => {
|
|
131
|
+
const error = new Error('formatted error');
|
|
132
|
+
const options = { error };
|
|
133
|
+
dialogHandler.error('test error', options);
|
|
134
|
+
expect(mockMessage.error).toHaveBeenCalledWith({
|
|
135
|
+
content: 'formatted error',
|
|
136
|
+
error
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should pass through additional options', () => {
|
|
141
|
+
const error = new Error('formatted error');
|
|
142
|
+
const options = { error, duration: 5000 };
|
|
143
|
+
dialogHandler.error('test error', options);
|
|
144
|
+
expect(mockMessage.error).toHaveBeenCalledWith({
|
|
145
|
+
content: 'formatted error',
|
|
146
|
+
error,
|
|
147
|
+
duration: 5000
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('info', () => {
|
|
153
|
+
it('should call message.info with content', () => {
|
|
154
|
+
dialogHandler.info('test info');
|
|
155
|
+
expect(mockMessage.info).toHaveBeenCalledWith({
|
|
156
|
+
content: 'test info'
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should pass through options', () => {
|
|
161
|
+
const options = { duration: 5000 };
|
|
162
|
+
dialogHandler.info('test info', options);
|
|
163
|
+
expect(mockMessage.info).toHaveBeenCalledWith({
|
|
164
|
+
content: 'test info',
|
|
165
|
+
...options
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('warn', () => {
|
|
171
|
+
it('should call message.warning with content', () => {
|
|
172
|
+
dialogHandler.warn('test warning');
|
|
173
|
+
expect(mockMessage.warning).toHaveBeenCalledWith({
|
|
174
|
+
content: 'test warning'
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should pass through options', () => {
|
|
179
|
+
const options = { duration: 5000 };
|
|
180
|
+
dialogHandler.warn('test warning', options);
|
|
181
|
+
expect(mockMessage.warning).toHaveBeenCalledWith({
|
|
182
|
+
content: 'test warning',
|
|
183
|
+
...options
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('confirm', () => {
|
|
190
|
+
beforeEach(() => {
|
|
191
|
+
dialogHandler.setModal(mockModal);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should call modal.confirm with options', () => {
|
|
195
|
+
const options: ConfirmOptions = {
|
|
196
|
+
title: 'Confirm',
|
|
197
|
+
content: 'Are you sure?',
|
|
198
|
+
onOk: vi.fn(),
|
|
199
|
+
onCancel: vi.fn()
|
|
200
|
+
};
|
|
201
|
+
dialogHandler.confirm(options);
|
|
202
|
+
expect(mockModal.confirm).toHaveBeenCalledWith(options);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('edge cases', () => {
|
|
207
|
+
it('should handle undefined message api', () => {
|
|
208
|
+
expect(() => dialogHandler.success('test')).not.toThrow();
|
|
209
|
+
expect(() => dialogHandler.error('test')).not.toThrow();
|
|
210
|
+
expect(() => dialogHandler.info('test')).not.toThrow();
|
|
211
|
+
expect(() => dialogHandler.warn('test')).not.toThrow();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should handle undefined modal api', () => {
|
|
215
|
+
const options: ConfirmOptions = {
|
|
216
|
+
title: 'test',
|
|
217
|
+
content: 'test content'
|
|
218
|
+
};
|
|
219
|
+
expect(() => dialogHandler.confirm(options)).not.toThrow();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should handle undefined notification api', () => {
|
|
223
|
+
expect(() =>
|
|
224
|
+
dialogHandler.setNotification(undefined as any)
|
|
225
|
+
).not.toThrow();
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
});
|