@deepstorm/cli 0.10.2 → 0.11.1

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/README.md +8 -8
  2. package/dist/build-registry.js +11 -5
  3. package/dist/cli.js +1256 -634
  4. package/dist/mcp/code-hosting/github.json +14 -1
  5. package/dist/mcp/docs-reference/context7.json +1 -10
  6. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +8 -5
  7. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +10 -8
  8. package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +21 -20
  9. package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +17 -17
  10. package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +20 -20
  11. package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +8 -8
  12. package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +13 -12
  13. package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +10 -10
  14. package/dist/registry.json +10 -0
  15. package/dist/skills/atoll-ops/SKILL.md +4 -0
  16. package/dist/skills/reef-commit/SKILL.md +9 -5
  17. package/dist/skills/reef-commit/scripts/branch-check.mjs +5 -11
  18. package/dist/skills/reef-commit/scripts/check-openspec-status.mjs +6 -12
  19. package/dist/skills/reef-commit/scripts/collect-git-context.mjs +13 -11
  20. package/dist/skills/reef-gen-backend/variants/java/steps.md +7 -7
  21. package/dist/skills/reef-gen-backend/variants/nodejs/steps.md +7 -7
  22. package/dist/skills/reef-gen-backend/variants/python/steps.md +7 -7
  23. package/dist/skills/reef-gen-frontend/variants/angular/steps.md +5 -5
  24. package/dist/skills/reef-gen-frontend/variants/react/steps.md +6 -6
  25. package/dist/skills/reef-gen-frontend/variants/vue/steps.md +6 -6
  26. package/dist/skills/reef-harden/EXAMPLES.md +12 -10
  27. package/dist/skills/reef-harden/SKILL.md +6 -0
  28. package/dist/skills/reef-harden/scripts/find-change-dir.mjs +13 -13
  29. package/dist/skills/reef-pr/SKILL.md +7 -0
  30. package/dist/skills/reef-pr/scripts/create-pr.mjs +31 -29
  31. package/dist/skills/reef-scope/SKILL.md +6 -6
  32. package/dist/skills/reef-start/references/jira-start-subagent.md +7 -7
  33. package/dist/skills/reef-start/references/risk-routing-card.md +28 -25
  34. package/dist/skills/reef-start/references/stage-4-implementation.md +33 -21
  35. package/dist/skills/reef-start/references/superpowers-gate.md +9 -9
  36. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +2 -2
  37. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +11 -0
  38. package/dist/skills/reef-style-backend/fragments/java/api-spec/jackson-polymorphism.md +36 -40
  39. package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +11 -10
  40. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +7 -7
  41. package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +24 -21
  42. package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +7 -7
  43. package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +10 -8
  44. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +2 -2
  45. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +5 -5
  46. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +1 -0
  47. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +12 -12
  48. package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +13 -12
  49. package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +11 -9
  50. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +7 -7
  51. package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +6 -6
  52. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +8 -8
  53. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +8 -7
  54. package/dist/skills/reef-style-backend/fragments/nodejs/eslint-config.json +1 -4
  55. package/dist/skills/reef-style-backend/fragments/nodejs/nestjs-structure.md +9 -9
  56. package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +2 -1
  57. package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +10 -10
  58. package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +24 -22
  59. package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +10 -9
  60. package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +3 -0
  61. package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +10 -7
  62. package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +2 -0
  63. package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +2 -0
  64. package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +11 -9
  65. package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +3 -0
  66. package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +3 -4
  67. package/dist/skills/reef-style-backend/variants/java/quick-reference.md +31 -29
  68. package/dist/skills/reef-style-backend/variants/nodejs/examples/module-example.md +2 -8
  69. package/dist/skills/reef-style-backend/variants/nodejs/quick-reference.md +35 -36
  70. package/dist/skills/reef-style-backend/variants/python/quick-reference.md +32 -31
  71. package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +9 -7
  72. package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +9 -4
  73. package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +4 -4
  74. package/dist/skills/reef-style-frontend/fragments/test/vitest-react/examples/testing.md +135 -140
  75. package/dist/skills/reef-style-frontend/fragments/test/vitest-react/quick-reference.md +32 -32
  76. package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/examples/testing.md +161 -162
  77. package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/quick-reference.md +42 -42
  78. package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +16 -10
  79. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/examples/ui-components.md +72 -87
  80. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/quick-reference.md +14 -10
  81. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/examples/ui-components.md +93 -111
  82. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/quick-reference.md +29 -33
  83. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +14 -23
  84. package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +15 -17
  85. package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +22 -5
  86. package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +52 -22
  87. package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +17 -20
  88. package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +8 -11
  89. package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +27 -27
  90. package/dist/skills/reef-style-frontend/variants/react/examples/code-wrapping.md +14 -27
  91. package/dist/skills/reef-style-frontend/variants/react/examples/component-types-pipes.md +45 -52
  92. package/dist/skills/reef-style-frontend/variants/react/examples/entity-types.md +44 -44
  93. package/dist/skills/reef-style-frontend/variants/react/examples/forms-layer.md +56 -85
  94. package/dist/skills/reef-style-frontend/variants/react/examples/service-routing.md +114 -88
  95. package/dist/skills/reef-style-frontend/variants/react/quick-reference.md +48 -42
  96. package/dist/skills/reef-style-frontend/variants/vue/examples/code-wrapping.md +24 -43
  97. package/dist/skills/reef-style-frontend/variants/vue/examples/component-types-pipes.md +48 -49
  98. package/dist/skills/reef-style-frontend/variants/vue/examples/entity-types.md +45 -45
  99. package/dist/skills/reef-style-frontend/variants/vue/examples/forms-layer.md +64 -68
  100. package/dist/skills/reef-style-frontend/variants/vue/examples/service-routing.md +85 -84
  101. package/dist/skills/reef-style-frontend/variants/vue/quick-reference.md +46 -51
  102. package/dist/skills/reef-testcase/SKILL.md +19 -19
  103. package/dist/skills/reef-testcase/references/coverage-dimensions.md +8 -0
  104. package/dist/skills/reef-testcase/references/test-case-template.md +16 -16
  105. package/dist/skills/sweep-explore/SKILL.md +824 -0
  106. package/dist/skills/sweep-explore/references/explore-flow-template.md +61 -0
  107. package/dist/skills/sweep-init/SKILL.md +4 -0
  108. package/dist/skills/sweep-init/scripts/flow-selector.mjs +6 -11
  109. package/dist/skills/sweep-init/scripts/init-project.mjs +68 -51
  110. package/dist/skills/sweep-plan/references/test-flow-template.md +12 -6
  111. package/dist/skills/sweep-record/SKILL.md +358 -0
  112. package/dist/skills/sweep-run/SKILL.md +59 -50
  113. package/dist/skills/sweep-run/scripts/env-manager.mjs +4 -5
  114. package/dist/skills/sweep-run/scripts/flow-parser.mjs +7 -6
  115. package/dist/skills/sweep-run/scripts/flow-selector.mjs +4 -15
  116. package/dist/skills/sweep-run/scripts/generate-report.mjs +1 -2
  117. package/dist/skills/sweep-run/scripts/mcp-manager.mjs +107 -14
  118. package/dist/skills/sweep-run/scripts/spec-compiler.mjs +19 -9
  119. package/dist/skills/tide-discuss/references/checklists.md +32 -32
  120. package/dist/skills/tide-discuss/references/data-format.md +85 -77
  121. package/dist/skills/tide-discuss/references/prd-template.md +21 -19
  122. package/dist/skills/tide-discuss/references/publish-flow.md +30 -11
  123. package/dist/skills/tide-discuss/references/role-prompts.md +5 -5
  124. package/package.json +5 -3
