mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-30 12:59:55 +00:00
Prior to the release of subgraphs, there was a single graph accessed through `app.graph`. Now that there's multiple graphs, there's a lot of code that needs to be reviewed and potentially updated depending on if it cares about nearby nodes, all nodes, or something else requiring specific attention. This was done by simply changing the type of `app.graph` to unknown so the typechecker will complain about every place it's currently used. References were then updated to `app.rootGraph` if the previous usage was correct, or actually rewritten. By not getting rid of `app.graph`, this change already ensures that there's no loss of functionality for custom nodes, but the prior typing of `app.graph` can always be restored if future dissuasion of `app.graph` usage creates issues. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7399-Cleanup-app-graph-usage-2c76d73d365081178743dfdcf07f44d0) by [Unito](https://www.unito.io)
220 lines
5.8 KiB
TypeScript
220 lines
5.8 KiB
TypeScript
import { createPinia, setActivePinia } from 'pinia'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { ref } from 'vue'
|
|
|
|
import { useCoreCommands } from '@/composables/useCoreCommands'
|
|
import { useSettingStore } from '@/platform/settings/settingStore'
|
|
import { api } from '@/scripts/api'
|
|
import { app } from '@/scripts/app'
|
|
|
|
// Mock vue-i18n for useExternalLink
|
|
const mockLocale = ref('en')
|
|
vi.mock('vue-i18n', async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import('vue-i18n')>()
|
|
return {
|
|
...actual,
|
|
useI18n: vi.fn(() => ({
|
|
locale: mockLocale
|
|
}))
|
|
}
|
|
})
|
|
|
|
vi.mock('@/scripts/app', () => {
|
|
const mockGraphClear = vi.fn()
|
|
const mockCanvas = { subgraph: undefined }
|
|
|
|
return {
|
|
app: {
|
|
clean: vi.fn(() => {
|
|
// Simulate app.clean() calling graph.clear() only when not in subgraph
|
|
if (!mockCanvas.subgraph) {
|
|
mockGraphClear()
|
|
}
|
|
}),
|
|
canvas: mockCanvas,
|
|
rootGraph: {
|
|
clear: mockGraphClear
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
vi.mock('@/scripts/api', () => ({
|
|
api: {
|
|
dispatchCustomEvent: vi.fn(),
|
|
apiURL: vi.fn(() => 'http://localhost:8188')
|
|
}
|
|
}))
|
|
|
|
vi.mock('@/platform/settings/settingStore')
|
|
|
|
vi.mock('@/stores/firebaseAuthStore', () => ({
|
|
useFirebaseAuthStore: vi.fn(() => ({}))
|
|
}))
|
|
|
|
vi.mock('@/composables/auth/useFirebaseAuth', () => ({
|
|
useFirebaseAuth: vi.fn(() => null)
|
|
}))
|
|
|
|
vi.mock('firebase/auth', () => ({
|
|
setPersistence: vi.fn(),
|
|
browserLocalPersistence: {},
|
|
onAuthStateChanged: vi.fn()
|
|
}))
|
|
|
|
vi.mock('@/platform/workflow/core/services/workflowService', () => ({
|
|
useWorkflowService: vi.fn(() => ({}))
|
|
}))
|
|
|
|
vi.mock('@/services/dialogService', () => ({
|
|
useDialogService: vi.fn(() => ({}))
|
|
}))
|
|
|
|
vi.mock('@/services/litegraphService', () => ({
|
|
useLitegraphService: vi.fn(() => ({}))
|
|
}))
|
|
|
|
vi.mock('@/stores/executionStore', () => ({
|
|
useExecutionStore: vi.fn(() => ({}))
|
|
}))
|
|
|
|
vi.mock('@/stores/toastStore', () => ({
|
|
useToastStore: vi.fn(() => ({}))
|
|
}))
|
|
|
|
vi.mock('@/platform/workflow/management/stores/workflowStore', () => ({
|
|
useWorkflowStore: vi.fn(() => ({}))
|
|
}))
|
|
|
|
vi.mock('@/stores/subgraphStore', () => ({
|
|
useSubgraphStore: vi.fn(() => ({}))
|
|
}))
|
|
|
|
vi.mock('@/stores/workspace/colorPaletteStore', () => ({
|
|
useColorPaletteStore: vi.fn(() => ({}))
|
|
}))
|
|
|
|
vi.mock('@/composables/auth/useFirebaseAuthActions', () => ({
|
|
useFirebaseAuthActions: vi.fn(() => ({}))
|
|
}))
|
|
|
|
vi.mock('@/platform/cloud/subscription/composables/useSubscription', () => ({
|
|
useSubscription: vi.fn(() => ({
|
|
isActiveSubscription: vi.fn().mockReturnValue(true),
|
|
showSubscriptionDialog: vi.fn()
|
|
}))
|
|
}))
|
|
|
|
describe('useCoreCommands', () => {
|
|
const mockSubgraph = {
|
|
nodes: [
|
|
// Mock input node
|
|
{
|
|
constructor: { comfyClass: 'SubgraphInputNode' },
|
|
id: 'input1'
|
|
},
|
|
// Mock output node
|
|
{
|
|
constructor: { comfyClass: 'SubgraphOutputNode' },
|
|
id: 'output1'
|
|
},
|
|
// Mock user node
|
|
{
|
|
constructor: { comfyClass: 'SomeUserNode' },
|
|
id: 'user1'
|
|
},
|
|
// Another mock user node
|
|
{
|
|
constructor: { comfyClass: 'AnotherUserNode' },
|
|
id: 'user2'
|
|
}
|
|
],
|
|
remove: vi.fn()
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
|
|
// Set up Pinia
|
|
setActivePinia(createPinia())
|
|
|
|
// Reset app state
|
|
app.canvas.subgraph = undefined
|
|
|
|
// Mock settings store
|
|
vi.mocked(useSettingStore).mockReturnValue({
|
|
get: vi.fn().mockReturnValue(false) // Skip confirmation dialog
|
|
} as any)
|
|
|
|
// Mock global confirm
|
|
global.confirm = vi.fn().mockReturnValue(true)
|
|
})
|
|
|
|
describe('ClearWorkflow command', () => {
|
|
it('should clear main graph when not in subgraph', async () => {
|
|
const commands = useCoreCommands()
|
|
const clearCommand = commands.find(
|
|
(cmd) => cmd.id === 'Comfy.ClearWorkflow'
|
|
)!
|
|
|
|
// Execute the command
|
|
await clearCommand.function()
|
|
|
|
expect(app.clean).toHaveBeenCalled()
|
|
expect(app.rootGraph.clear).toHaveBeenCalled()
|
|
expect(api.dispatchCustomEvent).toHaveBeenCalledWith('graphCleared')
|
|
})
|
|
|
|
it('should preserve input/output nodes when clearing subgraph', async () => {
|
|
// Set up subgraph context
|
|
app.canvas.subgraph = mockSubgraph as any
|
|
|
|
const commands = useCoreCommands()
|
|
const clearCommand = commands.find(
|
|
(cmd) => cmd.id === 'Comfy.ClearWorkflow'
|
|
)!
|
|
|
|
// Execute the command
|
|
await clearCommand.function()
|
|
|
|
expect(app.clean).toHaveBeenCalled()
|
|
expect(app.rootGraph.clear).not.toHaveBeenCalled()
|
|
|
|
// Should only remove user nodes, not input/output nodes
|
|
expect(mockSubgraph.remove).toHaveBeenCalledTimes(2)
|
|
expect(mockSubgraph.remove).toHaveBeenCalledWith(mockSubgraph.nodes[2]) // user1
|
|
expect(mockSubgraph.remove).toHaveBeenCalledWith(mockSubgraph.nodes[3]) // user2
|
|
expect(mockSubgraph.remove).not.toHaveBeenCalledWith(
|
|
mockSubgraph.nodes[0]
|
|
) // input1
|
|
expect(mockSubgraph.remove).not.toHaveBeenCalledWith(
|
|
mockSubgraph.nodes[1]
|
|
) // output1
|
|
|
|
expect(api.dispatchCustomEvent).toHaveBeenCalledWith('graphCleared')
|
|
})
|
|
|
|
it('should respect confirmation setting', async () => {
|
|
// Mock confirmation required
|
|
vi.mocked(useSettingStore).mockReturnValue({
|
|
get: vi.fn().mockReturnValue(true) // Require confirmation
|
|
} as any)
|
|
|
|
global.confirm = vi.fn().mockReturnValue(false) // User cancels
|
|
|
|
const commands = useCoreCommands()
|
|
const clearCommand = commands.find(
|
|
(cmd) => cmd.id === 'Comfy.ClearWorkflow'
|
|
)!
|
|
|
|
// Execute the command
|
|
await clearCommand.function()
|
|
|
|
// Should not clear anything when user cancels
|
|
expect(app.clean).not.toHaveBeenCalled()
|
|
expect(app.rootGraph.clear).not.toHaveBeenCalled()
|
|
expect(api.dispatchCustomEvent).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
})
|