Skip to content

Commit eb59077

Browse files
author
Connor Byrne
committed
test: cover boundary link preservation on subgraph creation
The only e2e coverage of Convert to Subgraph selects every node in the workflow, so no boundary link exists and the rewiring code in _convertToSubgraphImpl is never exercised. Partial selection - the operation users actually perform - has no coverage at all. Packs KSampler + VAE Decode out of the default workflow, leaving five boundary input links from four distinct source outputs and one boundary output link, and asserts each one lands on a type-compatible slot on the new subgraph node and survives a serialize/reload round trip.
1 parent 2ab67a6 commit eb59077

1 file changed

Lines changed: 100 additions & 0 deletions

File tree

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { expect } from '@playwright/test'
2+
3+
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
4+
5+
test.describe(
6+
'Subgraph creation boundary links',
7+
{ tag: ['@slow', '@subgraph'] },
8+
() => {
9+
test.beforeEach(async ({ comfyPage }) => {
10+
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled')
11+
await comfyPage.workflow.loadWorkflow('default')
12+
await comfyPage.nodeOps.selectNodes(['KSampler', 'VAE Decode'])
13+
const ksampler = await comfyPage.nodeOps.getNodeRefById('3')
14+
await ksampler.convertToSubgraph()
15+
})
16+
17+
test('rewires every boundary link onto the new subgraph node', async ({
18+
comfyPage
19+
}) => {
20+
await expect
21+
.poll(() =>
22+
comfyPage.page.evaluate(() => {
23+
const graph = window.app!.graph!
24+
const host = graph.nodes.find((node) => node.isSubgraphNode())
25+
if (!host) return { error: 'no subgraph node' }
26+
27+
const links = [...graph.links.values()]
28+
return {
29+
rootLinkCount: links.length,
30+
inputCount: host.inputs.length,
31+
outputCount: host.outputs.length,
32+
unconnectedInputs: host.inputs.filter(
33+
(input) => input.link == null
34+
).length,
35+
sources: links
36+
.filter((link) => link.target_id === host.id)
37+
.map((link) => `${link.origin_id}:${link.origin_slot}`)
38+
.sort(),
39+
saveImageFedByHost:
40+
links.find((link) => String(link.target_id) === '9')
41+
?.origin_id === host.id
42+
}
43+
})
44+
)
45+
.toEqual({
46+
rootLinkCount: 6,
47+
inputCount: 5,
48+
outputCount: 1,
49+
unconnectedInputs: 0,
50+
sources: ['4:0', '4:2', '5:0', '6:0', '7:0'],
51+
saveImageFedByHost: true
52+
})
53+
})
54+
55+
test('lands each boundary link on a type-compatible slot', async ({
56+
comfyPage
57+
}) => {
58+
await expect
59+
.poll(() =>
60+
comfyPage.page.evaluate(() => {
61+
const graph = window.app!.graph!
62+
const host = graph.nodes.find((node) => node.isSubgraphNode())
63+
if (!host) return ['no subgraph node']
64+
65+
return [...graph.links.values()]
66+
.filter((link) => link.target_id === host.id)
67+
.filter(
68+
(link) => host.inputs[link.target_slot]?.type !== link.type
69+
)
70+
.map(
71+
(link) =>
72+
`${link.type} link landed on slot ${link.target_slot} typed ${host.inputs[link.target_slot]?.type}`
73+
)
74+
})
75+
)
76+
.toEqual([])
77+
})
78+
79+
test('preserves the rewiring across a save and reload', async ({
80+
comfyPage
81+
}) => {
82+
const serialisedLinks = () =>
83+
comfyPage.page.evaluate(() =>
84+
[...window.app!.graph!.links.values()]
85+
.map(
86+
(link) =>
87+
`${link.origin_id}:${link.origin_slot}->${link.target_id}:${link.target_slot}`
88+
)
89+
.sort()
90+
)
91+
92+
const beforeReload = await serialisedLinks()
93+
await comfyPage.nodeOps.loadGraph(
94+
await comfyPage.nodeOps.getSerializedGraph()
95+
)
96+
97+
await expect.poll(serialisedLinks).toEqual(beforeReload)
98+
})
99+
}
100+
)

0 commit comments

Comments
 (0)