俊希全流程用例补充:前置数据检查判定+界面模拟点击脚本(cdp-test-junxi.mjs 实测通过)

This commit is contained in:
maogeigei committed 2026-08-31 17:23:58 +08:00
1 parent bd69d1c9a4
commit 0d63ffd8e0
3 files changed
+179 -9

No files matched your search

+8
View File
@@ -422,3 +422,11 @@
- **前置调研结论**:达人账号表仅 2 行(小咪橘座 70010/旧梦留声机 70013),**俊希不在表** → 首次登记 70014;`MCNSkill项目/俊希/` 仅含 `提示词01/`(分镜产出),无账号分析产出 → 全新执行场景
- **用例落盘**:`.workbuddy/test-cases/2026-08-31_俊希全流程.md`(TC-20260831-01),覆盖 TC-01 账号表登记 → TC-02 获取账号信息(含「有就刷新」分支:更新时间近1个月提示用户判断)→ TC-03 获取视频(≥60条/13列Excel)→ TC-04 筛选TOP(select-top6,n≤6按实际)→ TC-05 MCP解析(upload→3-5min→detail,补充规则)→ TC-06 json_tool 保存 → TC-07 人设卡12段 → TC-08 设定卡自动生成(grep一致性)→ TC-09 工作台AI选题(/api/ai/topics→3选题)→ TC-10 AI写脚本(S1-S11,automations触发)→ TC-11 收尾验证(git提交);含数据校验标准表 + 8 项异常分支预案 + 验收汇总表
- 用例引用的权威参数:视频筛选标准(TOP6上限/不足按实际,SKILL.md L43)、视频获取数量(≥60条停止,L44)、browser-harness 红线(L49)、路径配置(references/路径配置.md:开发机→桌面 MCNSkill项目/俊希/ 直挂)
## 俊希用例补充:数据检查 + 界面模拟点击(17:30)
- 用户补充要求:①测试用例需要**模拟点击界面**进行测试;②执行前要**判断项目文件夹和数据库是否已有数据、是否需要清除**
- **数据检查实测结论**(写入用例「二·补」章节):桌面 `MCNSkill项目/俊希/` 仅提示词01(分镜产出)→ 无需清除;达人账号表无俊希 → 首次登记 70014;**工作台 mcn-plugin.db 俊希有历史数据**——hot_accounts id=3(完整账号信息+content 设定文本,source_dir=`D:\dshworkspace\账号解析\俊希` **旧路径已不存在**)、account_videos 108 条(`抖音账号分析\俊希` 旧路径)、rewrite_log 3 条(2026-08-19 海鲜大餐主题);**关键判定:account_persona 表无俊希**(仅 1627-1630 测试占位)→ confirmAiScript 的 hasPersona=false → 弹窗走「先进行账号设定分析」分支,**首次场景成立,无需清除数据库**;dsh 原库(~/.dsh/mcn-plugin.db)同数据但只读红线不写
- **界面模拟点击脚本**:`mcn-work-shop/cdp-test-junxi.mjs`(新写,CDP 模式同 cdp-verify-*:连 9223 → Target.createTarget 新开标签页 → Runtime.evaluate 模拟点击 → 自动关标签页;`--confirm` 参数才真实触发创作任务);覆盖 #/accounts 定位俊希 `[data-create='俊希']` → 点击 → 轮询弹窗 → 点 #aiTopicGen → 轮询选题(100s 上限)→ 读 3 选题
- **实测通过**(安全模式):Step1 定位 PASS / Step2 弹窗结构 PASS(文案=「先进行账号设定分析,再生成完整脚本」首次分支)/ Step3-4 AI 选题 PASS(3 选题:突击妈妈公司送便当/替妈妈筹备周末闺蜜聚会/打工人妈妈病号特权日,贴合亲子Vlog人设)
- ⚠️ **坑**:`.modal` 选择器会被 index.html 隐藏的 `#settingsModal`(hidden 但 .modal 子元素在 DOM)干扰 → 判据须 `[...document.querySelectorAll('.modal-mask')].find(x => !x.hidden && x.querySelector('#aiTopicGen'))`
- **两条 AI写脚本入口差异**(写入用例 TC-09):#/accounts 每行「AI写脚本」(data-create→confirmAiScript)=自定义选题/AI结合人设生成选题(**主链路,对应「AI参考最新视频选题」**);#/rewrites 每行(data-rewrite→confirmRun)=基于视频《Y》改写全新 S9 脚本不复刻(次链路)
@@ -36,11 +36,44 @@
| # | 检查项 | 判定标准 | 本用例实际状态 |
|---|--------|---------|---------------|
| P1 | 达人账号表是否含「俊希」 | 有 → 走「有就刷新」分支;无 → Step 1.0 登记 | **无**(仅小咪橘座 70010 / 旧梦留声机 70013)→ 首次登记 70014 |
| P2 | 产出目录 `MCNSkill项目/俊希/` 现有内容 | 有账号设定/视频表格 → 刷新覆盖;无 → 全新创建 | **仅 `提示词01/` 子目录**,无账号设定/视频表格 → 全新 |
| P2 | 产出目录 `MCNSkill项目/俊希/` 现有内容 | 有账号设定/视频表格 → 刷新覆盖;无 → 全新创建 | **仅 `提示词01/` 子目录**(分镜技能产出,与本次链路不冲突),无账号分析产出 → 全新 |
| P3 | browser-harness 是否可用(功能一/二红线) | 可执行文件存在(Win `~/.local/bin/browser-harness.exe` / mac `~/.local/bin/browser-harness`)或 `where`/`command -v` 有输出 | 执行前检测,未装则按红线提醒/安装 |
| P4 | MCP 服务(myai-mcp-production)可用 | 当前会话 MCP 工具列表含 `upload_douyin_video`/`short_video_detail` 或 `~/.workbuddy/mcp.json` 含该服务配置 | 执行前检测,未装按 MCP 规范提醒 |
| P5 | MCN 工作台服务(端口 8899) | `http://localhost:8899` 可访问 | 未启动则先启动(`node server.js`,自动避让端口) |
| P6 | 输出路径权威配置 | 读 `references/路径配置.md` | 开发机 → 桌面 `MCNSkill项目/`,`俊希/` 账号层直挂 |
| P5 | MCN 工作台服务(端口 8899) | `http://localhost:8899` 可访问 | **运行中(200)**;未启动则先启动(`node server.js`,自动避让端口) |
| P6 | 浏览器 9223 调试实例 | `http://localhost:9223/json` 可访问(界面模拟点击测试用) | **运行中**;未启动则按浏览器操作规范启动调试 Chrome |
| P7 | 输出路径权威配置 | 读 `references/路径配置.md` | 开发机 → 桌面 `MCNSkill项目/`,`俊希/` 账号层直挂 |
## 二·补、前置数据检查与清理判定(本次实测 2026-08-31)
> 测试前先判断**项目文件夹**与**数据库**是否已有俊希数据,决定是否需要清除,避免测试污染/误判。
### 检查结果(本次实测)
| 数据源 | 检查项 | 实测结果 | 是否需清除 |
|-------|-------|---------|-----------|
| 项目文件夹(桌面 `MCNSkill项目/俊希/`) | 账号设定/视频表格/脚本 | **仅 `提示词01/`**(分镜产出,属短视频分镜提示词技能) | **否**(本链路产出全新创建;提示词01 保留) |
| 达人账号表.xlsx | 俊希行 | **无** | 否(首次登记 70014) |
| 工作台 mcn-plugin.db(`…\mcn-work-shop\`,自有副本) | hot_accounts 俊希 | **有**(id=3,含完整账号信息 + content 账号设定文本、douyin_id=JJX0827、sec_uid、278.7万粉、source_dir=`D:\dshworkspace\账号解析\俊希` 为**旧路径残留**,目录已不存在) | **否**(见下判定) |
| 同上 | account_videos 俊希 | **有 108 条**(source_dir=`D:\dshworkspace\抖音账号分析\俊希` 旧路径) | 否 |
| 同上 | account_persona 俊希 | **无**(5 行均为 1627-1630 测试占位,无 account_id=3) | 否 |
| 同上 | rewrite_log 俊希 | **有 3 条**(2026-08-19 AI 写脚本记录,id=12/15/16,topic 含完整选题) | 否 |
| dsh 原库(`~/.dsh/mcn-plugin.db`) | 俊希数据 | 与工作台副本一致(hot_accounts/videos/rewrite_log + account_video_source 2 条) | **否(.dsh 单向只读红线,绝不写)** |
| 旧目录 `D:\dshworkspace\账号解析\俊希`、`抖音账号分析\俊希` | 文件 | **已不存在**(数据库 source_dir 为历史残留) | 无需处理 |
### 清理判定逻辑(结论)
1. **文件层面**:桌面 `MCNSkill项目/俊希/` 无本链路产出 → **无需清除**,全新创建;已有产出时(有就刷新)→ 覆盖更新 `短视频表格.xlsx`、新增/更新设定与脚本,**不删除旧产出**(保留可追溯)
2. **数据库层面(工作台 mcn-plugin.db 自有副本)**:俊希有历史数据但**不影响本测试**——关键判定 `account_persona` 无俊希 → 工作台 `confirmAiScript` 的 hasPersona=false → 弹窗走「先进行账号设定分析」分支,**首次场景成立**(hot_accounts.content 的旧设定文本不会被界面读取,界面只读 account_persona)。因此**无需清除**;若追求「完全干净」需用户明确授权后选择性清除(hot_accounts id=3 及其 videos/rewrite_log 行),且只动工作台自有副本
3. **dsh 原库**:**红线只读,绝不写**(清理不适用)
4. **工作台副本与 dsh 原库的数据同步**:工作台读自有副本(`mcn-work-shop/mcn-plugin.db`),与 `~/.dsh/mcn-plugin.db` 隔离;测试新增数据(rewrite_log 第 4 条等)只落工作台副本,不影响 dsh 原库
### 清除操作(如用户要求执行,需先备份)
```bash
# 仅工作台自有副本;先 cp 备份 mcn-plugin.db 再操作
# 清除俊希:DELETE FROM rewrite_log WHERE account_id=3; DELETE FROM account_videos WHERE account_id=3; DELETE FROM hot_accounts WHERE id=3;
# 注意:达人账号表/桌面产出/dsh 原库一律不动
```
---
@@ -118,20 +151,31 @@
| 验证点 | ① 卡片 TOPn 与设定 md 内容基因板块数一致(**n 不写死 6**);② 双向联动一致性:交付前 **grep 校验**两文件关键术语一致(模块名「叙事结构」「内容标签」「内容基因·TOPn」等),零残留后再交付;③ 卡片内容与设定 md 内容级一致 |
| 通过标准 | `俊希账号设定卡.svg` 生成;grep 校验通过(模块名/TOPn 两文件一致) |
### TC-09 工作台 AI 参考最新视频选题
### TC-09 工作台 AI 参考最新视频选题(**界面模拟点击**)
> **本环节用可执行脚本模拟真实点击测试**,脚本:`mcn-work-shop/cdp-test-junxi.mjs`
> 用法:`node cdp-test-junxi.mjs`(安全模式:验证到选题生成,不触发任务);`node cdp-test-junxi.mjs --confirm`(继续确认执行,真实触发创作任务)
> 机制:连接浏览器 9223 调试实例 → 新开标签页打开 `#/accounts` → `Runtime.evaluate` 模拟点击 → 轮询弹窗/AI 选题 → 自动关闭标签页(不干扰用户页面)。脚本复用 cdp-verify-*.mjs 的 CDP 模式。
**两条「AI写脚本」入口差异(重要)**:
| 入口 | 位置 | 行为 | 本用例用途 |
|------|------|------|-----------|
| 账号列表「AI写脚本」 | `#/accounts` 每行(`[data-create]`) | `confirmAiScript` → 弹窗:手输选题 **或 点 AI 按钮结合人设生成选题**(`POST /api/ai/topics`,基于账号设定+最新视频提炼 3 个方向) | **主链路**(AI参考最新视频选题) |
| 视频列表「AI写脚本」 | `#/rewrites` 每行(`[data-rewrite]`) | `confirmRun` 改写脚本:「基于视频《Y》改写全新 S9 故事脚本(不复刻原视频)」 | 次链路(基于单条视频改写,可选回归) |
| 项 | 内容 |
|---|------|
| 操作 | ① 启动工作台(`node server.js`,8899 自动避让);② 打开「AI写脚本」页(#/ai-script),在「俊希」行点「创作」→ `confirmAiScript('俊希', btn)`;③ 因俊希**已有设定** → 弹窗文案为「基于当前账号设定生成脚本」;④ 点击右下角 `AI` 按钮(#aiTopicGen)→ `POST /api/ai/topics`(body `{accountName:'俊希'}`)→ 等待约 50s 返回 |
| 预期 | 返回 **3 个选题方向**(基于俊希账号设定 + 最新视频内容提炼);自动填入选题输入框并 toast「✅ 已生成 3 个选题方向」 |
| 验证点 | ① 弹窗文案正确识别「已有设定」分支;② 选题方向与俊希亲子 Vlog 人设相关(非泛泛);③ 选题可编辑(手输或修改) |
| 通过标准 | 3 个选题方向生成且贴合账号设定,填入输入框 |
| 操作(脚本自动执行) | ① 脚本新开标签页打开 `#/accounts` → 定位俊希行「AI写脚本」按钮(`[data-create='俊希']`)→ **模拟点击**;② 轮询等待弹窗出现(`.modal-mask:not([hidden])` 含 `#aiTopicGen`,排除 index.html 隐藏的 `#settingsModal` 干扰);③ 读取弹窗标题/文案 → 验证分支;④ **模拟点击 `AI` 按钮**(#aiTopicGen)→ `POST /api/ai/topics` → 轮询选题输入框(最长 100s,Dify 约 50s)→ 读取 3 个选题方向;⑤ 安全模式点「取消」关闭弹窗;`--confirm` 模式点「确认执行」触发任务 |
| 预期 | 弹窗标题「AI写脚本」;文案分支 = **「先进行账号设定分析,再生成完整脚本」**(account_persona 无俊希 → hasPersona=false → 首次场景);AI 选题返回 **3 个贴合俊希亲子 Vlog 人设的方向**(参考最新视频内容提炼,如职场探班/闺蜜聚会/病号特权等暖萌治愈向);toast「✅ 已生成 3 个选题方向」 |
| 验证点 | ① 弹窗结构齐全(#aiTopicInput/#aiTopicGen/[data-ok]);② 文案分支正确;③ 选题数量 ≥3 且与俊希人设相关(非泛泛);④ 选题可编辑;⑤ 脚本 Step1-4 全 PASS |
| 通过标准 | 脚本输出 `Step1 定位俊希按钮: PASS / Step2 弹窗结构: PASS / Step3-4 AI 选题: PASS`(**已实测通过 2026-08-31**:选题=突击妈妈公司送便当/替妈妈筹备周末闺蜜聚会/打工人妈妈病号特权日) |
### TC-10 AI 写脚本(S1-S11 完整流程)
| 项 | 内容 |
|---|------|
| 操作 | ① 选定一个选题方向(保留在输入框)→ 点「确认」→ `executeTask()` `POST /api/run` 写入 `~/.workbuddy/workbuddy.db` automations(once+now,cwds=`D:\AgentSkill\mcn-workshop`)→ WorkBuddy 自动执行创建会话(左侧会话栏 mcn-workshop 空间可见);② 轮询 `GET /api/run/status?id=`(5s 间隔,30min 上限)观察任务状态 running → done |
| 操作 | ① 选定一个选题方向(保留在输入框)→ 点「确认执行」(TC-09 脚本 `--confirm` 模式自动完成)→ `executeTask()` `POST /api/run` 写入 `~/.workbuddy/workbuddy.db` automations(once+now,cwds=`D:\AgentSkill\mcn-workshop`)→ WorkBuddy 自动执行创建会话(左侧会话栏 mcn-workshop 空间可见);② 轮询 `GET /api/run/status?id=`(5s 间隔,30min 上限)观察任务状态 running → done |
| 预期 | 创作会话按脚本创作技能执行 **S1-S11 完整流程**(三态创作默认自动模式=静默执行、只落最终交付物):S1 理解需求 → S2 检索对标 → S3 确定选题 → S4 素材库 → S5 极致事件 → S6 情绪规划 → S7 结构搭建 → S8 大纲 → S9 脚本(分镜)→ S10 可选细粒度分镜 → S11 诊断复盘;prompt 带「选题方向:{topic}」 |
| 验证点 | ① 任务状态流转正常(IN_PROGRESS → DONE/COMPLETED);② 脚本落盘 `MCNSkill项目/俊希/脚本1/`(产物链:05_故事选题 → 06_短视频框架 → 07_短视频大纲 → 09_短视频脚本,禁跳步);③ S9 脚本质量门禁:开场钩子绝对时长 3-7 秒(硬上限 10 秒,不看占比);④ 数字账取证(禁心算);⑤ 画面标注 5 字段【景别|环境|光线|场景|场景元素】;⑥ 人物标注格式统一;⑦ 交付物只含输出模板字段(验收口径=执行约束,不在正文自证) |
| 通过标准 | 脚本全流程完成落盘;S9 脚本完整可拍;工作台任务状态 done;按钮恢复 |
@@ -202,6 +246,9 @@
- 达人账号表当前仅 2 行:小咪橘座(70010,已解析/已生成)、旧梦留声机(70013,已生成)→ 俊希首次登记为 70014
- `MCNSkill项目/俊希/` 现有仅 `提示词01/` 子目录(分镜产出,属短视频分镜提示词技能),无账号分析产出
- **工作台 mcn-plugin.db 俊希历史数据**:hot_accounts id=3(完整账号信息 + content 设定文本,source_dir=`D:\dshworkspace\账号解析\俊希` **旧路径已不存在**)、account_videos 108 条(source_dir=`D:\dshworkspace\抖音账号分析\俊希` 旧路径)、rewrite_log 3 条(2026-08-19,海鲜大餐主题);**account_persona 无俊希** → hasPersona=false → 弹窗走「先进行账号设定分析」分支,首次场景成立,无需清除
- dsh 原库(~/.dsh/mcn-plugin.db)俊希数据与副本一致(.dsh 只读红线不写)
- **界面模拟点击测试脚本**:`mcn-work-shop/cdp-test-junxi.mjs`(2026-08-31 实测通过:账号列表定位俊希 PASS / AI写脚本弹窗结构 PASS / AI 选题 3 个方向 PASS);注意 `.modal` 选择器会被 index.html 隐藏的 `#settingsModal` 干扰,须限定 `:not([hidden])` 且含 `#aiTopicGen`
- MCP 解析单条视频需等待 3-5 分钟;已解析过的视频可直接查询
- 工作台 #/reviews「AI脚本诊断」复盘按钮明确按 **mcn-script-review(脚本复盘)技能**执行(本用例主链路不含复盘,如需回归可追加)
- 产出根目录:开发机 → 桌面 `MCNSkill项目/{达人昵称}/`(`references/路径配置.md` 权威)
@@ -0,0 +1,115 @@
// 模拟点击测试:俊希「AI写脚本」界面链路(#/accounts → AI写脚本弹窗 → AI 选题生成)
// 覆盖测试用例 TC-09 的界面交互环节(真实模拟用户点击,非仅文字步骤)
//
// 前置:
// 1. 工作台服务运行中(node server.js,端口 8899)
// 2. 浏览器 9223 调试实例已启动(browser-harness 调试实例)
// 3. 账号列表(hot_accounts)含「俊希」
// 用法:
// node cdp-test-junxi.mjs # 只验证到「选题生成」,不触发创作任务(安全模式)
// node cdp-test-junxi.mjs --confirm # 选题生成后继续点「确认执行」,真实触发创作任务
// 说明:脚本在 9223 实例新开标签页操作,不干扰用户已打开的页面;结束后自动关闭新开标签页。
const CONFIRM = process.argv.includes('--confirm');
const BASE = 'http://localhost:8899';
// 1) 连接 9223 调试实例
const list = await fetch('http://localhost:9223/json').then((r) => r.json());
const page = list.find((t) => t.type === 'page');
if (!page) { console.log('ERR: 9223 无可用页面,请先启动 browser-harness 调试实例'); process.exit(1); }
const ws = new WebSocket(page.webSocketDebuggerUrl);
let id = 0; const pending = new Map();
const send = (method, params = {}) => new Promise((res, rej) => { const i = ++id; pending.set(i, { res, rej }); ws.send(JSON.stringify({ id: i, method, params })); });
ws.onmessage = (e) => { const m = JSON.parse(e.data); if (m.id && pending.has(m.id)) { const p = pending.get(m.id); pending.delete(m.id); m.error ? p.rej(new Error(m.error.message)) : p.res(m.result); } };
await new Promise((r) => (ws.onopen = r));
const ev = async (expr) => { const r = await send('Runtime.evaluate', { expression: expr, returnByValue: true, awaitPromise: true }); if (r.exceptionDetails) return { __err: (r.exceptionDetails.exception?.description || r.exceptionDetails.text).slice(0, 300) }; return r.result.value; };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
await send('Page.enable'); await send('Runtime.enable');
// 2) 新开标签页打开账号列表(不干扰现有页面)
const { targetId } = await send('Target.createTarget', { url: BASE + '/#/accounts' });
await sleep(2500);
let targetPage = list2 => list2.find((t) => t.type === 'page' && t.url.includes('localhost:8899') && t.id === targetId);
let pages = await fetch('http://localhost:9223/json').then((r) => r.json());
let tp = targetPage(pages);
if (!tp) { console.log('ERR: 新开标签页未找到 8899 页面'); process.exit(1); }
// 切换 WS 到新页面
const ws2 = new WebSocket(tp.webSocketDebuggerUrl);
let id2 = 0; const pending2 = new Map();
const send2 = (method, params = {}) => new Promise((res, rej) => { const i = ++id2; pending2.set(i, { res, rej }); ws2.send(JSON.stringify({ id: i, method, params })); });
ws2.onmessage = (e) => { const m = JSON.parse(e.data); if (m.id && pending2.has(m.id)) { const p = pending2.get(m.id); pending2.delete(m.id); m.error ? p.rej(new Error(m.error.message)) : p.res(m.result); } };
await new Promise((r) => (ws2.onopen = r));
const ev2 = async (expr) => { const r = await send2('Runtime.evaluate', { expression: expr, returnByValue: true, awaitPromise: true }); if (r.exceptionDetails) return { __err: (r.exceptionDetails.exception?.description || r.exceptionDetails.text).slice(0, 300) }; return r.result.value; };
const sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
await send2('Page.enable'); await send2('Runtime.enable');
await send2('Page.reload', { ignoreCache: true }); await sleep2(3500);
// 3) 账号列表页:定位俊希行「AI写脚本」按钮
const findBtn = await ev2(`(() => {
const btns = [...document.querySelectorAll('[data-create]')];
const b = btns.find((x) => x.dataset.create === '俊希');
if (!b) return { found: false, total: btns.length, names: btns.slice(0, 20).map((x) => x.dataset.create) };
return { found: true, accId: b.dataset.accId, txt: b.textContent.trim() };
})()`);
console.log('【Step1 账号列表定位俊希】', JSON.stringify(findBtn));
if (!findBtn.found) {
console.log('FAIL: 账号列表未找到俊希「AI写脚本」按钮(若账号未关注,请先到周榜/账号列表关注俊希)');
await send('Target.closeTarget', { targetId }); ws.close(); ws2.close(); process.exit(1);
}
// 4) 模拟点击「AI写脚本」→ 轮询等待弹窗出现(confirmAiScript 先 await persona 接口再建弹窗,需等待)
await ev2(`(() => { const b = [...document.querySelectorAll('[data-create]')].find((x) => x.dataset.create === '俊希'); b.click(); return true; })()`);
const modal = await ev2(`(async () => {
for (let i = 0; i < 24; i++) {
// 只认非 hidden 且含 #aiTopicGen 的弹窗(index.html 有 hidden 的 #settingsModal 干扰)
const m = [...document.querySelectorAll('.modal-mask')].find((x) => !x.hidden && x.querySelector('#aiTopicGen'));
if (m) {
return { open: true, title: m.querySelector('h3')?.textContent.trim() || '', desc: m.querySelector('.dim')?.textContent.trim() || '', hasTopicInput: !!m.querySelector('#aiTopicInput'), hasGenBtn: !!m.querySelector('#aiTopicGen'), hasOk: !!m.querySelector('[data-ok]') };
}
await new Promise((r) => setTimeout(r, 500));
}
return { open: false };
})()`);
console.log('【Step2 AI写脚本弹窗】', JSON.stringify(modal, null, 1));
const descOk = modal.open && modal.title.includes('AI写脚本') && modal.hasTopicInput && modal.hasGenBtn;
const branch = modal.open && (modal.desc || '').includes('账号设定');
console.log(branch ? ' → 文案分支:先进行账号设定分析(首次场景,符合预期)' : ' → 文案分支:基于当前账号设定(有设定场景)');
// 5) 模拟点击「AI」选题按钮 → 轮询选题输入框(Dify 约 50s)
await ev2(`(() => { const g = document.querySelector('#aiTopicGen'); g.click(); return true; })()`);
console.log('【Step3 AI 选题生成中…(最长等待 100s)】');
let topics = '';
const t0 = Date.now();
while (Date.now() - t0 < 100000) {
await sleep2(3000);
topics = (await ev2(`(() => document.querySelector('#aiTopicInput')?.value || '')()`)) || '';
if (topics && !topics.includes('(未生成内容)')) break;
}
console.log('【Step4 选题结果】');
console.log(topics || '(超时未返回)');
const topicOk = topics.split('\n').filter((l) => l.trim()).length >= 3 || /选题|方向/.test(topics);
// 6) --confirm 模式:确认执行触发创作任务
if (CONFIRM) {
console.log('【Step5 --confirm】点击确认执行,触发创作任务…');
await ev2(`(() => { document.querySelector('[data-ok]').click(); return true; })()`);
await sleep2(1500);
const toast = await ev2(`(() => document.querySelector('.toast')?.textContent || '')()`);
console.log(' toast:', toast || '(未捕获)');
} else {
console.log('【Step5 安全模式】未触发创作任务(加 --confirm 参数可真实触发)');
await ev2(`(() => { document.querySelector('[data-cancel]')?.click(); return true; })()`);
}
// 7) 汇总
console.log('\n===== 测试结果 =====');
console.log('Step1 定位俊希按钮:', findBtn.found ? 'PASS' : 'FAIL');
console.log('Step2 弹窗结构:', descOk ? 'PASS' : 'FAIL');
console.log('Step3-4 AI 选题:', topicOk ? 'PASS' : 'CHECK');
console.log(CONFIRM ? 'Step5 任务触发: 已提交(到左侧会话栏查看)' : 'Step5 任务触发: 未触发(安全模式)');
// 关闭新开标签页
await send('Target.closeTarget', { targetId });
ws.close(); ws2.close();
process.exit(descOk && findBtn.found ? 0 : 1);