Files
ycode/scripts/e2e-cancel.mjs
T

115 lines
3.8 KiB
JavaScript

/**
* 端到端验证「取消」链路:发起长回合 → 中途点停止 → 校验状态与内核行为。
*
* 用法:node scripts/e2e-cancel.mjs <port>
*/
const port = Number(process.argv[2] ?? 9335)
async function main() {
const list = await (await fetch(`http://127.0.0.1:${port}/json/list`)).json()
const page = list.filter((t) => t.type === 'page')[0]
if (!page) throw new Error('未找到页面')
const ws = new WebSocket(page.webSocketDebuggerUrl)
await new Promise((r, j) => {
const t = setTimeout(() => j(new Error('连接超时')), 8000)
ws.addEventListener('open', () => {
clearTimeout(t)
r()
})
ws.addEventListener('error', () => {
clearTimeout(t)
j(new Error('连接失败'))
})
})
const send = (method, params = {}) =>
new Promise((res, rej) => {
const id = Math.floor(Math.random() * 1e9)
const timer = setTimeout(() => rej(new Error(`超时 ${method}`)), 25000)
const h = (ev) => {
const m = JSON.parse(ev.data)
if (m.id === id) {
clearTimeout(timer)
ws.removeEventListener('message', h)
res(m.result)
}
}
ws.addEventListener('message', h)
ws.send(JSON.stringify({ id, method, params }))
})
const evaluate = (expression) => send('Runtime.evaluate', { expression, returnByValue: true })
// 1) 发起一个长回合
const fill = `(() => {
const el = document.querySelector('.composer__input');
const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
setter.call(el, '写一篇 4000 字的软件架构设计文章,分十个章节,每章都要有完整内容。');
el.dispatchEvent(new Event('input', { bubbles: true }));
return 'OK';
})()`
await evaluate(fill)
const clicked = await evaluate(`(() => {
const btn = document.querySelector('.btn-send');
if (!btn || btn.disabled) return 'FAIL';
btn.click();
return 'SENT';
})()`)
console.log('发送:', clicked.result.value)
if (clicked.result.value !== 'SENT') process.exit(1)
// 2) 等出现停止按钮(说明回合在跑)
let runningSeen = false
for (let i = 0; i < 40; i++) {
await new Promise((r) => setTimeout(r, 500))
const probe = await evaluate(`JSON.stringify({ stop: !!document.querySelector('.btn-stop') })`)
if (JSON.parse(probe.result.value).stop) {
runningSeen = true
break
}
}
console.log('回合进行中:', runningSeen)
if (!runningSeen) {
console.log('⚠️ 未进入运行态,无法验证取消')
ws.close()
process.exit(1)
}
// 3) 让它跑一小会儿再取消
await new Promise((r) => setTimeout(r, 3000))
const cancel = await evaluate(`(() => {
const btn = document.querySelector('.btn-stop');
if (!btn) return 'NO_STOP';
btn.click();
return 'CANCELLED';
})()`)
console.log('点击停止:', cancel.result.value)
// 4) 等待终态
let final = null
for (let i = 0; i < 40; i++) {
await new Promise((r) => setTimeout(r, 700))
const probe = await evaluate(`JSON.stringify({
stopGone: !document.querySelector('.btn-stop'),
sendBack: !!document.querySelector('.btn-send'),
status: [...document.querySelectorAll('.msg--assistant .msg__status')].map((n) => n.textContent),
contentLen: [...document.querySelectorAll('.msg--assistant .msg__content')].pop()?.textContent.length ?? 0
})`)
final = JSON.parse(probe.result.value)
if (final.stopGone && final.sendBack) break
}
console.log('终态:', JSON.stringify(final))
const ok = final.sendBack && final.stopGone
console.log(ok ? '\n✅ 取消链路:停止按钮消失、发送按钮恢复' : '\n❌ 取消链路未恢复正常')
ws.close()
process.exit(ok ? 0 : 1)
}
main().catch((e) => {
console.error('失败:', e.message)
process.exit(1)
})