Files
crm-ai/scripts/e2e-widget.js
T

112 lines
4.4 KiB
JavaScript

#!/usr/bin/env node
'use strict';
/**
* 端到端验证:模拟 widget 访客发起对话 → Automation 分配给 AI Assistant → 等待 AI 回复。
* 用法:node scripts/e2e-widget.js "What is the minimum order value?"
*/
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
const question = process.argv.slice(2).join(' ') || 'What is the minimum order value?';
function loadEnv(file) {
const out = {};
for (const line of fs.readFileSync(file, 'utf8').split('\n')) {
const t = line.trim();
if (!t || t.startsWith('#')) continue;
const eq = t.indexOf('=');
if (eq < 0) continue;
let v = t.slice(eq + 1).trim();
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
out[t.slice(0, eq).trim()] = v;
}
return out;
}
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function main() {
const env = loadEnv(path.join(ROOT, '.env'));
const base = (env.LIBREDESK_BASE_URL || 'http://localhost:9000').replace(/\/+$/, '');
// 1. admin session: 找到 Website inbox 的 UUID
const loginRes = await fetch(`${base}/api/v1/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: env.LIBREDESK_SYSTEM_EMAIL || 'System', password: env.LIBREDESK_SYSTEM_USER_PASSWORD }),
});
const loginCookies = (loginRes.headers.getSetCookie ? loginRes.headers.getSetCookie() : [])
.map((c) => c.split(';')[0]);
const adminCookie = loginCookies.join('; ');
const csrf = (loginCookies.find((c) => c.startsWith('csrf_token=')) || '').split('=')[1];
if (!loginRes.ok) throw new Error(`admin login failed: HTTP ${loginRes.status}`);
const inboxesRes = await fetch(`${base}/api/v1/inboxes`, { headers: { Cookie: adminCookie } });
const inboxes = (await inboxesRes.json()).data || [];
const inbox = inboxes.find((i) => i.channel === 'livechat' && i.name === 'Website');
if (!inbox) throw new Error('未找到 Website livechat inbox,先跑 scripts/setup-defaults.js');
console.log(`inbox: #${inbox.id} ${inbox.uuid}`);
// 2. widget init(匿名访客,发第一条消息)
const initRes = await fetch(`${base}/api/v1/widget/chat/conversations/init`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Libredesk-Inbox-ID': inbox.uuid },
body: JSON.stringify({ message: question }),
});
const initText = await initRes.text();
if (!initRes.ok) throw new Error(`widget init failed: HTTP ${initRes.status} ${initText.slice(0, 300)}`);
const init = JSON.parse(initText).data;
const token = init.session_token;
const convUUID = init.conversation.uuid;
console.log(`conversation: ${convUUID}`);
console.log(`visitor message: ${question}`);
// 3. 轮询等待 AI 回复(用管理端 API 读消息,widget API 的返回结构不同)
const deadline = Date.now() + 120000;
let lastCount = 0;
let reply = null;
while (Date.now() < deadline) {
await sleep(3000);
const res = await fetch(`${base}/api/v1/conversations/${convUUID}/messages`, {
headers: { Cookie: adminCookie },
});
if (!res.ok) continue;
const body = await res.json();
const messages = (body.data && (body.data.results || body.data.messages)) || [];
if (messages.length !== lastCount) {
lastCount = messages.length;
const convRes = await fetch(`${base}/api/v1/conversations/${convUUID}`, { headers: { Cookie: adminCookie } });
const conv = (await convRes.json()).data || {};
console.log(` [${new Date().toLocaleTimeString()}] messages=${messages.length} assigned_user_id=${conv.assigned_user_id} status=${conv.status}`);
}
const aiMsg = messages
.filter((m) => m.type === 'outgoing' && m.sender_type === 'agent' && !m.private)
.slice(-1)[0];
if (aiMsg) { reply = aiMsg; break; }
}
if (!reply) {
console.log('\n超时:120 秒内没有收到 AI 回复。检查 docker logs crmnew-libredesk | grep -i ai');
process.exit(2);
}
console.log('\n===== AI 回复 =====');
console.log(reply.content || reply.text_content || '(empty)');
console.log('\n===== 元信息 =====');
console.log(JSON.stringify({
message_id: reply.id,
sender_type: reply.sender_type,
type: reply.type,
private: reply.private,
meta: reply.meta,
created_at: reply.created_at,
}, null, 2));
}
main().catch((err) => {
console.error('[e2e-widget]', err.message);
process.exit(1);
});