mirror of
https://github.com/n8n-io/n8n.git
synced 2025-11-20 17:46:34 +00:00
450 lines
18 KiB
TypeScript
450 lines
18 KiB
TypeScript
import fs from 'fs';
|
|
|
|
import {
|
|
MANUAL_TRIGGER_NODE_NAME,
|
|
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
|
SWITCH_NODE_NAME,
|
|
EDIT_FIELDS_SET_NODE_NAME,
|
|
MERGE_NODE_NAME,
|
|
CODE_NODE_NAME,
|
|
SCHEDULE_TRIGGER_NODE_NAME,
|
|
CODE_NODE_DISPLAY_NAME,
|
|
} from '../../config/constants';
|
|
import { test, expect } from '../../fixtures/base';
|
|
import type { n8nPage } from '../../pages/n8nPage';
|
|
import { resolveFromRoot } from '../../utils/path-helper';
|
|
|
|
const DEFAULT_ZOOM_FACTOR = 1;
|
|
const ZOOM_IN_X1_FACTOR = 1.25; // Expected zoom after 1 zoom-in click (125%)
|
|
const ZOOM_IN_X2_FACTOR = 1.5625; // Expected zoom after 2 zoom-in clicks (156.25%)
|
|
const ZOOM_OUT_X1_FACTOR = 0.8; // Expected zoom after 1 zoom-out click (80%)
|
|
const ZOOM_OUT_X2_FACTOR = 0.64; // Expected zoom after 2 zoom-out clicks (64%)
|
|
const ZOOM_TOLERANCE = 0.2; // Acceptable variance for floating-point zoom comparisons
|
|
|
|
test.describe('Canvas Node Manipulation and Navigation', () => {
|
|
test.beforeEach(async ({ n8n }) => {
|
|
await n8n.start.fromBlankCanvas();
|
|
});
|
|
|
|
test('should add switch node and test connections', async ({ n8n }) => {
|
|
const desiredOutputs = 4;
|
|
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
|
await n8n.canvas.addNode(SWITCH_NODE_NAME);
|
|
|
|
for (let i = 0; i < desiredOutputs; i++) {
|
|
await n8n.page.getByText('Add Routing Rule').click();
|
|
}
|
|
|
|
await n8n.ndv.close();
|
|
|
|
for (let i = 0; i < desiredOutputs; i++) {
|
|
await n8n.canvas.clickNodePlusEndpoint(SWITCH_NODE_NAME);
|
|
await expect(n8n.canvas.nodeCreatorSearchBar()).toBeVisible();
|
|
await n8n.canvas.fillNodeCreatorSearchBar(EDIT_FIELDS_SET_NODE_NAME);
|
|
await n8n.canvas.clickNodeCreatorItemName(EDIT_FIELDS_SET_NODE_NAME);
|
|
await n8n.page.keyboard.press('Escape');
|
|
await n8n.canvas.clickZoomToFitButton();
|
|
}
|
|
|
|
await n8n.canvas.canvasPane().click({ position: { x: 10, y: 10 } });
|
|
|
|
await n8n.canvas.clickNodePlusEndpoint('Edit Fields3');
|
|
await n8n.canvas.fillNodeCreatorSearchBar(SWITCH_NODE_NAME);
|
|
await n8n.canvas.clickNodeCreatorItemName(SWITCH_NODE_NAME);
|
|
await n8n.page.keyboard.press('Escape');
|
|
|
|
await n8n.canvas.saveWorkflow();
|
|
await expect(n8n.canvas.getWorkflowSaveButton()).toContainText('Saved');
|
|
|
|
await n8n.canvasComposer.reloadAndWaitForCanvas();
|
|
|
|
await expect(
|
|
n8n.canvas.connectionBetweenNodes('Edit Fields3', `${SWITCH_NODE_NAME}1`),
|
|
).toBeAttached();
|
|
|
|
const editFieldsNodes = ['Edit Fields', 'Edit Fields1', 'Edit Fields2', 'Edit Fields3'];
|
|
for (const nodeName of editFieldsNodes) {
|
|
await expect(n8n.canvas.connectionBetweenNodes(SWITCH_NODE_NAME, nodeName)).toBeAttached();
|
|
}
|
|
});
|
|
|
|
test('should add merge node and test connections', async ({ n8n }) => {
|
|
const editFieldsNodeCount = 2;
|
|
|
|
const checkConnections = async () => {
|
|
await expect(
|
|
n8n.canvas.connectionBetweenNodes(MANUAL_TRIGGER_NODE_DISPLAY_NAME, 'Edit Fields1').first(),
|
|
).toBeAttached();
|
|
await expect(
|
|
n8n.canvas.connectionBetweenNodes('Edit Fields', MERGE_NODE_NAME).first(),
|
|
).toBeAttached();
|
|
await expect(
|
|
n8n.canvas.connectionBetweenNodes('Edit Fields1', MERGE_NODE_NAME).first(),
|
|
).toBeAttached();
|
|
};
|
|
|
|
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
|
await n8n.canvas.nodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
|
|
|
for (let i = 0; i < editFieldsNodeCount; i++) {
|
|
await n8n.canvas.addNode(EDIT_FIELDS_SET_NODE_NAME, { closeNDV: true });
|
|
await n8n.canvas.canvasPane().click({
|
|
position: { x: (i + 1) * 200, y: (i + 1) * 200 },
|
|
// eslint-disable-next-line playwright/no-force-option
|
|
force: true,
|
|
});
|
|
}
|
|
await n8n.canvas.clickZoomToFitButton();
|
|
|
|
await n8n.canvas.addNode(MERGE_NODE_NAME, { closeNDV: true });
|
|
await n8n.canvas.clickZoomToFitButton();
|
|
|
|
await n8n.canvas.connectNodesByDrag(MANUAL_TRIGGER_NODE_DISPLAY_NAME, 'Edit Fields1', 0, 0);
|
|
await expect(
|
|
n8n.canvas.connectionBetweenNodes(MANUAL_TRIGGER_NODE_DISPLAY_NAME, 'Edit Fields1').first(),
|
|
).toBeAttached();
|
|
|
|
await n8n.canvas.connectNodesByDrag('Edit Fields', MERGE_NODE_NAME, 0, 0);
|
|
await expect(
|
|
n8n.canvas.connectionBetweenNodes('Edit Fields', MERGE_NODE_NAME).first(),
|
|
).toBeAttached();
|
|
|
|
await n8n.canvas.connectNodesByDrag('Edit Fields1', MERGE_NODE_NAME, 0, 1);
|
|
await expect(
|
|
n8n.canvas.connectionBetweenNodes('Edit Fields1', MERGE_NODE_NAME).first(),
|
|
).toBeAttached();
|
|
|
|
await n8n.canvas.saveWorkflow();
|
|
await expect(n8n.canvas.getWorkflowSaveButton()).toContainText('Saved');
|
|
|
|
await n8n.canvasComposer.reloadAndWaitForCanvas();
|
|
|
|
await checkConnections();
|
|
|
|
await n8n.canvas.clickExecuteWorkflowButton();
|
|
await expect(n8n.canvas.stopExecutionButton()).toBeHidden();
|
|
|
|
await n8n.canvasComposer.reloadAndWaitForCanvas();
|
|
|
|
await checkConnections();
|
|
|
|
await n8n.canvas.clickExecuteWorkflowButton();
|
|
await expect(n8n.canvas.stopExecutionButton()).toBeHidden();
|
|
|
|
await expect(
|
|
n8n.canvas.getConnectionLabelBetweenNodes('Edit Fields1', MERGE_NODE_NAME).first(),
|
|
).toContainText('2 items');
|
|
});
|
|
|
|
test('should add nodes and check execution success', async ({ n8n }) => {
|
|
const nodeCount = 3;
|
|
|
|
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
|
await n8n.canvas.nodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
|
|
|
for (let i = 0; i < nodeCount; i++) {
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
}
|
|
await n8n.canvas.clickZoomToFitButton();
|
|
|
|
await n8n.canvas.clickExecuteWorkflowButton();
|
|
await expect(n8n.canvas.stopExecutionButton()).toBeHidden();
|
|
|
|
await expect(n8n.canvas.getSuccessEdges()).toHaveCount(nodeCount);
|
|
await expect(n8n.canvas.getAllNodeSuccessIndicators()).toHaveCount(nodeCount + 1);
|
|
await expect(n8n.canvas.getCanvasHandlePlusWrapper()).toHaveAttribute(
|
|
'data-plus-type',
|
|
'success',
|
|
);
|
|
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
await n8n.canvas.clickZoomToFitButton();
|
|
|
|
await expect(n8n.canvas.getCanvasHandlePlus()).not.toHaveAttribute('data-plus-type', 'success');
|
|
|
|
await expect(n8n.canvas.getSuccessEdges()).toHaveCount(nodeCount + 1);
|
|
await expect(n8n.canvas.getAllNodeSuccessIndicators()).toHaveCount(nodeCount + 1);
|
|
});
|
|
|
|
test('should delete node using context menu', async ({ n8n }) => {
|
|
await n8n.canvas.addNode(SCHEDULE_TRIGGER_NODE_NAME, { closeNDV: true });
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
await n8n.canvas.clickZoomToFitButton();
|
|
|
|
await n8n.canvas.deleteNodeFromContextMenu(CODE_NODE_DISPLAY_NAME);
|
|
|
|
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(1);
|
|
await expect(n8n.canvas.nodeConnections()).toHaveCount(0);
|
|
});
|
|
|
|
test('should delete node using keyboard shortcut', async ({ n8n }) => {
|
|
await n8n.canvas.addNode(SCHEDULE_TRIGGER_NODE_NAME, { closeNDV: true });
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
await n8n.canvas.nodeByName(CODE_NODE_DISPLAY_NAME).click();
|
|
await n8n.page.keyboard.press('Backspace');
|
|
|
|
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(1);
|
|
await expect(n8n.canvas.nodeConnections()).toHaveCount(0);
|
|
});
|
|
|
|
test('should delete node between two connected nodes', async ({ n8n }) => {
|
|
await n8n.canvas.addNode(SCHEDULE_TRIGGER_NODE_NAME, { closeNDV: true });
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
await n8n.canvas.addNode(EDIT_FIELDS_SET_NODE_NAME, { closeNDV: true });
|
|
|
|
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(3);
|
|
await expect(n8n.canvas.nodeConnections()).toHaveCount(2);
|
|
|
|
await n8n.canvas.nodeByName(CODE_NODE_DISPLAY_NAME).click();
|
|
await n8n.canvas.clickZoomToFitButton();
|
|
await n8n.page.keyboard.press('Backspace');
|
|
|
|
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
|
|
await expect(n8n.canvas.nodeConnections()).toHaveCount(1);
|
|
});
|
|
|
|
test('should delete multiple nodes (context menu or shortcut)', async ({ n8n }) => {
|
|
await n8n.canvas.addNode(SCHEDULE_TRIGGER_NODE_NAME, { closeNDV: true });
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
await n8n.canvas.hitDeleteAllNodes();
|
|
|
|
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(0);
|
|
|
|
await n8n.canvas.addNode(SCHEDULE_TRIGGER_NODE_NAME, { closeNDV: true });
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
|
|
await n8n.canvas.rightClickCanvas();
|
|
await n8n.canvas.getContextMenuItem('select_all').click();
|
|
await n8n.canvas.rightClickCanvas();
|
|
await n8n.canvas.getContextMenuItem('delete').click();
|
|
|
|
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(0);
|
|
});
|
|
|
|
test('should move node', async ({ n8n }) => {
|
|
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
|
await n8n.canvas.nodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
|
|
await n8n.canvas.clickZoomToFitButton();
|
|
|
|
const pos1 = await n8n.canvas.getNodePosition(CODE_NODE_DISPLAY_NAME);
|
|
await n8n.canvas.dragNodeToRelativePosition(CODE_NODE_DISPLAY_NAME, 50, 150);
|
|
const pos2 = await n8n.canvas.getNodePosition(CODE_NODE_DISPLAY_NAME);
|
|
|
|
expect(pos2.x).toBeGreaterThan(pos1.x);
|
|
expect(pos2.y).toBeGreaterThan(pos1.y);
|
|
});
|
|
});
|
|
|
|
test.describe('Canvas Zoom Functionality', () => {
|
|
test.beforeEach(async ({ n8n }) => {
|
|
await n8n.start.fromBlankCanvas();
|
|
});
|
|
|
|
const expectZoomLevel = async (n8n: n8nPage, expectedFactor: number) => {
|
|
const actual = await n8n.canvas.getCanvasZoomLevel();
|
|
expect(actual).toBeGreaterThanOrEqual(expectedFactor - ZOOM_TOLERANCE);
|
|
expect(actual).toBeLessThanOrEqual(expectedFactor + ZOOM_TOLERANCE);
|
|
};
|
|
|
|
test('should zoom in', async ({ n8n }) => {
|
|
await expect(n8n.canvas.getZoomInButton()).toBeVisible();
|
|
|
|
const initialZoom = await n8n.canvas.getCanvasZoomLevel();
|
|
|
|
await n8n.canvas.clickZoomInButton();
|
|
await expectZoomLevel(n8n, initialZoom * ZOOM_IN_X1_FACTOR);
|
|
|
|
await n8n.canvas.clickZoomInButton();
|
|
await expectZoomLevel(n8n, initialZoom * ZOOM_IN_X2_FACTOR);
|
|
});
|
|
|
|
test('should zoom out', async ({ n8n }) => {
|
|
await n8n.canvas.clickZoomOutButton();
|
|
await expectZoomLevel(n8n, ZOOM_OUT_X1_FACTOR);
|
|
|
|
await n8n.canvas.clickZoomOutButton();
|
|
const finalZoom = await n8n.canvas.getCanvasZoomLevel();
|
|
expect(finalZoom).toBeGreaterThanOrEqual(ZOOM_OUT_X2_FACTOR - ZOOM_TOLERANCE);
|
|
expect(finalZoom).toBeLessThanOrEqual(ZOOM_OUT_X2_FACTOR + ZOOM_TOLERANCE);
|
|
});
|
|
|
|
test('should reset zoom', async ({ n8n }) => {
|
|
await expect(n8n.canvas.getResetZoomButton()).not.toBeAttached();
|
|
|
|
await n8n.canvas.clickZoomInButton();
|
|
|
|
await expect(n8n.canvas.getResetZoomButton()).toBeVisible();
|
|
await n8n.canvas.getResetZoomButton().click();
|
|
|
|
await expectZoomLevel(n8n, DEFAULT_ZOOM_FACTOR);
|
|
});
|
|
|
|
test('should zoom to fit', async ({ n8n }) => {
|
|
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
|
|
await n8n.canvas.clickZoomOutButton();
|
|
await n8n.canvas.clickZoomOutButton();
|
|
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
|
|
await n8n.canvas.clickZoomInButton();
|
|
await n8n.canvas.clickZoomInButton();
|
|
|
|
await expect(n8n.canvas.getCanvasNodes().last()).not.toBeInViewport();
|
|
|
|
await n8n.canvas.clickZoomToFitButton();
|
|
|
|
await expect(n8n.canvas.getCanvasNodes().last()).toBeInViewport();
|
|
});
|
|
|
|
test('should disable node (context menu or shortcut)', async ({ n8n }) => {
|
|
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
|
await n8n.canvas.nodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
await n8n.canvas.getCanvasNodes().last().click();
|
|
await n8n.page.keyboard.press('d');
|
|
await expect(n8n.canvas.disabledNodes()).toHaveCount(1);
|
|
|
|
await n8n.canvas.disableNodeFromContextMenu(CODE_NODE_DISPLAY_NAME);
|
|
await expect(n8n.canvas.disabledNodes()).toHaveCount(0);
|
|
});
|
|
|
|
test('should disable multiple nodes (context menu or shortcut)', async ({ n8n }) => {
|
|
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
|
await n8n.canvas.nodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript' });
|
|
await n8n.page.keyboard.press('Escape');
|
|
await n8n.page.keyboard.press('Escape');
|
|
|
|
await n8n.canvas.selectAll();
|
|
await n8n.page.keyboard.press('d');
|
|
await expect(n8n.canvas.disabledNodes()).toHaveCount(2);
|
|
await n8n.page.keyboard.press('d');
|
|
await expect(n8n.canvas.disabledNodes()).toHaveCount(0);
|
|
await n8n.canvas.deselectAll();
|
|
await n8n.canvas.nodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
|
await n8n.page.keyboard.press('d');
|
|
await expect(n8n.canvas.disabledNodes()).toHaveCount(1);
|
|
await n8n.canvas.selectAll();
|
|
await n8n.page.keyboard.press('d');
|
|
await expect(n8n.canvas.disabledNodes()).toHaveCount(2);
|
|
|
|
await n8n.canvas.selectAll();
|
|
await n8n.canvas.rightClickCanvas();
|
|
await n8n.canvas.getContextMenuItem('toggle_activation').click();
|
|
await expect(n8n.canvas.disabledNodes()).toHaveCount(0);
|
|
await n8n.canvas.rightClickCanvas();
|
|
await n8n.canvas.getContextMenuItem('toggle_activation').click();
|
|
await expect(n8n.canvas.disabledNodes()).toHaveCount(2);
|
|
await n8n.canvas.deselectAll();
|
|
await n8n.canvas.nodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
|
await n8n.canvas.rightClickCanvas();
|
|
await n8n.canvas.getContextMenuItem('toggle_activation').click();
|
|
await expect(n8n.canvas.disabledNodes()).toHaveCount(1);
|
|
await n8n.canvas.selectAll();
|
|
await n8n.canvas.rightClickCanvas();
|
|
await n8n.canvas.getContextMenuItem('toggle_activation').click();
|
|
await expect(n8n.canvas.disabledNodes()).toHaveCount(2);
|
|
});
|
|
|
|
test('should rename node (context menu or shortcut)', async ({ n8n }) => {
|
|
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
await n8n.canvasComposer.renameNodeViaShortcut(CODE_NODE_DISPLAY_NAME, 'Something else');
|
|
await expect(n8n.canvas.nodeByName('Something else')).toBeAttached();
|
|
|
|
await n8n.canvas.rightClickNode('Something else');
|
|
await n8n.canvas.getContextMenuItem('rename').click();
|
|
await expect(n8n.canvas.getRenamePrompt()).toBeVisible();
|
|
await n8n.page.keyboard.type('Something different');
|
|
await n8n.page.keyboard.press('Enter');
|
|
await expect(n8n.canvas.nodeByName('Something different')).toBeAttached();
|
|
});
|
|
|
|
test('should not allow empty strings for node names', async ({ n8n }) => {
|
|
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
await n8n.canvas.getCanvasNodes().last().click();
|
|
await n8n.page.keyboard.press('F2');
|
|
await expect(n8n.canvas.getRenamePrompt()).toBeVisible();
|
|
await n8n.page.keyboard.press('Backspace');
|
|
await n8n.page.keyboard.press('Enter');
|
|
await expect(n8n.canvas.getRenamePrompt()).toContainText('Invalid Name');
|
|
});
|
|
|
|
test('should duplicate nodes (context menu or shortcut)', async ({ n8n }) => {
|
|
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
|
await n8n.canvas.nodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
await n8n.canvas.duplicateNode(CODE_NODE_DISPLAY_NAME);
|
|
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(3);
|
|
await expect(n8n.canvas.nodeConnections()).toHaveCount(1);
|
|
|
|
await n8n.canvas.selectAll();
|
|
await n8n.page.keyboard.press('ControlOrMeta+d');
|
|
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(5);
|
|
});
|
|
|
|
test('should preserve connections after rename & node-view switch', async ({ n8n }) => {
|
|
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
|
await n8n.canvas.clickExecuteWorkflowButton();
|
|
|
|
await expect(
|
|
n8n.notifications.getNotificationByTitleOrContent('Workflow executed successfully'),
|
|
).toBeVisible();
|
|
await n8n.notifications.closeNotificationByText('Workflow executed successfully');
|
|
|
|
await n8n.canvas.openExecutions();
|
|
await expect(n8n.executions.getSuccessfulExecutionItems()).toHaveCount(1);
|
|
|
|
await n8n.canvas.clickEditorTab();
|
|
|
|
await n8n.canvas.openExecutions();
|
|
await expect(n8n.executions.getSuccessfulExecutionItems()).toHaveCount(1);
|
|
|
|
await n8n.canvas.clickEditorTab();
|
|
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
|
|
|
|
await n8n.canvasComposer.renameNodeViaShortcut(CODE_NODE_DISPLAY_NAME, 'Something else');
|
|
await expect(n8n.canvas.nodeByName('Something else')).toBeAttached();
|
|
|
|
await n8n.canvas.saveWorkflow();
|
|
await expect(n8n.canvas.getWorkflowSaveButton()).toContainText('Saved');
|
|
|
|
await n8n.canvasComposer.reloadAndWaitForCanvas();
|
|
|
|
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
|
|
await expect(n8n.canvas.nodeConnections()).toHaveCount(1);
|
|
});
|
|
|
|
test('should remove unknown credentials on pasting workflow', async ({ n8n }) => {
|
|
const workflowJson = fs.readFileSync(
|
|
resolveFromRoot('workflows', 'workflow-with-unknown-credentials.json'),
|
|
'utf-8',
|
|
);
|
|
|
|
await n8n.canvas.canvasPane().click();
|
|
|
|
await n8n.clipboard.paste(workflowJson);
|
|
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
|
|
|
|
await n8n.canvas.nodeByName('n8n').hover();
|
|
await n8n.canvas.nodeByName('n8n').getByTestId('overflow-node-button').click();
|
|
await n8n.page.getByTestId('context-menu-item-open').click();
|
|
|
|
await expect(n8n.ndv.getNodesWithIssues()).toHaveCount(1);
|
|
});
|
|
|
|
test.skip('should open and close the about modal on keyboard shortcut', async ({ n8n }) => {
|
|
await n8n.sideBar.openAboutModalViaShortcut();
|
|
await expect(n8n.sideBar.getAboutModal()).toBeVisible();
|
|
await n8n.sideBar.closeAboutModal();
|
|
});
|
|
});
|