@lobehub/lobehub 2.0.0-next.277 → 2.0.0-next.278
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/.cursor/rules/db-migrations.mdc +1 -1
- package/.cursor/rules/debug-usage.mdc +7 -5
- package/.cursor/rules/desktop-controller-tests.mdc +2 -1
- package/.cursor/rules/desktop-feature-implementation.mdc +9 -5
- package/.cursor/rules/desktop-local-tools-implement.mdc +67 -66
- package/.cursor/rules/desktop-menu-configuration.mdc +21 -9
- package/.cursor/rules/desktop-window-management.mdc +17 -2
- package/.cursor/rules/drizzle-schema-style-guide.mdc +6 -6
- package/.cursor/rules/hotkey.mdc +1 -0
- package/.cursor/rules/i18n.mdc +1 -0
- package/.cursor/rules/project-structure.mdc +16 -3
- package/.cursor/rules/react.mdc +17 -5
- package/.cursor/rules/recent-data-usage.mdc +2 -1
- package/.cursor/rules/testing-guide/testing-guide.mdc +262 -238
- package/.cursor/rules/testing-guide/zustand-store-action-test.mdc +1 -1
- package/.cursor/rules/zustand-action-patterns.mdc +1 -1
- package/.cursor/rules/zustand-slice-organization.mdc +4 -4
- package/CHANGELOG.md +25 -0
- package/CLAUDE.md +1 -1
- package/GEMINI.md +1 -1
- package/changelog/v1.json +5 -0
- package/docs/development/database-schema.dbml +16 -0
- package/locales/en-US/chat.json +24 -0
- package/locales/zh-CN/chat.json +24 -0
- package/package.json +1 -1
- package/packages/business/const/src/index.ts +3 -0
- package/packages/database/migrations/0069_add_topic_shares_table.sql +22 -0
- package/packages/database/migrations/meta/0069_snapshot.json +9704 -0
- package/packages/database/migrations/meta/_journal.json +7 -0
- package/packages/database/src/models/__tests__/topicShare.test.ts +318 -0
- package/packages/database/src/models/topicShare.ts +177 -0
- package/packages/database/src/schemas/topic.ts +44 -2
- package/packages/types/src/conversation.ts +5 -0
- package/packages/types/src/topic/topic.ts +46 -0
- package/src/app/[variants]/(main)/agent/features/Conversation/Header/ShareButton/index.tsx +24 -9
- package/src/app/[variants]/(main)/group/features/Conversation/Header/ShareButton/index.tsx +26 -9
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/AspectRatioSelect/index.tsx +1 -2
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/ImageNum.tsx +54 -173
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/ResolutionSelect.tsx +22 -67
- package/src/app/[variants]/(mobile)/router/mobileRouter.config.tsx +18 -0
- package/src/app/[variants]/router/desktopRouter.config.tsx +18 -0
- package/src/app/[variants]/share/t/[id]/SharedMessageList.tsx +54 -0
- package/src/app/[variants]/share/t/[id]/_layout/index.tsx +170 -0
- package/src/app/[variants]/share/t/[id]/features/Portal/index.tsx +66 -0
- package/src/app/[variants]/share/t/[id]/index.tsx +112 -0
- package/src/app/robots.tsx +1 -1
- package/src/business/client/BusinessMobileRoutes.tsx +1 -1
- package/src/features/Conversation/ChatList/index.tsx +12 -5
- package/src/features/Conversation/Messages/AssistantGroup/Tool/Render/index.tsx +8 -4
- package/src/features/Conversation/Messages/AssistantGroup/Tool/index.tsx +15 -10
- package/src/features/Conversation/Messages/AssistantGroup/Tools.tsx +3 -1
- package/src/features/Conversation/Messages/AssistantGroup/components/ContentBlock.tsx +3 -2
- package/src/features/Conversation/Messages/AssistantGroup/components/GroupItem.tsx +2 -2
- package/src/features/Conversation/Messages/Supervisor/components/ContentBlock.tsx +25 -26
- package/src/features/Conversation/Messages/Supervisor/components/Group.tsx +4 -2
- package/src/features/Conversation/Messages/Tool/Tool/index.tsx +16 -12
- package/src/features/Conversation/Messages/Tool/index.tsx +20 -11
- package/src/features/Conversation/Messages/index.tsx +1 -1
- package/src/features/Conversation/store/slices/data/action.ts +2 -1
- package/src/features/SharePopover/index.tsx +215 -0
- package/src/features/SharePopover/style.ts +10 -0
- package/src/libs/next/proxy/define-config.ts +4 -1
- package/src/locales/default/chat.ts +26 -0
- package/src/proxy.ts +1 -0
- package/src/server/routers/lambda/__tests__/message.test.ts +152 -0
- package/src/server/routers/lambda/__tests__/share.test.ts +227 -0
- package/src/server/routers/lambda/__tests__/topic.test.ts +174 -0
- package/src/server/routers/lambda/index.ts +2 -0
- package/src/server/routers/lambda/message.ts +37 -4
- package/src/server/routers/lambda/share.ts +55 -0
- package/src/server/routers/lambda/topic.ts +45 -0
- package/src/services/message/index.ts +1 -0
- package/src/services/topic/index.ts +16 -0
|
@@ -3,173 +3,199 @@ globs: *.test.ts,*.test.tsx
|
|
|
3
3
|
alwaysApply: false
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# LobeChat Testing Guide
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Test Overview
|
|
9
9
|
|
|
10
|
-
LobeChat
|
|
10
|
+
LobeChat testing consists of **E2E tests** and **Unit tests**. This guide focuses on **Unit tests**.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Unit tests are organized into three main categories:
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
```plaintext
|
|
15
|
+
+---------------------+---------------------------+-----------------------------+
|
|
16
|
+
| Category | Location | Config File |
|
|
17
|
+
+---------------------+---------------------------+-----------------------------+
|
|
18
|
+
| Next.js Webapp | src/**/*.test.ts(x) | vitest.config.ts |
|
|
19
|
+
| Packages | packages/*/**/*.test.ts | packages/*/vitest.config.ts |
|
|
20
|
+
| Desktop App | apps/desktop/**/*.test.ts | apps/desktop/vitest.config.ts |
|
|
21
|
+
+---------------------+---------------------------+-----------------------------+
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Next.js Webapp Tests
|
|
25
|
+
|
|
26
|
+
- **Config File**: `vitest.config.ts`
|
|
27
|
+
- **Environment**: Happy DOM (browser environment simulation)
|
|
28
|
+
- **Database**: PGLite (PostgreSQL for browser environments)
|
|
29
|
+
- **Setup File**: `tests/setup.ts`
|
|
30
|
+
- **Purpose**: Testing React components, hooks, stores, utilities, and client-side logic
|
|
31
|
+
|
|
32
|
+
### Packages Tests
|
|
33
|
+
|
|
34
|
+
Most packages use standard Vitest configuration. However, the `database` package is special:
|
|
19
35
|
|
|
20
|
-
|
|
36
|
+
#### Database Package (Special Case)
|
|
21
37
|
|
|
22
|
-
|
|
38
|
+
The database package supports **dual-environment testing**:
|
|
23
39
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- **用途**: 测试数据库模型、服务端逻辑、API 端点等
|
|
29
|
-
- **设置文件**: [packages/database/tests/setup-db.ts](mdc:packages/database/tests/setup-db.ts)
|
|
40
|
+
| Environment | Database | Config | Use Case |
|
|
41
|
+
|------------------|-----------------|---------------------------------------|-----------------------------------|
|
|
42
|
+
| Client (Default) | PGLite | `packages/database/vitest.config.mts` | Fast local development |
|
|
43
|
+
| Server | Real PostgreSQL | Set `TEST_SERVER_DB=1` | CI/CD, compatibility verification |
|
|
30
44
|
|
|
31
|
-
|
|
45
|
+
Server environment details:
|
|
32
46
|
|
|
33
|
-
**
|
|
47
|
+
- **Concurrency**: Single-threaded (`singleFork: true`)
|
|
48
|
+
- **Setup File**: `packages/database/tests/setup-db.ts`
|
|
49
|
+
- **Requirement**: `DATABASE_TEST_URL` environment variable must be set
|
|
34
50
|
|
|
35
|
-
###
|
|
51
|
+
### Desktop App Tests
|
|
52
|
+
|
|
53
|
+
- **Config File**: `apps/desktop/vitest.config.ts`
|
|
54
|
+
- **Environment**: Node.js
|
|
55
|
+
- **Purpose**: Testing Electron main process controllers, IPC handlers, and desktop-specific logic
|
|
56
|
+
|
|
57
|
+
## Test Commands
|
|
58
|
+
|
|
59
|
+
**Performance Warning**: The project contains 3000+ test cases. A full run takes approximately 10 minutes. Always use file filtering or test name filtering.
|
|
60
|
+
|
|
61
|
+
### Recommended Command Format
|
|
36
62
|
|
|
37
63
|
```bash
|
|
38
|
-
#
|
|
39
|
-
bunx vitest run --silent='passed-only' #
|
|
40
|
-
cd packages/database && TEST_SERVER_DB=1 bunx vitest run --silent='passed-only' #
|
|
64
|
+
# Run all client/server tests
|
|
65
|
+
bunx vitest run --silent='passed-only' # Client tests
|
|
66
|
+
cd packages/database && TEST_SERVER_DB=1 bunx vitest run --silent='passed-only' # Server tests
|
|
41
67
|
|
|
42
|
-
#
|
|
68
|
+
# Run specific test file (supports fuzzy matching)
|
|
43
69
|
bunx vitest run --silent='passed-only' user.test.ts
|
|
44
70
|
|
|
45
|
-
#
|
|
71
|
+
# Run specific test case by name (using -t flag)
|
|
46
72
|
bunx vitest run --silent='passed-only' -t "test case name"
|
|
47
73
|
|
|
48
|
-
#
|
|
74
|
+
# Combine file and test name filtering
|
|
49
75
|
bunx vitest run --silent='passed-only' filename.test.ts -t "specific test"
|
|
50
76
|
|
|
51
|
-
#
|
|
77
|
+
# Generate coverage report (using --coverage flag)
|
|
52
78
|
bunx vitest run --silent='passed-only' --coverage
|
|
53
79
|
```
|
|
54
80
|
|
|
55
|
-
###
|
|
81
|
+
### Commands to Avoid
|
|
56
82
|
|
|
57
83
|
```bash
|
|
58
|
-
#
|
|
84
|
+
# ❌ These commands run all 3000+ test cases, taking ~10 minutes!
|
|
59
85
|
npm test
|
|
60
86
|
npm test some-file.test.ts
|
|
61
87
|
|
|
62
|
-
#
|
|
88
|
+
# ❌ Don't use bare vitest (enters watch mode)
|
|
63
89
|
vitest test-file.test.ts
|
|
64
90
|
```
|
|
65
91
|
|
|
66
|
-
##
|
|
92
|
+
## Test Fixing Principles
|
|
67
93
|
|
|
68
|
-
###
|
|
94
|
+
### Core Principles
|
|
69
95
|
|
|
70
|
-
1.
|
|
71
|
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
96
|
+
1. **Gather Sufficient Context**
|
|
97
|
+
Before fixing tests, ensure you:
|
|
98
|
+
- Fully understand the test's intent and implementation
|
|
99
|
+
- Strongly recommended: review the current git diff and PR diff
|
|
74
100
|
|
|
75
|
-
2.
|
|
76
|
-
|
|
101
|
+
2. **Prioritize Test Fixes**
|
|
102
|
+
If the test itself is incorrect, fix the test first rather than the implementation code.
|
|
77
103
|
|
|
78
|
-
3.
|
|
79
|
-
|
|
104
|
+
3. **Focus on a Single Issue**
|
|
105
|
+
Only fix the specified test; don't add extra tests along the way.
|
|
80
106
|
|
|
81
|
-
4.
|
|
82
|
-
|
|
107
|
+
4. **Don't Act Unilaterally**
|
|
108
|
+
When discovering other issues, don't modify them directly—raise and discuss first.
|
|
83
109
|
|
|
84
|
-
###
|
|
110
|
+
### Testing Collaboration Best Practices
|
|
85
111
|
|
|
86
|
-
|
|
112
|
+
Important collaboration principles based on real development experience:
|
|
87
113
|
|
|
88
|
-
#### 1.
|
|
114
|
+
#### 1. Failure Handling Strategy
|
|
89
115
|
|
|
90
|
-
|
|
116
|
+
**Core Principle**: Avoid blind retries; quickly identify problems and seek help.
|
|
91
117
|
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
118
|
+
- **Failure Threshold**: After 1-2 consecutive failed fix attempts, stop immediately
|
|
119
|
+
- **Problem Summary**: Analyze failure reasons and document attempted solutions with their failure causes
|
|
120
|
+
- **Seek Help**: Approach the team with a clear problem summary and attempt history
|
|
121
|
+
- **Avoid the Trap**: Don't fall into the loop of repeatedly trying the same or similar approaches
|
|
96
122
|
|
|
97
123
|
```typescript
|
|
98
|
-
//
|
|
99
|
-
//
|
|
124
|
+
// ❌ Wrong approach: Keep blindly trying after consecutive failures
|
|
125
|
+
// 3rd, 4th attempts still using similar methods to fix the same problem
|
|
100
126
|
|
|
101
|
-
//
|
|
127
|
+
// ✅ Correct approach: Summarize after 1-2 failures
|
|
102
128
|
/*
|
|
103
|
-
|
|
104
|
-
1.
|
|
105
|
-
2.
|
|
106
|
-
3.
|
|
107
|
-
4.
|
|
129
|
+
Problem Summary:
|
|
130
|
+
1. Attempted method: Modified mock data structure
|
|
131
|
+
2. Failure reason: Still getting type mismatch error
|
|
132
|
+
3. Specific error: Expected 'UserData' but received 'UserProfile'
|
|
133
|
+
4. Help needed: Unsure about the latest UserData interface definition
|
|
108
134
|
*/
|
|
109
135
|
```
|
|
110
136
|
|
|
111
|
-
#### 2.
|
|
137
|
+
#### 2. Test Case Naming Conventions
|
|
112
138
|
|
|
113
|
-
|
|
139
|
+
**Core Principle**: Tests should focus on "behavior," not "implementation details."
|
|
114
140
|
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
141
|
+
- **Describe Business Scenarios**: `describe` and `it` titles should describe specific business scenarios and expected behaviors
|
|
142
|
+
- **Avoid Implementation Binding**: Don't mention specific line numbers, coverage goals, or implementation details in test names
|
|
143
|
+
- **Maintain Stability**: Test names should remain meaningful after code refactoring
|
|
118
144
|
|
|
119
145
|
```typescript
|
|
120
|
-
//
|
|
146
|
+
// ❌ Poor test naming
|
|
121
147
|
describe('User component coverage', () => {
|
|
122
148
|
it('covers line 45-50 in getUserData', () => {
|
|
123
|
-
//
|
|
149
|
+
// Test written just to cover lines 45-50
|
|
124
150
|
});
|
|
125
151
|
|
|
126
152
|
it('tests the else branch', () => {
|
|
127
|
-
//
|
|
153
|
+
// Exists only to test a specific branch
|
|
128
154
|
});
|
|
129
155
|
});
|
|
130
156
|
|
|
131
|
-
//
|
|
157
|
+
// ✅ Good test naming
|
|
132
158
|
describe('<UserAvatar />', () => {
|
|
133
159
|
it('should render fallback icon when image url is not provided', () => {
|
|
134
|
-
//
|
|
160
|
+
// Tests a specific business scenario, naturally covering relevant code branches
|
|
135
161
|
});
|
|
136
162
|
|
|
137
163
|
it('should display user initials when avatar image fails to load', () => {
|
|
138
|
-
//
|
|
164
|
+
// Describes user behavior and expected outcome
|
|
139
165
|
});
|
|
140
166
|
});
|
|
141
167
|
```
|
|
142
168
|
|
|
143
|
-
|
|
169
|
+
**The Right Approach to Improving Coverage**:
|
|
144
170
|
|
|
145
|
-
-
|
|
146
|
-
-
|
|
171
|
+
- Naturally improve coverage by designing various business scenarios (happy paths, edge cases, error handling)
|
|
172
|
+
- Don't write tests just to hit coverage numbers, and never comment "to cover line xxx" in tests
|
|
147
173
|
|
|
148
|
-
#### 3.
|
|
174
|
+
#### 3. Test Organization Structure
|
|
149
175
|
|
|
150
|
-
|
|
176
|
+
**Core Principle**: Maintain a clear test hierarchy; avoid redundant top-level test blocks.
|
|
151
177
|
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
-
-
|
|
178
|
+
- **Reuse Existing Structure**: When adding new tests, first look for an appropriate place in existing `describe` blocks
|
|
179
|
+
- **Logical Grouping**: Related test cases should be organized within the same `describe` block
|
|
180
|
+
- **Avoid Fragmentation**: Don't create a new top-level `describe` block for a single test case
|
|
155
181
|
|
|
156
182
|
```typescript
|
|
157
|
-
//
|
|
183
|
+
// ❌ Poor organization: Too many top-level blocks
|
|
158
184
|
describe('<UserProfile />', () => {
|
|
159
185
|
it('should render user name', () => {});
|
|
160
186
|
});
|
|
161
187
|
|
|
162
188
|
describe('UserProfile new prop test', () => {
|
|
163
|
-
//
|
|
189
|
+
// Unnecessary new block
|
|
164
190
|
it('should handle email display', () => {});
|
|
165
191
|
});
|
|
166
192
|
|
|
167
193
|
describe('UserProfile edge cases', () => {
|
|
168
|
-
//
|
|
194
|
+
// Unnecessary new block
|
|
169
195
|
it('should handle missing avatar', () => {});
|
|
170
196
|
});
|
|
171
197
|
|
|
172
|
-
//
|
|
198
|
+
// ✅ Good organization: Merge related tests
|
|
173
199
|
describe('<UserProfile />', () => {
|
|
174
200
|
it('should render user name', () => {});
|
|
175
201
|
|
|
@@ -178,78 +204,78 @@ describe('<UserProfile />', () => {
|
|
|
178
204
|
it('should handle missing avatar', () => {});
|
|
179
205
|
|
|
180
206
|
describe('when user data is incomplete', () => {
|
|
181
|
-
//
|
|
207
|
+
// Only create sub-groups when there are multiple related sub-scenarios
|
|
182
208
|
it('should show placeholder for missing name', () => {});
|
|
183
209
|
it('should hide email section when email is undefined', () => {});
|
|
184
210
|
});
|
|
185
211
|
});
|
|
186
212
|
```
|
|
187
213
|
|
|
188
|
-
|
|
214
|
+
**Organization Decision Flow**:
|
|
189
215
|
|
|
190
|
-
1.
|
|
191
|
-
2.
|
|
192
|
-
3.
|
|
216
|
+
1. Is there a logically related existing `describe` block? → If yes, add to it
|
|
217
|
+
2. Are there multiple (3+) related test cases? → If yes, consider creating a new sub-`describe`
|
|
218
|
+
3. Is it an independent, unrelated feature module? → Only then consider creating a new top-level `describe`
|
|
193
219
|
|
|
194
|
-
###
|
|
220
|
+
### Test Fixing Workflow
|
|
195
221
|
|
|
196
|
-
1.
|
|
197
|
-
2.
|
|
198
|
-
3.
|
|
199
|
-
4.
|
|
200
|
-
5.
|
|
201
|
-
6.
|
|
222
|
+
1. **Reproduce the Issue**: Locate and run the failing test; confirm it can be reproduced locally
|
|
223
|
+
2. **Analyze the Cause**: Read test code, error logs, and Git history of related files
|
|
224
|
+
3. **Form a Hypothesis**: Determine if the problem is in test logic, implementation code, or environment configuration
|
|
225
|
+
4. **Fix and Verify**: Apply the fix based on your hypothesis; rerun the test to confirm it passes
|
|
226
|
+
5. **Expand Verification**: Run all tests in the current file to ensure no new issues were introduced
|
|
227
|
+
6. **Write a Summary**: Document the error cause and fix method
|
|
202
228
|
|
|
203
|
-
###
|
|
229
|
+
### Post-Fix Summary
|
|
204
230
|
|
|
205
|
-
|
|
231
|
+
After completing a test fix, provide a brief explanation including:
|
|
206
232
|
|
|
207
|
-
1.
|
|
208
|
-
-
|
|
209
|
-
-
|
|
210
|
-
-
|
|
211
|
-
-
|
|
233
|
+
1. **Root Cause Analysis**: Explain the fundamental reason for the test failure
|
|
234
|
+
- Test logic error
|
|
235
|
+
- Implementation bug
|
|
236
|
+
- Environment configuration issue
|
|
237
|
+
- Dependency change
|
|
212
238
|
|
|
213
|
-
2.
|
|
214
|
-
-
|
|
215
|
-
-
|
|
216
|
-
-
|
|
239
|
+
2. **Fix Description**: Briefly describe the fix approach
|
|
240
|
+
- Which files were modified
|
|
241
|
+
- What solution was applied
|
|
242
|
+
- Why this fix approach was chosen
|
|
217
243
|
|
|
218
|
-
|
|
244
|
+
**Example Format**:
|
|
219
245
|
|
|
220
246
|
```markdown
|
|
221
|
-
##
|
|
247
|
+
## Test Fix Summary
|
|
222
248
|
|
|
223
|
-
|
|
249
|
+
**Root Cause**: The mock data format in the test didn't match the actual API response format, causing assertion failures.
|
|
224
250
|
|
|
225
|
-
|
|
251
|
+
**Fix**: Updated the mock data structure in the test file to match the latest API response format. Specifically modified the `mockUserData` object structure in `user.test.ts`.
|
|
226
252
|
```
|
|
227
253
|
|
|
228
|
-
##
|
|
254
|
+
## Test Writing Best Practices
|
|
229
255
|
|
|
230
|
-
### Mock
|
|
256
|
+
### Mock Data Strategy: Aim for "Low-Cost Authenticity"
|
|
231
257
|
|
|
232
|
-
|
|
258
|
+
**Core Principle**: Test data should default to authenticity; only simplify when it introduces "high testing costs."
|
|
233
259
|
|
|
234
|
-
####
|
|
260
|
+
#### What Are "High Testing Costs"?
|
|
235
261
|
|
|
236
|
-
"
|
|
262
|
+
"High cost" refers to introducing external dependencies in tests that make them slow, unstable, or complex:
|
|
237
263
|
|
|
238
|
-
-
|
|
239
|
-
-
|
|
240
|
-
-
|
|
264
|
+
- **File I/O Operations**: Reading/writing disk files
|
|
265
|
+
- **Network Requests**: HTTP calls, database connections
|
|
266
|
+
- **System Calls**: Getting system time, environment variables, etc.
|
|
241
267
|
|
|
242
|
-
####
|
|
268
|
+
#### Recommended Approach: Mock Dependencies, Keep Real Data
|
|
243
269
|
|
|
244
270
|
```typescript
|
|
245
|
-
//
|
|
271
|
+
// ✅ Good approach: Mock I/O operations but use real file content formats
|
|
246
272
|
describe('parseContentType', () => {
|
|
247
273
|
beforeEach(() => {
|
|
248
|
-
// Mock
|
|
274
|
+
// Mock file read operation (avoid real I/O)
|
|
249
275
|
vi.spyOn(fs, 'readFileSync').mockImplementation((path) => {
|
|
250
|
-
//
|
|
251
|
-
if (path.includes('.pdf')) return '%PDF-1.4\n%âãÏÓ'; //
|
|
252
|
-
if (path.includes('.png')) return '\x89PNG\r\n\x1a\n'; //
|
|
276
|
+
// But return real file content formats
|
|
277
|
+
if (path.includes('.pdf')) return '%PDF-1.4\n%âãÏÓ'; // Real PDF header
|
|
278
|
+
if (path.includes('.png')) return '\x89PNG\r\n\x1a\n'; // Real PNG header
|
|
253
279
|
return '';
|
|
254
280
|
});
|
|
255
281
|
});
|
|
@@ -260,40 +286,38 @@ describe('parseContentType', () => {
|
|
|
260
286
|
});
|
|
261
287
|
});
|
|
262
288
|
|
|
263
|
-
//
|
|
289
|
+
// ❌ Over-simplified: Using unrealistic data
|
|
264
290
|
describe('parseContentType', () => {
|
|
265
291
|
it('should detect PDF content type correctly', () => {
|
|
266
|
-
//
|
|
292
|
+
// This simplified data has no test value
|
|
267
293
|
const result = parseContentType('fake-pdf-content');
|
|
268
294
|
expect(result).toBe('application/pdf');
|
|
269
295
|
});
|
|
270
296
|
});
|
|
271
297
|
```
|
|
272
298
|
|
|
273
|
-
####
|
|
299
|
+
#### The Value of Real Identifiers
|
|
274
300
|
|
|
275
301
|
```typescript
|
|
276
|
-
// ✅
|
|
302
|
+
// ✅ Use real identifiers
|
|
277
303
|
const result = parseModelString('openai', '+gpt-4,+gpt-3.5-turbo');
|
|
278
304
|
|
|
279
|
-
// ❌
|
|
305
|
+
// ❌ Use placeholders (lower value)
|
|
280
306
|
const result = parseModelString('test-provider', '+model1,+model2');
|
|
281
307
|
```
|
|
282
308
|
|
|
283
|
-
###
|
|
284
|
-
|
|
285
|
-
**环境设置 + Mock方法结合使用**
|
|
309
|
+
### Modern Mocking Techniques: Environment Setup and Mock Methods
|
|
286
310
|
|
|
287
|
-
|
|
311
|
+
When testing client-side code, use environment annotations with modern mock methods:
|
|
288
312
|
|
|
289
313
|
```typescript
|
|
290
314
|
/**
|
|
291
|
-
* @vitest-environment happy-dom //
|
|
315
|
+
* @vitest-environment happy-dom // Provides browser APIs
|
|
292
316
|
*/
|
|
293
317
|
import { beforeEach, vi } from 'vitest';
|
|
294
318
|
|
|
295
319
|
beforeEach(() => {
|
|
296
|
-
//
|
|
320
|
+
// Modern method 1: Use vi.stubGlobal instead of global.xxx = ...
|
|
297
321
|
const mockImage = vi.fn().mockImplementation(() => ({
|
|
298
322
|
addEventListener: vi.fn(),
|
|
299
323
|
naturalHeight: 600,
|
|
@@ -301,72 +325,72 @@ beforeEach(() => {
|
|
|
301
325
|
}));
|
|
302
326
|
vi.stubGlobal('Image', mockImage);
|
|
303
327
|
|
|
304
|
-
//
|
|
328
|
+
// Modern method 2: Use vi.spyOn to preserve original functionality, only mock specific methods
|
|
305
329
|
vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:mock-url');
|
|
306
330
|
vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => {});
|
|
307
331
|
});
|
|
308
332
|
```
|
|
309
333
|
|
|
310
|
-
|
|
334
|
+
#### Environment Selection Priority
|
|
311
335
|
|
|
312
|
-
1. **@vitest-environment happy-dom** (
|
|
313
|
-
2. **@vitest-environment jsdom** -
|
|
314
|
-
3.
|
|
336
|
+
1. **@vitest-environment happy-dom** (Recommended) - Lightweight, fast, already installed in the project
|
|
337
|
+
2. **@vitest-environment jsdom** - Full-featured, but requires additional jsdom package installation
|
|
338
|
+
3. **No environment set** - Node.js environment, requires manually mocking all browser APIs
|
|
315
339
|
|
|
316
|
-
|
|
340
|
+
#### Mock Method Comparison
|
|
317
341
|
|
|
318
342
|
```typescript
|
|
319
|
-
// ❌
|
|
343
|
+
// ❌ Old method: Directly manipulating global object (type issues)
|
|
320
344
|
global.Image = mockImage;
|
|
321
345
|
global.URL = { ...global.URL, createObjectURL: mockFn };
|
|
322
346
|
|
|
323
|
-
// ✅
|
|
324
|
-
vi.stubGlobal('Image', mockImage); //
|
|
325
|
-
vi.spyOn(URL, 'createObjectURL'); //
|
|
347
|
+
// ✅ Modern method: Type-safe vi API
|
|
348
|
+
vi.stubGlobal('Image', mockImage); // Completely replace global object
|
|
349
|
+
vi.spyOn(URL, 'createObjectURL'); // Partial mock, preserve other functionality
|
|
326
350
|
```
|
|
327
351
|
|
|
328
|
-
###
|
|
352
|
+
### Test Coverage Principles: Code Branches Over Test Quantity
|
|
329
353
|
|
|
330
|
-
|
|
354
|
+
**Core Principle**: Prioritize covering all code branches rather than writing many repetitive test cases.
|
|
331
355
|
|
|
332
356
|
```typescript
|
|
333
|
-
// ❌
|
|
357
|
+
// ❌ Over-testing: 29 test cases all validating the same branch
|
|
334
358
|
describe('getImageDimensions', () => {
|
|
335
359
|
it('should reject .txt files');
|
|
336
360
|
it('should reject .pdf files');
|
|
337
|
-
// ... 25
|
|
361
|
+
// ... 25 similar tests, all hitting the same validation branch
|
|
338
362
|
});
|
|
339
363
|
|
|
340
|
-
// ✅
|
|
364
|
+
// ✅ Lean testing: 4 core cases covering all branches
|
|
341
365
|
describe('getImageDimensions', () => {
|
|
342
|
-
it('should return dimensions for valid File object'); //
|
|
343
|
-
it('should return dimensions for valid data URI'); //
|
|
344
|
-
it('should return undefined for invalid inputs'); //
|
|
345
|
-
it('should return undefined when image fails to load'); //
|
|
366
|
+
it('should return dimensions for valid File object'); // Success path - File
|
|
367
|
+
it('should return dimensions for valid data URI'); // Success path - String
|
|
368
|
+
it('should return undefined for invalid inputs'); // Input validation branch
|
|
369
|
+
it('should return undefined when image fails to load'); // Error handling branch
|
|
346
370
|
});
|
|
347
371
|
```
|
|
348
372
|
|
|
349
|
-
|
|
373
|
+
#### Branch Coverage Strategy
|
|
350
374
|
|
|
351
|
-
1.
|
|
352
|
-
2.
|
|
353
|
-
3.
|
|
354
|
-
4.
|
|
375
|
+
1. **Success Paths** - One test per input type is sufficient
|
|
376
|
+
2. **Boundary Conditions** - Consolidate similar scenarios into a single test
|
|
377
|
+
3. **Error Handling** - Test representative errors only
|
|
378
|
+
4. **Business Logic** - Cover all if/else branches
|
|
355
379
|
|
|
356
|
-
|
|
380
|
+
#### Reasonable Test Counts
|
|
357
381
|
|
|
358
|
-
-
|
|
359
|
-
-
|
|
360
|
-
-
|
|
382
|
+
- Simple utility functions: 2-5 tests
|
|
383
|
+
- Complex business logic: 5-10 tests
|
|
384
|
+
- Core security features: Add more as needed, but avoid duplicate paths
|
|
361
385
|
|
|
362
|
-
###
|
|
386
|
+
### Error Handling Tests: Test "Behavior" Not "Text"
|
|
363
387
|
|
|
364
|
-
|
|
388
|
+
**Core Principle**: Tests should verify that program behavior is predictable when errors occur, not verify error message text that may change.
|
|
365
389
|
|
|
366
|
-
####
|
|
390
|
+
#### Recommended Error Testing Approach
|
|
367
391
|
|
|
368
392
|
```typescript
|
|
369
|
-
// ✅
|
|
393
|
+
// ✅ Test error types and properties
|
|
370
394
|
expect(() => validateUser({})).toThrow(ValidationError);
|
|
371
395
|
expect(() => processPayment({})).toThrow(
|
|
372
396
|
expect.objectContaining({
|
|
@@ -375,136 +399,136 @@ expect(() => processPayment({})).toThrow(
|
|
|
375
399
|
}),
|
|
376
400
|
);
|
|
377
401
|
|
|
378
|
-
// ❌
|
|
379
|
-
expect(() => processUser({})).toThrow('
|
|
402
|
+
// ❌ Avoid testing specific error text
|
|
403
|
+
expect(() => processUser({})).toThrow('User data cannot be empty, please check input parameters');
|
|
380
404
|
```
|
|
381
405
|
|
|
382
|
-
###
|
|
406
|
+
### Troubleshooting: Beware of Module Pollution
|
|
383
407
|
|
|
384
|
-
|
|
408
|
+
**Warning Signs**: When your tests exhibit these "mysterious" behaviors, suspect module pollution first:
|
|
385
409
|
|
|
386
|
-
-
|
|
387
|
-
-
|
|
388
|
-
- Mock
|
|
410
|
+
- A test passes when run alone but fails when run with other tests
|
|
411
|
+
- Test execution order affects results
|
|
412
|
+
- Mock setup appears correct but actually uses an old mock version
|
|
389
413
|
|
|
390
|
-
####
|
|
414
|
+
#### Typical Scenario: Dynamic Mocking of the Same Module
|
|
391
415
|
|
|
392
416
|
```typescript
|
|
393
|
-
// ❌
|
|
417
|
+
// ❌ Problem: Dynamic mocking of the same module
|
|
394
418
|
it('dev mode', async () => {
|
|
395
419
|
vi.doMock('./config', () => ({ isDev: true }));
|
|
396
|
-
const { getSettings } = await import('./service'); //
|
|
420
|
+
const { getSettings } = await import('./service'); // May use cache
|
|
397
421
|
});
|
|
398
422
|
|
|
399
|
-
// ✅
|
|
423
|
+
// ✅ Solution: Clear module cache
|
|
400
424
|
beforeEach(() => {
|
|
401
|
-
vi.resetModules(); //
|
|
425
|
+
vi.resetModules(); // Ensure each test has a clean environment
|
|
402
426
|
});
|
|
403
427
|
```
|
|
404
428
|
|
|
405
|
-
|
|
429
|
+
**Remember**: `vi.resetModules()` is the ultimate weapon for resolving "mysterious" test failures.
|
|
406
430
|
|
|
407
|
-
##
|
|
431
|
+
## Test File Organization
|
|
408
432
|
|
|
409
|
-
###
|
|
433
|
+
### File Naming Convention
|
|
410
434
|
|
|
411
|
-
`*.test.ts`, `*.test.tsx` (
|
|
435
|
+
`*.test.ts`, `*.test.tsx` (any location)
|
|
412
436
|
|
|
413
|
-
###
|
|
437
|
+
### Test File Organization Style
|
|
414
438
|
|
|
415
|
-
|
|
439
|
+
The project uses a **co-located test files** organization style:
|
|
416
440
|
|
|
417
|
-
-
|
|
418
|
-
-
|
|
441
|
+
- Test files are placed in the same directory as the corresponding source files
|
|
442
|
+
- Naming format: `originalFileName.test.ts` or `originalFileName.test.tsx`
|
|
419
443
|
|
|
420
|
-
|
|
444
|
+
Example:
|
|
421
445
|
|
|
422
446
|
```plaintext
|
|
423
447
|
src/components/Button/
|
|
424
|
-
├── index.tsx #
|
|
425
|
-
└── index.test.tsx #
|
|
448
|
+
├── index.tsx # Source file
|
|
449
|
+
└── index.test.tsx # Test file
|
|
426
450
|
```
|
|
427
451
|
|
|
428
|
-
-
|
|
429
|
-
-
|
|
452
|
+
- In some cases, tests are consolidated in a `__tests__` folder, e.g., `packages/database/src/models/__tests__`
|
|
453
|
+
- Test helper files are placed in a fixtures folder
|
|
430
454
|
|
|
431
|
-
##
|
|
455
|
+
## Test Debugging Tips
|
|
432
456
|
|
|
433
|
-
###
|
|
457
|
+
### Test Debugging Steps
|
|
434
458
|
|
|
435
|
-
1.
|
|
436
|
-
2.
|
|
437
|
-
3.
|
|
438
|
-
4.
|
|
459
|
+
1. **Determine Test Environment**: Select the correct config file based on file path
|
|
460
|
+
2. **Isolate the Problem**: Use the `-t` flag to run only the failing test case
|
|
461
|
+
3. **Analyze the Error**: Carefully read error messages, stack traces, and recent file modification history
|
|
462
|
+
4. **Add Debugging**: Add `console.log` statements in tests to understand execution flow
|
|
439
463
|
|
|
440
|
-
### TypeScript
|
|
464
|
+
### TypeScript Type Handling
|
|
441
465
|
|
|
442
|
-
|
|
466
|
+
In tests, you can relax TypeScript type checking to improve writing efficiency and readability:
|
|
443
467
|
|
|
444
|
-
####
|
|
468
|
+
#### Recommended Type Relaxation Strategies
|
|
445
469
|
|
|
446
470
|
```typescript
|
|
447
|
-
//
|
|
471
|
+
// Use non-null assertion to access properties you're certain exist in tests
|
|
448
472
|
const result = await someFunction();
|
|
449
473
|
expect(result!.data).toBeDefined();
|
|
450
474
|
expect(result!.status).toBe('success');
|
|
451
475
|
|
|
452
|
-
//
|
|
476
|
+
// Use any type to simplify complex mock setups
|
|
453
477
|
const mockStream = new ReadableStream() as any;
|
|
454
478
|
mockStream.toReadableStream = () => mockStream;
|
|
455
479
|
|
|
456
|
-
//
|
|
457
|
-
await instance['getFromCache']('key'); //
|
|
458
|
-
await (instance as any).getFromCache('key'); //
|
|
480
|
+
// Access private members
|
|
481
|
+
await instance['getFromCache']('key'); // Bracket notation recommended
|
|
482
|
+
await (instance as any).getFromCache('key'); // Avoid as any
|
|
459
483
|
```
|
|
460
484
|
|
|
461
|
-
####
|
|
485
|
+
#### Applicable Scenarios
|
|
462
486
|
|
|
463
|
-
- **Mock
|
|
464
|
-
-
|
|
465
|
-
-
|
|
466
|
-
-
|
|
467
|
-
-
|
|
487
|
+
- **Mock Objects**: Use `as any` for test mock data to avoid complex type definitions
|
|
488
|
+
- **Third-Party Libraries**: Use `any` appropriately when handling complex third-party library types
|
|
489
|
+
- **Test Assertions**: Use `!` non-null assertion in test scenarios where you're certain the object exists
|
|
490
|
+
- **Private Member Access**: Prefer bracket notation `instance['privateMethod']()` over `(instance as any).privateMethod()`
|
|
491
|
+
- **Temporary Debugging**: When quickly writing tests, use `any` first to ensure functionality, then optionally optimize types later
|
|
468
492
|
|
|
469
|
-
####
|
|
493
|
+
#### Important Notes
|
|
470
494
|
|
|
471
|
-
-
|
|
472
|
-
-
|
|
473
|
-
-
|
|
474
|
-
-
|
|
495
|
+
- **Use Moderately**: Don't over-rely on `any`; core business logic types should remain strict
|
|
496
|
+
- **Private Member Access Priority**: Bracket notation > `as any` casting for better type safety
|
|
497
|
+
- **Documentation**: Add comments explaining the reason for complex `any` usage scenarios
|
|
498
|
+
- **Test Coverage**: Ensure tests still effectively verify correctness even when using `any`
|
|
475
499
|
|
|
476
|
-
###
|
|
500
|
+
### Checking Recent Modifications
|
|
477
501
|
|
|
478
|
-
|
|
502
|
+
**Core Principle**: When tests suddenly fail, first check recent code changes.
|
|
479
503
|
|
|
480
|
-
####
|
|
504
|
+
#### Quick Check Methods
|
|
481
505
|
|
|
482
506
|
```bash
|
|
483
|
-
git status #
|
|
484
|
-
git diff HEAD -- '*.test.*' #
|
|
485
|
-
git diff main...HEAD #
|
|
486
|
-
gh pr diff #
|
|
507
|
+
git status # View current modification status
|
|
508
|
+
git diff HEAD -- '*.test.*' # Check test file changes
|
|
509
|
+
git diff main...HEAD # Compare with main branch
|
|
510
|
+
gh pr diff # View all changes in the PR
|
|
487
511
|
```
|
|
488
512
|
|
|
489
|
-
####
|
|
513
|
+
#### Common Causes and Solutions
|
|
490
514
|
|
|
491
|
-
-
|
|
492
|
-
-
|
|
515
|
+
- **Latest commit introduced a bug** → Check and fix the implementation code
|
|
516
|
+
- **Branch code is outdated** → `git rebase main` to sync with main branch
|
|
493
517
|
|
|
494
|
-
##
|
|
518
|
+
## Special Testing Scenarios
|
|
495
519
|
|
|
496
|
-
|
|
520
|
+
For special testing scenarios, refer to the related rules:
|
|
497
521
|
|
|
498
|
-
-
|
|
499
|
-
-
|
|
522
|
+
- `electron-ipc-test.mdc` - Electron IPC Interface Testing Strategy
|
|
523
|
+
- `db-model-test.mdc` - Database Model Testing Guide
|
|
500
524
|
|
|
501
|
-
##
|
|
525
|
+
## Key Takeaways
|
|
502
526
|
|
|
503
|
-
-
|
|
504
|
-
-
|
|
505
|
-
-
|
|
506
|
-
-
|
|
507
|
-
-
|
|
508
|
-
-
|
|
509
|
-
-
|
|
510
|
-
-
|
|
527
|
+
- **Command Format**: Use `bunx vitest run --silent='passed-only'` with file filtering
|
|
528
|
+
- **Fix Principles**: Seek help after 1-2 failures; focus test naming on behavior, not implementation details
|
|
529
|
+
- **Debug Workflow**: Reproduce → Analyze → Hypothesize → Fix → Verify → Summarize
|
|
530
|
+
- **File Organization**: Prefer adding tests to existing `describe` blocks; avoid creating redundant top-level blocks
|
|
531
|
+
- **Data Strategy**: Default to authenticity; only simplify for high-cost scenarios (I/O, network, etc.)
|
|
532
|
+
- **Error Testing**: Test error types and behavior; avoid depending on specific error message text
|
|
533
|
+
- **Module Pollution**: When tests fail "mysteriously," suspect module pollution first; use `vi.resetModules()` to resolve
|
|
534
|
+
- **Security Requirements**: Model tests must include permission checks and pass in both environments
|