You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
319 lines
10 KiB
319 lines
10 KiB
// @ts-check |
|
/// <reference path="../node_modules/@types/jest/index.d.ts" /> |
|
|
|
const { start, makeNodeDef, checkBeforeAndAfterReload, assertNotNullOrUndefined } = require("../utils"); |
|
const lg = require("../utils/litegraph"); |
|
|
|
/** |
|
* @typedef { import("../utils/ezgraph") } Ez |
|
* @typedef { ReturnType<Ez["Ez"]["graph"]>["ez"] } EzNodeFactory |
|
*/ |
|
|
|
/** |
|
* @param { EzNodeFactory } ez |
|
* @param { InstanceType<Ez["EzGraph"]> } graph |
|
* @param { InstanceType<Ez["EzInput"]> } input |
|
* @param { string } widgetType |
|
* @param { boolean } hasControlWidget |
|
* @returns |
|
*/ |
|
async function connectPrimitiveAndReload(ez, graph, input, widgetType, hasControlWidget) { |
|
// Connect to primitive and ensure its still connected after |
|
let primitive = ez.PrimitiveNode(); |
|
primitive.outputs[0].connectTo(input); |
|
|
|
await checkBeforeAndAfterReload(graph, async () => { |
|
primitive = graph.find(primitive); |
|
let { connections } = primitive.outputs[0]; |
|
expect(connections).toHaveLength(1); |
|
expect(connections[0].targetNode.id).toBe(input.node.node.id); |
|
|
|
// Ensure widget is correct type |
|
const valueWidget = primitive.widgets.value; |
|
expect(valueWidget.widget.type).toBe(widgetType); |
|
|
|
// Check if control_after_generate should be added |
|
if (hasControlWidget) { |
|
const controlWidget = primitive.widgets.control_after_generate; |
|
expect(controlWidget.widget.type).toBe("combo"); |
|
} |
|
|
|
// Ensure we dont have other widgets |
|
expect(primitive.node.widgets).toHaveLength(1 + +!!hasControlWidget); |
|
}); |
|
|
|
return primitive; |
|
} |
|
|
|
describe("widget inputs", () => { |
|
beforeEach(() => { |
|
lg.setup(global); |
|
}); |
|
|
|
afterEach(() => { |
|
lg.teardown(global); |
|
}); |
|
|
|
[ |
|
{ name: "int", type: "INT", widget: "number", control: true }, |
|
{ name: "float", type: "FLOAT", widget: "number", control: true }, |
|
{ name: "text", type: "STRING" }, |
|
{ |
|
name: "customtext", |
|
type: "STRING", |
|
opt: { multiline: true }, |
|
}, |
|
{ name: "toggle", type: "BOOLEAN" }, |
|
{ name: "combo", type: ["a", "b", "c"], control: true }, |
|
].forEach((c) => { |
|
test(`widget conversion + primitive works on ${c.name}`, async () => { |
|
const { ez, graph } = await start({ |
|
mockNodeDefs: makeNodeDef("TestNode", { [c.name]: [c.type, c.opt ?? {}] }), |
|
}); |
|
|
|
// Create test node and convert to input |
|
const n = ez.TestNode(); |
|
const w = n.widgets[c.name]; |
|
w.convertToInput(); |
|
expect(w.isConvertedToInput).toBeTruthy(); |
|
const input = w.getConvertedInput(); |
|
expect(input).toBeTruthy(); |
|
|
|
// @ts-ignore : input is valid here |
|
await connectPrimitiveAndReload(ez, graph, input, c.widget ?? c.name, c.control); |
|
}); |
|
}); |
|
|
|
test("converted widget works after reload", async () => { |
|
const { ez, graph } = await start(); |
|
let n = ez.CheckpointLoaderSimple(); |
|
|
|
const inputCount = n.inputs.length; |
|
|
|
// Convert ckpt name to an input |
|
n.widgets.ckpt_name.convertToInput(); |
|
expect(n.widgets.ckpt_name.isConvertedToInput).toBeTruthy(); |
|
expect(n.inputs.ckpt_name).toBeTruthy(); |
|
expect(n.inputs.length).toEqual(inputCount + 1); |
|
|
|
// Convert back to widget and ensure input is removed |
|
n.widgets.ckpt_name.convertToWidget(); |
|
expect(n.widgets.ckpt_name.isConvertedToInput).toBeFalsy(); |
|
expect(n.inputs.ckpt_name).toBeFalsy(); |
|
expect(n.inputs.length).toEqual(inputCount); |
|
|
|
// Convert again and reload the graph to ensure it maintains state |
|
n.widgets.ckpt_name.convertToInput(); |
|
expect(n.inputs.length).toEqual(inputCount + 1); |
|
|
|
const primitive = await connectPrimitiveAndReload(ez, graph, n.inputs.ckpt_name, "combo", true); |
|
|
|
// Disconnect & reconnect |
|
primitive.outputs[0].connections[0].disconnect(); |
|
let { connections } = primitive.outputs[0]; |
|
expect(connections).toHaveLength(0); |
|
|
|
primitive.outputs[0].connectTo(n.inputs.ckpt_name); |
|
({ connections } = primitive.outputs[0]); |
|
expect(connections).toHaveLength(1); |
|
expect(connections[0].targetNode.id).toBe(n.node.id); |
|
|
|
// Convert back to widget and ensure input is removed |
|
n.widgets.ckpt_name.convertToWidget(); |
|
expect(n.widgets.ckpt_name.isConvertedToInput).toBeFalsy(); |
|
expect(n.inputs.ckpt_name).toBeFalsy(); |
|
expect(n.inputs.length).toEqual(inputCount); |
|
}); |
|
|
|
test("converted widget works on clone", async () => { |
|
const { graph, ez } = await start(); |
|
let n = ez.CheckpointLoaderSimple(); |
|
|
|
// Convert the widget to an input |
|
n.widgets.ckpt_name.convertToInput(); |
|
expect(n.widgets.ckpt_name.isConvertedToInput).toBeTruthy(); |
|
|
|
// Clone the node |
|
n.menu["Clone"].call(); |
|
expect(graph.nodes).toHaveLength(2); |
|
const clone = graph.nodes[1]; |
|
expect(clone.id).not.toEqual(n.id); |
|
|
|
// Ensure the clone has an input |
|
expect(clone.widgets.ckpt_name.isConvertedToInput).toBeTruthy(); |
|
expect(clone.inputs.ckpt_name).toBeTruthy(); |
|
|
|
// Ensure primitive connects to both nodes |
|
let primitive = ez.PrimitiveNode(); |
|
primitive.outputs[0].connectTo(n.inputs.ckpt_name); |
|
primitive.outputs[0].connectTo(clone.inputs.ckpt_name); |
|
expect(primitive.outputs[0].connections).toHaveLength(2); |
|
|
|
// Convert back to widget and ensure input is removed |
|
clone.widgets.ckpt_name.convertToWidget(); |
|
expect(clone.widgets.ckpt_name.isConvertedToInput).toBeFalsy(); |
|
expect(clone.inputs.ckpt_name).toBeFalsy(); |
|
}); |
|
|
|
test("shows missing node error on custom node with converted input", async () => { |
|
const { graph } = await start(); |
|
|
|
const dialogShow = jest.spyOn(graph.app.ui.dialog, "show"); |
|
|
|
await graph.app.loadGraphData({ |
|
last_node_id: 3, |
|
last_link_id: 4, |
|
nodes: [ |
|
{ |
|
id: 1, |
|
type: "TestNode", |
|
pos: [41.87329101561909, 389.7381480823742], |
|
size: { 0: 220, 1: 374 }, |
|
flags: {}, |
|
order: 1, |
|
mode: 0, |
|
inputs: [{ name: "test", type: "FLOAT", link: 4, widget: { name: "test" }, slot_index: 0 }], |
|
outputs: [], |
|
properties: { "Node name for S&R": "TestNode" }, |
|
widgets_values: [1], |
|
}, |
|
{ |
|
id: 3, |
|
type: "PrimitiveNode", |
|
pos: [-312, 433], |
|
size: { 0: 210, 1: 82 }, |
|
flags: {}, |
|
order: 0, |
|
mode: 0, |
|
outputs: [{ links: [4], widget: { name: "test" } }], |
|
title: "test", |
|
properties: {}, |
|
}, |
|
], |
|
links: [[4, 3, 0, 1, 6, "FLOAT"]], |
|
groups: [], |
|
config: {}, |
|
extra: {}, |
|
version: 0.4, |
|
}); |
|
|
|
expect(dialogShow).toBeCalledTimes(1); |
|
expect(dialogShow.mock.calls[0][0]).toContain("the following node types were not found"); |
|
expect(dialogShow.mock.calls[0][0]).toContain("TestNode"); |
|
}); |
|
|
|
test("defaultInput widgets can be converted back to inputs", async () => { |
|
const { graph, ez } = await start({ |
|
mockNodeDefs: makeNodeDef("TestNode", { example: ["INT", { defaultInput: true }] }), |
|
}); |
|
|
|
// Create test node and ensure it starts as an input |
|
let n = ez.TestNode(); |
|
let w = n.widgets.example; |
|
expect(w.isConvertedToInput).toBeTruthy(); |
|
let input = w.getConvertedInput(); |
|
expect(input).toBeTruthy(); |
|
|
|
// Ensure it can be converted to |
|
w.convertToWidget(); |
|
expect(w.isConvertedToInput).toBeFalsy(); |
|
expect(n.inputs.length).toEqual(0); |
|
// and from |
|
w.convertToInput(); |
|
expect(w.isConvertedToInput).toBeTruthy(); |
|
input = w.getConvertedInput(); |
|
|
|
// Reload and ensure it still only has 1 converted widget |
|
if (!assertNotNullOrUndefined(input)) return; |
|
|
|
await connectPrimitiveAndReload(ez, graph, input, "number", true); |
|
n = graph.find(n); |
|
expect(n.widgets).toHaveLength(1); |
|
w = n.widgets.example; |
|
expect(w.isConvertedToInput).toBeTruthy(); |
|
|
|
// Convert back to widget and ensure it is still a widget after reload |
|
w.convertToWidget(); |
|
await graph.reload(); |
|
n = graph.find(n); |
|
expect(n.widgets).toHaveLength(1); |
|
expect(n.widgets[0].isConvertedToInput).toBeFalsy(); |
|
expect(n.inputs.length).toEqual(0); |
|
}); |
|
|
|
test("forceInput widgets can not be converted back to inputs", async () => { |
|
const { graph, ez } = await start({ |
|
mockNodeDefs: makeNodeDef("TestNode", { example: ["INT", { forceInput: true }] }), |
|
}); |
|
|
|
// Create test node and ensure it starts as an input |
|
let n = ez.TestNode(); |
|
let w = n.widgets.example; |
|
expect(w.isConvertedToInput).toBeTruthy(); |
|
const input = w.getConvertedInput(); |
|
expect(input).toBeTruthy(); |
|
|
|
// Convert to widget should error |
|
expect(() => w.convertToWidget()).toThrow(); |
|
|
|
// Reload and ensure it still only has 1 converted widget |
|
if (assertNotNullOrUndefined(input)) { |
|
await connectPrimitiveAndReload(ez, graph, input, "number", true); |
|
n = graph.find(n); |
|
expect(n.widgets).toHaveLength(1); |
|
expect(n.widgets.example.isConvertedToInput).toBeTruthy(); |
|
} |
|
}); |
|
|
|
test("primitive can connect to matching combos on converted widgets", async () => { |
|
const { ez } = await start({ |
|
mockNodeDefs: { |
|
...makeNodeDef("TestNode1", { example: [["A", "B", "C"], { forceInput: true }] }), |
|
...makeNodeDef("TestNode2", { example: [["A", "B", "C"], { forceInput: true }] }), |
|
}, |
|
}); |
|
|
|
const n1 = ez.TestNode1(); |
|
const n2 = ez.TestNode2(); |
|
const p = ez.PrimitiveNode(); |
|
p.outputs[0].connectTo(n1.inputs[0]); |
|
p.outputs[0].connectTo(n2.inputs[0]); |
|
expect(p.outputs[0].connections).toHaveLength(2); |
|
const valueWidget = p.widgets.value; |
|
expect(valueWidget.widget.type).toBe("combo"); |
|
expect(valueWidget.widget.options.values).toEqual(["A", "B", "C"]); |
|
}); |
|
|
|
test("primitive can not connect to non matching combos on converted widgets", async () => { |
|
const { ez } = await start({ |
|
mockNodeDefs: { |
|
...makeNodeDef("TestNode1", { example: [["A", "B", "C"], { forceInput: true }] }), |
|
...makeNodeDef("TestNode2", { example: [["A", "B"], { forceInput: true }] }), |
|
}, |
|
}); |
|
|
|
const n1 = ez.TestNode1(); |
|
const n2 = ez.TestNode2(); |
|
const p = ez.PrimitiveNode(); |
|
p.outputs[0].connectTo(n1.inputs[0]); |
|
expect(() => p.outputs[0].connectTo(n2.inputs[0])).toThrow(); |
|
expect(p.outputs[0].connections).toHaveLength(1); |
|
}); |
|
|
|
test("combo output can not connect to non matching combos list input", async () => { |
|
const { ez } = await start({ |
|
mockNodeDefs: { |
|
...makeNodeDef("TestNode1", {}, [["A", "B"]]), |
|
...makeNodeDef("TestNode2", { example: [["A", "B"], { forceInput: true}] }), |
|
...makeNodeDef("TestNode3", { example: [["A", "B", "C"], { forceInput: true}] }), |
|
}, |
|
}); |
|
|
|
const n1 = ez.TestNode1(); |
|
const n2 = ez.TestNode2(); |
|
const n3 = ez.TestNode3(); |
|
|
|
n1.outputs[0].connectTo(n2.inputs[0]); |
|
expect(() => n1.outputs[0].connectTo(n3.inputs[0])).toThrow(); |
|
}); |
|
});
|
|
|