first commit
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
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
This commit is contained in:
@@ -0,0 +1,292 @@
|
||||
import {
|
||||
simpleAssistantResponse,
|
||||
aiDisabledRequirements,
|
||||
aiEnabledRequirements,
|
||||
aiEnabledWithWorkflowRequirements,
|
||||
aiEnabledWithQuickRepliesRequirements,
|
||||
aiEnabledWithEndSessionRequirements,
|
||||
aiEnabledWithSimpleChatRequirements,
|
||||
} from '../../../config/ai-assistant-fixtures';
|
||||
import { SCHEDULE_TRIGGER_NODE_NAME } from '../../../config/constants';
|
||||
import { test, expect } from '../../../fixtures/base';
|
||||
|
||||
type ChatRequestBody = {
|
||||
payload?: {
|
||||
type?: string;
|
||||
text?: string;
|
||||
question?: string;
|
||||
context?: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
|
||||
test.describe('AI Assistant::disabled', {
|
||||
annotation: [
|
||||
{ type: 'owner', description: 'AI' },
|
||||
],
|
||||
}, () => {
|
||||
test('does not show assistant button if feature is disabled', async ({
|
||||
n8n,
|
||||
setupRequirements,
|
||||
}) => {
|
||||
await setupRequirements(aiDisabledRequirements);
|
||||
await n8n.page.goto('/workflow/new');
|
||||
await expect(n8n.canvas.canvasPane()).toBeVisible();
|
||||
await expect(n8n.aiAssistant.getAskAssistantFloatingButton()).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('AI Assistant::enabled', () => {
|
||||
test('renders placeholder UI', async ({ n8n, setupRequirements }) => {
|
||||
await setupRequirements(aiEnabledRequirements);
|
||||
await n8n.page.goto('/workflow/new');
|
||||
|
||||
await expect(n8n.aiAssistant.getAskAssistantCanvasActionButton()).toBeVisible();
|
||||
|
||||
await n8n.aiAssistant.getAskAssistantCanvasActionButton().click();
|
||||
|
||||
await expect(n8n.aiAssistant.getAskAssistantChat()).toBeVisible();
|
||||
|
||||
await expect(n8n.aiAssistant.getPlaceholderMessage()).toBeVisible();
|
||||
|
||||
await expect(n8n.aiAssistant.getChatInput()).toBeVisible();
|
||||
|
||||
await expect(n8n.aiAssistant.getSendMessageButton()).toBeDisabled();
|
||||
|
||||
await expect(n8n.aiAssistant.getCloseChatButton()).toBeVisible();
|
||||
|
||||
await n8n.aiAssistant.getCloseChatButton().click();
|
||||
|
||||
await expect(n8n.aiAssistant.getAskAssistantChat()).toBeHidden();
|
||||
});
|
||||
|
||||
test('should show resizer when chat is open', async ({ n8n, setupRequirements }) => {
|
||||
await setupRequirements(aiEnabledRequirements);
|
||||
await n8n.page.goto('/workflow/new');
|
||||
|
||||
await n8n.aiAssistant.getAskAssistantCanvasActionButton().click();
|
||||
|
||||
await expect(n8n.aiAssistant.getAskAssistantSidebarResizer()).toBeVisible();
|
||||
|
||||
await expect(n8n.aiAssistant.getAskAssistantChat()).toBeVisible();
|
||||
|
||||
await n8n.aiAssistant.getAskAssistantSidebarResizer().hover();
|
||||
|
||||
await n8n.aiAssistant.getCloseChatButton().click();
|
||||
});
|
||||
|
||||
test('should start chat session from node error view', async ({ n8n, setupRequirements }) => {
|
||||
await setupRequirements(aiEnabledWithWorkflowRequirements);
|
||||
|
||||
await n8n.canvas.openNode('Stop and Error');
|
||||
|
||||
await n8n.ndv.execute();
|
||||
|
||||
await expect(n8n.aiAssistant.getNodeErrorViewAssistantButton()).toBeVisible();
|
||||
await expect(n8n.aiAssistant.getNodeErrorViewAssistantButton()).toBeEnabled();
|
||||
|
||||
await n8n.aiAssistant.getNodeErrorViewAssistantButton().click();
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesAll()).toHaveCount(1);
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesAll().first()).toContainText(
|
||||
'Hey, this is an assistant message',
|
||||
);
|
||||
});
|
||||
|
||||
test('should render chat input correctly', async ({ n8n, setupRequirements }) => {
|
||||
await setupRequirements(aiEnabledWithWorkflowRequirements);
|
||||
|
||||
await n8n.aiAssistant.getAskAssistantCanvasActionButton().click();
|
||||
|
||||
await expect(n8n.aiAssistant.getAskAssistantChat()).toBeVisible();
|
||||
await expect(n8n.aiAssistant.getChatInput()).toBeVisible();
|
||||
|
||||
await expect(n8n.aiAssistant.getSendMessageButton()).toBeDisabled();
|
||||
|
||||
await n8n.aiAssistant.getChatInput().fill('Test message');
|
||||
|
||||
await expect(n8n.aiAssistant.getChatInput()).toHaveValue('Test message');
|
||||
|
||||
await expect(n8n.aiAssistant.getSendMessageButton()).toBeEnabled();
|
||||
|
||||
await n8n.aiAssistant.getSendMessageButton().click();
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesUser()).toHaveCount(1);
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesUser()).toHaveCount(1);
|
||||
|
||||
await expect(n8n.aiAssistant.getChatInput()).toHaveValue('');
|
||||
});
|
||||
|
||||
test('should render and handle quick replies', async ({ n8n, setupRequirements }) => {
|
||||
await setupRequirements(aiEnabledWithQuickRepliesRequirements);
|
||||
|
||||
await n8n.canvas.openNode('Stop and Error');
|
||||
|
||||
await n8n.ndv.execute();
|
||||
|
||||
await n8n.aiAssistant.getNodeErrorViewAssistantButton().click();
|
||||
|
||||
await expect(n8n.aiAssistant.getQuickReplyButtons()).toHaveCount(2);
|
||||
|
||||
await expect(n8n.aiAssistant.getQuickReplyButtons()).toHaveCount(2);
|
||||
|
||||
await n8n.aiAssistant.getQuickReplyButtons().first().click();
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesUser()).toHaveCount(1);
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesUser()).toHaveCount(1);
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesUser().first()).toContainText("Sure, let's do it");
|
||||
});
|
||||
|
||||
test('should warn before starting a new session', async ({ n8n, setupRequirements }) => {
|
||||
await setupRequirements(aiEnabledWithWorkflowRequirements);
|
||||
|
||||
await n8n.canvas.openNode('Edit Fields');
|
||||
|
||||
await n8n.ndv.execute();
|
||||
|
||||
await n8n.aiAssistant.getNodeErrorViewAssistantButton().click();
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesAll()).toHaveCount(1);
|
||||
|
||||
await n8n.aiAssistant.getCloseChatButton().click();
|
||||
|
||||
await n8n.ndv.clickBackToCanvasButton();
|
||||
|
||||
await n8n.canvas.openNode('Stop and Error');
|
||||
|
||||
await n8n.ndv.execute();
|
||||
|
||||
await n8n.aiAssistant.getNodeErrorViewAssistantButton().click();
|
||||
|
||||
await expect(n8n.aiAssistant.getNewAssistantSessionModal()).toBeVisible();
|
||||
|
||||
await n8n.aiAssistant
|
||||
.getNewAssistantSessionModal()
|
||||
.getByRole('button', { name: 'Start new session' })
|
||||
.click();
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesAll()).toHaveCount(1);
|
||||
});
|
||||
|
||||
test('should end chat session when `end_session` event is received', async ({
|
||||
n8n,
|
||||
setupRequirements,
|
||||
}) => {
|
||||
await setupRequirements(aiEnabledWithEndSessionRequirements);
|
||||
|
||||
await n8n.canvas.openNode('Stop and Error');
|
||||
|
||||
await n8n.ndv.execute();
|
||||
|
||||
await n8n.aiAssistant.getNodeErrorViewAssistantButton().click();
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesSystem()).toHaveCount(1);
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesSystem()).toHaveCount(1);
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesSystem().first()).toContainText(
|
||||
'session has ended',
|
||||
);
|
||||
});
|
||||
|
||||
test('should reset session after it ended and sidebar is closed', async ({
|
||||
n8n,
|
||||
setupRequirements,
|
||||
}) => {
|
||||
await setupRequirements(aiEnabledRequirements);
|
||||
await n8n.page.goto('/workflow/new');
|
||||
|
||||
await n8n.page.route('**/rest/ai/chat', async (route) => {
|
||||
const requestBody = route.request().postDataJSON() as ChatRequestBody;
|
||||
const isInit = requestBody.payload?.type === 'init-support-chat';
|
||||
const response = isInit
|
||||
? simpleAssistantResponse
|
||||
: {
|
||||
sessionId: '1',
|
||||
messages: [
|
||||
{
|
||||
role: 'assistant',
|
||||
type: 'message',
|
||||
title: 'Glad to Help',
|
||||
text: "I'm glad I could help. If you have any more questions or need further assistance with your n8n workflows, feel free to ask!",
|
||||
},
|
||||
{
|
||||
role: 'assistant',
|
||||
type: 'event',
|
||||
eventName: 'end-session',
|
||||
},
|
||||
],
|
||||
};
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify(response),
|
||||
});
|
||||
});
|
||||
|
||||
await n8n.aiAssistant.getAskAssistantCanvasActionButton().click();
|
||||
await n8n.aiAssistant.sendMessage('Hello', 'enter-key');
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesAll()).toHaveCount(2);
|
||||
|
||||
await n8n.aiAssistant.getCloseChatButton().click();
|
||||
// Wait for sidebar to close
|
||||
await expect(n8n.aiAssistant.getAskAssistantChat()).toBeHidden();
|
||||
await n8n.aiAssistant.getAskAssistantCanvasActionButton().click();
|
||||
await expect(n8n.aiAssistant.getChatMessagesAll()).toHaveCount(2);
|
||||
|
||||
await n8n.aiAssistant.sendMessage('Thanks, bye', 'enter-key');
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesSystem()).toHaveCount(1);
|
||||
await expect(n8n.aiAssistant.getChatMessagesSystem().first()).toContainText(
|
||||
'session has ended',
|
||||
);
|
||||
|
||||
await n8n.aiAssistant.getCloseChatButton().click();
|
||||
await n8n.aiAssistant.getAskAssistantCanvasActionButton().click();
|
||||
await expect(n8n.aiAssistant.getPlaceholderMessage()).toBeVisible();
|
||||
});
|
||||
|
||||
test('should not reset assistant session when workflow is saved', async ({
|
||||
n8n,
|
||||
setupRequirements,
|
||||
}) => {
|
||||
await setupRequirements(aiEnabledWithSimpleChatRequirements);
|
||||
await n8n.page.goto('/workflow/new');
|
||||
|
||||
await n8n.canvas.addInitialNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
await n8n.ndv.clickBackToCanvasButton();
|
||||
|
||||
await n8n.aiAssistant.getAskAssistantCanvasActionButton().click();
|
||||
await n8n.aiAssistant.sendMessage('Hello', 'enter-key');
|
||||
|
||||
await expect(n8n.aiAssistant.getChatMessagesUser()).toHaveCount(1);
|
||||
|
||||
await n8n.canvas.openNode(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
await n8n.ndv.execute();
|
||||
|
||||
await expect(n8n.aiAssistant.getPlaceholderMessage()).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('should send message via shift + enter even with global NodeCreator panel opened', async ({
|
||||
n8n,
|
||||
setupRequirements,
|
||||
}) => {
|
||||
await setupRequirements(aiEnabledWithSimpleChatRequirements);
|
||||
await n8n.page.goto('/workflow/new');
|
||||
|
||||
await n8n.canvas.addInitialNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
await n8n.ndv.clickBackToCanvasButton();
|
||||
|
||||
await n8n.aiAssistant.getAskAssistantCanvasActionButton().click();
|
||||
await n8n.canvas.nodeCreator.open();
|
||||
|
||||
await n8n.aiAssistant.sendMessage('Hello', 'enter-key');
|
||||
|
||||
await expect(n8n.aiAssistant.getPlaceholderMessage()).toHaveCount(0);
|
||||
await expect(n8n.aiAssistant.getChatMessagesUser()).toHaveCount(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user