@@ -4,191 +4,192 @@
4
4
 
5
5
  ```tsx
6
6
  // UserAvatar.test.tsx
7
- import { render, screen } from '@testing-library/react'
8
- import { describe, it, expect } from 'vitest'
9
- import { UserAvatar } from './UserAvatar'
7
+ import { render, screen } from '@testing-library/react';
8
+ import { describe, it, expect } from 'vitest';
9
+ import { UserAvatar } from './UserAvatar';
10
10
 
11
11
  describe('UserAvatar', () => {
12
- const mockUser = { username: 'alice', avatar: 'https://example.com/avatar.png' }
12
+ const mockUser = { username: 'alice', avatar: 'https://example.com/avatar.png' };
13
13
 
14
14
  it('renders with avatar image', () => {
15
- render(<UserAvatar user={mockUser} />)
16
- const img = screen.getByAltText('alice')
17
- expect(img).toBeInTheDocument()
18
- expect(img).toHaveAttribute('src', mockUser.avatar)
19
- })
15
+ render(<UserAvatar user={mockUser} />);
16
+ const img = screen.getByAltText('alice');
17
+ expect(img).toBeInTheDocument();
18
+ expect(img).toHaveAttribute('src', mockUser.avatar);
19
+ });
20
20
 
21
21
  it('renders with size classes', () => {
22
- const { container } = render(<UserAvatar user={mockUser} size="lg" />)
22
+ const { container } = render(<UserAvatar user={mockUser} size="lg" />);
23
23
  // Ant Design Avatar 通过 size 属性控制尺寸
24
- expect(container.querySelector('.ant-avatar')).toBeInTheDocument()
25
- })
24
+ expect(container.querySelector('.ant-avatar')).toBeInTheDocument();
25
+ });
26
26
 
27
27
  it('is clickable when onClick provided', async () => {
28
- const handleClick = vi.fn()
29
- render(<UserAvatar user={mockUser} onClick={handleClick} />)
28
+ const handleClick = vi.fn();
29
+ render(<UserAvatar user={mockUser} onClick={handleClick} />);
30
30
 
31
- await userEvent.click(screen.getByAltText('alice'))
32
- expect(handleClick).toHaveBeenCalledTimes(1)
33
- })
34
- })
31
+ await userEvent.click(screen.getByAltText('alice'));
32
+ expect(handleClick).toHaveBeenCalledTimes(1);
33
+ });
34
+ });
35
35
  ```
