1- import type { ReactNode } from 'react'
2- import { fireEvent , render , screen } from '@testing-library/react'
1+ import { fireEvent , screen , waitFor } from '@testing-library/react'
32import PanelContextmenu from '../panel-contextmenu'
3+ import { BlockEnum } from '../types'
4+ import { createNode } from './fixtures'
5+ import { renderWorkflowFlowComponent } from './workflow-test-env'
46
5- const mockUseClickAway = vi . hoisted ( ( ) => vi . fn ( ) )
67const mockUseTranslation = vi . hoisted ( ( ) => vi . fn ( ) )
7- const mockUseStore = vi . hoisted ( ( ) => vi . fn ( ) )
88const mockUseNodesInteractions = vi . hoisted ( ( ) => vi . fn ( ) )
99const mockUsePanelInteractions = vi . hoisted ( ( ) => vi . fn ( ) )
1010const mockUseWorkflowStartRun = vi . hoisted ( ( ) => vi . fn ( ) )
1111const mockUseWorkflowMoveMode = vi . hoisted ( ( ) => vi . fn ( ) )
1212const mockUseOperator = vi . hoisted ( ( ) => vi . fn ( ) )
1313const mockUseDSL = vi . hoisted ( ( ) => vi . fn ( ) )
14-
15- vi . mock ( 'ahooks' , ( ) => ( {
16- useClickAway : ( ... args : unknown [ ] ) => mockUseClickAway ( ... args ) ,
17- } ) )
14+ const mockUseNodesReadOnly = vi . hoisted ( ( ) => vi . fn ( ) )
15+ const mockUseAvailableBlocks = vi . hoisted ( ( ) => vi . fn ( ) )
16+ const mockUseNodesMetaData = vi . hoisted ( ( ) => vi . fn ( ) )
17+ const mockUseIsChatMode = vi . hoisted ( ( ) => vi . fn ( ) )
1818
1919vi . mock ( 'react-i18next' , ( ) => ( {
2020 useTranslation : ( ) => mockUseTranslation ( ) ,
2121} ) )
2222
23- vi . mock ( '@/app/components/workflow/store' , ( ) => ( {
24- useStore : ( selector : ( state : {
25- panelMenu ?: { left : number , top : number }
26- clipboardElements : unknown [ ]
27- pendingComment : null | { pageX : number , pageY : number , elementX : number , elementY : number }
28- setCommentPlacing : ( placing : boolean ) => void
29- setCommentQuickAdd : ( quickAdd : boolean ) => void
30- setShowImportDSLModal : ( visible : boolean ) => void
31- } ) => unknown ) => mockUseStore ( selector ) ,
32- } ) )
33-
3423vi . mock ( '@/app/components/workflow/hooks' , ( ) => ( {
24+ useAvailableBlocks : ( ) => mockUseAvailableBlocks ( ) ,
25+ useDSL : ( ) => mockUseDSL ( ) ,
26+ useIsChatMode : ( ) => mockUseIsChatMode ( ) ,
3527 useNodesInteractions : ( ) => mockUseNodesInteractions ( ) ,
28+ useNodesMetaData : ( ) => mockUseNodesMetaData ( ) ,
29+ useNodesReadOnly : ( ) => mockUseNodesReadOnly ( ) ,
3630 usePanelInteractions : ( ) => mockUsePanelInteractions ( ) ,
37- useWorkflowStartRun : ( ) => mockUseWorkflowStartRun ( ) ,
3831 useWorkflowMoveMode : ( ) => mockUseWorkflowMoveMode ( ) ,
39- useDSL : ( ) => mockUseDSL ( ) ,
32+ useWorkflowStartRun : ( ) => mockUseWorkflowStartRun ( ) ,
4033} ) )
4134
4235vi . mock ( '@/app/components/workflow/operator/hooks' , ( ) => ( {
4336 useOperator : ( ) => mockUseOperator ( ) ,
4437} ) )
4538
46- vi . mock ( '@/app/components/workflow/operator/add-block' , ( ) => ( {
47- __esModule : true ,
48- default : ( { renderTrigger } : { renderTrigger : ( ) => ReactNode } ) => (
49- < div data-testid = "add-block" > { renderTrigger ( ) } </ div >
50- ) ,
51- } ) )
52-
53- vi . mock ( '@/app/components/base/divider' , ( ) => ( {
54- __esModule : true ,
55- default : ( { className } : { className ?: string } ) => < div data-testid = "divider" className = { className } /> ,
56- } ) )
57-
58- vi . mock ( '@/app/components/workflow/shortcuts-name' , ( ) => ( {
59- __esModule : true ,
60- default : ( { keys } : { keys : string [ ] } ) => < span data-testid = { `shortcut-${ keys . join ( '-' ) } ` } > { keys . join ( '+' ) } </ span > ,
61- } ) )
62-
6339describe ( 'PanelContextmenu' , ( ) => {
6440 const mockHandleNodesPaste = vi . fn ( )
6541 const mockHandlePaneContextmenuCancel = vi . fn ( )
6642 const mockHandleStartWorkflowRun = vi . fn ( )
43+ const mockHandleWorkflowStartRunInChatflow = vi . fn ( )
6744 const mockHandleAddNote = vi . fn ( )
6845 const mockExportCheck = vi . fn ( )
69- const mockSetShowImportDSLModal = vi . fn ( )
70- const mockSetCommentPlacing = vi . fn ( )
71- const mockSetCommentQuickAdd = vi . fn ( )
72- let panelMenu : { left : number , top : number } | undefined
73- let clipboardElements : unknown [ ]
74- let pendingComment : null | { pageX : number , pageY : number , elementX : number , elementY : number }
75- let clickAwayHandler : ( ( ) => void ) | undefined
46+ const defaultNodesMetaDataMap = {
47+ [ BlockEnum . Answer ] : {
48+ defaultValue : {
49+ title : 'Answer' ,
50+ desc : '' ,
51+ type : BlockEnum . Answer ,
52+ } ,
53+ } ,
54+ }
7655
7756 beforeEach ( ( ) => {
7857 vi . clearAllMocks ( )
79- panelMenu = undefined
80- clipboardElements = [ ]
81- pendingComment = null
82- clickAwayHandler = undefined
83-
84- mockUseClickAway . mockImplementation ( ( handler : ( ) => void ) => {
85- clickAwayHandler = handler
86- } )
8758 mockUseTranslation . mockReturnValue ( {
8859 t : ( key : string ) => key ,
8960 } )
90- mockUseStore . mockImplementation ( ( selector : ( state : {
91- panelMenu ?: { left : number , top : number }
92- clipboardElements : unknown [ ]
93- pendingComment : null | { pageX : number , pageY : number , elementX : number , elementY : number }
94- setCommentPlacing : ( placing : boolean ) => void
95- setCommentQuickAdd : ( quickAdd : boolean ) => void
96- setShowImportDSLModal : ( visible : boolean ) => void
97- } ) => unknown ) => selector ( {
98- panelMenu,
99- clipboardElements,
100- pendingComment,
101- setCommentPlacing : mockSetCommentPlacing ,
102- setCommentQuickAdd : mockSetCommentQuickAdd ,
103- setShowImportDSLModal : mockSetShowImportDSLModal ,
104- } ) )
10561 mockUseNodesInteractions . mockReturnValue ( {
10662 handleNodesPaste : mockHandleNodesPaste ,
10763 } )
@@ -110,6 +66,7 @@ describe('PanelContextmenu', () => {
11066 } )
11167 mockUseWorkflowStartRun . mockReturnValue ( {
11268 handleStartWorkflowRun : mockHandleStartWorkflowRun ,
69+ handleWorkflowStartRunInChatflow : mockHandleWorkflowStartRunInChatflow ,
11370 } )
11471 mockUseWorkflowMoveMode . mockReturnValue ( {
11572 isCommentModeAvailable : false ,
@@ -120,50 +77,86 @@ describe('PanelContextmenu', () => {
12077 mockUseDSL . mockReturnValue ( {
12178 exportCheck : mockExportCheck ,
12279 } )
80+ mockUseNodesReadOnly . mockReturnValue ( {
81+ nodesReadOnly : false ,
82+ } )
83+ mockUseAvailableBlocks . mockReturnValue ( {
84+ availableNextBlocks : [ BlockEnum . Answer ] ,
85+ } )
86+ mockUseNodesMetaData . mockReturnValue ( {
87+ nodesMap : defaultNodesMetaDataMap ,
88+ } )
89+ mockUseIsChatMode . mockReturnValue ( false )
12390 } )
12491
12592 it ( 'should stay hidden when the panel menu is absent' , ( ) => {
126- render ( < PanelContextmenu /> )
93+ renderWorkflowFlowComponent ( < PanelContextmenu /> )
12794
128- expect ( screen . queryByTestId ( 'add-block ') ) . not . toBeInTheDocument ( )
95+ expect ( screen . queryByText ( 'common.addBlock ') ) . not . toBeInTheDocument ( )
12996 } )
13097
131- it ( 'should keep paste disabled when the clipboard is empty' , ( ) => {
132- panelMenu = { left : 24 , top : 48 }
133-
134- render ( < PanelContextmenu /> )
98+ it ( 'should keep paste disabled when the clipboard is empty' , async ( ) => {
99+ renderWorkflowFlowComponent ( < PanelContextmenu /> , {
100+ initialStoreState : {
101+ panelMenu : { clientX : 24 , clientY : 48 } ,
102+ } ,
103+ hooksStoreProps : { } ,
104+ } )
135105
106+ await screen . findByText ( 'common.pasteHere' )
136107 fireEvent . click ( screen . getByText ( 'common.pasteHere' ) )
137108
138109 expect ( mockHandleNodesPaste ) . not . toHaveBeenCalled ( )
139110 expect ( mockHandlePaneContextmenuCancel ) . not . toHaveBeenCalled ( )
140111 } )
141112
142- it ( 'should render actions, position the menu, and execute each action' , ( ) => {
143- panelMenu = { left : 24 , top : 48 }
144- clipboardElements = [ { id : 'copied-node' } ]
145- const { container } = render ( < PanelContextmenu /> )
146-
147- expect ( screen . getByTestId ( 'add-block' ) ) . toHaveTextContent ( 'common.addBlock' )
148- expect ( screen . getByRole ( 'button' , { name : / c o m m o n \. r u n / i } ) ) . toHaveTextContent ( / A l t \s * R / )
149- expect ( screen . getByRole ( 'button' , { name : / c o m m o n \. p a s t e H e r e / i } ) ) . toHaveTextContent ( / C t r l \s * V / )
150- expect ( container . firstChild ) . toHaveStyle ( {
151- left : '24px' ,
152- top : '48px' ,
113+ it ( 'should render actions and execute enabled actions' , async ( ) => {
114+ const { store } = renderWorkflowFlowComponent ( < PanelContextmenu /> , {
115+ initialStoreState : {
116+ panelMenu : { clientX : 24 , clientY : 48 } ,
117+ clipboardElements : [ createNode ( { id : 'copied-node' } ) ] ,
118+ } ,
119+ hooksStoreProps : { } ,
153120 } )
154121
122+ expect ( await screen . findByText ( 'common.addBlock' ) ) . toBeInTheDocument ( )
123+ expect ( screen . getByText ( 'common.run' ) ) . toBeInTheDocument ( )
124+ expect ( screen . getByText ( 'common.pasteHere' ) ) . toBeInTheDocument ( )
125+
155126 fireEvent . click ( screen . getByText ( 'nodes.note.addNote' ) )
156127 fireEvent . click ( screen . getByText ( 'common.run' ) )
157128 fireEvent . click ( screen . getByText ( 'common.pasteHere' ) )
158129 fireEvent . click ( screen . getByText ( 'export' ) )
159130 fireEvent . click ( screen . getByText ( 'importApp' ) )
160- clickAwayHandler ?.( )
161-
162- expect ( mockHandleAddNote ) . toHaveBeenCalledTimes ( 1 )
163- expect ( mockHandleStartWorkflowRun ) . toHaveBeenCalledTimes ( 1 )
164- expect ( mockHandleNodesPaste ) . toHaveBeenCalledTimes ( 1 )
165- expect ( mockExportCheck ) . toHaveBeenCalledTimes ( 1 )
166- expect ( mockSetShowImportDSLModal ) . toHaveBeenCalledWith ( true )
167- expect ( mockHandlePaneContextmenuCancel ) . toHaveBeenCalledTimes ( 4 )
131+
132+ await waitFor ( ( ) => {
133+ expect ( mockHandleAddNote ) . toHaveBeenCalledTimes ( 1 )
134+ expect ( mockHandleStartWorkflowRun ) . toHaveBeenCalledTimes ( 1 )
135+ expect ( mockHandleNodesPaste ) . toHaveBeenCalledTimes ( 1 )
136+ expect ( mockExportCheck ) . toHaveBeenCalledTimes ( 1 )
137+ expect ( store . getState ( ) . showImportDSLModal ) . toBe ( true )
138+ } )
139+ } )
140+
141+ it ( 'should render preview action in chat mode' , async ( ) => {
142+ mockUseIsChatMode . mockReturnValue ( true )
143+
144+ renderWorkflowFlowComponent ( < PanelContextmenu /> , {
145+ initialStoreState : {
146+ panelMenu : { clientX : 24 , clientY : 48 } ,
147+ } ,
148+ hooksStoreProps : { } ,
149+ } )
150+
151+ expect ( await screen . findByText ( 'common.debugAndPreview' ) ) . toBeInTheDocument ( )
152+ expect ( screen . queryByText ( 'common.run' ) ) . not . toBeInTheDocument ( )
153+
154+ fireEvent . click ( screen . getByText ( 'common.debugAndPreview' ) )
155+
156+ await waitFor ( ( ) => {
157+ expect ( mockHandleWorkflowStartRunInChatflow ) . toHaveBeenCalledTimes ( 1 )
158+ expect ( mockHandleStartWorkflowRun ) . not . toHaveBeenCalled ( )
159+ expect ( mockHandlePaneContextmenuCancel ) . toHaveBeenCalled ( )
160+ } )
168161 } )
169162} )
0 commit comments