- build-standalone-fixed.cjs: reads from 4 real sources (agents md, kilo-meta.json, model-benchmarks-verified.json, agent-versions.json); computes recommendations dynamically - build-standalone-direct.cjs: direct data export + HTML embed pipeline - dashboard-smoke-test.ts: Playwright E2E smoke test covering all 6 tabs - model-benchmarks-verified.json: verified IF scores from artificialanalysis.ai for 15 models (SWE-bench unverifiable → null) - agent-versions.json: 347 git history entries extracted for 34 agents - kilo-meta.json: prompt-optimizer → qwen3.5-122b, memory-manager → deepseek-v4-pro-max - index.html: Recommendations tab rendering updated for dynamic data - Dockerfile + docker-compose.yml: mount-driven build, no image rebuild for data changes - README.md: updated dashboard docs and verified benchmark sources
169 lines
4.7 KiB
TypeScript
169 lines
4.7 KiB
TypeScript
#!/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<void> {
|
|
await page.click(`button[onclick="switchTab('${tabId}')"]`);
|
|
await page.waitForTimeout(800);
|
|
}
|
|
|
|
async function runChecks(page: Page, tabId: string, checks: string[]): Promise<string[]> {
|
|
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); });
|