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

62 lines
4.7 KiB
JavaScript

// 09-04 R65 验证:视频解析任务「执行中」状态跨刷新保持 + 终态自动恢复
// 模拟:先写入 wk_video_task_93(不存在的 taskId)→ 打开视频详情 → 应显示「执行中」
// → watch 轮询 /api/run/status → notfound → 清理 localStorage + load 恢复可点按钮
const CDP = 9223;
const list = await fetch(`http://localhost:${CDP}/json`).then((r) => r.json());
const page = list.find((t) => t.type === 'page' && (t.url || '').includes('8900')) || list.find((t) => t.type === 'page');
if (!page) { console.log('ERR: no page'); 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'); await send('Network.enable');
await send('Network.setCacheDisabled', { cacheDisabled: true });
await send('Emulation.setDeviceMetricsOverride', { width: 860, height: 1000, deviceScaleFactor: 1, mobile: false });
const waitFor = async (expr, ms = 15000) => { const t0 = Date.now(); while (Date.now() - t0 < ms) { if (await ev(expr)) return true; await sleep(300); } return false; };
const fail = (m) => { console.log('FAIL@' + m); process.exit(1); };
const fs = await import('node:fs');
// 1) 注入伪造执行中记录后打开视频 93
await ev(`localStorage.setItem('wk_video_task_93', JSON.stringify({ taskId: 'nonexist-test-1', ts: Date.now() }))`);
await send('Page.navigate', { url: `http://127.0.0.1:8900/?t=${Date.now()}#/video/93` });
if (!(await waitFor(`location.hash === '#/video/93' && !!document.querySelector('#view .video-grid') && [...document.scripts].some((s) => (s.src || '').includes('data-pages.js?v=20260904p'))`, 15000))) fail('v93 route');
await sleep(600);
// 2) 断言:出现「执行中」禁用按钮
const running1 = await ev(`(() => {
const b = document.querySelector('.tab-tools .btn-busy, .empty-state .btn-busy');
const rec = localStorage.getItem('wk_video_task_93');
return { busy: !!b, txt: b ? (b.textContent || '').trim() : null, disabled: b ? b.disabled : null, recKept: !!rec, v93Running: location.hash };
})()`);
console.log('STEP1 running state:', JSON.stringify(running1));
// 3) 等 watch 轮询(间隔 5s)走到 notfound → 清理 + load 恢复
const recovered = await waitFor(`!localStorage.getItem('wk_video_task_93') && !!document.querySelector('[data-parse]') && !document.querySelector('.tab-tools .btn-busy, .empty-state .btn-busy')`, 25000);
console.log('STEP2 recovered:', recovered);
const state2 = await ev(`(() => {
const b = document.querySelector('[data-parse]');
return { parseBtn: b ? (b.textContent || '').trim() : null, disabled: b ? b.disabled : null, rec: localStorage.getItem('wk_video_task_93') };
})()`);
console.log('STEP2 final:', JSON.stringify(state2));
// 4) 清理测试残留并截图执行中形态(重注入后快速抓拍)
await ev(`localStorage.setItem('wk_video_task_93', JSON.stringify({ taskId: 'nonexist-test-2', ts: Date.now() }))`);
await send('Page.navigate', { url: `http://127.0.0.1:8900/?t=${Date.now()}#/video/93` });
await waitFor(`!!document.querySelector('.tab-tools .btn-busy, .empty-state .btn-busy')`, 10000);
await sleep(400);
const rect = await ev(`(() => { const el = document.querySelector('.tab-tools .btn-busy, .empty-state .btn-busy'); if (!el) return null; const r = el.getBoundingClientRect(); return { x: r.x, y: r.y, w: r.width, h: r.height }; })()`);
if (rect) {
const shot = await send('Page.captureScreenshot', { format: 'png', clip: { x: Math.max(0, rect.x - 160), y: Math.max(0, rect.y - 12), width: rect.w + 300, height: rect.h + 24, scale: 1 } });
fs.writeFileSync('tmp/r65_running_btn.png', Buffer.from(shot.data, 'base64'));
}
// 等 notfound 清理
await waitFor(`!localStorage.getItem('wk_video_task_93')`, 20000);
const ok = running1 && running1.busy && running1.txt && running1.txt.includes('执行中') && running1.disabled === true && recovered && state2 && state2.rec === null && state2.parseBtn;
console.log(ok ? '\n=== PASS ===' : '\n=== CHECK ===');
ws.close();
process.exit(ok ? 0 : 1);