mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
test: 14-mapping tests migration (#18858)
This commit is contained in:
@@ -1,382 +0,0 @@
|
||||
import {
|
||||
MANUAL_TRIGGER_NODE_NAME,
|
||||
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
||||
SCHEDULE_TRIGGER_NODE_NAME,
|
||||
} from './../constants';
|
||||
import { WorkflowPage, NDV } from '../pages';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
const ndv = new NDV();
|
||||
|
||||
describe('Data mapping', () => {
|
||||
beforeEach(() => {
|
||||
workflowPage.actions.visit();
|
||||
});
|
||||
|
||||
it('maps expressions from table header', () => {
|
||||
cy.fixture('Test_workflow-actions_paste-data.json').then((data) => {
|
||||
cy.get('body').paste(JSON.stringify(data));
|
||||
});
|
||||
workflowPage.actions.zoomToFit();
|
||||
|
||||
workflowPage.actions.openNode('Set');
|
||||
ndv.actions.executePrevious();
|
||||
ndv.actions.switchInputMode('Table');
|
||||
ndv.getters.inputDataContainer().get('table', { timeout: 10000 }).should('exist');
|
||||
|
||||
ndv.getters.nodeParameters().find('input[placeholder*="Add Value"]').click();
|
||||
getVisibleSelect().find('li:nth-child(3)').should('have.text', 'String').click();
|
||||
ndv.getters
|
||||
.parameterInput('name')
|
||||
.should('have.length', 1)
|
||||
.find('input')
|
||||
.should('have.value', 'propertyName');
|
||||
ndv.getters
|
||||
.parameterInput('value')
|
||||
.should('have.length', 1)
|
||||
.find('input')
|
||||
.should('have.value', '');
|
||||
|
||||
ndv.actions.mapDataFromHeader(1, 'value');
|
||||
ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $json.timestamp }}');
|
||||
ndv.getters.inlineExpressionEditorInput().type('{esc}');
|
||||
ndv.getters
|
||||
.parameterExpressionPreview('value')
|
||||
.should('include.text', new Date().getFullYear());
|
||||
|
||||
ndv.actions.mapDataFromHeader(2, 'value');
|
||||
ndv.getters
|
||||
.inlineExpressionEditorInput()
|
||||
.should('have.text', "{{ $json['Readable date'] }}{{ $json.timestamp }}");
|
||||
});
|
||||
|
||||
it('maps expressions from table json, and resolves value based on hover', () => {
|
||||
cy.fixture('Test_workflow_3.json').then((data) => {
|
||||
cy.get('body').paste(JSON.stringify(data));
|
||||
});
|
||||
workflowPage.actions.zoomToFit();
|
||||
|
||||
workflowPage.actions.openNode('Set');
|
||||
ndv.actions.switchInputMode('Table');
|
||||
ndv.getters.inputDataContainer().get('table', { timeout: 10000 }).should('exist');
|
||||
|
||||
ndv.getters
|
||||
.parameterInput('name')
|
||||
.should('have.length', 1)
|
||||
.find('input')
|
||||
.should('have.value', 'other');
|
||||
ndv.getters
|
||||
.parameterInput('value')
|
||||
.should('have.length', 1)
|
||||
.find('input')
|
||||
.should('have.value', '');
|
||||
|
||||
ndv.getters
|
||||
.inputTbodyCell(1, 0)
|
||||
.find('span')
|
||||
.contains('count')
|
||||
.trigger('mousedown', { force: true, button: 0, buttons: 1 });
|
||||
ndv.actions.mapToParameter('value');
|
||||
|
||||
ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $json.input[0].count }}');
|
||||
ndv.getters.inlineExpressionEditorInput().type('{esc}');
|
||||
ndv.getters.parameterExpressionPreview('value').should('include.text', '0');
|
||||
|
||||
ndv.getters.inputTbodyCell(1, 0).realHover();
|
||||
ndv.getters
|
||||
.parameterExpressionPreview('value')
|
||||
.should('include.text', '0')
|
||||
.invoke('css', 'color')
|
||||
.should('equal', 'rgb(113, 116, 122)');
|
||||
|
||||
ndv.getters.inputTbodyCell(2, 0).realHover();
|
||||
ndv.getters
|
||||
.parameterExpressionPreview('value')
|
||||
.should('include.text', '1')
|
||||
.invoke('css', 'color')
|
||||
.should('equal', 'rgb(113, 116, 122)');
|
||||
|
||||
ndv.actions.execute();
|
||||
|
||||
ndv.getters.outputTbodyCell(1, 0).realHover();
|
||||
ndv.getters
|
||||
.parameterExpressionPreview('value')
|
||||
.should('include.text', '0')
|
||||
.invoke('css', 'color')
|
||||
.should('equal', 'rgb(113, 116, 122)'); // todo update color
|
||||
|
||||
ndv.getters.outputTbodyCell(2, 0).realHover();
|
||||
ndv.getters
|
||||
.parameterExpressionPreview('value')
|
||||
.should('include.text', '1')
|
||||
.invoke('css', 'color')
|
||||
.should('equal', 'rgb(113, 116, 122)');
|
||||
});
|
||||
|
||||
it('maps expressions from json view', () => {
|
||||
// ADO-3063 - followup to make this viewport global
|
||||
cy.viewport('macbook-16');
|
||||
cy.fixture('Test_workflow_3.json').then((data) => {
|
||||
cy.get('body').paste(JSON.stringify(data));
|
||||
});
|
||||
workflowPage.actions.zoomToFit();
|
||||
|
||||
workflowPage.actions.openNode('Set');
|
||||
ndv.actions.switchInputMode('JSON');
|
||||
|
||||
ndv.getters.inputDataContainer().should('exist');
|
||||
|
||||
ndv.getters
|
||||
.inputDataContainer()
|
||||
.find('.json-data')
|
||||
.should(
|
||||
'have.text',
|
||||
'[{"input": [{"count": 0,"with space": "!!","with.dot": "!!","with"quotes": "!!"}]},{"input": [{"count": 1}]}]',
|
||||
);
|
||||
|
||||
ndv.getters.inputDataContainer().find('span').contains('"count"').realMouseDown();
|
||||
|
||||
ndv.actions.mapToParameter('value');
|
||||
ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $json.input[0].count }}');
|
||||
ndv.getters.inlineExpressionEditorInput().type('{esc}');
|
||||
ndv.getters.parameterExpressionPreview('value').should('include.text', '0');
|
||||
|
||||
ndv.getters
|
||||
.inputDataContainer()
|
||||
.find('.json-data')
|
||||
.find('span')
|
||||
.contains('"input"')
|
||||
.realMouseDown();
|
||||
|
||||
ndv.actions.mapToParameter('value');
|
||||
ndv.getters
|
||||
.inlineExpressionEditorInput()
|
||||
.should('have.text', '{{ $json.input }}{{ $json.input[0].count }}');
|
||||
ndv.getters.inlineExpressionEditorInput().type('{esc}');
|
||||
ndv.actions.validateExpressionPreview('value', '[object Object]0');
|
||||
});
|
||||
|
||||
it('maps expressions from schema view', () => {
|
||||
cy.fixture('Test_workflow_3.json').then((data) => {
|
||||
cy.get('body').paste(JSON.stringify(data));
|
||||
});
|
||||
workflowPage.actions.zoomToFit();
|
||||
|
||||
workflowPage.actions.openNode('Set');
|
||||
ndv.actions.clearParameterInput('value');
|
||||
cy.get('body').type('{esc}');
|
||||
|
||||
ndv.getters.inputDataContainer().should('exist').find('span').contains('count').realMouseDown();
|
||||
|
||||
ndv.actions.mapToParameter('value');
|
||||
ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $json.input[0].count }}');
|
||||
ndv.getters.inlineExpressionEditorInput().type('{esc}');
|
||||
ndv.actions.validateExpressionPreview('value', '0');
|
||||
|
||||
ndv.getters.inputDataContainer().find('span').contains('input').realMouseDown();
|
||||
|
||||
ndv.actions.mapToParameter('value');
|
||||
ndv.getters
|
||||
.inlineExpressionEditorInput()
|
||||
.should('have.text', '{{ $json.input }}{{ $json.input[0].count }}');
|
||||
ndv.actions.validateExpressionPreview('value', '[object Object]0');
|
||||
});
|
||||
|
||||
it('maps expressions from previous nodes', () => {
|
||||
cy.createFixtureWorkflow('Test_workflow_3.json', 'My test workflow');
|
||||
workflowPage.actions.zoomToFit();
|
||||
workflowPage.actions.openNode('Set1');
|
||||
|
||||
ndv.actions.executePrevious();
|
||||
|
||||
ndv.getters.schemaViewNode().contains('Schedule').click();
|
||||
const dataPill = ndv.getters
|
||||
.inputDataContainer()
|
||||
.findChildByTestId('run-data-schema-item')
|
||||
.contains('count')
|
||||
.should('be.visible');
|
||||
dataPill.realMouseDown();
|
||||
ndv.actions.mapToParameter('value');
|
||||
ndv.getters
|
||||
.inlineExpressionEditorInput()
|
||||
.should('have.text', `{{ $('${SCHEDULE_TRIGGER_NODE_NAME}').item.json.input[0].count }}`);
|
||||
ndv.getters.inlineExpressionEditorInput().type('{esc}');
|
||||
|
||||
ndv.actions.switchInputMode('Table');
|
||||
ndv.actions.selectInputNode(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
ndv.actions.mapDataFromHeader(1, 'value');
|
||||
ndv.getters
|
||||
.inlineExpressionEditorInput()
|
||||
.should(
|
||||
'have.text',
|
||||
`{{ $('${SCHEDULE_TRIGGER_NODE_NAME}').item.json.input }}{{ $('${SCHEDULE_TRIGGER_NODE_NAME}').item.json.input[0].count }}`,
|
||||
);
|
||||
|
||||
ndv.actions.selectInputNode('Set');
|
||||
|
||||
ndv.getters.executingLoader().should('not.exist');
|
||||
ndv.getters.inputDataContainer().should('exist');
|
||||
ndv.actions.validateExpressionPreview('value', '[object Object]0');
|
||||
|
||||
ndv.getters.inputTbodyCell(2, 0).realHover();
|
||||
ndv.actions.validateExpressionPreview('value', '[object Object]1');
|
||||
});
|
||||
|
||||
it('maps keys to path', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
workflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||
workflowPage.actions.openNode(MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
ndv.actions.pastePinnedData([
|
||||
{
|
||||
input: [
|
||||
{
|
||||
'hello.world': {
|
||||
'my count': 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: [
|
||||
{
|
||||
'hello.world': {
|
||||
'my count': 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
ndv.actions.close();
|
||||
|
||||
workflowPage.actions.addNodeToCanvas('Sort');
|
||||
workflowPage.actions.openNode('Sort');
|
||||
|
||||
ndv.getters.nodeParameters().find('button').contains('Add Field To Sort By').click();
|
||||
|
||||
ndv.getters.inputDataContainer().find('span').contains('my count').realMouseDown();
|
||||
|
||||
ndv.actions.mapToParameter('fieldName');
|
||||
|
||||
ndv.getters.inlineExpressionEditorInput().should('have.length', 0);
|
||||
ndv.getters
|
||||
.parameterInput('fieldName')
|
||||
.find('input')
|
||||
.should('have.value', "input[0]['hello.world']['my count']");
|
||||
});
|
||||
|
||||
it('maps expressions to updated fields correctly', () => {
|
||||
cy.fixture('Test_workflow_3.json').then((data) => {
|
||||
cy.get('body').paste(JSON.stringify(data));
|
||||
});
|
||||
workflowPage.actions.zoomToFit();
|
||||
|
||||
workflowPage.actions.openNode('Set');
|
||||
|
||||
ndv.actions.typeIntoParameterInput('value', 'delete me');
|
||||
|
||||
ndv.actions.typeIntoParameterInput('name', 'test');
|
||||
ndv.getters.parameterInput('name').find('input').blur();
|
||||
|
||||
ndv.actions.typeIntoParameterInput('value', 'fun');
|
||||
ndv.actions.clearParameterInput('value'); // keep focus on param
|
||||
|
||||
ndv.getters.inputDataContainer().should('exist').find('span').contains('count').realMouseDown();
|
||||
|
||||
ndv.actions.mapToParameter('value');
|
||||
ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $json.input[0].count }}');
|
||||
ndv.getters.inlineExpressionEditorInput().type('{esc}');
|
||||
ndv.actions.validateExpressionPreview('value', '0');
|
||||
|
||||
ndv.getters.inputDataContainer().find('span').contains('input').realMouseDown();
|
||||
|
||||
ndv.actions.mapToParameter('value');
|
||||
ndv.getters
|
||||
.inlineExpressionEditorInput()
|
||||
.should('have.text', '{{ $json.input }}{{ $json.input[0].count }}');
|
||||
ndv.actions.validateExpressionPreview('value', '[object Object]0');
|
||||
});
|
||||
|
||||
it('renders expression preview when a previous node is selected', () => {
|
||||
cy.fixture('Test_workflow_3.json').then((data) => {
|
||||
cy.get('body').paste(JSON.stringify(data));
|
||||
});
|
||||
workflowPage.actions.zoomToFit();
|
||||
|
||||
workflowPage.actions.openNode('Set');
|
||||
ndv.actions.typeIntoParameterInput('value', 'test_value');
|
||||
ndv.actions.typeIntoParameterInput('name', '{selectall}test_name');
|
||||
ndv.actions.close();
|
||||
|
||||
workflowPage.actions.openNode('Set1');
|
||||
ndv.actions.executePrevious();
|
||||
ndv.getters.executingLoader().should('not.exist');
|
||||
ndv.getters.inputDataContainer().should('exist');
|
||||
ndv.actions.switchInputMode('Table');
|
||||
ndv.actions.mapDataFromHeader(1, 'value');
|
||||
ndv.actions.validateExpressionPreview('value', 'test_value');
|
||||
ndv.actions.selectInputNode(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
ndv.actions.validateExpressionPreview('value', 'test_value');
|
||||
});
|
||||
|
||||
it('shows you can drop to inputs, including booleans', () => {
|
||||
cy.fixture('Test_workflow_3.json').then((data) => {
|
||||
cy.get('body').paste(JSON.stringify(data));
|
||||
});
|
||||
workflowPage.actions.zoomToFit();
|
||||
|
||||
workflowPage.actions.openNode('Set');
|
||||
ndv.getters.parameterInput('includeOtherFields').find('input[type="checkbox"]').should('exist');
|
||||
ndv.getters.parameterInput('includeOtherFields').find('input[type="text"]').should('not.exist');
|
||||
const pill = ndv.getters.inputDataContainer().find('span').contains('count');
|
||||
pill.should('be.visible');
|
||||
pill.realMouseDown();
|
||||
pill.realMouseMove(100, 100);
|
||||
|
||||
ndv.getters
|
||||
.parameterInput('includeOtherFields')
|
||||
.find('input[type="checkbox"]')
|
||||
.should('not.exist');
|
||||
ndv.getters
|
||||
.parameterInput('includeOtherFields')
|
||||
.find('input[type="text"]')
|
||||
.should('exist')
|
||||
.invoke('css', 'border')
|
||||
.should('include', 'dashed rgb(90, 76, 194)');
|
||||
|
||||
ndv.getters
|
||||
.parameterInput('value')
|
||||
.find('input[type="text"]')
|
||||
.should('exist')
|
||||
.invoke('css', 'border')
|
||||
.should('include', 'dashed rgb(90, 76, 194)');
|
||||
});
|
||||
|
||||
it('maps expressions to a specific location in the editor', () => {
|
||||
cy.fixture('Test_workflow_3.json').then((data) => {
|
||||
cy.get('body').paste(JSON.stringify(data));
|
||||
});
|
||||
workflowPage.actions.zoomToFit();
|
||||
|
||||
workflowPage.actions.openNode('Set');
|
||||
ndv.actions.typeIntoParameterInput('value', '=');
|
||||
ndv.getters.inlineExpressionEditorInput().find('.cm-content').paste('hello world\n\nnewline');
|
||||
ndv.getters.inlineExpressionEditorInput().type('{esc}');
|
||||
|
||||
ndv.getters.inputDataContainer().should('exist').find('span').contains('count').realMouseDown();
|
||||
ndv.actions.mapToParameter('value');
|
||||
ndv.getters
|
||||
.inlineExpressionEditorInput()
|
||||
.should('have.text', '{{ $json.input[0].count }}hello worldnewline');
|
||||
ndv.getters.inlineExpressionEditorInput().type('{esc}');
|
||||
ndv.actions.validateExpressionPreview('value', '0hello world\n\nnewline');
|
||||
|
||||
ndv.getters.inputDataContainer().find('span').contains('input').realMouseDown();
|
||||
ndv.actions.mapToParameter('value', 'center');
|
||||
|
||||
ndv.getters
|
||||
.inlineExpressionEditorInput()
|
||||
.should('have.text', '{{ $json.input[0].count }}hello world{{ $json.input }}newline');
|
||||
});
|
||||
});
|
||||
16
packages/testing/playwright/docs/TROUBLESHOOTING.md
Normal file
16
packages/testing/playwright/docs/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Playwright Test Troubleshooting
|
||||
|
||||
Known issues and solutions for common test failures.
|
||||
|
||||
## 1. Hover/Tooltip Test Flakiness
|
||||
|
||||
**Problem:** Hover and tooltip tests become unreliable when running in parallel
|
||||
|
||||
**Root Cause:** When multiple tests run in parallel, the browser becomes overloaded and begins coalescing or delaying mouse events to maintain performance. Unlike page loads (which the browser commits to completing), hover interactions trigger chains of small, low-priority events that can be delayed or combined when the system is stressed.
|
||||
|
||||
**What happens under load:**
|
||||
- Multiple mousemove/pointermove events get merged into single events
|
||||
- The rendering pipeline (event → framework update → style → paint) gets backed up
|
||||
- Tooltip appearances become unpredictable as the browser skips frames to catch up
|
||||
|
||||
**Solution:** Run these tests serially to prevent browser event loop overload
|
||||
@@ -205,15 +205,17 @@ export class CanvasPage extends BasePage {
|
||||
return this.page.getByTestId('workflow-tags').locator('.el-tag');
|
||||
}
|
||||
async activateWorkflow() {
|
||||
const switchElement = this.page.getByTestId('workflow-activate-switch');
|
||||
const statusElement = this.page.getByTestId('workflow-activator-status');
|
||||
|
||||
const responsePromise = this.page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/rest/workflows/') && response.request().method() === 'PATCH',
|
||||
);
|
||||
|
||||
await this.page.getByTestId('workflow-activate-switch').click();
|
||||
await switchElement.click();
|
||||
await statusElement.locator('span').filter({ hasText: 'Active' }).waitFor({ state: 'visible' });
|
||||
await responsePromise;
|
||||
|
||||
await this.page.waitForTimeout(200);
|
||||
}
|
||||
|
||||
async clickZoomToFitButton(): Promise<void> {
|
||||
|
||||
48
packages/testing/playwright/pages/InteractionsPage.ts
Normal file
48
packages/testing/playwright/pages/InteractionsPage.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { expect } from '@playwright/test';
|
||||
|
||||
import { BasePage } from './BasePage';
|
||||
|
||||
export class InteractionsPage extends BasePage {
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
}
|
||||
|
||||
async precisionDragToTarget(
|
||||
sourceLocator: Locator,
|
||||
targetLocator: Locator,
|
||||
position: 'top' | 'center' | 'bottom' = 'bottom',
|
||||
): Promise<void> {
|
||||
await expect(sourceLocator).toBeVisible();
|
||||
await expect(targetLocator).toBeVisible();
|
||||
|
||||
const targetBox = await targetLocator.boundingBox();
|
||||
if (!targetBox) {
|
||||
throw new Error('Could not get bounding box for target element');
|
||||
}
|
||||
|
||||
let dropPosition: { x: number; y: number };
|
||||
switch (position) {
|
||||
case 'top':
|
||||
dropPosition = { x: targetBox.x + targetBox.width / 2, y: targetBox.y + 2 };
|
||||
break;
|
||||
case 'center':
|
||||
dropPosition = {
|
||||
x: targetBox.x + targetBox.width / 2,
|
||||
y: targetBox.y + targetBox.height / 2,
|
||||
};
|
||||
break;
|
||||
case 'bottom':
|
||||
dropPosition = {
|
||||
x: targetBox.x + targetBox.width / 2,
|
||||
y: targetBox.y + targetBox.height - 2,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
await sourceLocator.hover();
|
||||
await this.page.mouse.down();
|
||||
await this.page.mouse.move(dropPosition.x, dropPosition.y);
|
||||
await this.page.mouse.up();
|
||||
}
|
||||
}
|
||||
@@ -106,8 +106,13 @@ export class NodeDetailsViewPage extends BasePage {
|
||||
return this.getOutputTableRow(row).locator('td').nth(col);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a cell from the output table body, this doesn't include the header row
|
||||
* @param row - The row index
|
||||
* @param col - The column index
|
||||
*/
|
||||
getOutputTbodyCell(row: number, col: number) {
|
||||
return this.getOutputTableRow(row).locator('td').nth(col);
|
||||
return this.getOutputTable().locator('tbody tr').nth(row).locator('td').nth(col);
|
||||
}
|
||||
|
||||
// Pin data operations
|
||||
@@ -447,14 +452,104 @@ export class NodeDetailsViewPage extends BasePage {
|
||||
}
|
||||
}
|
||||
|
||||
async switchInputMode(mode: 'Schema' | 'Table' | 'JSON' | 'Binary'): Promise<void> {
|
||||
await this.getInputPanel().getByRole('radio', { name: mode }).click();
|
||||
}
|
||||
|
||||
async switchOutputMode(mode: 'Schema' | 'Table' | 'JSON' | 'Binary'): Promise<void> {
|
||||
await this.getOutputPanel().getByRole('radio', { name: mode }).click();
|
||||
}
|
||||
|
||||
getAssignmentCollectionContainer(paramName: string) {
|
||||
return this.page.getByTestId(`assignment-collection-${paramName}`);
|
||||
}
|
||||
|
||||
getJsonDataContainer() {
|
||||
return this.getInputPanel().locator('.json-data');
|
||||
}
|
||||
|
||||
getInputJsonProperty(propertyName: string) {
|
||||
return this.getInputPanel()
|
||||
.locator('.json-data')
|
||||
.locator('span')
|
||||
.filter({ hasText: new RegExp(`^"${propertyName}"$`) })
|
||||
.first();
|
||||
}
|
||||
|
||||
getInputJsonPropertyContaining(text: string) {
|
||||
return this.getInputPanel()
|
||||
.locator('.json-data')
|
||||
.locator('span')
|
||||
.filter({ hasText: `"${text}"` })
|
||||
.first();
|
||||
}
|
||||
|
||||
getInputSchemaItem(text: string) {
|
||||
return this.getInputPanel()
|
||||
.getByTestId('run-data-schema-item')
|
||||
.locator('span')
|
||||
.filter({ hasText: new RegExp(`^${text}$`) })
|
||||
.first();
|
||||
}
|
||||
|
||||
async selectInputNode(nodeName: string) {
|
||||
const inputSelect = this.getInputPanel().getByTestId('ndv-input-select');
|
||||
await inputSelect.click();
|
||||
await this.page.getByRole('option', { name: nodeName }).click();
|
||||
}
|
||||
|
||||
getInputTableHeader(index: number = 0) {
|
||||
return this.getInputPanel().locator('table th').nth(index);
|
||||
}
|
||||
|
||||
getInputTableCell(row: number, col: number) {
|
||||
return this.getInputPanel().locator('table tbody tr').nth(row).locator('td').nth(col);
|
||||
}
|
||||
|
||||
getInputTbodyCell(row: number, col: number) {
|
||||
return this.getInputTableCell(row, col);
|
||||
}
|
||||
|
||||
getAssignmentName(paramName: string, index = 0) {
|
||||
return this.getAssignmentCollectionContainer(paramName)
|
||||
.getByTestId('assignment')
|
||||
.nth(index)
|
||||
.getByTestId('assignment-name');
|
||||
}
|
||||
|
||||
getAddValueButton() {
|
||||
return this.getNodeParameters().locator('input[placeholder*="Add Value"]');
|
||||
}
|
||||
|
||||
getParameterSwitch(parameterName: string) {
|
||||
return this.getParameterInput(parameterName).locator('.el-switch');
|
||||
}
|
||||
|
||||
getParameterTextInput(parameterName: string) {
|
||||
return this.getParameterInput(parameterName).locator('input[type="text"]');
|
||||
}
|
||||
|
||||
getInlineExpressionEditorContent() {
|
||||
return this.getInlineExpressionEditorInput().locator('.cm-content');
|
||||
}
|
||||
|
||||
getInputTable() {
|
||||
return this.getInputPanel().locator('table');
|
||||
}
|
||||
|
||||
getInputTableCellSpan(row: number, col: number, dataName: string) {
|
||||
return this.getInputTableCell(row, col).locator(`span[data-name="${dataName}"]`).first();
|
||||
}
|
||||
|
||||
getAddFieldToSortByButton() {
|
||||
return this.getNodeParameters().getByText('Add Field To Sort By');
|
||||
}
|
||||
|
||||
async toggleCodeMode(switchTo: 'Run Once for Each Item' | 'Run Once for All Items') {
|
||||
await this.getParameterInput('mode').click();
|
||||
await this.page.getByRole('option', { name: switchTo }).click();
|
||||
// This is a workaround to wait for the code editor to reinitialize after the mode switch
|
||||
// eslint-disable-next-line playwright/no-wait-for-timeout
|
||||
await this.page.waitForTimeout(2500);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export class SidebarPage {
|
||||
|
||||
async openNewCredentialDialogForProject(projectName: string) {
|
||||
await this.universalAdd();
|
||||
await this.page.getByTestId('universal-add').getByText('Credential').click();
|
||||
await this.page.getByTestId('universal-add').getByText('Credential', { exact: true }).click();
|
||||
await this.page.getByTestId('universal-add').getByRole('link', { name: projectName }).click();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { CanvasPage } from './CanvasPage';
|
||||
import { CredentialsPage } from './CredentialsPage';
|
||||
import { ExecutionsPage } from './ExecutionsPage';
|
||||
import { IframePage } from './IframePage';
|
||||
import { InteractionsPage } from './InteractionsPage';
|
||||
import { NodeDetailsViewPage } from './NodeDetailsViewPage';
|
||||
import { NotificationsPage } from './NotificationsPage';
|
||||
import { NpsSurveyPage } from './NpsSurveyPage';
|
||||
@@ -34,6 +35,7 @@ export class n8nPage {
|
||||
readonly canvas: CanvasPage;
|
||||
|
||||
readonly iframe: IframePage;
|
||||
readonly interactions: InteractionsPage;
|
||||
readonly ndv: NodeDetailsViewPage;
|
||||
readonly npsSurvey: NpsSurveyPage;
|
||||
readonly projectSettings: ProjectSettingsPage;
|
||||
@@ -66,6 +68,7 @@ export class n8nPage {
|
||||
this.canvas = new CanvasPage(page);
|
||||
|
||||
this.iframe = new IframePage(page);
|
||||
this.interactions = new InteractionsPage(page);
|
||||
this.ndv = new NodeDetailsViewPage(page);
|
||||
this.npsSurvey = new NpsSurveyPage(page);
|
||||
this.projectSettings = new ProjectSettingsPage(page);
|
||||
|
||||
@@ -219,6 +219,7 @@ export class ApiHelpers {
|
||||
emailOrLdapLoginId: credentials.email,
|
||||
password: credentials.password,
|
||||
},
|
||||
maxRetries: 3,
|
||||
});
|
||||
|
||||
if (!response.ok()) {
|
||||
|
||||
@@ -23,8 +23,8 @@ test.describe('Canvas Actions', () => {
|
||||
await n8n.canvas.clickNodePlusEndpoint(MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
await n8n.canvas.fillNodeCreatorSearchBar(CODE_NODE_NAME);
|
||||
await n8n.page.keyboard.press('Enter');
|
||||
await n8n.canvas.nodeCreatorSubItem(CODE_NODE_DISPLAY_NAME).click();
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
await n8n.canvas.clickNodeCreatorItemName(CODE_NODE_DISPLAY_NAME);
|
||||
await n8n.page.keyboard.press('Enter');
|
||||
|
||||
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
|
||||
await expect(n8n.canvas.nodeConnections()).toHaveCount(1);
|
||||
@@ -61,7 +61,7 @@ test.describe('Canvas Actions', () => {
|
||||
test('should add disconnected node if nothing is selected', async ({ n8n }) => {
|
||||
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
||||
await n8n.canvas.deselectAll();
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: CODE_NODE_DISPLAY_NAME, closeNDV: true });
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
||||
|
||||
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
|
||||
await expect(n8n.canvas.nodeConnections()).toHaveCount(0);
|
||||
@@ -70,7 +70,7 @@ test.describe('Canvas Actions', () => {
|
||||
test('should add node between two connected nodes', 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_NODE_DISPLAY_NAME, closeNDV: true });
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
||||
|
||||
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
|
||||
await expect(n8n.canvas.nodeConnections()).toHaveCount(1);
|
||||
@@ -96,7 +96,7 @@ test.describe('Canvas Actions', () => {
|
||||
test('should delete connections by clicking on the delete button', 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_NODE_DISPLAY_NAME, closeNDV: true });
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
||||
await n8n.canvas.deleteConnectionBetweenNodes(
|
||||
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
||||
CODE_NODE_DISPLAY_NAME,
|
||||
@@ -119,7 +119,7 @@ test.describe('Canvas Actions', () => {
|
||||
|
||||
test('should disable and enable node', async ({ n8n }) => {
|
||||
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: CODE_NODE_DISPLAY_NAME, closeNDV: true });
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
||||
|
||||
const disableButton = n8n.canvas.nodeDisableButton(CODE_NODE_DISPLAY_NAME);
|
||||
await disableButton.click();
|
||||
@@ -133,7 +133,7 @@ test.describe('Canvas Actions', () => {
|
||||
|
||||
test('should delete node', async ({ n8n }) => {
|
||||
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: CODE_NODE_DISPLAY_NAME, closeNDV: true });
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
||||
await n8n.canvas.deleteNodeByName(CODE_NODE_DISPLAY_NAME);
|
||||
|
||||
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(1);
|
||||
@@ -143,7 +143,7 @@ test.describe('Canvas Actions', () => {
|
||||
|
||||
test('should copy selected nodes', async ({ n8n }) => {
|
||||
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: CODE_NODE_DISPLAY_NAME, closeNDV: true });
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
||||
await n8n.canvasComposer.selectAllAndCopy();
|
||||
await n8n.canvas.nodeByName(CODE_NODE_DISPLAY_NAME).click();
|
||||
await n8n.canvasComposer.copySelectedNodesWithToast();
|
||||
@@ -153,7 +153,7 @@ test.describe('Canvas Actions', () => {
|
||||
|
||||
test('should select/deselect all nodes', async ({ n8n }) => {
|
||||
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: CODE_NODE_DISPLAY_NAME, closeNDV: true });
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
||||
await n8n.canvas.selectAll();
|
||||
|
||||
await expect(n8n.canvas.selectedNodes()).toHaveCount(2);
|
||||
@@ -165,7 +165,7 @@ test.describe('Canvas Actions', () => {
|
||||
test('should select nodes using arrow keys', 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_NODE_DISPLAY_NAME, closeNDV: true });
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
||||
await n8n.canvas.getCanvasNodes().first().waitFor();
|
||||
await n8n.canvas.navigateNodesWithArrows('left');
|
||||
|
||||
@@ -180,7 +180,7 @@ test.describe('Canvas Actions', () => {
|
||||
test('should select nodes using shift and arrow keys', 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_NODE_DISPLAY_NAME, closeNDV: true });
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
|
||||
await n8n.canvas.getCanvasNodes().first().waitFor();
|
||||
await n8n.canvas.extendSelectionWithArrows('left');
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ test.describe('Data pinning', () => {
|
||||
await n8n.ndv.execute();
|
||||
await expect(n8n.ndv.getOutputPanel()).toBeVisible();
|
||||
|
||||
const prevValue = await n8n.ndv.getOutputTbodyCell(1, 0).textContent();
|
||||
const prevValue = await n8n.ndv.getOutputTbodyCell(0, 0).textContent();
|
||||
|
||||
await n8n.ndv.togglePinData();
|
||||
await n8n.ndv.close();
|
||||
@@ -48,7 +48,7 @@ test.describe('Data pinning', () => {
|
||||
await n8n.canvas.clickExecuteWorkflowButton();
|
||||
await n8n.canvas.openNode(NODES.SCHEDULE_TRIGGER);
|
||||
|
||||
await expect(n8n.ndv.getOutputTbodyCell(1, 0)).toHaveText(prevValue ?? '');
|
||||
await expect(n8n.ndv.getOutputTbodyCell(0, 0)).toHaveText(prevValue ?? '');
|
||||
});
|
||||
|
||||
test('should be able to set custom pinned data', async ({ n8n }) => {
|
||||
@@ -63,14 +63,14 @@ test.describe('Data pinning', () => {
|
||||
await expect(n8n.ndv.getOutputTableRows()).toHaveCount(2);
|
||||
await expect(n8n.ndv.getOutputTableHeaders()).toHaveCount(2);
|
||||
await expect(n8n.ndv.getOutputTableHeaders().first()).toContainText('test');
|
||||
await expect(n8n.ndv.getOutputTbodyCell(1, 0)).toContainText('1');
|
||||
await expect(n8n.ndv.getOutputTbodyCell(0, 0)).toContainText('1');
|
||||
|
||||
await n8n.ndv.close();
|
||||
await n8n.canvas.clickSaveWorkflowButton();
|
||||
await n8n.canvas.openNode(NODES.SCHEDULE_TRIGGER);
|
||||
|
||||
await expect(n8n.ndv.getOutputTableHeaders().first()).toContainText('test');
|
||||
await expect(n8n.ndv.getOutputTbodyCell(1, 0)).toContainText('1');
|
||||
await expect(n8n.ndv.getOutputTbodyCell(0, 0)).toContainText('1');
|
||||
});
|
||||
|
||||
test('should display pin data edit button for Webhook node', async ({ n8n }) => {
|
||||
@@ -102,7 +102,7 @@ test.describe('Data pinning', () => {
|
||||
await n8n.canvas.openNode('Edit Fields1');
|
||||
|
||||
await expect(n8n.ndv.getOutputTableHeaders().first()).toContainText('test');
|
||||
await expect(n8n.ndv.getOutputTbodyCell(1, 0)).toContainText('1');
|
||||
await expect(n8n.ndv.getOutputTbodyCell(0, 0)).toContainText('1');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -212,10 +212,7 @@ test.describe('Data pinning', () => {
|
||||
}) => {
|
||||
await setupRequirements(webhookTestRequirements);
|
||||
await expect(n8n.canvas.getWorkflowSaveButton()).toContainText('Saved');
|
||||
await n8n.page.waitForTimeout(500);
|
||||
await n8n.canvas.activateWorkflow();
|
||||
await n8n.page.waitForTimeout(500);
|
||||
|
||||
const webhookUrl = '/webhook/b0d79ddb-df2d-49b1-8555-9fa2b482608f';
|
||||
const response = await n8n.ndv.makeWebhookRequest(webhookUrl);
|
||||
expect(response.status(), 'Webhook response is: ' + (await response.text())).toBe(200);
|
||||
|
||||
367
packages/testing/playwright/tests/ui/14-mapping.spec.ts
Normal file
367
packages/testing/playwright/tests/ui/14-mapping.spec.ts
Normal file
@@ -0,0 +1,367 @@
|
||||
import { test, expect } from '../../fixtures/base';
|
||||
|
||||
test.describe('Data Mapping', () => {
|
||||
test.describe
|
||||
.serial('Expression Preview', () => {
|
||||
test('maps expressions from table json, and resolves value based on hover', async ({
|
||||
n8n,
|
||||
}) => {
|
||||
// This test is marked as serial because hover/tooltips are unreliable when running in parallel against a single server due to resource contention.
|
||||
|
||||
await n8n.start.fromImportedWorkflow('Test_workflow_3.json');
|
||||
await n8n.canvas.openNode('Set');
|
||||
await n8n.ndv.switchInputMode('Table');
|
||||
|
||||
await expect(n8n.ndv.getInputTable()).toBeVisible();
|
||||
|
||||
await expect(n8n.ndv.getParameterInputField('name')).toHaveValue('other');
|
||||
await expect(n8n.ndv.getParameterInputField('value')).toHaveValue('');
|
||||
|
||||
const countCell = n8n.ndv.getInputTableCellSpan(0, 0, 'count');
|
||||
await expect(countCell).toBeVisible();
|
||||
|
||||
const valueParameter = n8n.ndv.getParameterInput('value');
|
||||
await n8n.interactions.precisionDragToTarget(countCell, valueParameter, 'bottom');
|
||||
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText(
|
||||
'{{ $json.input[0].count }}',
|
||||
);
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('0');
|
||||
|
||||
await n8n.ndv.getInputTbodyCell(0, 0).hover();
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('0');
|
||||
|
||||
await n8n.ndv.getInputTbodyCell(1, 0).hover();
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('1');
|
||||
|
||||
await n8n.ndv.execute();
|
||||
|
||||
await expect(n8n.ndv.getOutputTable()).toBeVisible();
|
||||
|
||||
await n8n.ndv.getOutputTbodyCell(0, 0).hover();
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('0');
|
||||
|
||||
await n8n.ndv.getOutputTbodyCell(1, 0).hover();
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('1');
|
||||
});
|
||||
});
|
||||
test('maps expressions from json view', async ({ n8n }) => {
|
||||
await n8n.start.fromImportedWorkflow('Test_workflow_3.json');
|
||||
await n8n.canvas.openNode('Set');
|
||||
await n8n.ndv.switchInputMode('JSON');
|
||||
|
||||
const expectedJsonText =
|
||||
'[{"input": [{"count": 0,"with space": "!!","with.dot": "!!","with"quotes": "!!"}]},{"input": [{"count": 1}]}]';
|
||||
await expect(n8n.ndv.getInputPanel().getByText(expectedJsonText)).toBeVisible();
|
||||
|
||||
await expect(n8n.ndv.getJsonDataContainer()).toBeVisible();
|
||||
|
||||
const inputSpan = n8n.ndv.getInputJsonProperty('input');
|
||||
await expect(inputSpan).toBeVisible();
|
||||
|
||||
const valueParameterInput = n8n.ndv.getParameterInput('value');
|
||||
await expect(valueParameterInput).toBeVisible();
|
||||
|
||||
await inputSpan.dragTo(valueParameterInput);
|
||||
|
||||
const expressionEditor = n8n.ndv.getInlineExpressionEditorInput();
|
||||
await expect(expressionEditor).toBeVisible();
|
||||
await expect(expressionEditor).toHaveText('{{ $json.input }}');
|
||||
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('Array:');
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('"count": 0');
|
||||
|
||||
const countSpan = n8n.ndv.getInputJsonPropertyContaining('count');
|
||||
await expect(countSpan).toBeVisible();
|
||||
|
||||
await n8n.interactions.precisionDragToTarget(
|
||||
countSpan,
|
||||
n8n.ndv.getInlineExpressionEditorInput(),
|
||||
'bottom',
|
||||
);
|
||||
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText(
|
||||
'{{ $json.input }}{{ $json.input[0].count }}',
|
||||
);
|
||||
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
|
||||
const previewElement = n8n.ndv.getParameterExpressionPreviewValue();
|
||||
await expect(previewElement).toBeVisible();
|
||||
});
|
||||
|
||||
test('maps expressions from previous nodes', async ({ n8n }) => {
|
||||
await n8n.start.fromImportedWorkflow('Test_workflow_3.json');
|
||||
await n8n.canvas.openNode('Set1');
|
||||
await n8n.ndv.executePrevious();
|
||||
|
||||
const scheduleNode = n8n.ndv.getInputPanel().getByText('Schedule Trigger');
|
||||
await expect(scheduleNode).toBeVisible();
|
||||
await scheduleNode.click();
|
||||
|
||||
const schemaItem = n8n.ndv.getInputSchemaItem('count');
|
||||
await expect(schemaItem).toBeVisible();
|
||||
|
||||
const valueParameterInput = n8n.ndv.getParameterInput('value');
|
||||
await expect(valueParameterInput).toBeVisible();
|
||||
|
||||
await n8n.interactions.precisionDragToTarget(schemaItem, valueParameterInput, 'top');
|
||||
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText(
|
||||
"{{ $('Schedule Trigger').item.json.input[0].count }}",
|
||||
);
|
||||
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
|
||||
await n8n.ndv.switchInputMode('Table');
|
||||
await n8n.ndv.selectInputNode('Schedule Trigger');
|
||||
|
||||
const headerElement = n8n.ndv.getInputTableHeader(0);
|
||||
await expect(headerElement).toBeVisible();
|
||||
|
||||
await n8n.interactions.precisionDragToTarget(
|
||||
headerElement,
|
||||
n8n.ndv.getInlineExpressionEditorInput(),
|
||||
'top',
|
||||
);
|
||||
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText(
|
||||
"{{ $('Schedule Trigger').item.json.input }}{{ $('Schedule Trigger').item.json.input[0].count }}",
|
||||
);
|
||||
|
||||
await n8n.ndv.selectInputNode('Set');
|
||||
});
|
||||
test('maps expressions from table header', async ({ n8n }) => {
|
||||
await n8n.start.fromImportedWorkflow('Test_workflow-actions_paste-data.json');
|
||||
await n8n.canvas.openNode('Set');
|
||||
await n8n.ndv.executePrevious();
|
||||
await n8n.ndv.switchInputMode('Table');
|
||||
|
||||
await expect(n8n.ndv.getInputTable()).toBeVisible();
|
||||
|
||||
const addValueButton = n8n.ndv.getAddValueButton();
|
||||
await expect(addValueButton).toBeVisible();
|
||||
await addValueButton.click();
|
||||
|
||||
await n8n.page.getByRole('option', { name: 'String' }).click();
|
||||
|
||||
await expect(n8n.ndv.getParameterInputField('name')).toHaveValue('propertyName');
|
||||
await expect(n8n.ndv.getParameterInputField('value')).toHaveValue('');
|
||||
|
||||
const firstHeader = n8n.ndv.getInputTableHeader(0);
|
||||
await expect(firstHeader).toBeVisible();
|
||||
|
||||
const valueParameter = n8n.ndv.getParameterInput('value');
|
||||
await n8n.interactions.precisionDragToTarget(firstHeader, valueParameter, 'bottom');
|
||||
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toBeVisible();
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText('{{ $json.timestamp }}');
|
||||
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
|
||||
const currentYear = new Date().getFullYear().toString();
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText(currentYear);
|
||||
|
||||
const secondHeader = n8n.ndv.getInputTableHeader(1);
|
||||
await expect(secondHeader).toBeVisible();
|
||||
|
||||
await n8n.interactions.precisionDragToTarget(
|
||||
secondHeader,
|
||||
n8n.ndv.getInlineExpressionEditorInput(),
|
||||
'top',
|
||||
);
|
||||
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText(
|
||||
"{{ $json['Readable date'] }}{{ $json.timestamp }}",
|
||||
);
|
||||
});
|
||||
|
||||
test('maps expressions from schema view', async ({ n8n }) => {
|
||||
await n8n.start.fromImportedWorkflow('Test_workflow_3.json');
|
||||
await n8n.canvas.openNode('Set');
|
||||
|
||||
await n8n.ndv.getParameterInputField('value').clear();
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
|
||||
const countSchemaItem = n8n.ndv.getInputSchemaItem('count');
|
||||
await expect(countSchemaItem).toBeVisible();
|
||||
|
||||
const valueParameter = n8n.ndv.getParameterInput('value');
|
||||
await n8n.interactions.precisionDragToTarget(countSchemaItem, valueParameter, 'bottom');
|
||||
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText('{{ $json.input[0].count }}');
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('0');
|
||||
|
||||
const inputSchemaItem = n8n.ndv.getInputSchemaItem('input');
|
||||
await expect(inputSchemaItem).toBeVisible();
|
||||
|
||||
await n8n.interactions.precisionDragToTarget(inputSchemaItem, valueParameter, 'top');
|
||||
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText(
|
||||
'{{ $json.input }}{{ $json.input[0].count }}',
|
||||
);
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('[object Object]0');
|
||||
});
|
||||
|
||||
test('maps keys to path', async ({ n8n }) => {
|
||||
await n8n.start.fromBlankCanvas();
|
||||
await n8n.canvas.addNode('Manual Trigger');
|
||||
await n8n.canvas.openNode('When clicking ‘Execute workflow’');
|
||||
|
||||
await n8n.ndv.setPinnedData([
|
||||
{
|
||||
input: [
|
||||
{
|
||||
'hello.world': {
|
||||
'my count': 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: [
|
||||
{
|
||||
'hello.world': {
|
||||
'my count': 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
await n8n.ndv.close();
|
||||
|
||||
await n8n.canvas.addNode('Sort');
|
||||
|
||||
const addFieldButton = n8n.ndv.getAddFieldToSortByButton();
|
||||
await addFieldButton.click();
|
||||
|
||||
const myCountSpan = n8n.ndv.getInputSchemaItem('my count');
|
||||
await expect(myCountSpan).toBeVisible();
|
||||
|
||||
const fieldNameParameter = n8n.ndv.getParameterInput('fieldName');
|
||||
await n8n.interactions.precisionDragToTarget(myCountSpan, fieldNameParameter, 'bottom');
|
||||
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toBeHidden();
|
||||
await expect(n8n.ndv.getParameterInputField('fieldName')).toHaveValue(
|
||||
"input[0]['hello.world']['my count']",
|
||||
);
|
||||
});
|
||||
|
||||
test('maps expressions to updated fields correctly', async ({ n8n }) => {
|
||||
await n8n.start.fromImportedWorkflow('Test_workflow_3.json');
|
||||
await n8n.canvas.openNode('Set');
|
||||
|
||||
await n8n.ndv.fillParameterInputByName('value', 'delete me');
|
||||
await n8n.ndv.fillParameterInputByName('name', 'test');
|
||||
await n8n.ndv.getParameterInputField('name').blur();
|
||||
|
||||
await n8n.ndv.fillParameterInputByName('value', 'fun');
|
||||
await n8n.ndv.getParameterInputField('value').clear();
|
||||
|
||||
const countSchemaItem = n8n.ndv.getInputSchemaItem('count');
|
||||
await expect(countSchemaItem).toBeVisible();
|
||||
|
||||
const valueParameter = n8n.ndv.getParameterInput('value');
|
||||
await n8n.interactions.precisionDragToTarget(countSchemaItem, valueParameter, 'bottom');
|
||||
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText('{{ $json.input[0].count }}');
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('0');
|
||||
|
||||
const inputSchemaItem = n8n.ndv.getInputSchemaItem('input');
|
||||
await expect(inputSchemaItem).toBeVisible();
|
||||
|
||||
await n8n.interactions.precisionDragToTarget(inputSchemaItem, valueParameter, 'top');
|
||||
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText(
|
||||
'{{ $json.input }}{{ $json.input[0].count }}',
|
||||
);
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('[object Object]0');
|
||||
});
|
||||
|
||||
test('renders expression preview when a previous node is selected', async ({ n8n }) => {
|
||||
await n8n.start.fromImportedWorkflow('Test_workflow_3.json');
|
||||
await n8n.canvas.openNode('Set');
|
||||
|
||||
await n8n.ndv.fillParameterInputByName('value', 'test_value');
|
||||
await n8n.ndv.fillParameterInputByName('name', 'test_name');
|
||||
await n8n.ndv.close();
|
||||
|
||||
await n8n.canvas.openNode('Set1');
|
||||
await n8n.ndv.executePrevious();
|
||||
await n8n.ndv.switchInputMode('Table');
|
||||
|
||||
const firstHeader = n8n.ndv.getInputTableHeader(0);
|
||||
await expect(firstHeader).toBeVisible();
|
||||
|
||||
const valueParameter = n8n.ndv.getParameterInput('value');
|
||||
await n8n.interactions.precisionDragToTarget(firstHeader, valueParameter, 'bottom');
|
||||
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('test_value');
|
||||
|
||||
await n8n.ndv.selectInputNode('Schedule Trigger');
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('test_value');
|
||||
});
|
||||
|
||||
test('shows you can drop to inputs, including booleans', async ({ n8n }) => {
|
||||
await n8n.start.fromImportedWorkflow('Test_workflow_3.json');
|
||||
await n8n.canvas.openNode('Set');
|
||||
|
||||
await expect(n8n.ndv.getParameterSwitch('includeOtherFields')).toBeVisible();
|
||||
await expect(n8n.ndv.getParameterTextInput('includeOtherFields')).toBeHidden();
|
||||
|
||||
const countSpan = n8n.ndv.getInputSchemaItem('count');
|
||||
await expect(countSpan).toBeVisible();
|
||||
|
||||
await countSpan.hover();
|
||||
await n8n.page.mouse.down();
|
||||
await n8n.page.mouse.move(100, 100);
|
||||
|
||||
await expect(n8n.ndv.getParameterSwitch('includeOtherFields')).toBeHidden();
|
||||
await expect(n8n.ndv.getParameterTextInput('includeOtherFields')).toBeVisible();
|
||||
|
||||
const includeOtherFieldsInput = n8n.ndv.getParameterTextInput('includeOtherFields');
|
||||
await expect(includeOtherFieldsInput).toHaveCSS('border', /dashed.*rgb\(90, 76, 194\)/);
|
||||
|
||||
const valueInput = n8n.ndv.getParameterTextInput('value');
|
||||
await expect(valueInput).toHaveCSS('border', /dashed.*rgb\(90, 76, 194\)/);
|
||||
|
||||
await n8n.page.mouse.up();
|
||||
});
|
||||
|
||||
test('maps expressions to a specific location in the editor', async ({ n8n }) => {
|
||||
await n8n.start.fromImportedWorkflow('Test_workflow_3.json');
|
||||
await n8n.canvas.openNode('Set');
|
||||
|
||||
await n8n.ndv.fillParameterInputByName('value', '=');
|
||||
await n8n.ndv.getInlineExpressionEditorContent().fill('hello world\n\nnewline');
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
|
||||
const countSchemaItem = n8n.ndv.getInputSchemaItem('count');
|
||||
await expect(countSchemaItem).toBeVisible();
|
||||
|
||||
const valueParameter = n8n.ndv.getParameterInput('value');
|
||||
await n8n.interactions.precisionDragToTarget(countSchemaItem, valueParameter, 'top');
|
||||
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText(
|
||||
'{{ $json.input[0].count }}hello worldnewline',
|
||||
);
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText(
|
||||
'0hello world\n\nnewline',
|
||||
);
|
||||
|
||||
const inputSchemaItem = n8n.ndv.getInputSchemaItem('input');
|
||||
await expect(inputSchemaItem).toBeVisible();
|
||||
|
||||
await n8n.interactions.precisionDragToTarget(inputSchemaItem, valueParameter, 'center');
|
||||
|
||||
await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText(
|
||||
'{{ $json.input[0].count }}hello world{{ $json.input }}newline',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -69,7 +69,7 @@ test.describe('Projects', () => {
|
||||
|
||||
await n8n.ndv.selectWorkflowResource(`Create a Sub-Workflow in '${projectName}'`);
|
||||
|
||||
const subn8n = new n8nPage(await subWorkflowPagePromise);
|
||||
const subn8n = new n8nPage(await subWorkflowPagePromise, n8n.api);
|
||||
|
||||
await subn8n.ndv.clickBackToCanvasButton();
|
||||
|
||||
@@ -95,7 +95,7 @@ test.describe('Projects', () => {
|
||||
await expect(subn8n.page.getByRole('heading', { name: 'My Sub-Workflow' })).toBeVisible();
|
||||
|
||||
// Navigate to Credentials
|
||||
await subn8n.page.getByRole('link', { name: 'Credentials' }).click();
|
||||
await subn8n.page.getByRole('link', { name: 'Credentials', exact: true }).click();
|
||||
|
||||
// Assert that the credential is in the list
|
||||
await expect(subn8n.page.locator('[data-test-id="resources-list-item"]')).toHaveCount(1);
|
||||
|
||||
@@ -13,7 +13,7 @@ test.describe('Code node', () => {
|
||||
await n8n.goHome();
|
||||
await n8n.workflows.clickAddWorkflowButton();
|
||||
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
||||
await n8n.canvas.addNodeWithSubItem(CODE_NODE_NAME, CODE_NODE_DISPLAY_NAME);
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript' });
|
||||
});
|
||||
|
||||
test('should show correct placeholders switching modes', async ({ n8n }) => {
|
||||
@@ -54,21 +54,21 @@ test.describe('Code node', () => {
|
||||
});
|
||||
|
||||
test('should allow switching between sibling code nodes', async ({ n8n }) => {
|
||||
await n8n.ndv.getCodeEditor().fill("console.log('code node 1')");
|
||||
await n8n.ndv.getCodeEditor().fill("console.log('Code in JavaScript1')");
|
||||
await n8n.ndv.close();
|
||||
|
||||
await n8n.canvas.addNodeWithSubItem(CODE_NODE_NAME, CODE_NODE_DISPLAY_NAME);
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript' });
|
||||
|
||||
await n8n.ndv.getCodeEditor().fill("console.log('code node 2')");
|
||||
await n8n.ndv.getCodeEditor().fill("console.log('Code in JavaScript2')");
|
||||
await n8n.ndv.close();
|
||||
|
||||
await n8n.canvas.openNode(CODE_NODE_DISPLAY_NAME);
|
||||
|
||||
await n8n.ndv.clickFloatingNode(CODE_NODE_DISPLAY_NAME + '1');
|
||||
await expect(n8n.ndv.getCodeEditor()).toContainText("console.log('code node 2')");
|
||||
await expect(n8n.ndv.getCodeEditor()).toContainText("console.log('Code in JavaScript2')");
|
||||
|
||||
await n8n.ndv.clickFloatingNode(CODE_NODE_DISPLAY_NAME);
|
||||
await expect(n8n.ndv.getCodeEditor()).toContainText("console.log('code node 1')");
|
||||
await expect(n8n.ndv.getCodeEditor()).toContainText("console.log('Code in JavaScript1')");
|
||||
});
|
||||
|
||||
test('should show lint errors in `runOnceForAllItems` mode', async ({ n8n }) => {
|
||||
@@ -89,25 +89,31 @@ return
|
||||
'`.itemMatching()` expects an item index to be passed in as its argument.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('should show lint errors in `runOnceForEachItem` mode', async ({ n8n }) => {
|
||||
await n8n.ndv.getParameterInput('mode').click();
|
||||
await n8n.page.getByRole('option', { name: 'Run Once for Each Item' }).click();
|
||||
test.describe
|
||||
.serial('Run Once for Each Item', () => {
|
||||
test('should show lint errors in `runOnceForEachItem` mode', async ({ n8n }) => {
|
||||
await n8n.start.fromHome();
|
||||
await n8n.workflows.clickAddWorkflowButton();
|
||||
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript' });
|
||||
await n8n.ndv.toggleCodeMode('Run Once for Each Item');
|
||||
|
||||
await n8n.ndv.getCodeEditor().fill(`$input.itemMatching()
|
||||
await n8n.ndv.getCodeEditor().fill(`$input.itemMatching()
|
||||
$input.all()
|
||||
$input.first()
|
||||
$input.item()
|
||||
|
||||
return []
|
||||
`);
|
||||
await expect(n8n.ndv.getLintErrors()).toHaveCount(5);
|
||||
await n8n.ndv.getParameterInput('jsCode').getByText('all').hover();
|
||||
await expect(n8n.ndv.getLintTooltip()).toContainText(
|
||||
"Method `$input.all()` is only available in the 'Run Once for All Items' mode.",
|
||||
);
|
||||
await expect(n8n.ndv.getLintErrors()).toHaveCount(7);
|
||||
await n8n.ndv.getParameterInput('jsCode').getByText('all').hover();
|
||||
await expect(n8n.ndv.getLintTooltip()).toContainText(
|
||||
"Method `$input.all()` is only available in the 'Run Once for All Items' mode.",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Ask AI', () => {
|
||||
test.describe('Enabled', () => {
|
||||
@@ -116,10 +122,7 @@ return []
|
||||
await n8n.goHome();
|
||||
await n8n.workflows.clickAddWorkflowButton();
|
||||
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
||||
await n8n.canvas.clickNodeCreatorPlusButton();
|
||||
await n8n.canvas.fillNodeCreatorSearchBar(CODE_NODE_NAME);
|
||||
await n8n.page.keyboard.press('Enter');
|
||||
await n8n.canvas.clickNodeCreatorItemName(CODE_NODE_DISPLAY_NAME);
|
||||
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript' });
|
||||
});
|
||||
|
||||
test('tab should exist if experiment selected and be selectable', async ({ n8n }) => {
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import {
|
||||
MANUAL_TRIGGER_NODE_NAME,
|
||||
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
||||
CODE_NODE_NAME,
|
||||
CODE_NODE_DISPLAY_NAME,
|
||||
} from '../../config/constants';
|
||||
import { test, expect } from '../../fixtures/base';
|
||||
} from '../../../config/constants';
|
||||
import { test, expect } from '../../../fixtures/base';
|
||||
|
||||
test.describe('Canvas Node Actions', () => {
|
||||
test.beforeEach(async ({ n8n }) => {
|
||||
@@ -57,9 +55,11 @@ test.describe('Canvas Node Actions', () => {
|
||||
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
||||
|
||||
await n8n.canvas.clickNodePlusEndpoint(MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
await n8n.canvas.fillNodeCreatorSearchBar(CODE_NODE_NAME);
|
||||
await n8n.canvas.fillNodeCreatorSearchBar('Code');
|
||||
await n8n.page.keyboard.press('Enter');
|
||||
|
||||
await n8n.canvas.clickNodeCreatorItemName('Code in JavaScript');
|
||||
await n8n.page.keyboard.press('Enter');
|
||||
await n8n.canvas.nodeCreatorSubItem(CODE_NODE_DISPLAY_NAME).click();
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
|
||||
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
|
||||
@@ -69,11 +69,7 @@ test.describe('Canvas Node Actions', () => {
|
||||
test('should add disconnected node when nothing selected', async ({ n8n }) => {
|
||||
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
||||
await n8n.canvas.deselectAll();
|
||||
await n8n.canvas.clickNodeCreatorPlusButton();
|
||||
await n8n.canvas.fillNodeCreatorSearchBar(CODE_NODE_NAME);
|
||||
await n8n.page.keyboard.press('Enter');
|
||||
await n8n.canvas.nodeCreatorSubItem(CODE_NODE_DISPLAY_NAME).click();
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
await n8n.canvas.addNode('Code', { action: 'Code in JavaScript', closeNDV: true });
|
||||
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
|
||||
await expect(n8n.canvas.nodeConnections()).toHaveCount(0);
|
||||
});
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"name": "Test_workflow-actions_paste-data",
|
||||
"active": false,
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [{}]
|
||||
}
|
||||
},
|
||||
"id": "54b1cdeb-b453-4568-8107-c17fcf2aa25a",
|
||||
"name": "Schedule Trigger",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [240, 560]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "5f6dffef-61f7-459d-930c-ef701d08d49a",
|
||||
"name": "Set",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 1,
|
||||
"position": [460, 460]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"rules": {
|
||||
"rules": [
|
||||
{
|
||||
"outputKey": "a"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "c07d3a12-1ee2-4131-bec2-ab366457d042",
|
||||
"name": "Old version Switch Node",
|
||||
"type": "n8n-nodes-base.switch",
|
||||
"typeVersion": 2,
|
||||
"position": [460, 680]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "182a833d-3b93-4e86-a0db-3ceb19d6562b",
|
||||
"name": "Loop Over Items",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"typeVersion": 3,
|
||||
"position": [720, 560]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "b0d0aeb7-0c8f-4810-8b78-6c0db3c9a486",
|
||||
"name": "Replace Me",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [900, 560]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Schedule Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Set",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Old version Switch Node",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Over Items": {
|
||||
"main": [
|
||||
null,
|
||||
[
|
||||
{
|
||||
"node": "Replace Me",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Replace Me": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {}
|
||||
}
|
||||
112
packages/testing/playwright/workflows/Test_workflow_3.json
Normal file
112
packages/testing/playwright/workflows/Test_workflow_3.json
Normal file
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"name": "Used in 5-ndv and 14-mapping",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [{}]
|
||||
}
|
||||
},
|
||||
"id": "0f7d87ee-19c6-4576-bdff-1f3c4739392c",
|
||||
"name": "Schedule Trigger",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [720, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "2b0f25a2-9483-4579-9f6d-91b7ac2fcb71",
|
||||
"name": "other",
|
||||
"value": "",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "2dfc690a-95cf-48c2-85a6-2b3bb8cd1d1d",
|
||||
"name": "Set",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.3,
|
||||
"position": [920, 300]
|
||||
},
|
||||
{
|
||||
"id": "9bee04af-1bfc-4be2-a704-e975cb887ced",
|
||||
"name": "Set1",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.3,
|
||||
"position": [1120, 300],
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "2b0f25a2-9483-4579-9f6d-91b7ac2fcb71",
|
||||
"name": "other",
|
||||
"value": "",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Schedule Trigger": [
|
||||
{
|
||||
"json": {
|
||||
"input": [
|
||||
{
|
||||
"count": 0,
|
||||
"with space": "!!",
|
||||
"with.dot": "!!",
|
||||
"with\"quotes": "!!"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"input": [
|
||||
{
|
||||
"count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"Schedule Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Set",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Set": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Set1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "c26af749-dacb-45ef-8071-98aba8688075",
|
||||
"id": "1",
|
||||
"meta": {
|
||||
"instanceId": "fe45a93dd232270eb40d3ba1f7907ad3935bbd72ad5e4ee09ff61e96674f9aef"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -123,7 +123,7 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"active": true,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user