#!/usr/bin/env bun /** * Dashboard smoke test - navigates all tabs and reports console errors. * Run: bun run agent-evolution/scripts/dashboard-smoke-test.ts */ import { chromium, type Page } from 'playwright'; const TARGET = process.env.TARGET_URL || 'http://localhost:3003'; interface TabResult { name: string; selector: string; errors: string[]; checks: string[]; } async function clickTab(page: Page, tabId: string): Promise { await page.click(`button[onclick="switchTab('${tabId}')"]`); await page.waitForTimeout(800); } async function runChecks(page: Page, tabId: string, checks: string[]): Promise { const results: string[] = []; for (const check of checks) { try { const el = await page.$(check); results.push(el ? ` ✅ ${check}` : ` ❌ MISSING: ${check}`); } catch (e) { results.push(` ❌ ERROR: ${check} | ${String(e).slice(0, 80)}`); } } return results; } async function main() { console.log(`Dashboard Smoke Test - ${TARGET}\n`); const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ viewport: { width: 1280, height: 720 } }); const page = await context.newPage(); const allErrors: string[] = []; const allWarnings: string[] = []; page.on('console', msg => { const t = msg.type(); const txt = msg.text(); if (t === 'error') allErrors.push(txt); else if (t === 'warning') allWarnings.push(txt); }); page.on('pageerror', err => { allErrors.push(`PAGE ERROR: ${err.message} ${err.stack?.slice(0, 200) || ''}`); }); page.on('requestfailed', req => { const url = req.url(); if (!url.includes('favicon')) { allErrors.push(`NETWORK: ${req.method()} ${url} | ${req.failure()?.errorText}`); } }); // --- Tab definitions --- const tabs = [ { name: 'Overview', id: 'overview', checks: [ '#statsRow .stat-card', '#recentTimeline .timeline-item', '#recAgents .agent-card', ], }, { name: 'All Agents', id: 'agents', checks: [ '#agentsByCategory .category-section', '#agentSearch', '.agents-grid .agent-card', ], }, { name: 'Timeline', id: 'history', checks: [ '#fullTimeline .timeline-item', '.timeline-wrap .timeline-title', ], }, { name: 'Recommendations', id: 'recommendations', checks: [ '#allRecommendations .rec-card', ], }, { name: 'Heatmap', id: 'heatmap', /* Note: heatmap uses hmTable which may throw if model_benchmarks is empty */ checks: [ '#hmTable tbody tr', '.hm-legend-track', ], }, // Impact tab is NOT in tab bar (click is on onclick="switchTab('impact')") { name: 'Impact', id: 'impact', checks: [ '#agentScoreChart', '#modelDistChart', '#migrationImpactChart', ], }, ]; const results: TabResult[] = []; for (const tab of tabs) { await page.goto(`${TARGET}/`, { waitUntil: 'domcontentloaded', timeout: 30000 }); await page.waitForTimeout(1500); if (tab.id !== 'overview') { await clickTab(page, tab.id); } const checks = await runChecks(page, tab.id, tab.checks); results.push({ name: tab.name, selector: tab.id, errors: [...allErrors], checks, }); allErrors.length = 0; allWarnings.length = 0; } await browser.close(); // --- Report --- console.log('═══════════════════════════════════════════════════'); console.log(' Smoke Test Results'); console.log('═══════════════════════════════════════════════════\n'); let totalIssues = 0; for (const r of results) { const issues = r.errors.filter(e => !e.includes('favicon')); totalIssues += issues.length; console.log(`\n[${r.name}]`); console.log(r.checks.join('\n')); if (issues.length > 0) { console.log(' ❌ Console errors:'); issues.forEach(e => console.log(` ${e.slice(0, 120)}`)); } } console.log('\n═══════════════════════════════════════════════════'); console.log(` Total issues: ${totalIssues}`); console.log('═══════════════════════════════════════════════════'); process.exit(totalIssues > 0 ? 1 : 0); } main().catch(e => { console.error(e); process.exit(1); });