test: add unit tests for commandStore, extensionStore, widgetStore (STORE-04)#10647
test: add unit tests for commandStore, extensionStore, widgetStore (STORE-04)#10647christian-byrne wants to merge 5 commits intomainfrom
Conversation
…TORE-04) commandStore (18 tests): registerCommand, registerCommands, getCommand, execute with metadata/errorHandler, isRegistered, loadExtensionCommands, ComfyCommandImpl label/icon/menubarLabel resolution extensionStore (16 tests): registerExtension with validation, enable/disable lifecycle, always-disabled hardcoded extensions, enabledExtensions filter, isExtensionReadOnly, inactive disabled tracking, core extension capture and third-party detection widgetStore (9 tests): core widget availability, custom widget registration, core/custom precedence, inputIsWidget for v1 and v2 InputSpec formats
🎨 Storybook: ✅ Built — View Storybook |
🎭 Playwright: ✅ 761 passed, 0 failed · 5 flaky📊 Browser Reports
|
📝 WalkthroughWalkthroughAdds three new Vitest test suites for Pinia stores: Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (1 error)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
📦 Bundle: 5.09 MB gzip 🟢 -301 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 — 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) • ⚪ 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.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-29T02:42:27.550Z",
"gitSha": "4f4f2b221243940de7066247f3161789429c8b8f",
"branch": "test/priority-store-tests",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2020.955999999984,
"styleRecalcs": 11,
"styleRecalcDurationMs": 10.521999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 350.857,
"heapDeltaBytes": 20949148,
"heapUsedBytes": 64709432,
"domNodes": 22,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 24.249,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "canvas-idle",
"durationMs": 2015.7770000000141,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.574,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 343.353,
"heapDeltaBytes": 20319788,
"heapUsedBytes": 62826468,
"domNodes": 22,
"jsHeapTotalBytes": 22544384,
"scriptDurationMs": 20.221,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "canvas-idle",
"durationMs": 2001.846999999998,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.36,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 327.407,
"heapDeltaBytes": 20020360,
"heapUsedBytes": 63168328,
"domNodes": 17,
"jsHeapTotalBytes": 22544384,
"scriptDurationMs": 14.340000000000002,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2091.790000000003,
"styleRecalcs": 85,
"styleRecalcDurationMs": 48.736000000000004,
"layouts": 12,
"layoutDurationMs": 3.993,
"taskDurationMs": 1032.753,
"heapDeltaBytes": 18002932,
"heapUsedBytes": 66871840,
"domNodes": 67,
"jsHeapTotalBytes": 25427968,
"scriptDurationMs": 146.411,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1715.1870000000145,
"styleRecalcs": 73,
"styleRecalcDurationMs": 34.521,
"layouts": 12,
"layoutDurationMs": 3.3379999999999996,
"taskDurationMs": 700.3340000000001,
"heapDeltaBytes": 15274748,
"heapUsedBytes": 58385776,
"domNodes": 56,
"jsHeapTotalBytes": 23592960,
"scriptDurationMs": 123.997,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2035.712999999987,
"styleRecalcs": 81,
"styleRecalcDurationMs": 43.454,
"layouts": 12,
"layoutDurationMs": 3.418999999999999,
"taskDurationMs": 954.0940000000002,
"heapDeltaBytes": 16731644,
"heapUsedBytes": 59079616,
"domNodes": 67,
"jsHeapTotalBytes": 23330816,
"scriptDurationMs": 138.34,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1741.5260000000217,
"styleRecalcs": 31,
"styleRecalcDurationMs": 17.872,
"layouts": 6,
"layoutDurationMs": 0.5720000000000001,
"taskDurationMs": 311.051,
"heapDeltaBytes": 24692148,
"heapUsedBytes": 67413184,
"domNodes": 79,
"jsHeapTotalBytes": 20971520,
"scriptDurationMs": 26.244,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000073
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1748.3740000000125,
"styleRecalcs": 32,
"styleRecalcDurationMs": 17.575,
"layouts": 6,
"layoutDurationMs": 0.5640000000000001,
"taskDurationMs": 289.48799999999994,
"heapDeltaBytes": 24734000,
"heapUsedBytes": 67076768,
"domNodes": 80,
"jsHeapTotalBytes": 20709376,
"scriptDurationMs": 22.964000000000002,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1721.104999999966,
"styleRecalcs": 32,
"styleRecalcDurationMs": 17.547,
"layouts": 6,
"layoutDurationMs": 0.568,
"taskDurationMs": 288.856,
"heapDeltaBytes": 24686548,
"heapUsedBytes": 67106500,
"domNodes": 79,
"jsHeapTotalBytes": 20447232,
"scriptDurationMs": 23.163000000000004,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "dom-widget-clipping",
"durationMs": 604.2290000000037,
"styleRecalcs": 12,
"styleRecalcDurationMs": 8.011999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 364.178,
"heapDeltaBytes": 6086304,
"heapUsedBytes": 48939480,
"domNodes": 20,
"jsHeapTotalBytes": 14155776,
"scriptDurationMs": 69.804,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "dom-widget-clipping",
"durationMs": 542.1709999999962,
"styleRecalcs": 14,
"styleRecalcDurationMs": 9.898,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 336.53700000000003,
"heapDeltaBytes": 6786204,
"heapUsedBytes": 49120132,
"domNodes": 23,
"jsHeapTotalBytes": 13107200,
"scriptDurationMs": 63.153,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000027
},
{
"name": "dom-widget-clipping",
"durationMs": 549.6899999999414,
"styleRecalcs": 13,
"styleRecalcDurationMs": 9.08,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 333.495,
"heapDeltaBytes": 6145656,
"heapUsedBytes": 48968384,
"domNodes": 22,
"jsHeapTotalBytes": 14417920,
"scriptDurationMs": 64.824,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "large-graph-idle",
"durationMs": 2022.0339999999908,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.884999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 533.1709999999999,
"heapDeltaBytes": 5345384,
"heapUsedBytes": 55863456,
"domNodes": -255,
"jsHeapTotalBytes": 16191488,
"scriptDurationMs": 94.79799999999999,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "large-graph-idle",
"durationMs": 1995.1340000000073,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.463,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 519.795,
"heapDeltaBytes": 21275976,
"heapUsedBytes": 72966080,
"domNodes": -256,
"jsHeapTotalBytes": 15724544,
"scriptDurationMs": 91.083,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.67999999999993
},
{
"name": "large-graph-idle",
"durationMs": 2015.9720000000334,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.258000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 518.4960000000001,
"heapDeltaBytes": 4052240,
"heapUsedBytes": 55469120,
"domNodes": -260,
"jsHeapTotalBytes": 16191488,
"scriptDurationMs": 97.831,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "large-graph-pan",
"durationMs": 2147.4249999999984,
"styleRecalcs": 68,
"styleRecalcDurationMs": 14.592999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1067.844,
"heapDeltaBytes": 15025288,
"heapUsedBytes": 68327244,
"domNodes": -260,
"jsHeapTotalBytes": 19017728,
"scriptDurationMs": 387.17199999999997,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "large-graph-pan",
"durationMs": 2119.9609999999893,
"styleRecalcs": 69,
"styleRecalcDurationMs": 15.952000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1042.618,
"heapDeltaBytes": 17297312,
"heapUsedBytes": 70599628,
"domNodes": -261,
"jsHeapTotalBytes": 18755584,
"scriptDurationMs": 387.546,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "large-graph-pan",
"durationMs": 2184.407999999962,
"styleRecalcs": 69,
"styleRecalcDurationMs": 16.02,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1131.58,
"heapDeltaBytes": -6783600,
"heapUsedBytes": 47478692,
"domNodes": -260,
"jsHeapTotalBytes": 20705280,
"scriptDurationMs": 417.407,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000073
},
{
"name": "large-graph-zoom",
"durationMs": 3143.2490000000257,
"styleRecalcs": 66,
"styleRecalcDurationMs": 16.236,
"layouts": 60,
"layoutDurationMs": 7.243,
"taskDurationMs": 1304.33,
"heapDeltaBytes": 7110252,
"heapUsedBytes": 61577476,
"domNodes": -264,
"jsHeapTotalBytes": 16715776,
"scriptDurationMs": 485.50000000000006,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "large-graph-zoom",
"durationMs": 3146.3020000000483,
"styleRecalcs": 67,
"styleRecalcDurationMs": 17.185000000000002,
"layouts": 60,
"layoutDurationMs": 7.012,
"taskDurationMs": 1277.1560000000002,
"heapDeltaBytes": 1379804,
"heapUsedBytes": 55807396,
"domNodes": -262,
"jsHeapTotalBytes": 17764352,
"scriptDurationMs": 478.01,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "large-graph-zoom",
"durationMs": 3192.421999999965,
"styleRecalcs": 67,
"styleRecalcDurationMs": 18.171000000000003,
"layouts": 60,
"layoutDurationMs": 7.585999999999999,
"taskDurationMs": 1410.307,
"heapDeltaBytes": 14433536,
"heapUsedBytes": 69973172,
"domNodes": -261,
"jsHeapTotalBytes": 16248832,
"scriptDurationMs": 533.409,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "minimap-idle",
"durationMs": 2053.7669999999935,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.971999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 525.4890000000001,
"heapDeltaBytes": 4381968,
"heapUsedBytes": 56412088,
"domNodes": -258,
"jsHeapTotalBytes": 16453632,
"scriptDurationMs": 101.04299999999999,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "minimap-idle",
"durationMs": 2039.761999999996,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.805,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 500.27299999999997,
"heapDeltaBytes": 2885120,
"heapUsedBytes": 57027412,
"domNodes": -263,
"jsHeapTotalBytes": 15929344,
"scriptDurationMs": 85.95,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "minimap-idle",
"durationMs": 2001.263999999992,
"styleRecalcs": 7,
"styleRecalcDurationMs": 6.435,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 503.56899999999996,
"heapDeltaBytes": 621204,
"heapUsedBytes": 54831868,
"domNodes": -265,
"jsHeapTotalBytes": 14880768,
"scriptDurationMs": 84.46799999999999,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 572.1750000000156,
"styleRecalcs": 48,
"styleRecalcDurationMs": 12.078999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 355.51200000000006,
"heapDeltaBytes": 6139400,
"heapUsedBytes": 49301240,
"domNodes": 22,
"jsHeapTotalBytes": 13893632,
"scriptDurationMs": 126.70100000000001,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 538.6670000000322,
"styleRecalcs": 48,
"styleRecalcDurationMs": 12.046999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 359.13100000000003,
"heapDeltaBytes": 6452332,
"heapUsedBytes": 50612428,
"domNodes": 22,
"jsHeapTotalBytes": 13631488,
"scriptDurationMs": 126.23299999999999,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 538.7279999999919,
"styleRecalcs": 47,
"styleRecalcDurationMs": 10.566999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 347.46700000000004,
"heapDeltaBytes": 6350548,
"heapUsedBytes": 49348316,
"domNodes": 20,
"jsHeapTotalBytes": 14417920,
"scriptDurationMs": 121.54899999999999,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000027
},
{
"name": "subgraph-idle",
"durationMs": 2002.5989999999751,
"styleRecalcs": 11,
"styleRecalcDurationMs": 10.108,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 334.611,
"heapDeltaBytes": 19929952,
"heapUsedBytes": 62919380,
"domNodes": 22,
"jsHeapTotalBytes": 22544384,
"scriptDurationMs": 18.492999999999995,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "subgraph-idle",
"durationMs": 2015.265999999997,
"styleRecalcs": 13,
"styleRecalcDurationMs": 12.765,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 353.16700000000003,
"heapDeltaBytes": 19965896,
"heapUsedBytes": 62760568,
"domNodes": 25,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 25.211000000000002,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "subgraph-idle",
"durationMs": 2007.69600000001,
"styleRecalcs": 11,
"styleRecalcDurationMs": 10.433,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 339.727,
"heapDeltaBytes": 19926388,
"heapUsedBytes": 62753952,
"domNodes": 22,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 19.479,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1991.8419999999912,
"styleRecalcs": 86,
"styleRecalcDurationMs": 53.64099999999999,
"layouts": 16,
"layoutDurationMs": 4.923,
"taskDurationMs": 907.8919999999999,
"heapDeltaBytes": 11771132,
"heapUsedBytes": 54866436,
"domNodes": 75,
"jsHeapTotalBytes": 23592960,
"scriptDurationMs": 102.21699999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1977.8190000000109,
"styleRecalcs": 84,
"styleRecalcDurationMs": 46.257999999999996,
"layouts": 16,
"layoutDurationMs": 4.394,
"taskDurationMs": 886.164,
"heapDeltaBytes": 11856616,
"heapUsedBytes": 54911776,
"domNodes": 73,
"jsHeapTotalBytes": 22020096,
"scriptDurationMs": 98.545,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1965.6790000000228,
"styleRecalcs": 83,
"styleRecalcDurationMs": 43.056,
"layouts": 16,
"layoutDurationMs": 4.353,
"taskDurationMs": 909.9379999999999,
"heapDeltaBytes": 11773472,
"heapUsedBytes": 54892912,
"domNodes": 72,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 99.958,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "viewport-pan-sweep",
"durationMs": 8214.75700000002,
"styleRecalcs": 251,
"styleRecalcDurationMs": 43.475,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4163.579000000001,
"heapDeltaBytes": 39591300,
"heapUsedBytes": 90453396,
"domNodes": -257,
"jsHeapTotalBytes": 34222080,
"scriptDurationMs": 1627.3120000000001,
"eventListeners": -109,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000073
},
{
"name": "viewport-pan-sweep",
"durationMs": 8216.404000000011,
"styleRecalcs": 252,
"styleRecalcDurationMs": 45.565000000000005,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3604.418,
"heapDeltaBytes": 24755376,
"heapUsedBytes": 75368692,
"domNodes": -255,
"jsHeapTotalBytes": 19542016,
"scriptDurationMs": 1221.0149999999999,
"eventListeners": -111,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.67999999999993
},
{
"name": "viewport-pan-sweep",
"durationMs": 8179.8350000000255,
"styleRecalcs": 249,
"styleRecalcDurationMs": 41.941,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3681.836,
"heapDeltaBytes": 19333728,
"heapUsedBytes": 71971152,
"domNodes": -260,
"jsHeapTotalBytes": 21639168,
"scriptDurationMs": 1297.925,
"eventListeners": -111,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999989
},
{
"name": "vue-large-graph-idle",
"durationMs": 12263.558999999987,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12252.097,
"heapDeltaBytes": -35093668,
"heapUsedBytes": 165955592,
"domNodes": -8331,
"jsHeapTotalBytes": 27615232,
"scriptDurationMs": 608.513,
"eventListeners": -16462,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.32000000000007
},
{
"name": "vue-large-graph-idle",
"durationMs": 11835.770999999966,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 11819.184,
"heapDeltaBytes": -30831388,
"heapUsedBytes": 165582832,
"domNodes": -8331,
"jsHeapTotalBytes": 28139520,
"scriptDurationMs": 567.083,
"eventListeners": -16468,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.329999999999927
},
{
"name": "vue-large-graph-idle",
"durationMs": 12049.90900000007,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12037.541000000001,
"heapDeltaBytes": -22111580,
"heapUsedBytes": 186377540,
"domNodes": -5048,
"jsHeapTotalBytes": 28139520,
"scriptDurationMs": 599.843,
"eventListeners": -16462,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.339999999999783
},
{
"name": "vue-large-graph-pan",
"durationMs": 14188.022999999986,
"styleRecalcs": 65,
"styleRecalcDurationMs": 13.30300000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14166.91,
"heapDeltaBytes": -16021288,
"heapUsedBytes": 185125364,
"domNodes": -8331,
"jsHeapTotalBytes": 24907776,
"scriptDurationMs": 924.704,
"eventListeners": -16462,
"totalBlockingTimeMs": 0,
"frameDurationMs": 19.990000000000144
},
{
"name": "vue-large-graph-pan",
"durationMs": 14075.519000000042,
"styleRecalcs": 64,
"styleRecalcDurationMs": 12.955999999999968,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14054.618999999999,
"heapDeltaBytes": -21617860,
"heapUsedBytes": 173351520,
"domNodes": -8331,
"jsHeapTotalBytes": 24907776,
"scriptDurationMs": 827.269,
"eventListeners": -16460,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.339999999999783
},
{
"name": "vue-large-graph-pan",
"durationMs": 14283.21799999992,
"styleRecalcs": 67,
"styleRecalcDurationMs": 13.248999999999983,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14258.741,
"heapDeltaBytes": -53356692,
"heapUsedBytes": 147807576,
"domNodes": -8333,
"jsHeapTotalBytes": -958464,
"scriptDurationMs": 903.171,
"eventListeners": -16486,
"totalBlockingTimeMs": 34,
"frameDurationMs": 18.329999999999927
},
{
"name": "workflow-execution",
"durationMs": 117.24900000001526,
"styleRecalcs": 11,
"styleRecalcDurationMs": 25.9,
"layouts": 5,
"layoutDurationMs": 2.017,
"taskDurationMs": 104.64500000000001,
"heapDeltaBytes": 2948228,
"heapUsedBytes": 46876180,
"domNodes": 148,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 26.898000000000007,
"eventListeners": 37,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "workflow-execution",
"durationMs": 450.0679999999875,
"styleRecalcs": 18,
"styleRecalcDurationMs": 23.536,
"layouts": 5,
"layoutDurationMs": 1.5190000000000001,
"taskDurationMs": 120.01899999999999,
"heapDeltaBytes": 4478504,
"heapUsedBytes": 48551548,
"domNodes": 159,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 28.866000000000003,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "workflow-execution",
"durationMs": 457.0620000000645,
"styleRecalcs": 19,
"styleRecalcDurationMs": 27.227999999999998,
"layouts": 5,
"layoutDurationMs": 1.3019999999999998,
"taskDurationMs": 137.102,
"heapDeltaBytes": 4549540,
"heapUsedBytes": 48509016,
"domNodes": 158,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 33.551,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
}
]
} |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/stores/widgetStore.test.ts`:
- Around line 73-87: Tests call inputIsWidget with objects missing the required
name field; update each fixture in the three it blocks to include a name
property (e.g., name: 'test_input') so they conform to InputSpecV2.
Specifically, in the tests using useWidgetStore and calling inputIsWidget({
type: 'STRING' }), inputIsWidget({ type: 'LATENT' }), and inputIsWidget({ type:
'MY_WIDGET' }) add name: 'test_input' (or another suitable identifier), leaving
the registerCustomWidgets call and other assertions unchanged.
🪄 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: bba183fc-835f-4135-90c8-197668da464f
📒 Files selected for processing (3)
src/stores/commandStore.test.tssrc/stores/extensionStore.test.tssrc/stores/widgetStore.test.ts
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/stores/widgetStore.test.ts (1)
10-12:⚠️ Potential issue | 🟠 MajorAvoid
unknowncasts that hide invalidInputSpecfixturesLine 10 and Line 11 bypass type-shape checks, and the v2 fixtures at Line 81, Line 86, and Line 92 still omit required fields like
name. This weakens the test contract and can mask schema regressions.💡 Suggested fix
-const v1 = (spec: unknown) => spec as InputSpecV1 -const v2 = (spec: unknown) => spec as InputSpecV2 +function v1(spec: InputSpecV1): InputSpecV1 { + return spec +} + +function v2(spec: InputSpecV2): InputSpecV2 { + return spec +} @@ - expect(store.inputIsWidget(v2({ type: 'STRING' }))).toBe(true) + expect( + store.inputIsWidget(v2({ type: 'STRING', name: 'test_input' })) + ).toBe(true) @@ - expect(store.inputIsWidget(v2({ type: 'LATENT' }))).toBe(false) + expect( + store.inputIsWidget(v2({ type: 'LATENT', name: 'test_input' })) + ).toBe(false) @@ - expect(store.inputIsWidget(v2({ type: 'MY_WIDGET' }))).toBe(true) + expect( + store.inputIsWidget(v2({ type: 'MY_WIDGET', name: 'test_input' })) + ).toBe(true)#!/bin/bash set -euo pipefail SCHEMA_FILE="$(fd -i '^nodeDefSchemaV2.ts$' src | head -n 1)" echo "Schema file: ${SCHEMA_FILE}" rg -n -C3 'export type InputSpec|name:' "${SCHEMA_FILE}" echo echo "V2 fixtures in src/stores/widgetStore.test.ts:" rg -n -C1 "v2\\(\\{\\s*type:" src/stores/widgetStore.test.tsBased on learnings: In test files matching
**/*.test.tsundersrc, prefer usingsatisfies InterfaceTypefor shape validation instead of type assertions.Also applies to: 79-93
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/stores/widgetStore.test.ts` around lines 10 - 12, The test helpers v1 and v2 cast fixtures via "spec as InputSpecV1/InputSpecV2", which hides invalid shapes; replace these casts by creating literal fixtures with the TypeScript "satisfies InputSpecV1" / "satisfies InputSpecV2" so the compiler validates required fields, and update the v2 fixtures that currently omit required properties (e.g., missing name) to include all required fields of InputSpecV2; adjust the helper usage so tests use the validated literals (or a small factory that returns the literal) instead of the unknown casts in functions named v1 and v2.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/stores/extensionStore.test.ts`:
- Around line 34-45: The test that spies on console.warn in the suite using
useExtensionStore registers a spy in the test body but only calls
warnSpy.mockRestore() at the end, which can leak if the test throws; update the
test that calls loadDisabledExtensionNames and registerExtension to ensure the
spy is always restored by wrapping the test body in try/finally (create warnSpy
via vi.spyOn(console, 'warn') before the try, run the assertions in the try, and
call warnSpy.mockRestore() in finally), or alternatively add an afterEach hook
that calls vi.restoreAllMocks() for the file; reference the test using
useExtensionStore, loadDisabledExtensionNames, registerExtension, and warnSpy
when making the change.
---
Duplicate comments:
In `@src/stores/widgetStore.test.ts`:
- Around line 10-12: The test helpers v1 and v2 cast fixtures via "spec as
InputSpecV1/InputSpecV2", which hides invalid shapes; replace these casts by
creating literal fixtures with the TypeScript "satisfies InputSpecV1" /
"satisfies InputSpecV2" so the compiler validates required fields, and update
the v2 fixtures that currently omit required properties (e.g., missing name) to
include all required fields of InputSpecV2; adjust the helper usage so tests use
the validated literals (or a small factory that returns the literal) instead of
the unknown casts in functions named v1 and v2.
🪄 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: 9411c294-ac2d-4563-a328-17b05c51cae0
📒 Files selected for processing (2)
src/stores/extensionStore.test.tssrc/stores/widgetStore.test.ts
Addresses review feedback: #10647 (comment)
Addresses review feedback: #10647 (comment)
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/stores/widgetStore.test.ts (1)
9-11: Avoidunknowncast helpers forInputSpecfixturesThese helpers erase compile-time fixture validation and can hide schema drift.
Prefer typed literals withsatisfiesso fixture shape changes fail fast.Proposed refactor
-/** Cast shorthand — the mock bypasses Zod validation, so we only need the shape `inputIsWidget` reads. */ -const v1 = (spec: unknown) => spec as InputSpecV1 -const v2 = (spec: unknown) => spec as InputSpecV2 +function v1(spec: InputSpecV1): InputSpecV1 { + return spec +} + +function v2(spec: InputSpecV2): InputSpecV2 { + return spec +}- expect(store.inputIsWidget(v1(['INT', {}]))).toBe(true) + const spec = ['INT', {}] satisfies InputSpecV1 + expect(store.inputIsWidget(v1(spec))).toBe(true)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."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/stores/widgetStore.test.ts` around lines 9 - 11, The v1 and v2 test helper functions currently erase compile-time checks by casting an unknown to InputSpecV1/InputSpecV2; replace them with helpers that return strongly-typed fixtures using TypeScript's "satisfies" operator or explicit typed object literals so the shape is validated at compile time (e.g., make v1(spec: Partial<InputSpecV1>) => ({ ...defaultV1, ...spec } satisfies InputSpecV1) and similarly for v2 with InputSpecV2), and remove the unsafe "as unknown" casts so schema drift is caught by the compiler; keep function names v1 and v2 and the InputSpecV1/InputSpecV2 types to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/stores/extensionStore.test.ts`:
- Around line 79-86: The test can pass falsely if uninstalled extensions are
treated as disabled; update the test that uses useExtensionStore(),
loadDisabledExtensionNames([]) and isExtensionEnabled(...) to first register one
hardcoded-denylisted extension and one normal control extension (using the
store's extension registration/install helper) so their installed/enabled state
is explicit, then assert isExtensionEnabled('pysssss.Locking') is false and the
control extension is true; this ensures the denylist logic (checked by
isExtensionEnabled) is actually exercised rather than relying on default
uninstalled behavior.
In `@src/stores/widgetStore.test.ts`:
- Around line 47-53: The test currently only checks that
store.widgets.get('INT') is not the override, which can pass if the widget is
missing; update the assertion to compare against the expected core widget
function so the test fails on incorrect/missing values. In the test using
useWidgetStore() and registerCustomWidgets({ INT: override }), retrieve the core
widget reference (the known core implementation used by the codebase) and assert
store.widgets.get('INT') === <coreWidgetFunction> (replace <coreWidgetFunction>
with the actual core widget symbol/name used) to ensure core widgets truly take
precedence.
---
Nitpick comments:
In `@src/stores/widgetStore.test.ts`:
- Around line 9-11: The v1 and v2 test helper functions currently erase
compile-time checks by casting an unknown to InputSpecV1/InputSpecV2; replace
them with helpers that return strongly-typed fixtures using TypeScript's
"satisfies" operator or explicit typed object literals so the shape is validated
at compile time (e.g., make v1(spec: Partial<InputSpecV1>) => ({ ...defaultV1,
...spec } satisfies InputSpecV1) and similarly for v2 with InputSpecV2), and
remove the unsafe "as unknown" casts so schema drift is caught by the compiler;
keep function names v1 and v2 and the InputSpecV1/InputSpecV2 types to locate
the change.
🪄 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: b8b49334-defb-498c-818e-88bbd0a41dc3
📒 Files selected for processing (2)
src/stores/extensionStore.test.tssrc/stores/widgetStore.test.ts
| it('always disables hardcoded extensions', () => { | ||
| const store = useExtensionStore() | ||
| store.loadDisabledExtensionNames([]) | ||
| expect(store.isExtensionEnabled('pysssss.Locking')).toBe(false) | ||
| expect(store.isExtensionEnabled('pysssss.SnapToGrid')).toBe(false) | ||
| expect(store.isExtensionEnabled('pysssss.FaviconStatus')).toBe(false) | ||
| expect(store.isExtensionEnabled('KJNodes.browserstatus')).toBe(false) | ||
| }) |
There was a problem hiding this comment.
Make the hardcoded-disable test unambiguous
Right now this can pass even if the hardcoded denylist logic regresses, depending on how uninstalled extensions are treated.
Register at least one hardcoded extension and a normal control extension before asserting enabled state.
Proposed adjustment
it('always disables hardcoded extensions', () => {
const store = useExtensionStore()
store.loadDisabledExtensionNames([])
+ store.registerExtension({ name: 'pysssss.Locking' })
+ store.registerExtension({ name: 'regular.ext' })
+
expect(store.isExtensionEnabled('pysssss.Locking')).toBe(false)
expect(store.isExtensionEnabled('pysssss.SnapToGrid')).toBe(false)
expect(store.isExtensionEnabled('pysssss.FaviconStatus')).toBe(false)
expect(store.isExtensionEnabled('KJNodes.browserstatus')).toBe(false)
+ expect(store.isExtensionEnabled('regular.ext')).toBe(true)
})As per coding guidelines: "Do not write tests that just test the mocks in Vitest; ensure tests fail when code behaves unexpectedly."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/stores/extensionStore.test.ts` around lines 79 - 86, The test can pass
falsely if uninstalled extensions are treated as disabled; update the test that
uses useExtensionStore(), loadDisabledExtensionNames([]) and
isExtensionEnabled(...) to first register one hardcoded-denylisted extension and
one normal control extension (using the store's extension registration/install
helper) so their installed/enabled state is explicit, then assert
isExtensionEnabled('pysssss.Locking') is false and the control extension is
true; this ensures the denylist logic (checked by isExtensionEnabled) is
actually exercised rather than relying on default uninstalled behavior.
| it('core widgets take precedence over custom widgets with same key', () => { | ||
| const store = useWidgetStore() | ||
| const override = vi.fn() | ||
| store.registerCustomWidgets({ INT: override }) | ||
| // Core widgets are spread last, so they win | ||
| expect(store.widgets.get('INT')).not.toBe(override) | ||
| }) |
There was a problem hiding this comment.
Strengthen the precedence assertion to avoid false positives
not.toBe(override) still passes if the value is missing/incorrect.
Assert the exact expected core widget function instead.
Proposed fix
+import { ComfyWidgets } from '@/scripts/widgets'
@@
it('core widgets take precedence over custom widgets with same key', () => {
const store = useWidgetStore()
const override = vi.fn()
store.registerCustomWidgets({ INT: override })
- // Core widgets are spread last, so they win
- expect(store.widgets.get('INT')).not.toBe(override)
+ expect(store.widgets.get('INT')).toBe(ComfyWidgets.INT)
})As per coding guidelines: "Do not write tests that just test the mocks in Vitest; ensure tests fail when code behaves unexpectedly."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/stores/widgetStore.test.ts` around lines 47 - 53, The test currently only
checks that store.widgets.get('INT') is not the override, which can pass if the
widget is missing; update the assertion to compare against the expected core
widget function so the test fails on incorrect/missing values. In the test using
useWidgetStore() and registerCustomWidgets({ INT: override }), retrieve the core
widget reference (the known core implementation used by the codebase) and assert
store.widgets.get('INT') === <coreWidgetFunction> (replace <coreWidgetFunction>
with the actual core widget symbol/name used) to ensure core widgets truly take
precedence.
Summary
Adds 43 unit tests covering three priority Pinia stores that previously had zero test coverage.
commandStore (18 tests)
registerCommand/registerCommands— single and batch registration, duplicate warninggetCommand— retrieval and undefined for missingexecute— successful execution, metadata passing, error handler delegation, missing command errorisRegistered— presence checkloadExtensionCommands— extension command registration with source, skip when no commandsComfyCommandImpl— label/icon/tooltip resolution (string vs function), menubarLabel defaultingextensionStore (16 tests)
registerExtension— name validation, duplicate detection, disabled extension warningisExtensionEnabled/loadDisabledExtensionNames— enable/disable lifecycleenabledExtensions— computed filterisExtensionReadOnly— hardcoded list checkinactiveDisabledExtensionNames— ghost extension trackinghasThirdPartyExtensionsdetectionwidgetStore (9 tests)
ComfyWidgetsinputIsWidgetfor both v1 array and v2 object InputSpec formatsPart of
Test Coverage Q2 Overhaul — Phase 5 (Unit & Component Tests)
┆Issue is synchronized with this Notion page by Unito