fix: resolve subgraph promoted widget panel regressions#10648
fix: resolve subgraph promoted widget panel regressions#10648
Conversation
Add Playwright tests that expose three regressions in the subgraph promoted widget panel introduced by the proxy-widget-v2 refactor: - Linked promoted widgets (backed by subgraph input slots) show an enabled hide toggle in SubgraphEditor instead of a disabled link icon - Linked promoted widgets expose Hide/Show input in the WidgetActions three-dot menu on the Parameters tab - Widget labels in SubgraphEditor display raw widget.name instead of the renamed label from the subgraph input slot Also adds data-testid attributes to SubgraphEditor, SubgraphNodeWidget, and RightSidePanel for stable test selectors.
…S icon selectors with data-testid
…panels - Add isLinkedPromotion helper to promotionUtils that checks input._widget bindings - SubgraphEditor: linked promotions show link icon, disabled toggle, correct label - WidgetActions: hide Show/Hide menu for linked promotions - SectionWidgets: fix disambiguatingSourceNodeId lookup causing stale isShownOnParents - WidgetActions: fix handleHideInput using wrong key for promotion store demote
🎭 Playwright: ✅ 742 passed, 0 failed · 3 flaky📊 Browser Reports
|
🎨 Storybook: ✅ Built — View Storybook |
📝 WalkthroughWalkthroughAdds isLinkedPromotion utility, tracks linked promoted widgets across subgraph editor and widget actions, updates components and test IDs to reflect linkage, prevents demotion of linked widgets, and adds Playwright tests and selector constants to validate promoted-widget UI behavior. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
📦 Bundle: 5.09 MB gzip 🔴 +6 BDetailsSummary
Category Glance App Entry Points — 22.3 kB (baseline 22.3 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.14 MB (baseline 1.14 MB) • 🔴 +969 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 76.6 kB (baseline 76.6 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 9 added / 9 removed / 2 unchanged Panels & Settings — 484 kB (baseline 484 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 10 added / 10 removed / 12 unchanged User & Accounts — 17.1 kB (baseline 17.1 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 5 added / 5 removed / 2 unchanged Editors & Dialogs — 109 kB (baseline 109 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 2 added / 2 removed UI Components — 60.3 kB (baseline 60.3 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed / 8 unchanged Data & Services — 2.96 MB (baseline 2.96 MB) • 🔴 +550 BStores, services, APIs, and repositories
Status: 13 added / 13 removed / 4 unchanged Utilities & Hooks — 334 kB (baseline 334 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 13 added / 13 removed / 12 unchanged Vendor & Third-Party — 9.8 MB (baseline 9.8 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 8.43 MB (baseline 8.43 MB) • ⚪ 0 BBundles that do not match a named category
Status: 55 added / 55 removed / 79 unchanged ⚡ Performance Report
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-03-28T08:38:38.863Z",
"gitSha": "07ffd8266e5125597a11bd264865212cbe7a884c",
"branch": "fix/subgraph-promoted-widget-regressions",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2041.3509999999917,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.821999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 390.072,
"heapDeltaBytes": 19897792,
"heapUsedBytes": 63061432,
"domNodes": 16,
"jsHeapTotalBytes": 22544384,
"scriptDurationMs": 18.137,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "canvas-idle",
"durationMs": 2039.7980000000189,
"styleRecalcs": 10,
"styleRecalcDurationMs": 10.148000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 436.19399999999996,
"heapDeltaBytes": 20259112,
"heapUsedBytes": 64090328,
"domNodes": 20,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 30.161,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "canvas-idle",
"durationMs": 2058.9669999999387,
"styleRecalcs": 11,
"styleRecalcDurationMs": 11.380999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 381.94700000000006,
"heapDeltaBytes": 20312576,
"heapUsedBytes": 63073980,
"domNodes": 22,
"jsHeapTotalBytes": 22282240,
"scriptDurationMs": 24.554000000000002,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2066.768999999994,
"styleRecalcs": 84,
"styleRecalcDurationMs": 45.201,
"layouts": 12,
"layoutDurationMs": 3.4020000000000006,
"taskDurationMs": 976.5569999999999,
"heapDeltaBytes": 16550604,
"heapUsedBytes": 59370444,
"domNodes": 66,
"jsHeapTotalBytes": 23330816,
"scriptDurationMs": 134.76899999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1840.9879999999816,
"styleRecalcs": 75,
"styleRecalcDurationMs": 38.832,
"layouts": 12,
"layoutDurationMs": 3.783,
"taskDurationMs": 796.7579999999999,
"heapDeltaBytes": 16692928,
"heapUsedBytes": 59335608,
"domNodes": 58,
"jsHeapTotalBytes": 23330816,
"scriptDurationMs": 137.354,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000073
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1826.6700000000355,
"styleRecalcs": 76,
"styleRecalcDurationMs": 40.398,
"layouts": 12,
"layoutDurationMs": 3.6100000000000003,
"taskDurationMs": 785.5169999999999,
"heapDeltaBytes": 15909580,
"heapUsedBytes": 58715252,
"domNodes": 58,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 141.79899999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1725.4399999999634,
"styleRecalcs": 30,
"styleRecalcDurationMs": 16.86,
"layouts": 6,
"layoutDurationMs": 0.673,
"taskDurationMs": 310.17900000000003,
"heapDeltaBytes": 24658956,
"heapUsedBytes": 67003056,
"domNodes": 79,
"jsHeapTotalBytes": 20185088,
"scriptDurationMs": 25.514999999999997,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1729.219999999998,
"styleRecalcs": 32,
"styleRecalcDurationMs": 19.895000000000003,
"layouts": 6,
"layoutDurationMs": 0.68,
"taskDurationMs": 325.21199999999993,
"heapDeltaBytes": 24584172,
"heapUsedBytes": 67074316,
"domNodes": 80,
"jsHeapTotalBytes": 20185088,
"scriptDurationMs": 26.976,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1727.7390000000423,
"styleRecalcs": 31,
"styleRecalcDurationMs": 17.208000000000002,
"layouts": 6,
"layoutDurationMs": 0.702,
"taskDurationMs": 306.757,
"heapDeltaBytes": 24647504,
"heapUsedBytes": 67404796,
"domNodes": 77,
"jsHeapTotalBytes": 20447232,
"scriptDurationMs": 24.743000000000002,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000073
},
{
"name": "dom-widget-clipping",
"durationMs": 584.165999999982,
"styleRecalcs": 13,
"styleRecalcDurationMs": 9.68,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 349.298,
"heapDeltaBytes": 6549688,
"heapUsedBytes": 49080496,
"domNodes": 21,
"jsHeapTotalBytes": 13107200,
"scriptDurationMs": 69.021,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.67999999999997
},
{
"name": "dom-widget-clipping",
"durationMs": 560.5059999999753,
"styleRecalcs": 13,
"styleRecalcDurationMs": 9.275000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 357.292,
"heapDeltaBytes": 6528548,
"heapUsedBytes": 48959448,
"domNodes": 22,
"jsHeapTotalBytes": 12845056,
"scriptDurationMs": 68.292,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "dom-widget-clipping",
"durationMs": 577.2529999999279,
"styleRecalcs": 13,
"styleRecalcDurationMs": 9.395999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 355.27500000000003,
"heapDeltaBytes": 7013600,
"heapUsedBytes": 49463792,
"domNodes": 22,
"jsHeapTotalBytes": 13369344,
"scriptDurationMs": 68.822,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "large-graph-idle",
"durationMs": 2055.8489999999947,
"styleRecalcs": 10,
"styleRecalcDurationMs": 11.119,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 642.2829999999999,
"heapDeltaBytes": 5158740,
"heapUsedBytes": 55789860,
"domNodes": -257,
"jsHeapTotalBytes": 16191488,
"scriptDurationMs": 131.041,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000073
},
{
"name": "large-graph-idle",
"durationMs": 2040.5640000000176,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.645000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 568.1650000000001,
"heapDeltaBytes": 4273296,
"heapUsedBytes": 54953616,
"domNodes": -259,
"jsHeapTotalBytes": 16191488,
"scriptDurationMs": 108.86699999999999,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "large-graph-idle",
"durationMs": 2073.285999999939,
"styleRecalcs": 12,
"styleRecalcDurationMs": 14.751999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 625.3580000000001,
"heapDeltaBytes": 5473848,
"heapUsedBytes": 56187924,
"domNodes": -256,
"jsHeapTotalBytes": 15929344,
"scriptDurationMs": 126.69800000000001,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "large-graph-pan",
"durationMs": 2125.076999999976,
"styleRecalcs": 68,
"styleRecalcDurationMs": 16.282999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1157.06,
"heapDeltaBytes": 17664984,
"heapUsedBytes": 71061044,
"domNodes": -262,
"jsHeapTotalBytes": 18493440,
"scriptDurationMs": 425.649,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "large-graph-pan",
"durationMs": 2133.3829999999807,
"styleRecalcs": 69,
"styleRecalcDurationMs": 17.714000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1207.0249999999999,
"heapDeltaBytes": 16256004,
"heapUsedBytes": 69777624,
"domNodes": -261,
"jsHeapTotalBytes": 18231296,
"scriptDurationMs": 464.933,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "large-graph-pan",
"durationMs": 2150.6630000000087,
"styleRecalcs": 69,
"styleRecalcDurationMs": 18.641000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1195.115,
"heapDeltaBytes": 15778900,
"heapUsedBytes": 71159172,
"domNodes": -263,
"jsHeapTotalBytes": 17211392,
"scriptDurationMs": 420.317,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "large-graph-zoom",
"durationMs": 3162.7340000000004,
"styleRecalcs": 66,
"styleRecalcDurationMs": 16.569000000000003,
"layouts": 60,
"layoutDurationMs": 7.619000000000001,
"taskDurationMs": 1369.237,
"heapDeltaBytes": 7223968,
"heapUsedBytes": 61512640,
"domNodes": -263,
"jsHeapTotalBytes": 17240064,
"scriptDurationMs": 527.254,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "large-graph-zoom",
"durationMs": 3180.8530000000133,
"styleRecalcs": 66,
"styleRecalcDurationMs": 17.539,
"layouts": 60,
"layoutDurationMs": 7.692,
"taskDurationMs": 1399.37,
"heapDeltaBytes": 7061160,
"heapUsedBytes": 61677880,
"domNodes": -265,
"jsHeapTotalBytes": 17240064,
"scriptDurationMs": 532.087,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000073
},
{
"name": "large-graph-zoom",
"durationMs": 3198.54399999997,
"styleRecalcs": 65,
"styleRecalcDurationMs": 16.874999999999996,
"layouts": 60,
"layoutDurationMs": 7.5969999999999995,
"taskDurationMs": 1390.31,
"heapDeltaBytes": 9524872,
"heapUsedBytes": 64376040,
"domNodes": -266,
"jsHeapTotalBytes": 15405056,
"scriptDurationMs": 527.2829999999999,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "minimap-idle",
"durationMs": 2027.2970000000328,
"styleRecalcs": 10,
"styleRecalcDurationMs": 10.088,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 565.446,
"heapDeltaBytes": 4394928,
"heapUsedBytes": 56732628,
"domNodes": -258,
"jsHeapTotalBytes": 16191488,
"scriptDurationMs": 109.19900000000001,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "minimap-idle",
"durationMs": 2027.6949999999943,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.819999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 643.3709999999999,
"heapDeltaBytes": 3635628,
"heapUsedBytes": 56109324,
"domNodes": -258,
"jsHeapTotalBytes": 16453632,
"scriptDurationMs": 132.751,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000073
},
{
"name": "minimap-idle",
"durationMs": 2028.978000000052,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.188000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 563.1389999999999,
"heapDeltaBytes": 13332956,
"heapUsedBytes": 66861208,
"domNodes": -259,
"jsHeapTotalBytes": 15724544,
"scriptDurationMs": 107.817,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 558.1720000000132,
"styleRecalcs": 49,
"styleRecalcDurationMs": 13.781999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 366.5630000000001,
"heapDeltaBytes": 6443132,
"heapUsedBytes": 49197820,
"domNodes": 24,
"jsHeapTotalBytes": 12845056,
"scriptDurationMs": 128.48499999999999,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 582.6279999999997,
"styleRecalcs": 47,
"styleRecalcDurationMs": 11.632,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 376.12100000000004,
"heapDeltaBytes": 6477016,
"heapUsedBytes": 49484064,
"domNodes": 20,
"jsHeapTotalBytes": 13107200,
"scriptDurationMs": 132.36399999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 561.5889999999126,
"styleRecalcs": 48,
"styleRecalcDurationMs": 12.392,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 377.597,
"heapDeltaBytes": -2245716,
"heapUsedBytes": 49706152,
"domNodes": 22,
"jsHeapTotalBytes": 16252928,
"scriptDurationMs": 127.47599999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "subgraph-idle",
"durationMs": 1992.7920000000086,
"styleRecalcs": 11,
"styleRecalcDurationMs": 10.911999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 383.53799999999995,
"heapDeltaBytes": 19904944,
"heapUsedBytes": 62900236,
"domNodes": 22,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 20.634,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "subgraph-idle",
"durationMs": 1992.211999999995,
"styleRecalcs": 11,
"styleRecalcDurationMs": 10.271,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 353.89899999999994,
"heapDeltaBytes": 19898632,
"heapUsedBytes": 62902768,
"domNodes": 21,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 17.230999999999995,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "subgraph-idle",
"durationMs": 2026.3760000000275,
"styleRecalcs": 11,
"styleRecalcDurationMs": 10.398,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 378.87100000000004,
"heapDeltaBytes": 20331820,
"heapUsedBytes": 63227316,
"domNodes": 23,
"jsHeapTotalBytes": 23330816,
"scriptDurationMs": 20.921000000000003,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 2026.7880000000105,
"styleRecalcs": 86,
"styleRecalcDurationMs": 48.163,
"layouts": 16,
"layoutDurationMs": 4.395,
"taskDurationMs": 944.0450000000001,
"heapDeltaBytes": 11884408,
"heapUsedBytes": 54842092,
"domNodes": 73,
"jsHeapTotalBytes": 22544384,
"scriptDurationMs": 104.28099999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1698.3230000000162,
"styleRecalcs": 76,
"styleRecalcDurationMs": 38.538000000000004,
"layouts": 16,
"layoutDurationMs": 4.853,
"taskDurationMs": 708.485,
"heapDeltaBytes": 2088196,
"heapUsedBytes": 54900496,
"domNodes": 63,
"jsHeapTotalBytes": 25952256,
"scriptDurationMs": 101.79100000000001,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1967.585999999983,
"styleRecalcs": 83,
"styleRecalcDurationMs": 46.786,
"layouts": 16,
"layoutDurationMs": 4.883,
"taskDurationMs": 926.6959999999999,
"heapDeltaBytes": 11853680,
"heapUsedBytes": 54858356,
"domNodes": 71,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 110.879,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "vue-large-graph-idle",
"durationMs": 12872.869999999977,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12852.088000000002,
"heapDeltaBytes": -29633604,
"heapUsedBytes": 157038320,
"domNodes": -8335,
"jsHeapTotalBytes": 14245888,
"scriptDurationMs": 713.626,
"eventListeners": -16466,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.329999999999927
},
{
"name": "vue-large-graph-idle",
"durationMs": 12609.178999999984,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12597.821,
"heapDeltaBytes": -47442020,
"heapUsedBytes": 158206504,
"domNodes": -8331,
"jsHeapTotalBytes": 16867328,
"scriptDurationMs": 677.697,
"eventListeners": -16466,
"totalBlockingTimeMs": 0,
"frameDurationMs": 20
},
{
"name": "vue-large-graph-idle",
"durationMs": 12681.128000000059,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12668.26,
"heapDeltaBytes": -30097272,
"heapUsedBytes": 165220772,
"domNodes": -8331,
"jsHeapTotalBytes": 26042368,
"scriptDurationMs": 698.651,
"eventListeners": -16462,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.33000000000029
},
{
"name": "vue-large-graph-pan",
"durationMs": 14593.89299999998,
"styleRecalcs": 67,
"styleRecalcDurationMs": 14.097000000000026,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14565.514000000001,
"heapDeltaBytes": -22282280,
"heapUsedBytes": 172693328,
"domNodes": -8331,
"jsHeapTotalBytes": 24383488,
"scriptDurationMs": 948.6769999999999,
"eventListeners": -16464,
"totalBlockingTimeMs": 0,
"frameDurationMs": 19.98999999999978
},
{
"name": "vue-large-graph-pan",
"durationMs": 14976.828999999952,
"styleRecalcs": 71,
"styleRecalcDurationMs": 15.498000000000012,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14955.297,
"heapDeltaBytes": -21633448,
"heapUsedBytes": 172990300,
"domNodes": -8331,
"jsHeapTotalBytes": 24907776,
"scriptDurationMs": 1010.2739999999999,
"eventListeners": -16453,
"totalBlockingTimeMs": 0,
"frameDurationMs": 21.660000000000217
},
{
"name": "vue-large-graph-pan",
"durationMs": 14716.151999999965,
"styleRecalcs": 69,
"styleRecalcDurationMs": 14.537000000000022,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14692.951999999997,
"heapDeltaBytes": -33962160,
"heapUsedBytes": 159735964,
"domNodes": -8331,
"jsHeapTotalBytes": -3665920,
"scriptDurationMs": 983.394,
"eventListeners": -16458,
"totalBlockingTimeMs": 39,
"frameDurationMs": 20
},
{
"name": "workflow-execution",
"durationMs": 444.562000000019,
"styleRecalcs": 15,
"styleRecalcDurationMs": 23.835,
"layouts": 5,
"layoutDurationMs": 1.564,
"taskDurationMs": 124.92799999999998,
"heapDeltaBytes": 4586772,
"heapUsedBytes": 50349672,
"domNodes": 164,
"jsHeapTotalBytes": 262144,
"scriptDurationMs": 25.426999999999992,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000027
},
{
"name": "workflow-execution",
"durationMs": 465.53500000004533,
"styleRecalcs": 15,
"styleRecalcDurationMs": 21.159000000000002,
"layouts": 5,
"layoutDurationMs": 1.33,
"taskDurationMs": 116.178,
"heapDeltaBytes": 4482332,
"heapUsedBytes": 50160828,
"domNodes": 154,
"jsHeapTotalBytes": 262144,
"scriptDurationMs": 29.590000000000003,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "workflow-execution",
"durationMs": 451.8239999999878,
"styleRecalcs": 16,
"styleRecalcDurationMs": 21.141999999999996,
"layouts": 5,
"layoutDurationMs": 1.317,
"taskDurationMs": 122.76100000000001,
"heapDeltaBytes": 4489188,
"heapUsedBytes": 50277340,
"domNodes": 156,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 29.950999999999997,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
}
]
} |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts (1)
22-25: Guard against missing node refs before indexing[0].If fixture data changes, this fails with a less actionable runtime error. Add an
explicit assertion that at least one node ref exists before dereferencing.🧩 Suggested hardening
-const subgraphNode = ( - await comfyPage.nodeOps.getNodeRefsByTitle(nodeTitle) -)[0] +const subgraphNodes = await comfyPage.nodeOps.getNodeRefsByTitle(nodeTitle) +expect(subgraphNodes.length).toBeGreaterThan(0) +const subgraphNode = subgraphNodes[0]Also applies to: 133-136
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts` around lines 22 - 25, Guard against empty results from comfyPage.nodeOps.getNodeRefsByTitle before dereferencing: check the returned array from getNodeRefsByTitle (used where subgraphNode is assigned and also in the similar block around lines 133-136) and assert it has at least one element (e.g., expect(refs.length).toBeGreaterThan(0) or throw a descriptive Error) before accessing [0]; fail fast with a clear message mentioning the nodeTitle so the test failure is actionable.src/core/graph/subgraph/promotionUtils.test.ts (2)
373-385: Consolidate overlapping negative cases.The tests at Line 373 and Line 380 both exercise the same “no linked input
present” branch; combining them would keep the suite lean without reducing
behavioral coverage.As per coding guidelines: "Be parsimonious in testing; do not write redundant tests."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/core/graph/subgraph/promotionUtils.test.ts` around lines 373 - 385, Combine the two redundant negative tests into a single test that asserts isLinkedPromotion returns false for both "no inputs match" and "empty inputs" scenarios: replace the two it blocks with one it('returns false when no linked input is present', ...) that creates subgraph via createTestSubgraph and subgraphNode via createTestSubgraphNode and then calls expect(isLinkedPromotion(subgraphNode, '999', 'nonexistent')).toBe(false) and expect(isLinkedPromotion(subgraphNode, '1', 'value')).toBe(false); keep references to isLinkedPromotion, createTestSubgraph, and createTestSubgraphNode so the assertion coverage remains but the tests are not redundant.
350-364: Extract a typed widget fixture factory to avoid repeated double-casts.The repeated inline
_widgetobjects withas unknown as IBaseWidgetmake this
block harder to maintain. A single helper with a shared typed base object would
reduce drift across cases.Based on learnings: "In test files matching **/*.test.ts under src, when creating test helper functions that construct mock objects implementing an interface (e.g., AssetItem), prefer using satisfies InterfaceType for shape validation instead of type assertions like as Partial as InterfaceType or as any."♻️ Proposed refactor
+function createLinkedWidgetFixture( + sourceNodeId: string, + sourceWidgetName: string, + overrides: Partial<IBaseWidget> = {} +): IBaseWidget { + const base = { + sourceNodeId, + sourceWidgetName, + name: 'value', + type: 'text', + value: '', + options: {}, + y: 0 + } satisfies Partial<IBaseWidget> + + return { ...base, ...overrides } as IBaseWidget +} + function mockLinkedWidget( subgraphNode: ReturnType<typeof createSubgraphWithInput>, sourceNodeId: string, sourceWidgetName: string ) { - subgraphNode.inputs[0]._widget = { - sourceNodeId, - sourceWidgetName, - name: 'value', - type: 'text', - value: '', - options: {}, - y: 0 - } as unknown as IBaseWidget + subgraphNode.inputs[0]._widget = createLinkedWidgetFixture( + sourceNodeId, + sourceWidgetName + ) }Also applies to: 406-415, 430-438, 440-448, 462-470
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/core/graph/subgraph/promotionUtils.test.ts` around lines 350 - 364, The tests repeatedly assign inline objects to subgraphNode.inputs[0]._widget using "as unknown as IBaseWidget"; replace these with a small typed fixture factory function (e.g., createMockWidget) that returns a properly shaped object typed with "satisfies IBaseWidget" (or Partial<IBaseWidget> satisfies IBaseWidget when fields are intentionally omitted) and use that factory in mockLinkedWidget and the other similar test helpers; locate mockLinkedWidget, createSubgraphWithInput usage, and the other inline _widget assignments (the other occurrences you noted) and swap the double-cast object literals for calls to the new factory so the shape is validated without unsafe "as unknown as" casts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts`:
- Around line 146-148: The assertions are currently global (await
expect(comfyPage.page.getByText(...))) and can match unrelated UI; scope them to
the opened menu popover by targeting the popover locator (e.g., use
comfyPage.page.locator('.p-contextmenu') or a new data-testid added inside
WidgetActions.vue menu content) and then call .getByText('Hide
input')/.getByText('Show input')/.getByText('Rename') on that locator so the
checks are limited to the opened popover and deterministic.
In `@src/components/rightSidePanel/subgraph/SubgraphEditor.vue`:
- Around line 48-60: The linkedKeys computed currently constructs identity using
`${w.sourceNodeId}:${w.sourceWidgetName}` which can collide when a promoted
widget has a differing disambiguatingSourceNodeId; update the key construction
in linkedKeys (and the other places building the same identity) to include
w.disambiguatingSourceNodeId (e.g.,
`${w.sourceNodeId}:${w.sourceWidgetName}:${w.disambiguatingSourceNodeId}`) so
each promoted widget is uniquely identified; adjust any code that reads or
compares these keys to expect the extended form and ensure isPromotedWidgetView
accesses disambiguatingSourceNodeId safely.
---
Nitpick comments:
In `@browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts`:
- Around line 22-25: Guard against empty results from
comfyPage.nodeOps.getNodeRefsByTitle before dereferencing: check the returned
array from getNodeRefsByTitle (used where subgraphNode is assigned and also in
the similar block around lines 133-136) and assert it has at least one element
(e.g., expect(refs.length).toBeGreaterThan(0) or throw a descriptive Error)
before accessing [0]; fail fast with a clear message mentioning the nodeTitle so
the test failure is actionable.
In `@src/core/graph/subgraph/promotionUtils.test.ts`:
- Around line 373-385: Combine the two redundant negative tests into a single
test that asserts isLinkedPromotion returns false for both "no inputs match" and
"empty inputs" scenarios: replace the two it blocks with one it('returns false
when no linked input is present', ...) that creates subgraph via
createTestSubgraph and subgraphNode via createTestSubgraphNode and then calls
expect(isLinkedPromotion(subgraphNode, '999', 'nonexistent')).toBe(false) and
expect(isLinkedPromotion(subgraphNode, '1', 'value')).toBe(false); keep
references to isLinkedPromotion, createTestSubgraph, and createTestSubgraphNode
so the assertion coverage remains but the tests are not redundant.
- Around line 350-364: The tests repeatedly assign inline objects to
subgraphNode.inputs[0]._widget using "as unknown as IBaseWidget"; replace these
with a small typed fixture factory function (e.g., createMockWidget) that
returns a properly shaped object typed with "satisfies IBaseWidget" (or
Partial<IBaseWidget> satisfies IBaseWidget when fields are intentionally
omitted) and use that factory in mockLinkedWidget and the other similar test
helpers; locate mockLinkedWidget, createSubgraphWithInput usage, and the other
inline _widget assignments (the other occurrences you noted) and swap the
double-cast object literals for calls to the new factory so the shape is
validated without unsafe "as unknown as" casts.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ea621a47-40f9-48ff-9638-60fe503784a8
📒 Files selected for processing (10)
browser_tests/fixtures/selectors.tsbrowser_tests/tests/subgraphPromotedWidgetPanel.spec.tssrc/components/rightSidePanel/RightSidePanel.vuesrc/components/rightSidePanel/parameters/SectionWidgets.vuesrc/components/rightSidePanel/parameters/WidgetActions.test.tssrc/components/rightSidePanel/parameters/WidgetActions.vuesrc/components/rightSidePanel/subgraph/SubgraphEditor.vuesrc/components/rightSidePanel/subgraph/SubgraphNodeWidget.vuesrc/core/graph/subgraph/promotionUtils.test.tssrc/core/graph/subgraph/promotionUtils.ts
There was a problem hiding this comment.
♻️ Duplicate comments (1)
browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts (1)
145-147:⚠️ Potential issue | 🟡 MinorScope WidgetActions assertions to the opened menu container.
Line 145–147 currently query the whole page, so unrelated “Hide input” / “Show input” / “Rename” text can affect this test. Assert inside the opened menu popover only.
Suggested fix
await moreButtons.first().click() - await expect(comfyPage.page.getByText('Hide input')).toHaveCount(0) - await expect(comfyPage.page.getByText('Show input')).toHaveCount(0) - await expect(comfyPage.page.getByText('Rename')).toBeVisible() + const actionsMenu = comfyPage.page.getByRole('menu').last() + await expect(actionsMenu.getByText('Hide input')).toHaveCount(0) + await expect(actionsMenu.getByText('Show input')).toHaveCount(0) + await expect(actionsMenu.getByText('Rename')).toBeVisible()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts` around lines 145 - 147, The assertions currently query the whole page via comfyPage.page.getByText and can match unrelated elements; scope them to the opened menu popover by first locating the menu container (e.g., using comfyPage.page.locator(...) or comfyPage.page.getByRole('menu') / a specific data-testid/aria selector for the promoted-widget menu) and then call menuLocator.getByText('Hide input'), menuLocator.getByText('Show input'), and menuLocator.getByText('Rename') so the checks are limited to items inside the opened menu popover.
🧹 Nitpick comments (1)
browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts (1)
28-36: Avoid unconditional editor-toggle clicks to reduce state-dependent flakiness.Line 30 always toggles, which can collapse the section when it is already open. Gate the click on section visibility (or expanded state) before toggling.
Suggested fix
const editorToggle = comfyPage.page.getByTestId(TestIds.subgraphEditor.toggle) await expect(editorToggle).toBeVisible() - await editorToggle.click() const shownSection = comfyPage.page.getByTestId( TestIds.subgraphEditor.shownSection ) + if (!(await shownSection.isVisible())) { + await editorToggle.click() + } await expect(shownSection).toBeVisible() return shownSection🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts` around lines 28 - 36, The test currently always clicks editorToggle (TestIds.subgraphEditor.toggle) which can collapse the panel if it's already open; instead, query the shownSection (TestIds.subgraphEditor.shownSection) or check the toggle's expanded state on comfyPage.page and only call editorToggle.click() when the section is not visible/expanded. Implement a conditional: await shownSection.isVisible() (or check editorToggle.getAttribute('aria-expanded')) and only perform the click when that check indicates the panel is closed, then assert shownSection is visible before returning it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts`:
- Around line 145-147: The assertions currently query the whole page via
comfyPage.page.getByText and can match unrelated elements; scope them to the
opened menu popover by first locating the menu container (e.g., using
comfyPage.page.locator(...) or comfyPage.page.getByRole('menu') / a specific
data-testid/aria selector for the promoted-widget menu) and then call
menuLocator.getByText('Hide input'), menuLocator.getByText('Show input'), and
menuLocator.getByText('Rename') so the checks are limited to items inside the
opened menu popover.
---
Nitpick comments:
In `@browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts`:
- Around line 28-36: The test currently always clicks editorToggle
(TestIds.subgraphEditor.toggle) which can collapse the panel if it's already
open; instead, query the shownSection (TestIds.subgraphEditor.shownSection) or
check the toggle's expanded state on comfyPage.page and only call
editorToggle.click() when the section is not visible/expanded. Implement a
conditional: await shownSection.isVisible() (or check
editorToggle.getAttribute('aria-expanded')) and only perform the click when that
check indicates the panel is closed, then assert shownSection is visible before
returning it.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 24bd6c6e-18c1-4d6a-a9a8-75e168294070
📒 Files selected for processing (2)
browser_tests/tests/subgraphPromotedWidgetPanel.spec.tssrc/core/graph/subgraph/promotionUtils.test.ts
✅ Files skipped from review due to trivial changes (1)
- src/core/graph/subgraph/promotionUtils.test.ts
- Guard against missing node refs in E2E helpers with explicit assertion - Scope menu-item assertions to MoreButton popover via data-testid - Add data-testid to MoreButton popover content - Extract linkedWidget fixture factory in unit tests - Consolidate redundant negative test cases - Fix isLinkedPromotion optional chaining returning boolean | undefined
3598591 to
55423a5
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts (1)
81-101: Consider asserting link icon count for consistency.The test correctly validates that linked promoted widgets show link icons and no eye icons. However, Line 95 asserts only that the first link icon is visible without first checking the count. While
.first().toBeVisible()will fail if no icons exist (making the test safe), explicitly assertingawait expect(linkIcons).not.toHaveCount(0)before Line 95 would align with the pattern used in the first test (Line 74) and improve clarity.✨ Optional: Add explicit count check
const linkIcons = shownSection.getByTestId( TestIds.subgraphEditor.iconLink ) +await expect(linkIcons).not.toHaveCount(0) await expect(linkIcons.first()).toBeVisible()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts` around lines 81 - 101, Add an explicit non-zero count assertion for the link icons before checking visibility: in the test body (function starting with test('linked promoted widgets show link icon instead of eye icon', ...)) after obtaining linkIcons (const linkIcons = shownSection.getByTestId(TestIds.subgraphEditor.iconLink)), insert an assertion like await expect(linkIcons).not.toHaveCount(0) before awaiting expect(linkIcons.first()).toBeVisible(); this mirrors the pattern used earlier and makes the intent explicit while keeping the existing checks for iconEye count intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts`:
- Around line 81-101: Add an explicit non-zero count assertion for the link
icons before checking visibility: in the test body (function starting with
test('linked promoted widgets show link icon instead of eye icon', ...)) after
obtaining linkIcons (const linkIcons =
shownSection.getByTestId(TestIds.subgraphEditor.iconLink)), insert an assertion
like await expect(linkIcons).not.toHaveCount(0) before awaiting
expect(linkIcons.first()).toBeVisible(); this mirrors the pattern used earlier
and makes the intent explicit while keeping the existing checks for iconEye
count intact.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3f24c1cf-85ad-4a6b-840e-b0a78bbdddbc
📒 Files selected for processing (5)
browser_tests/fixtures/selectors.tsbrowser_tests/tests/subgraphPromotedWidgetPanel.spec.tssrc/components/button/MoreButton.vuesrc/core/graph/subgraph/promotionUtils.test.tssrc/core/graph/subgraph/promotionUtils.ts
✅ Files skipped from review due to trivial changes (1)
- src/components/button/MoreButton.vue
🚧 Files skipped from review as they are similar to previous changes (2)
- src/core/graph/subgraph/promotionUtils.test.ts
- browser_tests/fixtures/selectors.ts
Summary
Fix four bugs in the subgraph promoted widget panel where linked promotions were not distinguished from independent ones, causing incorrect UI state in both the SubgraphEditor (Settings) panel and the Parameters tab WidgetActions menu.
Changes
isLinkedPromotionhelper to correctly identify widgets driven by subgraph input connections. FixdisambiguatingSourceNodeIdlookup mismatch that brokeisWidgetShownOnParentsandhandleHideInputfor non-nested promoted widgets. Replace fragile CSS icon selectors withdata-testidattributes.Bugs fixed
Companion fix PR for #10502 (red-green test PR). All 4 E2E tests from #10502 now pass:
SubgraphEditoronly checkednode.id === -1(physical) — linked promotions from subgraph input connections were not detectedisLinkedPromotionhelper that checksinput._widgetbindings;SubgraphNodeWidget:is-physicalprop now covers both physical and linked casesisPhysicalprop was only true fornode.id === -1:is-physicalcondition to includeisLinkedPromotioncheckSubgraphEditorpassedwidget.nameinstead ofwidget.label || widget.name:widget-namebinding to preferwidget.labelv-if="hasParents"didn't exclude linked promotionscanToggleVisibilitycomputed that combineshasParentswith!isLinkedcheck viaisLinkedPromotionAdditional bugs discovered and fixed
SectionWidgets.isWidgetShownOnParentsusedgetSourceNodeId(widget)which falls back towidget.sourceNodeIdwhendisambiguatingSourceNodeIdis undefined — this mismatches the promotion store key (undefined)widget.disambiguatingSourceNodeIddirectlyWidgetActions.handleHideInputusedgetSourceNodeId(widget)for the same reason —demote()couldn't find the entry to removewidget.disambiguatingSourceNodeIddirectlyTests added
E2E (Playwright) —
browser_tests/tests/subgraphPromotedWidgetPanel.spec.tssubgraph-nested-promotionfixture)widget.labelis displayed when set, notwidget.nameUnit (Vitest) —
src/core/graph/subgraph/promotionUtils.test.ts7 tests covering
isLinkedPromotion: basic matching, negative cases, nested subgraph withdisambiguatingSourceNodeId, multiple inputs, and mixed linked/independent state.Unit (Vitest) —
src/components/rightSidePanel/parameters/WidgetActions.test.tsisSubgraphNode: () => falseto mock nodes to prevent crash from newisLinkedcomputedReview Focus
isLinkedPromotionreadsinput._widget(WeakRef-backed, non-reactive) directly in the template. This is intentional —_widgetbindings are set during subgraph initialization before the user opens the panel, so stale reads don't occur in practice. A computed-based approach was attempted but reverted because_widgetchanges cannot trigger Vue reactivity.getSourceNodeIdremoval inSectionWidgetsandWidgetActionsis intentional — the old fallback (?? widget.sourceNodeId) caused key mismatches with the promotion store for non-nested widgets.Screenshots
Before

After
