diff --git a/tests/lib/dashboard/dashboard.test.mjs b/tests/lib/dashboard/dashboard.test.mjs index 6221bcaf..7a130b26 100644 --- a/tests/lib/dashboard/dashboard.test.mjs +++ b/tests/lib/dashboard/dashboard.test.mjs @@ -148,6 +148,14 @@ test('scaleSweepMetrics maps fps/load/etc with context', () => { assert.equal(fps.context.quality, 'HIGH'); assert.equal(fps.better, 'higher'); }); +test('scaleSweepMetrics drops -1 not-measured sentinels', () => { + const m = scaleSweepMetrics({ size: 500, quality: 'ULTRA', interactionFps: -1, loadMs: 1200, settleMs: 0, avgTickMs: -1, queryP95Ms: -1, rmsFromSavedPx: -1, fps: -1, timestampISO: '2026-06-19T00:00:00Z' }); + for (const metric of ['graph.interactionFps', 'graph.avgTickMs', 'graph.queryP95Ms', 'graph.driftPx', 'graph.idleFps']) { + assert.equal(m.find((x) => x.metric === metric), undefined, `${metric} sentinel should be dropped`); + } + assert.equal(m.find((x) => x.metric === 'graph.loadMs').value, 1200); + assert.equal(m.find((x) => x.metric === 'graph.settleMs').value, 0); +}); test('largeGraphMetrics + physicsMetrics + vlmMetrics', () => { assert.equal(largeGraphMetrics({ quality: 'HIGH', graph: 'g', idleFps: 60, panFps: 55, dragFps: 50, zoomFps: 48, zoomedInDragFps: 40 }, 123).length, 5); const p = physicsMetrics({ graphId: 'g', summary: { settleSeconds: 4, overlapAfterOrganize: 0, labelOverlapAfter: 1 } }, 9); diff --git a/tests/lib/dashboard/metrics.mjs b/tests/lib/dashboard/metrics.mjs index fa8a7868..a8094180 100644 --- a/tests/lib/dashboard/metrics.mjs +++ b/tests/lib/dashboard/metrics.mjs @@ -36,14 +36,17 @@ export function reportMetrics(report) { export function scaleSweepMetrics(json) { const ts = Date.parse(json.timestampISO || '') || 0; const ctx = { graphSize: json.size, quality: json.quality }; + // The scale-sweep harness writes -1 for any measurement it could not take; + // those are not real data points, so keep them out of the trend charts. + const measured = (v) => (typeof v === 'number' && v < 0 ? NaN : v); return [ - pt('graph.interactionFps', json.interactionFps, 'fps', 'higher', ctx, ts), - pt('graph.loadMs', json.loadMs, 'ms', 'lower', ctx, ts), - pt('graph.settleMs', json.settleMs, 'ms', 'lower', ctx, ts), - pt('graph.avgTickMs', json.avgTickMs, 'ms', 'lower', ctx, ts), - pt('graph.queryP95Ms', json.queryP95Ms, 'ms', 'lower', ctx, ts), - pt('graph.driftPx', json.rmsFromSavedPx, 'px', 'lower', ctx, ts), - pt('graph.idleFps', json.fps, 'fps', 'higher', ctx, ts), + pt('graph.interactionFps', measured(json.interactionFps), 'fps', 'higher', ctx, ts), + pt('graph.loadMs', measured(json.loadMs), 'ms', 'lower', ctx, ts), + pt('graph.settleMs', measured(json.settleMs), 'ms', 'lower', ctx, ts), + pt('graph.avgTickMs', measured(json.avgTickMs), 'ms', 'lower', ctx, ts), + pt('graph.queryP95Ms', measured(json.queryP95Ms), 'ms', 'lower', ctx, ts), + pt('graph.driftPx', measured(json.rmsFromSavedPx), 'px', 'lower', ctx, ts), + pt('graph.idleFps', measured(json.fps), 'fps', 'higher', ctx, ts), ].filter(Boolean); }