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

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,67 @@
import { test, expect } from '../../../fixtures/base';
test.describe('Node Creator Actions', {
annotation: [
{ type: 'owner', description: 'Adore' },
],
}, () => {
test.beforeEach(async ({ n8n }) => {
await n8n.start.fromBlankCanvas();
});
test('should add node to canvas from actions panel', async ({ n8n }) => {
const editImageNode = 'Edit Image';
await n8n.canvas.nodeCreator.open();
await n8n.canvas.nodeCreator.searchFor(editImageNode);
await n8n.canvas.nodeCreator.selectItem(editImageNode);
await expect(n8n.canvas.nodeCreator.getActiveSubcategory()).toContainText(editImageNode);
await n8n.canvas.nodeCreator.selectItem('Crop Image');
await expect(n8n.ndv.getContainer()).toBeVisible();
await n8n.page.keyboard.press('Escape');
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
});
test('should search through actions and confirm added action', async ({ n8n }) => {
await n8n.canvas.nodeCreator.open();
await n8n.canvas.nodeCreator.searchFor('ftp');
await n8n.canvas.nodeCreator.selectItem('FTP');
await expect(n8n.canvas.nodeCreator.getActiveSubcategory()).toContainText('FTP');
await n8n.canvas.nodeCreator.clearSearch();
await n8n.canvas.nodeCreator.searchFor('rename');
await n8n.canvas.nodeCreator.selectItem('Rename');
await expect(n8n.ndv.getContainer()).toBeVisible();
await n8n.page.keyboard.press('Escape');
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
});
test('should show multiple actions for multi-action nodes', async ({ n8n }) => {
await n8n.canvas.nodeCreator.open();
await n8n.canvas.nodeCreator.searchFor('OpenWeatherMap');
await n8n.canvas.nodeCreator.selectItem('OpenWeatherMap');
await expect(n8n.canvas.nodeCreator.getActiveSubcategory()).toContainText('OpenWeatherMap');
await expect(n8n.canvas.nodeCreator.getNodeItems().first()).toBeVisible();
await expect(n8n.canvas.nodeCreator.getNodeItems().nth(1)).toBeVisible();
await n8n.canvas.nodeCreator.getNodeItems().first().click();
await n8n.page.keyboard.press('Escape');
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
});
test('should add node with specific operation configuration', async ({ n8n }) => {
await n8n.canvas.nodeCreator.open();
await n8n.canvas.nodeCreator.searchFor('Slack');
await n8n.canvas.nodeCreator.selectItem('Slack');
await expect(n8n.canvas.nodeCreator.getActiveSubcategory()).toContainText('Slack');
await n8n.canvas.nodeCreator.getNodeItems().first().click();
await n8n.page.keyboard.press('Escape');
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(1);
});
});
@@ -0,0 +1,105 @@
import { MANUAL_TRIGGER_NODE_DISPLAY_NAME } from '../../../config/constants';
import { test, expect } from '../../../fixtures/base';
test.describe('Node Creator Categories', {
annotation: [
{ type: 'owner', description: 'Adore' },
],
}, () => {
test.beforeEach(async ({ n8n }) => {
await n8n.start.fromBlankCanvas();
});
test('should have "Actions" section collapsed when opening actions view from Trigger root view', async ({
n8n,
}) => {
await n8n.canvas.nodeCreator.open();
await n8n.canvas.nodeCreator.searchFor('ActiveCampaign');
await n8n.canvas.nodeCreator.selectItem('ActiveCampaign');
await expect(n8n.canvas.nodeCreator.getCategoryItem('Actions')).toBeVisible();
await expect(n8n.canvas.nodeCreator.getCategoryItem('Triggers')).toBeVisible();
await expect(n8n.canvas.nodeCreator.getCategoryItem('Triggers').locator('..')).toHaveAttribute(
'data-category-collapsed',
'false',
);
await expect(n8n.canvas.nodeCreator.getCategoryItem('Actions').locator('..')).toHaveAttribute(
'data-category-collapsed',
'true',
);
await n8n.canvas.nodeCreator.selectCategoryItem('Actions');
await expect(n8n.canvas.nodeCreator.getCategoryItem('Actions').locator('..')).toHaveAttribute(
'data-category-collapsed',
'false',
);
});
test('should have "Triggers" section collapsed when opening actions view from Regular root view', async ({
n8n,
}) => {
await n8n.canvas.addNode('Manual Trigger');
await n8n.canvas.clickNodePlusEndpoint(MANUAL_TRIGGER_NODE_DISPLAY_NAME);
await n8n.canvas.nodeCreator.searchFor('n8n');
await n8n.canvas.nodeCreator.getNodeItems().filter({ hasText: 'n8n' }).first().click();
await expect(n8n.canvas.nodeCreator.getCategoryItem('Actions').locator('..')).toHaveAttribute(
'data-category-collapsed',
'false',
);
await n8n.canvas.nodeCreator.selectCategoryItem('Actions');
await expect(n8n.canvas.nodeCreator.getCategoryItem('Actions').locator('..')).toHaveAttribute(
'data-category-collapsed',
'true',
);
await expect(n8n.canvas.nodeCreator.getCategoryItem('Triggers').locator('..')).toHaveAttribute(
'data-category-collapsed',
'true',
);
await n8n.canvas.nodeCreator.selectCategoryItem('Triggers');
await expect(n8n.canvas.nodeCreator.getCategoryItem('Triggers').locator('..')).toHaveAttribute(
'data-category-collapsed',
'false',
);
});
test('should show callout and two suggested nodes if node has no trigger actions', async ({
n8n,
}) => {
await n8n.canvas.nodeCreator.open();
await n8n.canvas.nodeCreator.searchFor('Customer Datastore (n8n training)');
await n8n.canvas.nodeCreator.selectItem('Customer Datastore (n8n training)');
await expect(n8n.canvas.nodeCreator.getNoTriggersCallout()).toBeVisible();
await expect(n8n.canvas.nodeCreator.getItem('On a Schedule')).toBeVisible();
await expect(n8n.canvas.nodeCreator.getItem('On a Webhook call')).toBeVisible();
});
test('should show intro callout if user has not made a production execution', async ({ n8n }) => {
await n8n.canvas.nodeCreator.open();
await n8n.canvas.nodeCreator.searchFor('Customer Datastore (n8n training)');
await n8n.canvas.nodeCreator.selectItem('Customer Datastore (n8n training)');
await expect(n8n.canvas.nodeCreator.getActivationCallout()).toBeVisible();
});
test('should show Trigger and Actions sections during search', async ({ n8n }) => {
await n8n.canvas.nodeCreator.open();
await n8n.canvas.nodeCreator.searchFor('Customer Datastore (n8n training)');
await n8n.canvas.nodeCreator.selectItem('Customer Datastore (n8n training)');
await n8n.canvas.nodeCreator.searchFor('Non existent action name');
await expect(n8n.canvas.nodeCreator.getCategoryItem('Triggers')).toBeVisible();
await expect(n8n.canvas.nodeCreator.getCategoryItem('Actions')).toBeVisible();
await expect(n8n.canvas.nodeCreator.getNoTriggersCallout()).toBeVisible();
await expect(n8n.canvas.nodeCreator.getItem('On a Schedule')).toBeVisible();
await expect(n8n.canvas.nodeCreator.getItem('On a Webhook call')).toBeVisible();
});
});
@@ -0,0 +1,75 @@
import { MANUAL_TRIGGER_NODE_DISPLAY_NAME } from '../../../config/constants';
import { test, expect } from '../../../fixtures/base';
test.describe('Node Creator Navigation', {
annotation: [
{ type: 'owner', description: 'Adore' },
],
}, () => {
test.beforeEach(async ({ n8n }) => {
await n8n.start.fromBlankCanvas();
});
test('should open node creator on trigger tab if no trigger is on canvas', async ({ n8n }) => {
await n8n.canvas.clickCanvasPlusButton();
await expect(n8n.canvas.nodeCreator.getRoot()).toBeVisible();
await expect(n8n.canvas.nodeCreator.getTriggerText()).toBeVisible();
});
test('should navigate subcategory and return', async ({ n8n }) => {
await n8n.canvas.nodeCreator.open();
await n8n.canvas.nodeCreator.navigateToSubcategory('On app event');
await expect(n8n.canvas.nodeCreator.getActiveSubcategory()).toContainText('On app event');
await n8n.canvas.nodeCreator.goBackFromSubcategory();
await expect(n8n.canvas.nodeCreator.getActiveSubcategory()).not.toContainText('On app event');
});
test('should search for nodes with various queries', async ({ n8n }) => {
await n8n.canvas.nodeCreator.open();
await n8n.canvas.nodeCreator.searchFor('manual');
await expect(n8n.canvas.nodeCreator.getNodeItems()).toHaveCount(1);
await n8n.canvas.nodeCreator.clearSearch();
await n8n.canvas.nodeCreator.searchFor('manual123');
await expect(n8n.canvas.nodeCreator.getNodeItems()).toHaveCount(0);
await expect(n8n.canvas.nodeCreator.getNoResults()).toBeVisible();
await expect(n8n.canvas.nodeCreator.getNoResults()).toContainText("We didn't make that... yet");
await n8n.canvas.nodeCreator.clearSearch();
await n8n.canvas.nodeCreator.searchFor('edit image');
await expect(n8n.canvas.nodeCreator.getNodeItems()).toHaveCount(1);
await n8n.canvas.nodeCreator.clearSearch();
await n8n.canvas.nodeCreator.searchFor('this node totally does not exist');
await expect(n8n.canvas.nodeCreator.getNodeItems()).toHaveCount(0);
await n8n.canvas.nodeCreator.clearSearch();
await n8n.canvas.nodeCreator.navigateToSubcategory('On app event');
await n8n.canvas.nodeCreator.searchFor('edit image');
await expect(
n8n.canvas.nodeCreator.getCategoryItem('Results in other categories'),
).toBeVisible();
await expect(n8n.canvas.nodeCreator.getNodeItems()).toHaveCount(1);
await expect(n8n.canvas.nodeCreator.getItem('Edit Image')).toBeVisible();
await n8n.canvas.nodeCreator.clearSearch();
await n8n.canvas.nodeCreator.searchFor('edit image123123');
await expect(n8n.canvas.nodeCreator.getNodeItems()).toHaveCount(0);
});
test('should check correct view panels after adding manual trigger', async ({ n8n }) => {
await n8n.canvas.clickCanvasPlusButton();
await expect(n8n.canvas.nodeCreator.getTriggerText()).toBeVisible();
await n8n.canvas.nodeCreator.close();
await n8n.canvas.addNode('Manual Trigger');
await expect(n8n.canvas.getCanvasPlusButton()).toBeHidden();
await n8n.canvas.clickNodePlusEndpoint(MANUAL_TRIGGER_NODE_DISPLAY_NAME);
await expect(n8n.canvas.nodeCreator.getNextText()).toBeVisible();
});
});
@@ -0,0 +1,53 @@
import { MANUAL_TRIGGER_NODE_DISPLAY_NAME } from '../../../config/constants';
import { test, expect } from '../../../fixtures/base';
test.describe('Node Creator Special Nodes', {
annotation: [
{ type: 'owner', description: 'Adore' },
],
}, () => {
test.beforeEach(async ({ n8n }) => {
await n8n.start.fromBlankCanvas();
});
test('should correctly append a No Op node when Loop Over Items node is added (from add button)', async ({
n8n,
}) => {
await n8n.canvas.nodeCreator.open();
await n8n.canvas.nodeCreator.searchFor('Loop Over Items');
await n8n.canvas.nodeCreator.selectItem('Loop Over Items');
await n8n.ndv.close();
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(3);
await expect(n8n.canvas.nodeConnections()).toHaveCount(3);
await expect(n8n.canvas.nodeByName('Loop Over Items')).toBeVisible();
await expect(n8n.canvas.nodeByName('Replace Me')).toBeVisible();
});
test('should correctly append a No Op node when Loop Over Items node is added (from connection)', async ({
n8n,
}) => {
await n8n.canvas.addNode('Manual Trigger');
await n8n.canvas.clickNodePlusEndpoint(MANUAL_TRIGGER_NODE_DISPLAY_NAME);
await n8n.canvas.nodeCreator.searchFor('Loop Over Items');
await n8n.canvas.nodeCreator.selectItem('Loop Over Items');
await n8n.ndv.close();
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(3);
await expect(n8n.canvas.nodeConnections()).toHaveCount(3);
await expect(n8n.canvas.nodeByName('Loop Over Items')).toBeVisible();
await expect(n8n.canvas.nodeByName('Replace Me')).toBeVisible();
});
test('should add a Send and Wait for Response node', async ({ n8n }) => {
await n8n.canvas.addNode('Manual Trigger');
await n8n.canvas.clickNodePlusEndpoint(MANUAL_TRIGGER_NODE_DISPLAY_NAME);
await n8n.canvas.nodeCreator.navigateToSubcategory('Human review');
await n8n.canvas.nodeCreator.selectItem('Slack');
await n8n.ndv.setupHelper.setParameter('operation', 'Send and Wait for Response');
await n8n.ndv.close();
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
});
});
@@ -0,0 +1,50 @@
import { MANUAL_TRIGGER_NODE_DISPLAY_NAME } from '../../../config/constants';
import { test, expect } from '../../../fixtures/base';
test.describe('Node Creator Vector Stores', {
annotation: [
{ type: 'owner', description: 'Adore' },
],
}, () => {
test.beforeEach(async ({ n8n }) => {
await n8n.start.fromBlankCanvas();
await n8n.canvas.addNode('Manual Trigger');
});
test('should show vector stores actions', async ({ n8n }) => {
const expectedActions = [
'Get ranked documents from vector store',
'Add documents to vector store',
'Retrieve documents for Chain/Tool as Vector Store',
'Retrieve documents for AI Agent as Tool',
];
await n8n.canvas.clickNodePlusEndpoint(MANUAL_TRIGGER_NODE_DISPLAY_NAME);
await n8n.canvas.nodeCreator.searchFor('Vector Store');
await expect(n8n.canvas.nodeCreator.getNodeItems().first()).toBeVisible();
await n8n.canvas.nodeCreator.getItem('Simple Vector Store').click();
for (const action of expectedActions) {
await expect(n8n.canvas.nodeCreator.getItem(action)).toBeVisible();
}
await n8n.canvas.nodeCreator.goBackFromSubcategory();
await expect(n8n.canvas.nodeCreator.getNodeItems().first()).toBeVisible();
});
test('should find vector store nodes in creator', async ({ n8n }) => {
await n8n.canvas.clickNodePlusEndpoint(MANUAL_TRIGGER_NODE_DISPLAY_NAME);
await n8n.canvas.nodeCreator.searchFor('Vector Store');
await expect(n8n.canvas.nodeCreator.getNodeItems().first()).toBeVisible();
});
test('should search for specific vector store nodes', async ({ n8n }) => {
await n8n.canvas.clickNodePlusEndpoint(MANUAL_TRIGGER_NODE_DISPLAY_NAME);
await n8n.canvas.nodeCreator.searchFor('Simple Vector Store');
await expect(n8n.canvas.nodeCreator.getItem('Simple Vector Store')).toBeVisible();
});
});
@@ -0,0 +1,36 @@
import { test, expect } from '../../../fixtures/base';
test.describe('Node Creator Workflow Building', {
annotation: [
{ type: 'owner', description: 'Adore' },
],
}, () => {
test.beforeEach(async ({ n8n }) => {
await n8n.start.fromBlankCanvas();
});
test('should append manual trigger when adding action node from canvas add button', async ({
n8n,
}) => {
await n8n.canvas.clickCanvasPlusButton();
await n8n.canvas.nodeCreator.searchFor('n8n');
await n8n.canvas.nodeCreator.selectItem('n8n');
await n8n.canvas.nodeCreator.selectCategoryItem('Actions');
await n8n.canvas.nodeCreator.selectItem('Create a credential');
await n8n.page.keyboard.press('Escape');
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
await expect(n8n.canvas.nodeConnections()).toHaveCount(1);
});
test('should append manual trigger when adding action node from plus button', async ({ n8n }) => {
await n8n.canvas.clickCanvasPlusButton();
await n8n.canvas.nodeCreator.searchFor('n8n');
await n8n.canvas.nodeCreator.selectItem('n8n');
await n8n.canvas.nodeCreator.selectCategoryItem('Actions');
await n8n.canvas.nodeCreator.selectItem('Create a credential');
await n8n.page.keyboard.press('Escape');
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
});
});