// Optional large Overview charts: real status schema, counter resets, no polling. import assert from 'node:assert/strict'; import fs from 'node:fs'; import vm from 'node:vm'; const source = fs.readFileSync('src/admin/web/app/overview-wide-metrics.js','utf8'); const registry = JSON.parse(fs.readFileSync('src/admin/web/dashboard-widgets.json','utf8')); const ids = registry.filter(w => w.id.startsWith('wide_')).map(w => w.id); assert.equal(ids.length, 7); assert(ids.every(id => registry.find(w => w.id === id).default_hidden)); const nodes = new Map(ids.map(id => [id,{ hidden: false }])); const fields = new Map(); function $(id) { if (!fields.has(id)) fields.set(id,{ textContent:'-', d:'', setAttribute(name,value) {this[name]=value;} }); return fields.get(id); } let now = 1000; const ctx = {window:{},currentPanel:'overview',performance:{now:()=>now},fmt:(n,places=0)=>Number(n).toFixed(places),fmtBytes:n=>`${n} B`,$, document:{querySelector:selector=>nodes.get(selector.match(/data-dashboard-widget="([^"]+)"/)?.[1])||null}}; vm.createContext(ctx);vm.runInContext(source,ctx); const render=ctx.window.rustProxyWideOverview.render; const text=id=>$(id).textContent; const baseline = { revision:1, metrics:{ http1_requests:50,http2_requests:30,http3_requests:20,active_connections:9,websocket_active:3,mean_duration_ms:15 }, system:{cpu_percent:45,process_cpu_percent:16,process_rss_bytes:1500}, cache:{hits:20,misses:10,resident_bytes:220}, backend_pools:[{members:[{status:'up',active:1,last_probe_ms:100,latency_ms:12,health_mode:'active'},{status:'down',active:0}]}]}; render(baseline); assert.equal(text('overview-wide-wide_protocol_rates-summary'),'-'); assert.equal(text('overview-wide-wide_cpu-system-value'),'45.0%'); assert.equal(text('overview-wide-wide_memory-process-value'),'1500 B'); assert.equal(text('overview-wide-wide_backend_health-down-value'),'1'); assert.equal(text('overview-wide-wide_latency-probe-value'),'12.0 ms'); now = 3000; const second=structuredClone(baseline); second.metrics.http1_requests=70; second.metrics.http2_requests=40; second.metrics.http3_requests=22; second.cache.hits=30; second.cache.misses=18; render(second); assert.equal(text('overview-wide-wide_protocol_rates-h1-value'),'10.0/s'); assert.equal(text('overview-wide-wide_protocol_rates-h2-value'),'5.0/s'); assert.equal(text('overview-wide-wide_protocol_rates-h3-value'),'1.0/s'); assert.equal(text('overview-wide-wide_cache_activity-hits-value'),'5.0/s'); assert.equal(text('overview-wide-wide_cache_activity-misses-value'),'4.0/s'); assert.equal(text('overview-wide-wide_connections-open-value'),'9'); now = 4000; render({...second,metrics:{...second.metrics,http1_requests:0,http2_requests:0,http3_requests:0},cache:{...second.cache,hits:0,misses:0}}); assert.equal(text('overview-wide-wide_protocol_rates-h1-value'),'-', 'counter resets must not create negative traffic'); assert.equal(text('overview-wide-wide_cache_activity-hits-value'),'-'); nodes.get('wide_cpu').hidden=true; now=5000;render({...second, system:{}}); assert.equal(text('overview-wide-wide_cpu-system-value'),'45.0%', 'hidden chart should not re-render'); nodes.get('wide_cpu').hidden=false; now=6000;render({metrics:{},system:{},cache:{}}); assert.equal(text('overview-wide-wide_cpu-system-value'),'-', 'unavailable metrics are not zero'); assert.equal(text('overview-wide-wide_backend_health-up-value'),'-', 'missing backend array is no data'); ctx.window.rustProxyWideOverview.reset(); for(let i=0;i<75;i++){now+=1000;render({...baseline,system:{...baseline.system,cpu_percent:i}});} assert(source.includes('const limit = 60')); assert(source.includes('if (!node || node.hidden)')); assert(!/fetch\s*\(|setInterval\s*\(/.test(source),'no extra API calls or polling'); const css=fs.readFileSync('src/admin/web/style.css','utf8'); const aligned = fs.readFileSync('src/admin/web/app/overview-masonry.js', 'utf8'); assert.match(css, /\.dashboard-overview-grid\.is-aligned\s*\{[^}]*grid-auto-rows:\s*auto;/); assert.match(css, /\.dashboard-overview-grid\.is-aligned\s*\{[^}]*grid-auto-flow:\s*row;/); assert.match(css, /\.dashboard-overview-grid\.is-aligned\s*\{[^}]*gap:\s*var\(--ui-gap\);/); assert.match(css, /\.dashboard-overview-grid\.is-aligned\s*\{[^}]*align-items:\s*stretch;/); assert.match(css, /#panel-overview\s*\{\s*container:\s*overview-canvas \/ inline-size;/); assert.match(css, /@container overview-canvas \(max-width:\s*700px\)/); assert.match(css, /\.dashboard-overview-grid\.is-aligned\s*>\s*\[data-dashboard-widget\]\s*\{[^}]*align-self:\s*stretch;/); assert.match(css, /\.dashboard-overview-grid\.is-aligned\s*>\s*\[data-dashboard-widget\]\.overview-row-start\s*\{[^}]*grid-column-start:\s*1;/); assert.match(aligned, /node\.classList\.remove\("overview-row-start"\)/); assert.match(aligned, /node\.classList\.add\("overview-row-start"\)/); assert.match(aligned, /grid\.classList\.remove\("is-packed"\)/); assert(!/style\.position\s*=\s*["']absolute["']/.test(aligned)); assert(!aligned.includes('fetch(') && !aligned.includes('setInterval(')); console.log('PASS: 7 optional wide charts, regular rows, no uneven masonry');