Download ComfyUI-MiniMaxH3-Context-Loop/tests/_legacy_widget_width_fix_js_test.mjs from dzmdd/Fast_deploy: direct link, hf CLI and curl.
- Browser
- Download file 10.3 kB
-
https://huggingface.co/dzmdd/Fast_deploy/resolve/main/ComfyUI-MiniMaxH3-Context-Loop/tests/_legacy_widget_width_fix_js_test.mjs
- Command line
-
hf download hf://dzmdd/Fast_deploy/ComfyUI-MiniMaxH3-Context-Loop/tests/_legacy_widget_width_fix_js_test.mjs
-
curl -L -o _legacy_widget_width_fix_js_test.mjs https://huggingface.co/dzmdd/Fast_deploy/resolve/main/ComfyUI-MiniMaxH3-Context-Loop/tests/_legacy_widget_width_fix_js_test.mjs
10.3 kB
| import assert from "node:assert/strict"; | |
| import fs from "node:fs"; | |
| import { | |
| LEGACY_GUARD_KEY, | |
| LEGACY_WIDTH_KEY, | |
| createLegacyWidgetWidthController, | |
| graphNodes, | |
| } from "../web/h3_legacy_widget_width_fix_core.mjs"; | |
| class FakeNode { | |
| constructor(type = "Other") { | |
| this.type = type; | |
| this.comfyClass = type; | |
| this.widgets = []; | |
| this.size = [320, 200]; | |
| this.graph = null; | |
| } | |
| addWidget(type, name, value, callback, options) { | |
| const widget = {type, name, value, callback, options, width: 240}; | |
| this.widgets.push(widget); | |
| return widget; | |
| } | |
| addDOMWidget(name, type, element, options) { | |
| const widget = {name, type, element, options, width: 250}; | |
| this.widgets.push(widget); | |
| return widget; | |
| } | |
| addCustomWidget(widget) { | |
| this.widgets.push(widget); | |
| return widget; | |
| } | |
| } | |
| const frames = []; | |
| const liteGraph = {vueNodesMode: false}; | |
| const graph = { | |
| _nodes: [], | |
| dirtyCalls: 0, | |
| setDirtyCanvas() { | |
| this.dirtyCalls += 1; | |
| }, | |
| }; | |
| let relinquished = 0; | |
| const controller = createLegacyWidgetWidthController({ | |
| getGraph: () => graph, | |
| getLiteGraph: () => liteGraph, | |
| requestFrame: (callback) => frames.push(callback), | |
| onRelinquish: () => { relinquished += 1; }, | |
| }); | |
| controller.installHooks(FakeNode.prototype); | |
| class LateWidgetNode { | |
| constructor() { | |
| this.widgets = []; | |
| this.graph = null; | |
| } | |
| addWidget() { | |
| const widget = {name: "early-method", width: 230}; | |
| this.widgets.push(widget); | |
| return widget; | |
| } | |
| } | |
| controller.installHooks(LateWidgetNode.prototype); | |
| const existing = new FakeNode(); | |
| existing.graph = graph; | |
| existing.widgets.push({name: "existing", width: 180}); | |
| graph._nodes.push(existing); | |
| const host = new FakeNode("MiniMaxH3ChainPlan"); | |
| host.graph = graph; | |
| graph._nodes.push(host); | |
| controller.addHost(host); | |
| assert.equal(controller.active, true); | |
| assert.equal(controller.hostCount, 1); | |
| assert.equal(existing.widgets[0].width, undefined); | |
| LateWidgetNode.prototype.addDOMWidget = function addDOMWidget() { | |
| const widget = {name: "late-method", width: 235}; | |
| this.widgets.push(widget); | |
| return widget; | |
| }; | |
| controller.installHooks(LateWidgetNode.prototype); | |
| const lateNode = new LateWidgetNode(); | |
| lateNode.graph = graph; | |
| assert.equal(lateNode.addDOMWidget().width, undefined); | |
| existing.widgets[0].width = 400; | |
| assert.equal(existing.widgets[0].width, undefined, "LiteGraph writes are blocked"); | |
| liteGraph.vueNodesMode = true; | |
| existing.widgets[0].width = 410; | |
| assert.equal(existing.widgets[0].width, 410, "Vue renderer writes are preserved"); | |
| liteGraph.vueNodesMode = false; | |
| const created = new FakeNode(); | |
| created.graph = graph; | |
| graph._nodes.push(created); | |
| const regular = created.addWidget("number", "regular", 0, null, {}); | |
| const dom = created.addDOMWidget("dom", "custom", {}, {}); | |
| const custom = created.addCustomWidget({name: "custom", width: 260}); | |
| assert.equal(regular.width, undefined); | |
| assert.equal(dom.width, undefined); | |
| assert.equal(custom.width, undefined); | |
| const direct = {name: "direct", width: 300}; | |
| created.widgets.push(direct); | |
| controller.nodeCreated(created); | |
| assert.equal(direct.width, undefined); | |
| const deferred = {name: "deferred", width: 320}; | |
| created.widgets.push(deferred); | |
| frames.splice(0).forEach((callback) => callback()); | |
| assert.equal(deferred.width, undefined); | |
| const nested = new FakeNode(); | |
| const subgraph = {_nodes: [nested]}; | |
| const container = new FakeNode(); | |
| container.subgraph = subgraph; | |
| graph._nodes.push(container); | |
| assert.ok(graphNodes(graph).includes(nested)); | |
| const foreign = {name: "standalone-owned"}; | |
| foreign[LEGACY_WIDTH_KEY] = 275; | |
| Object.defineProperty(foreign, "width", { | |
| configurable: true, | |
| enumerable: true, | |
| get() { return this[LEGACY_WIDTH_KEY]; }, | |
| set(value) { this[LEGACY_WIDTH_KEY] = value; }, | |
| }); | |
| Object.defineProperty(foreign, LEGACY_GUARD_KEY, { | |
| configurable: true, | |
| value: true, | |
| }); | |
| created.widgets.push(foreign); | |
| controller.repairNode(created); | |
| assert.equal(controller.ownsWidget(foreign), true); | |
| foreign.width = 500; | |
| assert.equal(foreign.width, undefined, "embedded repair takes ownership safely"); | |
| controller.setAllowed(false); | |
| assert.equal(controller.active, false); | |
| assert.equal(relinquished, 1); | |
| existing.widgets[0].width = 430; | |
| assert.equal(existing.widgets[0].width, 430, "disabling restores normal writes"); | |
| controller.setAllowed(true); | |
| assert.equal(controller.active, true); | |
| assert.equal(existing.widgets[0].width, undefined); | |
| controller.removeHost(host); | |
| assert.equal(controller.active, false); | |
| assert.equal(relinquished, 2); | |
| const integrationSource = fs.readFileSync( | |
| new URL("../web/h3_legacy_widget_width_fix.js", import.meta.url), | |
| "utf8", | |
| ); | |
| assert.match(integrationSource, /MiniMaxH3ContexLoop\.legacyWidgetWidthFix/); | |
| assert.match(integrationSource, /LegacyWidgetWidthFix/); | |
| assert.match(integrationSource, /MiniMaxH3ChainScenePromptEditor/); | |
| assert.match(integrationSource, /MiniMaxH3ChainCheckpointManager/); | |
| assert.match(integrationSource, /MiniMaxH3ChainRunManager/); | |
| assert.match(integrationSource, /MiniMaxH3ContexLoopSeamProbe/); | |
| assert.match(integrationSource, /afterConfigureGraph/); | |
| assert.match(integrationSource, /controller\.syncHosts/); | |
| const integrationFrames = []; | |
| const settingValues = new Map(); | |
| let registeredSetting = null; | |
| let extension = null; | |
| class IntegrationNode extends FakeNode {} | |
| const integrationGraph = { | |
| _nodes: [], | |
| setDirtyCanvas() {}, | |
| }; | |
| globalThis.LGraphNode = IntegrationNode; | |
| globalThis.LiteGraph = {vueNodesMode: false}; | |
| globalThis.requestAnimationFrame = (callback) => integrationFrames.push(callback); | |
| globalThis.__h3WidthTestCore = { | |
| createLegacyWidgetWidthController, | |
| graphNodes, | |
| }; | |
| globalThis.__h3WidthTestApp = { | |
| graph: integrationGraph, | |
| registerExtension(value) { | |
| extension = value; | |
| }, | |
| ui: { | |
| settings: { | |
| addSetting(value) { | |
| registeredSetting = value; | |
| settingValues.set(value.id, value.defaultValue); | |
| }, | |
| getSettingValue(id) { | |
| return settingValues.get(id); | |
| }, | |
| }, | |
| }, | |
| }; | |
| const mockedIntegrationSource = integrationSource | |
| .replace( | |
| 'import {app} from "/scripts/app.js";', | |
| "const app = globalThis.__h3WidthTestApp;", | |
| ) | |
| .replace( | |
| /import \{\n createLegacyWidgetWidthController,\n graphNodes,\n\} from "\.\/h3_legacy_widget_width_fix_core\.mjs\?v=[^"]+";/, | |
| "const {createLegacyWidgetWidthController, graphNodes} = globalThis.__h3WidthTestCore;", | |
| ); | |
| await import(`data:text/javascript;base64,${Buffer.from(mockedIntegrationSource).toString("base64")}`); | |
| assert.ok(extension); | |
| extension.init(); | |
| assert.equal(registeredSetting.defaultValue, true); | |
| class IntegrationHost extends IntegrationNode {} | |
| extension.beforeRegisterNodeDef(IntegrationHost, {name: "MiniMaxH3ChainPlan"}); | |
| const integrationExisting = new IntegrationNode(); | |
| integrationExisting.graph = integrationGraph; | |
| integrationExisting.widgets.push({name: "existing", width: 190}); | |
| integrationGraph._nodes.push(integrationExisting); | |
| const integrationHost = new IntegrationHost("MiniMaxH3ChainPlan"); | |
| integrationHost.graph = integrationGraph; | |
| integrationGraph._nodes.push(integrationHost); | |
| integrationHost.onAdded(); | |
| extension.nodeCreated(integrationHost); | |
| assert.equal(integrationExisting.widgets[0].width, undefined); | |
| assert.equal(integrationExisting.addWidget("number", "new", 0).width, undefined); | |
| integrationHost.title = "MiniMax H3 Contex Loop Plan — CUSTOM TITLE"; | |
| integrationExisting.title = "MiniMax H3 Contex Loop is mentioned by another node"; | |
| extension.afterConfigureGraph(); | |
| assert.equal( | |
| integrationHost.title, | |
| "MiniMax H3 Context Loop Plan — CUSTOM TITLE", | |
| "saved H3 titles receive the spelling correction without losing suffixes", | |
| ); | |
| assert.equal( | |
| integrationExisting.title, | |
| "MiniMax H3 Contex Loop is mentioned by another node", | |
| "foreign node titles are never migrated", | |
| ); | |
| registeredSetting.onChange(false); | |
| integrationExisting.widgets[0].width = 440; | |
| assert.equal(integrationExisting.widgets[0].width, 440); | |
| registeredSetting.onChange(true); | |
| assert.equal(integrationExisting.widgets[0].width, undefined); | |
| class StandaloneFixNode extends IntegrationNode { | |
| onRemoved() { | |
| for (const node of integrationGraph._nodes) { | |
| for (const widget of node.widgets ?? []) { | |
| const savedWidth = widget[LEGACY_WIDTH_KEY]; | |
| delete widget.width; | |
| delete widget[LEGACY_WIDTH_KEY]; | |
| delete widget[LEGACY_GUARD_KEY]; | |
| widget.width = savedWidth; | |
| } | |
| } | |
| } | |
| } | |
| extension.beforeRegisterNodeDef(StandaloneFixNode, {name: "LegacyWidgetWidthFix"}); | |
| const standalone = new StandaloneFixNode("LegacyWidgetWidthFix"); | |
| standalone.graph = integrationGraph; | |
| integrationGraph._nodes.push(standalone); | |
| standalone.onRemoved(); | |
| integrationFrames.splice(0).forEach((callback) => callback()); | |
| integrationExisting.widgets[0].width = 445; | |
| assert.equal( | |
| integrationExisting.widgets[0].width, | |
| undefined, | |
| "embedded repair reclaims widgets after the standalone node is removed", | |
| ); | |
| integrationHost.onRemoved(); | |
| integrationExisting.widgets[0].width = 450; | |
| assert.equal(integrationExisting.widgets[0].width, 450); | |
| class StandaloneCheckpointManager extends IntegrationNode {} | |
| extension.beforeRegisterNodeDef(StandaloneCheckpointManager, { | |
| name: "MiniMaxH3ChainCheckpointManager", | |
| }); | |
| const checkpointManager = new StandaloneCheckpointManager( | |
| "MiniMaxH3ChainCheckpointManager", | |
| ); | |
| checkpointManager.graph = integrationGraph; | |
| integrationGraph._nodes.push(checkpointManager); | |
| checkpointManager.onAdded(); | |
| extension.nodeCreated(checkpointManager); | |
| assert.equal( | |
| integrationExisting.widgets[0].width, | |
| undefined, | |
| "a standalone Checkpoint Manager activates the embedded repair", | |
| ); | |
| checkpointManager.onRemoved(); | |
| integrationExisting.widgets[0].width = 455; | |
| assert.equal(integrationExisting.widgets[0].width, 455); | |
| console.log("H3 embedded legacy widget-width repair: lifecycle, all-node coverage, Vue passthrough, and standalone coexistence pass"); | |