Ctrl K
ComunidadGuías…

scripts/dashboard-cdp-check.mjs

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env node
/**
 * Dashboard CDP smoke-check (NOT in npm test — needs Chrome + a running server).
 *
 * Boots a throwaway gateway on an alt port with DASHBOARD_ALLOW_NO_AUTH=1, loads
 * /dashboard in headless Chrome, drives the page over a dependency-free CDP
 * WebSocket, and asserts: (a) zero console errors / uncaught exceptions, (b) no
 * unresolved i18n keys leaked into the body text, (c) a caller-supplied list of
 * DOM assertions (id present / absent, textContent). Screenshots don't render in
 * this environment, so we assert on computed content, not pixels.
 *
 * Usage: node scripts/dashboard-cdp-check.mjs
 * Exit 0 = all pass, 1 = failure.
 */
import { spawn } from 'node:child_process';
import { setTimeout as sleep } from 'node:timers/promises';
import net from 'node:net';
import { existsSync } from 'node:fs';

const PORT = 3995, DBG = 9227;
const CHROME_CANDIDATES = [
  'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
  'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
  '/usr/bin/google-chrome', '/usr/bin/chromium',
];
const CHROME = CHROME_CANDIDATES.find(p => existsSync(p));
if (!CHROME) { console.error('Chrome not found; skipping CDP check.'); process.exit(0); }

const env = { ...process.env, PORT: String(PORT), DASHBOARD_ALLOW_NO_AUTH: '1', HOST: '127.0.0.1', WINDSURFAPI_NO_OPEN: '1', API_KEY: 'cdp-test', DEVIN_CONNECT: '0' };
const srv = spawn(process.execPath, ['src/index.js'], { env, stdio: 'ignore' });
await sleep(4000);
const chrome = spawn(CHROME, ['--headless=new', '--disable-gpu', `--remote-debugging-port=${DBG}`, '--no-first-run', '--no-default-browser-check', '--user-data-dir=' + (process.env.TEMP || '/tmp') + '/dash-cdp-prof', 'about:blank'], { stdio: 'ignore' });
await sleep(2500);
const cleanup = () => { try { chrome.kill(); } catch {} try { srv.kill(); } catch {} };

function connectWS(url) {
  return new Promise((resolve, reject) => {
    const u = new URL(url);
    const sock = net.connect(Number(u.port), u.hostname, () => {
      sock.write(`GET ${u.pathname}${u.search} HTTP/1.1\r\nHost: ${u.host}\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: ${Buffer.from(Math.random().toString(36)).toString('base64')}\r\nSec-WebSocket-Version: 13\r\n\r\n`);
    });
    let hs = false, buf = Buffer.alloc(0); const ls = [];
    sock.on('data', (d) => {
      if (!hs) { buf = Buffer.concat([buf, d]); const i = buf.indexOf('\r\n\r\n'); if (i >= 0) { hs = true; buf = buf.slice(i + 4); resolve(api); if (buf.length) frames(); } return; }
      buf = Buffer.concat([buf, d]); frames();
    });
    sock.on('error', reject);
    function frames() {
      while (buf.length >= 2) {
        const l0 = buf[1] & 0x7f; let off = 2, len = l0;
        if (l0 === 126) { len = buf.readUInt16BE(2); off = 4; } else if (l0 === 127) { len = Number(buf.readBigUInt64BE(2)); off = 10; }
        if (buf.length < off + len) break;
        const pl = buf.slice(off, off + len); buf = buf.slice(off + len);
        try { const m = JSON.parse(pl.toString()); ls.forEach(f => f(m)); } catch {}
      }
    }
    function send(o) {
      const data = Buffer.from(JSON.stringify(o)); const h = [0x81];
      if (data.length < 126) h.push(0x80 | data.length);
      else if (data.length < 65536) h.push(0x80 | 126, (data.length >> 8) & 0xff, data.length & 0xff);
      else h.push(0x80 | 127, 0, 0, 0, 0, (data.length >>> 24) & 0xff, (data.length >> 16) & 0xff, (data.length >> 8) & 0xff, data.length & 0xff);
      sock.write(Buffer.concat([Buffer.from(h), Buffer.from([0, 0, 0, 0]), data]));
    }
    const api = { send, on: (f) => ls.push(f), close: () => sock.destroy() };
  });
}

let id = 0; const pending = new Map(); const errors = [];
let failed = false;
function pass(name, ok, detail) { console.log(`  ${ok ? 'PASS' : 'FAIL'}  ${name}${detail ? ' — ' + detail : ''}`); if (!ok) failed = true; }
try {
  const tab = await fetch(`http://127.0.0.1:${DBG}/json/new?http://127.0.0.1:${PORT}/dashboard`, { method: 'PUT' }).then(r => r.json());
  const ws = await connectWS(tab.webSocketDebuggerUrl);
  ws.on((m) => {
    if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id); }
    if (m.method === 'Runtime.exceptionThrown') errors.push('EXC: ' + (m.params?.exceptionDetails?.exception?.description || m.params?.exceptionDetails?.text || '?'));
    if (m.method === 'Runtime.consoleAPICalled' && m.params?.type === 'error') errors.push('CONSOLE: ' + (m.params.args || []).map(a => a.value || a.description || '').join(' '));
  });
  const cmd = (method, params) => new Promise((res) => { const i = ++id; pending.set(i, res); ws.send({ id: i, method, params: params || {} }); });
  await cmd('Runtime.enable'); await cmd('Page.enable');
  await sleep(3500);
  const ev = async (expr) => (await cmd('Runtime.evaluate', { expression: expr, returnByValue: true })).result?.value;

  // Assertions for the declutter change:
  pass('overview trend section removed', (await ev(`!document.getElementById('overview-trend-section')`)) === true);
  pass('overview trend canvas removed', (await ev(`!document.getElementById('overview-trend-canvas')`)) === true);
  pass('model pie canvas removed', (await ev(`!document.getElementById('model-pie-canvas')`)) === true);
  pass('model stats aggregate table present', (await ev(`!!document.getElementById('stats-agg-table')`)) === true);
  const rawKeys = await ev(`(document.body.innerText.match(/\\b(section|action|status|confirm|toast|overview|table|aria)\\.[a-zA-Z][a-zA-Z0-9.]+/g)||[]).slice(0,6)`);
  pass('no unresolved i18n keys in body', Array.isArray(rawKeys) && rawKeys.length === 0, JSON.stringify(rawKeys));
  pass('zero console errors / exceptions', errors.length === 0, errors.slice(0, 5).join(' | '));
  ws.close();
} catch (e) { pass('CDP driver ran', false, e.message); }
cleanup(); await sleep(400);
console.log(failed ? '\nFAILED' : '\nAll dashboard CDP checks passed.');
process.exit(failed ? 1 : 0);