36
36
 
37
37
  ## 表单测试
38
38
 
39
39
  ```tsx
40
40
  // CreateUserForm.test.tsx
41
- import { render, screen, waitFor } from '@testing-library/react'
42
- import userEvent from '@testing-library/user-event'
43
- import { describe, it, expect, vi, beforeEach } from 'vitest'
44
- import { CreateUserForm } from './CreateUserForm'
41
+ import { render, screen, waitFor } from '@testing-library/react';
42
+ import userEvent from '@testing-library/user-event';
43
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
44
+ import { CreateUserForm } from './CreateUserForm';
45
45
 
46
46
  // Mock message
47
47
  vi.mock('antd', async () => {
48
- const actual = await vi.importActual('antd')
49
- return { ...actual, message: { success: vi.fn(), error: vi.fn() } }
50
- })
48
+ const actual = await vi.importActual('antd');
49
+ return { ...actual, message: { success: vi.fn(), error: vi.fn() } };
50
+ });
51
51
 
52
52
  describe('CreateUserForm', () => {
53
53
  beforeEach(() => {
54
- vi.clearAllMocks()
55
- })
54
+ vi.clearAllMocks();
55
+ });
56
56
 
57
57
  it('renders all form fields', () => {
58
- render(<CreateUserForm />)
58
+ render(<CreateUserForm />);
59
59
 
60
- expect(screen.getByLabelText('用户名')).toBeInTheDocument()
61
- expect(screen.getByLabelText('邮箱')).toBeInTheDocument()
62
- expect(screen.getByLabelText('角色')).toBeInTheDocument()
63
- expect(screen.getByRole('button', { name: '创建' })).toBeInTheDocument()
64
- })
60
+ expect(screen.getByLabelText('用户名')).toBeInTheDocument();
61
+ expect(screen.getByLabelText('邮箱')).toBeInTheDocument();
62
+ expect(screen.getByLabelText('角色')).toBeInTheDocument();
63
+ expect(screen.getByRole('button', { name: '创建' })).toBeInTheDocument();
64
+ });
65
65
 
66
66
  it('shows validation errors on empty submit', async () => {
67
- render(<CreateUserForm />)
67
+ render(<CreateUserForm />);
68
68
 
69
- await userEvent.click(screen.getByRole('button', { name: '创建' }))
69
+ await userEvent.click(screen.getByRole('button', { name: '创建' }));
70
70
 
71
71
  await waitFor(() => {
72
- expect(screen.getByText('请输入用户名')).toBeInTheDocument()
73
- expect(screen.getByText('请输入邮箱')).toBeInTheDocument()
74
- })
75
- })
72
+ expect(screen.getByText('请输入用户名')).toBeInTheDocument();
73
+ expect(screen.getByText('请输入邮箱')).toBeInTheDocument();
74
+ });
75
+ });
76
76
 
77
77
  it('submits form with valid data', async () => {
78
- const createUser = vi.fn().mockResolvedValue({ id: '1' })
79
- render(<CreateUserForm onSubmit={createUser} />)
78
+ const createUser = vi.fn().mockResolvedValue({ id: '1' });
79
+ render(<CreateUserForm onSubmit={createUser} />);
80
80
 
81
- await userEvent.type(screen.getByLabelText('用户名'), 'newuser')
82
- await userEvent.type(screen.getByLabelText('邮箱'), 'new@example.com')
81
+ await userEvent.type(screen.getByLabelText('用户名'), 'newuser');
82
+ await userEvent.type(screen.getByLabelText('邮箱'), 'new@example.com');
83
83
 
84
84
  // Ant Design Select 需要特殊处理
85
- const roleSelect = screen.getByLabelText('角色')
86
- await userEvent.click(roleSelect)
87
- await userEvent.click(screen.getByText('编辑'))
85
+ const roleSelect = screen.getByLabelText('角色');
86
+ await userEvent.click(roleSelect);
87
+ await userEvent.click(screen.getByText('编辑'));
88
88
 
89
- await userEvent.click(screen.getByRole('button', { name: '创建' }))
89
+ await userEvent.click(screen.getByRole('button', { name: '创建' }));
90
90
 
91
91
  await waitFor(() => {
92
92
  expect(createUser).toHaveBeenCalledWith({
93
93
  username: 'newuser',
94
94
  email: 'new@example.com',
95
95
  role: 'EDITOR',
96
- })
97
- })
98
- })
99
- })
96
+ });
97
+ });
98
+ });
99
+ });
100
100
  ```
