test: repair vacuous save/load round-trip tests for widget labels - #15603
Conversation
Widget state is keyed `graphId:nodeId:name` and `LGraph._configureBase` re-adopts the payload's graph id, while `LGraph.clear()` only clears the store under the graph's *pre-configure* id. A test that serializes and reconfigures therefore leaves the source graph's `widgetValueStore` entries live under exactly the key the reloaded node looks up, so the reloaded node re-adopts them and the assertion passes without the serialized payload ever being read. Mutation-verified. Reintroducing #13861's own root cause (dropping `input.label = label` from `renameWidget`) killed 2 of the 5 tests in its regression file; after this change it kills 5 of 5. Making `serialize()` drop every input label and localized_name killed 1 of 5 before, 3 of 5 after -- and across the 29 round-trip test files in the repo it killed 1 of 922 tests before this change. - add `reloadSerializedGraph` test helper: forces the payload through JSON and drops the widget/preview stores for the payload's graph id - use it in the three reload tests in the #13861 regression file - give 'clearing a rename reverts the label' a live-rename control arm, so a build where renaming never persisted cannot pass it - rename SubgraphNode's 'preserve renamed label through serialize/configure round-trip' to what it actually asserts: it survives total payload blinding, so it is an in-place reconfigure test - add a real definition-reload test alongside it, killed by the input-label serialization mutation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
🎨 Storybook: ✅ Built — View Storybook🎭 Playwright: ✅ 1991 passed, 0 failed📊 Browser Reports
📦 Bundle Size
⚡ Performance Report
Absolute values
Raw data{
"timestamp": "2026-08-22T03:03:29.198Z",
"gitSha": "c79f7e2f64fe355ff00dff25e59401f60c765031",
"branch": "cb__roundtrip-vacuity-audit",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2004.480000000001,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.189999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 360.55199999999996,
"heapDeltaBytes": -128320,
"heapUsedBytes": 68939916,
"domNodes": -278,
"jsHeapTotalBytes": 4186112,
"scriptDurationMs": 8.262,
"eventListeners": -187,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-idle",
"durationMs": 2024.1889999999785,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.336,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 472.818,
"heapDeltaBytes": 1378812,
"heapUsedBytes": 71069028,
"domNodes": -281,
"jsHeapTotalBytes": 4710400,
"scriptDurationMs": 11.513,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1839.8590000000468,
"styleRecalcs": 76,
"styleRecalcDurationMs": 34.634,
"layouts": 12,
"layoutDurationMs": 3.477,
"taskDurationMs": 887.3549999999999,
"heapDeltaBytes": -9297392,
"heapUsedBytes": 60001544,
"domNodes": -280,
"jsHeapTotalBytes": 5234688,
"scriptDurationMs": 117.786,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1967.759000000001,
"styleRecalcs": 75,
"styleRecalcDurationMs": 39.311,
"layouts": 12,
"layoutDurationMs": 3.656,
"taskDurationMs": 989.569,
"heapDeltaBytes": -1246208,
"heapUsedBytes": 60649784,
"domNodes": -282,
"jsHeapTotalBytes": 5496832,
"scriptDurationMs": 131.85999999999999,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1664.447999999993,
"styleRecalcs": 31,
"styleRecalcDurationMs": 13.969000000000003,
"layouts": 6,
"layoutDurationMs": 0.6100000000000001,
"taskDurationMs": 258.93499999999995,
"heapDeltaBytes": 4054528,
"heapUsedBytes": 73386760,
"domNodes": 77,
"jsHeapTotalBytes": 4980736,
"scriptDurationMs": 10.393,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1708.737000000042,
"styleRecalcs": 30,
"styleRecalcDurationMs": 15.780000000000003,
"layouts": 6,
"layoutDurationMs": 0.664,
"taskDurationMs": 321.788,
"heapDeltaBytes": 4060948,
"heapUsedBytes": 73570780,
"domNodes": 75,
"jsHeapTotalBytes": 5242880,
"scriptDurationMs": 14.466999999999999,
"eventListeners": 21,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 624.7490000000084,
"styleRecalcs": 9,
"styleRecalcDurationMs": 5.119000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 321.571,
"heapDeltaBytes": -7431888,
"heapUsedBytes": 61524876,
"domNodes": 14,
"jsHeapTotalBytes": 7340032,
"scriptDurationMs": 27.365,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 691.353999999933,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.410000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 339.28400000000005,
"heapDeltaBytes": -6037164,
"heapUsedBytes": 63844592,
"domNodes": 16,
"jsHeapTotalBytes": 5505024,
"scriptDurationMs": 29.900999999999996,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2025.291999999979,
"styleRecalcs": 6,
"styleRecalcDurationMs": 4.546999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 385.193,
"heapDeltaBytes": -17538164,
"heapUsedBytes": 72611484,
"domNodes": -266,
"jsHeapTotalBytes": -2625536,
"scriptDurationMs": 32.78300000000001,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2041.2730000000465,
"styleRecalcs": 6,
"styleRecalcDurationMs": 5.469999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 523.9979999999999,
"heapDeltaBytes": -22080420,
"heapUsedBytes": 68629516,
"domNodes": -276,
"jsHeapTotalBytes": 2355200,
"scriptDurationMs": 39.95900000000001,
"eventListeners": -181,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 3124.6459999999843,
"styleRecalcs": 69,
"styleRecalcDurationMs": 14.321000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1884.82,
"heapDeltaBytes": 7617716,
"heapUsedBytes": 91709392,
"domNodes": -282,
"jsHeapTotalBytes": 5165056,
"scriptDurationMs": 636.6719999999999,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 3269.4770000000517,
"styleRecalcs": 66,
"styleRecalcDurationMs": 13.676999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 2141.736,
"heapDeltaBytes": -13551556,
"heapUsedBytes": 77308656,
"domNodes": -248,
"jsHeapTotalBytes": 2056192,
"scriptDurationMs": 710.599,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-zoom",
"durationMs": 4187.117,
"styleRecalcs": 69,
"styleRecalcDurationMs": 15.438999999999998,
"layouts": 60,
"layoutDurationMs": 8.936000000000002,
"taskDurationMs": 1898.4489999999998,
"heapDeltaBytes": 586752,
"heapUsedBytes": 73098024,
"domNodes": -256,
"jsHeapTotalBytes": -1875968,
"scriptDurationMs": 635.437,
"eventListeners": -175,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 4425.604000000021,
"styleRecalcs": 68,
"styleRecalcDurationMs": 16.596000000000004,
"layouts": 60,
"layoutDurationMs": 10.142999999999999,
"taskDurationMs": 2185.248,
"heapDeltaBytes": -15420572,
"heapUsedBytes": 73110044,
"domNodes": -256,
"jsHeapTotalBytes": 1269760,
"scriptDurationMs": 683.402,
"eventListeners": -177,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "legacy-node-drag",
"durationMs": 2654.943000000003,
"styleRecalcs": 43,
"styleRecalcDurationMs": 7.445,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1638.836,
"heapDeltaBytes": 12596596,
"heapUsedBytes": 78600964,
"domNodes": 4,
"jsHeapTotalBytes": 417792,
"scriptDurationMs": 174.594,
"eventListeners": 186,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "legacy-node-drag",
"durationMs": 3113.061000000016,
"styleRecalcs": 43,
"styleRecalcDurationMs": 7.431,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1871.487,
"heapDeltaBytes": -4178252,
"heapUsedBytes": 67283688,
"domNodes": -285,
"jsHeapTotalBytes": -1089536,
"scriptDurationMs": 216.29800000000003,
"eventListeners": 1,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2020.887000000016,
"styleRecalcs": 7,
"styleRecalcDurationMs": 5.646000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 440.232,
"heapDeltaBytes": -21959788,
"heapUsedBytes": 70032216,
"domNodes": -273,
"jsHeapTotalBytes": 3665920,
"scriptDurationMs": 33.119,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2037.0679999999766,
"styleRecalcs": 6,
"styleRecalcDurationMs": 5.109000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 534.702,
"heapDeltaBytes": -11143964,
"heapUsedBytes": 65627456,
"domNodes": -282,
"jsHeapTotalBytes": 2719744,
"scriptDurationMs": 41.821000000000005,
"eventListeners": -185,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 525.0819999999976,
"styleRecalcs": 47,
"styleRecalcDurationMs": 10.6,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 367.544,
"heapDeltaBytes": 17637784,
"heapUsedBytes": 87354388,
"domNodes": 20,
"jsHeapTotalBytes": 5242880,
"scriptDurationMs": 97.158,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999727
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 574.3579999999611,
"styleRecalcs": 46,
"styleRecalcDurationMs": 10.557999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 390.242,
"heapDeltaBytes": 17902208,
"heapUsedBytes": 87541280,
"domNodes": 18,
"jsHeapTotalBytes": 5242880,
"scriptDurationMs": 102.839,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-idle",
"durationMs": 2020.2600000000075,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.772,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 336.01300000000003,
"heapDeltaBytes": 2090904,
"heapUsedBytes": 71636096,
"domNodes": -294,
"jsHeapTotalBytes": 5234688,
"scriptDurationMs": 7.459,
"eventListeners": -229,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-idle",
"durationMs": 2007.4860000000854,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.633000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 451.703,
"heapDeltaBytes": 8430692,
"heapUsedBytes": 78111872,
"domNodes": -294,
"jsHeapTotalBytes": 4972544,
"scriptDurationMs": 10.581999999999999,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1725.6949999999733,
"styleRecalcs": 75,
"styleRecalcDurationMs": 32.45000000000001,
"layouts": 16,
"layoutDurationMs": 4.352,
"taskDurationMs": 780.1139999999999,
"heapDeltaBytes": 8706644,
"heapUsedBytes": 78604772,
"domNodes": -299,
"jsHeapTotalBytes": 6021120,
"scriptDurationMs": 91.96499999999999,
"eventListeners": -229,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1724.9500000000353,
"styleRecalcs": 75,
"styleRecalcDurationMs": 36.201,
"layouts": 16,
"layoutDurationMs": 4.64,
"taskDurationMs": 878.529,
"heapDeltaBytes": -9742788,
"heapUsedBytes": 59999964,
"domNodes": -296,
"jsHeapTotalBytes": 6283264,
"scriptDurationMs": 100.25300000000001,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 916.4000000000101,
"styleRecalcs": 19,
"styleRecalcDurationMs": 23.771999999999995,
"layouts": 14,
"layoutDurationMs": 10.106,
"taskDurationMs": 716.7989999999998,
"heapDeltaBytes": -7463948,
"heapUsedBytes": 83638028,
"domNodes": 13671,
"jsHeapTotalBytes": 10485760,
"scriptDurationMs": 22.77,
"eventListeners": 2383,
"totalBlockingTimeMs": 76,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 12004.457000000002,
"styleRecalcs": 249,
"styleRecalcDurationMs": 43.486999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 6559.871,
"heapDeltaBytes": -12221956,
"heapUsedBytes": 78751724,
"domNodes": -264,
"jsHeapTotalBytes": 3596288,
"scriptDurationMs": 2174.359,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 12258.649999999989,
"styleRecalcs": 247,
"styleRecalcDurationMs": 45.582,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 7594.443000000001,
"heapDeltaBytes": 3477528,
"heapUsedBytes": 94170836,
"domNodes": -284,
"jsHeapTotalBytes": 4907008,
"scriptDurationMs": 2495.638,
"eventListeners": -157,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 12754.401999999971,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12233.395999999999,
"heapDeltaBytes": -10691208,
"heapUsedBytes": 182322660,
"domNodes": -6548,
"jsHeapTotalBytes": 2871296,
"scriptDurationMs": 225.439,
"eventListeners": -7475,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.219999999999953,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-idle",
"durationMs": 13221.525999999925,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12667.731000000002,
"heapDeltaBytes": -25815248,
"heapUsedBytes": 177225956,
"domNodes": -6548,
"jsHeapTotalBytes": -2580480,
"scriptDurationMs": 236.74399999999997,
"eventListeners": -7473,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.219999999999953,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 15649.681999999984,
"styleRecalcs": 83,
"styleRecalcDurationMs": 17.276000000000014,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 15210.329,
"heapDeltaBytes": -32416504,
"heapUsedBytes": 175673060,
"domNodes": -6552,
"jsHeapTotalBytes": -6860800,
"scriptDurationMs": 759.5320000000002,
"eventListeners": -7473,
"totalBlockingTimeMs": 58,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 16053.022000000055,
"styleRecalcs": 91,
"styleRecalcDurationMs": 18.566,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 15604.098,
"heapDeltaBytes": -42667784,
"heapUsedBytes": 164529624,
"domNodes": -6548,
"jsHeapTotalBytes": -3653632,
"scriptDurationMs": 758.193,
"eventListeners": -7473,
"totalBlockingTimeMs": 18,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 436.6459999999961,
"styleRecalcs": 16,
"styleRecalcDurationMs": 20.609999999999996,
"layouts": 3,
"layoutDurationMs": 0.7440000000000001,
"taskDurationMs": 101.365,
"heapDeltaBytes": 5460604,
"heapUsedBytes": 71114788,
"domNodes": 126,
"jsHeapTotalBytes": 262144,
"scriptDurationMs": 7.1579999999999995,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "workflow-execution",
"durationMs": 101.86600000008639,
"styleRecalcs": 7,
"styleRecalcDurationMs": 13.598999999999998,
"layouts": 2,
"layoutDurationMs": 1.042,
"taskDurationMs": 71.144,
"heapDeltaBytes": 3275640,
"heapUsedBytes": 69642488,
"domNodes": 126,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 3.546999999999999,
"eventListeners": 25,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.800000000000182
}
]
} |
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## feature/ecs-migration #15603 +/- ##
=======================================================
Coverage 82.29% 82.30%
=======================================================
Files 1900 1900
Lines 107653 107660 +7
Branches 33652 34065 +413
=======================================================
+ Hits 88595 88608 +13
- Misses 18720 18723 +3
+ Partials 338 329 -9
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 17 files with indirect coverage changes 🚀 New features to boost your workflow:
|
| LiteGraph.registerNodeType('test/interior-label-reload', InteriorNode) | ||
| onTestFinished(() => { | ||
| delete LiteGraph.registered_node_types['test/interior-label-reload'] | ||
| }) | ||
|
|
||
| const interiorNode = LiteGraph.createNode('test/interior-label-reload')! |
There was a problem hiding this comment.
How do you feel about a const for this to make sure they're all consistently aligned?
| /** | ||
| * Reloads a serialized graph the way a fresh page would, and returns the new | ||
| * graph. | ||
| * | ||
| * Save/load assertions written as `configure(...serialize())` are vacuous by | ||
| * default. `LGraph.clear()` drops the widget store under the graph's | ||
| * *pre-configure* id, but `LGraph._configureBase` then re-adopts the *payload's* | ||
| * graph id — and `widgetValueStore` is keyed `graphId:nodeId:name`. The source | ||
| * graph's entries are therefore still live under exactly the key the reloaded | ||
| * node looks up, so `registerWidget` returns the existing state and the | ||
| * assertion passes without the serialized payload ever being read. | ||
| * | ||
| * This helper removes both escape hatches: the payload is forced through | ||
| * `JSON.parse(JSON.stringify(...))` (so live-object aliasing cannot leak) and | ||
| * the widget/preview stores are dropped for the payload's graph id before | ||
| * `configure()` runs. | ||
| * | ||
| * Verify with a mutation: a save/load test that still passes when the producer | ||
| * stops writing the field under test never read the payload. | ||
| */ |
| serialized: T, | ||
| graphFactory: () => LGraph | ||
| ): LGraph { | ||
| const payload = JSON.parse(JSON.stringify(serialized)) as T |
DrJKL
left a comment
There was a problem hiding this comment.
Tests now exercise the serialized payload rather than reusing live widget state. The regression suite is mutation-verified, and the reload helper preserves the graph serialization types without unsafe casts.
Co-authored-by: Alexander Brown <DrJKL0424@gmail.com>
Repairs three save/load tests that were passing without ever reading the JSON they produced, and adds a helper so the next author does not rediscover it.
Why they were vacuous
widgetValueStoreis keyedgraphId:nodeId:name.LGraph.clear()drops the store for the graph's pre-configure id (LGraph.ts:578), thenLGraph._configureBase()re-adopts the payload's graph id (LGraph.ts:2566-2568). Sonew LGraph().configure(source.serialize())ends up sharing the source graph's id — and the source's widget entries are still live under exactly the key the reloaded node looks up.registerWidgetreturns the existing state and the reloaded widget silently inherits the source's label.Verified directly in the #13861 test: both graphs report id
8016438e-…, both widgets reportwidgetId8016438e-…:1:text.Numbers
Mutation A — reintroduce #13861's own root cause (delete
if (input) input.label = labelfromrenameWidget,src/utils/widgetUtil.ts:65), againstLGraphNode.widgetLabelRename.regression.test.ts:Survivors before:
label survives a full graph serialize -> configure round-trip,label survives delete -> undo,clearing a rename reverts the label to its default after round-trip.Mutation B —
LGraphNode.serialize()dropslabelandlocalized_namefrom every input:src/Mutation C — total payload blinding of
SubgraphNode.configure(argument replaced with{ id, type }):should preserve renamed label through serialize/configure round-tripsurvived. It also survived Mutation B. It is killed only by a mutation that makesconfigureappend instead of resetting inputs — so it is an input-list-reset test, not a round trip. Renamed accordingly, and a real definition-reload test added beside it (killed by Mutation B).Unmutated control: 923 / 923 pass across the 29 files.
oxlint --type-awareclean on the three changed files, with a non-vacuous control (an injecteddebuggeris reported).oxfmtapplied.Changes
src/utils/__tests__/litegraphTestUtils.ts— newreloadSerializedGraph(serialized, graphFactory). Forces the payload throughJSON.parse(JSON.stringify(...))so live-object aliasing cannot leak, and drops the widget and preview stores for the payload's graph id beforeconfigure().src/lib/litegraph/src/LGraphNode.widgetLabelRename.regression.test.ts— the three reload tests go through the helper.clearing a rename reverts the labelgains a live-rename control arm, so a build where renaming never persisted at all cannot pass it by assertingundefined.src/lib/litegraph/src/subgraph/SubgraphNode.test.ts— vacuous test renamed tokeeps the renamed label after an in-place reconfigurewith a comment explaining what it cannot see; newpreserves a renamed label across a real definition reloadadded.Scope
Test-only. No production change. The open question of whether
LGraph.configureshould also clear the store for the incoming graph id is deliberately not answered here — a store-amnesia probe at_configureBasekilled 0 of 922 tests, so production rehydration is correct today, but it deserves an explicit decision rather than staying implicit.Full audit and per-test verdict table: #15604