@@ -788,4 +788,171 @@ test.describe('Vue Node Link Interaction', () => {
788
788
targetSlot : 2
789
789
} )
790
790
} )
791
+
792
+ test . describe ( 'Release actions (Shift-drop)' , ( ) => {
793
+ test ( 'Context menu opens and endpoint is pinned on Shift-drop' , async ( {
794
+ comfyPage,
795
+ comfyMouse
796
+ } ) => {
797
+ await comfyPage . setSetting (
798
+ 'Comfy.LinkRelease.ActionShift' ,
799
+ 'context menu'
800
+ )
801
+
802
+ const samplerNode = ( await comfyPage . getNodeRefsByType ( 'KSampler' ) ) [ 0 ]
803
+ expect ( samplerNode ) . toBeTruthy ( )
804
+
805
+ const outputCenter = await getSlotCenter (
806
+ comfyPage . page ,
807
+ samplerNode . id ,
808
+ 0 ,
809
+ false
810
+ )
811
+
812
+ const dropPos = { x : outputCenter . x + 180 , y : outputCenter . y - 140 }
813
+
814
+ await comfyMouse . move ( outputCenter )
815
+ await comfyPage . page . keyboard . down ( 'Shift' )
816
+ try {
817
+ await comfyMouse . drag ( dropPos )
818
+ await comfyMouse . drop ( )
819
+ } finally {
820
+ await comfyPage . page . keyboard . up ( 'Shift' ) . catch ( ( ) => { } )
821
+ }
822
+
823
+ // Context menu should be visible
824
+ const contextMenu = comfyPage . page . locator ( '.litecontextmenu' )
825
+ await expect ( contextMenu ) . toBeVisible ( )
826
+
827
+ // Pinned endpoint should not change with mouse movement while menu is open
828
+ const before = await comfyPage . page . evaluate ( ( ) => {
829
+ const snap = window [ 'app' ] ?. canvas ?. linkConnector ?. state ?. snapLinksPos
830
+ return Array . isArray ( snap ) ? [ snap [ 0 ] , snap [ 1 ] ] : null
831
+ } )
832
+ expect ( before ) . not . toBeNull ( )
833
+
834
+ // Move mouse elsewhere and verify snap position is unchanged
835
+ await comfyMouse . move ( { x : dropPos . x + 160 , y : dropPos . y + 100 } )
836
+ const after = await comfyPage . page . evaluate ( ( ) => {
837
+ const snap = window [ 'app' ] ?. canvas ?. linkConnector ?. state ?. snapLinksPos
838
+ return Array . isArray ( snap ) ? [ snap [ 0 ] , snap [ 1 ] ] : null
839
+ } )
840
+ expect ( after ) . toEqual ( before )
841
+ } )
842
+
843
+ test ( 'Context menu -> Search pre-filters by link type and connects after selection' , async ( {
844
+ comfyPage,
845
+ comfyMouse
846
+ } ) => {
847
+ await comfyPage . setSetting (
848
+ 'Comfy.LinkRelease.ActionShift' ,
849
+ 'context menu'
850
+ )
851
+ await comfyPage . setSetting ( 'Comfy.NodeSearchBoxImpl' , 'default' )
852
+
853
+ const samplerNode = ( await comfyPage . getNodeRefsByType ( 'KSampler' ) ) [ 0 ]
854
+ expect ( samplerNode ) . toBeTruthy ( )
855
+
856
+ const outputCenter = await getSlotCenter (
857
+ comfyPage . page ,
858
+ samplerNode . id ,
859
+ 0 ,
860
+ false
861
+ )
862
+ const dropPos = { x : outputCenter . x + 200 , y : outputCenter . y - 120 }
863
+
864
+ await comfyMouse . move ( outputCenter )
865
+ await comfyPage . page . keyboard . down ( 'Shift' )
866
+ try {
867
+ await comfyMouse . drag ( dropPos )
868
+ await comfyMouse . drop ( )
869
+ } finally {
870
+ await comfyPage . page . keyboard . up ( 'Shift' ) . catch ( ( ) => { } )
871
+ }
872
+
873
+ // Open Search from the context menu
874
+ await comfyPage . clickContextMenuItem ( 'Search' )
875
+
876
+ // Search box opens with prefilled type filter based on link type (LATENT)
877
+ await expect ( comfyPage . searchBox . input ) . toBeVisible ( )
878
+ const chips = comfyPage . searchBox . filterChips
879
+ // Ensure at least one filter chip exists and it matches the link type
880
+ const chipCount = await chips . count ( )
881
+ expect ( chipCount ) . toBeGreaterThan ( 0 )
882
+ await expect ( chips . first ( ) ) . toContainText ( 'LATENT' )
883
+
884
+ // Choose a compatible node and verify it auto-connects
885
+ await comfyPage . searchBox . fillAndSelectFirstNode ( 'VAEDecode' )
886
+ await comfyPage . nextFrame ( )
887
+
888
+ // KSampler output should now have an outgoing link
889
+ const samplerOutput = await samplerNode . getOutput ( 0 )
890
+ expect ( await samplerOutput . getLinkCount ( ) ) . toBe ( 1 )
891
+
892
+ // One of the VAEDecode nodes should have an incoming link on input[0]
893
+ const vaeNodes = await comfyPage . getNodeRefsByType ( 'VAEDecode' )
894
+ let linked = false
895
+ for ( const vae of vaeNodes ) {
896
+ const details = await getInputLinkDetails ( comfyPage . page , vae . id , 0 )
897
+ if ( details ) {
898
+ expect ( details . originId ) . toBe ( samplerNode . id )
899
+ linked = true
900
+ break
901
+ }
902
+ }
903
+ expect ( linked ) . toBe ( true )
904
+ } )
905
+
906
+ test ( 'Search box opens on Shift-drop and connects after selection' , async ( {
907
+ comfyPage,
908
+ comfyMouse
909
+ } ) => {
910
+ await comfyPage . setSetting ( 'Comfy.LinkRelease.ActionShift' , 'search box' )
911
+
912
+ const samplerNode = ( await comfyPage . getNodeRefsByType ( 'KSampler' ) ) [ 0 ]
913
+ expect ( samplerNode ) . toBeTruthy ( )
914
+
915
+ const outputCenter = await getSlotCenter (
916
+ comfyPage . page ,
917
+ samplerNode . id ,
918
+ 0 ,
919
+ false
920
+ )
921
+ const dropPos = { x : outputCenter . x + 140 , y : outputCenter . y - 100 }
922
+
923
+ await comfyMouse . move ( outputCenter )
924
+ await comfyPage . page . keyboard . down ( 'Shift' )
925
+ try {
926
+ await comfyMouse . drag ( dropPos )
927
+ await comfyMouse . drop ( )
928
+ } finally {
929
+ await comfyPage . page . keyboard . up ( 'Shift' ) . catch ( ( ) => { } )
930
+ }
931
+
932
+ // Search box should open directly
933
+ await expect ( comfyPage . searchBox . input ) . toBeVisible ( )
934
+ await expect ( comfyPage . searchBox . filterChips . first ( ) ) . toContainText (
935
+ 'LATENT'
936
+ )
937
+
938
+ // Select a compatible node and verify connection
939
+ await comfyPage . searchBox . fillAndSelectFirstNode ( 'VAEDecode' )
940
+ await comfyPage . nextFrame ( )
941
+
942
+ const samplerOutput = await samplerNode . getOutput ( 0 )
943
+ expect ( await samplerOutput . getLinkCount ( ) ) . toBe ( 1 )
944
+
945
+ const vaeNodes = await comfyPage . getNodeRefsByType ( 'VAEDecode' )
946
+ let linked = false
947
+ for ( const vae of vaeNodes ) {
948
+ const details = await getInputLinkDetails ( comfyPage . page , vae . id , 0 )
949
+ if ( details ) {
950
+ expect ( details . originId ) . toBe ( samplerNode . id )
951
+ linked = true
952
+ break
953
+ }
954
+ }
955
+ expect ( linked ) . toBe ( true )
956
+ } )
957
+ } )
791
958
} )
0 commit comments