mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
* [fix] resolve group node execution error when connecting to external nodes Fixed ExecutableGroupNodeChildDTO.resolveInput to properly handle connections from group node children to external nodes. The method now tries to find nodes by their full ID first (for external nodes) before falling back to the shortened ID (for internal group nodes). Added comprehensive unit tests to prevent regression. * [feat] Add error check for unsupported group nodes inside subgraphs Added validation to detect when group node children are executing within subgraph contexts (execution ID has >2 segments) and provide clear error message directing users to convert to subgraphs instead. Includes comprehensive test coverage for the new validation.
200 lines
5.4 KiB
TypeScript
200 lines
5.4 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
import type { GroupNodeHandler } from '@/extensions/core/groupNode'
|
|
import type {
|
|
ExecutableLGraphNode,
|
|
ExecutionId,
|
|
LGraphNode
|
|
} from '@/lib/litegraph/src/litegraph'
|
|
import { ExecutableGroupNodeChildDTO } from '@/utils/executableGroupNodeChildDTO'
|
|
|
|
describe('ExecutableGroupNodeChildDTO', () => {
|
|
let mockNode: LGraphNode
|
|
let mockInputNode: LGraphNode
|
|
let mockNodesByExecutionId: Map<ExecutionId, ExecutableLGraphNode>
|
|
let mockGroupNodeHandler: GroupNodeHandler
|
|
|
|
beforeEach(() => {
|
|
// Create mock nodes
|
|
mockNode = {
|
|
id: '3', // Simple node ID for most tests
|
|
graph: {},
|
|
getInputNode: vi.fn(),
|
|
getInputLink: vi.fn(),
|
|
inputs: []
|
|
} as any
|
|
|
|
mockInputNode = {
|
|
id: '1',
|
|
graph: {}
|
|
} as any
|
|
|
|
// Create the nodesByExecutionId map
|
|
mockNodesByExecutionId = new Map()
|
|
|
|
mockGroupNodeHandler = {} as GroupNodeHandler
|
|
})
|
|
|
|
describe('resolveInput', () => {
|
|
it('should resolve input from external node (node outside the group)', () => {
|
|
// Setup: Group node child with ID '10:3'
|
|
const groupNodeChild = {
|
|
id: '10:3',
|
|
graph: {},
|
|
getInputNode: vi.fn().mockReturnValue(mockInputNode),
|
|
getInputLink: vi.fn().mockReturnValue({
|
|
origin_slot: 0
|
|
}),
|
|
inputs: []
|
|
} as any
|
|
|
|
// External node with ID '1'
|
|
const externalNodeDto = {
|
|
id: '1',
|
|
type: 'TestNode'
|
|
} as ExecutableLGraphNode
|
|
|
|
mockNodesByExecutionId.set('1', externalNodeDto)
|
|
|
|
const dto = new ExecutableGroupNodeChildDTO(
|
|
groupNodeChild,
|
|
[], // No subgraph path - group is in root graph
|
|
mockNodesByExecutionId,
|
|
undefined,
|
|
mockGroupNodeHandler
|
|
)
|
|
|
|
const result = dto.resolveInput(0)
|
|
|
|
expect(result).toEqual({
|
|
node: externalNodeDto,
|
|
origin_id: '1',
|
|
origin_slot: 0
|
|
})
|
|
})
|
|
|
|
it('should resolve input from internal node (node inside the same group)', () => {
|
|
// Setup: Group node child with ID '10:3'
|
|
const groupNodeChild = {
|
|
id: '10:3',
|
|
graph: {},
|
|
getInputNode: vi.fn(),
|
|
getInputLink: vi.fn(),
|
|
inputs: []
|
|
} as any
|
|
|
|
// Internal node with ID '10:2'
|
|
const internalInputNode = {
|
|
id: '10:2',
|
|
graph: {}
|
|
} as LGraphNode
|
|
|
|
const internalNodeDto = {
|
|
id: '2',
|
|
type: 'InternalNode'
|
|
} as ExecutableLGraphNode
|
|
|
|
// Internal nodes are stored with just their index
|
|
mockNodesByExecutionId.set('2', internalNodeDto)
|
|
|
|
groupNodeChild.getInputNode.mockReturnValue(internalInputNode)
|
|
groupNodeChild.getInputLink.mockReturnValue({
|
|
origin_slot: 1
|
|
})
|
|
|
|
const dto = new ExecutableGroupNodeChildDTO(
|
|
groupNodeChild,
|
|
[],
|
|
mockNodesByExecutionId,
|
|
undefined,
|
|
mockGroupNodeHandler
|
|
)
|
|
|
|
const result = dto.resolveInput(0)
|
|
|
|
expect(result).toEqual({
|
|
node: internalNodeDto,
|
|
origin_id: '10:2',
|
|
origin_slot: 1
|
|
})
|
|
})
|
|
|
|
it('should return undefined if no input node exists', () => {
|
|
mockNode.getInputNode = vi.fn().mockReturnValue(null)
|
|
|
|
const dto = new ExecutableGroupNodeChildDTO(
|
|
mockNode,
|
|
[],
|
|
mockNodesByExecutionId,
|
|
undefined,
|
|
mockGroupNodeHandler
|
|
)
|
|
|
|
const result = dto.resolveInput(0)
|
|
|
|
expect(result).toBeUndefined()
|
|
})
|
|
|
|
it('should throw error if input link is missing', () => {
|
|
mockNode.getInputNode = vi.fn().mockReturnValue(mockInputNode)
|
|
mockNode.getInputLink = vi.fn().mockReturnValue(null)
|
|
|
|
const dto = new ExecutableGroupNodeChildDTO(
|
|
mockNode,
|
|
[],
|
|
mockNodesByExecutionId,
|
|
undefined,
|
|
mockGroupNodeHandler
|
|
)
|
|
|
|
expect(() => dto.resolveInput(0)).toThrow('Failed to get input link')
|
|
})
|
|
|
|
it('should throw error if input node cannot be found in nodesByExecutionId', () => {
|
|
// Node exists but is not in the map
|
|
mockNode.getInputNode = vi.fn().mockReturnValue(mockInputNode)
|
|
mockNode.getInputLink = vi.fn().mockReturnValue({
|
|
origin_slot: 0
|
|
})
|
|
|
|
const dto = new ExecutableGroupNodeChildDTO(
|
|
mockNode,
|
|
[],
|
|
mockNodesByExecutionId, // Empty map
|
|
undefined,
|
|
mockGroupNodeHandler
|
|
)
|
|
|
|
expect(() => dto.resolveInput(0)).toThrow(
|
|
'Failed to get input node 1 for group node child 3 with slot 0'
|
|
)
|
|
})
|
|
|
|
it('should throw error for group nodes inside subgraphs (unsupported)', () => {
|
|
// Setup: Group node child inside a subgraph (execution ID has more than 2 segments)
|
|
const nestedGroupNode = {
|
|
id: '1:2:3', // subgraph:groupnode:innernode
|
|
graph: {},
|
|
getInputNode: vi.fn().mockReturnValue(mockInputNode),
|
|
getInputLink: vi.fn().mockReturnValue({
|
|
origin_slot: 0
|
|
}),
|
|
inputs: []
|
|
} as any
|
|
|
|
// Create DTO with deeply nested path to simulate group node inside subgraph
|
|
const dto = new ExecutableGroupNodeChildDTO(
|
|
nestedGroupNode,
|
|
['1', '2'], // Path indicating it's inside a subgraph then group
|
|
mockNodesByExecutionId,
|
|
undefined,
|
|
mockGroupNodeHandler
|
|
)
|
|
|
|
expect(() => dto.resolveInput(0)).toThrow(
|
|
'Group nodes inside subgraphs are not supported. Please convert the group node to a subgraph instead.'
|
|
)
|
|
})
|
|
})
|
|
})
|