mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
fix(editor): Keep chat session when switching to other tabs (#19483)
This commit is contained in:
@@ -19,33 +19,33 @@ vi.mock('../logs.utils', () => {
|
|||||||
describe('useChatMessaging', () => {
|
describe('useChatMessaging', () => {
|
||||||
let chatMessaging: ReturnType<typeof useChatMessaging>;
|
let chatMessaging: ReturnType<typeof useChatMessaging>;
|
||||||
let chatTrigger: Ref<INodeUi | null>;
|
let chatTrigger: Ref<INodeUi | null>;
|
||||||
let messages: Ref<ChatMessage[]>;
|
let sessionId: string;
|
||||||
let sessionId: Ref<string>;
|
|
||||||
let executionResultData: ComputedRef<IRunExecutionData['resultData'] | undefined>;
|
let executionResultData: ComputedRef<IRunExecutionData['resultData'] | undefined>;
|
||||||
let onRunChatWorkflow: (
|
let onRunChatWorkflow: (
|
||||||
payload: RunWorkflowChatPayload,
|
payload: RunWorkflowChatPayload,
|
||||||
) => Promise<IExecutionPushResponse | undefined>;
|
) => Promise<IExecutionPushResponse | undefined>;
|
||||||
let ws: Ref<WebSocket | null>;
|
let ws: Ref<WebSocket | null>;
|
||||||
let executionData: IRunExecutionData['resultData'] | undefined = undefined;
|
let executionData: IRunExecutionData['resultData'] | undefined = undefined;
|
||||||
|
let onNewMessage: (message: ChatMessage) => void;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
executionData = undefined;
|
executionData = undefined;
|
||||||
createTestingPinia();
|
createTestingPinia();
|
||||||
chatTrigger = ref(null);
|
chatTrigger = ref(null);
|
||||||
messages = ref([]);
|
sessionId = 'session-id';
|
||||||
sessionId = ref('session-id');
|
|
||||||
executionResultData = computed(() => executionData);
|
executionResultData = computed(() => executionData);
|
||||||
onRunChatWorkflow = vi.fn().mockResolvedValue({
|
onRunChatWorkflow = vi.fn().mockResolvedValue({
|
||||||
executionId: 'execution-id',
|
executionId: 'execution-id',
|
||||||
} as IExecutionPushResponse);
|
} as IExecutionPushResponse);
|
||||||
|
onNewMessage = vi.fn();
|
||||||
ws = ref(null);
|
ws = ref(null);
|
||||||
|
|
||||||
chatMessaging = useChatMessaging({
|
chatMessaging = useChatMessaging({
|
||||||
chatTrigger,
|
chatTrigger,
|
||||||
messages,
|
|
||||||
sessionId,
|
sessionId,
|
||||||
executionResultData,
|
executionResultData,
|
||||||
onRunChatWorkflow,
|
onRunChatWorkflow,
|
||||||
|
onNewMessage,
|
||||||
ws,
|
ws,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -60,7 +60,13 @@ describe('useChatMessaging', () => {
|
|||||||
const messageText = 'Hello, world!';
|
const messageText = 'Hello, world!';
|
||||||
await chatMessaging.sendMessage(messageText);
|
await chatMessaging.sendMessage(messageText);
|
||||||
|
|
||||||
expect(messages.value).toHaveLength(1);
|
expect(onNewMessage).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onNewMessage).toHaveBeenCalledWith({
|
||||||
|
id: expect.any(String),
|
||||||
|
sender: 'user',
|
||||||
|
sessionId: 'session-id',
|
||||||
|
text: messageText,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should send message via WebSocket if open', async () => {
|
it('should send message via WebSocket if open', async () => {
|
||||||
@@ -74,7 +80,7 @@ describe('useChatMessaging', () => {
|
|||||||
|
|
||||||
expect(ws.value.send).toHaveBeenCalledWith(
|
expect(ws.value.send).toHaveBeenCalledWith(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
sessionId: sessionId.value,
|
sessionId,
|
||||||
action: 'sendMessage',
|
action: 'sendMessage',
|
||||||
chatInput: messageText,
|
chatInput: messageText,
|
||||||
}),
|
}),
|
||||||
@@ -99,7 +105,14 @@ describe('useChatMessaging', () => {
|
|||||||
} as unknown as IRunExecutionData['resultData'];
|
} as unknown as IRunExecutionData['resultData'];
|
||||||
|
|
||||||
await chatMessaging.sendMessage(messageText);
|
await chatMessaging.sendMessage(messageText);
|
||||||
expect(messages.value).toHaveLength(2);
|
expect(onNewMessage).toHaveBeenCalledTimes(2);
|
||||||
|
expect(onNewMessage).toHaveBeenCalledWith({
|
||||||
|
id: expect.any(String),
|
||||||
|
sender: 'user',
|
||||||
|
sessionId: 'session-id',
|
||||||
|
text: messageText,
|
||||||
|
});
|
||||||
|
expect(onNewMessage).toHaveBeenCalledWith('Last node response');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should startWorkflowWithMessage and not add final message if responseMode is responseNode and version is 1.3', async () => {
|
it('should startWorkflowWithMessage and not add final message if responseMode is responseNode and version is 1.3', async () => {
|
||||||
@@ -120,6 +133,12 @@ describe('useChatMessaging', () => {
|
|||||||
} as unknown as IRunExecutionData['resultData'];
|
} as unknown as IRunExecutionData['resultData'];
|
||||||
|
|
||||||
await chatMessaging.sendMessage(messageText);
|
await chatMessaging.sendMessage(messageText);
|
||||||
expect(messages.value).toHaveLength(1);
|
expect(onNewMessage).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onNewMessage).toHaveBeenCalledWith({
|
||||||
|
id: expect.any(String),
|
||||||
|
sender: 'user',
|
||||||
|
sessionId: 'session-id',
|
||||||
|
text: messageText,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -682,16 +682,18 @@ describe('LogsPanel', () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.spyOn(useChatMessaging, 'useChatMessaging').mockImplementation(({ messages }) => {
|
vi.spyOn(useChatMessaging, 'useChatMessaging').mockImplementation(
|
||||||
messages.value.push(...mockMessages);
|
({ onNewMessage: addChatMessage }) => {
|
||||||
|
addChatMessage(mockMessages[0]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sendMessage: vi.fn(),
|
sendMessage: vi.fn(),
|
||||||
previousMessageIndex: ref(0),
|
previousMessageIndex: ref(0),
|
||||||
isLoading: computed(() => false),
|
isLoading: computed(() => false),
|
||||||
setLoadingState: vi.fn(),
|
setLoadingState: vi.fn(),
|
||||||
};
|
};
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow copying session ID', async () => {
|
it('should allow copying session ID', async () => {
|
||||||
@@ -826,16 +828,19 @@ describe('LogsPanel', () => {
|
|||||||
sender: 'bot',
|
sender: 'bot',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
vi.spyOn(useChatMessaging, 'useChatMessaging').mockImplementation(({ messages }) => {
|
vi.spyOn(useChatMessaging, 'useChatMessaging').mockImplementation(
|
||||||
messages.value.push(...mockMessages);
|
({ onNewMessage: addChatMessage }) => {
|
||||||
|
addChatMessage(mockMessages[0]);
|
||||||
|
addChatMessage(mockMessages[1]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sendMessage: sendMessageSpy,
|
sendMessage: sendMessageSpy,
|
||||||
previousMessageIndex: ref(0),
|
previousMessageIndex: ref(0),
|
||||||
isLoading: computed(() => false),
|
isLoading: computed(() => false),
|
||||||
setLoadingState: vi.fn(),
|
setLoadingState: vi.fn(),
|
||||||
};
|
};
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should repost user message with new execution', async () => {
|
it('should repost user message with new execution', async () => {
|
||||||
|
|||||||
@@ -28,22 +28,22 @@ export type RunWorkflowChatPayload = {
|
|||||||
};
|
};
|
||||||
export interface ChatMessagingDependencies {
|
export interface ChatMessagingDependencies {
|
||||||
chatTrigger: Ref<INodeUi | null>;
|
chatTrigger: Ref<INodeUi | null>;
|
||||||
messages: Ref<ChatMessage[]>;
|
sessionId: string;
|
||||||
sessionId: Ref<string>;
|
|
||||||
executionResultData: ComputedRef<IRunExecutionData['resultData'] | undefined>;
|
executionResultData: ComputedRef<IRunExecutionData['resultData'] | undefined>;
|
||||||
onRunChatWorkflow: (
|
onRunChatWorkflow: (
|
||||||
payload: RunWorkflowChatPayload,
|
payload: RunWorkflowChatPayload,
|
||||||
) => Promise<IExecutionPushResponse | undefined>;
|
) => Promise<IExecutionPushResponse | undefined>;
|
||||||
ws: Ref<WebSocket | null>;
|
ws: Ref<WebSocket | null>;
|
||||||
|
onNewMessage: (message: ChatMessage) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useChatMessaging({
|
export function useChatMessaging({
|
||||||
chatTrigger,
|
chatTrigger,
|
||||||
messages,
|
|
||||||
sessionId,
|
sessionId,
|
||||||
executionResultData,
|
executionResultData,
|
||||||
onRunChatWorkflow,
|
onRunChatWorkflow,
|
||||||
ws,
|
ws,
|
||||||
|
onNewMessage,
|
||||||
}: ChatMessagingDependencies) {
|
}: ChatMessagingDependencies) {
|
||||||
const locale = useI18n();
|
const locale = useI18n();
|
||||||
const { showError } = useToast();
|
const { showError } = useToast();
|
||||||
@@ -116,7 +116,7 @@ export function useChatMessaging({
|
|||||||
|
|
||||||
const inputPayload: INodeExecutionData = {
|
const inputPayload: INodeExecutionData = {
|
||||||
json: {
|
json: {
|
||||||
sessionId: sessionId.value,
|
sessionId,
|
||||||
action: 'sendMessage',
|
action: 'sendMessage',
|
||||||
[inputKey]: message,
|
[inputKey]: message,
|
||||||
},
|
},
|
||||||
@@ -166,7 +166,7 @@ export function useChatMessaging({
|
|||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (chatMessage !== undefined) {
|
if (chatMessage !== undefined) {
|
||||||
messages.value.push(chatMessage);
|
onNewMessage(chatMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,16 +200,16 @@ export function useChatMessaging({
|
|||||||
const newMessage: ChatMessage & { sessionId: string } = {
|
const newMessage: ChatMessage & { sessionId: string } = {
|
||||||
text: message,
|
text: message,
|
||||||
sender: 'user',
|
sender: 'user',
|
||||||
sessionId: sessionId.value,
|
sessionId,
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
files,
|
files,
|
||||||
};
|
};
|
||||||
messages.value.push(newMessage);
|
onNewMessage(newMessage);
|
||||||
|
|
||||||
if (ws.value?.readyState === WebSocket.OPEN && !isLoading.value) {
|
if (ws.value?.readyState === WebSocket.OPEN && !isLoading.value) {
|
||||||
ws.value.send(
|
ws.value.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
sessionId: sessionId.value,
|
sessionId,
|
||||||
action: 'sendMessage',
|
action: 'sendMessage',
|
||||||
chatInput: message,
|
chatInput: message,
|
||||||
files: await processFiles(files),
|
files: await processFiles(files),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useChatMessaging } from '@/features/logs/composables/useChatMessaging';
|
|||||||
import { useI18n } from '@n8n/i18n';
|
import { useI18n } from '@n8n/i18n';
|
||||||
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
||||||
import { useRunWorkflow } from '@/composables/useRunWorkflow';
|
import { useRunWorkflow } from '@/composables/useRunWorkflow';
|
||||||
import { VIEWS } from '@/constants';
|
import { PLACEHOLDER_EMPTY_WORKFLOW_ID, VIEWS } from '@/constants';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import { useRootStore } from '@n8n/stores/useRootStore';
|
import { useRootStore } from '@n8n/stores/useRootStore';
|
||||||
import { ChatOptionsSymbol } from '@n8n/chat/constants';
|
import { ChatOptionsSymbol } from '@n8n/chat/constants';
|
||||||
@@ -44,8 +44,8 @@ export function useChatState(isReadOnly: boolean): ChatState {
|
|||||||
const { runWorkflow } = useRunWorkflow({ router });
|
const { runWorkflow } = useRunWorkflow({ router });
|
||||||
|
|
||||||
const ws = ref<WebSocket | null>(null);
|
const ws = ref<WebSocket | null>(null);
|
||||||
const messages = ref<ChatMessage[]>([]);
|
const messages = computed(() => logsStore.chatSessionMessages);
|
||||||
const currentSessionId = ref<string>(uuid().replace(/-/g, ''));
|
const currentSessionId = computed(() => logsStore.chatSessionId);
|
||||||
|
|
||||||
const previousChatMessages = computed(() => workflowsStore.getPastChatMessages);
|
const previousChatMessages = computed(() => workflowsStore.getPastChatMessages);
|
||||||
const chatTriggerNode = computed(() => workflowsStore.allNodes.find(isChatNode) ?? null);
|
const chatTriggerNode = computed(() => workflowsStore.allNodes.find(isChatNode) ?? null);
|
||||||
@@ -68,10 +68,10 @@ export function useChatState(isReadOnly: boolean): ChatState {
|
|||||||
|
|
||||||
const { sendMessage, isLoading, setLoadingState } = useChatMessaging({
|
const { sendMessage, isLoading, setLoadingState } = useChatMessaging({
|
||||||
chatTrigger: chatTriggerNode,
|
chatTrigger: chatTriggerNode,
|
||||||
messages,
|
sessionId: currentSessionId.value,
|
||||||
sessionId: currentSessionId,
|
|
||||||
executionResultData: computed(() => workflowsStore.getWorkflowExecution?.data?.resultData),
|
executionResultData: computed(() => workflowsStore.getWorkflowExecution?.data?.resultData),
|
||||||
onRunChatWorkflow,
|
onRunChatWorkflow,
|
||||||
|
onNewMessage: logsStore.addChatMessage,
|
||||||
ws,
|
ws,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@ export function useChatState(isReadOnly: boolean): ChatState {
|
|||||||
sessionId: currentSessionId.value,
|
sessionId: currentSessionId.value,
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
};
|
};
|
||||||
messages.value.push(newMessage);
|
logsStore.addChatMessage(newMessage);
|
||||||
|
|
||||||
if (logsStore.isOpen) {
|
if (logsStore.isOpen) {
|
||||||
chatEventBus.emit('focusInput');
|
chatEventBus.emit('focusInput');
|
||||||
@@ -216,8 +216,8 @@ export function useChatState(isReadOnly: boolean): ChatState {
|
|||||||
function refreshSession() {
|
function refreshSession() {
|
||||||
workflowsStore.setWorkflowExecutionData(null);
|
workflowsStore.setWorkflowExecutionData(null);
|
||||||
nodeHelpers.updateNodesExecutionIssues();
|
nodeHelpers.updateNodesExecutionIssues();
|
||||||
messages.value = [];
|
logsStore.resetChatSessionId();
|
||||||
currentSessionId.value = uuid().replace(/-/g, '');
|
logsStore.resetMessages();
|
||||||
|
|
||||||
if (logsStore.isOpen) {
|
if (logsStore.isOpen) {
|
||||||
chatEventBus.emit('focusInput');
|
chatEventBus.emit('focusInput');
|
||||||
@@ -232,9 +232,22 @@ export function useChatState(isReadOnly: boolean): ChatState {
|
|||||||
window.open(route.href, '_blank');
|
window.open(route.href, '_blank');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => workflowsStore.workflowId,
|
||||||
|
(_newWorkflowId, prevWorkflowId) => {
|
||||||
|
if (prevWorkflowId === PLACEHOLDER_EMPTY_WORKFLOW_ID) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshSession();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentSessionId,
|
currentSessionId: computed(() => logsStore.chatSessionId),
|
||||||
messages: computed(() => (isReadOnly ? restoredChatMessages.value : messages.value)),
|
messages: computed(() =>
|
||||||
|
isReadOnly ? restoredChatMessages.value : logsStore.chatSessionMessages,
|
||||||
|
),
|
||||||
previousChatMessages,
|
previousChatMessages,
|
||||||
sendMessage,
|
sendMessage,
|
||||||
refreshSession,
|
refreshSession,
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { useLocalStorage } from '@vueuse/core';
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { LOG_DETAILS_PANEL_STATE, LOGS_PANEL_STATE } from '@/features/logs/logs.constants';
|
import { LOG_DETAILS_PANEL_STATE, LOGS_PANEL_STATE } from '@/features/logs/logs.constants';
|
||||||
|
import type { ChatMessage } from '@n8n/chat/types';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
export const useLogsStore = defineStore('logs', () => {
|
export const useLogsStore = defineStore('logs', () => {
|
||||||
const isOpen = useLocalStorage(LOCAL_STORAGE_LOGS_PANEL_OPEN, false);
|
const isOpen = useLocalStorage(LOCAL_STORAGE_LOGS_PANEL_OPEN, false);
|
||||||
@@ -39,10 +41,25 @@ export const useLogsStore = defineStore('logs', () => {
|
|||||||
|
|
||||||
const telemetry = useTelemetry();
|
const telemetry = useTelemetry();
|
||||||
|
|
||||||
|
const chatSessionId = ref<string>(getNewSessionId());
|
||||||
|
const chatSessionMessages = ref<ChatMessage[]>([]);
|
||||||
|
|
||||||
function setHeight(value: number) {
|
function setHeight(value: number) {
|
||||||
height.value = value;
|
height.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNewSessionId(): string {
|
||||||
|
return uuid().replace(/-/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetChatSessionId() {
|
||||||
|
chatSessionId.value = getNewSessionId();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetMessages() {
|
||||||
|
chatSessionMessages.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
function toggleOpen(value?: boolean) {
|
function toggleOpen(value?: boolean) {
|
||||||
isOpen.value = value ?? !isOpen.value;
|
isOpen.value = value ?? !isOpen.value;
|
||||||
}
|
}
|
||||||
@@ -101,6 +118,10 @@ export const useLogsStore = defineStore('logs', () => {
|
|||||||
isLogSelectionSyncedWithCanvas.value = value ?? !isLogSelectionSyncedWithCanvas.value;
|
isLogSelectionSyncedWithCanvas.value = value ?? !isLogSelectionSyncedWithCanvas.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addChatMessage(message: ChatMessage) {
|
||||||
|
chatSessionMessages.value.push(message);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
state,
|
state,
|
||||||
isOpen: computed(() => state.value !== LOGS_PANEL_STATE.CLOSED),
|
isOpen: computed(() => state.value !== LOGS_PANEL_STATE.CLOSED),
|
||||||
@@ -109,6 +130,9 @@ export const useLogsStore = defineStore('logs', () => {
|
|||||||
),
|
),
|
||||||
height: computed(() => height.value),
|
height: computed(() => height.value),
|
||||||
isLogSelectionSyncedWithCanvas: computed(() => isLogSelectionSyncedWithCanvas.value),
|
isLogSelectionSyncedWithCanvas: computed(() => isLogSelectionSyncedWithCanvas.value),
|
||||||
|
chatSessionId: computed(() => chatSessionId.value),
|
||||||
|
chatSessionMessages: computed(() => chatSessionMessages.value),
|
||||||
|
addChatMessage,
|
||||||
setHeight,
|
setHeight,
|
||||||
toggleOpen,
|
toggleOpen,
|
||||||
setPreferPoppedOut,
|
setPreferPoppedOut,
|
||||||
@@ -116,5 +140,7 @@ export const useLogsStore = defineStore('logs', () => {
|
|||||||
toggleInputOpen,
|
toggleInputOpen,
|
||||||
toggleOutputOpen,
|
toggleOutputOpen,
|
||||||
toggleLogSelectionSync,
|
toggleLogSelectionSync,
|
||||||
|
resetChatSessionId,
|
||||||
|
resetMessages,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -207,6 +207,11 @@ export class CanvasPage extends BasePage {
|
|||||||
async clickExecutionsTab(): Promise<void> {
|
async clickExecutionsTab(): Promise<void> {
|
||||||
await this.page.getByRole('radio', { name: 'Executions' }).click();
|
await this.page.getByRole('radio', { name: 'Executions' }).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async clickEditorTab(): Promise<void> {
|
||||||
|
await this.page.getByRole('radio', { name: 'Editor' }).click();
|
||||||
|
}
|
||||||
|
|
||||||
async setWorkflowName(name: string): Promise<void> {
|
async setWorkflowName(name: string): Promise<void> {
|
||||||
await this.clickByTestId('inline-edit-preview');
|
await this.clickByTestId('inline-edit-preview');
|
||||||
await this.fillByTestId('inline-edit-input', name);
|
await this.fillByTestId('inline-edit-input', name);
|
||||||
|
|||||||
@@ -529,4 +529,24 @@ test.describe('Langchain Integration @capability:proxy', () => {
|
|||||||
await expect(n8n.canvas.getManualChatLatestBotMessage()).toContainText('this_my_field_4');
|
await expect(n8n.canvas.getManualChatLatestBotMessage()).toContainText('this_my_field_4');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should keep the same session when switching tabs', async ({ n8n }) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('Test_workflow_chat_partial_execution.json');
|
||||||
|
await n8n.canvas.clickZoomToFitButton();
|
||||||
|
|
||||||
|
await n8n.canvas.logsPanel.open();
|
||||||
|
|
||||||
|
// Send a message
|
||||||
|
await n8n.canvas.logsPanel.sendManualChatMessage('Test');
|
||||||
|
await expect(n8n.canvas.getManualChatLatestBotMessage()).toContainText('this_my_field');
|
||||||
|
|
||||||
|
await n8n.canvas.clickExecutionsTab();
|
||||||
|
|
||||||
|
await n8n.canvas.clickEditorTab();
|
||||||
|
await expect(n8n.canvas.getManualChatLatestBotMessage()).toContainText('this_my_field');
|
||||||
|
|
||||||
|
// Refresh session
|
||||||
|
await n8n.page.getByTestId('refresh-session-button').click();
|
||||||
|
await expect(n8n.canvas.getManualChatMessages()).not.toBeAttached();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user