mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-19 06:20:10 +00:00
## Summary Merges latest changes from `main` as of 10-06-2025. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5965-Merge-main-as-of-10-06-2025-into-rh-test-2856d73d3650812cb95fd8917278a770) by [Unito](https://www.unito.io) --------- Signed-off-by: Marcel Petrick <mail@marcelpetrick.it> Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com> Co-authored-by: Christian Byrne <cbyrne@comfy.org> Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Alexander Brown <drjkl@comfy.org> Co-authored-by: Benjamin Lu <benceruleanlu@proton.me> Co-authored-by: Terry Jia <terryjia88@gmail.com> Co-authored-by: snomiao <snomiao@gmail.com> Co-authored-by: Simula_r <18093452+simula-r@users.noreply.github.com> Co-authored-by: Jake Schroeder <jake.schroeder@isophex.com> Co-authored-by: Comfy Org PR Bot <snomiao+comfy-pr@gmail.com> Co-authored-by: AustinMroz <4284322+AustinMroz@users.noreply.github.com> Co-authored-by: GitHub Action <action@github.com> Co-authored-by: Johnpaul Chiwetelu <49923152+Myestery@users.noreply.github.com> Co-authored-by: Marcel Petrick <mail@marcelpetrick.it> Co-authored-by: Alexander Brown <DrJKL0424@gmail.com> Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com> Co-authored-by: Alexander Piskun <13381981+bigcat88@users.noreply.github.com> Co-authored-by: Rizumu Ayaka <rizumu@ayaka.moe> Co-authored-by: JakeSchroeder <jake@axiom.co> Co-authored-by: AustinMroz <austin@comfy.org> Co-authored-by: DrJKL <DrJKL@users.noreply.github.com> Co-authored-by: ComfyUI Wiki <contact@comfyui-wiki.com>
210 lines
5.3 KiB
TypeScript
210 lines
5.3 KiB
TypeScript
/**
|
|
* Tests for NodeHeader subgraph functionality
|
|
*/
|
|
import { createTestingPinia } from '@pinia/testing'
|
|
import { mount } from '@vue/test-utils'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
|
import NodeHeader from '@/renderer/extensions/vueNodes/components/NodeHeader.vue'
|
|
import { getNodeByLocatorId } from '@/utils/graphTraversalUtil'
|
|
|
|
// Mock dependencies
|
|
vi.mock('@/scripts/app', () => ({
|
|
app: {
|
|
graph: null as any
|
|
}
|
|
}))
|
|
|
|
vi.mock('@/utils/graphTraversalUtil', () => ({
|
|
getNodeByLocatorId: vi.fn(),
|
|
getLocatorIdFromNodeData: vi.fn((nodeData) =>
|
|
nodeData.subgraphId
|
|
? `${nodeData.subgraphId}:${String(nodeData.id)}`
|
|
: String(nodeData.id)
|
|
)
|
|
}))
|
|
|
|
vi.mock('@/composables/useErrorHandling', () => ({
|
|
useErrorHandling: () => ({
|
|
toastErrorHandler: vi.fn()
|
|
})
|
|
}))
|
|
|
|
vi.mock('vue-i18n', () => ({
|
|
useI18n: () => ({
|
|
t: vi.fn((key) => key)
|
|
}),
|
|
createI18n: vi.fn(() => ({
|
|
global: {
|
|
t: vi.fn((key) => key)
|
|
}
|
|
}))
|
|
}))
|
|
|
|
vi.mock('@/i18n', () => ({
|
|
st: vi.fn((key) => key),
|
|
t: vi.fn((key) => key),
|
|
i18n: {
|
|
global: {
|
|
t: vi.fn((key) => key)
|
|
}
|
|
}
|
|
}))
|
|
|
|
describe('NodeHeader - Subgraph Functionality', () => {
|
|
// Helper to setup common mocks
|
|
const setupMocks = async (isSubgraph = true, hasGraph = true) => {
|
|
const { app } = await import('@/scripts/app')
|
|
|
|
if (hasGraph) {
|
|
;(app as any).graph = { rootGraph: {} }
|
|
} else {
|
|
;(app as any).graph = null
|
|
}
|
|
|
|
vi.mocked(getNodeByLocatorId).mockReturnValue({
|
|
isSubgraphNode: () => isSubgraph
|
|
} as any)
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
const createMockNodeData = (
|
|
id: string,
|
|
subgraphId?: string
|
|
): VueNodeData => ({
|
|
id,
|
|
title: 'Test Node',
|
|
type: 'TestNode',
|
|
mode: 0,
|
|
selected: false,
|
|
executing: false,
|
|
subgraphId,
|
|
widgets: [],
|
|
inputs: [],
|
|
outputs: [],
|
|
hasErrors: false,
|
|
flags: {}
|
|
})
|
|
|
|
const createWrapper = (props = {}) => {
|
|
return mount(NodeHeader, {
|
|
props,
|
|
global: {
|
|
plugins: [createTestingPinia({ createSpy: vi.fn })],
|
|
mocks: {
|
|
$t: vi.fn((key: string) => key),
|
|
$primevue: { config: {} }
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
it('should show subgraph button for subgraph nodes', async () => {
|
|
await setupMocks(true) // isSubgraph = true
|
|
|
|
const wrapper = createWrapper({
|
|
nodeData: createMockNodeData('test-node-1'),
|
|
readonly: false
|
|
})
|
|
|
|
await wrapper.vm.$nextTick()
|
|
|
|
const subgraphButton = wrapper.find('[data-testid="subgraph-enter-button"]')
|
|
expect(subgraphButton.exists()).toBe(true)
|
|
})
|
|
|
|
it('should not show subgraph button for regular nodes', async () => {
|
|
await setupMocks(false) // isSubgraph = false
|
|
|
|
const wrapper = createWrapper({
|
|
nodeData: createMockNodeData('test-node-1'),
|
|
readonly: false
|
|
})
|
|
|
|
await wrapper.vm.$nextTick()
|
|
|
|
const subgraphButton = wrapper.find('[data-testid="subgraph-enter-button"]')
|
|
expect(subgraphButton.exists()).toBe(false)
|
|
})
|
|
|
|
it('should emit enter-subgraph event when button is clicked', async () => {
|
|
await setupMocks(true) // isSubgraph = true
|
|
|
|
const wrapper = createWrapper({
|
|
nodeData: createMockNodeData('test-node-1'),
|
|
readonly: false
|
|
})
|
|
|
|
await wrapper.vm.$nextTick()
|
|
|
|
const subgraphButton = wrapper.find('[data-testid="subgraph-enter-button"]')
|
|
await subgraphButton.trigger('click')
|
|
|
|
expect(wrapper.emitted('enter-subgraph')).toBeTruthy()
|
|
expect(wrapper.emitted('enter-subgraph')).toHaveLength(1)
|
|
})
|
|
|
|
it('should handle subgraph context correctly', async () => {
|
|
await setupMocks(true) // isSubgraph = true
|
|
|
|
const wrapper = createWrapper({
|
|
nodeData: createMockNodeData('test-node-1', 'subgraph-id'),
|
|
readonly: false
|
|
})
|
|
|
|
await wrapper.vm.$nextTick()
|
|
|
|
// Should call getNodeByLocatorId with correct locator ID
|
|
expect(vi.mocked(getNodeByLocatorId)).toHaveBeenCalledWith(
|
|
expect.anything(),
|
|
'subgraph-id:test-node-1'
|
|
)
|
|
|
|
const subgraphButton = wrapper.find('[data-testid="subgraph-enter-button"]')
|
|
expect(subgraphButton.exists()).toBe(true)
|
|
})
|
|
|
|
it('should handle missing graph gracefully', async () => {
|
|
await setupMocks(true, false) // isSubgraph = true, hasGraph = false
|
|
|
|
const wrapper = createWrapper({
|
|
nodeData: createMockNodeData('test-node-1'),
|
|
readonly: false
|
|
})
|
|
|
|
await wrapper.vm.$nextTick()
|
|
|
|
const subgraphButton = wrapper.find('[data-testid="subgraph-enter-button"]')
|
|
expect(subgraphButton.exists()).toBe(false)
|
|
})
|
|
|
|
it('should prevent event propagation on double click', async () => {
|
|
await setupMocks(true) // isSubgraph = true
|
|
|
|
const wrapper = createWrapper({
|
|
nodeData: createMockNodeData('test-node-1'),
|
|
readonly: false
|
|
})
|
|
|
|
await wrapper.vm.$nextTick()
|
|
|
|
const subgraphButton = wrapper.find('[data-testid="subgraph-enter-button"]')
|
|
|
|
// Mock event object
|
|
const mockEvent = {
|
|
stopPropagation: vi.fn()
|
|
}
|
|
|
|
// Trigger dblclick event
|
|
await subgraphButton.trigger('dblclick', mockEvent)
|
|
|
|
// Should prevent propagation (handled by @dblclick.stop directive)
|
|
// This is tested by ensuring the component doesn't error and renders correctly
|
|
expect(subgraphButton.exists()).toBe(true)
|
|
})
|
|
})
|