diff --git a/public/observer-detail.js b/public/observer-detail.js index f8108c9a..94e59f1a 100644 --- a/public/observer-detail.js +++ b/public/observer-detail.js @@ -311,8 +311,12 @@ window.ObserverDetailNaiveBanner = { data: { labels: cached.metrics.map(function(m) { return new Date(m.timestamp).toLocaleString(); }), datasets: [ - { label: 'SNR (dB)', data: cached.metrics.map(function(m) { return m.snr; }), borderColor: 'var(--accent)', yAxisID: 'y', tension: 0.2 }, - { label: 'Heard (s ago)', data: cached.metrics.map(function(m) { return m.heardSecsAgo; }), borderColor: 'var(--status-yellow)', yAxisID: 'y1', tension: 0.2 }, + // Canvas can't resolve CSS var() as a strokeStyle -- Chart.js + // silently falls back to black for both lines if given one + // (bug: both lines rendered black). CHART_COLORS is this page's + // established literal-hex palette (see the 4 main charts below). + { label: 'SNR (dB)', data: cached.metrics.map(function(m) { return m.snr; }), borderColor: CHART_COLORS[0], backgroundColor: CHART_COLORS[0] + '20', yAxisID: 'y', tension: 0.2 }, + { label: 'Heard (s ago)', data: cached.metrics.map(function(m) { return m.heardSecsAgo; }), borderColor: CHART_COLORS[6], backgroundColor: CHART_COLORS[6] + '20', yAxisID: 'y1', tension: 0.2 }, ], }, options: { diff --git a/test-observer-direct-neighbors-panel.js b/test-observer-direct-neighbors-panel.js index 30575444..e288d39e 100644 --- a/test-observer-direct-neighbors-panel.js +++ b/test-observer-direct-neighbors-panel.js @@ -305,6 +305,18 @@ test('clicking a cached sparkline opens a modal with a dual-axis SNR/heard-secs- assert.strictEqual(lastChartConfig.data.datasets[0].yAxisID, 'y'); assert.strictEqual(lastChartConfig.data.datasets[1].yAxisID, 'y1'); assert.ok(lastChartConfig.options.scales.y1.title.text.toLowerCase().includes('heard'), 'expected the second axis labeled for heard_secs_ago'); + // Regression: canvas can't resolve CSS var() as a strokeStyle, so a raw + // 'var(--x)' string silently renders as Chart.js's default black -- + // both lines looked identical/black in production before this was caught. + // Line colors must be resolved literal values (this page's CHART_COLORS + // palette), and the two datasets must be visually distinct. + const c0 = lastChartConfig.data.datasets[0].borderColor; + const c1 = lastChartConfig.data.datasets[1].borderColor; + assert.ok(!/^var\(/.test(c0), `dataset[0].borderColor must not be a CSS var() reference, got ${c0}`); + assert.ok(!/^var\(/.test(c1), `dataset[1].borderColor must not be a CSS var() reference, got ${c1}`); + assert.ok(/^#[0-9a-f]{6}$/i.test(c0), `dataset[0].borderColor must be a resolved hex color, got ${c0}`); + assert.ok(/^#[0-9a-f]{6}$/i.test(c1), `dataset[1].borderColor must be a resolved hex color, got ${c1}`); + assert.notStrictEqual(c0, c1, 'SNR and Heard(s ago) lines must be visually distinct colors'); }); test('openNeighborSnrModal is a no-op for a pubkey with no cached data', () => {