101
101
 
102
102
  ## Hook 测试
103
103
 
104
104
  ```tsx
105
105
  // useUsers.test.ts
106
- import { renderHook, act, waitFor } from '@testing-library/react'
107
- import { describe, it, expect, vi, beforeEach } from 'vitest'
108
- import { useUsers } from './useUsers'
106
+ import { renderHook, act, waitFor } from '@testing-library/react';
107
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
108
+ import { useUsers } from './useUsers';
109
109
 
110
110
  describe('useUsers', () => {
111
- const mockUsers = [
112
- { id: '1', username: 'alice', email: 'alice@example.com', role: 'ADMIN' },
113
- ]
111
+ const mockUsers = [{ id: '1', username: 'alice', email: 'alice@example.com', role: 'ADMIN' }];
114
112
 
115
113
  beforeEach(() => {
116
- vi.clearAllMocks()
117
- })
114
+ vi.clearAllMocks();
115
+ });
118
116
 
119
117
  it('returns loading state initially', () => {
120
- global.fetch = vi.fn().mockImplementation(() => new Promise(() => {})) // 永不 resolve
118
+ global.fetch = vi.fn().mockImplementation(() => new Promise(() => {})); // 永不 resolve
121
119
 
122
- const { result } = renderHook(() => useUsers())
120
+ const { result } = renderHook(() => useUsers());
123
121
 
124
- expect(result.current.loading).toBe(true)
125
- expect(result.current.users).toEqual([])
126
- expect(result.current.error).toBeNull()
127
- })
122
+ expect(result.current.loading).toBe(true);
123
+ expect(result.current.users).toEqual([]);
124
+ expect(result.current.error).toBeNull();
125
+ });
128
126
 
129
127
  it('returns users on successful fetch', async () => {
130
128
  global.fetch = vi.fn().mockResolvedValue({
131
129
  ok: true,
132
130
  json: () => Promise.resolve({ content: mockUsers, totalElements: 1 }),
133
- })
131
+ });
134
132
 
135
- const { result } = renderHook(() => useUsers())
133
+ const { result } = renderHook(() => useUsers());
136
134
 
137
135
  await waitFor(() => {
138
- expect(result.current.loading).toBe(false)
139
- })
136
+ expect(result.current.loading).toBe(false);
137
+ });
140
138
 
141
- expect(result.current.users).toEqual(mockUsers)
142
- expect(result.current.error).toBeNull()
143
- })
139
+ expect(result.current.users).toEqual(mockUsers);
140
+ expect(result.current.error).toBeNull();
141
+ });
144
142
 
145
143
  it('returns error on fetch failure', async () => {
146
- global.fetch = vi.fn().mockRejectedValue(new Error('Network error'))
144
+ global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
147
145
 
148
- const { result } = renderHook(() => useUsers())
146
+ const { result } = renderHook(() => useUsers());
149
147
 
150
148
  await waitFor(() => {
151
- expect(result.current.loading).toBe(false)
152
- })
149
+ expect(result.current.loading).toBe(false);
150
+ });
153
151
 
154
- expect(result.current.error).toBeInstanceOf(Error)
155
- expect(result.current.error?.message).toBe('Network error')
156
- expect(result.current.users).toEqual([])
157
- })
152
+ expect(result.current.error).toBeInstanceOf(Error);
153
+ expect(result.current.error?.message).toBe('Network error');
154
+ expect(result.current.users).toEqual([]);
155
+ });
158
156
 
159
157
  it('refresh resets loading state', async () => {
160
- let resolvePromise!: (value: unknown) => void
158
+ let resolvePromise!: (value: unknown) => void;
161
159
  global.fetch = vi.fn().mockImplementation(
162
- () => new Promise((resolve) => { resolvePromise = resolve })
163
- )
160
+ () =>
161
+ new Promise((resolve) => {
162
+ resolvePromise = resolve;
163
+ }),
164
+ );
164
165
 
165
- const { result } = renderHook(() => useUsers())
166
+ const { result } = renderHook(() => useUsers());
166
167
 
167
168
  act(() => {
168
- result.current.refresh()
169
- })
169
+ result.current.refresh();
170
+ });
170
171
 
171
172
  // refresh should trigger a new fetch, loading goes back to true
172
- expect(result.current.loading).toBe(true)
173
- })
174
- })
173
+ expect(result.current.loading).toBe(true);
174
+ });
175
+ });
175
176
  ```
