106 lines
3.4 KiB
JavaScript
106 lines
3.4 KiB
JavaScript
/**
|
||
* 端到端验证:通过 UI 发送一条消息,等待回复,报告结果。
|
||
*
|
||
* 用法:node scripts/e2e-send.mjs <port> <消息> [超时秒]
|
||
*/
|
||
|
||
const port = Number(process.argv[2] ?? 9335)
|
||
const message = process.argv[3] ?? '列出工作目录里有哪些文件'
|
||
const timeoutMs = Number(process.argv[4] ?? 180) * 1000
|
||
|
||
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) 填入消息(React 受控组件需用原生 setter)
|
||
const fill = `(() => {
|
||
const el = document.querySelector('.composer__input');
|
||
if (!el) return 'NO_INPUT';
|
||
const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
|
||
setter.call(el, ${JSON.stringify(message)});
|
||
el.dispatchEvent(new Event('input', { bubbles: true }));
|
||
return 'OK';
|
||
})()`
|
||
const filled = await evaluate(fill)
|
||
console.log('填入:', filled.result.value)
|
||
|
||
// 2) 点击发送
|
||
const click = `(() => {
|
||
const btn = document.querySelector('.btn-send');
|
||
if (!btn) return 'NO_BUTTON';
|
||
if (btn.disabled) return 'DISABLED';
|
||
btn.click();
|
||
return 'SENT';
|
||
})()`
|
||
const clicked = await evaluate(click)
|
||
console.log('发送:', clicked.result.value)
|
||
if (clicked.result.value !== 'SENT') {
|
||
ws.close()
|
||
process.exit(1)
|
||
}
|
||
|
||
// 3) 轮询等待回复完成
|
||
const started = Date.now()
|
||
let last = ''
|
||
while (Date.now() - started < timeoutMs) {
|
||
await new Promise((r) => setTimeout(r, 1200))
|
||
const probe = await evaluate(`(() => {
|
||
const msgs = [...document.querySelectorAll('.msg--assistant')];
|
||
const lastMsg = msgs[msgs.length - 1];
|
||
const status = lastMsg ? lastMsg.querySelector('.msg__status')?.textContent ?? '' : '';
|
||
return JSON.stringify({
|
||
count: msgs.length,
|
||
content: lastMsg ? lastMsg.querySelector('.msg__content')?.textContent.slice(0, 400) : '',
|
||
status
|
||
});
|
||
})()`)
|
||
const state = JSON.parse(probe.result.value)
|
||
last = state.content
|
||
if (state.status && /已完成|失败|已取消|error|done/i.test(state.status)) {
|
||
console.log('状态:', state.status)
|
||
console.log('回复:', state.content)
|
||
ws.close()
|
||
return
|
||
}
|
||
}
|
||
console.log('超时,最后内容:', last)
|
||
ws.close()
|
||
}
|
||
|
||
main().catch((e) => {
|
||
console.error('失败:', e.message)
|
||
process.exit(1)
|
||
})
|