123 lines
7.2 KiB
JavaScript
123 lines
7.2 KiB
JavaScript
// 验证「单向联动」v2:全部用 JS 设置 scrollTop 触发 scroll 事件(避免 mouseWheel SIGTERM)
|
||||
|
|
const list = await fetch('http://localhost:9223/json').then((r) => r.json());
|
|||
|
|
const page = list.find((t) => t.type === 'page' && t.url.includes('#/rewrites'));
|
|||
|
|
if (!page) { console.log('ERR: no #/rewrites 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('Page.reload', { ignoreCache: true });
|
|||
|
|
await sleep(3000);
|
|||
|
|
await ev(`document.querySelectorAll('.modal-mask').forEach((m) => m.remove()); 'cleaned'`);
|
|||
|
|
await ev(`(() => { const i = document.querySelector('#rwSearch'); if (i) { i.value = '快递'; i.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); } return !!i; })()`);
|
|||
|
|
await sleep(2000);
|
|||
|
|
const clickRes = await ev(`(() => {
|
|||
|
|
const btns = [...document.querySelectorAll('[data-detail]')];
|
|||
|
|
if (!btns.length) return { err: 'no data-detail' };
|
|||
|
|
btns[0].click();
|
|||
|
|
return { clicked: btns[0].dataset.detail };
|
|||
|
|
})()`);
|
|||
|
|
console.log('打开弹窗:', JSON.stringify(clickRes));
|
|||
|
|
await sleep(2500);
|
|||
|
|
|
|||
|
|
const st = async () => ev(`(() => {
|
|||
|
|
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
|
|||
|
|
if (!secs.length) return { err: 'no rw-stack' };
|
|||
|
|
const ai = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI脚本');
|
|||
|
|
const sb = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI分镜提示词');
|
|||
|
|
if (!ai || !sb) return { err: 'missing col', heads: secs.map((s) => s.querySelector('.rw-sec-head')?.textContent.trim()) };
|
|||
|
|
const aiBody = ai.querySelector('.rw-sec-body'); const sbBody = sb.querySelector('.rw-sec-body');
|
|||
|
|
const vis = [...sbBody.querySelectorAll('.sb-block')].find((b) => b.getBoundingClientRect().top >= sbBody.getBoundingClientRect().top - 4);
|
|||
|
|
return {
|
|||
|
|
aiScrollTop: Math.round(aiBody.scrollTop), sbScrollTop: Math.round(sbBody.scrollTop),
|
|||
|
|
aiHeads: aiBody.querySelectorAll('strong').length, sbBlocks: sbBody.querySelectorAll('.sb-block').length,
|
|||
|
|
sbVis: vis ? (vis.querySelector('.sb-head')?.textContent.trim() || '').slice(0, 30) : null,
|
|||
|
|
};
|
|||
|
|
})()`);
|
|||
|
|
|
|||
|
|
const init = await st();
|
|||
|
|
console.log('初始:', JSON.stringify(init));
|
|||
|
|
|
|||
|
|
// 翻到有分镜的 AI 脚本版本(rewrite 22)
|
|||
|
|
if ((init.sbBlocks || 0) === 0) {
|
|||
|
|
for (let i = 0; i < 5; i++) {
|
|||
|
|
const hasNext = await ev(`(() => { const b = document.querySelector('[data-vnext]'); if (!b || b.disabled) return false; b.click(); return true; })()`);
|
|||
|
|
if (!hasNext) break;
|
|||
|
|
await sleep(900);
|
|||
|
|
const s = await st();
|
|||
|
|
console.log('翻版本后:', JSON.stringify(s));
|
|||
|
|
if ((s.sbBlocks || 0) > 0) break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
const ready = await st();
|
|||
|
|
console.log('就绪:', JSON.stringify(ready));
|
|||
|
|
if (!(ready.sbBlocks > 0)) { console.log('ERR: 无分镜版本可验证'); process.exit(0); }
|
|||
|
|
|
|||
|
|
// ===== 链路①:AI脚本列滚动 → 分镜列应对齐(设置scrollTop+补发scroll事件模拟滚动) =====
|
|||
|
|
const setAi = async (delta, tag) => {
|
|||
|
|
const r = await ev(`(() => {
|
|||
|
|
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
|
|||
|
|
const aiBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI脚本').querySelector('.rw-sec-body');
|
|||
|
|
aiBody.scrollTop += ${delta};
|
|||
|
|
aiBody.dispatchEvent(new Event('scroll')); // CDP下scrollTop赋值不触发事件,补发
|
|||
|
|
return Math.round(aiBody.scrollTop);
|
|||
|
|
})()`);
|
|||
|
|
await sleep(350);
|
|||
|
|
const s = await st();
|
|||
|
|
console.log(`${tag}: aiScrollTop=${r} → sbScrollTop=${s.sbScrollTop} sbVis=${s.sbVis}`);
|
|||
|
|
return s;
|
|||
|
|
};
|
|||
|
|
let cur = await setAi(400, '链路① 第1步滚400');
|
|||
|
|
cur = await setAi(400, '链路① 第2步滚400');
|
|||
|
|
cur = await setAi(400, '链路① 第3步滚400');
|
|||
|
|
const link1 = {
|
|||
|
|
aiMoved: Math.abs(cur.aiScrollTop - ready.aiScrollTop) > 50,
|
|||
|
|
sbMoved: Math.abs(cur.sbScrollTop - ready.sbScrollTop) > 50,
|
|||
|
|
};
|
|||
|
|
console.log('链路①判定:', JSON.stringify(link1), link1.aiMoved && link1.sbMoved ? '✅ AI脚本主导联动生效' : '❌ 联动异常');
|
|||
|
|
|
|||
|
|
// ===== 链路②:分镜列滚动 → AI脚本列独立 =====
|
|||
|
|
const beforeSb = await st();
|
|||
|
|
const r2 = await ev(`(() => {
|
|||
|
|
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
|
|||
|
|
const sbBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI分镜提示词').querySelector('.rw-sec-body');
|
|||
|
|
sbBody.scrollTop += 600;
|
|||
|
|
sbBody.dispatchEvent(new Event('scroll'));
|
|||
|
|
return Math.round(sbBody.scrollTop);
|
|||
|
|
})()`);
|
|||
|
|
await sleep(350);
|
|||
|
|
const afterSb = await st();
|
|||
|
|
console.log(`链路② 分镜列滚600: sbScrollTop=${r2} → aiScrollTop=${afterSb.aiScrollTop} sbVis=${afterSb.sbVis}`);
|
|||
|
|
const link2 = {
|
|||
|
|
sbMoved: Math.abs(afterSb.sbScrollTop - beforeSb.sbScrollTop) > 50,
|
|||
|
|
aiMovedBack: Math.abs(afterSb.aiScrollTop - beforeSb.aiScrollTop) > 5,
|
|||
|
|
};
|
|||
|
|
console.log('链路②判定:', JSON.stringify(link2), link2.sbMoved && !link2.aiMovedBack ? '✅ 分镜列独立滚动,AI脚本列未动' : (link2.sbMoved ? '❌ AI脚本列被反向联动' : '❌ 分镜列未滚动'));
|
|||
|
|
|
|||
|
|
// ===== 链路①b:AI脚本列跳底部 → 分镜列跟随最后场 =====
|
|||
|
|
const jump = await ev(`(() => {
|
|||
|
|
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
|
|||
|
|
const aiBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI脚本').querySelector('.rw-sec-body');
|
|||
|
|
aiBody.scrollTop = aiBody.scrollHeight;
|
|||
|
|
aiBody.dispatchEvent(new Event('scroll'));
|
|||
|
|
return { set: Math.round(aiBody.scrollTop) };
|
|||
|
|
})()`);
|
|||
|
|
await sleep(400);
|
|||
|
|
const afterJump = await st();
|
|||
|
|
const detail = await ev(`(() => {
|
|||
|
|
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
|
|||
|
|
const aiBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI脚本').querySelector('.rw-sec-body');
|
|||
|
|
const heads = [...aiBody.querySelectorAll('strong, h1, h2, h3, h4')].filter((el) => /场\\s*\\d/.test(el.textContent));
|
|||
|
|
const sbBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI分镜提示词').querySelector('.rw-sec-body');
|
|||
|
|
const vis = [...sbBody.querySelectorAll('.sb-block')].find((b) => b.getBoundingClientRect().top >= sbBody.getBoundingClientRect().top - 4);
|
|||
|
|
return { lastAiHead: heads.length ? heads[heads.length - 1].textContent.trim().slice(0, 24) : null, sbVis: vis ? (vis.querySelector('.sb-head')?.textContent.trim() || '').slice(0, 30) : null };
|
|||
|
|
})()`);
|
|||
|
|
console.log('链路①b 跳底部:', JSON.stringify({ jump, sbScrollTop: afterJump.sbScrollTop, detail }));
|
|||
|
|
|
|||
|
|
try { ws.close(); } catch (e) {}
|
|||
|
|
process.exit(0);
|