Skip to content

Commit 5497bfa

Browse files
committed
fix(engine): make compile-snapshot test platform-independent
The snapshot was storing timezone-local Date objects and raw float positions for time-scale ticks. macOS runs CDT/PDT (UTC-5/6) so D3 produces midnight-local dates like T05:00:00.000Z, while Linux CI runs UTC so the same ticks land at T00:00:00.000Z. The fractional position difference from the date shift was also enough to nudge fitContinuousTicks onto a different candidate, producing an extra tick on Linux. Fix: serializeLayout now normalizes tick Date values to YYYY-MM-DD UTC strings and rounds positions to 2 decimal places before snapshotting. Both changes are purely presentational -- the snapshot still asserts the right labels and approximate positions.
1 parent 12e5a56 commit 5497bfa

2 files changed

Lines changed: 73 additions & 25 deletions

File tree

packages/engine/src/__tests__/__snapshots__/compile-snapshot.test.ts.snap

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,17 @@ exports[`compileChart snapshot (Step 7 oracle) > clipped-domain bar chart (data
7979
"ticks": [
8080
{
8181
"label": "0",
82-
"position": 35.269999999999996,
82+
"position": 35.27,
8383
"value": 0,
8484
},
8585
{
8686
"label": "20",
87-
"position": 251.96200000000002,
87+
"position": 251.96,
8888
"value": 20,
8989
},
9090
{
9191
"label": "40",
92-
"position": 468.654,
92+
"position": 468.65,
9393
"value": 40,
9494
},
9595
],
@@ -145,7 +145,7 @@ exports[`compileChart snapshot (Step 7 oracle) > clipped-domain bar chart (data
145145
"ticks": [
146146
{
147147
"label": "A",
148-
"position": 297.0761194029851,
148+
"position": 297.08,
149149
"value": "A",
150150
},
151151
{
@@ -155,7 +155,7 @@ exports[`compileChart snapshot (Step 7 oracle) > clipped-domain bar chart (data
155155
},
156156
{
157157
"label": "C",
158-
"position": 119.52388059701492,
158+
"position": 119.52,
159159
"value": "C",
160160
},
161161
],
@@ -597,23 +597,23 @@ exports[`compileChart snapshot (Step 7 oracle) > legend-heavy multi-series line
597597
"ticks": [
598598
{
599599
"label": "2020",
600-
"position": 42.99201707650273,
601-
"value": 2020-01-01T06:00:00.000Z,
600+
"position": 42.99,
601+
"value": "2020-01-01",
602602
},
603603
{
604604
"label": "Apr 2020",
605-
"position": 207.45089674408015,
606-
"value": 2020-04-01T05:00:00.000Z,
605+
"position": 207.45,
606+
"value": "2020-04-01",
607607
},
608608
{
609609
"label": "Jul 2020",
610-
"position": 371.98511259107465,
611-
"value": 2020-07-01T05:00:00.000Z,
610+
"position": 371.99,
611+
"value": "2020-07-01",
612612
},
613613
{
614614
"label": "Oct 2020",
615-
"position": 538.3273967440801,
616-
"value": 2020-10-01T05:00:00.000Z,
615+
"position": 538.33,
616+
"value": "2020-10-01",
617617
},
618618
],
619619
"titlePadding": undefined,
@@ -697,12 +697,12 @@ exports[`compileChart snapshot (Step 7 oracle) > legend-heavy multi-series line
697697
},
698698
{
699699
"label": "5",
700-
"position": 370.04999999999995,
700+
"position": 370.05,
701701
"value": 5,
702702
},
703703
{
704704
"label": "10",
705-
"position": 329.69999999999993,
705+
"position": 329.7,
706706
"value": 10,
707707
},
708708
{
@@ -717,17 +717,17 @@ exports[`compileChart snapshot (Step 7 oracle) > legend-heavy multi-series line
717717
},
718718
{
719719
"label": "25",
720-
"position": 208.64999999999998,
720+
"position": 208.65,
721721
"value": 25,
722722
},
723723
{
724724
"label": "30",
725-
"position": 168.29999999999998,
725+
"position": 168.3,
726726
"value": 30,
727727
},
728728
{
729729
"label": "35",
730-
"position": 127.94999999999999,
730+
"position": 127.95,
731731
"value": 35,
732732
},
733733
{
@@ -1485,22 +1485,22 @@ exports[`compileChart snapshot (Step 7 oracle) > watermarked column chart with g
14851485
"ticks": [
14861486
{
14871487
"label": "A",
1488-
"position": 124.62862068965518,
1488+
"position": 124.63,
14891489
"value": "A",
14901490
},
14911491
{
14921492
"label": "B",
1493-
"position": 247.7228735632184,
1493+
"position": 247.72,
14941494
"value": "B",
14951495
},
14961496
{
14971497
"label": "C",
1498-
"position": 370.8171264367816,
1498+
"position": 370.82,
14991499
"value": "C",
15001500
},
15011501
{
15021502
"label": "D",
1503-
"position": 493.9113793103449,
1503+
"position": 493.91,
15041504
"value": "D",
15051505
},
15061506
],
@@ -1565,17 +1565,17 @@ exports[`compileChart snapshot (Step 7 oracle) > watermarked column chart with g
15651565
},
15661566
{
15671567
"label": "20",
1568-
"position": 229.02857142857144,
1568+
"position": 229.03,
15691569
"value": 20,
15701570
},
15711571
{
15721572
"label": "40",
1573-
"position": 161.25714285714287,
1573+
"position": 161.26,
15741574
"value": 40,
15751575
},
15761576
{
15771577
"label": "60",
1578-
"position": 93.4857142857143,
1578+
"position": 93.49,
15791579
"value": 60,
15801580
},
15811581
],

packages/engine/src/__tests__/compile-snapshot.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,59 @@ import type { ChartLayout } from '@opendata-ai/openchart-core';
1414
import { describe, expect, it } from 'vitest';
1515
import { compileChart } from '../compile';
1616

17+
/**
18+
* Normalize a tick value for snapshot comparison. Date objects are converted
19+
* to their UTC ISO date string (YYYY-MM-DD) so the snapshot doesn't encode
20+
* the local timezone offset, which differs between macOS (CDT/PDT) and the
21+
* Linux CI runner (UTC).
22+
*/
23+
function normalizeTickValue(value: unknown): unknown {
24+
if (value instanceof Date) return value.toISOString().slice(0, 10);
25+
return value;
26+
}
27+
28+
/**
29+
* Normalize a tick position to 2 decimal places. D3 time-scale positions are
30+
* floating-point and shift slightly with timezone because the tick Date values
31+
* differ by hours. Rounding eliminates that noise without losing signal.
32+
*/
33+
function normalizePosition(pos: unknown): unknown {
34+
if (typeof pos === 'number') return Math.round(pos * 100) / 100;
35+
return pos;
36+
}
37+
38+
/** Normalize an axis tick array for platform-independent snapshot comparison. */
39+
function normalizeTicks(
40+
ticks: Array<{ label?: string; value?: unknown; position?: unknown }>,
41+
): unknown[] {
42+
return ticks.map((t) => ({
43+
...t,
44+
value: normalizeTickValue(t.value),
45+
position: normalizePosition(t.position),
46+
}));
47+
}
48+
49+
/** Normalize an axis object so tick values and positions are platform-stable. */
50+
function normalizeAxis(axis: Record<string, unknown> | undefined): unknown {
51+
if (!axis) return axis;
52+
const ticks = axis.ticks;
53+
return {
54+
...axis,
55+
ticks: Array.isArray(ticks)
56+
? normalizeTicks(ticks as Parameters<typeof normalizeTicks>[0])
57+
: ticks,
58+
};
59+
}
60+
1761
/** Convert ChartLayout into a fully serializable shape for snapshot comparison. */
1862
function serializeLayout(layout: ChartLayout): Record<string, unknown> {
1963
const { tooltipDescriptors, measureText: _measure, ...rest } = layout;
64+
const axes = rest.axes as
65+
| { x?: Record<string, unknown>; y?: Record<string, unknown> }
66+
| undefined;
2067
return {
2168
...rest,
69+
axes: axes ? { ...axes, x: normalizeAxis(axes.x), y: normalizeAxis(axes.y) } : axes,
2270
tooltipDescriptors: Array.from(tooltipDescriptors.entries()),
2371
};
2472
}

0 commit comments

Comments
 (0)