mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-20 06:44:32 +00:00
[Test] Add LinkConnector unit & integration tests (#816)
- Follow-up on #817 ### Unit tests Adds tests for: - LinkConnector - LGraph ### Integration tests for LinkConnector - Uses and configures a real graph + LGraph - Avoids mocks - User input is still mocked - Performs actual tasks as would be called by LGraphCanvas - A little verbose in places, but _many_ edge cases are caught by these tests
This commit is contained in:
@@ -32,84 +32,84 @@ describe("LGraph", () => {
|
||||
const fromOldSchema = new LGraph(oldSchemaGraph)
|
||||
expect(fromOldSchema).toMatchSnapshot("oldSchemaGraph")
|
||||
})
|
||||
})
|
||||
|
||||
describe("Reroutes", () => {
|
||||
test("Floating reroute should be removed when node and link are removed", ({ expect, floatingLinkGraph }) => {
|
||||
const graph = new LGraph(floatingLinkGraph)
|
||||
expect(graph.nodes.length).toBe(1)
|
||||
graph.remove(graph.nodes[0])
|
||||
expect(graph.nodes.length).toBe(0)
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(0)
|
||||
expect(graph.reroutes.size).toBe(0)
|
||||
})
|
||||
describe("Floating Links / Reroutes", () => {
|
||||
test("Floating reroute should be removed when node and link are removed", ({ expect, floatingLinkGraph }) => {
|
||||
const graph = new LGraph(floatingLinkGraph)
|
||||
expect(graph.nodes.length).toBe(1)
|
||||
graph.remove(graph.nodes[0])
|
||||
expect(graph.nodes.length).toBe(0)
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(0)
|
||||
expect(graph.reroutes.size).toBe(0)
|
||||
})
|
||||
|
||||
test("Can add reroute to existing link", ({ expect, linkedNodesGraph }) => {
|
||||
const graph = new LGraph(linkedNodesGraph)
|
||||
expect(graph.nodes.length).toBe(2)
|
||||
expect(graph.links.size).toBe(1)
|
||||
expect(graph.reroutes.size).toBe(0)
|
||||
test("Can add reroute to existing link", ({ expect, linkedNodesGraph }) => {
|
||||
const graph = new LGraph(linkedNodesGraph)
|
||||
expect(graph.nodes.length).toBe(2)
|
||||
expect(graph.links.size).toBe(1)
|
||||
expect(graph.reroutes.size).toBe(0)
|
||||
|
||||
graph.createReroute([0, 0], graph.links.values().next().value!)
|
||||
expect(graph.links.size).toBe(1)
|
||||
expect(graph.reroutes.size).toBe(1)
|
||||
})
|
||||
graph.createReroute([0, 0], graph.links.values().next().value!)
|
||||
expect(graph.links.size).toBe(1)
|
||||
expect(graph.reroutes.size).toBe(1)
|
||||
})
|
||||
|
||||
test("Create floating reroute when one side of node is removed", ({ expect, linkedNodesGraph }) => {
|
||||
const graph = new LGraph(linkedNodesGraph)
|
||||
graph.createReroute([0, 0], graph.links.values().next().value!)
|
||||
graph.remove(graph.nodes[0])
|
||||
test("Create floating reroute when one side of node is removed", ({ expect, linkedNodesGraph }) => {
|
||||
const graph = new LGraph(linkedNodesGraph)
|
||||
graph.createReroute([0, 0], graph.links.values().next().value!)
|
||||
graph.remove(graph.nodes[0])
|
||||
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(1)
|
||||
expect(graph.reroutes.size).toBe(1)
|
||||
expect(graph.reroutes.values().next().value!.floating).not.toBeUndefined()
|
||||
})
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(1)
|
||||
expect(graph.reroutes.size).toBe(1)
|
||||
expect(graph.reroutes.values().next().value!.floating).not.toBeUndefined()
|
||||
})
|
||||
|
||||
test("Create floating reroute when one side of link is removed", ({ expect, linkedNodesGraph }) => {
|
||||
const graph = new LGraph(linkedNodesGraph)
|
||||
graph.createReroute([0, 0], graph.links.values().next().value!)
|
||||
graph.nodes[0].disconnectOutput(0)
|
||||
test("Create floating reroute when one side of link is removed", ({ expect, linkedNodesGraph }) => {
|
||||
const graph = new LGraph(linkedNodesGraph)
|
||||
graph.createReroute([0, 0], graph.links.values().next().value!)
|
||||
graph.nodes[0].disconnectOutput(0)
|
||||
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(1)
|
||||
expect(graph.reroutes.size).toBe(1)
|
||||
expect(graph.reroutes.values().next().value!.floating).not.toBeUndefined()
|
||||
})
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(1)
|
||||
expect(graph.reroutes.size).toBe(1)
|
||||
expect(graph.reroutes.values().next().value!.floating).not.toBeUndefined()
|
||||
})
|
||||
|
||||
test("Reroutes and branches should be retained when the input node is removed", ({ expect, floatingBranchGraph: graph }) => {
|
||||
expect(graph.nodes.length).toBe(3)
|
||||
graph.remove(graph.nodes[2])
|
||||
expect(graph.nodes.length).toBe(2)
|
||||
expect(graph.links.size).toBe(1)
|
||||
expect(graph.floatingLinks.size).toBe(1)
|
||||
expect(graph.reroutes.size).toBe(4)
|
||||
graph.remove(graph.nodes[1])
|
||||
expect(graph.nodes.length).toBe(1)
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(2)
|
||||
expect(graph.reroutes.size).toBe(4)
|
||||
})
|
||||
test("Reroutes and branches should be retained when the input node is removed", ({ expect, floatingBranchGraph: graph }) => {
|
||||
expect(graph.nodes.length).toBe(3)
|
||||
graph.remove(graph.nodes[2])
|
||||
expect(graph.nodes.length).toBe(2)
|
||||
expect(graph.links.size).toBe(1)
|
||||
expect(graph.floatingLinks.size).toBe(1)
|
||||
expect(graph.reroutes.size).toBe(4)
|
||||
graph.remove(graph.nodes[1])
|
||||
expect(graph.nodes.length).toBe(1)
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(2)
|
||||
expect(graph.reroutes.size).toBe(4)
|
||||
})
|
||||
|
||||
test("Floating reroutes should be removed when neither input nor output is connected", ({ expect, floatingBranchGraph: graph }) => {
|
||||
// Remove output node
|
||||
graph.remove(graph.nodes[0])
|
||||
expect(graph.nodes.length).toBe(2)
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(2)
|
||||
// The original floating reroute should be removed
|
||||
expect(graph.reroutes.size).toBe(3)
|
||||
graph.remove(graph.nodes[0])
|
||||
expect(graph.nodes.length).toBe(1)
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(1)
|
||||
expect(graph.reroutes.size).toBe(3)
|
||||
graph.remove(graph.nodes[0])
|
||||
expect(graph.nodes.length).toBe(0)
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(0)
|
||||
expect(graph.reroutes.size).toBe(0)
|
||||
})
|
||||
test("Floating reroutes should be removed when neither input nor output is connected", ({ expect, floatingBranchGraph: graph }) => {
|
||||
// Remove output node
|
||||
graph.remove(graph.nodes[0])
|
||||
expect(graph.nodes.length).toBe(2)
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(2)
|
||||
// The original floating reroute should be removed
|
||||
expect(graph.reroutes.size).toBe(3)
|
||||
graph.remove(graph.nodes[0])
|
||||
expect(graph.nodes.length).toBe(1)
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(1)
|
||||
expect(graph.reroutes.size).toBe(3)
|
||||
graph.remove(graph.nodes[0])
|
||||
expect(graph.nodes.length).toBe(0)
|
||||
expect(graph.links.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(0)
|
||||
expect(graph.reroutes.size).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
801
test/LinkConnector.integration.test.ts
Normal file
801
test/LinkConnector.integration.test.ts
Normal file
@@ -0,0 +1,801 @@
|
||||
import { afterEach, describe, expect, vi } from "vitest"
|
||||
|
||||
import { LinkConnector } from "@/canvas/LinkConnector"
|
||||
import { LGraph } from "@/LGraph"
|
||||
import { LGraphNode } from "@/LGraphNode"
|
||||
import { LLink } from "@/LLink"
|
||||
import { Reroute, type RerouteId } from "@/Reroute"
|
||||
|
||||
import { test as baseTest } from "./testExtensions"
|
||||
|
||||
interface TestContext {
|
||||
graph: LGraph
|
||||
connector: LinkConnector
|
||||
setConnectingLinks: ReturnType<typeof vi.fn>
|
||||
createTestNode: (id: number) => LGraphNode
|
||||
reroutesBeforeTest: [rerouteId: RerouteId, reroute: Reroute][]
|
||||
validateIntegrityNoChanges: () => void
|
||||
validateIntegrityFloatingRemoved: () => void
|
||||
validateLinkIntegrity: () => void
|
||||
getNextLinkIds: (linkIds: Set<number>, expectedExtraLinks?: number) => number[]
|
||||
readonly floatingReroute: Reroute
|
||||
}
|
||||
|
||||
const test = baseTest.extend<TestContext>({
|
||||
reroutesBeforeTest: async ({ reroutesComplexGraph }, use) => {
|
||||
await use([...reroutesComplexGraph.reroutes])
|
||||
},
|
||||
|
||||
graph: async ({ reroutesComplexGraph }, use) => {
|
||||
const ctx = vi.fn(() => ({ measureText: vi.fn(() => ({ width: 10 })) }))
|
||||
for (const node of reroutesComplexGraph.nodes) {
|
||||
node.updateArea(ctx() as unknown as CanvasRenderingContext2D)
|
||||
}
|
||||
await use(reroutesComplexGraph)
|
||||
},
|
||||
setConnectingLinks: async ({}, use: (mock: ReturnType<typeof vi.fn>) => Promise<void>) => {
|
||||
const mock = vi.fn()
|
||||
await use(mock)
|
||||
},
|
||||
connector: async ({ setConnectingLinks }, use) => {
|
||||
const connector = new LinkConnector(setConnectingLinks)
|
||||
await use(connector)
|
||||
},
|
||||
createTestNode: async ({ graph }, use) => {
|
||||
await use((id): LGraphNode => {
|
||||
const node = new LGraphNode("test")
|
||||
node.id = id
|
||||
graph.add(node)
|
||||
return node
|
||||
})
|
||||
},
|
||||
|
||||
validateIntegrityNoChanges: async ({ graph, reroutesBeforeTest, expect }, use) => {
|
||||
await use(() => {
|
||||
expect(graph.floatingLinks.size).toBe(1)
|
||||
expect([...graph.reroutes]).toEqual(reroutesBeforeTest)
|
||||
|
||||
// Only the original reroute should be floating
|
||||
const reroutesExceptOne = [...graph.reroutes.values()].filter(reroute => reroute.id !== 1)
|
||||
for (const reroute of reroutesExceptOne) {
|
||||
expect(reroute.floating).toBeUndefined()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
validateIntegrityFloatingRemoved: async ({ graph, reroutesBeforeTest, expect }, use) => {
|
||||
await use(() => {
|
||||
expect(graph.floatingLinks.size).toBe(0)
|
||||
expect([...graph.reroutes]).toEqual(reroutesBeforeTest)
|
||||
|
||||
for (const reroute of graph.reroutes.values()) {
|
||||
expect(reroute.floating).toBeUndefined()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
validateLinkIntegrity: async ({ graph, expect }, use) => {
|
||||
await use(() => {
|
||||
for (const reroute of graph.reroutes.values()) {
|
||||
if (reroute.origin_id === undefined) {
|
||||
expect(reroute.linkIds.size).toBe(0)
|
||||
expect(reroute.floatingLinkIds.size).toBeGreaterThan(0)
|
||||
}
|
||||
|
||||
for (const linkId of reroute.linkIds) {
|
||||
const link = graph.links.get(linkId)
|
||||
expect(link).toBeDefined()
|
||||
expect(link!.origin_id).toEqual(reroute.origin_id)
|
||||
expect(link!.origin_slot).toEqual(reroute.origin_slot)
|
||||
}
|
||||
for (const linkId of reroute.floatingLinkIds) {
|
||||
const link = graph.floatingLinks.get(linkId)
|
||||
expect(link).toBeDefined()
|
||||
|
||||
if (link!.target_id === -1) {
|
||||
expect(link!.origin_id).not.toBe(-1)
|
||||
expect(link!.origin_slot).not.toBe(-1)
|
||||
expect(link!.target_slot).toBe(-1)
|
||||
} else {
|
||||
expect(link!.origin_id).toBe(-1)
|
||||
expect(link!.origin_slot).toBe(-1)
|
||||
expect(link!.target_slot).not.toBe(-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getNextLinkIds: async ({ graph }, use) => {
|
||||
await use((linkIds, expectedExtraLinks = 0) => {
|
||||
const indexes = [...new Array(linkIds.size + expectedExtraLinks).keys()]
|
||||
return indexes.map(index => graph.last_link_id + index + 1)
|
||||
})
|
||||
},
|
||||
|
||||
floatingReroute: async ({ graph, expect }, use) => {
|
||||
const floatingReroute = graph.reroutes.get(1)!
|
||||
expect(floatingReroute.floating).toEqual({ slotType: "output" })
|
||||
await use(floatingReroute)
|
||||
},
|
||||
})
|
||||
|
||||
describe("LinkConnector Integration", () => {
|
||||
afterEach<TestContext>(({ validateLinkIntegrity }) => {
|
||||
validateLinkIntegrity()
|
||||
})
|
||||
|
||||
describe("Moving input links", () => {
|
||||
test("Should move input links", ({ graph, connector }) => {
|
||||
const nextLinkId = graph.last_link_id + 1
|
||||
|
||||
const hasInputNode = graph.getNodeById(2)!
|
||||
const disconnectedNode = graph.getNodeById(9)!
|
||||
|
||||
const reroutesBefore = LLink.getReroutes(graph, graph.links.get(hasInputNode.inputs[0].link!)!)
|
||||
|
||||
connector.moveInputLink(graph, hasInputNode.inputs[0])
|
||||
expect(connector.state.connectingTo).toBe("input")
|
||||
expect(connector.state.draggingExistingLinks).toBe(true)
|
||||
expect(connector.renderLinks.length).toBe(1)
|
||||
expect(connector.inputLinks.length).toBe(1)
|
||||
|
||||
const canvasX = disconnectedNode.pos[0] + disconnectedNode.size[0] / 2
|
||||
const canvasY = disconnectedNode.pos[1] + 16
|
||||
const dropEvent = { canvasX, canvasY } as any
|
||||
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
expect(connector.renderLinks.length).toBe(0)
|
||||
expect(connector.inputLinks.length).toBe(0)
|
||||
|
||||
expect(disconnectedNode.inputs[0].link).toBe(nextLinkId)
|
||||
|
||||
const reroutesAfter = LLink.getReroutes(graph, graph.links.get(disconnectedNode.inputs[0].link!)!)
|
||||
expect(reroutesAfter).toEqual(reroutesBefore)
|
||||
})
|
||||
|
||||
test("Should connect from floating reroutes", ({ graph, connector, reroutesBeforeTest }) => {
|
||||
const nextLinkId = graph.last_link_id + 1
|
||||
|
||||
const floatingLink = graph.floatingLinks.values().next().value!
|
||||
expect(floatingLink).toBeInstanceOf(LLink)
|
||||
const floatingReroute = graph.reroutes.get(floatingLink.parentId!)!
|
||||
|
||||
const disconnectedNode = graph.getNodeById(9)!
|
||||
connector.dragFromReroute(graph, floatingReroute)
|
||||
|
||||
expect(connector.state.connectingTo).toBe("input")
|
||||
expect(connector.state.draggingExistingLinks).toBe(false)
|
||||
expect(connector.renderLinks.length).toBe(1)
|
||||
expect(connector.inputLinks.length).toBe(0)
|
||||
|
||||
const canvasX = disconnectedNode.pos[0] + disconnectedNode.size[0] / 2
|
||||
const canvasY = disconnectedNode.pos[1] + 16
|
||||
const dropEvent = { canvasX, canvasY } as any
|
||||
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
expect(connector.renderLinks.length).toBe(0)
|
||||
expect(connector.inputLinks.length).toBe(0)
|
||||
|
||||
// New link should have been created
|
||||
expect(disconnectedNode.inputs[0].link).toBe(nextLinkId)
|
||||
|
||||
// Check graph integrity
|
||||
expect(graph.floatingLinks.size).toBe(0)
|
||||
expect([...graph.reroutes]).toEqual(reroutesBeforeTest)
|
||||
|
||||
// All reroute floating property should be cleared
|
||||
for (const reroute of graph.reroutes.values()) {
|
||||
expect(reroute.floating).toBeUndefined()
|
||||
}
|
||||
})
|
||||
|
||||
test("Should drop floating links when both sides are disconnected", ({ graph, reroutesBeforeTest }) => {
|
||||
expect(graph.floatingLinks.size).toBe(1)
|
||||
|
||||
const floatingOutNode = graph.getNodeById(1)!
|
||||
floatingOutNode.disconnectOutput(0)
|
||||
|
||||
// Should have lost one reroute
|
||||
expect(graph.reroutes.size).toBe(reroutesBeforeTest.length - 1)
|
||||
expect(graph.reroutes.get(1)).toBeUndefined()
|
||||
|
||||
// The two normal links should now be floating
|
||||
expect(graph.floatingLinks.size).toBe(2)
|
||||
|
||||
graph.getNodeById(2)!.disconnectInput(0, true)
|
||||
expect(graph.floatingLinks.size).toBe(1)
|
||||
|
||||
graph.getNodeById(3)!.disconnectInput(0, false)
|
||||
expect(graph.floatingLinks.size).toBe(0)
|
||||
|
||||
// Removed 4 reroutes
|
||||
expect(graph.reroutes.size).toBe(9)
|
||||
|
||||
// All four nodes should have no links
|
||||
for (const nodeId of [1, 2, 3, 9]) {
|
||||
const { inputs: [input], outputs: [output] } = graph.getNodeById(nodeId)!
|
||||
|
||||
expect(input.link).toBeNull()
|
||||
expect(output.links?.length).toBeOneOf([0, undefined])
|
||||
|
||||
expect(input._floatingLinks?.size).toBeOneOf([0, undefined])
|
||||
expect(output._floatingLinks?.size).toBeOneOf([0, undefined])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("Moving output links", () => {
|
||||
test("Should move output links", ({ graph, connector }) => {
|
||||
const nextLinkIds = [graph.last_link_id + 1, graph.last_link_id + 2]
|
||||
|
||||
const hasOutputNode = graph.getNodeById(1)!
|
||||
const disconnectedNode = graph.getNodeById(9)!
|
||||
|
||||
const reroutesBefore = hasOutputNode.outputs[0].links
|
||||
?.map(linkId => graph.links.get(linkId)!)
|
||||
.map(link => LLink.getReroutes(graph, link))
|
||||
|
||||
connector.moveOutputLink(graph, hasOutputNode.outputs[0])
|
||||
expect(connector.state.connectingTo).toBe("output")
|
||||
expect(connector.state.draggingExistingLinks).toBe(true)
|
||||
expect(connector.renderLinks.length).toBe(3)
|
||||
expect(connector.outputLinks.length).toBe(2)
|
||||
expect(connector.floatingLinks.length).toBe(1)
|
||||
|
||||
const canvasX = disconnectedNode.pos[0] + disconnectedNode.size[0] / 2
|
||||
const canvasY = disconnectedNode.pos[1] + 16
|
||||
const dropEvent = { canvasX, canvasY } as any
|
||||
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
expect(connector.renderLinks.length).toBe(0)
|
||||
expect(connector.outputLinks.length).toBe(0)
|
||||
|
||||
expect(disconnectedNode.outputs[0].links).toEqual(nextLinkIds)
|
||||
|
||||
const reroutesAfter = disconnectedNode.outputs[0].links
|
||||
?.map(linkId => graph.links.get(linkId)!)
|
||||
.map(link => LLink.getReroutes(graph, link))
|
||||
|
||||
expect(reroutesAfter).toEqual(reroutesBefore)
|
||||
})
|
||||
|
||||
test("Should connect to floating reroutes from outputs", ({ graph, connector, reroutesBeforeTest }) => {
|
||||
const nextLinkIds = [graph.last_link_id + 1, graph.last_link_id + 2]
|
||||
|
||||
const floatingOutNode = graph.getNodeById(1)!
|
||||
floatingOutNode.disconnectOutput(0)
|
||||
|
||||
// Should have lost one reroute
|
||||
expect(graph.reroutes.size).toBe(reroutesBeforeTest.length - 1)
|
||||
expect(graph.reroutes.get(1)).toBeUndefined()
|
||||
|
||||
// The two normal links should now be floating
|
||||
expect(graph.floatingLinks.size).toBe(2)
|
||||
|
||||
const disconnectedNode = graph.getNodeById(9)!
|
||||
connector.dragNewFromOutput(graph, disconnectedNode, disconnectedNode.outputs[0])
|
||||
|
||||
expect(connector.state.connectingTo).toBe("input")
|
||||
expect(connector.state.draggingExistingLinks).toBe(false)
|
||||
expect(connector.renderLinks.length).toBe(1)
|
||||
expect(connector.outputLinks.length).toBe(0)
|
||||
expect(connector.floatingLinks.length).toBe(0)
|
||||
|
||||
const floatingLink = graph.floatingLinks.values().next().value!
|
||||
expect(floatingLink).toBeInstanceOf(LLink)
|
||||
const floatingReroute = LLink.getReroutes(graph, floatingLink)[0]
|
||||
|
||||
const canvasX = floatingReroute.pos[0]
|
||||
const canvasY = floatingReroute.pos[1]
|
||||
const dropEvent = { canvasX, canvasY } as any
|
||||
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
expect(connector.renderLinks.length).toBe(0)
|
||||
expect(connector.outputLinks.length).toBe(0)
|
||||
|
||||
// New link should have been created
|
||||
expect(disconnectedNode.outputs[0].links).toEqual(nextLinkIds)
|
||||
|
||||
// Check graph integrity
|
||||
expect(graph.floatingLinks.size).toBe(0)
|
||||
expect([...graph.reroutes]).toEqual(reroutesBeforeTest.slice(1))
|
||||
|
||||
for (const reroute of graph.reroutes.values()) {
|
||||
expect(reroute.floating).toBeUndefined()
|
||||
}
|
||||
})
|
||||
|
||||
test("Should drop floating links when both sides are disconnected", ({ graph, reroutesBeforeTest }) => {
|
||||
expect(graph.floatingLinks.size).toBe(1)
|
||||
|
||||
graph.getNodeById(2)!.disconnectInput(0, true)
|
||||
expect(graph.floatingLinks.size).toBe(1)
|
||||
|
||||
// Only the original reroute should be floating
|
||||
const reroutesExceptOne = [...graph.reroutes.values()].filter(reroute => reroute.id !== 1)
|
||||
for (const reroute of reroutesExceptOne) {
|
||||
expect(reroute.floating).toBeUndefined()
|
||||
}
|
||||
|
||||
graph.getNodeById(3)!.disconnectInput(0, true)
|
||||
expect([...graph.reroutes]).toEqual(reroutesBeforeTest)
|
||||
|
||||
// The normal link should now be floating
|
||||
expect(graph.floatingLinks.size).toBe(2)
|
||||
expect(graph.reroutes.get(3)!.floating).toEqual({ slotType: "output" })
|
||||
|
||||
const floatingOutNode = graph.getNodeById(1)!
|
||||
floatingOutNode.disconnectOutput(0)
|
||||
|
||||
// Should have lost one reroute
|
||||
expect(graph.reroutes.size).toBe(9)
|
||||
expect(graph.reroutes.get(1)).toBeUndefined()
|
||||
|
||||
// Removed 4 reroutes
|
||||
expect(graph.reroutes.size).toBe(9)
|
||||
|
||||
// All four nodes should have no links
|
||||
for (const nodeId of [1, 2, 3, 9]) {
|
||||
const { inputs: [input], outputs: [output] } = graph.getNodeById(nodeId)!
|
||||
|
||||
expect(input.link).toBeNull()
|
||||
expect(output.links?.length).toBeOneOf([0, undefined])
|
||||
|
||||
expect(input._floatingLinks?.size).toBeOneOf([0, undefined])
|
||||
expect(output._floatingLinks?.size).toBeOneOf([0, undefined])
|
||||
}
|
||||
})
|
||||
|
||||
test("Should support moving multiple output links to a floating reroute", ({ graph, connector, floatingReroute, validateIntegrityFloatingRemoved }) => {
|
||||
const manyOutputsNode = graph.getNodeById(4)!
|
||||
const canvasX = floatingReroute.pos[0]
|
||||
const canvasY = floatingReroute.pos[1]
|
||||
const floatingRerouteEvent = { canvasX, canvasY } as any
|
||||
|
||||
connector.moveOutputLink(graph, manyOutputsNode.outputs[0])
|
||||
connector.dropLinks(graph, floatingRerouteEvent)
|
||||
|
||||
expect(manyOutputsNode.outputs[0].links).toEqual([])
|
||||
expect(floatingReroute.linkIds.size).toBe(4)
|
||||
|
||||
validateIntegrityFloatingRemoved()
|
||||
})
|
||||
})
|
||||
|
||||
describe("Floating links", () => {
|
||||
test("Removed when connecting from reroute to input", ({ graph, connector, floatingReroute }) => {
|
||||
const disconnectedNode = graph.getNodeById(9)!
|
||||
const canvasX = disconnectedNode.pos[0]
|
||||
const canvasY = disconnectedNode.pos[1]
|
||||
|
||||
connector.dragFromReroute(graph, floatingReroute)
|
||||
connector.dropLinks(graph, { canvasX, canvasY } as any)
|
||||
|
||||
expect(graph.floatingLinks.size).toBe(0)
|
||||
expect(floatingReroute.floating).toBeUndefined()
|
||||
})
|
||||
|
||||
test("Removed when connecting from reroute to another reroute", ({ graph, connector, floatingReroute, validateIntegrityFloatingRemoved }) => {
|
||||
const reroute8 = graph.reroutes.get(8)!
|
||||
const canvasX = reroute8.pos[0]
|
||||
const canvasY = reroute8.pos[1]
|
||||
|
||||
connector.dragFromReroute(graph, floatingReroute)
|
||||
connector.dropLinks(graph, { canvasX, canvasY } as any)
|
||||
|
||||
expect(graph.floatingLinks.size).toBe(0)
|
||||
expect(floatingReroute.floating).toBeUndefined()
|
||||
expect(reroute8.floating).toBeUndefined()
|
||||
|
||||
validateIntegrityFloatingRemoved()
|
||||
})
|
||||
})
|
||||
|
||||
test("Should drop floating links when both sides are disconnected", ({ graph, connector, reroutesBeforeTest, validateIntegrityNoChanges }) => {
|
||||
const floatingOutNode = graph.getNodeById(1)!
|
||||
connector.moveOutputLink(graph, floatingOutNode.outputs[0])
|
||||
|
||||
const manyOutputsNode = graph.getNodeById(4)!
|
||||
const dropEvent = { canvasX: manyOutputsNode.pos[0], canvasY: manyOutputsNode.pos[1] } as any
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
|
||||
const output = manyOutputsNode.outputs[0]
|
||||
expect(output.links!.length).toBe(6)
|
||||
expect(output._floatingLinks!.size).toBe(1)
|
||||
|
||||
validateIntegrityNoChanges()
|
||||
|
||||
// Move again
|
||||
connector.moveOutputLink(graph, manyOutputsNode.outputs[0])
|
||||
|
||||
const disconnectedNode = graph.getNodeById(9)!
|
||||
dropEvent.canvasX = disconnectedNode.pos[0]
|
||||
dropEvent.canvasY = disconnectedNode.pos[1]
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
|
||||
const newOutput = disconnectedNode.outputs[0]
|
||||
expect(newOutput.links!.length).toBe(6)
|
||||
expect(newOutput._floatingLinks!.size).toBe(1)
|
||||
|
||||
validateIntegrityNoChanges()
|
||||
|
||||
disconnectedNode.disconnectOutput(0)
|
||||
|
||||
expect(newOutput._floatingLinks!.size).toBe(0)
|
||||
expect(graph.floatingLinks.size).toBe(6)
|
||||
|
||||
// The final reroutes should all be floating
|
||||
for (const reroute of graph.reroutes.values()) {
|
||||
if ([3, 7, 15, 12].includes(reroute.id)) {
|
||||
expect(reroute.floating).toEqual({ slotType: "input" })
|
||||
} else {
|
||||
expect(reroute.floating).toBeUndefined()
|
||||
}
|
||||
}
|
||||
|
||||
// Removed one reroute
|
||||
expect(graph.reroutes.size).toBe(reroutesBeforeTest.length - 1)
|
||||
|
||||
// Original nodes should have no links
|
||||
for (const nodeId of [1, 4]) {
|
||||
const { inputs: [input], outputs: [output] } = graph.getNodeById(nodeId)!
|
||||
|
||||
expect(input.link).toBeNull()
|
||||
expect(output.links?.length).toBeOneOf([0, undefined])
|
||||
|
||||
expect(input._floatingLinks?.size).toBeOneOf([0, undefined])
|
||||
expect(output._floatingLinks?.size).toBeOneOf([0, undefined])
|
||||
}
|
||||
})
|
||||
|
||||
type TestData = {
|
||||
/** Drop link on this reroute */
|
||||
targetRerouteId: number
|
||||
/** Parent reroutes of the target reroute */
|
||||
parentIds: number[]
|
||||
/** Number of links before the drop */
|
||||
linksBefore: number[]
|
||||
/** Number of links after the drop */
|
||||
linksAfter: (number | undefined)[]
|
||||
/** Whether to run the integrity check */
|
||||
runIntegrityCheck: boolean
|
||||
}
|
||||
|
||||
test.for<TestData>([
|
||||
{
|
||||
targetRerouteId: 8,
|
||||
parentIds: [13, 10],
|
||||
linksBefore: [3, 4],
|
||||
linksAfter: [1, 2],
|
||||
runIntegrityCheck: true,
|
||||
},
|
||||
{
|
||||
targetRerouteId: 7,
|
||||
parentIds: [6, 8, 13, 10],
|
||||
linksBefore: [2, 2, 3, 4],
|
||||
linksAfter: [undefined, undefined, 1, 2],
|
||||
runIntegrityCheck: false,
|
||||
},
|
||||
{
|
||||
targetRerouteId: 6,
|
||||
parentIds: [8, 13, 10],
|
||||
linksBefore: [2, 3, 4],
|
||||
linksAfter: [undefined, 1, 2],
|
||||
runIntegrityCheck: false,
|
||||
},
|
||||
{
|
||||
targetRerouteId: 13,
|
||||
parentIds: [10],
|
||||
linksBefore: [4],
|
||||
linksAfter: [1],
|
||||
runIntegrityCheck: true,
|
||||
},
|
||||
{
|
||||
targetRerouteId: 4,
|
||||
parentIds: [],
|
||||
linksBefore: [],
|
||||
linksAfter: [],
|
||||
runIntegrityCheck: true,
|
||||
},
|
||||
{
|
||||
targetRerouteId: 2,
|
||||
parentIds: [4],
|
||||
linksBefore: [2],
|
||||
linksAfter: [undefined],
|
||||
runIntegrityCheck: false,
|
||||
},
|
||||
{
|
||||
targetRerouteId: 3,
|
||||
parentIds: [2, 4],
|
||||
linksBefore: [2, 2],
|
||||
linksAfter: [0, 0],
|
||||
runIntegrityCheck: true,
|
||||
},
|
||||
])("Should allow reconnect from output to any reroute", (
|
||||
{ targetRerouteId, parentIds, linksBefore, linksAfter, runIntegrityCheck },
|
||||
{ graph, connector, validateIntegrityNoChanges, getNextLinkIds },
|
||||
) => {
|
||||
const linkCreatedCallback = vi.fn()
|
||||
connector.listenUntilReset("link-created", linkCreatedCallback)
|
||||
|
||||
const disconnectedNode = graph.getNodeById(9)!
|
||||
|
||||
// Parent reroutes of the target reroute
|
||||
for (const [index, parentId] of parentIds.entries()) {
|
||||
const reroute = graph.reroutes.get(parentId)!
|
||||
expect(reroute.linkIds.size).toBe(linksBefore[index])
|
||||
}
|
||||
|
||||
const targetReroute = graph.reroutes.get(targetRerouteId)!
|
||||
const nextLinkIds = getNextLinkIds(targetReroute.linkIds)
|
||||
const dropEvent = { canvasX: targetReroute.pos[0], canvasY: targetReroute.pos[1] } as any
|
||||
|
||||
connector.dragNewFromOutput(graph, disconnectedNode, disconnectedNode.outputs[0])
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
|
||||
expect(disconnectedNode.outputs[0].links).toEqual(nextLinkIds)
|
||||
expect([...targetReroute.linkIds.values()]).toEqual(nextLinkIds)
|
||||
|
||||
// Parent reroutes should have lost the links or been removed
|
||||
for (const [index, parentId] of parentIds.entries()) {
|
||||
const reroute = graph.reroutes.get(parentId)!
|
||||
if (linksAfter[index] === undefined) {
|
||||
expect(reroute).toBeUndefined()
|
||||
} else {
|
||||
expect(reroute.linkIds.size).toBe(linksAfter[index])
|
||||
}
|
||||
}
|
||||
|
||||
expect(linkCreatedCallback).toHaveBeenCalledTimes(nextLinkIds.length)
|
||||
|
||||
if (runIntegrityCheck) {
|
||||
validateIntegrityNoChanges()
|
||||
}
|
||||
})
|
||||
|
||||
type ReconnectTestData = {
|
||||
/** Drag link from this reroute */
|
||||
fromRerouteId: number
|
||||
/** Drop link on this reroute */
|
||||
toRerouteId: number
|
||||
/** Reroute IDs that should be removed from the resultant reroute chain */
|
||||
shouldBeRemoved: number[]
|
||||
/** Reroutes that should have NONE of the link IDs that toReroute has */
|
||||
shouldHaveLinkIdsRemoved: number[]
|
||||
/** Whether to test floating inputs */
|
||||
testFloatingInputs?: true
|
||||
/** Number of expected extra links to be created */
|
||||
expectedExtraLinks?: number
|
||||
}
|
||||
|
||||
test.for<ReconnectTestData>([
|
||||
{
|
||||
fromRerouteId: 10,
|
||||
toRerouteId: 15,
|
||||
shouldBeRemoved: [14],
|
||||
shouldHaveLinkIdsRemoved: [13, 8, 6, 7],
|
||||
},
|
||||
{
|
||||
fromRerouteId: 8,
|
||||
toRerouteId: 2,
|
||||
shouldBeRemoved: [4],
|
||||
shouldHaveLinkIdsRemoved: [],
|
||||
},
|
||||
{
|
||||
fromRerouteId: 3,
|
||||
toRerouteId: 12,
|
||||
shouldBeRemoved: [11],
|
||||
shouldHaveLinkIdsRemoved: [10, 13, 14, 15, 8, 6, 7],
|
||||
},
|
||||
{
|
||||
fromRerouteId: 15,
|
||||
toRerouteId: 7,
|
||||
shouldBeRemoved: [8, 6],
|
||||
shouldHaveLinkIdsRemoved: [],
|
||||
},
|
||||
{
|
||||
fromRerouteId: 1,
|
||||
toRerouteId: 7,
|
||||
shouldBeRemoved: [8, 6],
|
||||
shouldHaveLinkIdsRemoved: [],
|
||||
},
|
||||
{
|
||||
fromRerouteId: 1,
|
||||
toRerouteId: 10,
|
||||
shouldBeRemoved: [],
|
||||
shouldHaveLinkIdsRemoved: [],
|
||||
},
|
||||
{
|
||||
fromRerouteId: 4,
|
||||
toRerouteId: 8,
|
||||
shouldBeRemoved: [],
|
||||
shouldHaveLinkIdsRemoved: [],
|
||||
testFloatingInputs: true,
|
||||
expectedExtraLinks: 2,
|
||||
},
|
||||
{
|
||||
fromRerouteId: 2,
|
||||
toRerouteId: 12,
|
||||
shouldBeRemoved: [11],
|
||||
shouldHaveLinkIdsRemoved: [],
|
||||
testFloatingInputs: true,
|
||||
expectedExtraLinks: 1,
|
||||
},
|
||||
])("Should allow connecting from reroutes to another reroute", (
|
||||
{ fromRerouteId, toRerouteId, shouldBeRemoved, shouldHaveLinkIdsRemoved, testFloatingInputs, expectedExtraLinks },
|
||||
{ graph, connector, getNextLinkIds },
|
||||
) => {
|
||||
if (testFloatingInputs) {
|
||||
// Start by disconnecting the output of the 3x3 array of reroutes
|
||||
graph.getNodeById(4)!.disconnectOutput(0)
|
||||
}
|
||||
|
||||
const fromReroute = graph.reroutes.get(fromRerouteId)!
|
||||
const toReroute = graph.reroutes.get(toRerouteId)!
|
||||
const nextLinkIds = getNextLinkIds(toReroute.linkIds, expectedExtraLinks)
|
||||
|
||||
const originalParentChain = LLink.getReroutes(graph, toReroute)
|
||||
|
||||
const sortAndJoin = (numbers: Iterable<number>) => [...numbers].sort().join(",")
|
||||
const hasIdenticalLinks = (a: Reroute, b: Reroute) =>
|
||||
sortAndJoin(a.linkIds) === sortAndJoin(b.linkIds) &&
|
||||
sortAndJoin(a.floatingLinkIds) === sortAndJoin(b.floatingLinkIds)
|
||||
|
||||
// Sanity check shouldBeRemoved
|
||||
const reroutesWithIdenticalLinkIds = originalParentChain.filter(parent => hasIdenticalLinks(parent, toReroute))
|
||||
expect(reroutesWithIdenticalLinkIds.map(reroute => reroute.id)).toEqual(shouldBeRemoved)
|
||||
|
||||
connector.dragFromReroute(graph, fromReroute)
|
||||
|
||||
const dropEvent = { canvasX: toReroute.pos[0], canvasY: toReroute.pos[1] } as any
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
|
||||
const newParentChain = LLink.getReroutes(graph, toReroute)
|
||||
for (const rerouteId of shouldBeRemoved) {
|
||||
expect(originalParentChain.map(reroute => reroute.id)).toContain(rerouteId)
|
||||
expect(newParentChain.map(reroute => reroute.id)).not.toContain(rerouteId)
|
||||
}
|
||||
|
||||
expect([...toReroute.linkIds.values()]).toEqual(nextLinkIds)
|
||||
|
||||
// Parent reroutes should have lost the links or been removed
|
||||
for (const rerouteId of shouldBeRemoved) {
|
||||
const reroute = graph.reroutes.get(rerouteId)!
|
||||
expect(reroute).toBeUndefined()
|
||||
}
|
||||
|
||||
for (const rerouteId of shouldHaveLinkIdsRemoved) {
|
||||
const reroute = graph.reroutes.get(rerouteId)!
|
||||
for (const linkId of toReroute.linkIds) {
|
||||
expect(reroute.linkIds).not.toContain(linkId)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate all links in a reroute share the same origin
|
||||
for (const reroute of graph.reroutes.values()) {
|
||||
for (const linkId of reroute.linkIds) {
|
||||
const link = graph.links.get(linkId)
|
||||
expect(link?.origin_id).toEqual(reroute.origin_id)
|
||||
expect(link?.origin_slot).toEqual(reroute.origin_slot)
|
||||
}
|
||||
for (const linkId of reroute.floatingLinkIds) {
|
||||
if (reroute.origin_id === undefined) continue
|
||||
|
||||
const link = graph.floatingLinks.get(linkId)
|
||||
expect(link?.origin_id).toEqual(reroute.origin_id)
|
||||
expect(link?.origin_slot).toEqual(reroute.origin_slot)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test.for([
|
||||
{ from: 8, to: 13 },
|
||||
{ from: 7, to: 13 },
|
||||
{ from: 6, to: 13 },
|
||||
{ from: 13, to: 10 },
|
||||
{ from: 14, to: 10 },
|
||||
{ from: 15, to: 10 },
|
||||
{ from: 14, to: 13 },
|
||||
{ from: 10, to: 10 },
|
||||
])("Connecting reroutes to invalid targets should do nothing", (
|
||||
{ from, to },
|
||||
{ graph, connector, validateIntegrityNoChanges },
|
||||
) => {
|
||||
const listener = vi.fn()
|
||||
connector.listenUntilReset("link-created", listener)
|
||||
|
||||
const fromReroute = graph.reroutes.get(from)!
|
||||
const toReroute = graph.reroutes.get(to)!
|
||||
|
||||
const dropEvent = { canvasX: toReroute.pos[0], canvasY: toReroute.pos[1] } as any
|
||||
|
||||
connector.dragFromReroute(graph, fromReroute)
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
|
||||
expect(listener).not.toHaveBeenCalled()
|
||||
validateIntegrityNoChanges()
|
||||
})
|
||||
|
||||
const nodeReroutePairs = [
|
||||
{ nodeId: 1, rerouteId: 1 },
|
||||
{ nodeId: 1, rerouteId: 3 },
|
||||
{ nodeId: 1, rerouteId: 4 },
|
||||
{ nodeId: 1, rerouteId: 2 },
|
||||
{ nodeId: 4, rerouteId: 7 },
|
||||
{ nodeId: 4, rerouteId: 6 },
|
||||
{ nodeId: 4, rerouteId: 8 },
|
||||
{ nodeId: 4, rerouteId: 10 },
|
||||
{ nodeId: 4, rerouteId: 12 },
|
||||
]
|
||||
test.for(nodeReroutePairs)("Should ignore connections from input to same node via reroutes", (
|
||||
{ nodeId, rerouteId },
|
||||
{ graph, connector, validateIntegrityNoChanges },
|
||||
) => {
|
||||
const listener = vi.fn()
|
||||
connector.listenUntilReset("link-created", listener)
|
||||
|
||||
const node = graph.getNodeById(nodeId)!
|
||||
const input = node.inputs[0]
|
||||
const reroute = graph.getReroute(rerouteId)!
|
||||
const dropEvent = { canvasX: reroute.pos[0], canvasY: reroute.pos[1] } as any
|
||||
|
||||
connector.dragNewFromInput(graph, node, input)
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
|
||||
expect(listener).not.toHaveBeenCalled()
|
||||
validateIntegrityNoChanges()
|
||||
|
||||
// No links should have the same origin_id and target_id
|
||||
for (const link of graph.links.values()) {
|
||||
expect(link.origin_id).not.toEqual(link.target_id)
|
||||
}
|
||||
})
|
||||
|
||||
test.for(nodeReroutePairs)("Should ignore connections looping back to the origin node from a reroute", (
|
||||
{ nodeId, rerouteId },
|
||||
{ graph, connector, validateIntegrityNoChanges },
|
||||
) => {
|
||||
const listener = vi.fn()
|
||||
connector.listenUntilReset("link-created", listener)
|
||||
|
||||
const node = graph.getNodeById(nodeId)!
|
||||
const reroute = graph.getReroute(rerouteId)!
|
||||
const dropEvent = { canvasX: node.pos[0], canvasY: node.pos[1] } as any
|
||||
|
||||
connector.dragFromReroute(graph, reroute)
|
||||
connector.dropLinks(graph, dropEvent)
|
||||
|
||||
expect(listener).not.toHaveBeenCalled()
|
||||
validateIntegrityNoChanges()
|
||||
|
||||
// No links should have the same origin_id and target_id
|
||||
for (const link of graph.links.values()) {
|
||||
expect(link.origin_id).not.toEqual(link.target_id)
|
||||
}
|
||||
})
|
||||
|
||||
test.for(nodeReroutePairs)("Should ignore connections looping back to the origin node input from a reroute", (
|
||||
{ nodeId, rerouteId },
|
||||
{ graph, connector, validateIntegrityNoChanges },
|
||||
) => {
|
||||
const listener = vi.fn()
|
||||
connector.listenUntilReset("link-created", listener)
|
||||
|
||||
const node = graph.getNodeById(nodeId)!
|
||||
const reroute = graph.getReroute(rerouteId)!
|
||||
const inputPos = node.getInputPos(0)
|
||||
const dropOnInputEvent = { canvasX: inputPos[0], canvasY: inputPos[1] } as any
|
||||
|
||||
connector.dragFromReroute(graph, reroute)
|
||||
connector.dropLinks(graph, dropOnInputEvent)
|
||||
|
||||
expect(listener).not.toHaveBeenCalled()
|
||||
validateIntegrityNoChanges()
|
||||
|
||||
// No links should have the same origin_id and target_id
|
||||
for (const link of graph.links.values()) {
|
||||
expect(link.origin_id).not.toEqual(link.target_id)
|
||||
}
|
||||
})
|
||||
})
|
||||
1
test/assets/reroutesComplex.json
Normal file
1
test/assets/reroutesComplex.json
Normal file
@@ -0,0 +1 @@
|
||||
{"id":"e5ffd5e1-1c01-45ac-90dd-b7d83a206b0f","revision":0,"last_node_id":9,"last_link_id":12,"nodes":[{"id":3,"type":"InvertMask","pos":[390,270],"size":[140,26],"flags":{},"order":8,"mode":0,"inputs":[{"localized_name":"mask","name":"mask","type":"MASK","link":3}],"outputs":[{"localized_name":"MASK","name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]},{"id":7,"type":"InvertMask","pos":[390,560],"size":[140,26],"flags":{},"order":4,"mode":0,"inputs":[{"localized_name":"mask","name":"mask","type":"MASK","link":10}],"outputs":[{"localized_name":"MASK","name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]},{"id":8,"type":"InvertMask","pos":[390,640],"size":[140,26],"flags":{},"order":3,"mode":0,"inputs":[{"localized_name":"mask","name":"mask","type":"MASK","link":9}],"outputs":[{"localized_name":"MASK","name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]},{"id":5,"type":"InvertMask","pos":[390,480],"size":[140,26],"flags":{"collapsed":false},"order":5,"mode":0,"inputs":[{"localized_name":"mask","name":"mask","type":"MASK","link":11}],"outputs":[{"localized_name":"MASK","name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]},{"id":6,"type":"InvertMask","pos":[390,400],"size":[140,26],"flags":{},"order":6,"mode":0,"inputs":[{"localized_name":"mask","name":"mask","type":"MASK","link":12}],"outputs":[{"localized_name":"MASK","name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]},{"id":4,"type":"InvertMask","pos":[50,640],"size":[140,26],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"mask","name":"mask","type":"MASK","link":null}],"outputs":[{"localized_name":"MASK","name":"MASK","type":"MASK","links":[9,10,11,12]}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]},{"id":2,"type":"InvertMask","pos":[390,180],"size":[140,26],"flags":{},"order":7,"mode":0,"inputs":[{"localized_name":"mask","name":"mask","type":"MASK","link":2}],"outputs":[{"localized_name":"MASK","name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]},{"id":1,"type":"InvertMask","pos":[50,170],"size":[140,26],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"mask","name":"mask","type":"MASK","link":null}],"outputs":[{"localized_name":"MASK","name":"MASK","type":"MASK","links":[2,3]}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]},{"id":9,"type":"InvertMask","pos":[50,410],"size":[140,26],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"mask","name":"mask","type":"MASK","link":null}],"outputs":[{"localized_name":"MASK","name":"MASK","type":"MASK","links":[]}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]}],"links":[[2,1,0,2,0,"MASK"],[3,1,0,3,0,"MASK"],[9,4,0,8,0,"MASK"],[10,4,0,7,0,"MASK"],[11,4,0,5,0,"MASK"],[12,4,0,6,0,"MASK"]],"floatingLinks":[{"id":6,"origin_id":1,"origin_slot":0,"target_id":-1,"target_slot":-1,"type":"MASK","parentId":1}],"groups":[],"config":{},"extra":{"ds":{"scale":1,"offset":[0,0]},"linkExtensions":[{"id":2,"parentId":3},{"id":3,"parentId":3},{"id":9,"parentId":12},{"id":10,"parentId":15},{"id":11,"parentId":7},{"id":12,"parentId":7}],"reroutes":[{"id":1,"parentId":2,"pos":[340,160],"linkIds":[],"floating":{"slotType":"output"}},{"id":2,"parentId":4,"pos":[290,190],"linkIds":[2,3]},{"id":3,"parentId":2,"pos":[350,220],"linkIds":[2,3]},{"id":4,"pos":[250,190],"linkIds":[2,3]},{"id":6,"parentId":8,"pos":[300,450],"linkIds":[11,12]},{"id":7,"parentId":6,"pos":[350,450],"linkIds":[11,12]},{"id":8,"parentId":13,"pos":[250,450],"linkIds":[11,12]},{"id":10,"pos":[250,650],"linkIds":[9,10,11,12]},{"id":11,"parentId":10,"pos":[300,650],"linkIds":[9]},{"id":12,"parentId":11,"pos":[350,650],"linkIds":[9]},{"id":13,"parentId":10,"pos":[250,570],"linkIds":[10,11,12]},{"id":14,"parentId":13,"pos":[300,570],"linkIds":[10]},{"id":15,"parentId":14,"pos":[350,570],"linkIds":[10]}]},"version":0.4}
|
||||
@@ -8,6 +8,7 @@ import { LiteGraph } from "@/litegraph"
|
||||
import floatingBranch from "./assets/floatingBranch.json"
|
||||
import floatingLink from "./assets/floatingLink.json"
|
||||
import linkedNodes from "./assets/linkedNodes.json"
|
||||
import reroutesComplex from "./assets/reroutesComplex.json"
|
||||
import { basicSerialisableGraph, minimalSerialisableGraph, oldSchemaGraph } from "./assets/testGraphs"
|
||||
|
||||
interface LitegraphFixtures {
|
||||
@@ -16,8 +17,8 @@ interface LitegraphFixtures {
|
||||
oldSchemaGraph: ISerialisedGraph
|
||||
floatingLinkGraph: ISerialisedGraph
|
||||
linkedNodesGraph: ISerialisedGraph
|
||||
floatingBranchSerialisedGraph: ISerialisedGraph
|
||||
floatingBranchGraph: LGraph
|
||||
reroutesComplexGraph: LGraph
|
||||
}
|
||||
|
||||
/** These fixtures alter global state, and are difficult to reset. Relies on a single test per-file to reset state. */
|
||||
@@ -38,9 +39,13 @@ export const test = baseTest.extend<LitegraphFixtures>({
|
||||
oldSchemaGraph: structuredClone(oldSchemaGraph),
|
||||
floatingLinkGraph: structuredClone(floatingLink as unknown as ISerialisedGraph),
|
||||
linkedNodesGraph: structuredClone(linkedNodes as unknown as ISerialisedGraph),
|
||||
floatingBranchSerialisedGraph: structuredClone(floatingBranch as unknown as ISerialisedGraph),
|
||||
floatingBranchGraph: async ({ floatingBranchSerialisedGraph }, use) => {
|
||||
const cloned = structuredClone(floatingBranchSerialisedGraph)
|
||||
floatingBranchGraph: async ({}, use) => {
|
||||
const cloned = structuredClone(floatingBranch as unknown as ISerialisedGraph)
|
||||
const graph = new LGraph(cloned)
|
||||
await use(graph)
|
||||
},
|
||||
reroutesComplexGraph: async ({}, use) => {
|
||||
const cloned = structuredClone(reroutesComplex as unknown as ISerialisedGraph)
|
||||
const graph = new LGraph(cloned)
|
||||
await use(graph)
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user