mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
test: Migrate cypress tests batch 1 to playwright (#19569)
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { expect } from '@playwright/test';
|
||||
|
||||
import type { n8nPage } from '../pages/n8nPage';
|
||||
|
||||
export class CanvasComposer {
|
||||
@@ -37,4 +39,62 @@ export class CanvasComposer {
|
||||
await this.n8n.canvas.selectAll();
|
||||
await this.copySelectedNodesWithToast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch between editor and workflow history and back
|
||||
*/
|
||||
async switchBetweenEditorAndHistory(): Promise<void> {
|
||||
await this.n8n.page.getByTestId('workflow-history-button').click();
|
||||
await this.n8n.page.getByTestId('workflow-history-close-button').click();
|
||||
await this.n8n.page.waitForLoadState();
|
||||
await expect(this.n8n.canvas.getCanvasNodes().first()).toBeVisible();
|
||||
await expect(this.n8n.canvas.getCanvasNodes().last()).toBeVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch between editor and workflow list and back
|
||||
*/
|
||||
async switchBetweenEditorAndWorkflowList(): Promise<void> {
|
||||
await this.n8n.page.getByTestId('menu-item').first().click();
|
||||
await this.n8n.page.getByTestId('resources-list-item-workflow').first().click();
|
||||
await expect(this.n8n.canvas.getCanvasNodes().first()).toBeVisible();
|
||||
await expect(this.n8n.canvas.getCanvasNodes().last()).toBeVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Zoom in and validate that zoom functionality works
|
||||
*/
|
||||
async zoomInAndCheckNodes(): Promise<void> {
|
||||
await this.n8n.canvas.getCanvasNodes().first().waitFor();
|
||||
|
||||
const initialNodeSize = await this.n8n.page.evaluate(() => {
|
||||
const firstNode = document.querySelector('[data-test-id="canvas-node"]');
|
||||
if (!firstNode) {
|
||||
throw new Error('Canvas node not found during initial measurement');
|
||||
}
|
||||
return firstNode.getBoundingClientRect().width;
|
||||
});
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
await this.n8n.canvas.clickZoomInButton();
|
||||
}
|
||||
|
||||
const finalNodeSize = await this.n8n.page.evaluate(() => {
|
||||
const firstNode = document.querySelector('[data-test-id="canvas-node"]');
|
||||
if (!firstNode) {
|
||||
throw new Error('Canvas node not found during final measurement');
|
||||
}
|
||||
return firstNode.getBoundingClientRect().width;
|
||||
});
|
||||
|
||||
// Validate zoom increased node sizes by at least 50%
|
||||
const zoomWorking = finalNodeSize > initialNodeSize * 1.5;
|
||||
|
||||
if (!zoomWorking) {
|
||||
throw new Error(
|
||||
"Zoom functionality not working: nodes didn't scale properly. " +
|
||||
`Initial: ${initialNodeSize.toFixed(1)}px, Final: ${finalNodeSize.toFixed(1)}px`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import { expect } from '@playwright/test';
|
||||
|
||||
import type { n8nPage } from '../pages/n8nPage';
|
||||
|
||||
/**
|
||||
* A class for partial execution testing workflows that involve
|
||||
* complex multi-step scenarios across pages.
|
||||
*/
|
||||
export class PartialExecutionComposer {
|
||||
constructor(private readonly n8n: n8nPage) {}
|
||||
|
||||
/**
|
||||
* Sets up partial execution version 2 in localStorage
|
||||
* This enables the v2 partial execution feature
|
||||
*/
|
||||
async enablePartialExecutionV2(): Promise<void> {
|
||||
await this.n8n.page.evaluate(() => {
|
||||
window.localStorage.setItem('PartialExecution.version', '2');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a full workflow and verifies all nodes show success status
|
||||
* @param nodeNames - Array of node names to verify
|
||||
*/
|
||||
async executeFullWorkflowAndVerifySuccess(nodeNames: string[]): Promise<void> {
|
||||
await this.n8n.canvas.clickExecuteWorkflowButton();
|
||||
|
||||
// Verify all nodes show success status
|
||||
for (const nodeName of nodeNames) {
|
||||
await expect(this.n8n.canvas.getNodeSuccessStatusIndicator(nodeName)).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures output data from a node for later comparison
|
||||
* @param nodeName - The node to capture data from
|
||||
* @returns The captured text content
|
||||
*/
|
||||
async captureNodeOutputData(nodeName: string): Promise<string> {
|
||||
await this.n8n.canvas.openNode(nodeName);
|
||||
await this.n8n.ndv.outputPanel.getTable().waitFor();
|
||||
// Note: Using row 0 for tbody (equivalent to row 1 in Cypress which includes header)
|
||||
const cell = this.n8n.ndv.outputPanel.getTbodyCell(0, 0);
|
||||
await expect(cell).toHaveText(/.+/);
|
||||
const beforeText = await cell.textContent();
|
||||
await this.n8n.ndv.close();
|
||||
|
||||
return beforeText!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies a node parameter to trigger stale state
|
||||
* @param nodeName - The node to modify
|
||||
*/
|
||||
async modifyNodeToTriggerStaleState(nodeName: string): Promise<void> {
|
||||
await this.n8n.canvas.openNode(nodeName);
|
||||
await this.n8n.ndv.clickAssignmentCollectionDropArea();
|
||||
|
||||
// Verify stale node indicator appears after parameter change
|
||||
await expect(this.n8n.ndv.getStaleNodeIndicator()).toBeVisible();
|
||||
await this.n8n.ndv.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies node states after parameter change for partial execution v2
|
||||
* @param unchangedNodes - Nodes that should still show success
|
||||
* @param modifiedNodes - Nodes that should show warning (need re-execution)
|
||||
*/
|
||||
async verifyNodeStatesAfterChange(
|
||||
unchangedNodes: string[],
|
||||
modifiedNodes: string[],
|
||||
): Promise<void> {
|
||||
// Verify unchanged nodes still show success
|
||||
for (const nodeName of unchangedNodes) {
|
||||
await expect(this.n8n.canvas.getNodeSuccessStatusIndicator(nodeName)).toBeVisible();
|
||||
}
|
||||
|
||||
// Verify modified nodes show warning status
|
||||
for (const nodeName of modifiedNodes) {
|
||||
await expect(this.n8n.canvas.getNodeWarningStatusIndicator(nodeName)).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs partial execution on a node and verifies all nodes return to success
|
||||
* @param targetNodeName - The node to execute from
|
||||
* @param allNodeNames - All nodes that should show success after partial execution
|
||||
*/
|
||||
async performPartialExecutionAndVerifySuccess(
|
||||
targetNodeName: string,
|
||||
allNodeNames: string[],
|
||||
): Promise<void> {
|
||||
// Perform partial execution by clicking execute button on target node
|
||||
await this.n8n.canvas.executeNode(targetNodeName);
|
||||
|
||||
// Verify all nodes show success status after partial execution
|
||||
for (const nodeName of allNodeNames) {
|
||||
await expect(this.n8n.canvas.getNodeSuccessStatusIndicator(nodeName)).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a node for data verification (test should handle the assertion)
|
||||
* @param nodeName - The node to open for verification
|
||||
* @returns Promise that resolves when node is open and ready for verification
|
||||
*/
|
||||
async openNodeForDataVerification(nodeName: string): Promise<void> {
|
||||
await this.n8n.canvas.openNode(nodeName);
|
||||
await this.n8n.ndv.outputPanel.getTable().waitFor();
|
||||
}
|
||||
}
|
||||
@@ -62,4 +62,37 @@ export class WorkflowComposer {
|
||||
await this.n8n.canvas.importWorkflow(fileName, workflowName);
|
||||
return { workflowName };
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new workflow by importing from a URL
|
||||
* @param url - The URL to import the workflow from
|
||||
* @returns Promise that resolves when the import is complete
|
||||
*/
|
||||
async importWorkflowFromURL(url: string): Promise<void> {
|
||||
await this.n8n.workflows.clickAddWorkflowButton();
|
||||
await this.n8n.canvas.clickWorkflowMenu();
|
||||
await this.n8n.canvas.clickImportFromURL();
|
||||
await this.n8n.canvas.fillImportURLInput(url);
|
||||
await this.n8n.canvas.clickConfirmImportURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the import from URL dialog and then dismisses it by clicking outside
|
||||
*/
|
||||
async openAndDismissImportFromURLDialog(): Promise<void> {
|
||||
await this.n8n.workflows.clickAddWorkflowButton();
|
||||
await this.n8n.canvas.clickWorkflowMenu();
|
||||
await this.n8n.canvas.clickImportFromURL();
|
||||
await this.n8n.canvas.clickOutsideModal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the import from URL dialog and then cancels it
|
||||
*/
|
||||
async openAndCancelImportFromURLDialog(): Promise<void> {
|
||||
await this.n8n.workflows.clickAddWorkflowButton();
|
||||
await this.n8n.canvas.clickWorkflowMenu();
|
||||
await this.n8n.canvas.clickImportFromURL();
|
||||
await this.n8n.canvas.clickCancelImportURL();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ export class CanvasPage extends BasePage {
|
||||
|
||||
const [fileChooser] = await Promise.all([
|
||||
this.page.waitForEvent('filechooser'),
|
||||
this.clickByText('Import from File...'),
|
||||
this.clickByTestId('workflow-menu-item-import-from-file'),
|
||||
]);
|
||||
await fileChooser.setFiles(resolveFromRoot('workflows', fixtureKey));
|
||||
|
||||
@@ -237,6 +237,40 @@ export class CanvasPage extends BasePage {
|
||||
await this.page.getByTestId('inline-edit-input').press('Enter');
|
||||
}
|
||||
|
||||
// Import workflow locators
|
||||
getImportURLInput(): Locator {
|
||||
return this.page.getByTestId('workflow-url-import-input');
|
||||
}
|
||||
|
||||
// Import workflow actions
|
||||
async clickWorkflowMenu(): Promise<void> {
|
||||
await this.clickByTestId('workflow-menu');
|
||||
}
|
||||
|
||||
async clickImportFromURL(): Promise<void> {
|
||||
await this.clickByTestId('workflow-menu-item-import-from-url');
|
||||
}
|
||||
|
||||
async clickImportFromFile(): Promise<void> {
|
||||
await this.clickByTestId('workflow-menu-item-import-from-file');
|
||||
}
|
||||
|
||||
async fillImportURLInput(url: string): Promise<void> {
|
||||
await this.getImportURLInput().fill(url);
|
||||
}
|
||||
|
||||
async clickConfirmImportURL(): Promise<void> {
|
||||
await this.clickByTestId('confirm-workflow-import-url-button');
|
||||
}
|
||||
|
||||
async clickCancelImportURL(): Promise<void> {
|
||||
await this.clickByTestId('cancel-workflow-import-url-button');
|
||||
}
|
||||
|
||||
async clickOutsideModal(): Promise<void> {
|
||||
await this.page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
}
|
||||
|
||||
getWorkflowTags() {
|
||||
return this.page.getByTestId('workflow-tags').locator('.el-tag');
|
||||
}
|
||||
@@ -660,7 +694,46 @@ export class CanvasPage extends BasePage {
|
||||
await this.page.getByTestId('radio-button-executions').click();
|
||||
}
|
||||
|
||||
async clickZoomInButton(): Promise<void> {
|
||||
await this.clickByTestId('zoom-in-button');
|
||||
}
|
||||
|
||||
async clickZoomOutButton(): Promise<void> {
|
||||
await this.clickByTestId('zoom-out-button');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current zoom level of the canvas
|
||||
* @returns The current zoom/scale factor as a number
|
||||
*/
|
||||
async getCanvasZoomLevel(): Promise<number> {
|
||||
return await this.page.evaluate(() => {
|
||||
const canvasViewport = document.querySelector('.vue-flow__viewport');
|
||||
if (canvasViewport) {
|
||||
const transform = window.getComputedStyle(canvasViewport).transform;
|
||||
if (transform && transform !== 'none') {
|
||||
const matrix = transform.match(/matrix\(([^)]+)\)/);
|
||||
if (matrix) {
|
||||
const values = matrix[1].split(',').map((v) => v.trim());
|
||||
return parseFloat(values[0]); // First value is scaleX
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: return default zoom level
|
||||
return 1.0;
|
||||
});
|
||||
}
|
||||
|
||||
waitingForTriggerEvent() {
|
||||
return this.getExecuteWorkflowButton().getByText('Waiting for trigger event');
|
||||
}
|
||||
|
||||
getNodeSuccessStatusIndicator(nodeName: string): Locator {
|
||||
return this.nodeByName(nodeName).getByTestId('canvas-node-status-success');
|
||||
}
|
||||
|
||||
getNodeWarningStatusIndicator(nodeName: string): Locator {
|
||||
return this.nodeByName(nodeName).getByTestId('canvas-node-status-warning');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +166,14 @@ export class NodeDetailsViewPage extends BasePage {
|
||||
.getByTestId('assignment-collection-drop-area');
|
||||
}
|
||||
|
||||
getAssignmentCollectionDropArea() {
|
||||
return this.page.getByTestId('assignment-collection-drop-area');
|
||||
}
|
||||
|
||||
async clickAssignmentCollectionDropArea() {
|
||||
await this.getAssignmentCollectionDropArea().click();
|
||||
}
|
||||
|
||||
getAssignmentValue(paramName: string) {
|
||||
return this.page
|
||||
.getByTestId(`assignment-collection-${paramName}`)
|
||||
@@ -723,6 +731,18 @@ export class NodeDetailsViewPage extends BasePage {
|
||||
return this.page.getByTestId('node-run-info');
|
||||
}
|
||||
|
||||
getStaleNodeIndicator() {
|
||||
return this.page.getByTestId('node-run-info-stale');
|
||||
}
|
||||
|
||||
getExecuteStepButton() {
|
||||
return this.page.getByRole('button').filter({ hasText: 'Execute step' });
|
||||
}
|
||||
|
||||
async clickExecuteStep() {
|
||||
await this.getExecuteStepButton().click();
|
||||
}
|
||||
|
||||
async openSettings() {
|
||||
await this.page.getByTestId('tab-settings').click();
|
||||
}
|
||||
|
||||
@@ -265,4 +265,20 @@ export class NotificationsPage {
|
||||
// Silent fail
|
||||
}
|
||||
}
|
||||
|
||||
getErrorNotifications(): Locator {
|
||||
return this.page.locator('.el-notification:has(.el-notification--error)');
|
||||
}
|
||||
|
||||
getSuccessNotifications(): Locator {
|
||||
return this.page.locator('.el-notification:has(.el-notification--success)');
|
||||
}
|
||||
|
||||
getWarningNotifications(): Locator {
|
||||
return this.page.locator('.el-notification:has(.el-notification--warning)');
|
||||
}
|
||||
|
||||
getInfoNotifications(): Locator {
|
||||
return this.page.locator('.el-notification:has(.el-notification--info)');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import { WorkflowSharingModal } from './WorkflowSharingModal';
|
||||
import { WorkflowsPage } from './WorkflowsPage';
|
||||
import { CanvasComposer } from '../composables/CanvasComposer';
|
||||
import { CredentialsComposer } from '../composables/CredentialsComposer';
|
||||
import { PartialExecutionComposer } from '../composables/PartialExecutionComposer';
|
||||
import { ProjectComposer } from '../composables/ProjectComposer';
|
||||
import { TestEntryComposer } from '../composables/TestEntryComposer';
|
||||
import { WorkflowComposer } from '../composables/WorkflowComposer';
|
||||
@@ -68,6 +69,7 @@ export class n8nPage {
|
||||
readonly projectComposer: ProjectComposer;
|
||||
readonly canvasComposer: CanvasComposer;
|
||||
readonly credentialsComposer: CredentialsComposer;
|
||||
readonly partialExecutionComposer: PartialExecutionComposer;
|
||||
readonly start: TestEntryComposer;
|
||||
|
||||
// Helpers
|
||||
@@ -109,6 +111,7 @@ export class n8nPage {
|
||||
this.projectComposer = new ProjectComposer(this);
|
||||
this.canvasComposer = new CanvasComposer(this);
|
||||
this.credentialsComposer = new CredentialsComposer(this);
|
||||
this.partialExecutionComposer = new PartialExecutionComposer(this);
|
||||
this.start = new TestEntryComposer(this);
|
||||
|
||||
// Helpers
|
||||
|
||||
@@ -351,7 +351,7 @@ test.describe('Credentials', () => {
|
||||
const saveBtn = n8n.canvas.credentialModal.getSaveButton();
|
||||
await saveBtn.click();
|
||||
|
||||
const errorNotification = page.locator('.el-notification:has(.el-notification--error)');
|
||||
const errorNotification = n8n.notifications.getErrorNotifications();
|
||||
await expect(errorNotification).toBeVisible();
|
||||
await expect(n8n.canvas.credentialModal.getModal()).toBeVisible();
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { test, expect } from '../../fixtures/base';
|
||||
|
||||
const NOTIFICATIONS = {
|
||||
WORKFLOW_CREATED: 'Workflow successfully created',
|
||||
};
|
||||
|
||||
test.describe('Editor zoom should work after route changes', () => {
|
||||
test.beforeEach(async ({ n8n, api }) => {
|
||||
await api.enableFeature('debugInEditor');
|
||||
await api.enableFeature('workflowHistory');
|
||||
|
||||
await n8n.workflowComposer.createWorkflowFromJsonFile(
|
||||
'Lots_of_nodes.json',
|
||||
'Lots of nodes test',
|
||||
);
|
||||
await n8n.notifications.waitForNotificationAndClose(NOTIFICATIONS.WORKFLOW_CREATED);
|
||||
});
|
||||
|
||||
test('should maintain zoom functionality after switching between Editor and Workflow history and Workflow list', async ({
|
||||
n8n,
|
||||
}) => {
|
||||
const initialNodeCount = await n8n.canvas.getCanvasNodes().count();
|
||||
expect(initialNodeCount).toBeGreaterThan(0);
|
||||
|
||||
await n8n.canvasComposer.switchBetweenEditorAndHistory();
|
||||
await n8n.canvasComposer.zoomInAndCheckNodes();
|
||||
|
||||
await n8n.canvasComposer.switchBetweenEditorAndHistory();
|
||||
await n8n.canvasComposer.switchBetweenEditorAndHistory();
|
||||
await n8n.canvasComposer.zoomInAndCheckNodes();
|
||||
|
||||
await n8n.canvasComposer.switchBetweenEditorAndWorkflowList();
|
||||
await n8n.canvasComposer.zoomInAndCheckNodes();
|
||||
|
||||
await n8n.canvasComposer.switchBetweenEditorAndWorkflowList();
|
||||
await n8n.canvasComposer.switchBetweenEditorAndWorkflowList();
|
||||
await n8n.canvasComposer.zoomInAndCheckNodes();
|
||||
|
||||
await n8n.canvasComposer.switchBetweenEditorAndHistory();
|
||||
await n8n.canvasComposer.switchBetweenEditorAndWorkflowList();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
import { test, expect } from '../../fixtures/base';
|
||||
import onboardingWorkflow from '../../workflows/Onboarding_workflow.json';
|
||||
|
||||
test.describe('Import workflow', () => {
|
||||
test.describe('From URL', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.route('**/rest/workflows/from-url*', async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ data: onboardingWorkflow }),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should import workflow', async ({ n8n }) => {
|
||||
await n8n.navigate.toWorkflow('new');
|
||||
await n8n.page.waitForLoadState('load');
|
||||
|
||||
await n8n.canvas.clickWorkflowMenu();
|
||||
await n8n.canvas.clickImportFromURL();
|
||||
|
||||
await expect(n8n.canvas.getImportURLInput()).toBeVisible();
|
||||
|
||||
await n8n.canvas.fillImportURLInput('https://fakepage.com/workflow.json');
|
||||
await n8n.canvas.clickConfirmImportURL();
|
||||
|
||||
await n8n.canvas.clickZoomToFitButton();
|
||||
|
||||
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(4);
|
||||
|
||||
await expect(n8n.notifications.getErrorNotifications()).toHaveCount(0);
|
||||
await expect(n8n.notifications.getSuccessNotifications()).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('clicking outside modal should not show error toast', async ({ n8n }) => {
|
||||
await n8n.navigate.toWorkflow('new');
|
||||
await n8n.page.waitForLoadState('load');
|
||||
|
||||
await n8n.canvas.clickWorkflowMenu();
|
||||
await n8n.canvas.clickImportFromURL();
|
||||
|
||||
await n8n.canvas.clickOutsideModal();
|
||||
|
||||
await expect(n8n.notifications.getErrorNotifications()).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('canceling modal should not show error toast', async ({ n8n }) => {
|
||||
await n8n.navigate.toWorkflow('new');
|
||||
await n8n.page.waitForLoadState('load');
|
||||
|
||||
await n8n.canvas.clickWorkflowMenu();
|
||||
await n8n.canvas.clickImportFromURL();
|
||||
|
||||
await n8n.canvas.clickCancelImportURL();
|
||||
|
||||
await expect(n8n.notifications.getErrorNotifications()).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('From File', () => {
|
||||
test('should import workflow', async ({ n8n }) => {
|
||||
await n8n.navigate.toWorkflow('new');
|
||||
await n8n.page.waitForLoadState('load');
|
||||
|
||||
await n8n.canvas.importWorkflow(
|
||||
'Test_workflow-actions_paste-data.json',
|
||||
'Import Test Workflow',
|
||||
);
|
||||
|
||||
await n8n.page.waitForLoadState('load');
|
||||
|
||||
await n8n.canvas.clickZoomToFitButton();
|
||||
|
||||
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(5);
|
||||
|
||||
const connections = n8n.page.getByTestId('edge');
|
||||
await expect(connections).toHaveCount(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
import { test, expect } from '../../fixtures/base';
|
||||
|
||||
test.describe('Manual partial execution', () => {
|
||||
test('should not execute parent nodes with no run data', async ({ n8n }) => {
|
||||
await n8n.start.fromImportedWorkflow('manual-partial-execution.json');
|
||||
await n8n.canvas.clickZoomToFitButton();
|
||||
|
||||
await n8n.canvas.openNode('Edit Fields');
|
||||
|
||||
await n8n.ndv.clickExecuteStep();
|
||||
|
||||
await n8n.ndv.close();
|
||||
|
||||
await n8n.canvas.openNode('Webhook1');
|
||||
|
||||
await expect(n8n.ndv.getNodeRunSuccessIndicator()).toBeHidden();
|
||||
await expect(n8n.ndv.getNodeRunTooltipIndicator()).toBeHidden();
|
||||
await expect(n8n.ndv.outputPanel.getRunSelector()).toBeHidden();
|
||||
});
|
||||
|
||||
test.describe('partial execution v2', () => {
|
||||
test('should execute from the first dirty node up to the current node', async ({ n8n }) => {
|
||||
const nodeNames = ['A', 'B', 'C'];
|
||||
|
||||
await n8n.navigate.toWorkflow('new');
|
||||
await n8n.partialExecutionComposer.enablePartialExecutionV2();
|
||||
await n8n.start.fromImportedWorkflow('Test_workflow_partial_execution_v2.json');
|
||||
await n8n.canvas.clickZoomToFitButton();
|
||||
|
||||
await n8n.partialExecutionComposer.executeFullWorkflowAndVerifySuccess(nodeNames);
|
||||
|
||||
const beforeText = await n8n.partialExecutionComposer.captureNodeOutputData('A');
|
||||
|
||||
await n8n.partialExecutionComposer.modifyNodeToTriggerStaleState('B');
|
||||
|
||||
await n8n.partialExecutionComposer.verifyNodeStatesAfterChange(['A', 'C'], ['B']);
|
||||
|
||||
await n8n.partialExecutionComposer.performPartialExecutionAndVerifySuccess('C', nodeNames);
|
||||
|
||||
await n8n.partialExecutionComposer.openNodeForDataVerification('A');
|
||||
|
||||
await expect(n8n.ndv.outputPanel.getTbodyCell(0, 0)).toHaveText(beforeText);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -135,7 +135,7 @@ test.describe('Security Notifications', () => {
|
||||
await expect(notification).toContainText('More info');
|
||||
|
||||
// Verify warning styling
|
||||
await expect(notification.locator('.el-notification--warning')).toBeVisible();
|
||||
await expect(n8n.notifications.getWarningNotifications()).toBeVisible();
|
||||
|
||||
// Close the notification
|
||||
await n8n.notifications.closeNotificationByText('Critical update available');
|
||||
|
||||
913
packages/testing/playwright/workflows/Lots_of_nodes.json
Normal file
913
packages/testing/playwright/workflows/Lots_of_nodes.json
Normal file
@@ -0,0 +1,913 @@
|
||||
{
|
||||
"name": "Lots of nodes",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "369fe424-dd3b-4399-9de3-50bd4ce1f75b",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [860, 740]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "dce967a7-8c5e-43cc-ba2b-e0fb0c9cf14c",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1080, 740]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "df7a719e-b25a-43e3-b941-7091a7d9a1a8",
|
||||
"name": "Edit Fields",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [1300, 740]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "32968b79-6a8b-43ed-b884-eb906b597661",
|
||||
"name": "IF",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [1520, 740]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "e9a72745-6dbb-4be1-b286-aaa679b95e36",
|
||||
"name": "Code1",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1820, 80]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "f831d21b-c3a9-4bd8-9fc3-6daef12bd43f",
|
||||
"name": "Edit Fields1",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [2040, 80]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "6e6b2a4f-9e61-4245-8502-ca01e851fcbe",
|
||||
"name": "IF1",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [2260, 80]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "535b9786-ead9-44f9-bff2-ef2e019a4cf9",
|
||||
"name": "Code3",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [2560, -260]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "6a181d75-f2f2-4ad1-be3c-81ebe077ccc8",
|
||||
"name": "Edit Fields3",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [2780, -260]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "4b45828e-4e2b-4046-b9ae-24b373a81863",
|
||||
"name": "IF7",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [3000, -260]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "059534cb-820c-4fb7-933c-eeed2ae74f1c",
|
||||
"name": "Code7",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [3260, -400]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "4f5c0d94-b69d-4ad3-aa8f-f1dd5824ec4a",
|
||||
"name": "Edit Fields7",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [3480, -400]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "cd74f840-7b0f-425d-8ecd-e247a7d8abf5",
|
||||
"name": "IF8",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [3700, -400]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "3c97fd14-9c23-45e2-a1ac-934d743e9a01",
|
||||
"name": "Code8",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [3260, -80]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "9e7bd7e9-5142-4751-b132-735d27007d82",
|
||||
"name": "Edit Fields8",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [3480, -80]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "8d3968b6-16d4-4e03-9026-eeaf70b17805",
|
||||
"name": "IF9",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [3700, -80]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "141edef3-ea0f-4e90-9b6a-09f5d5551195",
|
||||
"name": "Code4",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [2560, 440]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "b5b93cd7-9448-4290-91b7-c3c8429925fd",
|
||||
"name": "Edit Fields4",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [2780, 440]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "79d2c11c-0378-4ff5-b166-ae1bf773f53a",
|
||||
"name": "IF14",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [3000, 440]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "8483e962-24e7-4495-9c8e-481481ebe897",
|
||||
"name": "Code13",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [3260, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "74dfb8f9-6d14-493e-97d5-729e1f44856b",
|
||||
"name": "Edit Fields13",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [3480, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "0c2e8e54-958d-4932-91b5-b23979460c97",
|
||||
"name": "IF15",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [3700, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "bfed29c6-c453-4850-8acf-7aa11b1d0d8e",
|
||||
"name": "Code14",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [3260, 620]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "d8415057-c597-40a9-95f6-bafbe3fafac0",
|
||||
"name": "Edit Fields14",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [3480, 620]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "51ed9040-bb6c-4f77-9740-74b54ac56a00",
|
||||
"name": "IF16",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [3700, 620]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "5864e701-eb16-4412-ae8b-be1f2a1f16a5",
|
||||
"name": "Code2",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1820, 1480]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "4b7de291-f1c7-4ae8-a545-81aaa2ebd1fb",
|
||||
"name": "Edit Fields2",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [2040, 1480]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "328aa16f-82ed-465e-b548-9436f21eb519",
|
||||
"name": "IF2",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [2260, 1480]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "90aaf0b0-57b6-4a08-b000-abb2956ba640",
|
||||
"name": "Code5",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [2560, 1140]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "7d327c87-da3b-4f4b-9f9a-51c9c622990d",
|
||||
"name": "Edit Fields5",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [2780, 1140]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "fa2a3b1b-53de-454e-a16d-e2bf62cb05ec",
|
||||
"name": "IF21",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [3000, 1140]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "8efaa5a3-982e-41b4-af6e-28e35c64093d",
|
||||
"name": "Code19",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [3260, 1000]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "987e27fd-778a-4562-85a9-369b1ec232de",
|
||||
"name": "Edit Fields19",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [3480, 1000]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "b3f4e9b3-9995-4019-9b0f-dadd64e036b4",
|
||||
"name": "IF22",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [3700, 1000]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "681c1b30-063d-4c1e-b550-942a9dd3eb9a",
|
||||
"name": "Code20",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [3260, 1320]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "024770b6-7bf4-44f6-9675-d4f7dc73d6ac",
|
||||
"name": "Edit Fields20",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [3480, 1320]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "24699015-3ccf-4ffa-b52f-8ba4c4853963",
|
||||
"name": "IF23",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [3700, 1320]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "f4b2d116-2fda-4a3a-9509-0e8c64e7796e",
|
||||
"name": "Code6",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [2560, 1840]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "535e5b12-6743-4c01-9fc5-e27b10421423",
|
||||
"name": "Edit Fields6",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [2780, 1840]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "3dcbecdf-686b-445f-9c77-2902d0dc1f56",
|
||||
"name": "IF28",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [3000, 1840]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "7223c6ef-664b-426a-8d08-eca1b34e6b23",
|
||||
"name": "Code25",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [3260, 1700]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "496414a6-384a-4f94-97ec-d2e5ad646f82",
|
||||
"name": "Edit Fields25",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [3480, 1700]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "82f9562d-e4a8-49f3-924d-983effb4b6c6",
|
||||
"name": "IF29",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [3700, 1700]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"id": "c91d4bc5-3c60-4c22-aa31-44e84e0816ec",
|
||||
"name": "Code26",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [3260, 2020]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "49b61f23-bf3f-474d-8bba-a3b7de6f6441",
|
||||
"name": "Edit Fields26",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [3480, 2020]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "1cad6ae3-1064-4f30-a9ec-502891868332",
|
||||
"name": "IF30",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [3700, 2020]
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"IF": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Code2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code3": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields3": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF7",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"IF1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Code4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"IF7": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code7",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Code8",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code7": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields7",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields7": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF8",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code8": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields8",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields8": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF9",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code4": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields4": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF14",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"IF14": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code13",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Code14",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code13": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields13",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields13": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF15",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code14": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields14",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields14": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF16",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code2": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields2": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"IF2": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code5",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Code6",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code5": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields5",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields5": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF21",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"IF21": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code19",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Code20",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code19": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields19",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields19": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF22",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code20": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields20",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields20": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF23",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code6": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields6",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields6": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF28",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"IF28": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code25",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Code26",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code25": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields25",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields25": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF29",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code26": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields26",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields26": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF30",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "d38289e0-49d3-4e1d-8e4b-46e4eb85a2c9",
|
||||
"id": "iKlx4AGIjCNJSu9M",
|
||||
"meta": {
|
||||
"instanceId": "8a47b83b4479b11330fdf21ccc96d4a8117035a968612e452b4c87bfd09c16c7"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
1003
packages/testing/playwright/workflows/Onboarding_workflow.json
Normal file
1003
packages/testing/playwright/workflows/Onboarding_workflow.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [{}]
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"typeVersion": 1.2,
|
||||
"position": [0, 0],
|
||||
"id": "dcc1c5e1-c6c1-45f8-80d5-65c88d66d56e",
|
||||
"name": "A"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "3d8f0810-84f0-41ce-a81b-0e7f04fd88cb",
|
||||
"name": "",
|
||||
"value": "",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [220, 0],
|
||||
"id": "097ffa30-d37b-4de6-bd5c-ccd945f31df1",
|
||||
"name": "B"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [440, 0],
|
||||
"id": "dc44e635-916f-4f76-a745-1add5762f730",
|
||||
"name": "C"
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"A": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "B",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"B": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "C",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"meta": {
|
||||
"instanceId": "b0d9447cff9c96796e4ac4f00fcd899b03cfac3ab3d4f748ae686d34881eae0c"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "f4467143-fdb9-46fa-8020-6417cc5eea7d",
|
||||
"name": "Edit Fields",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.3,
|
||||
"position": [1140, 260]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"path": "30ff316d-405f-4288-a0ac-e713546c9d4e",
|
||||
"options": {}
|
||||
},
|
||||
"id": "4760aafb-5d56-4633-99d3-7a97c576a216",
|
||||
"name": "Webhook1",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2,
|
||||
"position": [680, 340],
|
||||
"webhookId": "30ff316d-405f-4288-a0ac-e713546c9d4e"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"articleId": "123",
|
||||
"additionalFields": {}
|
||||
},
|
||||
"id": "8c811eca-8978-44d9-b8f7-ef2c7725784c",
|
||||
"name": "Hacker News",
|
||||
"type": "n8n-nodes-base.hackerNews",
|
||||
"typeVersion": 1,
|
||||
"position": [920, 260]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"path": "4a3398e4-1388-4e10-9d21-add90b804955",
|
||||
"options": {}
|
||||
},
|
||||
"id": "1c2c2d06-45c9-4712-9fa0-c655bef8d0e5",
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2,
|
||||
"position": [680, 180],
|
||||
"webhookId": "4a3398e4-1388-4e10-9d21-add90b804955"
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Webhook1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Hacker News",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Hacker News": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Hacker News",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {
|
||||
"Webhook": [
|
||||
{
|
||||
"name": "First item",
|
||||
"code": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user