@lobehub/lobehub 2.0.0-next.352 → 2.0.0-next.354

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/.agents/skills/add-provider-doc/SKILL.md +90 -0
  2. package/.agents/skills/add-setting-env/SKILL.md +106 -0
  3. package/.agents/skills/debug/SKILL.md +66 -0
  4. package/.agents/skills/desktop/SKILL.md +78 -0
  5. package/.agents/skills/desktop/references/feature-implementation.md +99 -0
  6. package/.agents/skills/desktop/references/local-tools.md +133 -0
  7. package/.agents/skills/desktop/references/menu-config.md +103 -0
  8. package/.agents/skills/desktop/references/window-management.md +143 -0
  9. package/.agents/skills/drizzle/SKILL.md +129 -0
  10. package/.agents/skills/drizzle/references/db-migrations.md +50 -0
  11. package/.agents/skills/hotkey/SKILL.md +90 -0
  12. package/{.cursor/rules/i18n.mdc → .agents/skills/i18n/SKILL.md} +14 -23
  13. package/.agents/skills/linear/SKILL.md +51 -0
  14. package/.agents/skills/microcopy/SKILL.md +83 -0
  15. package/.agents/skills/modal/SKILL.md +102 -0
  16. package/{.cursor/rules/project-structure.mdc → .agents/skills/project-overview/SKILL.md} +65 -37
  17. package/.agents/skills/react/SKILL.md +73 -0
  18. package/.agents/skills/react/references/layout-kit.md +100 -0
  19. package/.agents/skills/recent-data/SKILL.md +108 -0
  20. package/.agents/skills/testing/SKILL.md +89 -0
  21. package/.agents/skills/testing/references/agent-runtime-e2e.md +126 -0
  22. package/.agents/skills/testing/references/db-model-test.md +124 -0
  23. package/.agents/skills/testing/references/desktop-controller-test.md +124 -0
  24. package/.agents/skills/testing/references/electron-ipc-test.md +63 -0
  25. package/.agents/skills/testing/references/zustand-store-action-test.md +150 -0
  26. package/.agents/skills/typescript/SKILL.md +52 -0
  27. package/.agents/skills/zustand/SKILL.md +78 -0
  28. package/.agents/skills/zustand/references/action-patterns.md +125 -0
  29. package/.agents/skills/zustand/references/slice-organization.md +125 -0
  30. package/AGENTS.md +42 -55
  31. package/CHANGELOG.md +58 -0
  32. package/CLAUDE.md +57 -46
  33. package/GEMINI.md +47 -39
  34. package/changelog/v1.json +14 -0
  35. package/docs/development/database-schema.dbml +5 -0
  36. package/package.json +1 -1
  37. package/packages/database/migrations/0071_add_async_task_extend.sql +5 -0
  38. package/packages/database/migrations/meta/0071_snapshot.json +10720 -0
  39. package/packages/database/migrations/meta/_journal.json +7 -0
  40. package/packages/database/src/schemas/asyncTask.ts +12 -2
  41. package/src/features/FileViewer/Renderer/PDF/index.tsx +2 -3
  42. package/src/features/ShareModal/SharePdf/PdfPreview.tsx +1 -2
  43. package/src/libs/pdfjs/index.tsx +25 -0
  44. package/src/store/test-coverage.md +5 -5
  45. package/.cursor/rules/add-provider-doc.mdc +0 -183
  46. package/.cursor/rules/add-setting-env.mdc +0 -175
  47. package/.cursor/rules/cursor-rules.mdc +0 -28
  48. package/.cursor/rules/db-migrations.mdc +0 -46
  49. package/.cursor/rules/debug-usage.mdc +0 -86
  50. package/.cursor/rules/desktop-controller-tests.mdc +0 -189
  51. package/.cursor/rules/desktop-feature-implementation.mdc +0 -155
  52. package/.cursor/rules/desktop-local-tools-implement.mdc +0 -81
  53. package/.cursor/rules/desktop-menu-configuration.mdc +0 -209
  54. package/.cursor/rules/desktop-window-management.mdc +0 -301
  55. package/.cursor/rules/drizzle-schema-style-guide.mdc +0 -218
  56. package/.cursor/rules/hotkey.mdc +0 -162
  57. package/.cursor/rules/linear.mdc +0 -53
  58. package/.cursor/rules/microcopy-cn.mdc +0 -158
  59. package/.cursor/rules/microcopy-en.mdc +0 -148
  60. package/.cursor/rules/modal-imperative.mdc +0 -162
  61. package/.cursor/rules/packages/react-layout-kit.mdc +0 -122
  62. package/.cursor/rules/project-introduce.mdc +0 -36
  63. package/.cursor/rules/react.mdc +0 -169
  64. package/.cursor/rules/recent-data-usage.mdc +0 -139
  65. package/.cursor/rules/rules-index.mdc +0 -44
  66. package/.cursor/rules/testing-guide/agent-runtime-e2e.mdc +0 -285
  67. package/.cursor/rules/testing-guide/db-model-test.mdc +0 -455
  68. package/.cursor/rules/testing-guide/electron-ipc-test.mdc +0 -80
  69. package/.cursor/rules/testing-guide/testing-guide.mdc +0 -534
  70. package/.cursor/rules/testing-guide/zustand-store-action-test.mdc +0 -574
  71. package/.cursor/rules/typescript.mdc +0 -55
  72. package/.cursor/rules/zustand-action-patterns.mdc +0 -328
  73. package/.cursor/rules/zustand-slice-organization.mdc +0 -308
  74. package/src/libs/pdfjs/pdf.worker.ts +0 -1
  75. package/src/libs/pdfjs/worker.ts +0 -12
  76. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/AGENTS.md +0 -0
  77. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/SKILL.md +0 -0
  78. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-event-handler-refs.md +0 -0
  79. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-use-latest.md +0 -0
  80. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-api-routes.md +0 -0
  81. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-defer-await.md +0 -0
  82. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-dependencies.md +0 -0
  83. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-parallel.md +0 -0
  84. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-suspense-boundaries.md +0 -0
  85. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-barrel-imports.md +0 -0
  86. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-conditional.md +0 -0
  87. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-defer-third-party.md +0 -0
  88. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-dynamic-imports.md +0 -0
  89. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-preload.md +0 -0
  90. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-event-listeners.md +0 -0
  91. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-localstorage-schema.md +0 -0
  92. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-passive-event-listeners.md +0 -0
  93. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-swr-dedup.md +0 -0
  94. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-batch-dom-css.md +0 -0
  95. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-function-results.md +0 -0
  96. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-property-access.md +0 -0
  97. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-storage.md +0 -0
  98. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-combine-iterations.md +0 -0
  99. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-early-exit.md +0 -0
  100. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-hoist-regexp.md +0 -0
  101. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-index-maps.md +0 -0
  102. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-length-check-first.md +0 -0
  103. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-min-max-loop.md +0 -0
  104. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-set-map-lookups.md +0 -0
  105. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-tosorted-immutable.md +0 -0
  106. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-activity.md +0 -0
  107. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-animate-svg-wrapper.md +0 -0
  108. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-conditional-render.md +0 -0
  109. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-content-visibility.md +0 -0
  110. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hoist-jsx.md +0 -0
  111. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hydration-no-flicker.md +0 -0
  112. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-svg-precision.md +0 -0
  113. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-defer-reads.md +0 -0
  114. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-dependencies.md +0 -0
  115. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-derived-state.md +0 -0
  116. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-functional-setstate.md +0 -0
  117. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-lazy-state-init.md +0 -0
  118. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-memo.md +0 -0
  119. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-transitions.md +0 -0
  120. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-after-nonblocking.md +0 -0
  121. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-lru.md +0 -0
  122. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-react.md +0 -0
  123. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-parallel-fetching.md +0 -0
  124. /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