@@ -788,4 +788,171 @@ test.describe('Vue Node Link Interaction', () => {
788788 targetSlot : 2
789789 } )
790790 } )
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+ } )
791958} )
0 commit comments