'use strict'; /* 缓存命中验证:连续两次「分析周边目的地」,第二次应秒回并出现缓存提示条 */ const fs = require('fs'); const path = require('path'); const OUT = path.join(__dirname, '..', 'preview'); const BASE = process.env.BASE || 'http://127.0.0.1:8788'; const AMAP = process.env.AMAP_KEY || ''; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); class CDP { constructor(ws) { this.ws = ws; this.id = 0; this.waits = new Map(); } static async connect(url) { const ws = new WebSocket(url); await new Promise((res, rej) => { ws.onopen = res; ws.onerror = rej; }); const c = new CDP(ws); ws.onmessage = (e) => { const m = JSON.parse(e.data); if (m.id && c.waits.has(m.id)) { c.waits.get(m.id)(m); c.waits.delete(m.id); } }; return c; } send(method, params = {}, sessionId) { const id = ++this.id; return new Promise((res) => { this.waits.set(id, res); this.ws.send(JSON.stringify({ id, method, params, sessionId })); }); } } (async () => { const { webSocketDebuggerUrl } = await (await fetch('http://127.0.0.1:9333/json/version')).json(); const browser = await CDP.connect(webSocketDebuggerUrl); const { result: { targetId } } = await browser.send('Target.createTarget', { url: 'about:blank' }); const { result: { sessionId } } = await browser.send('Target.attachToTarget', { targetId, flatten: true }); const S = (m, p) => browser.send(m, p, sessionId); await S('Page.enable'); await S('Runtime.enable'); await S('Emulation.setDeviceMetricsOverride', { width: 1360, height: 1400, deviceScaleFactor: 1, mobile: false }); const evalJs = async (expr) => { const r = await S('Runtime.evaluate', { expression: expr, awaitPromise: true, returnByValue: true }); if (r.result?.exceptionDetails) throw new Error(JSON.stringify(r.result.exceptionDetails).slice(0, 400)); return r.result?.result?.value; }; await S('Page.navigate', { url: BASE }); await sleep(2500); if (AMAP) { await evalJs(`(() => { const raw = JSON.parse(localStorage.getItem('tripsense.ai') || '{}'); raw.amapKey = ${JSON.stringify(AMAP)}; localStorage.setItem('tripsense.ai', JSON.stringify(raw)); return true; })()`); await S('Page.reload'); await sleep(2500); } await evalJs(`(() => { const el = document.querySelector('#originInput'); el.value = '柳州'; el.dispatchEvent(new Event('input', { bubbles: true })); return true; })()`); for (let i = 0; i < 20; i++) { await sleep(1000); const ok = await evalJs(`(() => { const it = document.querySelector('#suggestBox .suggest-item'); if (it && !document.querySelector('#suggestBox').classList.contains('hidden')) { it.click(); return true; } return false; })()`); if (ok) break; } console.log('origin =', await evalJs(`document.querySelector('#originInput')?.value`)); const runAnalyze = async (label) => { const t0 = Date.now(); await evalJs(`(() => { const cards = document.querySelectorAll('.dest-card'); cards.forEach(c => c.remove()); const b = document.querySelector('#analyzeBtn'); if (b) b.click(); return true; })()`); let n = 0; for (let i = 0; i < 40; i++) { await sleep(500); n = await evalJs(`document.querySelectorAll('.dest-card').length`); if (n) break; } const ms = Date.now() - t0; console.log(`\n[${label}] candidates=${n} elapsed=${ms}ms`); console.log(' progress =', JSON.stringify(await evalJs(`(document.querySelector('#progress')?.innerText||'').slice(0,200)`))); console.log(' cacheBar hidden =', await evalJs(`document.querySelector('#cacheBar')?.classList.contains('hidden')`)); console.log(' cacheBar text =', JSON.stringify(await evalJs(`(document.querySelector('#cacheBar')?.innerText||'').slice(0,300)`))); return ms; }; const cold = await runAnalyze('第一次(冷)'); await sleep(1500); const warm = await runAnalyze('第二次(应命中缓存)'); // 点「重新获取最新数据」 await evalJs(`document.querySelector('#cacheBar button, #cacheBar .link-btn')?.click()`); for (let i = 0; i < 60; i++) { await sleep(500); const hit = await evalJs(`document.querySelector('#cacheBar')?.classList.contains('hidden')`); if (hit) break; } console.log('\n=== 强制刷新后 ==='); console.log(' cacheBar hidden =', await evalJs(`document.querySelector('#cacheBar')?.classList.contains('hidden')`)); console.log(' cacheBar text =', JSON.stringify(await evalJs(`(document.querySelector('#cacheBar')?.innerText||'').slice(0,200)`))); console.log(' progress =', JSON.stringify(await evalJs(`(document.querySelector('#progress')?.innerText||'').slice(0,120)`))); console.log(' candidates =', await evalJs(`document.querySelectorAll('.dest-card').length`)); // 服务端缓存统计 const health = await (await fetch(BASE + '/api/health')).json(); console.log('\nserver cache =', JSON.stringify(health.cache)); const shot = await S('Page.captureScreenshot', { format: 'png' }); if (shot.result?.data) fs.writeFileSync(path.join(OUT, 'preview-cache.png'), Buffer.from(shot.result.data, 'base64')); console.log('screenshot -> preview/preview-cache.png'); browser.ws.close(); process.exit(0); })().catch((e) => { console.error('FAIL', e.message); process.exit(1); });