forked from admin/PlantAI-rag-new
- BFF 薄适配层(FastAPI,8090):QA 代理/SSE 中继/Neo4j 直连/抽取代理/会话持久化 - Vue3 前端:五相位检索流水线、文献来源与证据链、知识图谱、提取任务四 tab - 四档响应式(桌面/笔记本/平板/手机)、深浅主题 - 81 个 FR 全部实现,测试追溯矩阵 81/81,后端 121 测/前端 156 测 - 真实凭据已脱敏,运行时配置在 backend/.env(不入 git) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
76 lines
3.2 KiB
JavaScript
76 lines
3.2 KiB
JavaScript
// deploy/m2_probe5.js —— PipelineCard props 与渲染链探针(PM 使用,在远端运行)
|
||
// 读组件实例 props(vnode.props.block)并比较与 pinia store 的引用/状态。
|
||
const { chromium } = require('playwright');
|
||
|
||
(async () => {
|
||
const browser = await chromium.launch();
|
||
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } });
|
||
const page = await ctx.newPage();
|
||
await page.goto('http://127.0.0.1:5180', { waitUntil: 'domcontentloaded' });
|
||
await page.waitForSelector('.app-shell', { timeout: 15000 });
|
||
await page.click('.new-task-btn');
|
||
await page.waitForSelector('.composer textarea', { timeout: 15000 });
|
||
await page.fill('.composer textarea', 'SPL gene family in rice');
|
||
await page.click('.send-btn');
|
||
|
||
const t0 = Date.now();
|
||
let last = '';
|
||
while (Date.now() - t0 < 150000) {
|
||
const snap = await page.evaluate(() => {
|
||
const cardEl = document.querySelector('.pipeline-card');
|
||
if (!cardEl) return { noCard: true };
|
||
// 沿 vnode 链找组件实例
|
||
let vnode = cardEl.__vueParentComponent;
|
||
const found = [];
|
||
for (let i = 0; i < 6 && vnode; i++) {
|
||
const p = vnode.props;
|
||
if (p && p.block) {
|
||
const b = p.block;
|
||
found.push({
|
||
comp: vnode.type?.name || vnode.type?.__name || 'anon',
|
||
blockStatus: b.status,
|
||
steps: (b.steps || []).map((s) => `${s.phase}:${s.state}`).join(','),
|
||
isReactiveProxy: !!b.__v_raw || typeof b.status !== 'string' || false,
|
||
});
|
||
// 读原始 store 对象比较(raw)
|
||
const raw = b.__v_raw || b;
|
||
found[found.length - 1].rawStatus = raw.status;
|
||
found[found.length - 1].rawSteps = (raw.steps || []).map((s) => s.state).join(',');
|
||
break;
|
||
}
|
||
vnode = vnode.parent;
|
||
}
|
||
// pinia 直接读
|
||
let storeBlock = null;
|
||
const rootEl = document.querySelector('#app') || document.querySelector('.app-shell');
|
||
let n = rootEl;
|
||
let app = null;
|
||
while (n && !app) { if (n.__vue_app__) app = n.__vue_app__; n = n.__vueParentComponent?.parent || null; }
|
||
if (app) {
|
||
const pinia = app.config.globalProperties.$pinia;
|
||
const chat = pinia._s.get('chat');
|
||
const aMsg = [...chat.messages].reverse().find((m) => m.role === 'assistant');
|
||
const sb = aMsg?.blocks?.[0];
|
||
if (sb) storeBlock = { status: sb.status, steps: sb.steps.map((s) => s.state).join(',') };
|
||
}
|
||
return { found, storeBlock, domText: cardEl.textContent.slice(0, 60) };
|
||
});
|
||
const key = JSON.stringify(snap);
|
||
if (key !== last) {
|
||
last = key;
|
||
console.log(`T+${Math.round((Date.now() - t0) / 1000)}s`, JSON.stringify(snap, null, 1));
|
||
}
|
||
// 流结束后看最终状态
|
||
const doneNow = await page.evaluate(() => {
|
||
const rootEl = document.querySelector('#app');
|
||
let n = rootEl; let app = null;
|
||
while (n && !app) { if (n.__vue_app__) app = n.__vue_app__; n = n.__vueParentComponent?.parent || null; }
|
||
if (!app) return false;
|
||
return !app.config.globalProperties.$pinia._s.get('chat').busy;
|
||
});
|
||
if (doneNow && Date.now() - t0 > 30000) break;
|
||
await new Promise((r) => setTimeout(r, 2500));
|
||
}
|
||
await browser.close();
|
||
})();
|