@deepstorm/cli 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sweep-explore
|
|
3
|
+
description: 系统性为没有需求文档的现有系统生成 E2E 测试。通过前端源码分析自动发现模块、梳理业务流程、生成 .flow.md 和 Playwright 测试脚本。支持 React / Angular / Vue 3 / Vue 2。
|
|
4
|
+
allowed-tools: Read, Write, Edit, Agent, Bash
|
|
5
|
+
deepstorm:
|
|
6
|
+
tool: sweep
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Sweep Explore — 无文档系统的 E2E 测试自动生成
|
|
10
|
+
|
|
11
|
+
通过 **前端源码分析**,自动发现现有系统的功能模块和业务流程,系统性生成 E2E 测试。
|
|
12
|
+
支持 **React**、**Angular**、**Vue 3**、**Vue 2** 四种前端框架。
|
|
13
|
+
|
|
14
|
+
## 适用场景
|
|
15
|
+
|
|
16
|
+
**何时使用:**
|
|
17
|
+
- 已有线上运行的系统,但缺乏需求文档/PRD
|
|
18
|
+
- 想为遗留系统添加 E2E 测试覆盖
|
|
19
|
+
- 团队接手一个不熟悉的新项目,想快速理解其功能结构
|
|
20
|
+
- 需要为大型系统按模块逐步添加测试,而非一次性覆盖
|
|
21
|
+
|
|
22
|
+
**何时不使用:**
|
|
23
|
+
- 已有完善的需求文档 → 请使用 `/sweep-plan`(正向流程)
|
|
24
|
+
- 已有现成的 .flow.md 和测试脚本 → 无需再次生成
|
|
25
|
+
- 非主流前端框架(如 Svelte、Solid.js、Preact 等),暂不支持
|
|
26
|
+
|
|
27
|
+
## 前置条件
|
|
28
|
+
|
|
29
|
+
运行 `/sweep-explore` 前,请确保项目已通过 `/sweep-init` 初始化(`.deepstorm/settings.json` 中存在 `sweep.e2eProjectPath` 配置),且目标前端项目源码可访问。
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 五阶段工作流
|
|
34
|
+
|
|
35
|
+
```mermaid
|
|
36
|
+
flowchart LR
|
|
37
|
+
P0["阶段零:框架检测<br>检测 React/Angular/Vue"] --> P1["阶段一:模块发现<br>分析路由 + 菜单 + 目录"]
|
|
38
|
+
P1 --> P2["阶段二:业务流程分析<br>分析组件树 + CRUD + 状态流转"]
|
|
39
|
+
P2 --> P3["阶段三:测试场景生成<br>正常/边界/异常路径 → .flow.md"]
|
|
40
|
+
P3 --> P4["阶段四:Playwright 脚本生成<br>.flow.md → .spec.ts"]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
每个阶段产出物作为下一阶段的输入,用户可在每个阶段审核和选择。
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 使用方式
|
|
48
|
+
|
|
49
|
+
| 方式 | 说明 |
|
|
50
|
+
|------|------|
|
|
51
|
+
| **直接启动** | `/sweep-explore` → 自动检测框架并进入探索流程 |
|
|
52
|
+
| **指定项目路径** | `/sweep-explore path=../my-app` → 分析指定路径的前端项目 |
|
|
53
|
+
| **指定框架** | `/sweep-explore framework=react` → 跳过框架检测,按指定框架分析 |
|
|
54
|
+
| **指定模块** | `/sweep-explore module=user-management` → 跳过阶段零和一,直接分析指定模块 |
|
|
55
|
+
| **增量模式** | `/sweep-explore incremental` → 只分析新增模块 |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 阶段零:框架检测(Framework Detection)
|
|
60
|
+
|
|
61
|
+
**目标:** 自动检测目标项目使用的前端框架,为后续分析选择正确的模式。
|
|
62
|
+
|
|
63
|
+
### 检测方法
|
|
64
|
+
|
|
65
|
+
读取项目根目录或 `src/` 下的 `package.json`,检查 dependencies/devDependencies:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# 读取 package.json 中的关键依赖
|
|
69
|
+
cat package.json | grep -E '"react"|"@angular/core"|"vue"|"vue-router"|"react-router-dom"' 2>/dev/null
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
| 框架 | 关键依赖 | 特征文件 | 路由方案 |
|
|
73
|
+
|------|---------|---------|---------|
|
|
74
|
+
| **React** | `react` + `react-router-dom` / `react-router` | `.jsx` / `.tsx` | React Router v5/v6 |
|
|
75
|
+
| **Angular** | `@angular/core` + `@angular/router` | `@Component` 装饰器 | Angular Router |
|
|
76
|
+
| **Vue 3** | `vue` (3.x) + `vue-router` (4.x) | `.vue` Composition API | vue-router 4 |
|
|
77
|
+
| **Vue 2** | `vue` (2.x) + `vue-router` (3.x) | `.vue` Options API | vue-router 3 |
|
|
78
|
+
|
|
79
|
+
### 降级策略
|
|
80
|
+
|
|
81
|
+
如果 `package.json` 不可用或依赖不明确:
|
|
82
|
+
|
|
83
|
+
1. 扫描 `.jsx` / `.tsx` 文件 → 很可能为 React
|
|
84
|
+
2. 扫描 `.vue` 文件 → 很可能为 Vue
|
|
85
|
+
2. 扫描 `@Component` / `@Injectable` 装饰器 → 很可能为 Angular
|
|
86
|
+
3. 对每种可能框架都尝试分析路由,命中最多的结果作为框架推断
|
|
87
|
+
4. 输出检测结果供用户确认
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
🔍 框架检测结果:
|
|
91
|
+
|
|
92
|
+
检测到 React 特征(package.json 中发现 react + react-router-dom)
|
|
93
|
+
确认框架:React ✅
|
|
94
|
+
|
|
95
|
+
按 Enter 确认,或输入框架名称修改(react/angular/vue3/vue2)>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 阶段一:模块发现(Module Discovery)
|
|
101
|
+
|
|
102
|
+
**目标:** 从前端源码中自动发现系统包含哪些功能模块。
|
|
103
|
+
|
|
104
|
+
### 分析流程
|
|
105
|
+
|
|
106
|
+
```mermaid
|
|
107
|
+
flowchart TD
|
|
108
|
+
FRAMEWORK["框架检测已完成"] --> ROUTES["1. 按框架分析路由配置"]
|
|
109
|
+
ROUTES --> MENU["2. 分析菜单导航"]
|
|
110
|
+
MENU --> DIR["3. 分析目录结构"]
|
|
111
|
+
DIR --> MERGE["4. 合并信息源 → 模块清单"]
|
|
112
|
+
MERGE --> SELECT["5. 用户选择要测试的模块"]
|
|
113
|
+
SELECT --> DONE["✅ 阶段一完成"]
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 1.1 按框架分析路由配置
|
|
117
|
+
|
|
118
|
+
根据阶段零检测到的框架类型,使用对应的分析模式。
|
|
119
|
+
|
|
120
|
+
#### React(react-router)
|
|
121
|
+
|
|
122
|
+
**常见配置文件:**
|
|
123
|
+
|
|
124
|
+
| 文件路径 | 路由版本 | 特征 API |
|
|
125
|
+
|---------|---------|---------|
|
|
126
|
+
| `src/routes.tsx` / `src/routes.ts` | v5/v6 | `<Routes>`, `<Route>`, `createBrowserRouter` |
|
|
127
|
+
| `src/router/index.tsx` | v6 | `createBrowserRouter`, `RouterProvider` |
|
|
128
|
+
| `src/App.tsx` | v5/v6 | 内联 `<Switch>` / `<Routes>` |
|
|
129
|
+
| `src/config/routes.ts` | v5 | 对象数组 + `<Route>` 渲染 |
|
|
130
|
+
|
|
131
|
+
**分析方法:**
|
|
132
|
+
- 提取 `<Route path="..." element={<Component />}>` 的 path 和组件名
|
|
133
|
+
- 提取 `createBrowserRouter([{ path, element }])` 的路由配置
|
|
134
|
+
- 注意 `React.lazy(() => import(...))` 懒加载模式
|
|
135
|
+
- 嵌套路由表示父子模块关系
|
|
136
|
+
|
|
137
|
+
#### Angular(Angular Router)
|
|
138
|
+
|
|
139
|
+
**常见配置文件:**
|
|
140
|
+
|
|
141
|
+
| 文件路径 | 特征 API |
|
|
142
|
+
|---------|---------|
|
|
143
|
+
| `src/app/app-routing.module.ts` | `RouterModule.forRoot(routes)` |
|
|
144
|
+
| `src/app/app.module.ts` | 内联路由配置 |
|
|
145
|
+
| `src/app/*.module.ts` | 各模块的路由配置 |
|
|
146
|
+
|
|
147
|
+
**分析方法:**
|
|
148
|
+
- 读取 `Routes` 类型数组:`{ path: 'users', component: UserListComponent }`
|
|
149
|
+
- 提取 `loadChildren: () => import('./users/users.module').then(m => m.UsersModule)` 懒加载模块
|
|
150
|
+
- 关注 `canActivate`, `canLoad` 权限守卫——这些是业务规则线索
|
|
151
|
+
- Angular 的 `NgModule` 结构天然对应模块划分
|
|
152
|
+
- 提取 `children` 嵌套路由
|
|
153
|
+
|
|
154
|
+
#### Vue 3(vue-router 4)
|
|
155
|
+
|
|
156
|
+
**常见配置文件:**
|
|
157
|
+
|
|
158
|
+
| 文件路径 | 特征 API |
|
|
159
|
+
|---------|---------|
|
|
160
|
+
| `src/router/index.ts` | `createRouter({ routes })` |
|
|
161
|
+
| `src/router/routes.ts` | 单独的路由配置数组 |
|
|
162
|
+
|
|
163
|
+
**分析方法:**
|
|
164
|
+
- 读取 `createRouter` 的 `routes` 数组
|
|
165
|
+
- 提取 `{ path, name, component }` 配置
|
|
166
|
+
- 注意 `() => import('@/views/...')` 懒加载模式
|
|
167
|
+
- `children` 嵌套路由表示父子模块
|
|
168
|
+
|
|
169
|
+
#### Vue 2(vue-router 3)
|
|
170
|
+
|
|
171
|
+
**常见配置文件:**
|
|
172
|
+
|
|
173
|
+
| 文件路径 | 特征 API |
|
|
174
|
+
|---------|---------|
|
|
175
|
+
| `src/router/index.js` | `new VueRouter({ routes })` |
|
|
176
|
+
| `src/router/index.ts` | `new VueRouter({ routes })` |
|
|
177
|
+
|
|
178
|
+
**分析方法:**
|
|
179
|
+
- 读取 `new VueRouter({ routes })` 配置
|
|
180
|
+
- 与 Vue 3 类似,使用 `() => import(...)` 懒加载
|
|
181
|
+
- `children` 嵌套路由表示父子模块
|
|
182
|
+
|
|
183
|
+
#### 查找路由文件的 Shell 命令
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# React
|
|
187
|
+
find src -name "*.tsx" -o -name "*.ts" | xargs grep -l "Routes\|Route\|createBrowserRouter\|useRoutes" 2>/dev/null | head -10
|
|
188
|
+
|
|
189
|
+
# Angular
|
|
190
|
+
find src -name "*.ts" | xargs grep -l "RouterModule\|Routes\|loadChildren" 2>/dev/null | head -10
|
|
191
|
+
|
|
192
|
+
# Vue 3
|
|
193
|
+
find src -name "*.ts" -o -name "*.js" | xargs grep -l "createRouter\|createWebHistory\|createWebHashHistory" 2>/dev/null | head -10
|
|
194
|
+
|
|
195
|
+
# Vue 2
|
|
196
|
+
find src -name "*.ts" -o -name "*.js" | xargs grep -l "new VueRouter\|new Router" 2>/dev/null | head -10
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 1.2 按框架分析菜单导航
|
|
200
|
+
|
|
201
|
+
根据检测到的框架,查找对应的导航/菜单组件:
|
|
202
|
+
|
|
203
|
+
| 框架 | 常见组件/库 | 查找关键词 |
|
|
204
|
+
|------|-----------|-----------|
|
|
205
|
+
| **React** | Ant Design Menu, Material UI List/Sidebar | `Menu`, `Sidebar`, `Sider`, `Nav` |
|
|
206
|
+
| **Angular** | Angular Material Sidenav, PrimeNG Menu | `mat-sidenav`, `p-menu`, `nav`, `sidebar` |
|
|
207
|
+
| **Vue 3** | Element Plus Menu, Naive UI Menu | `el-menu`, `n-menu`, `Sidebar` |
|
|
208
|
+
| **Vue 2** | Element UI NavMenu, iView Menu | `el-menu`, `i-menu`, `NavMenu` |
|
|
209
|
+
|
|
210
|
+
**分析查找:**
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
# 根据框架类型查找菜单组件
|
|
214
|
+
# React
|
|
215
|
+
find src -name "*.tsx" -o -name "*.tsx" | xargs grep -l "Menu\|Sidebar\|Sider" 2>/dev/null | head -10
|
|
216
|
+
|
|
217
|
+
# Angular
|
|
218
|
+
find src -name "*.html" -o -name "*.ts" | xargs grep -l "mat-sidenav\|p-menu\|sidebar\|sidenav" 2>/dev/null | head -10
|
|
219
|
+
|
|
220
|
+
# Vue 3 / Vue 2
|
|
221
|
+
find src -name "*.vue" | xargs grep -l "el-menu\|n-menu\|sidebar\|Sidebar" 2>/dev/null | head -10
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**菜单组织方式:**
|
|
225
|
+
|
|
226
|
+
| 方式 | 说明 |
|
|
227
|
+
|------|------|
|
|
228
|
+
| 硬编码 UI | 组件内直接写菜单项(如 `<el-menu-item>用户管理</el-menu-item>`) |
|
|
229
|
+
| 配置驱动 | 菜单数据与渲染分离,如 `menuConfig: { label: '用户管理', path: '/users' }` |
|
|
230
|
+
| 后端返回 | 菜单由 API 动态返回(`fetchMenu()` 后渲染) |
|
|
231
|
+
| 权限过滤 | 菜单项按角色/权限条件渲染 |
|
|
232
|
+
|
|
233
|
+
**分析要点:**
|
|
234
|
+
- 菜单项的 label 是业务模块名称的第一手来源
|
|
235
|
+
- 菜单层级组织反映模块的父子关系(如 系统管理 → 用户管理)
|
|
236
|
+
- 注意 `v-if` / `*ngIf` / 条件渲染的菜单——按角色/权限控制是重要线索
|
|
237
|
+
- 配置驱动的菜单是最可靠的模块清单来源
|
|
238
|
+
|
|
239
|
+
### 1.3 分析目录结构
|
|
240
|
+
|
|
241
|
+
当路由和菜单信息不足时,通过目录结构辅助判断。不同框架的页面目录命名习惯:
|
|
242
|
+
|
|
243
|
+
| 框架 | 常见页面目录 | 页面文件扩展名 |
|
|
244
|
+
|------|------------|--------------|
|
|
245
|
+
| **React** | `src/pages/`, `src/views/`, `src/routes/` | `.tsx`, `.jsx` |
|
|
246
|
+
| **Angular** | `src/app/`(每个模块一个目录) | `.ts`(组件类)+ `.html`(模板) |
|
|
247
|
+
| **Vue** | `src/views/`, `src/pages/` | `.vue`(单文件组件) |
|
|
248
|
+
|
|
249
|
+
**示例——视图目录结构(React/Vue):**
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
src/views/ 或 src/pages/
|
|
253
|
+
├── user/ ← 用户管理模块
|
|
254
|
+
│ ├── list/ ← 用户列表页面
|
|
255
|
+
│ ├── create/ ← 创建用户页面
|
|
256
|
+
│ ├── edit/ ← 编辑用户页面
|
|
257
|
+
│ └── detail/ ← 用户详情页面
|
|
258
|
+
├── order/ ← 订单管理模块
|
|
259
|
+
│ ├── list/
|
|
260
|
+
│ ├── detail/
|
|
261
|
+
│ ├── approve/ ← 订单审核页面
|
|
262
|
+
│ └── refund/ ← 订单退款页面
|
|
263
|
+
└── dashboard/ ← 仪表盘模块
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**示例——Angular 目录结构:**
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
src/app/
|
|
270
|
+
├── users/ ← 用户管理模块
|
|
271
|
+
│ ├── users.module.ts ← 模块定义
|
|
272
|
+
│ ├── users-routing.module.ts ← 子路由
|
|
273
|
+
│ ├── user-list/ ← 用户列表
|
|
274
|
+
│ ├── user-form/ ← 用户表单(创建/编辑)
|
|
275
|
+
│ └── user-detail/ ← 用户详情
|
|
276
|
+
├── orders/ ← 订单管理模块
|
|
277
|
+
│ ├── orders.module.ts
|
|
278
|
+
│ ├── orders-routing.module.ts
|
|
279
|
+
│ ├── order-list/
|
|
280
|
+
│ └── order-approve/
|
|
281
|
+
└── dashboard/
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**组合推断规则:**
|
|
285
|
+
- 路由 path + 组件名 + 目录名 → 互相印证,取多数一致的结果
|
|
286
|
+
- 路由不足以确定模块归属时,以菜单结构为准(菜单反映业务组织)
|
|
287
|
+
- 路由和菜单都没有时,使用目录结构并标注"推测"
|
|
288
|
+
- Angular 的 `NgModule` 目录结构本身就是模块划分的直接映射,优先级最高
|
|
289
|
+
|
|
290
|
+
### 1.4 输出模块清单
|
|
291
|
+
|
|
292
|
+
向用户展示发现的模块清单:
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
📦 发现以下功能模块(共 N 个):
|
|
296
|
+
|
|
297
|
+
[ ] 1. 用户管理 /users → 用户列表、创建、编辑、详情
|
|
298
|
+
[ ] 2. 订单管理 /orders → 订单列表、详情、审核、退款
|
|
299
|
+
[ ] 3. 仪表盘 /dashboard → 数据概览、图表
|
|
300
|
+
[ ] 4. 设置 /settings → 个人设置、系统配置
|
|
301
|
+
...
|
|
302
|
+
|
|
303
|
+
? 请选择要生成测试的模块(可多选,输入编号或 all)>
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### 1.5 多选与继续
|
|
307
|
+
|
|
308
|
+
- 用户输入编号(如 `1,2`)或 `all` 选择模块
|
|
309
|
+
- 已选模块进入阶段二,未选模块跳过
|
|
310
|
+
- 支持后续单独对未选模块执行 `/sweep-explore module=<name>`
|
|
311
|
+
|
|
312
|
+
#### 降级处理:未发现任何模块
|
|
313
|
+
|
|
314
|
+
如果路由、菜单、目录结构三种信息源均无法确定功能模块:
|
|
315
|
+
|
|
316
|
+
1. 分析 `src/` 顶层目录结构,列出所有一级子目录
|
|
317
|
+
2. 检查是否有页面相关的目录命名约定(如 `pages/`、`views/`、`components/`)
|
|
318
|
+
3. **向用户报告分析结果**:
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
⚠️ 系统无法自动发现功能模块。
|
|
322
|
+
|
|
323
|
+
可能原因:
|
|
324
|
+
- 路由配置方式非标准(如动态路由、后端下发路由)
|
|
325
|
+
- 菜单由后端动态返回,前端无静态配置
|
|
326
|
+
- 项目结构不符合常见约定
|
|
327
|
+
|
|
328
|
+
建议操作:
|
|
329
|
+
1. 手动输入模块名称,系统将按目录结构分析
|
|
330
|
+
2. 检查路由配置后重新运行
|
|
331
|
+
3. 使用 `/sweep-plan`(如已有需求文档)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
4. 提供手动输入模块的支持,用户输入后直接进入阶段二分析
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## 阶段二:业务流程分析(Business Flow Analysis)
|
|
339
|
+
|
|
340
|
+
**目标:** 针对选定的功能模块,识别该模块下的业务操作流程。
|
|
341
|
+
|
|
342
|
+
### 分析流程
|
|
343
|
+
|
|
344
|
+
```mermaid
|
|
345
|
+
flowchart TD
|
|
346
|
+
INPUT["选定模块的组件入口"] --> COMP["1. 分析组件树"]
|
|
347
|
+
COMP --> CRUD["2. CRUD 识别"]
|
|
348
|
+
COMP --> STATE["3. 状态流转识别"]
|
|
349
|
+
COMP --> IO["4. 导入导出识别"]
|
|
350
|
+
CRUD --> LIST["输出业务流程清单"]
|
|
351
|
+
STATE --> LIST
|
|
352
|
+
IO --> LIST
|
|
353
|
+
LIST --> SELECT["5. 用户选择业务流"]
|
|
354
|
+
SELECT --> DONE["✅ 阶段二完成"]
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### 2.1 按框架分析组件树
|
|
358
|
+
|
|
359
|
+
针对选定模块的入口页面组件,按框架特性分析子组件关系。
|
|
360
|
+
|
|
361
|
+
**通用分析方法:**
|
|
362
|
+
1. 读取页面组件的代码
|
|
363
|
+
2. 查看 import / 引用语句,识别子组件
|
|
364
|
+
3. 跟踪子组件文件,理解其功能
|
|
365
|
+
4. 从组件命名推断业务功能
|
|
366
|
+
|
|
367
|
+
**React 组件分析:**
|
|
368
|
+
- 查看 JSX 中使用的子组件标签:`<UserTable>`, `<CreateUserModal>`
|
|
369
|
+
- 从 import 语句追踪组件路径:`import CreateUserModal from './modals/CreateUserModal'`
|
|
370
|
+
- 注意 `React.lazy` 和动态导入
|
|
371
|
+
|
|
372
|
+
**Angular 组件分析:**
|
|
373
|
+
- 查看模板(`.html`)中的选择器标签:`<app-user-table>`, `<app-create-user-modal>`
|
|
374
|
+
- 从模块装饰器 `declarations` / `imports` 中查找组件注册
|
|
375
|
+
- 注意 `@Input()` / `@Output()` 数据流接口
|
|
376
|
+
- 留意 `*ngIf`, `*ngFor` 等结构性指令中的业务逻辑
|
|
377
|
+
|
|
378
|
+
**Vue 组件分析:**
|
|
379
|
+
- 查看 `.vue` 文件中 `<template>` 段的子组件标签
|
|
380
|
+
- 从 `<script>` 段的 `import` / `components` 注册中追踪组件
|
|
381
|
+
- 注意 `v-if`, `v-for` 中的业务逻辑
|
|
382
|
+
- Composition API(Vue 3)或 Options API(Vue 2)的组件选项
|
|
383
|
+
|
|
384
|
+
**组件树示例:**
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
用户管理模块入口
|
|
388
|
+
├── UserSearchBar / search-bar ← 搜索/筛选
|
|
389
|
+
├── UserTable / user-table ← 用户列表展示
|
|
390
|
+
│ ├── Pagination / pager ← 分页
|
|
391
|
+
│ └── ActionButtons ← 操作按钮
|
|
392
|
+
├── CreateUserModal ← 创建用户弹窗
|
|
393
|
+
├── EditUserModal ← 编辑用户弹窗
|
|
394
|
+
└── DeleteConfirmDialog ← 删除确认弹窗
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**组件命名约定——常见推断模式(跨框架通用):**
|
|
398
|
+
|
|
399
|
+
| 组件名模式 | 推断的业务功能 |
|
|
400
|
+
|-----------|--------------|
|
|
401
|
+
| `*List*`, `*Table*`, `*Grid*` | 列表查看/搜索/分页 |
|
|
402
|
+
| `*Create*`, `*Add*`, `*New*` | 新增创建 |
|
|
403
|
+
| `*Edit*`, `*Update*`, `*Form*` | 编辑修改 |
|
|
404
|
+
| `*Delete*`, `*Remove*` | 删除操作 |
|
|
405
|
+
| `*Detail*`, `*View*` | 详情查看 |
|
|
406
|
+
| `*Modal*`, `*Dialog*`, `*Drawer*` | 弹窗表单 |
|
|
407
|
+
| `*Import*`, `*Upload*` | 数据导入 |
|
|
408
|
+
| `*Export*`, `*Download*` | 数据导出 |
|
|
409
|
+
| `*Approve*`, `*Reject*`, `*Audit*` | 审批/审核 |
|
|
410
|
+
| `*Setting*`, `*Config*` | 配置管理 |
|
|
411
|
+
| `*Chart*`, `*Stat*`, `*Dashboard*` | 数据统计 |
|
|
412
|
+
|
|
413
|
+
### 2.2 识别 CRUD 流程
|
|
414
|
+
|
|
415
|
+
根据组件组合推断标准 CRUD 业务流程:
|
|
416
|
+
|
|
417
|
+
**列表流程(Read):**
|
|
418
|
+
- 页面进入时调用列表 API → 展示分页表格 → 搜索/筛选 → 重新查询
|
|
419
|
+
|
|
420
|
+
**创建流程(Create):**
|
|
421
|
+
- 点击"新增"按钮 → 打开创建弹窗/页面 → 填写表单 → 提交 → 成功后关闭/刷新列表
|
|
422
|
+
|
|
423
|
+
**编辑流程(Update):**
|
|
424
|
+
- 点击列表中的"编辑"操作 → 打开编辑弹窗/页面 → 预填数据 → 修改表单 → 提交 → 刷新列表
|
|
425
|
+
|
|
426
|
+
**删除流程(Delete):**
|
|
427
|
+
- 点击删除操作 → 弹出确认对话框 → 用户确认 → 执行删除 → 刷新列表
|
|
428
|
+
|
|
429
|
+
### 2.3 识别状态流转
|
|
430
|
+
|
|
431
|
+
对于包含状态变更的业务流程(如审批、订单流转):
|
|
432
|
+
|
|
433
|
+
**查找线索:**
|
|
434
|
+
- 状态枚举/常量定义(`type OrderStatus = 'pending' | 'approved' | 'rejected'`)
|
|
435
|
+
- 状态机逻辑(`switch` / `if-else` 状态判断)
|
|
436
|
+
- 业务状态对应的操作按钮(条件渲染的 action)
|
|
437
|
+
|
|
438
|
+
**示例——订单状态流转:**
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
// 状态枚举
|
|
442
|
+
enum OrderStatus {
|
|
443
|
+
PENDING = 'PENDING', // 待审核
|
|
444
|
+
APPROVED = 'APPROVED', // 已通过
|
|
445
|
+
REJECTED = 'REJECTED', // 已驳回
|
|
446
|
+
CANCELLED = 'CANCELLED', // 已取消
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// 状态 → 可操作映射
|
|
450
|
+
const STATUS_ACTIONS = {
|
|
451
|
+
[OrderStatus.PENDING]: ['approve', 'reject'],
|
|
452
|
+
[OrderStatus.APPROVED]: ['view'],
|
|
453
|
+
[OrderStatus.REJECTED]: ['edit', 'delete'],
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
### 2.4 识别导入导出
|
|
458
|
+
|
|
459
|
+
**导入流程(Import/Upload):**
|
|
460
|
+
- 查找 `<input type="file">` 或 Upload/Dropzone 组件
|
|
461
|
+
- 关联的提交按钮和结果页面(导入进度、导入结果)
|
|
462
|
+
|
|
463
|
+
**导出流程(Export/Download):**
|
|
464
|
+
- 查找导出按钮(如 `<Button>导出</Button>`)
|
|
465
|
+
- 关联的文件格式选择、导出参数设置、下载处理
|
|
466
|
+
|
|
467
|
+
### 2.5 输出业务流程清单
|
|
468
|
+
|
|
469
|
+
```
|
|
470
|
+
📋 用户管理模块 — 业务流程清单
|
|
471
|
+
|
|
472
|
+
[ ] 1. 用户列表查看 → 列表显示、搜索、分页
|
|
473
|
+
[ ] 2. 创建用户 → 表单填写、提交、结果确认
|
|
474
|
+
[ ] 3. 编辑用户 → 数据预填、修改、保存
|
|
475
|
+
[ ] 4. 删除用户 → 确认弹窗、执行、列表刷新
|
|
476
|
+
|
|
477
|
+
? 请选择要生成测试的业务流程(可多选,输入编号或 all)>
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
#### 降级处理:无法自动识别业务流程
|
|
481
|
+
|
|
482
|
+
如果 AI 无法从组件树和代码逻辑中推断业务流程:
|
|
483
|
+
|
|
484
|
+
1. 输出该模块的完整组件目录树和文件清单
|
|
485
|
+
2. 列出可疑的业务逻辑组件(如表单、表格、状态枚举、按钮等)
|
|
486
|
+
3. **引导用户手动描述业务流程**:
|
|
487
|
+
|
|
488
|
+
```
|
|
489
|
+
⚠️ 系统无法自动推断 [模块名] 的业务流程。
|
|
490
|
+
|
|
491
|
+
可能原因:
|
|
492
|
+
- 组件命名不包含业务语义(如 GenericForm、MyTable)
|
|
493
|
+
- 业务逻辑通过配置驱动或后端下发
|
|
494
|
+
- 代码结构复杂,涉及跨模块引用
|
|
495
|
+
|
|
496
|
+
以下是该模块的组件目录结构:
|
|
497
|
+
src/modules/xxx/
|
|
498
|
+
├── components/
|
|
499
|
+
│ ├── GenericForm.vue ← 可能是表单组件
|
|
500
|
+
│ ├── DataTable.vue ← 可能是列表组件
|
|
501
|
+
│ └── ActionBar.vue ← 可能是操作按钮
|
|
502
|
+
|
|
503
|
+
请手动描述该模块包含哪些业务功能,例如:
|
|
504
|
+
- "用户列表查看 → 创建用户 → 编辑用户 → 删除用户"
|
|
505
|
+
- "订单列表 → 审核订单 → 导出订单"
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
4. 用户描述后,根据其输入直接进入阶段三(测试场景生成)
|
|
509
|
+
|
|
510
|
+
用户选择后进入阶段三。
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## 阶段三:测试场景生成(Test Scenario Generation)
|
|
515
|
+
|
|
516
|
+
**目标:** 针对选定的业务流程,生成 .flow.md 测试意图文档。
|
|
517
|
+
|
|
518
|
+
### 场景类型覆盖
|
|
519
|
+
|
|
520
|
+
每个业务流程的 .flow.md 需要覆盖以下三类场景:
|
|
521
|
+
|
|
522
|
+
| 场景类型 | 说明 | 优先级 |
|
|
523
|
+
|---------|------|-------|
|
|
524
|
+
| **正常流程** | 业务操作成功完成的路径 | P0 |
|
|
525
|
+
| **边界条件** | 输入边界值、空数据、极限情况 | P1 |
|
|
526
|
+
| **异常流程** | 必填校验、重复提交、网络超时等 | P1 |
|
|
527
|
+
|
|
528
|
+
### 3.1 生成正常路径场景
|
|
529
|
+
|
|
530
|
+
从业务流程的操作步骤推导正常流程:
|
|
531
|
+
|
|
532
|
+
对于「创建用户」业务流程:
|
|
533
|
+
|
|
534
|
+
```markdown
|
|
535
|
+
## Flow: L01 - 创建用户正常流程
|
|
536
|
+
|
|
537
|
+
### 前置条件
|
|
538
|
+
- 用户已登录系统
|
|
539
|
+
- 用户有「用户管理」权限
|
|
540
|
+
- 用户列表页面已加载
|
|
541
|
+
|
|
542
|
+
### 执行步骤
|
|
543
|
+
1. 点击"新增用户"按钮
|
|
544
|
+
✅ 验证点:弹出创建用户弹窗,表单包含用户名、邮箱、角色等字段
|
|
545
|
+
2. 填写必填字段(用户名:`testuser`、邮箱:`test@example.com`、角色:`普通用户`)
|
|
546
|
+
✅ 验证点:表单字段输入正常,无校验错误提示
|
|
547
|
+
3. 点击"保存"按钮
|
|
548
|
+
✅ 验证点:弹窗关闭,列表刷新,新用户出现在列表中
|
|
549
|
+
4. 在搜索框中输入用户名 `testuser` 搜索
|
|
550
|
+
✅ 验证点:搜索结果仅包含新创建的用户
|
|
551
|
+
|
|
552
|
+
### 环境要求
|
|
553
|
+
- 目标环境:test
|
|
554
|
+
- 所需账号:管理员权限账号
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### 3.2 生成边界条件场景
|
|
558
|
+
|
|
559
|
+
```markdown
|
|
560
|
+
## Flow: L02 - 创建用户边界条件
|
|
561
|
+
|
|
562
|
+
### 前置条件
|
|
563
|
+
- 用户已登录系统
|
|
564
|
+
|
|
565
|
+
### 执行步骤
|
|
566
|
+
1. 点击"新增用户"按钮,不填写任何字段,直接点击"保存"
|
|
567
|
+
✅ 验证点:表单校验提示必填字段错误,弹窗未关闭
|
|
568
|
+
2. 用户名输入超过最大长度的文本(如 50 个字符)
|
|
569
|
+
✅ 验证点:输入框限制字符数或显示超长提示
|
|
570
|
+
3. 输入已存在的用户名
|
|
571
|
+
✅ 验证点:提交后提示"用户名已存在"错误信息
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### 3.3 生成异常场景
|
|
575
|
+
|
|
576
|
+
```markdown
|
|
577
|
+
## Flow: L03 - 创建用户异常场景
|
|
578
|
+
|
|
579
|
+
### 前置条件
|
|
580
|
+
- 用户已登录系统
|
|
581
|
+
|
|
582
|
+
### 执行步骤
|
|
583
|
+
1. 点击"新增用户"按钮,填写必填字段,提交前断网
|
|
584
|
+
2. 点击"保存"按钮
|
|
585
|
+
✅ 验证点:显示网络错误提示,弹窗未关闭,数据未提交
|
|
586
|
+
3. 恢复网络,再次点击"保存"
|
|
587
|
+
✅ 验证点:用户创建成功
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### 3.4 输出格式规范
|
|
591
|
+
|
|
592
|
+
.flow.md 的格式**必须**与 sweep-plan 一致,确保可被 sweep-run 复用:
|
|
593
|
+
|
|
594
|
+
```markdown
|
|
595
|
+
# E2E 测试流程:{模块名} — {业务流程}
|
|
596
|
+
|
|
597
|
+
**来源:** sweep-explore 源码分析
|
|
598
|
+
**创建时间:** {YYYY-MM-DD HH:mm}
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
## 场景清单
|
|
603
|
+
|
|
604
|
+
| ID | 场景 | 类型 | 优先级 |
|
|
605
|
+
|----|------|------|--------|
|
|
606
|
+
| L01 | 创建用户正常流程 | 正常流程 | P0 |
|
|
607
|
+
| L02 | 创建用户边界条件 | 边界条件 | P1 |
|
|
608
|
+
| L03 | 创建用户异常场景 | 异常场景 | P1 |
|
|
609
|
+
|
|
610
|
+
---
|
|
611
|
+
|
|
612
|
+
## Flow: L01 - 创建用户正常流程
|
|
613
|
+
|
|
614
|
+
### 前置条件
|
|
615
|
+
...
|
|
616
|
+
|
|
617
|
+
### 执行步骤
|
|
618
|
+
1. {操作步骤}
|
|
619
|
+
✅ 验证点:{预期结果}
|
|
620
|
+
|
|
621
|
+
### 环境要求
|
|
622
|
+
...
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
### 3.5 用户审核
|
|
626
|
+
|
|
627
|
+
生成 .flow.md 后,向用户展示场景摘要并确认:
|
|
628
|
+
|
|
629
|
+
```
|
|
630
|
+
✅ 已生成 3 个测试场景:
|
|
631
|
+
|
|
632
|
+
L01 - 创建用户正常流程 [P0]
|
|
633
|
+
L02 - 创建用户边界条件 [P1]
|
|
634
|
+
L03 - 创建用户异常场景 [P1]
|
|
635
|
+
|
|
636
|
+
? 是否满意这些场景?
|
|
637
|
+
1. 满意,继续生成测试脚本(进入阶段四)
|
|
638
|
+
2. 需要修改(描述修改意见)
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
---
|
|
642
|
+
|
|
643
|
+
## 阶段四:Playwright 脚本生成(Playwright Script Generation)
|
|
644
|
+
|
|
645
|
+
**目标:** 基于 .flow.md,自动生成可执行的 Playwright E2E 测试脚本。
|
|
646
|
+
|
|
647
|
+
### 4.1 .flow.md → Playwright 语义映射
|
|
648
|
+
|
|
649
|
+
| .flow.md 元素 | Playwright 代码 | 说明 |
|
|
650
|
+
|--------------|----------------|------|
|
|
651
|
+
| 前置条件 | `test.beforeEach()` | 登录、导航到页面 |
|
|
652
|
+
| 执行步骤 中的操作 | `page.locator().click()` / `.fill()` / `.selectOption()` | 页面交互 |
|
|
653
|
+
| ✅ 验证点 | `expect().toBeVisible()` / `.toHaveText()` / `.toHaveValue()` | 断言 |
|
|
654
|
+
| 环境要求 | `baseURL` 配置 | 测试配置 |
|
|
655
|
+
|
|
656
|
+
### 4.2 生成脚本模板
|
|
657
|
+
|
|
658
|
+
```typescript
|
|
659
|
+
import { test, expect } from '@playwright/test';
|
|
660
|
+
|
|
661
|
+
test.describe('用户管理 — 创建用户', () => {
|
|
662
|
+
test.beforeEach(async ({ page }) => {
|
|
663
|
+
// 前置条件:登录
|
|
664
|
+
await page.goto('/login');
|
|
665
|
+
await page.fill('[data-testid="username"]', 'admin');
|
|
666
|
+
await page.fill('[data-testid="password"]', 'password');
|
|
667
|
+
await page.click('[data-testid="login-btn"]');
|
|
668
|
+
await expect(page).toHaveURL('/dashboard');
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
test('L01 - 创建用户正常流程', async ({ page }) => {
|
|
672
|
+
// 导航到用户管理
|
|
673
|
+
await page.click('text=用户管理');
|
|
674
|
+
await expect(page).toHaveURL('/users');
|
|
675
|
+
|
|
676
|
+
// 点击新增按钮
|
|
677
|
+
await page.click('text=新增用户');
|
|
678
|
+
await expect(page.locator('[data-testid="create-user-modal"]')).toBeVisible();
|
|
679
|
+
|
|
680
|
+
// 填写表单
|
|
681
|
+
await page.fill('[data-testid="username-input"]', 'testuser');
|
|
682
|
+
await page.fill('[data-testid="email-input"]', 'test@example.com');
|
|
683
|
+
await page.selectOption('[data-testid="role-select"]', '普通用户');
|
|
684
|
+
|
|
685
|
+
// 提交
|
|
686
|
+
await page.click('text=保存');
|
|
687
|
+
await expect(page.locator('[data-testid="create-user-modal"]')).not.toBeVisible();
|
|
688
|
+
|
|
689
|
+
// 验证列表中新增的用户
|
|
690
|
+
await page.fill('[data-testid="search-input"]', 'testuser');
|
|
691
|
+
await page.click('text=搜索');
|
|
692
|
+
await expect(page.locator('table >> text=testuser')).toBeVisible();
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
test('L02 - 创建用户边界条件', async ({ page }) => {
|
|
696
|
+
await page.click('text=用户管理');
|
|
697
|
+
await page.click('text=新增用户');
|
|
698
|
+
|
|
699
|
+
// 空表单提交
|
|
700
|
+
await page.click('text=保存');
|
|
701
|
+
await expect(page.locator('text=请填写用户名')).toBeVisible();
|
|
702
|
+
await expect(page.locator('text=请填写邮箱')).toBeVisible();
|
|
703
|
+
});
|
|
704
|
+
});
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
### 4.3 目录组织
|
|
708
|
+
|
|
709
|
+
生成的测试脚本放置在 `sweep.e2eProjectPath` 配置的 E2E 项目目录中:
|
|
710
|
+
|
|
711
|
+
```
|
|
712
|
+
{ e2eProjectPath }/
|
|
713
|
+
├── modules/
|
|
714
|
+
│ ├── user-management/
|
|
715
|
+
│ │ ├── create-user.flow.md
|
|
716
|
+
│ │ ├── create-user.spec.ts
|
|
717
|
+
│ │ ├── edit-user.flow.md
|
|
718
|
+
│ │ └── edit-user.spec.ts
|
|
719
|
+
│ ├── order-management/
|
|
720
|
+
│ │ ├── approve-order.flow.md
|
|
721
|
+
│ │ └── approve-order.spec.ts
|
|
722
|
+
│ └── ...
|
|
723
|
+
└── ...
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### 4.4 Page Object 复用检测
|
|
727
|
+
|
|
728
|
+
在生成脚本前,检查 E2E 项目目录中是否已有 Page Object 文件:
|
|
729
|
+
|
|
730
|
+
```bash
|
|
731
|
+
# 查找已有 Page Object
|
|
732
|
+
find {e2eProjectPath} -name "*.page.ts" -o -name "*.po.ts" 2>/dev/null
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
如果发现已有 Page Object,分析其暴露的定位器和方法,生成的脚本中的 locator 优先复用:
|
|
736
|
+
|
|
737
|
+
```typescript
|
|
738
|
+
// 检测到已有的 UserPage Object
|
|
739
|
+
import UserPage from '../pages/user.page';
|
|
740
|
+
|
|
741
|
+
test('L01 - 创建用户正常流程', async ({ page }) => {
|
|
742
|
+
const userPage = new UserPage(page);
|
|
743
|
+
await userPage.goto();
|
|
744
|
+
await userPage.clickCreate();
|
|
745
|
+
await userPage.fillCreateForm({ username: 'testuser', email: 'test@example.com' });
|
|
746
|
+
await userPage.submitForm();
|
|
747
|
+
await expect(userPage.userTable).toContainText('testuser');
|
|
748
|
+
});
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
---
|
|
752
|
+
|
|
753
|
+
## 产出物总览
|
|
754
|
+
|
|
755
|
+
一个完整的 sweep-explore 会话产出以下文件:
|
|
756
|
+
|
|
757
|
+
```
|
|
758
|
+
{ e2eProjectPath }/
|
|
759
|
+
├── modules/
|
|
760
|
+
│ ├── {模块名}/
|
|
761
|
+
│ │ ├── {业务流程}.flow.md ← 测试意图文档
|
|
762
|
+
│ │ └── {业务流程}.spec.ts ← Playwright E2E 脚本
|
|
763
|
+
│ └── ...
|
|
764
|
+
└── _explore-report.md ← 探索报告(可选)
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
其中 `.flow.md` 格式与 `sweep-plan` 完全一致,`sweep-run` 可无缝执行。
|
|
768
|
+
|
|
769
|
+
---
|
|
770
|
+
|
|
771
|
+
## 快速参考
|
|
772
|
+
|
|
773
|
+
### 命令速查
|
|
774
|
+
|
|
775
|
+
| 命令 | 说明 |
|
|
776
|
+
|------|------|
|
|
777
|
+
| `/sweep-explore` | 启动完整探索流程(阶段零 → 四) |
|
|
778
|
+
| `/sweep-explore module=<name>` | 跳过阶段零和一,直接分析指定模块 |
|
|
779
|
+
| `/sweep-explore framework=react\|angular\|vue3\|vue2` | 跳过框架检测,按指定框架执行 |
|
|
780
|
+
| `/sweep-explore path=<project-path>` | 分析指定路径的前端项目 |
|
|
781
|
+
| `/sweep-explore incremental` | 增量模式,只分析新增模块 |
|
|
782
|
+
|
|
783
|
+
### 源码分析优先级
|
|
784
|
+
|
|
785
|
+
```
|
|
786
|
+
路由配置(最高)> 菜单导航 > 目录结构(降级使用)
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
### 场景类型覆盖
|
|
790
|
+
|
|
791
|
+
| 类型 | 覆盖内容 | 优先级 |
|
|
792
|
+
|------|---------|-------|
|
|
793
|
+
| 正常流程 | 业务操作成功完成的路径 | P0 |
|
|
794
|
+
| 边界条件 | 空数据、极限值、特殊字符 | P1 |
|
|
795
|
+
| 异常场景 | 必填校验、重复提交、网络超时 | P1 |
|