176
177
 
177
178
  ## Ant Design 组件集成测试
178
179
 
179
180
  ```tsx
180
181
  // UserTable.test.tsx
181
- import { render, screen, within } from '@testing-library/react'
182
- import userEvent from '@testing-library/user-event'
183
- import { describe, it, expect, vi } from 'vitest'
184
- import { Table } from 'antd'
185
- import type { ColumnsType } from 'antd/es/table'
182
+ import { render, screen, within } from '@testing-library/react';
183
+ import userEvent from '@testing-library/user-event';
184
+ import { describe, it, expect, vi } from 'vitest';
185
+ import { Table } from 'antd';
186
+ import type { ColumnsType } from 'antd/es/table';
186
187
 
187
188
  interface User {
188
- id: string
189
- username: string
190
- email: string
191
- role: string
189
+ id: string;
190
+ username: string;
191
+ email: string;
192
+ role: string;
192
193
  }
193
194
 
194
195
  describe('UserTable with Ant Design', () => {
@@ -196,41 +197,28 @@ describe('UserTable with Ant Design', () => {
196
197
  { title: '用户名', dataIndex: 'username', key: 'username' },
197
198
  { title: '邮箱', dataIndex: 'email', key: 'email' },
198
199
  { title: '角色', dataIndex: 'role', key: 'role' },
199
- ]
200
+ ];
200
201
 
201
202
  const mockData: User[] = [
202
203
  { id: '1', username: 'alice', email: 'alice@example.com', role: 'ADMIN' },
203
204
  { id: '2', username: 'bob', email: 'bob@example.com', role: 'EDITOR' },
204
- ]
205
+ ];
205
206
 
206
207
  it('renders table with data', () => {
207
- render(
208
- <Table
209
- columns={columns}
210
- dataSource={mockData}
211
- rowKey="id"
212
- />
213
- )
208
+ render(<Table columns={columns} dataSource={mockData} rowKey="id" />);
214
209
 
215
- expect(screen.getByText('alice')).toBeInTheDocument()
216
- expect(screen.getByText('bob')).toBeInTheDocument()
217
- })
210
+ expect(screen.getByText('alice')).toBeInTheDocument();
211
+ expect(screen.getByText('bob')).toBeInTheDocument();
212
+ });
218
213
 
219
214
  it('shows empty state when no data', () => {
220
- render(
221
- <Table
222
- columns={columns}
223
- dataSource={[]}
224
- rowKey="id"
225
- locale={{ emptyText: '暂无数据' }}
226
- />
227
- )
215
+ render(<Table columns={columns} dataSource={[]} rowKey="id" locale={{ emptyText: '暂无数据' }} />);
228
216
 
229
- expect(screen.getByText('暂无数据')).toBeInTheDocument()
230
- })
217
+ expect(screen.getByText('暂无数据')).toBeInTheDocument();
218
+ });
231
219
 
232
220
  it('handles row click', async () => {
233
- const handleRow = vi.fn()
221
+ const handleRow = vi.fn();
234
222
  render(
235
223
  <Table
236
224
  columns={columns}
@@ -239,31 +227,31 @@ describe('UserTable with Ant Design', () => {
239
227
  onRow={() => ({
240
228
  onClick: () => handleRow(),
241
229
  })}
242
- />
243
- )
230
+ />,
231
+ );
244
232
 
245
- const rows = screen.getAllByRole('row')
233
+ const rows = screen.getAllByRole('row');
246
234
  // 第一行是 header, 第二行是第一个数据行
247
- await userEvent.click(rows[1])
248
- expect(handleRow).toHaveBeenCalled()
249
- })
250
- })
235
+ await userEvent.click(rows[1]);
236
+ expect(handleRow).toHaveBeenCalled();
237
+ });
238
+ });
251
239
  ```
