speccore 8.3.158 → 8.3.160

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 (85) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/analyze.d.ts.map +1 -1
  3. package/dist/commands/analyze.js +88 -96
  4. package/dist/commands/analyze.js.map +1 -1
  5. package/dist/commands/ask.d.ts.map +1 -1
  6. package/dist/commands/ask.js +0 -13
  7. package/dist/commands/ask.js.map +1 -1
  8. package/dist/commands/execute.d.ts.map +1 -1
  9. package/dist/commands/execute.js +67 -6
  10. package/dist/commands/execute.js.map +1 -1
  11. package/dist/commands/init.d.ts.map +1 -1
  12. package/dist/commands/init.js +71 -216
  13. package/dist/commands/init.js.map +1 -1
  14. package/dist/commands/iteration/split.d.ts.map +1 -1
  15. package/dist/commands/iteration/split.js +73 -9
  16. package/dist/commands/iteration/split.js.map +1 -1
  17. package/dist/commands/migrate.d.ts.map +1 -1
  18. package/dist/commands/migrate.js +1 -2
  19. package/dist/commands/migrate.js.map +1 -1
  20. package/dist/commands/update.d.ts.map +1 -1
  21. package/dist/commands/update.js +1 -4
  22. package/dist/commands/update.js.map +1 -1
  23. package/dist/core/agent-adapter.d.ts +81 -0
  24. package/dist/core/agent-adapter.d.ts.map +1 -0
  25. package/dist/core/agent-adapter.js +425 -0
  26. package/dist/core/agent-adapter.js.map +1 -0
  27. package/dist/core/agents/defaults/_INDEX.md +15 -15
  28. package/dist/core/agents/defaults/code-reviewer.md +9 -0
  29. package/dist/core/agents/defaults/compiler.md +12 -3
  30. package/dist/core/agents/defaults/dependency-analyst.md +7 -0
  31. package/dist/core/agents/defaults/doc-sync-agent.md +7 -0
  32. package/dist/core/agents/defaults/impact-analyst.md +7 -0
  33. package/dist/core/agents/defaults/interaction-designer.md +17 -0
  34. package/dist/core/agents/defaults/performance-expert.md +9 -0
  35. package/dist/core/agents/defaults/regression-tester.md +7 -0
  36. package/dist/core/agents/defaults/security-reviewer.md +9 -0
  37. package/dist/core/agents/defaults/test-engineer.md +10 -0
  38. package/dist/core/agents/defaults/test-reviewer.md +10 -0
  39. package/dist/core/lock-manager.d.ts.map +1 -1
  40. package/dist/core/lock-manager.js +2 -0
  41. package/dist/core/lock-manager.js.map +1 -1
  42. package/dist/core/pipeline-engine.d.ts +49 -8
  43. package/dist/core/pipeline-engine.d.ts.map +1 -1
  44. package/dist/core/pipeline-engine.js +221 -34
  45. package/dist/core/pipeline-engine.js.map +1 -1
  46. package/dist/core/prompt-builder.d.ts +4 -0
  47. package/dist/core/prompt-builder.d.ts.map +1 -1
  48. package/dist/core/prompt-builder.js +83 -10
  49. package/dist/core/prompt-builder.js.map +1 -1
  50. package/dist/core/rules/defaults/ANALYSIS_QUALITY.md +73 -0
  51. package/dist/core/rules/defaults/EXECUTION.md +20 -0
  52. package/dist/core/rules/defaults/ITERATION_QUALITY.md +49 -0
  53. package/dist/core/rules/defaults/TASK_SPLIT.md +16 -0
  54. package/dist/core/spec-paths.d.ts.map +1 -1
  55. package/dist/core/spec-paths.js +2 -1
  56. package/dist/core/spec-paths.js.map +1 -1
  57. package/package.json +4 -6
  58. package/templates/api-design-example.md +0 -338
  59. package/templates/ci/github-actions.yml +0 -44
  60. package/templates/dashboard.html +0 -425
  61. package/templates/database-example.md +0 -223
  62. package/templates/deploy-docker/README.md +0 -284
  63. package/templates/deploy-docker/deploy-remote.sh +0 -79
  64. package/templates/deploy-docker/docker-compose.yml +0 -113
  65. package/templates/deploy-docker/frontend.Dockerfile +0 -43
  66. package/templates/deploy-docker/nginx.conf +0 -30
  67. package/templates/deploy-examples.yaml +0 -436
  68. package/templates/deploy-java/Dockerfile +0 -50
  69. package/templates/deploy-java/README.md +0 -212
  70. package/templates/deploy-java/deploy.sh +0 -120
  71. package/templates/deploy-java/spring-boot.service +0 -35
  72. package/templates/html/speccore-ask-explain.html +0 -10
  73. package/templates/html/speccore-ask-guide.html +0 -10
  74. package/templates/html/speccore-ask-match.html +0 -10
  75. package/templates/html/speccore-ask-pipeline.html +0 -1
  76. package/templates/html/speccore-ask-result.html +0 -10
  77. package/templates/html/speccore-demo.html +0 -449
  78. package/templates/html/speccore-dev.html +0 -202
  79. package/templates/html/speccore-help.html +0 -1
  80. package/templates/html/speccore-knowledge-graph.html +0 -656
  81. package/templates/html/speccore-retro-T-001.html +0 -1
  82. package/templates/html/speccore-setup-guide.html +0 -289
  83. package/templates/html/speccore-welcome.html +0 -1
  84. package/templates/security-example.md +0 -226
  85. package/templates/verify-spec-example.yaml +0 -982
