Fast_deploy / ComfyUI-MiniMaxH3-Context-Loop /tests /_legacy_widget_width_fix_js_test.mjs
dzmdd's picture
Upload folder using huggingface_hub (part 2)
bc50ea0 verified
Raw History Blame Contribute Delete
10.3 kB
#!/usr/bin/env node
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");