252
240
 
253
241
  ## 路由组件测试
254
242
 
255
243
  ```tsx
256
244
  // ProtectedRoute.test.tsx
257
- import { render, screen } from '@testing-library/react'
258
- import { MemoryRouter, Routes, Route } from 'react-router-dom'
259
- import { describe, it, expect, vi } from 'vitest'
245
+ import { render, screen } from '@testing-library/react';
246
+ import { MemoryRouter, Routes, Route } from 'react-router-dom';
247
+ import { describe, it, expect, vi } from 'vitest';
260
248
 
261
249
  // Mock auth hook
262
250
  vi.mock('../hooks/useAuth', () => ({
263
251
  useAuth: () => ({ user: null, loading: false }),
264
- }))
252
+ }));
265
253
 
266
- import { ProtectedRoute } from './ProtectedRoute'
254
+ import { ProtectedRoute } from './ProtectedRoute';
267
255
 
268
256
  describe('ProtectedRoute', () => {
269
257
  it('redirects to login when not authenticated', () => {
@@ -271,13 +259,20 @@ describe('ProtectedRoute', () => {
271
259
  <MemoryRouter initialEntries={['/dashboard']}>
272
260
  <Routes>
273
261
  <Route path="/login" element={<div>登录页</div>} />
274
- <Route path="/dashboard" element={<ProtectedRoute><div>仪表盘</div></ProtectedRoute>} />
262
+ <Route
263
+ path="/dashboard"
264
+ element={
265
+ <ProtectedRoute>
266
+ <div>仪表盘</div>
267
+ </ProtectedRoute>
268
+ }
269
+ />
275
270
  </Routes>
276
- </MemoryRouter>
277
- )
271
+ </MemoryRouter>,
272
+ );
278
273
 
279
- expect(screen.getByText('登录页')).toBeInTheDocument()
280
- expect(screen.queryByText('仪表盘')).not.toBeInTheDocument()
281
- })
282
- })
274
+ expect(screen.getByText('登录页')).toBeInTheDocument();
275
+ expect(screen.queryByText('仪表盘')).not.toBeInTheDocument();
276
+ });
277
+ });
283
278
  ```
@@ -4,8 +4,8 @@
4
4
 
5
5
  ```ts
6
6
  // vitest.config.ts
7
- import { defineConfig } from 'vitest/config'
8
- import react from '@vitejs/plugin-react'
7
+ import { defineConfig } from 'vitest/config';
8
+ import react from '@vitejs/plugin-react';
9
9
 
10
10
  export default defineConfig({
11
11
  plugins: [react()],
@@ -26,12 +26,12 @@ export default defineConfig({
26
26
  },
27
27
  },
28
28
  },
29
- })
29
+ });
30
30
  ```
31
31
 
32
32
  ```ts
33
33
  // src/test/setup.ts
34
- import '@testing-library/jest-dom'
34
+ import '@testing-library/jest-dom';
35
35
  ```
36
36
 
37
37
  ## 通用原则
@@ -44,16 +44,16 @@ import '@testing-library/jest-dom'
44
44
  ## 组件测试
45
45
 