@@ -1,982 +0,0 @@
1
- # VERIFY_SPEC.yaml — UI 冒烟测试规格(真实值示例版)
2
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
3
- # 格式类型: VERIFY_SPEC 格式(scenarios + actions + assertions 结构)
4
- # 对应命令:
5
- # • speccore verify --ui --spec ./tests/smoke.yaml ← 推荐
6
- # • speccore verify --ui --config ./tests/smoke.yaml ← 兼容
7
- # 按模块过滤: speccore verify --ui --spec ./tests/smoke.yaml --module=login
8
- #
9
- # 本文件所有 value / selector 均为真实示例值,使用时替换为你的实际值即可。
10
- # 选择器建议优先使用 data-testid(最稳定),其次 name / id,避免随机 class。
11
- #
12
- #
13
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
14
- # 【重要】前端需要配合加 data-testid 吗?选择器怎么写最稳定?
15
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
16
- #
17
- # 问题: 像「点击保存按钮」这种操作,selector 写什么才不会因为前端改一下就失效?
18
- #
19
- # 稳定性排序(从高到低):
20
- #
21
- # 1. data-testid="xxx" ← 最稳定,专门给测试用的属性,前端不会随意改
22
- # 前端写法: <button data-testid="save-profile-btn">保存</button>
23
- # 测试写法: selector: '[data-testid="save-profile-btn"]'
24
- #
25
- # 2. id="xxx" / name="xxx" ← HTML 原生属性,较稳定,但可能有重复(name)
26
- # 前端写法: <input id="username" name="username" />
27
- # 测试写法: selector: '#username' 或 'input[name="username"]'
28
- #
29
- # 3. 稳定的 class ← 确保 class 名不会随构建变化
30
- # 前端写法: <button class="save-btn primary">保存</button>
31
- # 测试写法: selector: '.save-btn'
32
- # ⚠️ 注意: 避免使用带随机后缀的 class(如 Vue scoped: .button__xx3a / React CSS Modules: .Button_module_abc123)
33
- #
34
- # 4. 文本内容定位 ← 当前端没给任何标识时的兜底方案
35
- # 测试写法: selector: 'button:has-text("保存")' 或 'button >> text=保存'
36
- # ⚠️ 注意: 多语言项目会失效,仅作兜底
37
- #
38
- # 5. 绝对不要用的 ← 这些选择器页面一改就失效
39
- # ❌ div:nth-child(3) ← 布局变化就失效
40
- # ❌ /html/body/div[1]/div[2] ← XPath 绝对路径
41
- # ❌ .sc-xxx-123 ← styled-components 随机 class
42
- # ❌ .v-xxx-456 ← Vue scoped CSS hash
43
- # ❌ .Button_module_abc123 ← CSS Modules 随机 class
44
- #
45
- # 前端配合建议(最佳实践):
46
- # 在关键交互元素上添加 data-testid,不影响业务逻辑,只用于测试:
47
- # <input data-testid="login-username" name="username" />
48
- # <input data-testid="login-password" type="password" />
49
- # <button data-testid="login-submit">登录</button>
50
- # <button data-testid="save-profile-btn">保存</button>
51
- # <div data-testid="dashboard-container">...</div>
52
- #
53
- # 如果前端暂时不配合,测试脚本怎么写?
54
- # 先用浏览器开发者工具(F12)→ Elements 面板 → 右键元素 → Copy → Copy selector
55
- # 但复制出来的通常是绝对路径,不稳定。建议:
56
- # 1. 优先找 name / id / 稳定的 class
57
- # 2. 其次用属性选择器: [type="submit"] / [placeholder="请输入用户名"]
58
- # 3. 最后才用层级组合: .form-group > button.primary
59
- #
60
- #
61
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
62
- # 【重要】关于"需要登录的场景"怎么处理?
63
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
64
- #
65
- # 本文件场景1-9 是测试"登录功能本身"的,必须走完整的登录流程。
66
- # 场景10-42 是测试"登录后的业务功能"(如个人资料、订单、购物车),这些场景默认已登录。
67
- #
68
- # 你有两种方式让"业务场景"拿到登录态:
69
- #
70
- # 方式A: Cookie/Token 注入(推荐,最简洁)
71
- # 把「场景13: Token注入免登录」或「场景33: Cookie注入免登录」复制到业务场景前面,
72
- # 作为第一个场景执行。注入后同一浏览器会话内后续场景共享登录态。
73
- # 示例顺序:
74
- # 1. Token注入免登录(注入 accessToken + refreshToken)
75
- # 2. 个人资料修改
76
- # 3. 商品列表浏览
77
- # 4. 创建订单...
78
- #
79
- # 方式B: 每个业务场景自己注入(最独立,适合并行执行)
80
- # 在每个需要登录的场景的 actions 开头加 cookie 或 localStorage 注入:
81
- # actions:
82
- # - type: localStorage
83
- # selector: accessToken
84
- # value: 'Bearer eyJhbGci...'
85
- # - type: navigate
86
- # value: /profile
87
- #
88
- # 方式C: 先执行登录场景,再执行业务场景(顺序依赖)
89
- # 把「场景1: 账号密码登录」放第一个,登录成功后同一浏览器会话内后续场景共享登录态。
90
- # 注意: 如果登录有验证码/短信,这种方式不稳定,建议用方式A。
91
- #
92
- # ⚠️ 每个 scenario 独立执行,但 cookie/localStorage 在同一浏览器会话内共享。
93
-
94
- name: 示例项目冒烟测试
95
- platform: h5-mobile
96
- url: /
97
-
98
- scenarios:
99
-
100
- # ═══════════════════════════════════════════════════════════════════════════
101
- # 一、登录认证 (auth)
102
- # ═══════════════════════════════════════════════════════════════════════════
103
-
104
- # ── 场景1: 账号密码登录 ──
105
- # 说明: 最基础的登录方式,填写用户名密码后点击登录
106
- # 如何替换: 把 selector 换成你页面上的真实选择器,value 换成你的测试账号
107
- - name: 登录-账号密码登录
108
- description: 模块:auth 页面:/login
109
- actions:
110
- - type: navigate
111
- value: /login # 登录页路由
112
- - type: fill
113
- selector: 'input[name="username"]' # 用户名输入框(换成你实际的,如 '#username' / '[data-testid="user-input"]')
114
- value: test_user # 测试账号用户名
115
- - type: fill
116
- selector: 'input[name="password"]' # 密码输入框
117
- value: Test@123456 # 测试账号密码
118
- - type: click
119
- selector: 'button[type="submit"]' # 登录按钮
120
- waitFor: '.dashboard-container' # 登录成功后出现的元素(用于等待跳转完成)
121
- assertions:
122
- - type: url
123
- contains: /dashboard # 断言 URL 跳转到控制台
124
- - type: visible
125
- selector: '.dashboard-container' # 断言控制台容器可见
126
- - type: text
127
- selector: '.user-name' # 断言页面显示用户名
128
- contains: test_user # 预期显示的用户名
129
-
130
- # ── 场景2: 手机号+验证码登录 ──
131
- # 说明: 输入手机号 → 点击获取验证码 → 输入验证码 → 登录
132
- # 如何替换: 测试环境通常有固定验证码(如123456),或需要 mock 验证码接口
133
- - name: 登录-手机号验证码
134
- description: 模块:auth 页面:/login/phone
135
- actions:
136
- - type: navigate
137
- value: /login/phone # 手机号登录页路由
138
- - type: fill
139
- selector: 'input[name="phone"]' # 手机号输入框
140
- value: '13800138000' # 测试手机号
141
- - type: click
142
- selector: '.send-code-btn' # 获取验证码按钮
143
- - type: wait
144
- delay: 2000 # 等待短信发送(测试环境可缩短)
145
- - type: fill
146
- selector: 'input[name="verifyCode"]' # 验证码输入框
147
- value: '123456' # 测试环境固定验证码
148
- - type: click
149
- selector: 'button[type="submit"]' # 登录按钮
150
- waitFor: '.dashboard-container'
151
- assertions:
152
- - type: url
153
- contains: /dashboard
154
- - type: visible
155
- selector: '.dashboard-container'
156
-
157
- # ── 场景3: 微信 OAuth 登录 ──
158
- # 说明: 点击微信登录按钮 → 跳转微信授权页 → 授权后回调
159
- # 如何替换: 需配置微信开放平台测试账号,回调域名需与测试环境一致
160
- - name: 登录-微信OAuth
161
- description: 模块:auth 页面:/login 第三方登录
162
- actions:
163
- - type: navigate
164
- value: /login # 登录页
165
- - type: click
166
- selector: '.wechat-login-btn' # 微信登录按钮(如 [data-testid="wechat-login"])
167
- - type: waitForRequest
168
- selector: 'https://open.weixin.qq.com/connect/oauth2/authorize' # 等待跳转微信授权页
169
- timeout: 5000
170
- - type: wait
171
- delay: 3000 # 等待授权回调
172
- assertions:
173
- - type: url
174
- contains: wechat # 断言 URL 包含微信域名
175
-
176
- # ── 场景4: 企业微信 SSO 登录 ──
177
- # 说明: 页面内嵌 iframe 展示企业微信登录 → 填写企业 ID → 授权
178
- # 如何替换: 企业 ID 从企业微信管理后台获取(格式: wwxxxxxxxxxxxxxxxx)
179
- - name: 登录-企业微信SSO
180
- description: 模块:auth 页面:/login/sso
181
- actions:
182
- - type: navigate
183
- value: /login/sso # SSO 登录页
184
- - type: iframe
185
- selector: '#sso-frame' # iframe 选择器(切换上下文到 iframe 内部)
186
- - type: fill
187
- selector: 'input[name="corpId"]' # 企业 ID 输入框(在 iframe 内)
188
- value: 'ww1234567890abcdef' # 企业微信企业 ID
189
- - type: click
190
- selector: '.sso-submit-btn' # 确认按钮(在 iframe 内)
191
- waitFor: '.dashboard-container'
192
- assertions:
193
- - type: url
194
- contains: /dashboard
195
- - type: visible
196
- selector: '.dashboard-container'
197
-
198
- # ── 场景5: 扫码登录 ──
199
- # 说明: 进入扫码页 → 二维码渲染 → 等待扫码 → 登录成功
200
- # 如何替换: 测试环境可用 mock 接口模拟扫码成功,避免人工操作
201
- - name: 登录-扫码登录
202
- description: 模块:auth 页面:/login/qrcode
203
- actions:
204
- - type: navigate
205
- value: /login/qrcode # 扫码登录页
206
- - type: visible
207
- selector: '.qrcode-img' # 断言二维码图片已渲染
208
- - type: wait
209
- delay: 5000 # 等待扫码(测试环境可 mock 缩短)
210
- assertions:
211
- - type: visible
212
- selector: '.qrcode-img'
213
- - type: attribute
214
- selector: '.qrcode-img' # 断言二维码图片
215
- attribute: src # 检查 src 属性
216
- contains: data:image # 包含 base64 图片数据
217
-
218
- # ── 场景6: 登录失败-密码错误 ──
219
- # 说明: 故意输错密码,验证错误提示是否正确
220
- # 如何替换: value 填存在的账号 + 错误的密码
221
- - name: 登录-密码错误提示
222
- description: 模块:auth 页面:/login 异常场景
223
- actions:
224
- - type: navigate
225
- value: /login
226
- - type: fill
227
- selector: 'input[name="username"]'
228
- value: test_user # 存在的用户名
229
- - type: fill
230
- selector: 'input[name="password"]'
231
- value: wrong_password # 故意填错密码
232
- - type: click
233
- selector: 'button[type="submit"]'
234
- - type: wait
235
- delay: 1000 # 等待错误提示出现
236
- assertions:
237
- - type: visible
238
- selector: '.error-message' # 错误提示元素
239
- - type: text
240
- selector: '.error-message'
241
- contains: 密码错误 # 根据实际文案调整(如"用户名或密码错误")
242
-
243
- # ── 场景7: 登录失败-账号锁定 ──
244
- # 说明: 使用被锁定的账号登录,验证锁定提示
245
- - name: 登录-账号锁定提示
246
- description: 模块:auth 页面:/login 异常场景
247
- actions:
248
- - type: navigate
249
- value: /login
250
- - type: fill
251
- selector: 'input[name="username"]'
252
- value: locked_user # 被锁定的测试账号
253
- - type: fill
254
- selector: 'input[name="password"]'
255
- value: any_password # 任意密码
256
- - type: click
257
- selector: 'button[type="submit"]'
258
- - type: wait
259
- delay: 1000
260
- assertions:
261
- - type: visible
262
- selector: '.error-message'
263
- - type: text
264
- selector: '.error-message'
265
- contains: 账号已被锁定 # 根据实际文案调整
266
-
267
- # ── 场景8: 记住密码 ──
268
- # 说明: 勾选记住密码 → 登录 → 验证 Cookie 中写入记住密码标记
269
- - name: 登录-记住密码
270
- description: 模块:auth 页面:/login 记住密码勾选
271
- actions:
272
- - type: navigate
273
- value: /login
274
- - type: check
275
- selector: 'input[name="rememberMe"]' # 记住密码复选框
276
- - type: fill
277
- selector: 'input[name="username"]'
278
- value: test_user
279
- - type: fill
280
- selector: 'input[name="password"]'
281
- value: Test@123456
282
- - type: click
283
- selector: 'button[type="submit"]'
284
- waitFor: '.dashboard-container'
285
- assertions:
286
- - type: cookie
287
- selector: remember_me # Cookie 名称
288
- contains: 'true' # Cookie 值包含 true
289
-
290
- # ── 场景9: 退出登录 ──
291
- # 说明: 点击用户头像展开菜单 → 点击退出 → 验证跳回登录页
292
- - name: 登录-退出登录
293
- description: 模块:auth 页面:/dashboard
294
- actions:
295
- - type: navigate
296
- value: /dashboard # 已登录后的首页
297
- - type: click
298
- selector: '.user-avatar' # 用户头像/菜单触发器
299
- - type: click
300
- selector: '.logout-btn' # 退出按钮
301
- - type: wait
302
- delay: 1000 # 等待跳转
303
- assertions:
304
- - type: url
305
- contains: /login # 断言回到登录页
306
- - type: visible
307
- selector: '.login-form' # 断言登录表单可见
308
-
309
- # ═══════════════════════════════════════════════════════════════════════════
310
- # 二、用户中心 (user)
311
- # ═══════════════════════════════════════════════════════════════════════════
312
-
313
- # ── 场景10: 修改个人资料 ──
314
- # 说明: 修改昵称、性别、邮箱,点击保存
315
- # ⚠️ 前置条件: 用户已登录。如需登录态,参考文件开头「方式A」把「场景13: Token注入」放第一个
316
- # select 说明: value 填 option 的 value 属性(不是显示文本)
317
- # 保存按钮 selector 写法示例(按稳定性排序):
318
- # 1. '[data-testid="save-profile-btn"]' ← 最稳定,需前端配合添加 data-testid
319
- # 2. '#save-profile-btn' ← 有 id 时用 id
320
- # 3. '.save-profile-btn' ← 稳定的 class
321
- # 4. 'button[type="submit"]' ← 页面只有一个提交按钮时可用
322
- # 5. '.profile-form button:last-child' ← 兜底:表单内最后一个按钮
323
- - name: 用户-修改个人资料
324
- description: 模块:user 页面:/profile/edit
325
- actions:
326
- - type: navigate
327
- value: /profile/edit # 个人资料编辑页
328
- - type: fill
329
- selector: 'input[name="nickname"]' # 昵称输入框
330
- value: 新昵称测试 # 新昵称
331
- - type: select
332
- selector: 'select[name="gender"]' # 性别下拉框
333
- value: female # option 的 value 值(如 female/male/unknown)
334
- - type: fill
335
- selector: 'input[name="email"]' # 邮箱输入框
336
- value: 'newemail@example.com' # 新邮箱
337
- - type: click
338
- selector: '.save-profile-btn' # 保存按钮
339
- - type: wait
340
- delay: 1500 # 等待保存接口返回
341
- assertions:
342
- - type: visible
343
- selector: '.toast-success' # 成功提示元素
344
- - type: text
345
- selector: '.toast-success'
346
- contains: 保存成功 # 预期提示文案
347
-
348
- # ── 场景11: 上传头像 ──
349
- # 说明: 选择本地图片文件上传,验证预览图更新
350
- # upload 说明: value 为本地文件路径(相对项目根目录或绝对路径)
351
- - name: 用户-上传头像
352
- description: 模块:user 页面:/profile/edit 头像上传
353
- actions:
354
- - type: navigate
355
- value: /profile/edit
356
- - type: upload
357
- selector: 'input[type="file"].avatar-upload' # 文件输入框
358
- value: ./fixtures/test-avatar.png # 本地图片路径(确保文件存在)
359
- - type: wait
360
- delay: 2000 # 等待上传完成
361
- assertions:
362
- - type: visible
363
- selector: '.avatar-preview img' # 头像预览图
364
- - type: attribute
365
- selector: '.avatar-preview img'
366
- attribute: src # 断言 src 属性已更新
367
- contains: avatar # 包含 avatar 路径
368
-
369
- # ── 场景12: 修改密码 ──
370
- # 说明: 填写旧密码 + 新密码 + 确认密码,点击修改
371
- - name: 用户-修改密码
372
- description: 模块:user 页面:/profile/security
373
- actions:
374
- - type: navigate
375
- value: /profile/security # 安全设置页
376
- - type: fill
377
- selector: 'input[name="oldPassword"]' # 旧密码
378
- value: OldPass123 # 当前密码
379
- - type: fill
380
- selector: 'input[name="newPassword"]' # 新密码
381
- value: NewPass456 # 新密码
382
- - type: fill
383
- selector: 'input[name="confirmPassword"]' # 确认密码
384
- value: NewPass456 # 与新密码保持一致
385
- - type: click
386
- selector: '.change-password-btn' # 确认修改按钮
387
- - type: wait
388
- delay: 1500
389
- assertions:
390
- - type: visible
391
- selector: '.toast-success'
392
- - type: text
393
- selector: '.toast-success'
394
- contains: 密码修改成功
395
-
396
- # ── 场景13: Token 注入免登录 ──
397
- # 说明: 直接通过 localStorage 注入 JWT,跳过登录流程
398
- # ⚠️ 核心用途: 作为「前置场景」放在业务场景(场景10-42)之前,为后续场景提供登录态
399
- # 适用场景: 测试环境已有固定账号,不需重复测试登录。把这个场景放第一个,后面所有场景共享登录态
400
- - name: 用户-Token注入免登录
401
- description: 模块:user 页面:/profile Token注入方式
402
- actions:
403
- - type: localStorage
404
- selector: accessToken # localStorage key 名
405
- value: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEwMDg2fQ.test' # JWT Token
406
- - type: localStorage
407
- selector: refreshToken # 同时注入 refreshToken
408
- value: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoicmVmcmVzaCJ9.test'
409
- - type: navigate
410
- value: /profile # 直接访问需要登录的页面
411
- - type: wait
412
- delay: 800
413
- assertions:
414
- - type: visible
415
- selector: '.profile-header' # 断言个人中心头部可见
416
- - type: text
417
- selector: '.username' # 断言显示用户名
418
- contains: test_user
419
-
420
- # ═══════════════════════════════════════════════════════════════════════════
421
- # 三、商品模块 (product)
422
- # ═══════════════════════════════════════════════════════════════════════════
423
-
424
- # ── 场景14: 商品列表浏览 ──
425
- # 说明: 进入商品列表 → 滚动到底部 → 验证商品数量
426
- - name: 商品-列表浏览
427
- description: 模块:product 页面:/products
428
- actions:
429
- - type: navigate
430
- value: /products # 商品列表页
431
- - type: scroll
432
- selector: bottom # 滚动到页面底部(触发懒加载)
433
- - type: wait
434
- delay: 1000
435
- assertions:
436
- - type: visible
437
- selector: '.product-list' # 商品列表容器
438
- - type: count
439
- selector: '.product-item' # 商品卡片
440
- count: 10 # 预期至少 10 个商品
441
-
442
- # ── 场景15: 商品搜索 ──
443
- # 说明: 输入关键词 → 按回车 → 验证搜索结果
444
- - name: 商品-搜索
445
- description: 模块:product 页面:/products 搜索功能
446
- actions:
447
- - type: navigate
448
- value: /products
449
- - type: fill
450
- selector: 'input[name="keyword"]' # 搜索框
451
- value: iPhone # 搜索关键词
452
- - type: press
453
- key: Enter # 按回车触发搜索
454
- - type: wait
455
- delay: 1500 # 等待搜索结果返回
456
- assertions:
457
- - type: visible
458
- selector: '.search-result-list' # 搜索结果列表
459
- - type: count
460
- selector: '.product-item'
461
- count: 3 # 预期至少 3 个结果
462
-
463
- # ── 场景16: 商品详情 ──
464
- # 说明: 点击商品标题进入详情页,验证详情信息展示
465
- - name: 商品-详情页
466
- description: 模块:product 页面:/products/:id
467
- actions:
468
- - type: navigate
469
- value: /products
470
- - type: click
471
- selector: '.product-item:first-child .product-title' # 点击第一个商品的标题
472
- - type: wait
473
- delay: 1000
474
- assertions:
475
- - type: visible
476
- selector: '.product-detail' # 详情页容器
477
- - type: text
478
- selector: '.product-name' # 商品名称
479
- contains: iPhone # 包含搜索关键词
480
- - type: visible
481
- selector: '.product-price' # 价格元素
482
-
483
- # ── 场景17: 商品多条件筛选 ──
484
- # 说明: 同时选择分类 + 库存状态,验证筛选结果
485
- - name: 商品-多条件筛选
486
- description: 模块:product 页面:/products 筛选功能
487
- actions:
488
- - type: navigate
489
- value: /products
490
- - type: select
491
- selector: 'select[name="category"]' # 分类下拉框
492
- value: electronics # option value(如 electronics/clothing/food)
493
- - type: check
494
- selector: 'input[value="inStock"]' # 仅看有库存(复选框)
495
- - type: click
496
- selector: '.filter-submit-btn' # 确认筛选按钮
497
- - type: wait
498
- delay: 1200
499
- assertions:
500
- - type: visible
501
- selector: '.product-list'
502
- - type: count
503
- selector: '.product-item'
504
- count: 5 # 筛选后预期 5 个商品
505
-
506
- # ── 场景18: 商品收藏 ──
507
- # 说明: 点击收藏按钮,验证按钮颜色变为红色(已收藏状态)
508
- - name: 商品-收藏/取消收藏
509
- description: 模块:product 页面:/products/:id 收藏功能
510
- actions:
511
- - type: navigate
512
- value: /products/123 # 某个商品详情页(123 为商品 ID)
513
- - type: click
514
- selector: '.favorite-btn' # 收藏按钮(空心)
515
- - type: wait
516
- delay: 500
517
- assertions:
518
- - type: style
519
- selector: '.favorite-btn' # 断言按钮样式变化
520
- style: color # CSS 属性: 颜色
521
- value: rgb(255, 77, 79) # 预期变为红色(根据实际样式调整)
522
-
523
- # ═══════════════════════════════════════════════════════════════════════════
524
- # 四、购物车 (cart)
525
- # ═══════════════════════════════════════════════════════════════════════════
526
-
527
- # ── 场景19: 添加商品到购物车 ──
528
- # 说明: 商品详情页点击加购,验证购物车角标数字更新
529
- - name: 购物车-添加商品
530
- description: 模块:cart 页面:/products/:id
531
- actions:
532
- - type: navigate
533
- value: /products/123 # 商品详情页
534
- - type: click
535
- selector: '.add-to-cart-btn' # 加入购物车按钮
536
- - type: wait
537
- delay: 800 # 等待加购动画/接口
538
- assertions:
539
- - type: visible
540
- selector: '.cart-badge' # 购物车角标
541
- - type: text
542
- selector: '.cart-badge'
543
- contains: '1' # 角标显示 1
544
-
545
- # ── 场景20: 修改购物车商品数量 ──
546
- # 说明: 修改数量输入框 → 失去焦点触发计算 → 验证小计金额更新
547
- - name: 购物车-修改数量
548
- description: 模块:cart 页面:/cart
549
- actions:
550
- - type: navigate
551
- value: /cart # 购物车页面
552
- - type: fill
553
- selector: '.cart-item:first-child .quantity-input' # 第一个商品的数量输入框
554
- value: '3' # 修改为 3 件
555
- - type: blur
556
- selector: '.cart-item:first-child .quantity-input' # 触发 blur 事件(触发价格重算)
557
- - type: wait
558
- delay: 800
559
- assertions:
560
- - type: text
561
- selector: '.cart-item:first-child .subtotal' # 小计金额
562
- contains: '¥' # 包含人民币符号(断言金额已更新)
563
-
564
- # ── 场景21: 购物车去结算 ──
565
- # 说明: 勾选商品 → 点击结算 → 验证跳转到结算页
566
- - name: 购物车-去结算
567
- description: 模块:cart 页面:/cart
568
- actions:
569
- - type: navigate
570
- value: /cart
571
- - type: check
572
- selector: '.cart-item:first-child .item-checkbox' # 勾选第一个商品
573
- - type: click
574
- selector: '.checkout-btn' # 去结算按钮
575
- - type: wait
576
- delay: 1500
577
- assertions:
578
- - type: url
579
- contains: /checkout # 断言跳转到结算页
580
- - type: visible
581
- selector: '.checkout-form' # 结算表单
582
-
583
- # ── 场景22: 删除购物车商品 ──
584
- # 说明: 点击删除按钮,验证商品被移除
585
- - name: 购物车-删除商品
586
- description: 模块:cart 页面:/cart
587
- actions:
588
- - type: navigate
589
- value: /cart
590
- - type: click
591
- selector: '.cart-item:first-child .delete-btn' # 删除按钮
592
- - type: wait
593
- delay: 800
594
- assertions:
595
- - type: count
596
- selector: '.cart-item' # 断言购物车商品数量
597
- count: 0 # 预期为 0(空购物车)
598
-
599
- # ═══════════════════════════════════════════════════════════════════════════
600
- # 五、订单模块 (order)
601
- # ═══════════════════════════════════════════════════════════════════════════
602
-
603
- # ── 场景23: 创建订单 ──
604
- # 说明: 填写收货信息 → 提交订单 → 验证订单创建成功
605
- - name: 订单-创建订单
606
- description: 模块:order 页面:/checkout
607
- actions:
608
- - type: navigate
609
- value: /checkout # 结算页
610
- - type: fill
611
- selector: 'input[name="receiverName"]' # 收货人姓名
612
- value: 张三 # 真实姓名或测试姓名
613
- - type: fill
614
- selector: 'input[name="phone"]' # 手机号
615
- value: '13800138000'
616
- - type: fill
617
- selector: 'input[name="address"]' # 详细地址
618
- value: 北京市朝阳区测试街道123号 # 测试地址
619
- - type: click
620
- selector: '.submit-order-btn' # 提交订单按钮
621
- - type: wait
622
- delay: 2000 # 等待订单创建接口
623
- assertions:
624
- - type: url
625
- contains: /order/success # 断言跳转到成功页
626
- - type: visible
627
- selector: '.order-success' # 成功提示
628
- - type: text
629
- selector: '.order-success'
630
- contains: 订单提交成功
631
-
632
- # ── 场景24: 订单支付 ──
633
- # 说明: 选择支付方式 → 确认支付 → 等待支付接口返回 → 验证成功
634
- - name: 订单-支付订单
635
- description: 模块:order 页面:/pay/:orderId
636
- actions:
637
- - type: navigate
638
- value: /pay/ORD202409110001 # 支付页(ORD202409110001 为订单号)
639
- - type: click
640
- selector: '.pay-method-wechat' # 选择微信支付
641
- - type: click
642
- selector: '.confirm-pay-btn' # 确认支付按钮
643
- - type: waitForResponse
644
- selector: '/api/pay/create' # 等待支付创建接口返回
645
- timeout: 10000 # 支付接口较慢,超时设 10 秒
646
- - type: wait
647
- delay: 2000
648
- assertions:
649
- - type: url
650
- contains: /pay/success # 断言跳转到支付成功页
651
- - type: visible
652
- selector: '.pay-success'
653
-
654
- # ── 场景25: 订单列表查询 ──
655
- - name: 订单-列表查询
656
- description: 模块:order 页面:/orders
657
- actions:
658
- - type: navigate
659
- value: /orders # 订单列表页
660
- - type: wait
661
- delay: 1000
662
- assertions:
663
- - type: visible
664
- selector: '.order-list'
665
- - type: count
666
- selector: '.order-item'
667
- count: 5 # 预期至少 5 个订单
668
-
669
- # ── 场景26: 订单详情查看 ──
670
- - name: 订单-详情查看
671
- description: 模块:order 页面:/orders/:id
672
- actions:
673
- - type: navigate
674
- value: /orders
675
- - type: click
676
- selector: '.order-item:first-child .view-detail-btn' # 查看第一个订单详情
677
- - type: wait
678
- delay: 800
679
- assertions:
680
- - type: visible
681
- selector: '.order-detail'
682
- - type: text
683
- selector: '.order-status' # 订单状态
684
- contains: 待付款 # 预期状态文案
685
-
686
- # ── 场景27: 取消订单 ──
687
- # 说明: 点击取消 → 确认取消 → 验证状态变为已取消
688
- - name: 订单-取消订单
689
- description: 模块:order 页面:/orders/:id
690
- actions:
691
- - type: navigate
692
- value: /orders/ORD202409110001 # 某个待付款订单
693
- - type: click
694
- selector: '.cancel-order-btn' # 取消订单按钮
695
- - type: wait
696
- delay: 500 # 等待确认弹窗
697
- - type: click
698
- selector: '.confirm-cancel-btn' # 确认取消按钮
699
- - type: wait
700
- delay: 1500 # 等待取消接口返回
701
- assertions:
702
- - type: text
703
- selector: '.order-status'
704
- contains: 已取消 # 状态变为已取消
705
-
706
- # ═══════════════════════════════════════════════════════════════════════════
707
- # 六、消息通知 (notification)
708
- # ═══════════════════════════════════════════════════════════════════════════
709
-
710
- # ── 场景28: 消息列表查看 ──
711
- - name: 消息-列表查看
712
- description: 模块:notification 页面:/notifications
713
- actions:
714
- - type: navigate
715
- value: /notifications # 消息中心
716
- - type: wait
717
- delay: 1000
718
- assertions:
719
- - type: visible
720
- selector: '.notification-list'
721
- - type: count
722
- selector: '.notification-item'
723
- count: 3 # 预期 3 条未读消息
724
-
725
- # ── 场景29: 消息标记已读 ──
726
- # 说明: 点击消息 → 验证字体从粗体变为正常(已读状态)
727
- - name: 消息-标记已读
728
- description: 模块:notification 页面:/notifications
729
- actions:
730
- - type: navigate
731
- value: /notifications
732
- - type: click
733
- selector: '.notification-item:first-child' # 点击第一条消息
734
- - type: wait
735
- delay: 500
736
- assertions:
737
- - type: style
738
- selector: '.notification-item:first-child'
739
- style: font-weight # CSS 属性: 字重
740
- value: '400' # 400 = normal(已读),700 = bold(未读)
741
-
742
- # ═══════════════════════════════════════════════════════════════════════════
743
- # 七、后台管理 (admin)
744
- # ═══════════════════════════════════════════════════════════════════════════
745
-
746
- # ── 场景30: 用户管理列表 ──
747
- - name: 后台-用户管理
748
- description: 模块:admin 页面:/admin/users
749
- actions:
750
- - type: navigate
751
- value: /admin/users # 后台用户管理页
752
- - type: wait
753
- delay: 1500 # 等待表格数据加载
754
- assertions:
755
- - type: visible
756
- selector: '.user-table' # 用户表格
757
- - type: count
758
- selector: '.user-table tbody tr' # 表格行数
759
- count: 10 # 预期 10 条用户数据
760
-
761
- # ── 场景31: 数据报表(iframe 嵌入) ──
762
- # 说明: 后台报表通常用 iframe 嵌入第三方 BI 工具(如帆软、Tableau)
763
- - name: 后台-数据报表iframe
764
- description: 模块:admin 页面:/admin/reports iframe嵌入
765
- actions:
766
- - type: navigate
767
- value: /admin/reports # 报表页面
768
- - type: iframe
769
- selector: '#report-frame' # 报表 iframe
770
- - type: wait
771
- delay: 2000 # 等待 iframe 内报表加载
772
- assertions:
773
- - type: visible
774
- selector: '.chart-container' # 图表容器(在 iframe 内)
775
- - type: count
776
- selector: '.chart-item' # 图表数量
777
- count: 4 # 预期 4 个图表
778
-
779
- # ── 场景32: 拖拽排序 ──
780
- # 说明: 拖拽分类/菜单项调整顺序
781
- - name: 后台-拖拽排序
782
- description: 模块:admin 页面:/admin/categories 拖拽排序
783
- actions:
784
- - type: navigate
785
- value: /admin/categories # 分类管理页
786
- - type: drag
787
- selector: '.category-item:first-child' # 拖拽第一个分类
788
- toSelector: '.category-item:last-child' # 放到最后一个位置
789
- - type: wait
790
- delay: 1000 # 等待保存接口
791
- assertions:
792
- - type: text
793
- selector: '.category-item:last-child .category-name' # 断言最后一个分类的名称
794
- contains: 原第一个分类 # 确认拖拽成功
795
-
796
- # ═══════════════════════════════════════════════════════════════════════════
797
- # 八、通用能力
798
- # ═══════════════════════════════════════════════════════════════════════════
799
-
800
- # ── 场景33: Cookie 注入免登录 ──
801
- # 说明: 直接注入 session Cookie,跳过登录页,适合批量测试业务模块
802
- # ⚠️ 核心用途: 作为「前置场景」放在业务场景(场景10-42)之前,为后续场景提供登录态
803
- # 如何获取 Cookie: 在浏览器开发者工具 → Application → Cookies 中复制
804
- - name: 通用-Cookie注入免登录
805
- description: 模块:common Cookie注入跳过登录
806
- actions:
807
- - type: cookie
808
- selector: session_token # Cookie 名称
809
- value: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEwMDg2fQ.test' # Cookie 值
810
- domain: example.com # Cookie 域名(必须与测试站点一致)
811
- path: / # Cookie 路径
812
- secure: true # 仅 HTTPS 传输
813
- httpOnly: true # 禁止 JS 读取
814
- - type: cookie
815
- selector: user_id # 同时注入 user_id
816
- value: '10086'
817
- domain: example.com
818
- path: /
819
- - type: navigate
820
- value: /dashboard # 直接访问需登录页面
821
- - type: wait
822
- delay: 1000
823
- assertions:
824
- - type: visible
825
- selector: '.dashboard-container'
826
- - type: text
827
- selector: '.user-name'
828
- contains: test_user
829
-
830
- # ── 场景34: 首页滚动加载 ──
831
- - name: 通用-首页滚动加载
832
- description: 模块:home 页面:/
833
- actions:
834
- - type: navigate
835
- value: /
836
- - type: scroll
837
- selector: bottom # 滚动到底部
838
- - type: wait
839
- delay: 1500
840
- - type: scroll
841
- selector: bottom # 再次滚动(触发第二轮加载)
842
- - type: wait
843
- delay: 1500
844
- assertions:
845
- - type: visible
846
- selector: '.load-more-btn' # 加载更多按钮
847
- - type: count
848
- selector: '.product-card' # 商品卡片总数
849
- count: 20 # 预期加载了 20 个商品
850
-
851
- # ── 场景35: 样式断言 ──
852
- # 说明: 验证按钮颜色、圆角等样式是否符合设计稿
853
- - name: 通用-按钮样式检查
854
- description: 模块:ui 页面:/
855
- actions:
856
- - type: navigate
857
- value: /
858
- assertions:
859
- - type: style
860
- selector: '.primary-btn' # 主按钮
861
- style: background-color # CSS 属性: 背景色
862
- value: rgb(24, 144, 255) # 预期颜色(根据设计稿调整)
863
- - type: style
864
- selector: '.primary-btn'
865
- style: border-radius # CSS 属性: 圆角
866
- value: 4px # 预期 4px 圆角
867
-
868
- # ── 场景36: 视觉对比测试 ──
869
- # 说明: 截图后与基线图片对比,检测 UI 回归
870
- # 前置条件: 先执行一次生成基线图片,后续对比以此为准
871
- - name: 通用-视觉对比测试
872
- description: 模块:ui 页面:/ 视觉回归
873
- actions:
874
- - type: navigate
875
- value: /
876
- - type: screenshot # 页面截图
877
- assertions:
878
- - type: visual
879
- selector: 'body' # 对比整个页面
880
- threshold: normal # 阈值: strict/normal/loose
881
-
882
- # ── 场景37: 键盘操作 ──
883
- # 说明: 聚焦搜索框 → 输入关键词 → 按回车触发搜索
884
- - name: 通用-键盘操作
885
- description: 模块:common 页面:/products 键盘导航
886
- actions:
887
- - type: navigate
888
- value: /products
889
- - type: focus
890
- selector: 'input[name="keyword"]' # 聚焦搜索框
891
- - type: fill
892
- selector: 'input[name="keyword"]'
893
- value: MacBook
894
- - type: press
895
- key: Enter # 按回车
896
- - type: wait
897
- delay: 1500
898
- assertions:
899
- - type: visible
900
- selector: '.search-result-list'
901
- - type: count
902
- selector: '.product-item'
903
- count: 2 # 预期 2 个结果
904
-
905
- # ── 场景38: hover 效果 ──
906
- # 说明: 鼠标悬停在商品卡片上,验证阴影/边框等 hover 样式
907
- - name: 通用-hover效果
908
- description: 模块:ui 页面:/products hover交互
909
- actions:
910
- - type: navigate
911
- value: /products
912
- - type: hover
913
- selector: '.product-item:first-child' # 悬停在第一个商品
914
- - type: wait
915
- delay: 500 # 等待 hover 动画
916
- assertions:
917
- - type: style
918
- selector: '.product-item:first-child'
919
- style: box-shadow # CSS 属性: 阴影
920
- contains: rgba # 包含 rgba(有阴影)
921
-
922
- # ── 场景39: 元素隐藏检查 ──
923
- # 说明: 验证未登录状态下,用户菜单和退出按钮不显示
924
- - name: 通用-元素隐藏
925
- description: 模块:ui 页面:/login 未登录状态
926
- actions:
927
- - type: navigate
928
- value: /login # 未登录页面
929
- assertions:
930
- - type: hidden
931
- selector: '.user-menu' # 用户菜单应隐藏
932
- - type: hidden
933
- selector: '.logout-btn' # 退出按钮应隐藏
934
-
935
- # ── 场景40: 表单值断言 ──
936
- # 说明: 验证表单字段的默认值是否正确
937
- - name: 通用-表单值检查
938
- description: 模块:user 页面:/profile/edit 表单默认值
939
- actions:
940
- - type: navigate
941
- value: /profile/edit
942
- assertions:
943
- - type: value
944
- selector: 'input[name="username"]' # 断言表单值
945
- equals: test_user # 精确匹配
946
- - type: value
947
- selector: 'input[name="email"]'
948
- contains: '@' # 包含 @ 符号
949
-
950
- # ── 场景41: JavaScript 注入执行 ──
951
- # 说明: 执行自定义 JS,如修改全局变量、调用前端方法
952
- - name: 通用-JS脚本注入
953
- description: 模块:common 页面:/ JS注入测试
954
- actions:
955
- - type: navigate
956
- value: /
957
- - type: script
958
- value: 'window.testFlag = "injected";' # 设置全局变量
959
- - type: wait
960
- delay: 500
961
- - type: script
962
- value: 'return window.testFlag;' # 读取全局变量(返回给引擎)
963
- assertions:
964
- - type: text
965
- selector: 'body'
966
- contains: injected # 断言页面包含 injected 文本
967
-
968
- # ── 场景42: 响应式布局测试 ──
969
- # 说明: 验证移动端下显示移动端导航,隐藏桌面端侧边栏
970
- # 注意: Playwright 可通过 viewport 配置模拟不同设备尺寸
971
- - name: 通用-响应式布局
972
- description: 模块:ui 页面:/ 移动端适配
973
- actions:
974
- - type: navigate
975
- value: /
976
- - type: wait
977
- delay: 800
978
- assertions:
979
- - type: visible
980
- selector: '.mobile-nav' # 移动端导航应可见
981
- - type: hidden
982
- selector: '.desktop-sidebar' # 桌面端侧边栏应隐藏