/** * 端到端验证:通过 UI 发送一条消息,等待回复,报告结果。 * * 用法:node scripts/e2e-send.mjs <消息> [超时秒] */ 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) })