11import { createPinia , setActivePinia } from 'pinia'
22import { beforeEach , describe , expect , it , vi } from 'vitest'
3+ import { nextTick } from 'vue'
34
45import { api } from '@/scripts/api'
6+ import { app as comfyApp } from '@/scripts/app'
57import { defaultGraph , defaultGraphJSON } from '@/scripts/defaultGraph'
68import {
79 ComfyWorkflow ,
@@ -15,7 +17,16 @@ vi.mock('@/scripts/api', () => ({
1517 api : {
1618 getUserData : vi . fn ( ) ,
1719 storeUserData : vi . fn ( ) ,
18- listUserDataFullInfo : vi . fn ( )
20+ listUserDataFullInfo : vi . fn ( ) ,
21+ apiURL : vi . fn ( ) ,
22+ addEventListener : vi . fn ( )
23+ }
24+ } ) )
25+
26+ // Mock comfyApp globally for the store setup
27+ vi . mock ( '@/scripts/app' , ( ) => ( {
28+ app : {
29+ canvas : null // Start with canvas potentially undefined or null
1930 }
2031} ) )
2132
@@ -448,4 +459,117 @@ describe('useWorkflowStore', () => {
448459 expect ( newWorkflow . isModified ) . toBe ( false )
449460 } )
450461 } )
462+
463+ describe ( 'Subgraphs' , ( ) => {
464+ beforeEach ( async ( ) => {
465+ // Ensure canvas exists for these tests
466+ vi . mocked ( comfyApp ) . canvas = { subgraph : null } as any
467+
468+ // Setup an active workflow as updateActiveGraph depends on it
469+ const workflow = store . createTemporary ( 'test-subgraph-workflow.json' )
470+ // Mock load to avoid actual file operations/parsing
471+ vi . spyOn ( workflow , 'load' ) . mockImplementation ( async ( ) => {
472+ workflow . changeTracker = { activeState : { } } as any // Minimal mock
473+ workflow . originalContent = '{}'
474+ workflow . content = '{}'
475+ return workflow as LoadedComfyWorkflow
476+ } )
477+ await store . openWorkflow ( workflow )
478+
479+ // Reset mocks before each subgraph test
480+ vi . mocked ( comfyApp . canvas ) . subgraph = undefined // Use undefined for root graph
481+ } )
482+
483+ it ( 'should handle when comfyApp.canvas is not available' , async ( ) => {
484+ // Arrange
485+ vi . mocked ( comfyApp ) . canvas = null as any // Simulate canvas not ready
486+
487+ // Act
488+ console . debug ( store . isSubgraphActive )
489+ store . updateActiveGraph ( )
490+ await nextTick ( )
491+
492+ // Assert
493+ console . debug ( store . isSubgraphActive )
494+ expect ( store . isSubgraphActive ) . toBe ( false ) // Should default to false
495+ expect ( store . subgraphNamePath ) . toEqual ( [ ] ) // Should default to empty
496+ } )
497+
498+ it ( 'should correctly update state when the root graph is active' , async ( ) => {
499+ // Arrange: Ensure comfyApp indicates root graph is active
500+ vi . mocked ( comfyApp . canvas ) . subgraph = undefined // Use undefined for root graph
501+
502+ // Act: Trigger the update
503+ store . updateActiveGraph ( )
504+ await nextTick ( ) // Wait for Vue reactivity
505+
506+ // Assert: Check store state
507+ expect ( store . isSubgraphActive ) . toBe ( false )
508+ expect ( store . subgraphNamePath ) . toEqual ( [ ] ) // Path is empty for root graph
509+ } )
510+
511+ it ( 'should correctly update state when a subgraph is active' , async ( ) => {
512+ // Arrange: Setup mock subgraph structure
513+ const mockSubgraph = {
514+ name : 'Level 2 Subgraph' ,
515+ isRootGraph : false ,
516+ pathToRootGraph : [
517+ { name : 'Root' } , // Root Graph (index 0, ignored)
518+ { name : 'Level 1 Subgraph' } ,
519+ { name : 'Level 2 Subgraph' }
520+ ]
521+ }
522+ vi . mocked ( comfyApp . canvas ) . subgraph = mockSubgraph as any
523+
524+ // Act: Trigger the update
525+ store . updateActiveGraph ( )
526+ await nextTick ( ) // Wait for Vue reactivity
527+
528+ // Assert: Check store state
529+ expect ( store . isSubgraphActive ) . toBe ( true )
530+ expect ( store . subgraphNamePath ) . toEqual ( [
531+ 'Level 1 Subgraph' ,
532+ 'Level 2 Subgraph'
533+ ] ) // Path excludes the root
534+ } )
535+
536+ it ( 'should update automatically when activeWorkflow changes' , async ( ) => {
537+ // Arrange: Set initial canvas state (e.g., a subgraph)
538+ const initialSubgraph = {
539+ name : 'Initial Subgraph' ,
540+ pathToRootGraph : [ { name : 'Root' } , { name : 'Initial Subgraph' } ] ,
541+ isRootGraph : false
542+ }
543+ vi . mocked ( comfyApp . canvas ) . subgraph = initialSubgraph as any
544+
545+ // Trigger initial update based on the *first* workflow opened in beforeEach
546+ store . updateActiveGraph ( )
547+ await nextTick ( )
548+
549+ // Verify initial state
550+ expect ( store . isSubgraphActive ) . toBe ( true )
551+ expect ( store . subgraphNamePath ) . toEqual ( [ 'Initial Subgraph' ] )
552+
553+ // Act: Change the active workflow
554+ const workflow2 = store . createTemporary ( 'workflow2.json' )
555+ // Mock load for the second workflow
556+ vi . spyOn ( workflow2 , 'load' ) . mockImplementation ( async ( ) => {
557+ workflow2 . changeTracker = { activeState : { } } as any
558+ workflow2 . originalContent = '{}'
559+ workflow2 . content = '{}'
560+ return workflow2 as LoadedComfyWorkflow
561+ } )
562+
563+ // Before changing workflow, set the canvas state to something different (e.g., root)
564+ // This ensures the watcher *does* cause a state change we can assert
565+ vi . mocked ( comfyApp . canvas ) . subgraph = undefined
566+
567+ await store . openWorkflow ( workflow2 ) // This changes activeWorkflow and triggers the watch
568+ await nextTick ( ) // Allow watcher and potential async operations in updateActiveGraph to complete
569+
570+ // Assert: Check that the state was updated by the watcher based on the *new* canvas state
571+ expect ( store . isSubgraphActive ) . toBe ( false ) // Should reflect the change to undefined subgraph
572+ expect ( store . subgraphNamePath ) . toEqual ( [ ] ) // Path should be empty for root
573+ } )
574+ } )
451575} )
0 commit comments