import { LGraph, LiteGraph, Subgraph, type SlotLayout, NodeMode } from "@litegraph-ts/core" import { Watch } from "@litegraph-ts/nodes-basic" import { expect } from 'vitest' import UnitTest from "./UnitTest" import ComfyGraph from "$lib/ComfyGraph"; import ComfyPromptSerializer from "$lib/components/ComfyPromptSerializer"; import { ComfyBackendNode } from "$lib/nodes/ComfyBackendNode"; import ComfyGraphNode from "$lib/nodes/ComfyGraphNode"; import { graphToGraphVis } from "$lib/utils"; class MockBackendInput extends ComfyGraphNode { override isBackendNode = true; comfyClass: string = "MockBackendInput"; static slotLayout: SlotLayout = { inputs: [ { name: "in", type: "*" }, ], } } LiteGraph.registerNodeType({ class: MockBackendInput, title: "Test.MockBackendInput", desc: "one input", type: "test/input" }) class MockBackendLink extends ComfyGraphNode { override isBackendNode = true; comfyClass: string = "MockBackendLink"; static slotLayout: SlotLayout = { inputs: [ { name: "in", type: "*" }, ], outputs: [ { name: "out", type: "*" }, ], } } LiteGraph.registerNodeType({ class: MockBackendLink, title: "Test.MockBackendLink", desc: "one input, one output", type: "test/link" }) class MockBackendOutput extends ComfyGraphNode { override isBackendNode = true; comfyClass: string = "MockBackendOutput"; static slotLayout: SlotLayout = { outputs: [ { name: "out", type: "*" }, ], } } LiteGraph.registerNodeType({ class: MockBackendOutput, title: "Test.MockBackendOutput", desc: "one output", type: "test/output" }) export default class ComfyPromptSerializerTests extends UnitTest { test__serialize__shouldIgnoreFrontend() { const ser = new ComfyPromptSerializer(); const graph = new ComfyGraph(); const nodeA = LiteGraph.createNode(Watch) const nodeB = LiteGraph.createNode(Watch) graph.add(nodeA) graph.add(nodeB) const result = ser.serialize(graph) expect(result.output).toEqual({}) } test__serialize__shouldSerializeBackendNodes() { const ser = new ComfyPromptSerializer(); const graph = new ComfyGraph(); const input = LiteGraph.createNode(MockBackendInput) const link = LiteGraph.createNode(MockBackendLink) const output = LiteGraph.createNode(MockBackendOutput) graph.add(input) graph.add(link) graph.add(output) output.connect(0, link, 0) link.connect(0, input, 0) const result = ser.serialize(graph) console.warn(result.output) expect(Object.keys(result.output)).toHaveLength(3); expect(result.output[input.id].inputs["in"]).toBeInstanceOf(Array) expect(result.output[input.id].inputs["in"][0]).toEqual(link.id) expect(result.output[link.id].inputs["in"]).toBeInstanceOf(Array) expect(result.output[link.id].inputs["in"][0]).toEqual(output.id) expect(Object.keys(result.output[output.id].inputs)).toHaveLength(0); } test__serialize__shouldFollowSubgraphs() { const ser = new ComfyPromptSerializer(); const graph = new ComfyGraph(); const output = LiteGraph.createNode(MockBackendOutput) const link = LiteGraph.createNode(MockBackendLink) const input = LiteGraph.createNode(MockBackendInput) const subgraph = LiteGraph.createNode(Subgraph) const graphInput = subgraph.addGraphInput("testIn", "number") const graphOutput = subgraph.addGraphOutput("testOut", "number") graph.add(subgraph) graph.add(output) subgraph.subgraph.add(link) graph.add(input) output.connect(0, subgraph, 0) graphInput.innerNode.connect(0, link, 0) link.connect(0, graphOutput.innerNode, 0) subgraph.connect(0, input, 0) const result = ser.serialize(graph) expect(Object.keys(result.output)).toHaveLength(3); expect(result.output[input.id].inputs["in"]).toBeInstanceOf(Array) expect(result.output[input.id].inputs["in"][0]).toEqual(link.id) expect(result.output[link.id].inputs["in"]).toBeInstanceOf(Array) expect(result.output[link.id].inputs["in"][0]).toEqual(output.id) expect(result.output[output.id].inputs).toEqual({}) } test__serialize__shouldIgnoreInactiveSubgraphs() { const ser = new ComfyPromptSerializer(); const graph = new ComfyGraph(); const output = LiteGraph.createNode(MockBackendOutput) const link = LiteGraph.createNode(MockBackendLink) const input = LiteGraph.createNode(MockBackendInput) const subgraph = LiteGraph.createNode(Subgraph) const graphInput = subgraph.addGraphInput("testIn", "number") const graphOutput = subgraph.addGraphOutput("testOut", "number") graph.add(subgraph) graph.add(output) subgraph.subgraph.add(link) graph.add(input) output.connect(0, subgraph, 0) graphInput.innerNode.connect(0, link, 0) link.connect(0, graphOutput.innerNode, 0) subgraph.connect(0, input, 0) subgraph.mode = NodeMode.NEVER; const result = ser.serialize(graph) expect(Object.keys(result.output)).toHaveLength(2); expect(result.output[input.id].inputs["in"]).toBeUndefined(); expect(result.output[link.id]).toBeUndefined(); expect(result.output[output.id].inputs).toEqual({}) } test__serialize__shouldFollowSubgraphsRecursively() { const ser = new ComfyPromptSerializer(); const graph = new ComfyGraph(); const output = LiteGraph.createNode(MockBackendOutput) const link = LiteGraph.createNode(MockBackendLink) const input = LiteGraph.createNode(MockBackendInput) const subgraphA = LiteGraph.createNode(Subgraph) const subgraphB = LiteGraph.createNode(Subgraph) const graphInputA = subgraphA.addGraphInput("testIn", "number") const graphOutputA = subgraphA.addGraphOutput("testOut", "number") const graphInputB = subgraphB.addGraphInput("testIn", "number") const graphOutputB = subgraphB.addGraphOutput("testOut", "number") graph.add(subgraphA) subgraphA.subgraph.add(subgraphB) graph.add(output) subgraphB.subgraph.add(link) graph.add(input) output.connect(0, subgraphA, 0) graphInputA.innerNode.connect(0, subgraphB, 0) graphInputB.innerNode.connect(0, link, 0) link.connect(0, graphOutputB.innerNode, 0) subgraphB.connect(0, graphOutputA.innerNode, 0) subgraphA.connect(0, input, 0) const result = ser.serialize(graph) console.warn(graphToGraphVis(graph)) console.warn(result.output) expect(Object.keys(result.output)).toHaveLength(3); expect(result.output[input.id].inputs["in"]).toBeInstanceOf(Array) expect(result.output[input.id].inputs["in"][0]).toEqual(link.id) expect(result.output[link.id].inputs["in"]).toBeInstanceOf(Array) expect(result.output[link.id].inputs["in"][0]).toEqual(output.id) expect(result.output[output.id].inputs).toEqual({}) } test__serialize__shouldIgnoreInactiveSubgraphsRecursively() { const ser = new ComfyPromptSerializer(); const graph = new ComfyGraph(); const output = LiteGraph.createNode(MockBackendOutput) const link = LiteGraph.createNode(MockBackendLink) const input = LiteGraph.createNode(MockBackendInput) const subgraphA = LiteGraph.createNode(Subgraph) const subgraphB = LiteGraph.createNode(Subgraph) const graphInputA = subgraphA.addGraphInput("testIn", "number") const graphOutputA = subgraphA.addGraphOutput("testOut", "number") const graphInputB = subgraphB.addGraphInput("testIn", "number") const graphOutputB = subgraphB.addGraphOutput("testOut", "number") graph.add(subgraphA) subgraphA.subgraph.add(subgraphB) graph.add(output) subgraphB.subgraph.add(link) graph.add(input) output.connect(0, subgraphA, 0) graphInputA.innerNode.connect(0, subgraphB, 0) graphInputB.innerNode.connect(0, link, 0) link.connect(0, graphOutputB.innerNode, 0) subgraphB.connect(0, graphOutputA.innerNode, 0) subgraphA.connect(0, input, 0) subgraphA.mode = NodeMode.NEVER; const result = ser.serialize(graph) expect(Object.keys(result.output)).toHaveLength(2); expect(result.output[input.id].inputs["in"]).toBeUndefined(); expect(result.output[link.id]).toBeUndefined(); expect(result.output[output.id].inputs).toEqual({}) } }