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