46
46
  ```tsx
47
- import { render, screen } from '@testing-library/react'
48
- import userEvent from '@testing-library/user-event'
49
- import { describe, it, expect, vi } from 'vitest'
47
+ import { render, screen } from '@testing-library/react';
48
+ import userEvent from '@testing-library/user-event';
49
+ import { describe, it, expect, vi } from 'vitest';
50
50
 
51
51
  // ✅ 正确:通过文本寻找元素
52
- render(<Button>提交</Button>)
53
- expect(screen.getByRole('button', { name: '提交' })).toBeInTheDocument()
52
+ render(<Button>提交</Button>);
53
+ expect(screen.getByRole('button', { name: '提交' })).toBeInTheDocument();
54
54
 
55
55
  // ✅ 正确:模拟用户交互
56
- await userEvent.click(screen.getByRole('button'))
56
+ await userEvent.click(screen.getByRole('button'));
57
57
 
58
58
  // ❌ 避免:通过测试 ID 或实现细节选择
59
59
  // screen.getByTestId('submit-btn') // 仅在无其他方式时使用
@@ -63,20 +63,20 @@ await userEvent.click(screen.getByRole('button'))
63
63
  ## Hook 测试
64
64
 
65
65
  ```tsx
66
- import { renderHook, act } from '@testing-library/react'
67
- import { describe, it, expect } from 'vitest'
66
+ import { renderHook, act } from '@testing-library/react';
67
+ import { describe, it, expect } from 'vitest';
68
68
 
69
69
  describe('useCounter', () => {
70
70
  it('should increment count', () => {
71
- const { result } = renderHook(() => useCounter())
71
+ const { result } = renderHook(() => useCounter());
72
72
 
73
73
  act(() => {
74
- result.current.increment()
75
- })
74
+ result.current.increment();
75
+ });
76
76
 
77
- expect(result.current.count).toBe(1)
78
- })
79
- })
77
+ expect(result.current.count).toBe(1);
78
+ });
79
+ });
80
80
  ```
81
81
 
82
82
  ## 测试文件命名与位置
@@ -89,22 +89,22 @@ describe('useCounter', () => {
89
89
 
90
90
  ```tsx
91
91
  // 全局 mock API 调用
92
- import { vi } from 'vitest'
92
+ import { vi } from 'vitest';
93
93
 
94
94
  // Mock fetch
95
- global.fetch = vi.fn()
95
+ global.fetch = vi.fn();
96
96
 
97
97
  // Mock 模块
98
98
  vi.mock('antd', async () => {
99
- const actual = await vi.importActual('antd')
99
+ const actual = await vi.importActual('antd');
100
100
  return {
101
101
  ...actual,
102
102
  message: {
103
103
  success: vi.fn(),
104
104
  error: vi.fn(),
105
105
  },
106
- }
107
- })
106
+ };
107
+ });
108
108
 
109
109
  // Mock 自定义 Hook
110
110
  vi.mock('../hooks/useUsers', () => ({
@@ -114,7 +114,7 @@ vi.mock('../hooks/useUsers', () => ({
114
114
  error: null,
115
115
  refresh: vi.fn(),
116
116
  }),
117
- }))
117
+ }));
118
118
  ```
119
119
 
120
120
  ## 异步测试
@@ -125,23 +125,23 @@ it('should load and display users', async () => {
125
125
  const mockUsers = [
126
126
  { id: '1', username: 'alice', email: 'alice@example.com', role: 'ADMIN' },
127
127
  { id: '2', username: 'bob', email: 'bob@example.com', role: 'EDITOR' },
128
- ]
128
+ ];
129
129
 
130
130
  // 模拟 API 响应
131
131
  const mockFetch = vi.fn().mockResolvedValue({
132
132
  ok: true,
133
133
  json: () => Promise.resolve({ content: mockUsers, totalElements: 2 }),
134
- })
135
- global.fetch = mockFetch
134
+ });
135
+ global.fetch = mockFetch;
136
136
 
137
137
  // Act
138
- render(<UserList />)
138
+ render(<UserList />);
139
139
 
140
140
  // Assert — 等待异步渲染完成
141
- expect(await screen.findByText('alice')).toBeInTheDocument()
142
- expect(screen.getByText('bob')).toBeInTheDocument()
143
- expect(screen.getByText('ADMIN')).toBeInTheDocument()
144
- })
141
+ expect(await screen.findByText('alice')).toBeInTheDocument();
142
+ expect(screen.getByText('bob')).toBeInTheDocument();
143
+ expect(screen.getByText('ADMIN')).toBeInTheDocument();
144
+ });
145
145
  ```
146
146
 
147
147
  ## 覆盖率要求