@@ -4,6 +4,7 @@ import { waitFor } from '@testing-library/vue';
4
4
import { userEvent } from '@testing-library/user-event' ;
5
5
import { createRouter , createWebHistory } from 'vue-router' ;
6
6
import { computed , ref } from 'vue' ;
7
+ import type { INodeTypeDescription } from 'n8n-workflow' ;
7
8
import { NodeConnectionType } from 'n8n-workflow' ;
8
9
9
10
import CanvasChat from './CanvasChat.vue' ;
@@ -15,14 +16,14 @@ import { ChatOptionsSymbol, ChatSymbol } from '@n8n/chat/constants';
15
16
import { chatEventBus } from '@n8n/chat/event-buses' ;
16
17
17
18
import { useWorkflowsStore } from '@/stores/workflows.store' ;
18
- import { useUIStore } from '@/stores/ui.store' ;
19
19
import { useCanvasStore } from '@/stores/canvas.store' ;
20
20
import * as useChatMessaging from './composables/useChatMessaging' ;
21
21
import * as useChatTrigger from './composables/useChatTrigger' ;
22
22
import { useToast } from '@/composables/useToast' ;
23
23
24
24
import type { IExecutionResponse , INodeUi } from '@/Interface' ;
25
25
import type { ChatMessage } from '@n8n/chat/types' ;
26
+ import { useNodeTypesStore } from '@/stores/nodeTypes.store' ;
26
27
27
28
vi . mock ( '@/composables/useToast' , ( ) => {
28
29
const showMessage = vi . fn ( ) ;
@@ -61,6 +62,26 @@ const mockNodes: INodeUi[] = [
61
62
position : [ 960 , 860 ] ,
62
63
} ,
63
64
] ;
65
+ const mockNodeTypes : INodeTypeDescription [ ] = [
66
+ {
67
+ displayName : 'AI Agent' ,
68
+ name : '@n8n/n8n-nodes-langchain.agent' ,
69
+ properties : [ ] ,
70
+ defaults : {
71
+ name : 'AI Agent' ,
72
+ } ,
73
+ inputs : [ NodeConnectionType . Main ] ,
74
+ outputs : [ NodeConnectionType . Main ] ,
75
+ version : 0 ,
76
+ group : [ ] ,
77
+ description : '' ,
78
+ codex : {
79
+ subcategories : {
80
+ AI : [ 'Agents' ] ,
81
+ } ,
82
+ } ,
83
+ } ,
84
+ ] ;
64
85
65
86
const mockConnections = {
66
87
'When chat message received' : {
@@ -110,8 +131,8 @@ describe('CanvasChat', () => {
110
131
} ) ;
111
132
112
133
let workflowsStore : ReturnType < typeof mockedStore < typeof useWorkflowsStore > > ;
113
- let uiStore : ReturnType < typeof mockedStore < typeof useUIStore > > ;
114
134
let canvasStore : ReturnType < typeof mockedStore < typeof useCanvasStore > > ;
135
+ let nodeTypeStore : ReturnType < typeof mockedStore < typeof useNodeTypesStore > > ;
115
136
116
137
beforeEach ( ( ) => {
117
138
const pinia = createTestingPinia ( {
@@ -131,8 +152,8 @@ describe('CanvasChat', () => {
131
152
setActivePinia ( pinia ) ;
132
153
133
154
workflowsStore = mockedStore ( useWorkflowsStore ) ;
134
- uiStore = mockedStore ( useUIStore ) ;
135
155
canvasStore = mockedStore ( useCanvasStore ) ;
156
+ nodeTypeStore = mockedStore ( useNodeTypesStore ) ;
136
157
137
158
// Setup default mocks
138
159
workflowsStore . getCurrentWorkflow . mockReturnValue (
@@ -141,12 +162,21 @@ describe('CanvasChat', () => {
141
162
connections : mockConnections ,
142
163
} ) ,
143
164
) ;
144
- workflowsStore . getNodeByName . mockImplementation (
145
- ( name ) => mockNodes . find ( ( node ) => node . name === name ) ?? null ,
146
- ) ;
165
+ workflowsStore . getNodeByName . mockImplementation ( ( name ) => {
166
+ const matchedNode = mockNodes . find ( ( node ) => node . name === name ) ?? null ;
167
+
168
+ return matchedNode ;
169
+ } ) ;
147
170
workflowsStore . isChatPanelOpen = true ;
171
+ workflowsStore . isLogsPanelOpen = true ;
148
172
workflowsStore . getWorkflowExecution = mockWorkflowExecution as unknown as IExecutionResponse ;
149
173
workflowsStore . getPastChatMessages = [ 'Previous message 1' , 'Previous message 2' ] ;
174
+
175
+ nodeTypeStore . getNodeType = vi . fn ( ) . mockImplementation ( ( nodeTypeName ) => {
176
+ return mockNodeTypes . find ( ( node ) => node . name === nodeTypeName ) ?? null ;
177
+ } ) ;
178
+
179
+ workflowsStore . runWorkflow . mockResolvedValue ( { executionId : 'test-execution-issd' } ) ;
150
180
} ) ;
151
181
152
182
afterEach ( ( ) => {
@@ -190,6 +220,10 @@ describe('CanvasChat', () => {
190
220
// Verify message and response
191
221
expect ( await findByText ( 'Hello AI!' ) ) . toBeInTheDocument ( ) ;
192
222
await waitFor ( async ( ) => {
223
+ workflowsStore . getWorkflowExecution = {
224
+ ...( mockWorkflowExecution as unknown as IExecutionResponse ) ,
225
+ status : 'success' ,
226
+ } ;
193
227
expect ( await findByText ( 'AI response message' ) ) . toBeInTheDocument ( ) ;
194
228
} ) ;
195
229
@@ -231,11 +265,12 @@ describe('CanvasChat', () => {
231
265
await userEvent . type ( input , 'Test message' ) ;
232
266
await userEvent . keyboard ( '{Enter}' ) ;
233
267
234
- // Verify loading states
235
- uiStore . isActionActive = { workflowRunning : true } ;
236
268
await waitFor ( ( ) => expect ( queryByTestId ( 'chat-message-typing' ) ) . toBeInTheDocument ( ) ) ;
237
269
238
- uiStore . isActionActive = { workflowRunning : false } ;
270
+ workflowsStore . getWorkflowExecution = {
271
+ ...( mockWorkflowExecution as unknown as IExecutionResponse ) ,
272
+ status : 'success' ,
273
+ } ;
239
274
await waitFor ( ( ) => expect ( queryByTestId ( 'chat-message-typing' ) ) . not . toBeInTheDocument ( ) ) ;
240
275
} ) ;
241
276
@@ -269,7 +304,7 @@ describe('CanvasChat', () => {
269
304
sendMessage : vi . fn ( ) ,
270
305
extractResponseMessage : vi . fn ( ) ,
271
306
previousMessageIndex : ref ( 0 ) ,
272
- waitForExecution : vi . fn ( ) ,
307
+ isLoading : computed ( ( ) => false ) ,
273
308
} ) ;
274
309
} ) ;
275
310
@@ -339,7 +374,7 @@ describe('CanvasChat', () => {
339
374
sendMessage : vi . fn ( ) ,
340
375
extractResponseMessage : vi . fn ( ) ,
341
376
previousMessageIndex : ref ( 0 ) ,
342
- waitForExecution : vi . fn ( ) ,
377
+ isLoading : computed ( ( ) => false ) ,
343
378
} ) ;
344
379
345
380
workflowsStore . isChatPanelOpen = true ;
@@ -437,7 +472,7 @@ describe('CanvasChat', () => {
437
472
sendMessage : sendMessageSpy ,
438
473
extractResponseMessage : vi . fn ( ) ,
439
474
previousMessageIndex : ref ( 0 ) ,
440
- waitForExecution : vi . fn ( ) ,
475
+ isLoading : computed ( ( ) => false ) ,
441
476
} ) ;
442
477
workflowsStore . messages = mockMessages ;
443
478
} ) ;
@@ -449,26 +484,25 @@ describe('CanvasChat', () => {
449
484
await userEvent . click ( repostButton ) ;
450
485
451
486
expect ( sendMessageSpy ) . toHaveBeenCalledWith ( 'Original message' ) ;
452
- // expect.objectContaining({
453
- // runData: expect.objectContaining({
454
- // 'When chat message received': expect.arrayContaining([
455
- // expect.objectContaining({
456
- // data: expect.objectContaining({
457
- // main: expect.arrayContaining([
458
- // expect.arrayContaining([
459
- // expect.objectContaining({
460
- // json: expect.objectContaining({
461
- // chatInput: 'Original message',
462
- // }),
463
- // }),
464
- // ]),
465
- // ]),
466
- // }),
467
- // }),
468
- // ]),
469
- // }),
470
- // }),
471
- // );
487
+ expect . objectContaining ( {
488
+ runData : expect . objectContaining ( {
489
+ 'When chat message received' : expect . arrayContaining ( [
490
+ expect . objectContaining ( {
491
+ data : expect . objectContaining ( {
492
+ main : expect . arrayContaining ( [
493
+ expect . arrayContaining ( [
494
+ expect . objectContaining ( {
495
+ json : expect . objectContaining ( {
496
+ chatInput : 'Original message' ,
497
+ } ) ,
498
+ } ) ,
499
+ ] ) ,
500
+ ] ) ,
501
+ } ) ,
502
+ } ) ,
503
+ ] ) ,
504
+ } ) ,
505
+ } ) ;
472
506
} ) ;
473
507
474
508
it ( 'should show message options only for appropriate messages' , async ( ) => {
@@ -494,32 +528,6 @@ describe('CanvasChat', () => {
494
528
} ) ;
495
529
} ) ;
496
530
497
- describe ( 'execution handling' , ( ) => {
498
- it ( 'should update UI when execution is completed' , async ( ) => {
499
- const { findByTestId, queryByTestId } = renderComponent ( ) ;
500
-
501
- // Start execution
502
- const input = await findByTestId ( 'chat-input' ) ;
503
- await userEvent . type ( input , 'Test message' ) ;
504
- await userEvent . keyboard ( '{Enter}' ) ;
505
-
506
- // Simulate execution completion
507
- uiStore . isActionActive = { workflowRunning : true } ;
508
- await waitFor ( ( ) => {
509
- expect ( queryByTestId ( 'chat-message-typing' ) ) . toBeInTheDocument ( ) ;
510
- } ) ;
511
-
512
- uiStore . isActionActive = { workflowRunning : false } ;
513
- workflowsStore . setWorkflowExecutionData (
514
- mockWorkflowExecution as unknown as IExecutionResponse ,
515
- ) ;
516
-
517
- await waitFor ( ( ) => {
518
- expect ( queryByTestId ( 'chat-message-typing' ) ) . not . toBeInTheDocument ( ) ;
519
- } ) ;
520
- } ) ;
521
- } ) ;
522
-
523
531
describe ( 'panel state synchronization' , ( ) => {
524
532
it ( 'should update canvas height when chat or logs panel state changes' , async ( ) => {
525
533
renderComponent ( ) ;
0 commit comments