Files
alighasami 3d5eaf9445
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
first commit
2026-03-17 16:22:57 +03:30

247 lines
8.0 KiB
TypeScript

import {
AGENT_NODE_NAME,
EDIT_FIELDS_SET_NODE_NAME,
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
AI_MEMORY_REDIS_CHAT_NODE_NAME,
AI_TOOL_CALCULATOR_NODE_NAME,
AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME,
AI_TOOL_CODE_NODE_NAME,
AI_TOOL_WIKIPEDIA_NODE_NAME,
SCHEDULE_TRIGGER_NODE_NAME,
TOOL_SUBCATEGORY,
} from '../../../config/constants';
import { test, expect } from '../../../fixtures/base';
import type { n8nPage } from '../../../pages/n8nPage';
// Helper functions for common operations
async function addOpenAILanguageModelWithCredentials(
n8n: n8nPage,
parentNode: string,
options: { exactMatch?: boolean; closeNDV?: boolean } = { exactMatch: true, closeNDV: false },
) {
await n8n.canvas.addSupplementalNodeToParent(
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
'ai_languageModel',
parentNode,
options,
);
await n8n.credentialsComposer.createFromNdv({
apiKey: 'abcd',
});
await n8n.ndv.clickBackToCanvasButton();
}
async function waitForWorkflowSuccess(n8n: n8nPage, timeout = 3000) {
await n8n.notifications.waitForNotificationAndClose('Workflow executed successfully', {
timeout,
});
}
async function executeChatAndWaitForResponse(n8n: n8nPage, message: string) {
await n8n.canvas.logsPanel.sendManualChatMessage(message);
await waitForWorkflowSuccess(n8n);
}
async function verifyChatMessages(n8n: n8nPage, expectedCount: number, inputMessage?: string) {
const messages = n8n.canvas.getManualChatMessages();
await expect(messages).toHaveCount(expectedCount);
if (inputMessage) {
await expect(messages.first()).toContainText(inputMessage);
}
await expect(messages.last()).toBeVisible();
return messages;
}
async function verifyLogsPanelEntries(n8n: n8nPage, expectedEntries: string[]) {
await expect(n8n.canvas.logsPanel.getLogEntries().first()).toBeVisible();
await expect(n8n.canvas.logsPanel.getLogEntries()).toHaveCount(expectedEntries.length);
for (let i = 0; i < expectedEntries.length; i++) {
await expect(n8n.canvas.logsPanel.getLogEntries().nth(i)).toHaveText(expectedEntries[i]);
}
}
async function setupBasicAgentWorkflow(n8n: n8nPage, additionalNodes: string[] = []) {
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });
// Add additional nodes if specified
for (const nodeName of additionalNodes) {
await n8n.canvas.addSupplementalNodeToParent(nodeName, 'ai_tool', AGENT_NODE_NAME, {
closeNDV: true,
});
}
// Always add OpenAI Language Model
await addOpenAILanguageModelWithCredentials(n8n, AGENT_NODE_NAME);
}
test.use({ capability: 'proxy' });
test.describe('Langchain Integration @capability:proxy', {
annotation: [
{ type: 'owner', description: 'AI' },
],
}, () => {
test.beforeEach(async ({ n8n, services }) => {
await services.proxy.clearAllExpectations();
await services.proxy.loadExpectations('langchain');
await n8n.canvas.openNewWorkflow();
});
test.describe('Workflow Execution Behavior', () => {
test('should not open chat modal', async ({ n8n }) => {
await n8n.canvas.addNode(EDIT_FIELDS_SET_NODE_NAME, { closeNDV: true });
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });
await n8n.canvas.addSupplementalNodeToParent(
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
'ai_languageModel',
AGENT_NODE_NAME,
{ exactMatch: true, closeNDV: true },
);
await n8n.canvas.clickExecuteWorkflowButton();
await expect(n8n.canvas.getManualChatModal()).toBeHidden();
});
test('should remove test workflow button', async ({ n8n }) => {
await n8n.canvas.addNode(SCHEDULE_TRIGGER_NODE_NAME, { closeNDV: true });
await n8n.canvas.addNode(EDIT_FIELDS_SET_NODE_NAME, { closeNDV: true });
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });
await n8n.canvas.addSupplementalNodeToParent(
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
'ai_languageModel',
AGENT_NODE_NAME,
{ exactMatch: true, closeNDV: true },
);
await n8n.canvas.disableNodeFromContextMenu(SCHEDULE_TRIGGER_NODE_NAME);
await expect(n8n.canvas.getExecuteWorkflowButton()).toBeHidden();
});
});
test.describe('Node Connection and Configuration', () => {
test('should add nodes to all Agent node input types', async ({ n8n }) => {
const agentSubNodes = [
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
AI_MEMORY_REDIS_CHAT_NODE_NAME,
AI_TOOL_CALCULATOR_NODE_NAME,
AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME,
];
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: false });
await n8n.ndv.checkParameterCheckboxInputByName('hasOutputParser');
await n8n.ndv.clickBackToCanvasButton();
await n8n.canvas.addSupplementalNodeToParent(
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
'ai_languageModel',
AGENT_NODE_NAME,
{ exactMatch: true, closeNDV: true },
);
await n8n.canvas.addSupplementalNodeToParent(
AI_MEMORY_REDIS_CHAT_NODE_NAME,
'ai_memory',
AGENT_NODE_NAME,
{ closeNDV: true },
);
await n8n.canvas.addSupplementalNodeToParent(
AI_TOOL_CALCULATOR_NODE_NAME,
'ai_tool',
AGENT_NODE_NAME,
{ closeNDV: true, subcategory: TOOL_SUBCATEGORY },
);
await n8n.canvas.addSupplementalNodeToParent(
AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME,
'ai_outputParser',
AGENT_NODE_NAME,
{ closeNDV: true },
);
for (const nodeName of agentSubNodes) {
await expect(n8n.canvas.connectionBetweenNodes(nodeName, AGENT_NODE_NAME)).toBeAttached();
}
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2 + agentSubNodes.length); // Chat Trigger + Agent + 4 inputs
});
test('should add multiple tool nodes to Agent node tool input type', async ({ n8n }) => {
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });
const tools = [
{ name: AI_TOOL_CALCULATOR_NODE_NAME, subcategory: TOOL_SUBCATEGORY },
{ name: AI_TOOL_CODE_NODE_NAME },
{ name: AI_TOOL_CODE_NODE_NAME },
{ name: AI_TOOL_WIKIPEDIA_NODE_NAME, subcategory: TOOL_SUBCATEGORY },
];
for (const tool of tools) {
await n8n.canvas.addSupplementalNodeToParent(tool.name, 'ai_tool', AGENT_NODE_NAME, {
closeNDV: true,
subcategory: tool.subcategory,
});
await expect(n8n.canvas.connectionBetweenNodes(tool.name, AGENT_NODE_NAME)).toBeAttached();
}
// Chat Trigger + Agent + Tools
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2 + tools.length);
});
});
test.describe('Chat Execution and Interaction', () => {
test('should be able to open and execute Agent node', async ({ n8n }) => {
await setupBasicAgentWorkflow(n8n);
const inputMessage = 'Hello!';
await n8n.canvas.clickManualChatButton();
await executeChatAndWaitForResponse(n8n, inputMessage);
// Verify chat message appears
await expect(n8n.canvas.getManualChatLatestBotMessage()).toBeVisible();
});
test('should add and use Manual Chat Trigger node together with Agent node', async ({
n8n,
}) => {
await setupBasicAgentWorkflow(n8n);
const inputMessage = 'Hello!';
await n8n.canvas.clickManualChatButton();
await executeChatAndWaitForResponse(n8n, inputMessage);
await verifyChatMessages(n8n, 2, inputMessage);
await verifyLogsPanelEntries(n8n, [
'When chat message received',
'AI Agent',
'OpenAI Chat Model',
]);
await n8n.canvas.closeManualChatModal();
await expect(n8n.canvas.logsPanel.getLogEntries()).toBeHidden();
await expect(n8n.canvas.getManualChatInput()).toBeHidden();
});
});
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.canvas.logsPanel.refreshSession();
await expect(n8n.canvas.logsPanel.getManualChatMessages()).not.toBeAttached();
});
});