// Headless guard for the trace-chart viewer (chart-viewer.js). Property protected: // buildCharts maps an AURA_TRACES payload to uPlot configs WITHOUT a DOM — overlay // is one chart with every series on its own y-scale over the shared xs; panels is // one chart per series, each on the SAME shared xs (timestamp-aligned). It loads the // real viewer module (a fix/regression there is observed here) and drives the // exported pure `buildCharts`; uPlot itself (which needs `document`) is never // instantiated — mount() is browser-only and not called here. import { createRequire } from "node:module"; import { readFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { dirname, join } from "node:path"; const require = createRequire(import.meta.url); const here = dirname(fileURLToPath(import.meta.url)); const traces = JSON.parse(readFileSync(join(here, "fixtures", "sample-traces.json"), "utf8")); const fail = (msg) => { console.error("FAIL: " + msg); process.exit(1); }; const eq = (a, b, msg) => { if (a !== b) fail(`${msg} (got ${a}, want ${b})`); }; const sameArr = (a, b, msg) => { if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length || a.some((v, i) => v !== b[i])) fail(msg); }; // No `document` global -> chart-viewer.js's browser mount is skipped; require gives // us only the pure export (mirrors viewer_dot_ids.mjs stubbing window before require). global.window = { AURA_TRACES: traces }; const { buildCharts } = require(join(here, "..", "assets", "chart-viewer.js")); const n = traces.series.length; const idx = Array.from({ length: traces.xs.length }, (_, i) => i); // overlay: ONE chart, all series superimposed, each on its own scale. Default x spine is // ORDINAL (indices) so non-trading gaps collapse. const overlay = buildCharts(traces, "overlay"); eq(overlay.length, 1, "overlay is one chart"); eq(overlay[0].data.length, 1 + n, "overlay data = xs + N series"); eq(overlay[0].opts.series.length, 1 + n, "overlay uPlot series = x + N"); sameArr(overlay[0].data[0], idx, "default overlay x-axis is the ordinal index spine"); const scaleIds = overlay[0].opts.series.slice(1).map((s) => s.scale); eq(new Set(scaleIds).size, n, "each overlay series has a distinct y-scale"); // continuous mode restores the real-timestamp x spine. const overlayC = buildCharts(traces, "overlay", "continuous"); sameArr(overlayC[0].data[0], traces.xs, "continuous overlay x-axis is the shared xs"); // panels: ONE chart per series, all positionally aligned on the SAME ordinal spine. const panels = buildCharts(traces, "panels"); eq(panels.length, n, "panels = one chart per series"); for (const p of panels) { eq(p.data.length, 2, "each panel = xs + its one series"); sameArr(p.data[0], idx, "panel shares the same ordinal index spine (position-aligned)"); } console.log(`OK — overlay 1 chart / ${n} series, panels ${n} charts, shared xs.`); process.exit(0);