@lobehub/lobehub 2.0.0-next.353 → 2.0.0-next.355
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/.agents/skills/add-provider-doc/SKILL.md +90 -0
- package/.agents/skills/add-setting-env/SKILL.md +106 -0
- package/.agents/skills/debug/SKILL.md +66 -0
- package/.agents/skills/desktop/SKILL.md +78 -0
- package/.agents/skills/desktop/references/feature-implementation.md +99 -0
- package/.agents/skills/desktop/references/local-tools.md +133 -0
- package/.agents/skills/desktop/references/menu-config.md +103 -0
- package/.agents/skills/desktop/references/window-management.md +143 -0
- package/.agents/skills/drizzle/SKILL.md +129 -0
- package/.agents/skills/drizzle/references/db-migrations.md +50 -0
- package/.agents/skills/hotkey/SKILL.md +90 -0
- package/{.cursor/rules/i18n.mdc → .agents/skills/i18n/SKILL.md} +14 -23
- package/.agents/skills/linear/SKILL.md +51 -0
- package/.agents/skills/microcopy/SKILL.md +83 -0
- package/.agents/skills/modal/SKILL.md +102 -0
- package/{.cursor/rules/project-structure.mdc → .agents/skills/project-overview/SKILL.md} +65 -37
- package/.agents/skills/react/SKILL.md +73 -0
- package/.agents/skills/react/references/layout-kit.md +100 -0
- package/.agents/skills/recent-data/SKILL.md +108 -0
- package/.agents/skills/testing/SKILL.md +89 -0
- package/.agents/skills/testing/references/agent-runtime-e2e.md +126 -0
- package/.agents/skills/testing/references/db-model-test.md +124 -0
- package/.agents/skills/testing/references/desktop-controller-test.md +124 -0
- package/.agents/skills/testing/references/electron-ipc-test.md +63 -0
- package/.agents/skills/testing/references/zustand-store-action-test.md +150 -0
- package/.agents/skills/typescript/SKILL.md +52 -0
- package/.agents/skills/zustand/SKILL.md +78 -0
- package/.agents/skills/zustand/references/action-patterns.md +125 -0
- package/.agents/skills/zustand/references/slice-organization.md +125 -0
- package/AGENTS.md +42 -55
- package/CHANGELOG.md +60 -0
- package/CLAUDE.md +57 -46
- package/GEMINI.md +47 -39
- package/changelog/v1.json +18 -0
- package/locales/en-US/plugin.json +3 -0
- package/locales/zh-CN/plugin.json +3 -0
- package/package.json +1 -1
- package/packages/builtin-tool-memory/src/client/Render/SearchUserMemory/index.tsx +3 -11
- package/packages/context-engine/src/engine/messages/MessagesEngine.ts +0 -13
- package/packages/context-engine/src/engine/messages/__tests__/MessagesEngine.test.ts +0 -25
- package/packages/database/src/models/__tests__/topics/topic.create.test.ts +3 -3
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/List/Item/index.tsx +1 -0
- package/src/app/[variants]/(main)/agent/features/Conversation/ConversationArea.tsx +4 -0
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/List/Item/index.tsx +1 -0
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/index.tsx +1 -1
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/InboxItem.tsx +19 -29
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/List.tsx +1 -1
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/ModalProvider.tsx +1 -1
- package/src/features/FileViewer/Renderer/PDF/index.tsx +2 -3
- package/src/features/ShareModal/SharePdf/PdfPreview.tsx +1 -2
- package/src/libs/pdfjs/index.tsx +25 -0
- package/src/locales/default/plugin.ts +3 -0
- package/src/server/modules/Mecha/ContextEngineering/__tests__/serverMessagesEngine.test.ts +0 -25
- package/src/services/chat/chat.test.ts +19 -19
- package/src/services/chat/index.ts +8 -3
- package/src/services/chat/mecha/agentConfigResolver.test.ts +72 -55
- package/src/services/chat/mecha/agentConfigResolver.ts +28 -4
- package/src/services/chat/mecha/contextEngineering.test.ts +21 -14
- package/src/services/chat/mecha/contextEngineering.ts +12 -0
- package/src/services/chat/types.ts +7 -1
- package/src/store/chat/agents/createAgentExecutors.ts +15 -4
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +1 -0
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +6 -2
- package/src/store/test-coverage.md +5 -5
- package/.cursor/rules/add-provider-doc.mdc +0 -183
- package/.cursor/rules/add-setting-env.mdc +0 -175
- package/.cursor/rules/cursor-rules.mdc +0 -28
- package/.cursor/rules/db-migrations.mdc +0 -46
- package/.cursor/rules/debug-usage.mdc +0 -86
- package/.cursor/rules/desktop-controller-tests.mdc +0 -189
- package/.cursor/rules/desktop-feature-implementation.mdc +0 -155
- package/.cursor/rules/desktop-local-tools-implement.mdc +0 -81
- package/.cursor/rules/desktop-menu-configuration.mdc +0 -209
- package/.cursor/rules/desktop-window-management.mdc +0 -301
- package/.cursor/rules/drizzle-schema-style-guide.mdc +0 -218
- package/.cursor/rules/hotkey.mdc +0 -162
- package/.cursor/rules/linear.mdc +0 -53
- package/.cursor/rules/microcopy-cn.mdc +0 -158
- package/.cursor/rules/microcopy-en.mdc +0 -148
- package/.cursor/rules/modal-imperative.mdc +0 -162
- package/.cursor/rules/packages/react-layout-kit.mdc +0 -122
- package/.cursor/rules/project-introduce.mdc +0 -36
- package/.cursor/rules/react.mdc +0 -169
- package/.cursor/rules/recent-data-usage.mdc +0 -139
- package/.cursor/rules/rules-index.mdc +0 -44
- package/.cursor/rules/testing-guide/agent-runtime-e2e.mdc +0 -285
- package/.cursor/rules/testing-guide/db-model-test.mdc +0 -455
- package/.cursor/rules/testing-guide/electron-ipc-test.mdc +0 -80
- package/.cursor/rules/testing-guide/testing-guide.mdc +0 -534
- package/.cursor/rules/testing-guide/zustand-store-action-test.mdc +0 -574
- package/.cursor/rules/typescript.mdc +0 -55
- package/.cursor/rules/zustand-action-patterns.mdc +0 -328
- package/.cursor/rules/zustand-slice-organization.mdc +0 -308
- package/src/libs/pdfjs/pdf.worker.ts +0 -1
- package/src/libs/pdfjs/worker.ts +0 -12
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/AGENTS.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/SKILL.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-event-handler-refs.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-use-latest.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-api-routes.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-defer-await.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-dependencies.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-parallel.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-suspense-boundaries.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-barrel-imports.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-conditional.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-defer-third-party.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-dynamic-imports.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-preload.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-event-listeners.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-localstorage-schema.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-passive-event-listeners.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-swr-dedup.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-batch-dom-css.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-function-results.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-property-access.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-storage.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-combine-iterations.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-early-exit.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-hoist-regexp.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-index-maps.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-length-check-first.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-min-max-loop.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-set-map-lookups.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-tosorted-immutable.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-activity.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-animate-svg-wrapper.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-conditional-render.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-content-visibility.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hoist-jsx.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hydration-no-flicker.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-svg-precision.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-defer-reads.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-dependencies.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-derived-state.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-functional-setstate.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-lazy-state-init.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-memo.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-transitions.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-after-nonblocking.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-lru.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-react.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-parallel-fetching.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-serialization.md +0 -0
|
@@ -1,534 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
globs: *.test.ts,*.test.tsx
|
|
3
|
-
alwaysApply: false
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# LobeChat Testing Guide
|
|
7
|
-
|
|
8
|
-
## Test Overview
|
|
9
|
-
|
|
10
|
-
LobeChat testing consists of **E2E tests** and **Unit tests**. This guide focuses on **Unit tests**.
|
|
11
|
-
|
|
12
|
-
Unit tests are organized into three main categories:
|
|
13
|
-
|
|
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:
|
|
35
|
-
|
|
36
|
-
#### Database Package (Special Case)
|
|
37
|
-
|
|
38
|
-
The database package supports **dual-environment testing**:
|
|
39
|
-
|
|
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 |
|
|
44
|
-
|
|
45
|
-
Server environment details:
|
|
46
|
-
|
|
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
|
|
50
|
-
|
|
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
|
|
62
|
-
|
|
63
|
-
```bash
|
|
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
|
|
67
|
-
|
|
68
|
-
# Run specific test file (supports fuzzy matching)
|
|
69
|
-
bunx vitest run --silent='passed-only' user.test.ts
|
|
70
|
-
|
|
71
|
-
# Run specific test case by name (using -t flag)
|
|
72
|
-
bunx vitest run --silent='passed-only' -t "test case name"
|
|
73
|
-
|
|
74
|
-
# Combine file and test name filtering
|
|
75
|
-
bunx vitest run --silent='passed-only' filename.test.ts -t "specific test"
|
|
76
|
-
|
|
77
|
-
# Generate coverage report (using --coverage flag)
|
|
78
|
-
bunx vitest run --silent='passed-only' --coverage
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Commands to Avoid
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
# ❌ These commands run all 3000+ test cases, taking ~10 minutes!
|
|
85
|
-
npm test
|
|
86
|
-
npm test some-file.test.ts
|
|
87
|
-
|
|
88
|
-
# ❌ Don't use bare vitest (enters watch mode)
|
|
89
|
-
vitest test-file.test.ts
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## Test Fixing Principles
|
|
93
|
-
|
|
94
|
-
### Core Principles
|
|
95
|
-
|
|
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
|
|
100
|
-
|
|
101
|
-
2. **Prioritize Test Fixes**
|
|
102
|
-
If the test itself is incorrect, fix the test first rather than the implementation code.
|
|
103
|
-
|
|
104
|
-
3. **Focus on a Single Issue**
|
|
105
|
-
Only fix the specified test; don't add extra tests along the way.
|
|
106
|
-
|
|
107
|
-
4. **Don't Act Unilaterally**
|
|
108
|
-
When discovering other issues, don't modify them directly—raise and discuss first.
|
|
109
|
-
|
|
110
|
-
### Testing Collaboration Best Practices
|
|
111
|
-
|
|
112
|
-
Important collaboration principles based on real development experience:
|
|
113
|
-
|
|
114
|
-
#### 1. Failure Handling Strategy
|
|
115
|
-
|
|
116
|
-
**Core Principle**: Avoid blind retries; quickly identify problems and seek help.
|
|
117
|
-
|
|
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
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
// ❌ Wrong approach: Keep blindly trying after consecutive failures
|
|
125
|
-
// 3rd, 4th attempts still using similar methods to fix the same problem
|
|
126
|
-
|
|
127
|
-
// ✅ Correct approach: Summarize after 1-2 failures
|
|
128
|
-
/*
|
|
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
|
|
134
|
-
*/
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
#### 2. Test Case Naming Conventions
|
|
138
|
-
|
|
139
|
-
**Core Principle**: Tests should focus on "behavior," not "implementation details."
|
|
140
|
-
|
|
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
|
|
144
|
-
|
|
145
|
-
```typescript
|
|
146
|
-
// ❌ Poor test naming
|
|
147
|
-
describe('User component coverage', () => {
|
|
148
|
-
it('covers line 45-50 in getUserData', () => {
|
|
149
|
-
// Test written just to cover lines 45-50
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('tests the else branch', () => {
|
|
153
|
-
// Exists only to test a specific branch
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// ✅ Good test naming
|
|
158
|
-
describe('<UserAvatar />', () => {
|
|
159
|
-
it('should render fallback icon when image url is not provided', () => {
|
|
160
|
-
// Tests a specific business scenario, naturally covering relevant code branches
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should display user initials when avatar image fails to load', () => {
|
|
164
|
-
// Describes user behavior and expected outcome
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
**The Right Approach to Improving Coverage**:
|
|
170
|
-
|
|
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
|
|
173
|
-
|
|
174
|
-
#### 3. Test Organization Structure
|
|
175
|
-
|
|
176
|
-
**Core Principle**: Maintain a clear test hierarchy; avoid redundant top-level test blocks.
|
|
177
|
-
|
|
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
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
// ❌ Poor organization: Too many top-level blocks
|
|
184
|
-
describe('<UserProfile />', () => {
|
|
185
|
-
it('should render user name', () => {});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
describe('UserProfile new prop test', () => {
|
|
189
|
-
// Unnecessary new block
|
|
190
|
-
it('should handle email display', () => {});
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
describe('UserProfile edge cases', () => {
|
|
194
|
-
// Unnecessary new block
|
|
195
|
-
it('should handle missing avatar', () => {});
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
// ✅ Good organization: Merge related tests
|
|
199
|
-
describe('<UserProfile />', () => {
|
|
200
|
-
it('should render user name', () => {});
|
|
201
|
-
|
|
202
|
-
it('should handle email display', () => {});
|
|
203
|
-
|
|
204
|
-
it('should handle missing avatar', () => {});
|
|
205
|
-
|
|
206
|
-
describe('when user data is incomplete', () => {
|
|
207
|
-
// Only create sub-groups when there are multiple related sub-scenarios
|
|
208
|
-
it('should show placeholder for missing name', () => {});
|
|
209
|
-
it('should hide email section when email is undefined', () => {});
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
**Organization Decision Flow**:
|
|
215
|
-
|
|
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`
|
|
219
|
-
|
|
220
|
-
### Test Fixing Workflow
|
|
221
|
-
|
|
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
|
|
228
|
-
|
|
229
|
-
### Post-Fix Summary
|
|
230
|
-
|
|
231
|
-
After completing a test fix, provide a brief explanation including:
|
|
232
|
-
|
|
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
|
|
238
|
-
|
|
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
|
|
243
|
-
|
|
244
|
-
**Example Format**:
|
|
245
|
-
|
|
246
|
-
```markdown
|
|
247
|
-
## Test Fix Summary
|
|
248
|
-
|
|
249
|
-
**Root Cause**: The mock data format in the test didn't match the actual API response format, causing assertion failures.
|
|
250
|
-
|
|
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`.
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## Test Writing Best Practices
|
|
255
|
-
|
|
256
|
-
### Mock Data Strategy: Aim for "Low-Cost Authenticity"
|
|
257
|
-
|
|
258
|
-
**Core Principle**: Test data should default to authenticity; only simplify when it introduces "high testing costs."
|
|
259
|
-
|
|
260
|
-
#### What Are "High Testing Costs"?
|
|
261
|
-
|
|
262
|
-
"High cost" refers to introducing external dependencies in tests that make them slow, unstable, or complex:
|
|
263
|
-
|
|
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.
|
|
267
|
-
|
|
268
|
-
#### Recommended Approach: Mock Dependencies, Keep Real Data
|
|
269
|
-
|
|
270
|
-
```typescript
|
|
271
|
-
// ✅ Good approach: Mock I/O operations but use real file content formats
|
|
272
|
-
describe('parseContentType', () => {
|
|
273
|
-
beforeEach(() => {
|
|
274
|
-
// Mock file read operation (avoid real I/O)
|
|
275
|
-
vi.spyOn(fs, 'readFileSync').mockImplementation((path) => {
|
|
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
|
|
279
|
-
return '';
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
it('should detect PDF content type correctly', () => {
|
|
284
|
-
const result = parseContentType('/path/to/file.pdf');
|
|
285
|
-
expect(result).toBe('application/pdf');
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
// ❌ Over-simplified: Using unrealistic data
|
|
290
|
-
describe('parseContentType', () => {
|
|
291
|
-
it('should detect PDF content type correctly', () => {
|
|
292
|
-
// This simplified data has no test value
|
|
293
|
-
const result = parseContentType('fake-pdf-content');
|
|
294
|
-
expect(result).toBe('application/pdf');
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
#### The Value of Real Identifiers
|
|
300
|
-
|
|
301
|
-
```typescript
|
|
302
|
-
// ✅ Use real identifiers
|
|
303
|
-
const result = parseModelString('openai', '+gpt-4,+gpt-3.5-turbo');
|
|
304
|
-
|
|
305
|
-
// ❌ Use placeholders (lower value)
|
|
306
|
-
const result = parseModelString('test-provider', '+model1,+model2');
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
### Modern Mocking Techniques: Environment Setup and Mock Methods
|
|
310
|
-
|
|
311
|
-
When testing client-side code, use environment annotations with modern mock methods:
|
|
312
|
-
|
|
313
|
-
```typescript
|
|
314
|
-
/**
|
|
315
|
-
* @vitest-environment happy-dom // Provides browser APIs
|
|
316
|
-
*/
|
|
317
|
-
import { beforeEach, vi } from 'vitest';
|
|
318
|
-
|
|
319
|
-
beforeEach(() => {
|
|
320
|
-
// Modern method 1: Use vi.stubGlobal instead of global.xxx = ...
|
|
321
|
-
const mockImage = vi.fn().mockImplementation(() => ({
|
|
322
|
-
addEventListener: vi.fn(),
|
|
323
|
-
naturalHeight: 600,
|
|
324
|
-
naturalWidth: 800,
|
|
325
|
-
}));
|
|
326
|
-
vi.stubGlobal('Image', mockImage);
|
|
327
|
-
|
|
328
|
-
// Modern method 2: Use vi.spyOn to preserve original functionality, only mock specific methods
|
|
329
|
-
vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:mock-url');
|
|
330
|
-
vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => {});
|
|
331
|
-
});
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
#### Environment Selection Priority
|
|
335
|
-
|
|
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
|
|
339
|
-
|
|
340
|
-
#### Mock Method Comparison
|
|
341
|
-
|
|
342
|
-
```typescript
|
|
343
|
-
// ❌ Old method: Directly manipulating global object (type issues)
|
|
344
|
-
global.Image = mockImage;
|
|
345
|
-
global.URL = { ...global.URL, createObjectURL: mockFn };
|
|
346
|
-
|
|
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
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
### Test Coverage Principles: Code Branches Over Test Quantity
|
|
353
|
-
|
|
354
|
-
**Core Principle**: Prioritize covering all code branches rather than writing many repetitive test cases.
|
|
355
|
-
|
|
356
|
-
```typescript
|
|
357
|
-
// ❌ Over-testing: 29 test cases all validating the same branch
|
|
358
|
-
describe('getImageDimensions', () => {
|
|
359
|
-
it('should reject .txt files');
|
|
360
|
-
it('should reject .pdf files');
|
|
361
|
-
// ... 25 similar tests, all hitting the same validation branch
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
// ✅ Lean testing: 4 core cases covering all branches
|
|
365
|
-
describe('getImageDimensions', () => {
|
|
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
|
|
370
|
-
});
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
#### Branch Coverage Strategy
|
|
374
|
-
|
|
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
|
|
379
|
-
|
|
380
|
-
#### Reasonable Test Counts
|
|
381
|
-
|
|
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
|
|
385
|
-
|
|
386
|
-
### Error Handling Tests: Test "Behavior" Not "Text"
|
|
387
|
-
|
|
388
|
-
**Core Principle**: Tests should verify that program behavior is predictable when errors occur, not verify error message text that may change.
|
|
389
|
-
|
|
390
|
-
#### Recommended Error Testing Approach
|
|
391
|
-
|
|
392
|
-
```typescript
|
|
393
|
-
// ✅ Test error types and properties
|
|
394
|
-
expect(() => validateUser({})).toThrow(ValidationError);
|
|
395
|
-
expect(() => processPayment({})).toThrow(
|
|
396
|
-
expect.objectContaining({
|
|
397
|
-
code: 'INVALID_PAYMENT_DATA',
|
|
398
|
-
statusCode: 400,
|
|
399
|
-
}),
|
|
400
|
-
);
|
|
401
|
-
|
|
402
|
-
// ❌ Avoid testing specific error text
|
|
403
|
-
expect(() => processUser({})).toThrow('User data cannot be empty, please check input parameters');
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
### Troubleshooting: Beware of Module Pollution
|
|
407
|
-
|
|
408
|
-
**Warning Signs**: When your tests exhibit these "mysterious" behaviors, suspect module pollution first:
|
|
409
|
-
|
|
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
|
|
413
|
-
|
|
414
|
-
#### Typical Scenario: Dynamic Mocking of the Same Module
|
|
415
|
-
|
|
416
|
-
```typescript
|
|
417
|
-
// ❌ Problem: Dynamic mocking of the same module
|
|
418
|
-
it('dev mode', async () => {
|
|
419
|
-
vi.doMock('./config', () => ({ isDev: true }));
|
|
420
|
-
const { getSettings } = await import('./service'); // May use cache
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
// ✅ Solution: Clear module cache
|
|
424
|
-
beforeEach(() => {
|
|
425
|
-
vi.resetModules(); // Ensure each test has a clean environment
|
|
426
|
-
});
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
**Remember**: `vi.resetModules()` is the ultimate weapon for resolving "mysterious" test failures.
|
|
430
|
-
|
|
431
|
-
## Test File Organization
|
|
432
|
-
|
|
433
|
-
### File Naming Convention
|
|
434
|
-
|
|
435
|
-
`*.test.ts`, `*.test.tsx` (any location)
|
|
436
|
-
|
|
437
|
-
### Test File Organization Style
|
|
438
|
-
|
|
439
|
-
The project uses a **co-located test files** organization style:
|
|
440
|
-
|
|
441
|
-
- Test files are placed in the same directory as the corresponding source files
|
|
442
|
-
- Naming format: `originalFileName.test.ts` or `originalFileName.test.tsx`
|
|
443
|
-
|
|
444
|
-
Example:
|
|
445
|
-
|
|
446
|
-
```plaintext
|
|
447
|
-
src/components/Button/
|
|
448
|
-
├── index.tsx # Source file
|
|
449
|
-
└── index.test.tsx # Test file
|
|
450
|
-
```
|
|
451
|
-
|
|
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
|
|
454
|
-
|
|
455
|
-
## Test Debugging Tips
|
|
456
|
-
|
|
457
|
-
### Test Debugging Steps
|
|
458
|
-
|
|
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
|
|
463
|
-
|
|
464
|
-
### TypeScript Type Handling
|
|
465
|
-
|
|
466
|
-
In tests, you can relax TypeScript type checking to improve writing efficiency and readability:
|
|
467
|
-
|
|
468
|
-
#### Recommended Type Relaxation Strategies
|
|
469
|
-
|
|
470
|
-
```typescript
|
|
471
|
-
// Use non-null assertion to access properties you're certain exist in tests
|
|
472
|
-
const result = await someFunction();
|
|
473
|
-
expect(result!.data).toBeDefined();
|
|
474
|
-
expect(result!.status).toBe('success');
|
|
475
|
-
|
|
476
|
-
// Use any type to simplify complex mock setups
|
|
477
|
-
const mockStream = new ReadableStream() as any;
|
|
478
|
-
mockStream.toReadableStream = () => mockStream;
|
|
479
|
-
|
|
480
|
-
// Access private members
|
|
481
|
-
await instance['getFromCache']('key'); // Bracket notation recommended
|
|
482
|
-
await (instance as any).getFromCache('key'); // Avoid as any
|
|
483
|
-
```
|
|
484
|
-
|
|
485
|
-
#### Applicable Scenarios
|
|
486
|
-
|
|
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
|
|
492
|
-
|
|
493
|
-
#### Important Notes
|
|
494
|
-
|
|
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`
|
|
499
|
-
|
|
500
|
-
### Checking Recent Modifications
|
|
501
|
-
|
|
502
|
-
**Core Principle**: When tests suddenly fail, first check recent code changes.
|
|
503
|
-
|
|
504
|
-
#### Quick Check Methods
|
|
505
|
-
|
|
506
|
-
```bash
|
|
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
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
#### Common Causes and Solutions
|
|
514
|
-
|
|
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
|
|
517
|
-
|
|
518
|
-
## Special Testing Scenarios
|
|
519
|
-
|
|
520
|
-
For special testing scenarios, refer to the related rules:
|
|
521
|
-
|
|
522
|
-
- `electron-ipc-test.mdc` - Electron IPC Interface Testing Strategy
|
|
523
|
-
- `db-model-test.mdc` - Database Model Testing Guide
|
|
524
|
-
|
|
525
|
-
## Key Takeaways
|
|
526
|
-
|
|
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
|