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.
196 lines
6.8 KiB
196 lines
6.8 KiB
// @ts-check |
|
/// <reference path="../node_modules/@types/jest/index.d.ts" /> |
|
const { start } = require("../utils"); |
|
const lg = require("../utils/litegraph"); |
|
|
|
describe("extensions", () => { |
|
beforeEach(() => { |
|
lg.setup(global); |
|
}); |
|
|
|
afterEach(() => { |
|
lg.teardown(global); |
|
}); |
|
|
|
it("calls each extension hook", async () => { |
|
const mockExtension = { |
|
name: "TestExtension", |
|
init: jest.fn(), |
|
setup: jest.fn(), |
|
addCustomNodeDefs: jest.fn(), |
|
getCustomWidgets: jest.fn(), |
|
beforeRegisterNodeDef: jest.fn(), |
|
registerCustomNodes: jest.fn(), |
|
loadedGraphNode: jest.fn(), |
|
nodeCreated: jest.fn(), |
|
beforeConfigureGraph: jest.fn(), |
|
afterConfigureGraph: jest.fn(), |
|
}; |
|
|
|
const { app, ez, graph } = await start({ |
|
async preSetup(app) { |
|
app.registerExtension(mockExtension); |
|
}, |
|
}); |
|
|
|
// Basic initialisation hooks should be called once, with app |
|
expect(mockExtension.init).toHaveBeenCalledTimes(1); |
|
expect(mockExtension.init).toHaveBeenCalledWith(app); |
|
|
|
// Adding custom node defs should be passed the full list of nodes |
|
expect(mockExtension.addCustomNodeDefs).toHaveBeenCalledTimes(1); |
|
expect(mockExtension.addCustomNodeDefs.mock.calls[0][1]).toStrictEqual(app); |
|
const defs = mockExtension.addCustomNodeDefs.mock.calls[0][0]; |
|
expect(defs).toHaveProperty("KSampler"); |
|
expect(defs).toHaveProperty("LoadImage"); |
|
|
|
// Get custom widgets is called once and should return new widget types |
|
expect(mockExtension.getCustomWidgets).toHaveBeenCalledTimes(1); |
|
expect(mockExtension.getCustomWidgets).toHaveBeenCalledWith(app); |
|
|
|
// Before register node def will be called once per node type |
|
const nodeNames = Object.keys(defs); |
|
const nodeCount = nodeNames.length; |
|
expect(mockExtension.beforeRegisterNodeDef).toHaveBeenCalledTimes(nodeCount); |
|
for (let i = 0; i < 10; i++) { |
|
// It should be send the JS class and the original JSON definition |
|
const nodeClass = mockExtension.beforeRegisterNodeDef.mock.calls[i][0]; |
|
const nodeDef = mockExtension.beforeRegisterNodeDef.mock.calls[i][1]; |
|
|
|
expect(nodeClass.name).toBe("ComfyNode"); |
|
expect(nodeClass.comfyClass).toBe(nodeNames[i]); |
|
expect(nodeDef.name).toBe(nodeNames[i]); |
|
expect(nodeDef).toHaveProperty("input"); |
|
expect(nodeDef).toHaveProperty("output"); |
|
} |
|
|
|
// Register custom nodes is called once after registerNode defs to allow adding other frontend nodes |
|
expect(mockExtension.registerCustomNodes).toHaveBeenCalledTimes(1); |
|
|
|
// Before configure graph will be called here as the default graph is being loaded |
|
expect(mockExtension.beforeConfigureGraph).toHaveBeenCalledTimes(1); |
|
// it gets sent the graph data that is going to be loaded |
|
const graphData = mockExtension.beforeConfigureGraph.mock.calls[0][0]; |
|
|
|
// A node created is fired for each node constructor that is called |
|
expect(mockExtension.nodeCreated).toHaveBeenCalledTimes(graphData.nodes.length); |
|
for (let i = 0; i < graphData.nodes.length; i++) { |
|
expect(mockExtension.nodeCreated.mock.calls[i][0].type).toBe(graphData.nodes[i].type); |
|
} |
|
|
|
// Each node then calls loadedGraphNode to allow them to be updated |
|
expect(mockExtension.loadedGraphNode).toHaveBeenCalledTimes(graphData.nodes.length); |
|
for (let i = 0; i < graphData.nodes.length; i++) { |
|
expect(mockExtension.loadedGraphNode.mock.calls[i][0].type).toBe(graphData.nodes[i].type); |
|
} |
|
|
|
// After configure is then called once all the setup is done |
|
expect(mockExtension.afterConfigureGraph).toHaveBeenCalledTimes(1); |
|
|
|
expect(mockExtension.setup).toHaveBeenCalledTimes(1); |
|
expect(mockExtension.setup).toHaveBeenCalledWith(app); |
|
|
|
// Ensure hooks are called in the correct order |
|
const callOrder = [ |
|
"init", |
|
"addCustomNodeDefs", |
|
"getCustomWidgets", |
|
"beforeRegisterNodeDef", |
|
"registerCustomNodes", |
|
"beforeConfigureGraph", |
|
"nodeCreated", |
|
"loadedGraphNode", |
|
"afterConfigureGraph", |
|
"setup", |
|
]; |
|
for (let i = 1; i < callOrder.length; i++) { |
|
const fn1 = mockExtension[callOrder[i - 1]]; |
|
const fn2 = mockExtension[callOrder[i]]; |
|
expect(fn1.mock.invocationCallOrder[0]).toBeLessThan(fn2.mock.invocationCallOrder[0]); |
|
} |
|
|
|
graph.clear(); |
|
|
|
// Ensure adding a new node calls the correct callback |
|
ez.LoadImage(); |
|
expect(mockExtension.loadedGraphNode).toHaveBeenCalledTimes(graphData.nodes.length); |
|
expect(mockExtension.nodeCreated).toHaveBeenCalledTimes(graphData.nodes.length + 1); |
|
expect(mockExtension.nodeCreated.mock.lastCall[0].type).toBe("LoadImage"); |
|
|
|
// Reload the graph to ensure correct hooks are fired |
|
await graph.reload(); |
|
|
|
// These hooks should not be fired again |
|
expect(mockExtension.init).toHaveBeenCalledTimes(1); |
|
expect(mockExtension.addCustomNodeDefs).toHaveBeenCalledTimes(1); |
|
expect(mockExtension.getCustomWidgets).toHaveBeenCalledTimes(1); |
|
expect(mockExtension.registerCustomNodes).toHaveBeenCalledTimes(1); |
|
expect(mockExtension.beforeRegisterNodeDef).toHaveBeenCalledTimes(nodeCount); |
|
expect(mockExtension.setup).toHaveBeenCalledTimes(1); |
|
|
|
// These should be called again |
|
expect(mockExtension.beforeConfigureGraph).toHaveBeenCalledTimes(2); |
|
expect(mockExtension.nodeCreated).toHaveBeenCalledTimes(graphData.nodes.length + 2); |
|
expect(mockExtension.loadedGraphNode).toHaveBeenCalledTimes(graphData.nodes.length + 1); |
|
expect(mockExtension.afterConfigureGraph).toHaveBeenCalledTimes(2); |
|
}, 15000); |
|
|
|
it("allows custom nodeDefs and widgets to be registered", async () => { |
|
const widgetMock = jest.fn((node, inputName, inputData, app) => { |
|
expect(node.constructor.comfyClass).toBe("TestNode"); |
|
expect(inputName).toBe("test_input"); |
|
expect(inputData[0]).toBe("CUSTOMWIDGET"); |
|
expect(inputData[1]?.hello).toBe("world"); |
|
expect(app).toStrictEqual(app); |
|
|
|
return { |
|
widget: node.addWidget("button", inputName, "hello", () => {}), |
|
}; |
|
}); |
|
|
|
// Register our extension that adds a custom node + widget type |
|
const mockExtension = { |
|
name: "TestExtension", |
|
addCustomNodeDefs: (nodeDefs) => { |
|
nodeDefs["TestNode"] = { |
|
output: [], |
|
output_name: [], |
|
output_is_list: [], |
|
name: "TestNode", |
|
display_name: "TestNode", |
|
category: "Test", |
|
input: { |
|
required: { |
|
test_input: ["CUSTOMWIDGET", { hello: "world" }], |
|
}, |
|
}, |
|
}; |
|
}, |
|
getCustomWidgets: jest.fn(() => { |
|
return { |
|
CUSTOMWIDGET: widgetMock, |
|
}; |
|
}), |
|
}; |
|
|
|
const { graph, ez } = await start({ |
|
async preSetup(app) { |
|
app.registerExtension(mockExtension); |
|
}, |
|
}); |
|
|
|
expect(mockExtension.getCustomWidgets).toBeCalledTimes(1); |
|
|
|
graph.clear(); |
|
expect(widgetMock).toBeCalledTimes(0); |
|
const node = ez.TestNode(); |
|
expect(widgetMock).toBeCalledTimes(1); |
|
|
|
// Ensure our custom widget is created |
|
expect(node.inputs.length).toBe(0); |
|
expect(node.widgets.length).toBe(1); |
|
const w = node.widgets[0].widget; |
|
expect(w.name).toBe("test_input"); |
|
expect(w.type).toBe("button"); |
|
}); |
|
});
|
|
|