test: Add test to prevent regression of workflow corruption during graph loading#10623
Conversation
📝 WalkthroughWalkthroughA new Playwright browser test file is added to validate the "Change Tracker - isLoadingGraph guard" behavior. The test suite verifies that workflow state remains correct when switching between tabs while a graph is loading. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 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 |
🎨 Storybook: ✅ Built — View Storybook |
🎭 Playwright: ✅ 719 passed, 0 failed · 4 flaky📊 Browser Reports
|
📦 Bundle: 5.09 MB gzip 🟢 -68 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) • ⚪ 0 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 — 61.1 kB (baseline 61.1 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed / 8 unchanged Data & Services — 2.96 MB (baseline 2.96 MB) • ⚪ 0 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.42 MB (baseline 8.42 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-27T17:25:07.996Z",
"gitSha": "4defe467dea10f0d94d761ea9febcbc66f06ebe0",
"branch": "pysssss/test/switching-tab-regression-test",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2004.6290000000226,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.339,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 369.78,
"heapDeltaBytes": 21040692,
"heapUsedBytes": 63360452,
"domNodes": 22,
"jsHeapTotalBytes": 22544384,
"scriptDurationMs": 25.593999999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "canvas-idle",
"durationMs": 2010.5649999999855,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.174,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 374.4789999999999,
"heapDeltaBytes": 21004156,
"heapUsedBytes": 64715176,
"domNodes": 22,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 26.962999999999994,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "canvas-idle",
"durationMs": 1998.80399999995,
"styleRecalcs": 12,
"styleRecalcDurationMs": 10.293,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 366.475,
"heapDeltaBytes": 20124148,
"heapUsedBytes": 63010304,
"domNodes": 23,
"jsHeapTotalBytes": 22544384,
"scriptDurationMs": 20.072,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000073
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1877.758999999969,
"styleRecalcs": 75,
"styleRecalcDurationMs": 42.361000000000004,
"layouts": 12,
"layoutDurationMs": 5.569,
"taskDurationMs": 831.9960000000001,
"heapDeltaBytes": 15857480,
"heapUsedBytes": 58253784,
"domNodes": 58,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 131.672,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1988.2809999999722,
"styleRecalcs": 84,
"styleRecalcDurationMs": 43.669,
"layouts": 12,
"layoutDurationMs": 3.626,
"taskDurationMs": 969.129,
"heapDeltaBytes": 15876976,
"heapUsedBytes": 58334040,
"domNodes": 66,
"jsHeapTotalBytes": 23855104,
"scriptDurationMs": 123.59800000000001,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1809.7270000000663,
"styleRecalcs": 76,
"styleRecalcDurationMs": 38.441,
"layouts": 12,
"layoutDurationMs": 3.9020000000000006,
"taskDurationMs": 751.7909999999999,
"heapDeltaBytes": 15533724,
"heapUsedBytes": 58244232,
"domNodes": 60,
"jsHeapTotalBytes": 24117248,
"scriptDurationMs": 121.53699999999999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1720.158999999967,
"styleRecalcs": 32,
"styleRecalcDurationMs": 16.636000000000003,
"layouts": 6,
"layoutDurationMs": 0.745,
"taskDurationMs": 300.87,
"heapDeltaBytes": 24594912,
"heapUsedBytes": 67423872,
"domNodes": 79,
"jsHeapTotalBytes": 20971520,
"scriptDurationMs": 24.815000000000005,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000027
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1753.2620000000065,
"styleRecalcs": 34,
"styleRecalcDurationMs": 18.89,
"layouts": 6,
"layoutDurationMs": 1.5389999999999997,
"taskDurationMs": 315.072,
"heapDeltaBytes": 24610844,
"heapUsedBytes": 67349708,
"domNodes": 82,
"jsHeapTotalBytes": 20447232,
"scriptDurationMs": 27.983999999999995,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000073
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1770.6819999999652,
"styleRecalcs": 32,
"styleRecalcDurationMs": 21.159000000000002,
"layouts": 6,
"layoutDurationMs": 0.8469999999999999,
"taskDurationMs": 306.765,
"heapDeltaBytes": 24643896,
"heapUsedBytes": 67060560,
"domNodes": 79,
"jsHeapTotalBytes": 20447232,
"scriptDurationMs": 27.046999999999997,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "dom-widget-clipping",
"durationMs": 539.8910000000114,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.653,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 324.682,
"heapDeltaBytes": 6131196,
"heapUsedBytes": 49178236,
"domNodes": 14,
"jsHeapTotalBytes": 13107200,
"scriptDurationMs": 54.607,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "dom-widget-clipping",
"durationMs": 557.1360000000141,
"styleRecalcs": 14,
"styleRecalcDurationMs": 11.088999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 345.059,
"heapDeltaBytes": -1823332,
"heapUsedBytes": 50884060,
"domNodes": 22,
"jsHeapTotalBytes": 15990784,
"scriptDurationMs": 58.934000000000005,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "dom-widget-clipping",
"durationMs": 523.832000000084,
"styleRecalcs": 13,
"styleRecalcDurationMs": 7.755000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 331.227,
"heapDeltaBytes": 6697260,
"heapUsedBytes": 50328580,
"domNodes": 22,
"jsHeapTotalBytes": 13893632,
"scriptDurationMs": 58.02299999999999,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "large-graph-idle",
"durationMs": 2029.5429999999897,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.848000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 563.34,
"heapDeltaBytes": 4627008,
"heapUsedBytes": 54865472,
"domNodes": -257,
"jsHeapTotalBytes": 15929344,
"scriptDurationMs": 96.17,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "large-graph-idle",
"durationMs": 2015.9230000000434,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.745999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 571.5110000000001,
"heapDeltaBytes": 19814316,
"heapUsedBytes": 71473944,
"domNodes": -253,
"jsHeapTotalBytes": 14675968,
"scriptDurationMs": 95.064,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "large-graph-idle",
"durationMs": 2047.166000000061,
"styleRecalcs": 12,
"styleRecalcDurationMs": 9.361999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 579.7909999999999,
"heapDeltaBytes": 5632580,
"heapUsedBytes": 55254892,
"domNodes": -252,
"jsHeapTotalBytes": 15929344,
"scriptDurationMs": 96.12799999999999,
"eventListeners": -121,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "large-graph-pan",
"durationMs": 2127.1050000000287,
"styleRecalcs": 70,
"styleRecalcDurationMs": 16.605,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1058.298,
"heapDeltaBytes": 16417476,
"heapUsedBytes": 67978876,
"domNodes": -258,
"jsHeapTotalBytes": 18231296,
"scriptDurationMs": 372.335,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "large-graph-pan",
"durationMs": 2141.985000000034,
"styleRecalcs": 69,
"styleRecalcDurationMs": 16.007,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1105.157,
"heapDeltaBytes": 19549564,
"heapUsedBytes": 71220840,
"domNodes": -260,
"jsHeapTotalBytes": 17969152,
"scriptDurationMs": 388.243,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "large-graph-pan",
"durationMs": 2109.2409999999973,
"styleRecalcs": 69,
"styleRecalcDurationMs": 15.879000000000005,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1065.211,
"heapDeltaBytes": -5319868,
"heapUsedBytes": 47580744,
"domNodes": -257,
"jsHeapTotalBytes": 21491712,
"scriptDurationMs": 365.55,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "large-graph-zoom",
"durationMs": 3134.1059999999743,
"styleRecalcs": 65,
"styleRecalcDurationMs": 15.736,
"layouts": 60,
"layoutDurationMs": 7.738,
"taskDurationMs": 1304.197,
"heapDeltaBytes": 7250708,
"heapUsedBytes": 61962708,
"domNodes": -266,
"jsHeapTotalBytes": 17240064,
"scriptDurationMs": 463.79800000000006,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "large-graph-zoom",
"durationMs": 3152.238000000011,
"styleRecalcs": 66,
"styleRecalcDurationMs": 17.02,
"layouts": 60,
"layoutDurationMs": 7.646,
"taskDurationMs": 1320.521,
"heapDeltaBytes": 9351040,
"heapUsedBytes": 64614052,
"domNodes": -264,
"jsHeapTotalBytes": 15249408,
"scriptDurationMs": 476.02299999999997,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "large-graph-zoom",
"durationMs": 3134.65999999994,
"styleRecalcs": 65,
"styleRecalcDurationMs": 16.813000000000002,
"layouts": 60,
"layoutDurationMs": 7.644,
"taskDurationMs": 1293.215,
"heapDeltaBytes": 6752760,
"heapUsedBytes": 61486296,
"domNodes": -266,
"jsHeapTotalBytes": 17240064,
"scriptDurationMs": 451.566,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "minimap-idle",
"durationMs": 2059.886000000006,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.498999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 543.5490000000001,
"heapDeltaBytes": 4478928,
"heapUsedBytes": 57019372,
"domNodes": -259,
"jsHeapTotalBytes": 16453632,
"scriptDurationMs": 95.47999999999999,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "minimap-idle",
"durationMs": 2043.7430000000063,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.771999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 556.332,
"heapDeltaBytes": 20497712,
"heapUsedBytes": 72456032,
"domNodes": -258,
"jsHeapTotalBytes": 15929344,
"scriptDurationMs": 101.71,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "minimap-idle",
"durationMs": 2024.0720000000465,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.028,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 528.093,
"heapDeltaBytes": -7011876,
"heapUsedBytes": 46413512,
"domNodes": -260,
"jsHeapTotalBytes": 16510976,
"scriptDurationMs": 87.23599999999999,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000073
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 587.931000000026,
"styleRecalcs": 49,
"styleRecalcDurationMs": 12.400999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 363.767,
"heapDeltaBytes": 6537052,
"heapUsedBytes": 49182932,
"domNodes": 22,
"jsHeapTotalBytes": 12582912,
"scriptDurationMs": 126.247,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 562.7210000000105,
"styleRecalcs": 47,
"styleRecalcDurationMs": 10.479000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 340.92,
"heapDeltaBytes": 6442716,
"heapUsedBytes": 50780108,
"domNodes": 20,
"jsHeapTotalBytes": 13631488,
"scriptDurationMs": 115.386,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 525.7930000000215,
"styleRecalcs": 48,
"styleRecalcDurationMs": 10.488000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 331.688,
"heapDeltaBytes": 6734628,
"heapUsedBytes": 50725664,
"domNodes": 21,
"jsHeapTotalBytes": 13369344,
"scriptDurationMs": 110.791,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "subgraph-idle",
"durationMs": 2004.854000000023,
"styleRecalcs": 12,
"styleRecalcDurationMs": 8.536999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 346.858,
"heapDeltaBytes": 19814988,
"heapUsedBytes": 62859108,
"domNodes": 23,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 19.442999999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "subgraph-idle",
"durationMs": 1996.0330000000113,
"styleRecalcs": 11,
"styleRecalcDurationMs": 10.110000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 349.402,
"heapDeltaBytes": 19882348,
"heapUsedBytes": 62899392,
"domNodes": 22,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 19.349999999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000073
},
{
"name": "subgraph-idle",
"durationMs": 2005.4710000000568,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.468,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 340.009,
"heapDeltaBytes": 19889652,
"heapUsedBytes": 62899112,
"domNodes": 21,
"jsHeapTotalBytes": 23330816,
"scriptDurationMs": 18.185,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1971.8809999999962,
"styleRecalcs": 85,
"styleRecalcDurationMs": 45.724000000000004,
"layouts": 16,
"layoutDurationMs": 4.699,
"taskDurationMs": 896.894,
"heapDeltaBytes": 11819672,
"heapUsedBytes": 54492972,
"domNodes": 74,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 93.252,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1996.1210000000165,
"styleRecalcs": 88,
"styleRecalcDurationMs": 46.931,
"layouts": 16,
"layoutDurationMs": 4.652,
"taskDurationMs": 940.9110000000001,
"heapDeltaBytes": 2352372,
"heapUsedBytes": 54504352,
"domNodes": 74,
"jsHeapTotalBytes": 25690112,
"scriptDurationMs": 93.652,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1697.9179999999587,
"styleRecalcs": 75,
"styleRecalcDurationMs": 36.113,
"layouts": 16,
"layoutDurationMs": 6.324000000000001,
"taskDurationMs": 671.84,
"heapDeltaBytes": 11743408,
"heapUsedBytes": 54823680,
"domNodes": 61,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 88.822,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000027
},
{
"name": "vue-large-graph-idle",
"durationMs": 10643.439999999999,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 10627.072,
"heapDeltaBytes": -33395952,
"heapUsedBytes": 162514768,
"domNodes": -8331,
"jsHeapTotalBytes": 23945216,
"scriptDurationMs": 579.811,
"eventListeners": -16468,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.329999999999927
},
{
"name": "vue-large-graph-idle",
"durationMs": 10895.164000000022,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 10882.683,
"heapDeltaBytes": -44555652,
"heapUsedBytes": 165708548,
"domNodes": -8331,
"jsHeapTotalBytes": 26828800,
"scriptDurationMs": 554.5390000000001,
"eventListeners": -16466,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.32000000000007
},
{
"name": "vue-large-graph-idle",
"durationMs": 10592.265999999996,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 10580.347,
"heapDeltaBytes": -40213296,
"heapUsedBytes": 157259648,
"domNodes": -8331,
"jsHeapTotalBytes": 15294464,
"scriptDurationMs": 538.761,
"eventListeners": -16470,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.31999999999971
},
{
"name": "vue-large-graph-pan",
"durationMs": 12432.480999999996,
"styleRecalcs": 64,
"styleRecalcDurationMs": 13.601000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12414.766999999998,
"heapDeltaBytes": -22589708,
"heapUsedBytes": 172944164,
"domNodes": -8331,
"jsHeapTotalBytes": 24383488,
"scriptDurationMs": 839.5890000000002,
"eventListeners": -16468,
"totalBlockingTimeMs": 0,
"frameDurationMs": 20
},
{
"name": "vue-large-graph-pan",
"durationMs": 12375.834999999995,
"styleRecalcs": 64,
"styleRecalcDurationMs": 15.017000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12356.28,
"heapDeltaBytes": -19957964,
"heapUsedBytes": 173236120,
"domNodes": -8329,
"jsHeapTotalBytes": 25169920,
"scriptDurationMs": 801.074,
"eventListeners": -16462,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.329999999999927
},
{
"name": "vue-large-graph-pan",
"durationMs": 12534.01000000008,
"styleRecalcs": 67,
"styleRecalcDurationMs": 17.412999999999982,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12514.356000000002,
"heapDeltaBytes": -34690396,
"heapUsedBytes": 159555028,
"domNodes": -8331,
"jsHeapTotalBytes": -4452352,
"scriptDurationMs": 821.378,
"eventListeners": -16464,
"totalBlockingTimeMs": 81,
"frameDurationMs": 18.340000000000146
},
{
"name": "workflow-execution",
"durationMs": 458.27500000001464,
"styleRecalcs": 22,
"styleRecalcDurationMs": 21.679999999999996,
"layouts": 5,
"layoutDurationMs": 1.7820000000000003,
"taskDurationMs": 116.75700000000003,
"heapDeltaBytes": 4668468,
"heapUsedBytes": 50255856,
"domNodes": 190,
"jsHeapTotalBytes": 524288,
"scriptDurationMs": 21.224,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "workflow-execution",
"durationMs": 444.95899999992616,
"styleRecalcs": 16,
"styleRecalcDurationMs": 24.335,
"layouts": 5,
"layoutDurationMs": 1.2599999999999998,
"taskDurationMs": 125.31199999999998,
"heapDeltaBytes": 4495780,
"heapUsedBytes": 48914236,
"domNodes": 156,
"jsHeapTotalBytes": 262144,
"scriptDurationMs": 30.315999999999995,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "workflow-execution",
"durationMs": 446.42899999996644,
"styleRecalcs": 16,
"styleRecalcDurationMs": 19.924999999999997,
"layouts": 5,
"layoutDurationMs": 1.289,
"taskDurationMs": 115.213,
"heapDeltaBytes": 4479420,
"heapUsedBytes": 50082888,
"domNodes": 156,
"jsHeapTotalBytes": 524288,
"scriptDurationMs": 27.021000000000004,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000027
}
]
} |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
browser_tests/tests/changeTrackerLoadGuard.spec.ts (2)
18-20: Avoid hard-coding the default workflow node count (7).This makes the test fragile to default-workflow changes unrelated to the regression. Capture the baseline once, then assert it stays unchanged across tab switches.
♻️ Suggested refactor
- // Tab 0: default workflow (7 nodes) - expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(7) + // Tab 0: capture baseline node count for this environment/workflow + const workflowANodeCount = await comfyPage.nodeOps.getGraphNodesCount() + expect(workflowANodeCount).toBeGreaterThan(0) @@ - expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(7) + expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(workflowANodeCount) @@ - expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(7) + expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(workflowANodeCount)Based on learnings: default workflow composition can change, making fixed count assertions fragile.
Also applies to: 52-53, 63-63
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@browser_tests/tests/changeTrackerLoadGuard.spec.ts` around lines 18 - 20, The test currently hard-codes the default workflow node count (expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(7)), which is brittle; change the test to capture the baseline count once (call comfyPage.nodeOps.getGraphNodesCount() and store it in a variable like baselineCount) and then reuse that variable for subsequent assertions across tab switches instead of asserting against 7; update all occurrences (the initial check and the later checks around lines referenced) to compare to baselineCount so the test adapts to legitimate default-workflow changes.
55-56: Avoid relying on the literal'Unsaved Workflow'tab label.That label can change (naming/localization/product copy). Give tab 1 a deterministic name and switch by that.
♻️ Suggested refactor
await comfyPage.menu.topbar.triggerTopbarCommand(['New']) await comfyPage.nextFrame() expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(0) + await comfyPage.menu.topbar.saveWorkflow('workflow-b') @@ - const tab1 = comfyPage.menu.topbar.getWorkflowTab('Unsaved Workflow') + const tab1 = comfyPage.menu.topbar.getWorkflowTab('workflow-b')🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@browser_tests/tests/changeTrackerLoadGuard.spec.ts` around lines 55 - 56, The test currently selects the tab by the literal label 'Unsaved Workflow'; instead create or set a deterministic workflow name earlier in the test (e.g., const workflowName = 'test-workflow-<uniqueId>') and use that variable when locating the tab instead of the hardcoded string; update the call to comfyPage.menu.topbar.getWorkflowTab(...) to use the deterministic workflowName and ensure any workflow creation/rename step uses the same variable so the tab lookup is stable across copy/localization changes (refer to comfyPage.menu.topbar.getWorkflowTab and the tab1 variable).
🤖 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/changeTrackerLoadGuard.spec.ts`:
- Around line 18-20: The test currently hard-codes the default workflow node
count (expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(7)), which is
brittle; change the test to capture the baseline count once (call
comfyPage.nodeOps.getGraphNodesCount() and store it in a variable like
baselineCount) and then reuse that variable for subsequent assertions across tab
switches instead of asserting against 7; update all occurrences (the initial
check and the later checks around lines referenced) to compare to baselineCount
so the test adapts to legitimate default-workflow changes.
- Around line 55-56: The test currently selects the tab by the literal label
'Unsaved Workflow'; instead create or set a deterministic workflow name earlier
in the test (e.g., const workflowName = 'test-workflow-<uniqueId>') and use that
variable when locating the tab instead of the hardcoded string; update the call
to comfyPage.menu.topbar.getWorkflowTab(...) to use the deterministic
workflowName and ensure any workflow creation/rename step uses the same variable
so the tab lookup is stable across copy/localization changes (refer to
comfyPage.menu.topbar.getWorkflowTab and the tab1 variable).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 58c7a549-e6b9-4bdc-892a-bedf692ca774
📒 Files selected for processing (1)
browser_tests/tests/changeTrackerLoadGuard.spec.ts
| await comfyPage.page.evaluate(() => { | ||
| window.app!.registerExtension({ | ||
| name: 'TestCheckStateDuringLoad', | ||
| afterConfigureGraph() { | ||
| const workflow = (window.app!.extensionManager as WorkspaceStore) | ||
| .workflow.activeWorkflow | ||
| if (!workflow) throw new Error('No workflow found') | ||
| // Bypass the guard to reproduce the corruption bug: | ||
| // ; (workflow.changeTracker.constructor as unknown as { isLoadingGraph: boolean }).isLoadingGraph = false | ||
|
|
||
| // Simulate the user clicking during graph loading | ||
| workflow.changeTracker.checkState() | ||
| } | ||
| }) | ||
| }) |
There was a problem hiding this comment.
This is pretty creative. @coderabbitai make a tracking Issue to make a helper for registering any of the ComfyExtension hooks/callbacks in a browser test easily (if the time comes when we are doing this in multiple places).
There was a problem hiding this comment.
🧩 Analysis chain
@christian-byrne, I've created the tracking issue! Here's the summary of what was captured:
Issue created: the title is "test: Add helper for registering ComfyExtension hooks/callbacks in browser tests", assigned to you.
The issue documents:
- The motivating pattern from this PR (inline
page.evaluate(() => window.app!.registerExtension({...}))) - A sketch of a proposed API (e.g.,
comfyPage.registerExtension({...})as a helper onComfyPageor a dedicatedExtensionHelperfixture) - Key benefits: reduced boilerplate, a single place to handle
page.evaluateserialization constraints, and clearer test intent - Backlinks to this PR and comment
🏁 Script executed:
#!/bin/bash
gh issue create \
--repo Comfy-Org/ComfyUI_frontend \
--assignee christian-byrne \
--title "test: Add helper for registering ComfyExtension hooks/callbacks in browser tests" \
--body "## Summary
When writing E2E regression tests that need to simulate extension behavior, tests currently must inline a full \`window.app!.registerExtension({...})\` call inside \`page.evaluate()\`. See the pattern introduced in PR `#10623`:
\`\`\`ts
await comfyPage.page.evaluate(() => {
window.app!.registerExtension({
name: 'TestCheckStateDuringLoad',
afterConfigureGraph() {
// ...
}
})
})
\`\`\`
As more tests adopt this pattern, it will be beneficial to introduce a reusable browser-test helper (e.g., on \`ComfyPage\` or a dedicated \`ExtensionHelper\`) that allows callers to declaratively register any \`ComfyExtension\` hook/callback without repeating the boilerplate evaluate wrapper.
## Proposed API (sketch)
\`\`\`ts
// Possible surface on ComfyPage or a new ExtensionHelper fixture
await comfyPage.registerExtension({
name: 'MyTestExtension',
afterConfigureGraph() { ... },
// ...any other ComfyExtension hooks
})
\`\`\`
## Motivation
- Reduces boilerplate across spec files.
- Provides a single place to handle serialization constraints (e.g., functions passed to \`page.evaluate\` must be serializable).
- Makes test intent clearer — callers express *what* hooks to register, not *how* to wire them into the page context.
## Context
- Tracking issue requested in: https://github.com/Comfy-Org/ComfyUI_frontend/pull/10623#discussion_r3002645765
- First usage / motivating example: https://github.com/Comfy-Org/ComfyUI_frontend/pull/10623
- Requested by: `@christian-byrne`
"
Length of output: 58
@christian-byrne, the tracking issue has been created: #10626
It documents the motivating pattern from this PR, a sketch of a proposed comfyPage.registerExtension(...) helper API, and the key benefits (reduced boilerplate, centralized handling of page.evaluate serialization constraints, and clearer test intent). It's assigned to you.
🧠 Learnings used
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 10547
File: browser_tests/tests/workflowPersistence.spec.ts:459-471
Timestamp: 2026-03-27T05:12:16.400Z
Learning: In `browser_tests/tests/workflowPersistence.spec.ts` (Comfy-Org/ComfyUI_frontend PR `#10547`), the G10 test for PR `#8715` (transient blob:/api-view URL serialization) intentionally validates only the static/default workflow export — not an executed/transient widget state. Putting an image widget into its executed state requires actual model inference (image output generation), which is not available in the current E2E test infrastructure. The test still covers the regression if the `widget.serialize` disable path breaks. Do not flag the absence of transient-state setup in this test; full integration coverage is tracked separately.
Learnt from: xmarre
Repo: Comfy-Org/ComfyUI_frontend PR: 10367
File: browser_tests/tests/workflowPersistence.spec.ts:68-77
Timestamp: 2026-03-21T16:57:48.590Z
Learning: In `browser_tests/tests/workflowPersistence.spec.ts`, the `waitForTabStatePersistence` helper intentionally does NOT validate `activeIndex` on the persisted `OpenPaths` session storage entry. The test immediately overwrites `openPointer.activeIndex = 1` after the wait resolves, so the previously persisted `activeIndex` value is irrelevant. The only invariant being guarded is that `openPointer.paths.length >= expectedMinPaths` (i.e., all workflow paths have been flushed into storage before the test proceeds). Do not suggest adding `activeIndex` validation to this wait predicate in future reviews.
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 10548
File: browser_tests/tests/propertiesPanel/propertiesPanel.spec.ts:57-66
Timestamp: 2026-03-27T05:12:28.417Z
Learning: In Comfy-Org/ComfyUI_frontend browser tests for the properties panel (`browser_tests/tests/propertiesPanel/propertiesPanel.spec.ts`), do not assert that the rendered node-entry count in the Nodes tab matches `comfyPage.nodeOps.getNodeCount()`. The default workflow composition can change, making count-based assertions fragile. Prefer asserting that a known node (e.g., 'KSampler') is visible. For filter assertions, asserting the matching entry is visible is sufficient; a non-match disappearing assertion is deferred to a future iteration.
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/AGENTS.md:0-0
Timestamp: 2026-02-03T20:33:39.759Z
Learning: Applies to browser_tests/**/*.spec.ts : Use premade JSON workflows in the `assets/` directory to load desired graph state in E2E tests
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7415
File: browser_tests/tests/mobileBaseline.spec.ts:17-22
Timestamp: 2025-12-13T05:54:35.779Z
Learning: In browser_tests tests for the Comfy-Org/ComfyUI_frontend repository, the `comfyPage.loadWorkflow()` method already handles all necessary synchronization and waiting. No additional `await comfyPage.nextFrame()` call is needed before taking screenshots after loading a workflow.
Learnt from: pythongosssss
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: :0-0
Timestamp: 2026-03-18T15:38:16.169Z
Learning: In Comfy-Org/ComfyUI_frontend, race conditions between WebSocket events and HTTP responses (e.g., the WebSocket/HTTP race in job tracking in `src/renderer/extensions/linearMode/linearOutputStore.ts`) are not suitable candidates for E2E testing because the non-deterministic ordering cannot be reliably reproduced in Playwright. Unit tests (e.g., in `linearOutputStore.test.ts`) that deterministically simulate event ordering are the correct approach. Do not request E2E tests for such race condition scenarios.
Learnt from: jaeone94
Repo: Comfy-Org/ComfyUI_frontend PR: 10309
File: browser_tests/tests/missingMedia.spec.ts:141-145
Timestamp: 2026-03-25T15:30:09.361Z
Learning: In `browser_tests/tests/missingMedia.spec.ts` (Comfy-Org/ComfyUI_frontend PR `#10309`), the library-select E2E test uses `test.skip()` when no options are found because the OSS test server's input directory content varies per environment and cannot be guaranteed to contain specific library items. The upload flow test already covers the full 2-step confirm path. Do not flag this `test.skip()` guard as a regression risk or suggest pre-uploading files (which creates inter-test dependency). If deterministic coverage is needed in the future, suggest mocking the input-file list endpoint via Playwright's `page.route()` instead.
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: :0-0
Timestamp: 2026-03-27T05:26:01.429Z
Learning: In Comfy-Org/ComfyUI_frontend PR `#10526`, the async scope-disposal race in `src/platform/cloud/notification/components/DesktopCloudNotificationController.vue` (desktop/mac-only cloud promo flow) is intentionally covered only at the unit level via `DesktopCloudNotificationController.test.ts`, using a deferred `settingStore.load()` promise and fake timers to deterministically verify that the dialog is not scheduled or shown after the owning scope is disposed. A Playwright E2E test is not appropriate here because: (1) the non-deterministic ordering cannot be reliably reproduced in Playwright, (2) the feature requires a real macOS Electron environment not available in CI, and (3) a timing-driven pseudo-E2E would be flaky and weaker than the unit test. If broader coverage is needed later, the right approach is a separate desktop promo smoke test, not a race-specific E2E regression. Do not request a Playwright/E2E test for this specific race condition scenario.
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/AGENTS.md:0-0
Timestamp: 2026-03-17T08:09:41.456Z
Learning: Applies to browser_tests/**/*.spec.ts : Refer to `docs/guidance/playwright.md` for Playwright best practices when writing E2E tests
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 9554
File: browser_tests/fixtures/ComfyPage.ts:189-193
Timestamp: 2026-03-12T10:03:55.383Z
Learning: In browser_tests, seed localStorage-based feature flags at init time using FeatureFlagHelper.seedFlags() (which uses page.addInitScript) before any navigation. Ensure this happens before comfyPage.setup() is called. The comfyPageFixture currently yields after calling comfyPage.setup(), so do not defer setup() from the fixture or add a pre-setup hook, as changing this would break existing tests.
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-27T01:16:09.991Z
Learning: Applies to browser_tests/**/*.spec.ts : Write E2E tests in `browser_tests/**/*.spec.ts` using Playwright
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-27T01:16:09.991Z
Learning: Applies to browser_tests/**/*.spec.ts : Follow Playwright best practices from Playwright documentation for E2E tests
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-03-24T17:16:12.386Z
Learning: For bug-fix PRs, ensure end-to-end regression coverage by either: (1) changing at least one file under `browser_tests/`, or (2) providing a concrete, non-placeholder explanation in the PR description of why an end-to-end regression test was not added. Use only PR metadata available in the review context (PR title, commit subjects, files changed relative to base, PR description). Do not rely on shell commands or inspect files outside the PR.
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/AGENTS.md:0-0
Timestamp: 2026-03-17T08:09:41.456Z
Learning: Applies to browser_tests/**/*.ts : Run `pnpm typecheck:browser` after modifying TypeScript files in the browser_tests directory
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 10140
File: apps/website/package.json:1-79
Timestamp: 2026-03-17T07:28:27.677Z
Learning: In Comfy-Org/ComfyUI_frontend, PRs managed via Graphite stacked sets (using `gt split`) may show browser_tests/ files in the diff as artifacts of the split process. These files contain no actual changes relative to origin/main and should not be flagged as unrelated scope changes. Verify against the PR title/description stacking context before raising scope concerns.
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-03-17T07:39:57.336Z
Learning: End-to-end regression coverage for fixes: Use PR metadata (PR title, commit subjects, files changed relative to PR base, PR description) to verify bug-fix PRs include end-to-end regression test coverage. Pass if: (1) PR title and commit subjects don't use bug-fix language (fix, fixed, fixes, fixing, bugfix, hotfix), OR (2) PR changes at least one file under browser_tests/, OR (3) PR description includes concrete non-placeholder explanation for why end-to-end regression test wasn't added. Fail otherwise, mentioning the bug-fix signal found and asking to add/update Playwright regression test under browser_tests/ or provide concrete explanation in PR description.
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-03-27T04:23:01.330Z
Learning: End-to-end regression coverage for fixes: For PRs that include bug-fix language in the title or commit subjects (fix, fixed, fixes, fixing, bugfix, hotfix), ensure at least one of the following: (1) files are changed under browser_tests/, or (2) the PR description includes a concrete, non-placeholder explanation of why an end-to-end regression test was not added. Only use PR metadata available in the review context (PR title, commit subjects, files changed relative to base, PR description). Do not rely on shell commands, reverse diffs, or files outside the PR.
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 8951
File: src/platform/workflow/management/stores/workflowStore.ts:301-301
Timestamp: 2026-03-05T23:21:47.113Z
Learning: In src/platform/workflow/management/stores/workflowStore.ts, `createTemporary()` and `createNewTemporary()` intentionally inject a UUID `id` into workflow data via `ensureWorkflowId()` to give workflows stable identity for sharing. This is a known behavioral change introduced in PR `#8951`. Extensions relying on serialized content matching their input may be silently affected.
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7415
File: browser_tests/tests/mobileBaseline.spec.ts:7-15
Timestamp: 2025-12-13T05:34:15.488Z
Learning: In Playwright tests for the ComfyUI frontend, the toPass() assertion uses incremental backoff during retries. When a test may involve async operations, increasing the timeout (e.g., to 5000 ms) can be sufficient instead of aggressively extending timeouts. Apply this understanding to tests under browser_tests/tests/; if not resolved, review the toPass() backoff behavior and ensure timeouts align with expected async completion without masking issues.
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8248
File: src/platform/assets/schemas/assetSchema.ts:109-118
Timestamp: 2026-01-22T23:44:59.888Z
Learning: Do not export declarations (e.g., schemas, constants, types) unless they are actually used elsewhere in the codebase. Keep the public API surface minimal by exporting only items with concrete usage. This reduces coupling and keeps imports explicit. In src/platform/assets/schemas/assetSchema.ts, ensure only necessary exports are exposed; remove unused exports or switch to non-exported/internal where appropriate.
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8722
File: src/composables/maskeditor/useBrushDrawing.ts:5-5
Timestamp: 2026-02-08T03:19:12.009Z
Learning: Treat unresolved import warnings caused by pnpm workspace catalog mappings (pnpm-workspace.yaml) and unplugin-typegpu in vite.config.mts as false positives. Do not flag these in PR reviews; they are expected in sandboxes without node_modules and are not indicative of actual missing dependencies in the TS codebase.
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 8966
File: src/extensions/core/load3d.ts:427-430
Timestamp: 2026-02-19T02:06:23.468Z
Learning: In TypeScript, you can use typeof SomeClass in type annotation positions (e.g., param: typeof LGraphNode) even when SomeClass is imported with import type. This is a type query that only exists at compile time and is erased in runtime, so it is safe to combine with type-only imports. Apply this pattern to TS files broadly when you need a type that references the shape of a class or constructor function without importing the value at runtime.
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 8966
File: src/extensions/core/uploadAudio.ts:91-94
Timestamp: 2026-02-19T02:06:38.395Z
Learning: In TypeScript files, you can use a type annotation like 'nodeType: typeof MyClass' even if MyClass is imported via 'import type'. Both the type-only import and 'typeof' operate at the type level and are erased at compile time. This pattern is commonly used for constructor types (e.g., 'nodeType: typeof LGraphNode'). Apply this pattern across TypeScript files in the repository (src/**/*.ts) as appropriate, ensuring the imported symbol is a type-only import when possible for clarity and to avoid runtime imports.
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8992
File: src/lib/litegraph/src/widgets/GradientSliderWidget.ts:18-18
Timestamp: 2026-02-20T21:08:19.814Z
Learning: When drawing with CanvasRenderingContext2D in TypeScript/JavaScript, wrap the drawing logic with ctx.save() at the start and ctx.restore() at the end to preserve and restore the canvas state. Do not manually destructure and restore individual properties (e.g., fillStyle, strokeStyle); rely on save/restore to manage state changes in a scoped manner. This should be applied to all TS files that perform canvas drawing.
Learnt from: dante01yoon
Repo: Comfy-Org/ComfyUI_frontend PR: 9075
File: src/scripts/api.featureFlags.test.ts:237-268
Timestamp: 2026-02-22T04:27:33.379Z
Learning: In Vite/Vitest, import.meta.env.DEV is true for any mode that is not 'production' (i.e., DEV is the opposite of PROD, and can be true in 'test', 'development', etc.). Do not assume DEV implies only 'development' mode. When reviewing code and tests, treat DEV as a non-production flag and verify environment-specific logic accordingly. Reference: https://vite.dev/guide/env-and-mode#modes
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 9427
File: src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownMenuFilter.vue:33-33
Timestamp: 2026-03-06T00:53:28.835Z
Learning: When reviewing code, note that the enforce-canonical-classes (better-tailwindcss) rule may auto-fix Tailwind v3 !class-name syntax by converting it to v4 class-name! syntax. Do not treat these auto-fixed class-name! instances as newly introduced issues; the perceived change is in syntax placement, not in usage or intent. This guidance applies across all .vue and .ts files in the repository.
Learnt from: sonnybox
Repo: Comfy-Org/ComfyUI_frontend PR: 9446
File: src/renderer/extensions/vueNodes/widgets/components/WidgetTextarea.vue:45-45
Timestamp: 2026-03-06T01:55:00.013Z
Learning: Treat wrap-break-word as a valid Tailwind CSS utility for overflow-wrap: break-word in Tailwind v4+ projects. Do not flag this class as invalid in any Vue (.vue) or TypeScript (.ts/.tsx) files within the repository (e.g., Comfy-Org/ComfyUI_frontend) or other Tailwind v4+ projects. When reviewing, verify that the class is used to enable word breaking in long text content and reference the Tailwind docs: https://tailwindcss.com/docs/overflow-wrap.
Learnt from: Yourz
Repo: Comfy-Org/ComfyUI_frontend PR: 10310
File: browser_tests/tests/topbarMenuDismissal.spec.ts:0-0
Timestamp: 2026-03-19T16:16:40.350Z
Learning: In Comfy-Org/ComfyUI_frontend, when reviewing changes limited to adding/revising `:modal="false"` (or similar modal/non-modal behavior props) on reakit-ui/reka-ui primitives such as `ContextMenuRoot` and `DropdownMenuRoot`, do not request Playwright/E2E regression coverage solely for that prop-level change. The dismissal behavior for modal vs non-modal is documented and tested upstream in the reka-ui library; only ask for E2E tests when the change affects ComfyUI_frontend behavior beyond these primitive prop wiring differences (e.g., custom logic, integration behavior, or new user flows not covered upstream).
Learnt from: jaeone94
Repo: Comfy-Org/ComfyUI_frontend PR: 10302
File: browser_tests/tests/dialog.spec.ts:31-32
Timestamp: 2026-03-22T14:21:45.793Z
Learning: In E2E tests, when asserting errors rendered by `ErrorOverlay.vue` (container has `data-testid="error-overlay"`), do not use `TestIds.dialogs.missingNodePacksGroup` (`error-group-missing-node`) or `TestIds.dialogs.missingModelsGroup` (`error-group-missing-model`)—those test IDs are rendered on `PropertiesAccordionItem` elements inside `TabErrors.vue`, not inside the overlay. For overlay-level assertions, scope queries to the overlay container and assert using the visible text (e.g., `errorOverlay.getByText(/Missing Node Packs/)`) rather than `getByTestId`.
Learnt from: viva-jinyi
Repo: Comfy-Org/ComfyUI_frontend PR: 10499
File: browser_tests/tests/vueNodes/widgets/load/formDropdownPosition.spec.ts:55-59
Timestamp: 2026-03-25T03:30:28.527Z
Learning: In Comfy-Org/ComfyUI_frontend browser tests, follow the established canvas zoom convention: set zoom in tests using direct assignment `canvas.ds.scale = s` (as used by `browser_tests/fixtures/helpers/CanvasHelper.ts` in `CanvasHelper.setScale()`). Do not recommend switching to `canvas.ds.changeScale()` in spec files when the test re-queries element positions using Playwright `boundingBox()`, since boundingBox coordinates are already in the post-transform viewport space and any offset recalculation associated with `changeScale()` is not relevant there.
Learnt from: jtydhr88
Repo: Comfy-Org/ComfyUI_frontend PR: 10574
File: browser_tests/tests/maskEditor.spec.ts:70-75
Timestamp: 2026-03-26T23:45:21.231Z
Learning: In Comfy-Org/ComfyUI_frontend browser tests, only use `dispatchEvent('contextmenu', { bubbles: true, cancelable: true, button: 2 })` when the ComfyUI canvas z-999 overlay is intercepting pointer events on canvas-level DOM widgets. Vue-rendered elements (e.g., selectors like `.lg-node-header`) are not occluded by the canvas overlay—so for those, follow the established pattern used in `imagePreview.spec.ts`: click to select with `locator.click()` (left click), then trigger the context menu with `locator.click({ button: 'right' })`.
Also, ensure you’re using the correct Playwright API: `getNodeLocator()` returns a Playwright `Locator` (not a NodeReference). Therefore, `Locator.click('title')` is invalid syntax—if you need to click a string part of a node, only `NodeReference.click('title')` supports the string part argument.
Learnt from: dante01yoon
Repo: Comfy-Org/ComfyUI_frontend PR: 10580
File: browser_tests/tests/sidebar/textSizeRename.spec.ts:20-20
Timestamp: 2026-03-27T13:58:46.129Z
Learning: In ComfyUI_frontend browser tests, when selecting the element with test id `node-tree-leaf`, use `getByTestId('node-tree-leaf')` with the raw string even if `node-tree-leaf` is intentionally NOT present in the centralized `TestIds` (see `browser_tests/fixtures/selectors.ts`). Do not flag this as a violation of the centralized `TestIds` guideline for this specific test id.
| .workflow.activeWorkflow | ||
| if (!workflow) throw new Error('No workflow found') | ||
| // Bypass the guard to reproduce the corruption bug: | ||
| // ; (workflow.changeTracker.constructor as unknown as { isLoadingGraph: boolean }).isLoadingGraph = false |
There was a problem hiding this comment.
question (non-blocking): What does this mean exactly?
There was a problem hiding this comment.
In order to recreate the issue, uncommenting the line of code removes the guard, triggering the workflow overwrite on tab switch
…aph loading (#10623) ## Summary Adds regression test for #9531 ## Changes - **What**: - registers extension that triggers checkState during afterConfigureGraph (at this point the workflow data and active graph are not in sync), previously causing it to overwrite the workflow data - switches between tabs - ensures they are not corrupted Line 35 can be uncommented to cause the test to fail by clearing the isLoadingGraph flag ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10623-test-Add-test-to-prevent-regression-of-workflow-corruption-during-graph-loading-3306d73d3650815fbf02ef23dfdcddce) by [Unito](https://www.unito.io)
…aph loading (#10623) ## Summary Adds regression test for #9531 ## Changes - **What**: - registers extension that triggers checkState during afterConfigureGraph (at this point the workflow data and active graph are not in sync), previously causing it to overwrite the workflow data - switches between tabs - ensures they are not corrupted Line 35 can be uncommented to cause the test to fail by clearing the isLoadingGraph flag ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10623-test-Add-test-to-prevent-regression-of-workflow-corruption-during-graph-loading-3306d73d3650815fbf02ef23dfdcddce) by [Unito](https://www.unito.io)
- Remove module-level comment block - Rename describe to 'Workflow Persistence' - Remove redundant UseNewMenu setting (already default in fixture) - Replace JSDoc PR references with Playwright test.info().annotations - Remove duplicate G1 tests (covered by merged PR #10623) - Use waitForWorkflowIdle() helper instead of inline isBusy patterns - Move API workflow JSON to fixtures/assets - Reduce excessive comments throughout - Consolidate localStorage patterns Addresses review feedback: #10547 (comment) #10547 (comment) #10547 (comment) #10547 (comment) #10547 (comment) #10547 (comment) #10547 (comment) #10547 (comment) #10547 (comment) #10547 (comment) #10547 (comment)
Summary
Adds regression test for #9531
Changes
Line 35 can be uncommented to cause the test to fail by clearing the isLoadingGraph flag
┆Issue is synchronized with this Notion page by Unito