ykq-playwright-ci 0.1.0 → 0.1.2
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/README.md +127 -393
- package/bin/playwright-ci.mjs +33 -0
- package/package.json +2 -1
- package/playwright.config.mjs +62 -0
- package/playwright.config.ts +11 -6
- package/scripts/run-project.mjs +120 -3
package/README.md
CHANGED
|
@@ -1,261 +1,111 @@
|
|
|
1
|
-
# Playwright CI
|
|
1
|
+
# Playwright CI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
一个本地 Playwright Runner:按项目名拉取对应用例库,然后在测试环境跑自动化用例。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
当前优先覆盖医生端测试环境的基础链路:
|
|
8
|
-
|
|
9
|
-
- 登录页是否能正常渲染。
|
|
10
|
-
- 未登录访问首页是否按路由守卫跳回登录页。
|
|
11
|
-
- 用户协议页是否允许未登录访问。
|
|
12
|
-
- 登录表单的基础校验是否符合 `doctor-master` 逻辑。
|
|
13
|
-
|
|
14
|
-
这些用例不需要真实医生账号,也不会真的登录、开方或提交处方。登录后业务链路需要先解决测试账号、验证码或登录态方案。
|
|
15
|
-
|
|
16
|
-
## 环境要求
|
|
17
|
-
|
|
18
|
-
本地使用这个 runner 前,需要准备这些环境:
|
|
19
|
-
|
|
20
|
-
| 环境 | 要求 | 说明 |
|
|
21
|
-
| --- | --- | --- |
|
|
22
|
-
| Node.js | 建议使用 `.nvmrc` 指定的 `22` | 本工具运行在 Node 侧,不受医生端业务仓 Node 14 限制 |
|
|
23
|
-
| npm | 随 Node 安装即可 | 推荐用 `npm ci` 或 `npm install` 安装依赖 |
|
|
24
|
-
| Git | 需要可执行 `git clone`、`git fetch`、`git pull` | 用于拉取各项目的用例库 |
|
|
25
|
-
| Git 权限 | 需要能访问 `git.int.ybm100.com` | 可用本机 Git 凭据,也可临时传 `GIT_USERNAME` / `GIT_PASSWORD` |
|
|
26
|
-
| 浏览器 | Playwright Chromium | 首次运行前执行 `npx playwright install chromium` |
|
|
27
|
-
| 网络 | 需要访问 Git 仓库和目标测试环境 | 医生端默认访问 `https://ys-test.heyejk.com` |
|
|
28
|
-
|
|
29
|
-
当前基础用例不需要医生测试账号。后续如果跑登录后链路,还需要测试医生账号、验证码绕过方案,或 `playwright/.auth/doctor.json` 登录态文件。
|
|
30
|
-
|
|
31
|
-
不要把账号、密码、token、登录态写进 Git:
|
|
5
|
+
当前默认项目是医生端 PC:
|
|
32
6
|
|
|
33
7
|
```text
|
|
34
|
-
.
|
|
35
|
-
playwright/.auth/*.json
|
|
36
|
-
GIT_PASSWORD
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## 环境准备
|
|
40
|
-
|
|
41
|
-
进入工程目录:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
cd /Users/huangcongqiang/Desktop/products/playwright-ci
|
|
8
|
+
doctor-pc -> https://git.int.ybm100.com/ykq/doctor-pc-web-test-store
|
|
45
9
|
```
|
|
46
10
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
npm install
|
|
51
|
-
```
|
|
11
|
+
## 先跑起来
|
|
52
12
|
|
|
53
|
-
|
|
13
|
+
需要先准备:
|
|
54
14
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
15
|
+
- Node.js `22+`
|
|
16
|
+
- npm
|
|
17
|
+
- Git,并且能访问 `git.int.ybm100.com`
|
|
18
|
+
- 能访问目标测试环境,例如 `https://ys-test.heyejk.com`
|
|
58
19
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
npx playwright install chromium
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
如果机器已经装过对应版本,Playwright 会复用缓存。
|
|
66
|
-
|
|
67
|
-
## 通过 npm 安装使用
|
|
68
|
-
|
|
69
|
-
npm 包名使用 `ykq-playwright-ci`。`playwright-ci` 这个包名已经被 npm 上其它作者占用,所以本工具发布时不能继续使用原仓库名。
|
|
70
|
-
|
|
71
|
-
全局安装:
|
|
20
|
+
安装工具:
|
|
72
21
|
|
|
73
22
|
```bash
|
|
74
23
|
npm install -g ykq-playwright-ci
|
|
75
24
|
```
|
|
76
25
|
|
|
77
|
-
|
|
26
|
+
建一个专门放配置和用例缓存的目录:
|
|
78
27
|
|
|
79
28
|
```bash
|
|
80
29
|
mkdir playwright-runner
|
|
81
30
|
cd playwright-runner
|
|
82
|
-
playwright-ci init
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
初始化后会在当前目录生成:
|
|
86
|
-
|
|
87
|
-
```text
|
|
88
|
-
projects.config.json
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
之后可以直接按项目运行:
|
|
92
|
-
|
|
93
|
-
```bash
|
|
94
|
-
playwright-ci list
|
|
95
|
-
playwright-ci run doctor-pc
|
|
96
|
-
playwright-ci run doctor-pc --headed
|
|
97
|
-
playwright-ci run doctor-pc --debug
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
也可以使用项目配置命令:
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
playwright-ci add
|
|
104
|
-
playwright-ci show doctor-pc
|
|
105
|
-
playwright-ci update doctor-pc
|
|
106
|
-
playwright-ci delete doctor-pc
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
如果不想全局安装,发布后也可以用 `npx`:
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
npx ykq-playwright-ci init
|
|
113
|
-
npx ykq-playwright-ci run doctor-pc
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
从 npm 安装后,Git 用例库仍然拉到当前工作目录的 `.case-repos/`,`projects.config.json` 也保存在当前工作目录,不会写到全局 `node_modules` 目录。
|
|
117
|
-
|
|
118
|
-
## 最常用命令
|
|
119
|
-
|
|
120
|
-
查看已配置的项目:
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
npm run project:list
|
|
124
31
|
```
|
|
125
32
|
|
|
126
|
-
|
|
33
|
+
初始化配置:
|
|
127
34
|
|
|
128
35
|
```bash
|
|
129
|
-
|
|
36
|
+
playwright-ci init
|
|
130
37
|
```
|
|
131
38
|
|
|
132
|
-
|
|
39
|
+
安装浏览器:
|
|
133
40
|
|
|
134
41
|
```bash
|
|
135
|
-
|
|
42
|
+
playwright-ci install chromium
|
|
136
43
|
```
|
|
137
44
|
|
|
138
|
-
|
|
45
|
+
运行医生端用例:
|
|
139
46
|
|
|
140
47
|
```bash
|
|
141
|
-
|
|
48
|
+
playwright-ci run doctor-pc
|
|
142
49
|
```
|
|
143
50
|
|
|
144
|
-
|
|
51
|
+
如果 Git 需要账号密码:
|
|
145
52
|
|
|
146
53
|
```bash
|
|
147
|
-
|
|
54
|
+
GIT_USERNAME=你的用户名 GIT_PASSWORD='你的密码或访问令牌' playwright-ci run doctor-pc
|
|
148
55
|
```
|
|
149
56
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
```bash
|
|
153
|
-
npm run doctor:test
|
|
154
|
-
```
|
|
57
|
+
不要把账号密码写进 `projects.config.json`。
|
|
155
58
|
|
|
156
|
-
|
|
59
|
+
## 常用命令
|
|
157
60
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
61
|
+
| 命令 | 用途 |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `playwright-ci init` | 在当前目录生成 `projects.config.json` |
|
|
64
|
+
| `playwright-ci list` | 查看已有项目 |
|
|
65
|
+
| `playwright-ci run doctor-pc` | 后台运行医生端用例 |
|
|
66
|
+
| `playwright-ci run doctor-pc --headed` | 打开浏览器窗口运行 |
|
|
67
|
+
| `playwright-ci run doctor-pc --debug` | 调试模式运行 |
|
|
68
|
+
| `playwright-ci run doctor-pc --ui` | 打开 Playwright UI |
|
|
69
|
+
| `playwright-ci add` | 交互式新增项目 |
|
|
70
|
+
| `playwright-ci show doctor-pc` | 查看项目配置 |
|
|
71
|
+
| `playwright-ci update doctor-pc` | 修改项目配置 |
|
|
72
|
+
| `playwright-ci delete doctor-pc` | 删除项目配置 |
|
|
73
|
+
| `playwright-ci install chromium` | 安装 Chromium 浏览器 |
|
|
161
74
|
|
|
162
|
-
|
|
75
|
+
也可以不全局安装,直接用 `npx`:
|
|
163
76
|
|
|
164
77
|
```bash
|
|
165
|
-
|
|
78
|
+
npx ykq-playwright-ci init
|
|
79
|
+
npx ykq-playwright-ci run doctor-pc
|
|
166
80
|
```
|
|
167
81
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
```bash
|
|
171
|
-
npm run doctor:test:ui
|
|
172
|
-
```
|
|
82
|
+
## 它实际做了什么
|
|
173
83
|
|
|
174
|
-
|
|
84
|
+
执行:
|
|
175
85
|
|
|
176
86
|
```bash
|
|
177
|
-
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## 命令说明
|
|
181
|
-
|
|
182
|
-
| 命令 | 用途 | 是否打开浏览器窗口 |
|
|
183
|
-
| --- | --- | --- |
|
|
184
|
-
| `npm run project:list` | 查看当前 runner 已配置的项目 | 否 |
|
|
185
|
-
| `npm run project:show -- doctor-pc` | 查看单个项目完整配置 | 否 |
|
|
186
|
-
| `npm run project:add` | 交互式新增项目配置 | 否 |
|
|
187
|
-
| `npm run project:update -- doctor-pc` | 交互式更新项目配置 | 否 |
|
|
188
|
-
| `npm run project:delete -- doctor-pc` | 删除项目配置,需要输入 `delete` 确认 | 否 |
|
|
189
|
-
| `npm run project:test -- doctor-pc` | 按项目名运行用例 | 否 |
|
|
190
|
-
| `npm run doctor:test` | 跑医生端测试环境基础用例,适合日常验证和 CI | 否 |
|
|
191
|
-
| `npm run doctor:test:headed` | 跑同一批用例,同时显示浏览器窗口 | 是 |
|
|
192
|
-
| `npm run doctor:test:debug` | 调试用例,可暂停、逐步执行 | 是 |
|
|
193
|
-
| `npm run doctor:test:ui` | 打开 Playwright UI 选择和重跑用例 | 是 |
|
|
194
|
-
| `npm run doctor:test:local` | 不拉远程仓库,直接跑当前 runner 内置医生端用例 | 否 |
|
|
195
|
-
| `npm run patient:test` | 跑患者端用例库;需要先配置 repo | 否 |
|
|
196
|
-
| `npm run admin:test` | 跑后台用例库;需要先配置 repo | 否 |
|
|
197
|
-
| `npm run report` | 打开 HTML 测试报告 | 是 |
|
|
198
|
-
| `npm run typecheck` | TypeScript 类型检查 | 否 |
|
|
199
|
-
| `npm run lint` | ESLint 检查 | 否 |
|
|
200
|
-
|
|
201
|
-
## 多项目用例库
|
|
202
|
-
|
|
203
|
-
本工程现在是一个本地 Playwright Runner。Runner 负责:
|
|
204
|
-
|
|
205
|
-
- 管理 Playwright 版本、浏览器、报告和通用命令。
|
|
206
|
-
- 按项目名读取配置。
|
|
207
|
-
- 如果项目配置了 Git 用例库,运行前自动 clone 或 `git pull --ff-only`。
|
|
208
|
-
- 根据项目配置设置测试环境变量。
|
|
209
|
-
- 调用 Playwright 执行对应用例目录。
|
|
210
|
-
|
|
211
|
-
医生端用例库:
|
|
212
|
-
|
|
213
|
-
```text
|
|
214
|
-
https://git.int.ybm100.com/ykq/doctor-pc-web-test-store
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
项目配置文件:
|
|
218
|
-
|
|
219
|
-
```text
|
|
220
|
-
projects.config.json
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
当前结构:
|
|
224
|
-
|
|
225
|
-
```json
|
|
226
|
-
{
|
|
227
|
-
"caseCacheDir": ".case-repos",
|
|
228
|
-
"projects": {
|
|
229
|
-
"doctor-pc": {
|
|
230
|
-
"repo": "https://git.int.ybm100.com/ykq/doctor-pc-web-test-store",
|
|
231
|
-
"branch": "master",
|
|
232
|
-
"caseDir": ".case-repos/doctor-pc",
|
|
233
|
-
"env": {
|
|
234
|
-
"DOCTOR_BASE_URL": "https://ys-test.heyejk.com",
|
|
235
|
-
"DOCTOR_PUBLIC_PATH": "/physician-admin/",
|
|
236
|
-
"DOCTOR_LOGIN_PATH": "/physician-admin/login"
|
|
237
|
-
},
|
|
238
|
-
"playwrightArgs": ["--project=chromium", "--no-deps"]
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
87
|
+
playwright-ci run doctor-pc
|
|
242
88
|
```
|
|
243
89
|
|
|
244
|
-
|
|
90
|
+
流程是:
|
|
245
91
|
|
|
246
92
|
```text
|
|
247
|
-
.
|
|
93
|
+
读取当前目录的 projects.config.json
|
|
94
|
+
拉取 doctor-pc 对应的 Git 用例库
|
|
95
|
+
缓存到 .case-repos/doctor-pc
|
|
96
|
+
设置 DOCTOR_BASE_URL 等环境变量
|
|
97
|
+
调用 Playwright 跑测试
|
|
248
98
|
```
|
|
249
99
|
|
|
250
|
-
|
|
100
|
+
默认是无头浏览器,所以不会弹窗口。要看真实页面操作,用:
|
|
251
101
|
|
|
252
102
|
```bash
|
|
253
|
-
|
|
103
|
+
playwright-ci run doctor-pc --headed
|
|
254
104
|
```
|
|
255
105
|
|
|
256
|
-
|
|
106
|
+
## 默认医生端配置
|
|
257
107
|
|
|
258
|
-
|
|
108
|
+
`playwright-ci init` 生成的 `doctor-pc` 配置大致是:
|
|
259
109
|
|
|
260
110
|
```json
|
|
261
111
|
{
|
|
@@ -271,283 +121,167 @@ GIT_USERNAME=你的用户名 GIT_PASSWORD='你的密码或访问令牌' npm run
|
|
|
271
121
|
}
|
|
272
122
|
```
|
|
273
123
|
|
|
274
|
-
|
|
124
|
+
当前基础用例会访问:
|
|
275
125
|
|
|
276
126
|
```text
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
-> 设置 DOCTOR_BASE_URL 等环境变量
|
|
281
|
-
-> 执行 Playwright
|
|
127
|
+
https://ys-test.heyejk.com/physician-admin/login
|
|
128
|
+
https://ys-test.heyejk.com/physician-admin/home
|
|
129
|
+
https://ys-test.heyejk.com/physician-admin/serviceAgreement
|
|
282
130
|
```
|
|
283
131
|
|
|
284
|
-
|
|
132
|
+
当前基础用例不需要真实医生账号,不会真的登录、开方或提交处方。
|
|
133
|
+
|
|
134
|
+
## 项目配置怎么改
|
|
285
135
|
|
|
286
|
-
|
|
136
|
+
查看:
|
|
287
137
|
|
|
288
|
-
|
|
138
|
+
```bash
|
|
139
|
+
playwright-ci show doctor-pc
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
交互式修改:
|
|
289
143
|
|
|
290
144
|
```bash
|
|
291
|
-
|
|
292
|
-
npm run project:test -- doctor-pc --headed
|
|
293
|
-
npm run project:test -- doctor-pc --debug
|
|
145
|
+
playwright-ci update doctor-pc
|
|
294
146
|
```
|
|
295
147
|
|
|
296
|
-
|
|
148
|
+
新增一个项目:
|
|
297
149
|
|
|
298
150
|
```bash
|
|
299
|
-
|
|
300
|
-
npm run project:show -- doctor-pc
|
|
301
|
-
npm run project:add
|
|
302
|
-
npm run project:update -- doctor-pc
|
|
303
|
-
npm run project:delete -- doctor-pc
|
|
151
|
+
playwright-ci add
|
|
304
152
|
```
|
|
305
153
|
|
|
306
|
-
|
|
154
|
+
新增或修改时会问这些内容:
|
|
307
155
|
|
|
308
156
|
- 项目名
|
|
309
157
|
- 用例库 Git 地址
|
|
310
158
|
- 分支
|
|
311
|
-
-
|
|
159
|
+
- 本地缓存目录
|
|
312
160
|
- 项目说明
|
|
313
161
|
- Playwright 参数
|
|
314
162
|
- 环境变量
|
|
315
163
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
```bash
|
|
319
|
-
GIT_USERNAME=你的用户名 GIT_PASSWORD='你的密码或访问令牌' npm run project:test -- 项目名
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
如果只想跑 runner 内置的临时本地用例,不拉远程用例库:
|
|
323
|
-
|
|
324
|
-
```bash
|
|
325
|
-
npm run doctor:test:local
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
## 默认测试环境
|
|
329
|
-
|
|
330
|
-
医生端测试环境命令默认使用:
|
|
164
|
+
配置文件就是当前目录的:
|
|
331
165
|
|
|
332
166
|
```text
|
|
333
|
-
|
|
334
|
-
DOCTOR_PUBLIC_PATH=/physician-admin/
|
|
335
|
-
DOCTOR_LOGIN_PATH=/physician-admin/login
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
也就是说,基础用例会访问:
|
|
339
|
-
|
|
340
|
-
```text
|
|
341
|
-
https://ys-test.heyejk.com/physician-admin/login
|
|
342
|
-
https://ys-test.heyejk.com/physician-admin/home
|
|
343
|
-
https://ys-test.heyejk.com/physician-admin/serviceAgreement
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
如果要临时切换环境,可以在命令前覆盖变量:
|
|
347
|
-
|
|
348
|
-
```bash
|
|
349
|
-
DOCTOR_BASE_URL=https://your-test-host npm run doctor:test
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
如果路径也不同,可以一起覆盖:
|
|
353
|
-
|
|
354
|
-
```bash
|
|
355
|
-
DOCTOR_BASE_URL=https://your-test-host \
|
|
356
|
-
DOCTOR_PUBLIC_PATH=/physician-admin/ \
|
|
357
|
-
DOCTOR_LOGIN_PATH=/physician-admin/login \
|
|
358
|
-
npm run doctor:test
|
|
167
|
+
projects.config.json
|
|
359
168
|
```
|
|
360
169
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
文件位置:
|
|
170
|
+
用例库缓存目录是:
|
|
364
171
|
|
|
365
172
|
```text
|
|
366
|
-
|
|
173
|
+
.case-repos/
|
|
367
174
|
```
|
|
368
175
|
|
|
369
|
-
|
|
176
|
+
`.case-repos/` 只是本地缓存,不需要提交。
|
|
370
177
|
|
|
371
|
-
|
|
372
|
-
| --- | --- | --- |
|
|
373
|
-
| `PW-TEST-001` | 登录页控件渲染:手机号、图形验证码、短信验证码、协议、登录按钮 | 直接访问登录路由 |
|
|
374
|
-
| `PW-TEST-002` | 未登录访问 `/home` 会被路由守卫跳到 `/login` | 直接访问受保护路由 |
|
|
375
|
-
| `PW-TEST-003` | `/serviceAgreement` 在白名单内,未登录可访问 | 直接访问白名单路由 |
|
|
376
|
-
| `PW-TEST-004` | 登录表单校验:必填、手机号格式、未勾协议提示 | 真实输入和点击 |
|
|
178
|
+
## 看结果
|
|
377
179
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
## 如何看结果
|
|
381
|
-
|
|
382
|
-
命令行里看到类似结果表示通过:
|
|
180
|
+
命令行看到类似结果表示通过:
|
|
383
181
|
|
|
384
182
|
```text
|
|
385
183
|
4 passed
|
|
386
184
|
```
|
|
387
185
|
|
|
388
|
-
|
|
186
|
+
失败时会生成:
|
|
389
187
|
|
|
390
188
|
```text
|
|
189
|
+
test-results/summary.md
|
|
391
190
|
test-results/
|
|
392
191
|
playwright-report/
|
|
393
192
|
```
|
|
394
193
|
|
|
395
|
-
|
|
194
|
+
其中 `test-results/summary.md` 是 Markdown 摘要,适合直接发给别人看。
|
|
195
|
+
|
|
196
|
+
在源码仓库里可以打开报告:
|
|
396
197
|
|
|
397
198
|
```bash
|
|
398
199
|
npm run report
|
|
399
200
|
```
|
|
400
201
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
- 失败截图
|
|
404
|
-
- 操作视频
|
|
405
|
-
- trace 回放
|
|
406
|
-
- 报错堆栈
|
|
407
|
-
- 失败前后的页面状态
|
|
408
|
-
|
|
409
|
-
trace 可以帮助判断到底是页面没出来、定位器没找到、跳转不符合预期,还是网络/环境问题。
|
|
410
|
-
|
|
411
|
-
## 第一次运行常见问题
|
|
412
|
-
|
|
413
|
-
### 缺少 Chromium
|
|
414
|
-
|
|
415
|
-
报错类似:
|
|
416
|
-
|
|
417
|
-
```text
|
|
418
|
-
Executable doesn't exist
|
|
419
|
-
Please run the following command to download new browsers:
|
|
420
|
-
npx playwright install
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
处理:
|
|
202
|
+
通过 npm 安装使用时,可以在运行目录打开报告:
|
|
424
203
|
|
|
425
204
|
```bash
|
|
426
|
-
npx playwright
|
|
205
|
+
npx playwright show-report playwright-report
|
|
427
206
|
```
|
|
428
207
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
如果测试偶发超时,先用可视化模式看页面是否真的打开:
|
|
208
|
+
## 常见问题
|
|
432
209
|
|
|
433
|
-
|
|
434
|
-
npm run doctor:test:headed
|
|
435
|
-
```
|
|
210
|
+
### 提示缺少浏览器
|
|
436
211
|
|
|
437
|
-
|
|
212
|
+
执行:
|
|
438
213
|
|
|
439
214
|
```bash
|
|
440
|
-
|
|
215
|
+
playwright-ci install chromium
|
|
441
216
|
```
|
|
442
217
|
|
|
443
|
-
###
|
|
218
|
+
### 拉取用例库失败
|
|
444
219
|
|
|
445
|
-
|
|
220
|
+
先确认当前机器能访问内部 Git。需要账号密码时这样跑:
|
|
446
221
|
|
|
447
|
-
```
|
|
448
|
-
|
|
222
|
+
```bash
|
|
223
|
+
GIT_USERNAME=你的用户名 GIT_PASSWORD='你的密码或访问令牌' playwright-ci run doctor-pc
|
|
449
224
|
```
|
|
450
225
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
医生端测试环境基础用例优先放在:
|
|
226
|
+
### 当前目录缺少 projects.config.json
|
|
454
227
|
|
|
455
|
-
|
|
456
|
-
cases/doctor-pc/test-env/
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
建议命名:
|
|
228
|
+
先执行:
|
|
460
229
|
|
|
461
|
-
```
|
|
462
|
-
|
|
230
|
+
```bash
|
|
231
|
+
playwright-ci init
|
|
463
232
|
```
|
|
464
233
|
|
|
465
|
-
|
|
234
|
+
### 本地用例库有未提交改动
|
|
466
235
|
|
|
467
|
-
|
|
468
|
-
login-form-validation.spec.ts
|
|
469
|
-
home-route-guard.spec.ts
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
每条用例建议补充 `annotateCase`,方便报告里追踪用例 ID:
|
|
236
|
+
Runner 会停止自动 `pull`,避免覆盖你正在调试的用例。处理 `.case-repos/项目名` 里的 Git 状态后再运行。
|
|
473
237
|
|
|
474
|
-
|
|
475
|
-
annotateCase(testInfo, {
|
|
476
|
-
caseId: 'PW-TEST-005',
|
|
477
|
-
evidenceId: 'TEST-some-business-evidence',
|
|
478
|
-
priority: 'Smoke'
|
|
479
|
-
});
|
|
480
|
-
```
|
|
238
|
+
## 在源码仓库里开发
|
|
481
239
|
|
|
482
|
-
|
|
240
|
+
如果你是维护这个 runner 本身:
|
|
483
241
|
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
242
|
+
```bash
|
|
243
|
+
git clone https://git.int.ybm100.com/ykq/playwright-ci
|
|
244
|
+
cd playwright-ci
|
|
245
|
+
npm install
|
|
246
|
+
npx playwright install chromium
|
|
488
247
|
```
|
|
489
248
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
## 登录后用例怎么做
|
|
493
|
-
|
|
494
|
-
当前基础用例不需要登录。后续要测登录后页面,比如首页、问诊列表、处方 tab、开方、撤回处方,需要先满足至少一种方案:
|
|
495
|
-
|
|
496
|
-
- 后端提供测试登录接口。
|
|
497
|
-
- 测试环境提供固定验证码或验证码绕过能力。
|
|
498
|
-
- 手工生成 `playwright/.auth/doctor.json` 登录态。
|
|
499
|
-
|
|
500
|
-
手工登录态方式参考:
|
|
249
|
+
运行内置医生端命令:
|
|
501
250
|
|
|
502
251
|
```bash
|
|
503
|
-
|
|
252
|
+
npm run doctor:test
|
|
253
|
+
npm run doctor:test:headed
|
|
254
|
+
npm run doctor:test:debug
|
|
504
255
|
```
|
|
505
256
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
注意:`playwright/.auth/*.json` 包含登录态,已被 `.gitignore` 排除,不要提交。
|
|
509
|
-
|
|
510
|
-
## CI 或服务器运行
|
|
511
|
-
|
|
512
|
-
服务器不需要显示器,可以直接跑无头模式:
|
|
257
|
+
检查代码:
|
|
513
258
|
|
|
514
259
|
```bash
|
|
515
|
-
npm
|
|
516
|
-
|
|
517
|
-
npm run doctor:test
|
|
260
|
+
npm run lint
|
|
261
|
+
npm run typecheck
|
|
518
262
|
```
|
|
519
263
|
|
|
520
|
-
|
|
264
|
+
## 当前基础用例
|
|
521
265
|
|
|
522
|
-
|
|
523
|
-
playwright-report/
|
|
524
|
-
test-results/
|
|
525
|
-
```
|
|
266
|
+
医生端测试环境基础用例包括:
|
|
526
267
|
|
|
527
|
-
|
|
268
|
+
| 用例 | 验证内容 |
|
|
269
|
+
| --- | --- |
|
|
270
|
+
| `PW-TEST-001` | 登录页控件是否渲染 |
|
|
271
|
+
| `PW-TEST-002` | 未登录访问 `/home` 是否跳回登录页 |
|
|
272
|
+
| `PW-TEST-003` | `/serviceAgreement` 是否允许未登录访问 |
|
|
273
|
+
| `PW-TEST-004` | 登录表单基础校验 |
|
|
528
274
|
|
|
529
|
-
|
|
530
|
-
playwright/.auth/*.json
|
|
531
|
-
.env
|
|
532
|
-
```
|
|
275
|
+
## 安全注意
|
|
533
276
|
|
|
534
|
-
|
|
277
|
+
不要提交这些内容:
|
|
535
278
|
|
|
536
279
|
```text
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
src/mocks/ # route / websocket mock
|
|
543
|
-
docs/ # 运行、登录、测试数据、选择器和 CI 说明
|
|
544
|
-
scripts/ # 命令封装脚本
|
|
280
|
+
.env
|
|
281
|
+
playwright/.auth/*.json
|
|
282
|
+
账号密码
|
|
283
|
+
访问令牌
|
|
284
|
+
npm token
|
|
545
285
|
```
|
|
546
286
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
- 先用少量稳定 Smoke 验证页面和路由,再扩展登录后 P0 链路。
|
|
550
|
-
- 用例失败必须能留下截图、视频或 trace。
|
|
551
|
-
- 不在测试工程里硬编码真实账号密码。
|
|
552
|
-
- 不把 Playwright 依赖塞回医生端业务仓。
|
|
553
|
-
- 涉及处方、问诊、药品、自动关诊等业务链路时,要先确认测试数据和登录态方案。
|
|
287
|
+
登录后业务用例需要额外准备测试账号、验证码绕过方案,或 `playwright/.auth/doctor.json` 登录态文件。
|
package/bin/playwright-ci.mjs
CHANGED
|
@@ -3,14 +3,18 @@
|
|
|
3
3
|
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
5
|
import fs from 'node:fs';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
6
7
|
import path from 'node:path';
|
|
7
8
|
import { fileURLToPath } from 'node:url';
|
|
8
9
|
|
|
9
10
|
const packageRootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
10
12
|
const defaultConfigPath = path.join(packageRootDir, 'projects.config.json');
|
|
11
13
|
const userConfigPath = process.env.PROJECTS_CONFIG_PATH
|
|
12
14
|
? path.resolve(process.env.PROJECTS_CONFIG_PATH)
|
|
13
15
|
: path.resolve(process.cwd(), 'projects.config.json');
|
|
16
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(packageRootDir, 'package.json'), 'utf8'));
|
|
17
|
+
const playwrightCliPath = path.join(path.dirname(require.resolve('@playwright/test/package.json')), 'cli.js');
|
|
14
18
|
|
|
15
19
|
const configActions = new Set(['list', 'show', 'add', 'update', 'delete', 'remove']);
|
|
16
20
|
|
|
@@ -22,11 +26,13 @@ function printHelp() {
|
|
|
22
26
|
playwright-ci add [project]
|
|
23
27
|
playwright-ci update <project>
|
|
24
28
|
playwright-ci delete <project>
|
|
29
|
+
playwright-ci install [browser]
|
|
25
30
|
playwright-ci run <project> [playwright args]
|
|
26
31
|
playwright-ci <project> [playwright args]
|
|
27
32
|
|
|
28
33
|
Examples:
|
|
29
34
|
playwright-ci init
|
|
35
|
+
playwright-ci install chromium
|
|
30
36
|
playwright-ci run doctor-pc --headed
|
|
31
37
|
GIT_USERNAME=user GIT_PASSWORD=token playwright-ci run doctor-pc`);
|
|
32
38
|
}
|
|
@@ -69,6 +75,23 @@ function runNodeScript(scriptName, args) {
|
|
|
69
75
|
});
|
|
70
76
|
}
|
|
71
77
|
|
|
78
|
+
function runPlaywright(args) {
|
|
79
|
+
const child = spawn(process.execPath, [playwrightCliPath, ...args], {
|
|
80
|
+
cwd: process.cwd(),
|
|
81
|
+
env: process.env,
|
|
82
|
+
stdio: 'inherit'
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
child.on('exit', (code, signal) => {
|
|
86
|
+
if (signal) {
|
|
87
|
+
process.kill(process.pid, signal);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
process.exit(code ?? 1);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
72
95
|
function main() {
|
|
73
96
|
const [command = 'help', ...args] = process.argv.slice(2);
|
|
74
97
|
|
|
@@ -77,11 +100,21 @@ function main() {
|
|
|
77
100
|
return;
|
|
78
101
|
}
|
|
79
102
|
|
|
103
|
+
if (command === 'version' || command === '--version' || command === '-v') {
|
|
104
|
+
console.log(packageJson.version);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
80
108
|
if (command === 'init') {
|
|
81
109
|
initConfig();
|
|
82
110
|
return;
|
|
83
111
|
}
|
|
84
112
|
|
|
113
|
+
if (command === 'install') {
|
|
114
|
+
runPlaywright(['install', ...(args.length ? args : ['chromium'])]);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
85
118
|
if (command === 'run' || command === 'project:test') {
|
|
86
119
|
ensureConfigExists();
|
|
87
120
|
runNodeScript('run-project.mjs', args);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ykq-playwright-ci",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Company-level Playwright E2E test project for doctor PC workflows.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"playwright-ci": "bin/playwright-ci.mjs"
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
".nvmrc",
|
|
17
17
|
"eslint.config.mjs",
|
|
18
18
|
"package.json",
|
|
19
|
+
"playwright.config.mjs",
|
|
19
20
|
"playwright.config.ts",
|
|
20
21
|
"projects.config.json",
|
|
21
22
|
"README.md",
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/* global process */
|
|
2
|
+
|
|
3
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
4
|
+
import dotenv from 'dotenv';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
|
|
7
|
+
dotenv.config();
|
|
8
|
+
|
|
9
|
+
const resultsDir = process.env.PLAYWRIGHT_CI_RESULTS_DIR || 'test-results';
|
|
10
|
+
const reportDir = process.env.PLAYWRIGHT_CI_REPORT_DIR || 'playwright-report';
|
|
11
|
+
const testDir = process.env.PLAYWRIGHT_CI_TEST_DIR || '.';
|
|
12
|
+
|
|
13
|
+
function optional(name) {
|
|
14
|
+
const value = process.env[name]?.trim();
|
|
15
|
+
return value ? value : undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function trimTrailingSlash(value) {
|
|
19
|
+
return value?.replace(/\/+$/, '');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default defineConfig({
|
|
23
|
+
testDir,
|
|
24
|
+
testMatch: ['**/*.spec.ts', '**/*.setup.ts'],
|
|
25
|
+
outputDir: resultsDir,
|
|
26
|
+
timeout: 60_000,
|
|
27
|
+
expect: {
|
|
28
|
+
timeout: 10_000
|
|
29
|
+
},
|
|
30
|
+
fullyParallel: false,
|
|
31
|
+
forbidOnly: Boolean(process.env.CI),
|
|
32
|
+
retries: process.env.CI ? 2 : 0,
|
|
33
|
+
workers: process.env.CI ? 2 : undefined,
|
|
34
|
+
reporter: [
|
|
35
|
+
['list'],
|
|
36
|
+
['html', { outputFolder: reportDir, open: 'never' }],
|
|
37
|
+
['junit', { outputFile: path.join(resultsDir, 'junit.xml') }],
|
|
38
|
+
['json', { outputFile: path.join(resultsDir, 'results.json') }]
|
|
39
|
+
],
|
|
40
|
+
use: {
|
|
41
|
+
baseURL: trimTrailingSlash(optional('DOCTOR_BASE_URL')),
|
|
42
|
+
actionTimeout: 15_000,
|
|
43
|
+
navigationTimeout: 30_000,
|
|
44
|
+
trace: 'retain-on-failure',
|
|
45
|
+
screenshot: 'only-on-failure',
|
|
46
|
+
video: 'retain-on-failure'
|
|
47
|
+
},
|
|
48
|
+
projects: [
|
|
49
|
+
{
|
|
50
|
+
name: 'setup',
|
|
51
|
+
testMatch: /.*\.setup\.ts/
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'chromium',
|
|
55
|
+
dependencies: ['setup'],
|
|
56
|
+
testIgnore: /.*\.setup\.ts/,
|
|
57
|
+
use: {
|
|
58
|
+
...devices['Desktop Chrome']
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
});
|
package/playwright.config.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { defineConfig, devices } from '@playwright/test';
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
import { appEnv } from './src/utils/env';
|
|
3
4
|
|
|
5
|
+
const resultsDir = process.env.PLAYWRIGHT_CI_RESULTS_DIR || 'test-results';
|
|
6
|
+
const reportDir = process.env.PLAYWRIGHT_CI_REPORT_DIR || 'playwright-report';
|
|
7
|
+
const testDir = process.env.PLAYWRIGHT_CI_TEST_DIR || '.';
|
|
8
|
+
|
|
4
9
|
export default defineConfig({
|
|
5
|
-
testDir
|
|
6
|
-
testMatch: ['
|
|
7
|
-
outputDir:
|
|
10
|
+
testDir,
|
|
11
|
+
testMatch: ['**/*.spec.ts', '**/*.setup.ts'],
|
|
12
|
+
outputDir: resultsDir,
|
|
8
13
|
timeout: 60_000,
|
|
9
14
|
expect: {
|
|
10
15
|
timeout: 10_000
|
|
@@ -15,9 +20,9 @@ export default defineConfig({
|
|
|
15
20
|
workers: process.env.CI ? 2 : undefined,
|
|
16
21
|
reporter: [
|
|
17
22
|
['list'],
|
|
18
|
-
['html', { outputFolder:
|
|
19
|
-
['junit', { outputFile: '
|
|
20
|
-
['json', { outputFile: '
|
|
23
|
+
['html', { outputFolder: reportDir, open: 'never' }],
|
|
24
|
+
['junit', { outputFile: path.join(resultsDir, 'junit.xml') }],
|
|
25
|
+
['json', { outputFile: path.join(resultsDir, 'results.json') }]
|
|
21
26
|
],
|
|
22
27
|
use: {
|
|
23
28
|
baseURL: appEnv.doctorBaseURL,
|
package/scripts/run-project.mjs
CHANGED
|
@@ -13,9 +13,11 @@ const configPath = process.env.PROJECTS_CONFIG_PATH
|
|
|
13
13
|
? path.resolve(process.env.PROJECTS_CONFIG_PATH)
|
|
14
14
|
: path.join(rootDir, 'projects.config.json');
|
|
15
15
|
const workspaceDir = process.env.PROJECTS_CONFIG_PATH ? path.dirname(configPath) : rootDir;
|
|
16
|
-
const playwrightConfigPath = path.join(rootDir, 'playwright.config.
|
|
16
|
+
const playwrightConfigPath = path.join(rootDir, 'playwright.config.mjs');
|
|
17
17
|
const playwrightCliPath = path.join(path.dirname(require.resolve('@playwright/test/package.json')), 'cli.js');
|
|
18
18
|
const knownModeArgs = new Set(['--headed', '--debug', '--ui']);
|
|
19
|
+
const resultsDir = process.env.PLAYWRIGHT_CI_RESULTS_DIR || path.join(workspaceDir, 'test-results');
|
|
20
|
+
const reportDir = process.env.PLAYWRIGHT_CI_REPORT_DIR || path.join(workspaceDir, 'playwright-report');
|
|
19
21
|
let gitAskpassPath = '';
|
|
20
22
|
|
|
21
23
|
function readConfig() {
|
|
@@ -201,6 +203,112 @@ function buildPlaywrightArgs(caseDir, project, passthroughArgs) {
|
|
|
201
203
|
return args;
|
|
202
204
|
}
|
|
203
205
|
|
|
206
|
+
function formatDuration(durationMs = 0) {
|
|
207
|
+
if (durationMs < 1000) {
|
|
208
|
+
return `${Math.round(durationMs)}ms`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return `${(durationMs / 1000).toFixed(1)}s`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function escapeMarkdown(value = '') {
|
|
215
|
+
return String(value).replace(/\|/g, '\\|').replace(/\r?\n/g, ' ');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function collectSpecs(suites = [], rows = []) {
|
|
219
|
+
for (const suite of suites) {
|
|
220
|
+
for (const spec of suite.specs || []) {
|
|
221
|
+
for (const test of spec.tests || []) {
|
|
222
|
+
const result = test.results?.[test.results.length - 1] || {};
|
|
223
|
+
const annotations = [...(test.annotations || []), ...(result.annotations || [])];
|
|
224
|
+
const caseId = annotations.find((item) => item.type === 'case-id')?.description || '-';
|
|
225
|
+
const priority = annotations.find((item) => item.type === 'priority')?.description || '-';
|
|
226
|
+
const error = result.errors?.[0]?.message || '';
|
|
227
|
+
|
|
228
|
+
rows.push({
|
|
229
|
+
status: result.status || test.status || (spec.ok ? 'passed' : 'failed'),
|
|
230
|
+
caseId,
|
|
231
|
+
priority,
|
|
232
|
+
title: spec.title,
|
|
233
|
+
projectName: test.projectName || '-',
|
|
234
|
+
duration: result.duration || 0,
|
|
235
|
+
file: spec.file || suite.file || '-',
|
|
236
|
+
line: spec.line || 0,
|
|
237
|
+
error
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
collectSpecs(suite.suites || [], rows);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return rows;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function statusText(status) {
|
|
249
|
+
if (status === 'passed') return '通过';
|
|
250
|
+
if (status === 'skipped') return '跳过';
|
|
251
|
+
if (status === 'timedOut') return '超时';
|
|
252
|
+
if (status === 'failed' || status === 'unexpected') return '失败';
|
|
253
|
+
return status || '-';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function writeMarkdownSummary(projectName, caseDir, exitCode) {
|
|
257
|
+
const jsonPath = path.join(resultsDir, 'results.json');
|
|
258
|
+
if (!fs.existsSync(jsonPath)) {
|
|
259
|
+
console.warn(`未找到 Playwright JSON 结果,已跳过 Markdown 摘要:${jsonPath}`);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const result = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
|
264
|
+
const stats = result.stats || {};
|
|
265
|
+
const rows = collectSpecs(result.suites || []);
|
|
266
|
+
const summaryPath = path.join(resultsDir, 'summary.md');
|
|
267
|
+
const failedRows = rows.filter((row) => !['passed', 'skipped'].includes(row.status));
|
|
268
|
+
const status = exitCode === 0 && failedRows.length === 0 ? '通过' : '失败';
|
|
269
|
+
const reportPath = path.relative(resultsDir, path.join(reportDir, 'index.html')) || 'index.html';
|
|
270
|
+
|
|
271
|
+
const lines = [
|
|
272
|
+
'# Playwright 测试结果',
|
|
273
|
+
'',
|
|
274
|
+
`- 项目:${projectName}`,
|
|
275
|
+
`- 状态:${status}`,
|
|
276
|
+
`- 开始时间:${stats.startTime || '-'}`,
|
|
277
|
+
`- 总耗时:${formatDuration(stats.duration || 0)}`,
|
|
278
|
+
`- 用例数:${rows.length}`,
|
|
279
|
+
`- 通过:${stats.expected ?? 0}`,
|
|
280
|
+
`- 失败:${stats.unexpected ?? 0}`,
|
|
281
|
+
`- 跳过:${stats.skipped ?? 0}`,
|
|
282
|
+
`- 不稳定:${stats.flaky ?? 0}`,
|
|
283
|
+
`- 用例目录:${path.relative(workspaceDir, caseDir) || caseDir}`,
|
|
284
|
+
`- HTML 报告:${reportPath}`,
|
|
285
|
+
'',
|
|
286
|
+
'## 用例明细',
|
|
287
|
+
'',
|
|
288
|
+
'| 状态 | 用例 ID | 优先级 | 用例 | 浏览器 | 耗时 | 文件 |',
|
|
289
|
+
'| --- | --- | --- | --- | --- | --- | --- |'
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
for (const row of rows) {
|
|
293
|
+
const file = row.line ? `${row.file}:${row.line}` : row.file;
|
|
294
|
+
lines.push(
|
|
295
|
+
`| ${statusText(row.status)} | ${escapeMarkdown(row.caseId)} | ${escapeMarkdown(row.priority)} | ${escapeMarkdown(row.title)} | ${escapeMarkdown(row.projectName)} | ${formatDuration(row.duration)} | ${escapeMarkdown(file)} |`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (failedRows.length) {
|
|
300
|
+
lines.push('', '## 失败信息', '');
|
|
301
|
+
for (const row of failedRows) {
|
|
302
|
+
lines.push(`### ${row.caseId} ${row.title}`, '');
|
|
303
|
+
lines.push(row.error ? `\`\`\`text\n${row.error.trim()}\n\`\`\`` : '无错误详情。', '');
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
fs.mkdirSync(resultsDir, { recursive: true });
|
|
308
|
+
fs.writeFileSync(summaryPath, `${lines.join('\n')}\n`);
|
|
309
|
+
console.log(`Markdown 测试摘要已生成:${summaryPath}`);
|
|
310
|
+
}
|
|
311
|
+
|
|
204
312
|
function main() {
|
|
205
313
|
const config = readConfig();
|
|
206
314
|
const { projectName, passthroughArgs, skipPull } = parseArgs(process.argv.slice(2));
|
|
@@ -227,7 +335,10 @@ function main() {
|
|
|
227
335
|
cwd: workspaceDir,
|
|
228
336
|
env: {
|
|
229
337
|
...(project.env || {}),
|
|
230
|
-
...process.env
|
|
338
|
+
...process.env,
|
|
339
|
+
PLAYWRIGHT_CI_RESULTS_DIR: resultsDir,
|
|
340
|
+
PLAYWRIGHT_CI_REPORT_DIR: reportDir,
|
|
341
|
+
PLAYWRIGHT_CI_TEST_DIR: workspaceDir
|
|
231
342
|
},
|
|
232
343
|
stdio: 'inherit'
|
|
233
344
|
}
|
|
@@ -239,7 +350,13 @@ function main() {
|
|
|
239
350
|
return;
|
|
240
351
|
}
|
|
241
352
|
|
|
242
|
-
|
|
353
|
+
const exitCode = code ?? 1;
|
|
354
|
+
try {
|
|
355
|
+
writeMarkdownSummary(projectName, caseDir, exitCode);
|
|
356
|
+
} catch (error) {
|
|
357
|
+
console.warn(`Markdown 测试摘要生成失败:${error instanceof Error ? error.message : error}`);
|
|
358
|
+
}
|
|
359
|
+
process.exit(exitCode);
|
|
243
360
|
});
|
|
244
361
|
}
|
|
245
362
|
|