Files
mcn-short-video/project/短视频脚本创作/V1.0/mcn-work-shop/cdp-test-junxi.mjs
T

116 lines
7.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 模拟点击测试:俊希「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);