helixlife-v5-cli 1.1.6 → 1.1.7
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/lib/cli.js
CHANGED
|
@@ -205,15 +205,8 @@ main.printHelp = printHelp;
|
|
|
205
205
|
main.runPlaywrightCliPassthrough = runPlaywrightCliPassthrough;
|
|
206
206
|
main.playwrightCliEntry = playwrightCliEntry;
|
|
207
207
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
(code) => process.exit(code),
|
|
211
|
-
(err) => {
|
|
212
|
-
console.error(err);
|
|
213
|
-
process.exit(2);
|
|
214
|
-
}
|
|
215
|
-
);
|
|
216
|
-
}
|
|
208
|
+
// 注意:`require.main === module` 入口守卫统一放在 helix-cli.js;
|
|
209
|
+
// 本文件仅导出,不在此处重复启动。
|
|
217
210
|
|
|
218
211
|
module.exports = {
|
|
219
212
|
main,
|
package/package.json
CHANGED
|
@@ -41,29 +41,9 @@
|
|
|
41
41
|
- **悬停与点击 ·「部分章节匹配」等说明标签**:搜索等列表卡片上常有一行灰色小字(典型文案「部分章节匹配」)。**分流规则**:
|
|
42
42
|
- **仅展示 / 阅读意图 → `hover`**:用户说「查看 / 看一下 / 想看 / 悬停 / 指到 … 上」等,且对象是该说明标签本身 → 使用 `helixlife-v5-cli hover …` 或 `run-code` 内 `locator(...).hover()`;勿用 `click` 代替。
|
|
43
43
|
- **明确激活 / 导航意图 → `click`**:用户说「进入 / 打开 / 点进去 / 点击」等 → `click` 应落在**卡片主点击区**(封面、标题等);若用户点名要「点击部分章节匹配」才把 `click` 落在该文案上。
|
|
44
|
-
- **定位与多命中**:推荐 `page.locator('main').getByText('部分章节匹配', { exact: true })`;多卡并列时**必须先缩小到目标课程卡片**,避免 strict
|
|
45
|
-
- 自测(2026-05-11):`/edu/search?keywords=…` 课程结果首条 `hover` 后,可见 tooltip 浮层(`helixlife-v5-cli run-code --filename=scripts/hover-partial-chapter-match.js` 返回 `
|
|
44
|
+
- **定位与多命中**:推荐 `page.locator('main').getByText('部分章节匹配', { exact: true })`;多卡并列时**必须先缩小到目标课程卡片**,避免 strict 或误触(`scripts/hover-partial-chapter-match.js` 的 `cardTitle` 参数可直接用于此场景)。
|
|
45
|
+
- 自测(2026-05-11):`/edu/search?keywords=…` 课程结果首条 `hover` 后,可见 tooltip 浮层(`helixlife-v5-cli run-code --filename=scripts/hover-partial-chapter-match.js` 返回 `ok: true`,即 `tooltipLikeAfter > tooltipLikeBefore`)。
|
|
46
46
|
- **数据分析 · 详情页 · 上传文件**:用户说「上传文件 / 上传数据 / 换数据文件 / 导入 csv xlsx」等,且 `pathname` 为 `/analysis/{toolId}` 时,**一律走 D-2 三步弹窗流程**(打开弹窗 → 弹窗内选文件 → 弹窗内「确认」),**禁止**把左侧参数卡片底部「确认」(D-6 生成结果)当成上传确认。细则见下文「上传文件 · Agent 硬门禁」。
|
|
47
|
-
- **AI 应用 · 详情页 · 复制文本 / 编辑文本(须先 hover)**:用户说「复制文本 / 复制这段 / 复制这一块 / 编辑文本 / 编辑生成的结果 / 修改生成内容 / 改一下输出」等,且 `pathname` 为 `/application/{appId}`、右侧「生成结果」区已有输出块时,**必须先 `hover` 目标输出块**,再 `click` 该块浮动按钮 `getByLabel('复制文本')` 或 `getByLabel('编辑文本')`(见 AP-10 / AP-14、规约 C)。**禁止**在未 hover 时直接 `click` 这两个按钮(会 `visibility: hidden` 超时);**禁止**把输出区顶栏「全部复制」(AP-11,无需 hover)与单块「复制文本」混用;**禁止**在编辑模式下直接向 markdown 预览区 `type`/`fill`——须先点「编辑文本」进入编辑态(出现 `textbox` + 「保存」)再改内容。
|
|
48
|
-
|
|
49
|
-
#### AI 应用 · 详情页 · 复制/编辑输出 · Agent 硬门禁
|
|
50
|
-
|
|
51
|
-
**适用范围**:`pathname` 为 `/application/{appId}`;右侧「生成结果」区已有至少一块 AI 输出(markdown / 文本块等);用户要求复制或编辑**某一块**生成内容。
|
|
52
|
-
|
|
53
|
-
- **悬停前置(必做)**:「复制文本」「编辑文本」按钮默认隐藏,仅当鼠标悬停在**该输出块**(块标题或块正文区域)上时才显示(规约 C)。Agent **不得**跳过 hover 直接 `click`;也不得因 `snapshot` 未列出这两个按钮而判定「页面无编辑/复制能力」——应先 `hover` 目标块后再 `snapshot` 或 `click`。
|
|
54
|
-
- **用户说法 → 动作**:
|
|
55
|
-
- 「复制文本 / 复制这段 / 复制这一块 / 复制输出」→ **AP-10**(先 hover 目标块 → `getByLabel('复制文本')`)。
|
|
56
|
-
- 「编辑文本 / 编辑生成的结果 / 修改生成内容 / 改一下输出 / 我想编辑结果」→ **AP-14**(先 hover 目标块 → `getByLabel('编辑文本')` → 在 `textbox` 内修改 → `getByRole('button', { name: '保存' })`)。
|
|
57
|
-
- 「全部复制 / 复制全部 / 复制整份输出」→ **AP-11**(输出区顶栏 `getByLabel('全部复制')`,**无需** hover 某块)。
|
|
58
|
-
- **命令衔接**:hover 与 click 须在**同一次** `run-code`(或等价脚本)内连续执行;分两条 CLI 命令时 hover 态会丢失,导致 click 超时。推荐:
|
|
59
|
-
|
|
60
|
-
```js
|
|
61
|
-
await page.getByText('<输出块标题>', { exact: true }).hover();
|
|
62
|
-
await page.getByLabel('编辑文本').click(); // 或 '复制文本'
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
- **编辑态验收**:点击「编辑文本」后,该块内出现可编辑 `textbox` 与「保存」按钮;修改完成后须点「保存」,再用 `snapshot` 确认回到 markdown 预览态。取消编辑可点「保存」旁的关闭图标(以页面实测为准)。
|
|
66
|
-
- **禁止误导**:不得向用户声称「已进入编辑模式」或「已复制」而未执行 hover → click 或未通过上述验收。
|
|
67
47
|
|
|
68
48
|
#### 任务范围与跨域
|
|
69
49
|
|
|
@@ -175,9 +155,8 @@
|
|
|
175
155
|
| 对话 · 新建会话 | S-09 |
|
|
176
156
|
| 工作站 · 新建任务弹窗 | S-10 |
|
|
177
157
|
| 工作台 · 进入子工作台 | S-11 |
|
|
178
|
-
| 应用 · 标准操作(搜索 / 级联分类 / 滚动加载 / 收藏 / 进入详情 / 多块复制 / mindmap) | S-12(细则 AP-1~AP-
|
|
158
|
+
| 应用 · 标准操作(搜索 / 级联分类 / 滚动加载 / 收藏 / 进入详情 / 多块复制 / mindmap) | S-12(细则 AP-1~AP-13 + 规约 A~H) |
|
|
179
159
|
| 应用 · 取消收藏 · 二次确认弹窗 | AP-8(规约 E) |
|
|
180
|
-
| 应用 · 详情 · 单块复制文本 / 编辑文本(须先 hover 输出块) | AP-10 / AP-14(规约 C · 硬门禁见上文) |
|
|
181
160
|
| 应用 · 多块输出 · 单块复制 vs 全部复制 | AP-10 / AP-11(规约 F) |
|
|
182
161
|
| 应用 · mindmap 缩放 vs 页面滚动 | AP-12 / AP-13(规约 G) |
|
|
183
162
|
| 应用 · 级联分类(必选到叶子级) | AP-3(规约 H) |
|
|
@@ -398,14 +377,12 @@ div.side-menu--item[to='/edu/courses']
|
|
|
398
377
|
|
|
399
378
|
**规约 C · 悬停才显示的隐藏操作按钮**
|
|
400
379
|
|
|
401
|
-
|
|
380
|
+
列表项/输入输出块上部分操作按钮(复制、收藏、删除、重新生成、举报等)默认隐藏,仅在 `hover` 时出现:
|
|
402
381
|
|
|
403
|
-
1. 先 `hover`
|
|
382
|
+
1. 先 `hover` 目标项目块;
|
|
404
383
|
2. 再 `click` 出现的按钮(按规约 A 优先 `aria-label`)。
|
|
405
384
|
|
|
406
385
|
> 直接对未 hover 的按钮 `click`:常因 `visibility: hidden` / `pointer-events: none` 超时或被遮挡;勿用 `force: true` 绕过业务态。
|
|
407
|
-
>
|
|
408
|
-
> **应用详情 · 高频踩坑**:「复制文本」「编辑文本」的 `aria-label` 即按钮文案本身;未 hover 时 `snapshot` 往往**看不到**这两个按钮,Agent 易误判「页面不支持编辑」。用户说「编辑生成的结果」时,默认走 **AP-14**(先 hover 再点「编辑文本」),细则见上文「AI 应用 · 详情页 · 复制/编辑输出 · Agent 硬门禁」。
|
|
409
386
|
|
|
410
387
|
**规约 D · 卡片以「新标签页」方式打开详情**
|
|
411
388
|
|
|
@@ -431,9 +408,9 @@ div.side-menu--item[to='/edu/courses']
|
|
|
431
408
|
|
|
432
409
|
应用的输入/输出区可能由多个独立内容块组成(多段提示、分章节回答、多张图、多段代码/表格/mindmap 等):
|
|
433
410
|
|
|
434
|
-
-
|
|
411
|
+
- **单块操作**(单段「复制」、「重新生成」、「评分」):必须先 `hover` 目标块内部才会出现该块浮动按钮(规约 C),再使用 `aria-label`(规约 A)定位**该块作用域内**的按钮;勿直接命中全局同名按钮。
|
|
435
412
|
- **整块操作**(「全部复制」、「导出全部」、「全部重新生成」):通常位于输出区顶/底全局工具条,不需要悬停某块;用 `getByLabel('全部复制')` 或 `getByRole('button', { name: '全部复制' })` 直接命中。
|
|
436
|
-
-
|
|
413
|
+
- **分流原则**:根据用户原话——「复制这段/这一块/第 N 段」→ **单块**(先 `hover`);「全部复制/复制全部/复制整份输出」→ **整块**。`全部复制` 与 `复制` 在 `aria-label` 上严格区分,勿混用。
|
|
437
414
|
|
|
438
415
|
**规约 G · mindmap 缩放与页面滚动(鼠标位置决定)**
|
|
439
416
|
|
|
@@ -467,11 +444,10 @@ div.side-menu--item[to='/edu/courses']
|
|
|
467
444
|
| AP-7 | **收藏(单击直成)** | 先 `hover` 卡片(规约 C)→ `click` → `getByLabel('收藏')` | 文案/`aria-label` 切换为「已收藏」;无二次确认 |
|
|
468
445
|
| AP-8 | **取消收藏(需二次确认)** | 见规约 E:`hover` → `click`「取消收藏」 → `click` 弹层「确认」 | 验收文案回到「收藏」,或卡片移出「我的收藏」;勿用 `dialog-accept` |
|
|
469
446
|
| AP-9 | **多块输入 · 单块操作** | 先 `hover` 目标输入块 → 在该块作用域内用 `getByLabel('<操作>')` → `click` | 同规约 F;勿误触全局/其它块按钮 |
|
|
470
|
-
| AP-10 | **多块输出 ·
|
|
471
|
-
| AP-11 | **多块输出 · 全部复制(整块)** | `click` → `getByLabel('全部复制')` 或 `getByRole('button', { name: '全部复制' })` |
|
|
447
|
+
| AP-10 | **多块输出 · 单块复制** | 先 `hover` 目标输出块 → 在该块作用域内 `click` → `getByLabel('复制')` | 出现复制成功 toast 或剪贴板更新 |
|
|
448
|
+
| AP-11 | **多块输出 · 全部复制(整块)** | `click` → `getByLabel('全部复制')` 或 `getByRole('button', { name: '全部复制' })` | 同上提示;勿用「复制」代替 |
|
|
472
449
|
| AP-12 | **mindmap · 缩放** | 见规约 G:`mousemove` 到画布内 → `mousewheel` | 节点尺寸变化 |
|
|
473
450
|
| AP-13 | **mindmap 区域附近 · 滚动页面** | 见规约 G:`mousemove` 到 mindmap 外的安全空白区(仍在外层容器内) → `mousewheel` | 页面/外层容器滚动,mindmap 大小不变 |
|
|
474
|
-
| AP-14 | **多块输出 · 单块编辑文本** | 先 `hover` 目标输出块 → **同一次** `run-code` 内 `click` → `getByLabel('编辑文本')` → 在块内 `textbox` 修改 → `click` → `getByRole('button', { name: '保存' })` | 回到 markdown 预览;用户说「编辑生成的结果」等走本步;取消可点「保存」旁关闭图标 |
|
|
475
451
|
|
|
476
452
|
##### AI 应用 · 稳定定位器速查
|
|
477
453
|
|
|
@@ -485,9 +461,8 @@ div.side-menu--item[to='/edu/courses']
|
|
|
485
461
|
| 收藏 / 已收藏 | `getByLabel('收藏')` / `getByLabel('已收藏')`(兼用 `getByText('收藏', { exact: true })`) | 取消收藏须见规约 E |
|
|
486
462
|
| 取消收藏 | `getByLabel('取消收藏')` | 触发二次确认 |
|
|
487
463
|
| 取消收藏 · 确认 | 弹层 `getByRole('button', { name: '确认' })`(文案以实测为准) | 必接动作;禁用 `dialog-accept` |
|
|
488
|
-
|
|
|
489
|
-
|
|
|
490
|
-
| 整块复制 | `getByLabel('全部复制')` | 勿与「复制文本」混用;无需 hover |
|
|
464
|
+
| 单块复制 | 目标块作用域内 `getByLabel('复制')` | 先 `hover` 该块 |
|
|
465
|
+
| 整块复制 | `getByLabel('全部复制')` | 勿与「复制」混用 |
|
|
491
466
|
| 重新生成(单块) | 目标块作用域内 `getByLabel('重新生成')` | 先 `hover` 该块 |
|
|
492
467
|
| 分类入口 | `getByLabel('分类')` 优先;否则 `getByRole('button', { name: '分类' })` | 级联见规约 H |
|
|
493
468
|
| 应用卡片 · 标题 | `getByText('<应用主标题>', { exact: true })` | 多卡时先缩到目标卡片 |
|
|
@@ -532,7 +507,7 @@ div.side-menu--item[to='/edu/courses']
|
|
|
532
507
|
| --- | --- |
|
|
533
508
|
| 参数行 | 每行左侧为参数名(如 `数据矩阵`、`样本信息`),右侧为上传入口 + 当前文件名 + 「下载示例数据」 |
|
|
534
509
|
| **步骤 1 · 打开弹窗** | 点该行 **`aria-label="上传文件 - 打开上传弹窗"`** 的控件(规约 A)。多行并存时**必须先按参数名缩到单行**,再点该行上传入口 |
|
|
535
|
-
| **步骤 2 · 弹窗内选文件** | 出现 `getByRole('dialog', { name: '上传文件' })`
|
|
510
|
+
| **步骤 2 · 弹窗内选文件** | 出现 `getByRole('dialog', { name: '上传文件' })` 后,**任选其一**:**(A) filechooser 方式**:在**弹窗内**点 `button`,文案匹配 `/将文件拖到此处\|点击上传/`,配合 `filechooser.setFiles(<本地绝对路径>)`;**(B) setInputFiles 方式**(更简洁,推荐多文件脚本使用):直接 `dlg.locator('input[type="file"]').setInputFiles(<本地绝对路径>)`,无需等待 filechooser 事件 |
|
|
536
511
|
| **步骤 3 · 弹窗内确认** | 仍在**同一弹窗**内 `click` → `getByRole('button', { name: '确认' })`;弹窗关闭后,对应参数行文件名更新 |
|
|
537
512
|
|
|
538
513
|
**两个「确认」勿混淆**
|
|
@@ -560,9 +535,11 @@ div.side-menu--item[to='/edu/courses']
|
|
|
560
535
|
|
|
561
536
|
3. **弹窗**:`page.getByRole('dialog', { name: '上传文件' })`(须 `waitFor({ state: 'visible' })`)
|
|
562
537
|
|
|
563
|
-
4.
|
|
538
|
+
4. **弹窗内选文件(A · filechooser)**:`dlg.getByRole('button', { name: /将文件拖到此处\|点击上传/ })`,配合 `page.waitForEvent('filechooser')` → `chooser.setFiles(<路径>)`
|
|
539
|
+
|
|
540
|
+
5. **弹窗内选文件(B · setInputFiles,更简洁)**:`dlg.locator('input[type="file"]').setInputFiles(<路径>)`(无需 filechooser 事件;`scripts/analysis-detail-upload-both.js` 采用此方式)
|
|
564
541
|
|
|
565
|
-
|
|
542
|
+
6. **弹窗内确认**:`dlg.getByRole('button', { name: '确认' })`
|
|
566
543
|
|
|
567
544
|
**`helixlife-v5-cli` 逐步命令示例**(单文件 · 参数名 `数据矩阵` · 文件路径按任务替换):
|
|
568
545
|
|
|
@@ -584,7 +561,7 @@ helixlife-v5-cli run-code --filename=scripts/analysis-detail-upload-file.js
|
|
|
584
561
|
- **禁止**用整页 `getByLabel('上传文件')` 当唯一定位器——双文件工具会 strict 或多点;必须带参数名 `filter({ has: getByText('<参数名>') })`。
|
|
585
562
|
- **禁止**依赖某次 `snapshot` 的 `e*` ref 或肉眼猜 `img` 位置。
|
|
586
563
|
|
|
587
|
-
**多文件工具**(如差异分析:数据矩阵 + 样本信息):对每个参数**重复完整 3 步**;参考 `scripts/analysis-detail-upload-both.js
|
|
564
|
+
**多文件工具**(如差异分析:数据矩阵 + 样本信息):对每个参数**重复完整 3 步**;参考 `scripts/analysis-detail-upload-both.js`(采用 setInputFiles 方式顺序执行,勿并行多个 `filechooser`)。
|
|
588
565
|
|
|
589
566
|
**验收(对用户回复前必审)**
|
|
590
567
|
|
|
@@ -598,7 +575,7 @@ helixlife-v5-cli run-code --filename=scripts/analysis-detail-upload-file.js
|
|
|
598
575
|
| # | 用户语义 | 推荐操作 | 验收 / 备注 |
|
|
599
576
|
| --- | --- | --- | --- |
|
|
600
577
|
| D-1 | **数据参数 · 重置** | `click` → `locator('main').getByRole('button', { name: '重置参数' }).first()` | 数据参数区恢复默认;可能清除已上传文件名 |
|
|
601
|
-
| D-2 | **上传文件** | **三步弹窗**(细则见上文「上传文件 · Agent 硬门禁」):① `click` → 目标参数行 `getByLabel('上传文件 - 打开上传弹窗')`(兼容旧版:同行 `getByLabel('上传文件')`)→ ② 等待 `dialog "上传文件"` →
|
|
578
|
+
| D-2 | **上传文件** | **三步弹窗**(细则见上文「上传文件 · Agent 硬门禁」):① `click` → 目标参数行 `getByLabel('上传文件 - 打开上传弹窗')`(兼容旧版:同行 `getByLabel('上传文件')`)→ ② 等待 `dialog "上传文件"` → 弹窗内选文件:**A** `click` → `button` `/将文件拖到此处\|点击上传/` + `filechooser.setFiles(<路径>)`;或 **B**(更简洁)`dlg.locator('input[type="file"]').setInputFiles(<路径>)` → ③ 弹窗内 `click` → `button`「确认」。推荐 `run-code --filename=scripts/analysis-detail-upload-file.js`(单文件·A 方式)或 `analysis-detail-upload-both.js`(多参数·B 方式)。**反模式**:勿 `upload` 未开弹窗;勿点左侧底部「确认」代替弹窗「确认」 | 弹窗关闭;对应参数行展示新文件名;格式须符合工具模板 |
|
|
602
579
|
| D-3 | **数据校验(验证)** | 若 `main` 出现 `getByRole('button', { name: '验证' })`:`click` 一次;若已为 `验证成功` 可跳过 | `验证成功` → 可 D-6;`验证失败` → 调整文件后重试 D-2~D-3 |
|
|
603
580
|
| D-4 | **主要参数 · 重置** | `click` → `locator('main').getByRole('button', { name: '重置参数' }).nth(1)` | 主要参数折叠组恢复默认 |
|
|
604
581
|
| D-5 | **主要参数 · 保存** | `click` → `locator('main').getByRole('button', { name: '保存参数' })` | 页面级 toast `保存成功`;`pathname` 不变 |
|
|
@@ -618,7 +595,8 @@ helixlife-v5-cli run-code --filename=scripts/analysis-detail-upload-file.js
|
|
|
618
595
|
| 打开上传弹窗(首选) | `locator('main .u-filter-card').getByLabel('上传文件 - 打开上传弹窗')`;多参数时 `getByText('<参数名>', { exact: true }).locator('..').getByLabel('上传文件 - 打开上传弹窗')` |
|
|
619
596
|
| 打开上传弹窗(旧版兼容) | 同上结构,`.or(getByLabel('上传文件'))` 或直接用同行 `getByLabel('上传文件')` |
|
|
620
597
|
| 上传弹窗 | `getByRole('dialog', { name: '上传文件' })` |
|
|
621
|
-
| 弹窗 ·
|
|
598
|
+
| 弹窗 · 选文件(A) | `getByRole('dialog', { name: '上传文件' }).getByRole('button', { name: /将文件拖到此处\|点击上传/ })` + filechooser |
|
|
599
|
+
| 弹窗 · 选文件(B) | `getByRole('dialog', { name: '上传文件' }).locator('input[type="file"]').setInputFiles(<路径>)`(更简洁) |
|
|
622
600
|
| 弹窗 · 确认(上传落盘) | `getByRole('dialog', { name: '上传文件' }).getByRole('button', { name: '确认' })` |
|
|
623
601
|
| 左侧底部 · 确认(生成结果,非上传) | `locator('main .u-filter-card').getByRole('button', { name: '确认' })` |
|
|
624
602
|
| 手动验证 | `locator('main').getByRole('button', { name: '验证' })` |
|
|
@@ -630,8 +608,8 @@ helixlife-v5-cli run-code --filename=scripts/analysis-detail-upload-file.js
|
|
|
630
608
|
|
|
631
609
|
###### 可复用 `run-code`(可选)
|
|
632
610
|
|
|
633
|
-
- **单参数上传并尝试验证**:`helixlife-v5-cli run-code --filename=scripts/analysis-detail-upload-file.js
|
|
634
|
-
- **多参数顺序上传**(如差异分析):`helixlife-v5-cli run-code --filename=scripts/analysis-detail-upload-both.js
|
|
611
|
+
- **单参数上传并尝试验证**:`helixlife-v5-cli run-code --filename=scripts/analysis-detail-upload-file.js`(三步弹窗,采用 **filechooser** 方式;脚本内改 `filePath` 与可选 `paramLabel`;格式须符合工具模板,大小以页面上限为准)。
|
|
612
|
+
- **多参数顺序上传**(如差异分析):`helixlife-v5-cli run-code --filename=scripts/analysis-detail-upload-both.js`(采用 **setInputFiles** 方式,每个参数各走一遍打开弹窗 → 选文件 → 弹窗确认;无需 filechooser 事件)。
|
|
635
613
|
|
|
636
614
|
##### 数据分析 · 分类体系(主分类 vs 子分类 · Agent 必读)
|
|
637
615
|
|
|
@@ -1126,7 +1104,7 @@ async page => {
|
|
|
1126
1104
|
|
|
1127
1105
|
#### S-12:AI 应用 · 标准操作
|
|
1128
1106
|
|
|
1129
|
-
> **细则**见上文「AI 应用 `/application/`」(规约 A~H + AP-1~AP-
|
|
1107
|
+
> **细则**见上文「AI 应用 `/application/`」(规约 A~H + AP-1~AP-13)。本节为最短串联步骤;每个关键交互后 `snapshot` 一次直至验收通过。
|
|
1130
1108
|
|
|
1131
1109
|
1. **进入模块**:AP-1。验收:`pathname` `/application`/`/application/`;`title` 以 `AI应用-` 开头。
|
|
1132
1110
|
2. **(可选)搜索**:AP-2。验收:列表刷新。
|
|
@@ -1135,13 +1113,10 @@ async page => {
|
|
|
1135
1113
|
5. **(可选)`aria-label` 高频图标**(历史记录/回到顶部 等):AP-5(规约 A)。
|
|
1136
1114
|
6. **(可选)收藏/取消收藏**:AP-7/AP-8(取消收藏必接二次确认弹层,见规约 E)。
|
|
1137
1115
|
7. **进入某一应用(新标签)**:AP-6(规约 D);`tab-list` 记录 → `click` 卡片 → `tab-select` 切到新标签。勿在详情标签上 `go-back`。
|
|
1138
|
-
8. **应用详情 · 多块输入输出**(规约 C/F
|
|
1139
|
-
-
|
|
1140
|
-
-
|
|
1141
|
-
-
|
|
1142
|
-
- 整块操作(全部复制等)→ AP-11(**无需** hover);
|
|
1143
|
-
- hover 与 click 须在同一次 `run-code` 内连续执行,勿分两条命令;
|
|
1144
|
-
- 勿把 `复制文本` 与 `全部复制` 互换。
|
|
1116
|
+
8. **应用详情 · 多块输入输出**(规约 C/F):
|
|
1117
|
+
- 单块操作(复制某段/重新生成某段)→ 先 `hover` 该块 → AP-9/AP-10;
|
|
1118
|
+
- 整块操作(全部复制等)→ AP-11;
|
|
1119
|
+
- 勿把 `复制` 与 `全部复制` 互换。
|
|
1145
1120
|
9. **(可选)mindmap**:缩放 → AP-12(`hover` 画布内);滚动页面/外层 → AP-13(`hover` 画布外、容器内安全空白区)。
|
|
1146
1121
|
10. **收尾**:返回列表用 `tab-close`(详情标签)→ `tab-select` 回 `/application` 标签。
|
|
1147
1122
|
|
|
@@ -1205,7 +1180,7 @@ async page => {
|
|
|
1205
1180
|
2. **验收**:出现**新标签**,URL 为 `https://vip.helixlife.cn/edu/search?keywords=…`;`title` 为 `搜索-…`。继续操作需 `tab-list` → `tab-select <新索引>`。
|
|
1206
1181
|
3. **同关键字下切结果类型**:在 `/edu/search` 内用 `main` 作用域 `click` → `main.getByText('课程', { exact: true })` / `main.getByText('直播', { exact: true })`(推荐 `run-code`)。**勿**为此 `click` → `div.side-menu--item[to='/edu/courses']` 或走 S-14。验收:URL 仍 `/edu/search?keywords=…`,常带 `activeTab=course`/`activeTab=live`;「共找到 * 个相关内容」随 Tab 更新。
|
|
1207
1182
|
4. **收尾**(可选):`tab-close` → `tab-select 0` 回学习中心。
|
|
1208
|
-
5. **结果卡片 · 匹配说明(如「部分章节匹配」)**:按用户动词选 `hover` 或 `click`(细则见用户说法映射)。查看类 → `hover`(例:`run-code` 内 `await page.locator('main').getByText('部分章节匹配', { exact: true }).first().hover()`)。进入/点击类 → `click` 卡片主区或用户点名的控件。复现脚本:`helixlife-v5-cli run-code --filename=scripts/hover-partial-chapter-match.js
|
|
1183
|
+
5. **结果卡片 · 匹配说明(如「部分章节匹配」)**:按用户动词选 `hover` 或 `click`(细则见用户说法映射)。查看类 → `hover`(例:`run-code` 内 `await page.locator('main').getByText('部分章节匹配', { exact: true }).first().hover()`)。进入/点击类 → `click` 卡片主区或用户点名的控件。复现脚本:`helixlife-v5-cli run-code --filename=scripts/hover-partial-chapter-match.js`(多卡并存时可在脚本顶部设 `cardTitle` 缩小到目标课程卡片)。
|
|
1209
1184
|
|
|
1210
1185
|
#### S-19:学习中心 · 课程详情页与进入播放
|
|
1211
1186
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 搜索页 · 结果卡片「部分章节匹配」等说明标签 hover 验证。
|
|
3
|
+
* 在 /edu/search?keywords=… 页面,对目标课程卡片上的说明文案执行 hover,
|
|
4
|
+
* 然后检查是否出现 tooltip 类浮层。
|
|
5
|
+
*
|
|
6
|
+
* 可选:在脚本顶部设置 cardTitle 以缩小到特定课程卡片(多卡场景);
|
|
7
|
+
* 留空则取首条含「部分章节匹配」的卡片。
|
|
8
|
+
*
|
|
9
|
+
* 运行:helixlife-v5-cli run-code --filename=scripts/hover-partial-chapter-match.js
|
|
10
|
+
*/
|
|
11
|
+
async page => {
|
|
12
|
+
const cardTitle = null; // 如 '科研选题三十六策';null 表示取首条
|
|
13
|
+
const main = page.locator('main');
|
|
14
|
+
|
|
15
|
+
// 确定目标卡片作用域:用 exact 匹配标题文本,再向上一层到卡片根节点
|
|
16
|
+
// (避免 hasText 子串匹配命中外层容器或无关 div)
|
|
17
|
+
const scope = cardTitle
|
|
18
|
+
? main.getByText(cardTitle, { exact: true }).first().locator('..')
|
|
19
|
+
: main;
|
|
20
|
+
|
|
21
|
+
// 定位目标卡片中的「部分章节匹配」文案
|
|
22
|
+
const matchLabel = scope.getByText('部分章节匹配', { exact: true }).first();
|
|
23
|
+
if ((await matchLabel.count()) === 0) {
|
|
24
|
+
return {
|
|
25
|
+
ok: false,
|
|
26
|
+
reason: cardTitle
|
|
27
|
+
? `未在卡片「${cardTitle}」中找到「部分章节匹配」文案`
|
|
28
|
+
: '未找到「部分章节匹配」文案,可能当前搜索结果不含该标签',
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 仅使用语义明确的 tooltip 选择器,避免 [class*="tooltip"] 假阳性
|
|
33
|
+
const tooltipSelector = page.locator('[role="tooltip"], .ant-tooltip:not(.ant-tooltip-hidden)');
|
|
34
|
+
|
|
35
|
+
const beforeCount = await tooltipSelector.count();
|
|
36
|
+
|
|
37
|
+
await matchLabel.hover();
|
|
38
|
+
await page.waitForTimeout(1000);
|
|
39
|
+
|
|
40
|
+
const afterCount = await tooltipSelector.count();
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
ok: afterCount > beforeCount,
|
|
44
|
+
tooltipLikeBefore: beforeCount,
|
|
45
|
+
tooltipLikeAfter: afterCount,
|
|
46
|
+
labelVisible: await matchLabel.isVisible(),
|
|
47
|
+
cardTitle: cardTitle || '(首条)',
|
|
48
|
+
};
|
|
49
|
+
}
|