mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
test: Migrate NDV tests from Cypress -> Playwright (#19148)
This commit is contained in:
@@ -1,966 +0,0 @@
|
|||||||
import { setCredentialValues } from '../composables/modals/credential-modal';
|
|
||||||
import {
|
|
||||||
clickCreateNewCredential,
|
|
||||||
clickGetBackToCanvas,
|
|
||||||
setParameterSelectByContent,
|
|
||||||
} from '../composables/ndv';
|
|
||||||
import { openNode } from '../composables/workflow';
|
|
||||||
import {
|
|
||||||
EDIT_FIELDS_SET_NODE_NAME,
|
|
||||||
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
|
||||||
NOTION_NODE_NAME,
|
|
||||||
} from '../constants';
|
|
||||||
import { NDV, WorkflowPage } from '../pages';
|
|
||||||
import { NodeCreator } from '../pages/features/node-creator';
|
|
||||||
|
|
||||||
const workflowPage = new WorkflowPage();
|
|
||||||
const ndv = new NDV();
|
|
||||||
|
|
||||||
describe('NDV', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
workflowPage.actions.visit();
|
|
||||||
workflowPage.actions.renameWithUniqueName();
|
|
||||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show up when double clicked on a node and close when Back to canvas clicked', () => {
|
|
||||||
workflowPage.actions.addInitialNodeToCanvas('Manual');
|
|
||||||
workflowPage.getters.canvasNodes().first().dblclick();
|
|
||||||
ndv.getters.container().should('be.visible');
|
|
||||||
ndv.getters.backToCanvas().click();
|
|
||||||
ndv.getters.container().should('not.be.visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show input panel when node is not connected', () => {
|
|
||||||
workflowPage.actions.addInitialNodeToCanvas('Manual');
|
|
||||||
workflowPage.actions.deselectAll();
|
|
||||||
workflowPage.actions.addNodeToCanvas('Set');
|
|
||||||
workflowPage.getters.canvasNodes().last().dblclick();
|
|
||||||
ndv.getters.container().should('be.visible').should('contain', 'Wire me up');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should test webhook node', () => {
|
|
||||||
workflowPage.actions.addInitialNodeToCanvas('Webhook');
|
|
||||||
workflowPage.getters.canvasNodes().first().dblclick();
|
|
||||||
|
|
||||||
ndv.actions.execute();
|
|
||||||
ndv.getters.copyInput().click();
|
|
||||||
|
|
||||||
cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite');
|
|
||||||
|
|
||||||
cy.readClipboard().then((url) => {
|
|
||||||
cy.request({
|
|
||||||
method: 'GET',
|
|
||||||
url,
|
|
||||||
}).then((resp) => {
|
|
||||||
expect(resp.status).to.eq(200);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
ndv.getters.outputDisplayMode().should('have.length.at.least', 1).and('be.visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should change input and go back to canvas', () => {
|
|
||||||
cy.createFixtureWorkflow('NDV-test-select-input.json', 'NDV test select input');
|
|
||||||
workflowPage.actions.zoomToFit();
|
|
||||||
workflowPage.getters.canvasNodes().last().dblclick();
|
|
||||||
ndv.actions.switchInputMode('Table');
|
|
||||||
ndv.getters.inputSelect().click();
|
|
||||||
ndv.getters.inputOption().last().click();
|
|
||||||
ndv.getters.inputDataContainer().should('be.visible');
|
|
||||||
ndv.getters.inputDataContainer().should('contain', 'start');
|
|
||||||
ndv.getters.backToCanvas().click();
|
|
||||||
ndv.getters.container().should('not.be.visible');
|
|
||||||
cy.shouldNotHaveConsoleErrors();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show correct validation state for resource locator params', () => {
|
|
||||||
workflowPage.actions.addNodeToCanvas('Typeform', true, true);
|
|
||||||
ndv.getters.container().should('be.visible');
|
|
||||||
cy.get('.has-issues').should('have.length', 0);
|
|
||||||
cy.get('[class*=hasIssues]').should('have.length', 0);
|
|
||||||
ndv.getters.backToCanvas().click();
|
|
||||||
// Both credentials and resource locator errors should be visible
|
|
||||||
workflowPage.actions.openNode('Typeform');
|
|
||||||
cy.get('.has-issues').should('have.length', 1);
|
|
||||||
cy.get('[class*=hasIssues]').should('have.length', 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show validation errors only after blur or re-opening of NDV', () => {
|
|
||||||
workflowPage.actions.addNodeToCanvas('Manual');
|
|
||||||
workflowPage.actions.addNodeToCanvas('Airtable', true, true, 'Search records');
|
|
||||||
ndv.getters.container().should('be.visible');
|
|
||||||
cy.get('.has-issues').should('have.length', 0);
|
|
||||||
ndv.getters.parameterInput('table').find('input').eq(1).focus().blur();
|
|
||||||
ndv.getters.parameterInput('base').find('input').eq(1).focus().blur();
|
|
||||||
cy.get('.has-issues').should('have.length', 2);
|
|
||||||
ndv.getters.backToCanvas().click();
|
|
||||||
workflowPage.actions.openNode('Search records');
|
|
||||||
cy.get('.has-issues').should('have.length', 2);
|
|
||||||
cy.get('[class*=hasIssues]').should('have.length', 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Correctly failing in V2 - node issues are only shows after execution
|
|
||||||
it('should show all validation errors when opening pasted node', () => {
|
|
||||||
cy.createFixtureWorkflow('Test_workflow_ndv_errors.json', 'Validation errors');
|
|
||||||
workflowPage.getters.canvasNodes().should('have.have.length', 1);
|
|
||||||
workflowPage.actions.openNode('Airtable');
|
|
||||||
cy.get('.has-issues').should('have.length', 3);
|
|
||||||
cy.get('[class*=hasIssues]').should('have.length', 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render run errors correctly', () => {
|
|
||||||
cy.createFixtureWorkflow('Test_workflow_ndv_run_error.json', 'Run error');
|
|
||||||
workflowPage.actions.openNode('Error');
|
|
||||||
ndv.actions.execute();
|
|
||||||
ndv.getters
|
|
||||||
.nodeRunErrorMessage()
|
|
||||||
.should(
|
|
||||||
'have.text',
|
|
||||||
"Paired item data for item from node 'Break pairedItem chain' is unavailable. Ensure 'Break pairedItem chain' is providing the required output.",
|
|
||||||
);
|
|
||||||
ndv.getters
|
|
||||||
.nodeRunErrorDescription()
|
|
||||||
.should(
|
|
||||||
'contains.text',
|
|
||||||
"An expression here won't work because it uses .item and n8n can't figure out the matching item.",
|
|
||||||
);
|
|
||||||
ndv.getters.nodeRunErrorIndicator().should('be.visible');
|
|
||||||
ndv.getters.nodeRunTooltipIndicator().should('be.visible');
|
|
||||||
// The error details should be hidden behind a tooltip
|
|
||||||
ndv.getters.nodeRunTooltipIndicator().should('not.contain', 'Start Time');
|
|
||||||
ndv.getters.nodeRunTooltipIndicator().should('not.contain', 'Execution Time');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should save workflow using keyboard shortcut from NDV', () => {
|
|
||||||
workflowPage.actions.addNodeToCanvas('Manual');
|
|
||||||
workflowPage.actions.addNodeToCanvas('Set', true, true);
|
|
||||||
ndv.getters.container().should('be.visible');
|
|
||||||
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
|
|
||||||
workflowPage.getters.isWorkflowSaved();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('test output schema view', () => {
|
|
||||||
const schemaKeys = [
|
|
||||||
'id',
|
|
||||||
'name',
|
|
||||||
'email',
|
|
||||||
'notes',
|
|
||||||
'country',
|
|
||||||
'created',
|
|
||||||
'objectValue',
|
|
||||||
'prop1',
|
|
||||||
'prop2',
|
|
||||||
];
|
|
||||||
function setupSchemaWorkflow() {
|
|
||||||
cy.createFixtureWorkflow('Test_workflow_schema_test.json');
|
|
||||||
workflowPage.actions.zoomToFit();
|
|
||||||
workflowPage.actions.openNode('Set');
|
|
||||||
ndv.actions.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should switch to output schema view and validate it', () => {
|
|
||||||
setupSchemaWorkflow();
|
|
||||||
ndv.getters.outputDisplayMode().children().should('have.length', 3);
|
|
||||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Table');
|
|
||||||
ndv.actions.switchOutputMode('Schema');
|
|
||||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Schema');
|
|
||||||
|
|
||||||
schemaKeys.forEach((key) => {
|
|
||||||
ndv.getters
|
|
||||||
.outputPanel()
|
|
||||||
.find('[data-test-id=run-data-schema-item]')
|
|
||||||
.contains(key)
|
|
||||||
.should('exist');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('should preserve schema view after execution', () => {
|
|
||||||
setupSchemaWorkflow();
|
|
||||||
ndv.actions.switchOutputMode('Schema');
|
|
||||||
ndv.actions.execute();
|
|
||||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Schema');
|
|
||||||
});
|
|
||||||
it('should collapse and expand nested schema object', () => {
|
|
||||||
setupSchemaWorkflow();
|
|
||||||
const expandedObjectProps = ['prop1', 'prop2'];
|
|
||||||
const getObjectValueItem = () =>
|
|
||||||
ndv.getters
|
|
||||||
.outputPanel()
|
|
||||||
.find('[data-test-id=run-data-schema-item]')
|
|
||||||
.filter(':contains("objectValue")');
|
|
||||||
ndv.actions.switchOutputMode('Schema');
|
|
||||||
|
|
||||||
expandedObjectProps.forEach((key) => {
|
|
||||||
ndv.getters
|
|
||||||
.outputPanel()
|
|
||||||
.find('[data-test-id=run-data-schema-item]')
|
|
||||||
.contains(key)
|
|
||||||
.should('be.visible');
|
|
||||||
});
|
|
||||||
getObjectValueItem().find('.toggle').click({ force: true });
|
|
||||||
expandedObjectProps.forEach((key) => {
|
|
||||||
ndv.getters
|
|
||||||
.outputPanel()
|
|
||||||
.find('[data-test-id=run-data-schema-item]')
|
|
||||||
.contains(key)
|
|
||||||
.should('not.be.visible');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not display pagination for schema', () => {
|
|
||||||
setupSchemaWorkflow();
|
|
||||||
ndv.getters.backToCanvas().click();
|
|
||||||
workflowPage.actions.deselectAll();
|
|
||||||
workflowPage.getters.canvasNodeByName('Set').click();
|
|
||||||
workflowPage.actions.addNodeToCanvas(
|
|
||||||
'Customer Datastore (n8n training)',
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
'Get All People',
|
|
||||||
);
|
|
||||||
ndv.actions.execute();
|
|
||||||
ndv.getters.outputPanel().contains('25 items').should('exist');
|
|
||||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
|
|
||||||
ndv.actions.switchOutputMode('Schema');
|
|
||||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('not.exist');
|
|
||||||
ndv.actions.switchOutputMode('JSON');
|
|
||||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
|
|
||||||
});
|
|
||||||
it('should display large schema', () => {
|
|
||||||
cy.createFixtureWorkflow(
|
|
||||||
'Test_workflow_schema_test_pinned_data.json',
|
|
||||||
'NDV test schema view 2',
|
|
||||||
);
|
|
||||||
workflowPage.actions.zoomToFit();
|
|
||||||
workflowPage.actions.openNode('Set');
|
|
||||||
|
|
||||||
ndv.getters.outputPanel().contains('20 items').should('exist');
|
|
||||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
|
|
||||||
ndv.actions.switchOutputMode('Schema');
|
|
||||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('not.exist');
|
|
||||||
ndv.getters
|
|
||||||
.outputPanel()
|
|
||||||
.find('[data-test-id=run-data-schema-item]')
|
|
||||||
.should('have.length.above', 10);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can link and unlink run selectors between input and output', () => {
|
|
||||||
cy.createFixtureWorkflow('Test_workflow_5.json', 'Test');
|
|
||||||
workflowPage.actions.zoomToFit();
|
|
||||||
workflowPage.actions.executeWorkflow();
|
|
||||||
workflowPage.actions.openNode('Set3');
|
|
||||||
|
|
||||||
ndv.actions.switchInputMode('Table');
|
|
||||||
ndv.actions.switchOutputMode('Table');
|
|
||||||
|
|
||||||
// Start from linked state
|
|
||||||
ndv.getters.outputLinkRun().then(($el) => {
|
|
||||||
const classList = Array.from($el[0].classList);
|
|
||||||
if (!classList.includes('linked')) {
|
|
||||||
ndv.actions.toggleOutputRunLinking();
|
|
||||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ndv.getters
|
|
||||||
.inputRunSelector()
|
|
||||||
.should('exist')
|
|
||||||
.find('input')
|
|
||||||
.should('include.value', '2 of 2 (6 items)');
|
|
||||||
ndv.getters
|
|
||||||
.outputRunSelector()
|
|
||||||
.should('exist')
|
|
||||||
.find('input')
|
|
||||||
.should('include.value', '2 of 2 (6 items)');
|
|
||||||
|
|
||||||
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
|
|
||||||
ndv.getters.inputRunSelector().find('input').should('include.value', '1 of 2 (6 items)');
|
|
||||||
ndv.getters.inputTbodyCell(1, 0).should('have.text', '1111');
|
|
||||||
ndv.getters.outputTbodyCell(1, 0).should('have.text', '1111');
|
|
||||||
|
|
||||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
|
||||||
ndv.actions.changeInputRunSelector('2 of 2 (6 items)');
|
|
||||||
ndv.getters.outputRunSelector().find('input').should('include.value', '2 of 2 (6 items)');
|
|
||||||
|
|
||||||
// unlink
|
|
||||||
ndv.actions.toggleOutputRunLinking();
|
|
||||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
|
||||||
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
|
|
||||||
ndv.getters
|
|
||||||
.inputRunSelector()
|
|
||||||
.should('exist')
|
|
||||||
.find('input')
|
|
||||||
.should('include.value', '2 of 2 (6 items)');
|
|
||||||
|
|
||||||
// link again
|
|
||||||
ndv.actions.toggleOutputRunLinking();
|
|
||||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
|
||||||
ndv.getters.inputRunSelector().find('input').should('include.value', '1 of 2 (6 items)');
|
|
||||||
|
|
||||||
// unlink again
|
|
||||||
ndv.actions.toggleInputRunLinking();
|
|
||||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
|
||||||
ndv.actions.changeInputRunSelector('2 of 2 (6 items)');
|
|
||||||
ndv.getters.outputRunSelector().find('input').should('include.value', '1 of 2 (6 items)');
|
|
||||||
|
|
||||||
// link again
|
|
||||||
ndv.actions.toggleInputRunLinking();
|
|
||||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
|
||||||
ndv.getters.outputRunSelector().find('input').should('include.value', '2 of 2 (6 items)');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display parameter hints correctly', () => {
|
|
||||||
workflowPage.actions.visit();
|
|
||||||
|
|
||||||
cy.createFixtureWorkflow('Test_workflow_3.json', 'My test workflow 1');
|
|
||||||
workflowPage.actions.openNode('Set1');
|
|
||||||
|
|
||||||
ndv.actions.typeIntoParameterInput('value', '='); // switch to expressions
|
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
input: 'hello',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '',
|
|
||||||
output: '[empty]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: ' test',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: ' ',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '<div></div>',
|
|
||||||
},
|
|
||||||
].forEach(({ input, output }) => {
|
|
||||||
if (input) {
|
|
||||||
ndv.actions.typeIntoParameterInput('value', input);
|
|
||||||
}
|
|
||||||
ndv.getters.parameterInput('name').click(); // remove focus from input, hide expression preview
|
|
||||||
|
|
||||||
ndv.actions.validateExpressionPreview('value', output ?? input);
|
|
||||||
ndv.getters.parameterInput('value').clear();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('webhook shoul fallback to webhookId if path is empty', () => {
|
|
||||||
workflowPage.actions.addInitialNodeToCanvas('Webhook');
|
|
||||||
workflowPage.getters.canvasNodes().first().dblclick();
|
|
||||||
|
|
||||||
workflowPage.getters.nodeIssuesByName('Webhook').should('not.exist');
|
|
||||||
ndv.getters.nodeExecuteButton().should('not.be.disabled');
|
|
||||||
ndv.getters.triggerPanelExecuteButton().should('exist');
|
|
||||||
|
|
||||||
ndv.getters.parameterInput('path').clear();
|
|
||||||
|
|
||||||
const urrWithUUID =
|
|
||||||
/https?:\/\/[^\/]+\/webhook-test\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
|
|
||||||
|
|
||||||
cy.contains('Webhook URLs')
|
|
||||||
.parent()
|
|
||||||
.invoke('text')
|
|
||||||
.then((text) => {
|
|
||||||
const match = text.match(urrWithUUID);
|
|
||||||
expect(match, 'Should contain dynamic URL with UUID').to.not.be.null;
|
|
||||||
});
|
|
||||||
|
|
||||||
ndv.actions.close();
|
|
||||||
|
|
||||||
workflowPage.getters.canvasNodes().first().dblclick();
|
|
||||||
ndv.getters.parameterInput('path').type('test-path');
|
|
||||||
|
|
||||||
const urlWithCustomPath = /https?:\/\/[^\/]+\/webhook-test\/test-path/i;
|
|
||||||
|
|
||||||
cy.contains('Webhook URLs')
|
|
||||||
.parent()
|
|
||||||
.invoke('text')
|
|
||||||
.then((text) => {
|
|
||||||
const match = text.match(urlWithCustomPath);
|
|
||||||
expect(match, 'Should contain URL with custom path').to.not.be.null;
|
|
||||||
});
|
|
||||||
|
|
||||||
ndv.getters.nodeExecuteButton().should('not.be.disabled');
|
|
||||||
ndv.getters.triggerPanelExecuteButton().should('exist');
|
|
||||||
|
|
||||||
ndv.actions.close();
|
|
||||||
workflowPage.getters.nodeIssuesByName('Webhook').should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not push NDV header out with a lot of code in Code node editor', () => {
|
|
||||||
workflowPage.actions.addInitialCodeNodeToCanvas({ keepNdvOpen: true });
|
|
||||||
ndv.getters.parameterInput('jsCode').get('.cm-content').type('{selectall}').type('{backspace}');
|
|
||||||
cy.fixture('Dummy_javascript.txt').then((code) => {
|
|
||||||
ndv.getters.parameterInput('jsCode').get('.cm-content').paste(code);
|
|
||||||
});
|
|
||||||
ndv.getters.nodeExecuteButton().should('be.visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow editing code in fullscreen in the code editors', () => {
|
|
||||||
// Code (JavaScript)
|
|
||||||
workflowPage.actions.addInitialCodeNodeToCanvas({ keepNdvOpen: true });
|
|
||||||
ndv.actions.openCodeEditorFullscreen();
|
|
||||||
|
|
||||||
ndv.getters.codeEditorFullscreen().type('{selectall}').type('{backspace}').type('foo()');
|
|
||||||
ndv.getters.codeEditorFullscreen().should('contain.text', 'foo()');
|
|
||||||
ndv.getters.codeEditorDialog().find('.el-dialog__close').click();
|
|
||||||
ndv.getters.parameterInput('jsCode').get('.cm-content').should('contain.text', 'foo()');
|
|
||||||
ndv.actions.close();
|
|
||||||
|
|
||||||
// SQL
|
|
||||||
workflowPage.actions.addNodeToCanvas('Postgres', true, true, 'Execute a SQL query');
|
|
||||||
ndv.actions.openCodeEditorFullscreen();
|
|
||||||
|
|
||||||
ndv.getters
|
|
||||||
.codeEditorFullscreen()
|
|
||||||
.type('{selectall}')
|
|
||||||
.type('{backspace}')
|
|
||||||
.paste('SELECT * FROM workflows');
|
|
||||||
ndv.getters.codeEditorFullscreen().should('contain.text', 'SELECT * FROM workflows');
|
|
||||||
ndv.getters.codeEditorDialog().find('.el-dialog__close').click();
|
|
||||||
ndv.getters
|
|
||||||
.parameterInput('query')
|
|
||||||
.get('.cm-content')
|
|
||||||
.should('contain.text', 'SELECT * FROM workflows');
|
|
||||||
ndv.actions.close();
|
|
||||||
|
|
||||||
// HTML
|
|
||||||
workflowPage.actions.addNodeToCanvas('HTML', true, true, 'Generate HTML template');
|
|
||||||
ndv.actions.openCodeEditorFullscreen();
|
|
||||||
|
|
||||||
ndv.getters
|
|
||||||
.codeEditorFullscreen()
|
|
||||||
.type('{selectall}')
|
|
||||||
.type('{backspace}')
|
|
||||||
.type('<div>Hello World</div>');
|
|
||||||
ndv.getters.codeEditorFullscreen().should('contain.text', '<div>Hello World</div>');
|
|
||||||
ndv.getters.codeEditorDialog().find('.el-dialog__close').click();
|
|
||||||
ndv.getters
|
|
||||||
.parameterInput('html')
|
|
||||||
.get('.cm-content')
|
|
||||||
.should('contain.text', '<div>Hello World</div>');
|
|
||||||
ndv.actions.close();
|
|
||||||
|
|
||||||
// JSON
|
|
||||||
workflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, true, true);
|
|
||||||
setParameterSelectByContent('mode', 'JSON');
|
|
||||||
ndv.actions.openCodeEditorFullscreen();
|
|
||||||
ndv.getters
|
|
||||||
.codeEditorFullscreen()
|
|
||||||
.type('{selectall}')
|
|
||||||
.type('{backspace}')
|
|
||||||
.type('{ "key": "value" }', { parseSpecialCharSequences: false });
|
|
||||||
ndv.getters.codeEditorFullscreen().should('contain.text', '{ "key": "value" }');
|
|
||||||
cy.wait(200);
|
|
||||||
ndv.getters.codeEditorDialog().find('.el-dialog__close').click();
|
|
||||||
ndv.getters
|
|
||||||
.parameterInput('jsonOutput')
|
|
||||||
.get('.cm-content')
|
|
||||||
.should('contain.text', '{ "key": "value" }');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not retrieve remote options when a parameter value changes', () => {
|
|
||||||
cy.intercept(
|
|
||||||
'POST',
|
|
||||||
'/rest/dynamic-node-parameters/options',
|
|
||||||
cy.spy().as('fetchParameterOptions'),
|
|
||||||
);
|
|
||||||
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Remote Options' });
|
|
||||||
// Type something into the field
|
|
||||||
ndv.actions.typeIntoParameterInput('otherField', 'test');
|
|
||||||
// Should call the endpoint only once (on mount), not for every keystroke
|
|
||||||
cy.get('@fetchParameterOptions').should('have.been.calledOnce');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('floating nodes', () => {
|
|
||||||
function getFloatingNodeByPosition(
|
|
||||||
position: 'inputMain' | 'outputMain' | 'outputSub' | 'inputSub',
|
|
||||||
) {
|
|
||||||
return cy.get(`[data-node-placement=${position}]`);
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should traverse floating nodes with mouse', () => {
|
|
||||||
cy.createFixtureWorkflow('Floating_Nodes.json', 'Floating Nodes');
|
|
||||||
|
|
||||||
workflowPage.actions.deselectAll();
|
|
||||||
|
|
||||||
workflowPage.getters.canvasNodes().first().dblclick();
|
|
||||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
|
||||||
getFloatingNodeByPosition('outputMain').should('exist');
|
|
||||||
// Traverse 4 connected node forwards
|
|
||||||
Array.from(Array(4).keys()).forEach((i) => {
|
|
||||||
getFloatingNodeByPosition('outputMain').click({ force: true });
|
|
||||||
ndv.getters.nodeNameContainer().should('contain', `Node ${i + 1}`);
|
|
||||||
getFloatingNodeByPosition('inputMain').should('exist');
|
|
||||||
getFloatingNodeByPosition('outputMain').should('exist');
|
|
||||||
ndv.actions.close();
|
|
||||||
// These two lines are broken in V2
|
|
||||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
|
||||||
workflowPage.getters
|
|
||||||
.selectedNodes()
|
|
||||||
.first()
|
|
||||||
.should('contain', `Node ${i + 1}`);
|
|
||||||
workflowPage.getters.selectedNodes().first().dblclick();
|
|
||||||
});
|
|
||||||
getFloatingNodeByPosition('outputMain').click({ force: true });
|
|
||||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
|
||||||
// Traverse 4 connected node backwards
|
|
||||||
Array.from(Array(4).keys()).forEach((i) => {
|
|
||||||
getFloatingNodeByPosition('inputMain').click({ force: true });
|
|
||||||
ndv.getters.nodeNameContainer().should('contain', `Node ${4 - i}`);
|
|
||||||
getFloatingNodeByPosition('outputMain').should('exist');
|
|
||||||
getFloatingNodeByPosition('inputMain').should('exist');
|
|
||||||
});
|
|
||||||
getFloatingNodeByPosition('inputMain').click({ force: true });
|
|
||||||
workflowPage.getters
|
|
||||||
.selectedNodes()
|
|
||||||
.first()
|
|
||||||
.should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
|
||||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
|
||||||
getFloatingNodeByPosition('inputSub').should('not.exist');
|
|
||||||
getFloatingNodeByPosition('outputSub').should('not.exist');
|
|
||||||
ndv.actions.close();
|
|
||||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
|
||||||
workflowPage.getters
|
|
||||||
.selectedNodes()
|
|
||||||
.first()
|
|
||||||
.should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should traverse floating nodes with keyboard', () => {
|
|
||||||
cy.createFixtureWorkflow('Floating_Nodes.json', 'Floating Nodes');
|
|
||||||
workflowPage.actions.deselectAll();
|
|
||||||
|
|
||||||
workflowPage.getters.canvasNodes().first().dblclick();
|
|
||||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
|
||||||
getFloatingNodeByPosition('outputMain').should('exist');
|
|
||||||
// Traverse 4 connected node forwards
|
|
||||||
Array.from(Array(4).keys()).forEach((i) => {
|
|
||||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight']);
|
|
||||||
ndv.getters.nodeNameContainer().should('contain', `Node ${i + 1}`);
|
|
||||||
getFloatingNodeByPosition('inputMain').should('exist');
|
|
||||||
getFloatingNodeByPosition('outputMain').should('exist');
|
|
||||||
ndv.actions.close();
|
|
||||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
|
||||||
workflowPage.getters
|
|
||||||
.selectedNodes()
|
|
||||||
.first()
|
|
||||||
.should('contain', `Node ${i + 1}`);
|
|
||||||
workflowPage.getters.selectedNodes().first().dblclick();
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight']);
|
|
||||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
|
||||||
|
|
||||||
// Traverse 4 connected node backwards
|
|
||||||
Array.from(Array(4).keys()).forEach((i) => {
|
|
||||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft']);
|
|
||||||
ndv.getters.nodeNameContainer().should('contain', `Node ${4 - i}`);
|
|
||||||
getFloatingNodeByPosition('outputMain').should('exist');
|
|
||||||
getFloatingNodeByPosition('inputMain').should('exist');
|
|
||||||
});
|
|
||||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft']);
|
|
||||||
workflowPage.getters
|
|
||||||
.selectedNodes()
|
|
||||||
.first()
|
|
||||||
.should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
|
||||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
|
||||||
getFloatingNodeByPosition('inputSub').should('not.exist');
|
|
||||||
getFloatingNodeByPosition('outputSub').should('not.exist');
|
|
||||||
ndv.actions.close();
|
|
||||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
|
||||||
workflowPage.getters
|
|
||||||
.selectedNodes()
|
|
||||||
.first()
|
|
||||||
.should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should connect floating sub-nodes', () => {
|
|
||||||
const nodeCreator = new NodeCreator();
|
|
||||||
const connectionGroups = [
|
|
||||||
{
|
|
||||||
title: 'Language Models',
|
|
||||||
id: 'ai_languageModel',
|
|
||||||
index: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Tools',
|
|
||||||
id: 'ai_tool',
|
|
||||||
index: 0,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
workflowPage.actions.addInitialNodeToCanvas('AI Agent', { keepNdvOpen: true });
|
|
||||||
|
|
||||||
connectionGroups.forEach((group) => {
|
|
||||||
cy.getByTestId(`add-subnode-${group.id}-${group.index}`).should('exist');
|
|
||||||
cy.getByTestId(`add-subnode-${group.id}-${group.index}`).click();
|
|
||||||
|
|
||||||
cy.getByTestId('nodes-list-header').contains(group.title).should('exist');
|
|
||||||
// Add HTTP Request tool
|
|
||||||
nodeCreator.getters.getNthCreatorItem(2).click();
|
|
||||||
getFloatingNodeByPosition('outputSub').should('exist');
|
|
||||||
getFloatingNodeByPosition('outputSub').click({ force: true });
|
|
||||||
|
|
||||||
if (group.id === 'ai_languageModel') {
|
|
||||||
cy.getByTestId(`add-subnode-${group.id}-${group.index}`).should('not.exist');
|
|
||||||
} else {
|
|
||||||
cy.getByTestId(`add-subnode-${group.id}-${group.index}`).should('exist');
|
|
||||||
// Expand the subgroup
|
|
||||||
cy.getByTestId('subnode-connection-group-ai_tool-0').click();
|
|
||||||
cy.getByTestId(`add-subnode-${group.id}-${group.index}`).click();
|
|
||||||
// Add HTTP Request tool
|
|
||||||
nodeCreator.getters.getNthCreatorItem(2).click();
|
|
||||||
getFloatingNodeByPosition('outputSub').click({ force: true });
|
|
||||||
cy.getByTestId('subnode-connection-group-ai_tool-0')
|
|
||||||
.findChildByTestId('floating-subnode')
|
|
||||||
.should('have.length', 2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Since language model has no credentials set, it should show an error
|
|
||||||
// Since HTTP Request tool requires URL it would also show an error(2 errors, 1 for each tool node)
|
|
||||||
cy.get('[class*=hasIssues]').should('have.length', 3);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have the floating nodes in correct order', () => {
|
|
||||||
cy.createFixtureWorkflow('Floating_Nodes.json', 'Floating Nodes');
|
|
||||||
|
|
||||||
workflowPage.actions.deselectAll();
|
|
||||||
|
|
||||||
// The first merge node has the wires crossed, so `Edit Fields1` is first in the order of connected nodes
|
|
||||||
openNode('Merge');
|
|
||||||
getFloatingNodeByPosition('inputMain').should('exist');
|
|
||||||
getFloatingNodeByPosition('inputMain').should('have.length', 2);
|
|
||||||
getFloatingNodeByPosition('inputMain')
|
|
||||||
.first()
|
|
||||||
.should('have.attr', 'data-node-name', 'Edit Fields1');
|
|
||||||
getFloatingNodeByPosition('inputMain')
|
|
||||||
.last()
|
|
||||||
.should('have.attr', 'data-node-name', 'Edit Fields0');
|
|
||||||
|
|
||||||
clickGetBackToCanvas();
|
|
||||||
|
|
||||||
// The second merge node does not have wires crossed, so `Edit Fields0` is first
|
|
||||||
openNode('Merge1');
|
|
||||||
getFloatingNodeByPosition('inputMain').should('exist');
|
|
||||||
getFloatingNodeByPosition('inputMain').should('have.length', 2);
|
|
||||||
getFloatingNodeByPosition('inputMain')
|
|
||||||
.first()
|
|
||||||
.should('have.attr', 'data-node-name', 'Edit Fields0');
|
|
||||||
getFloatingNodeByPosition('inputMain')
|
|
||||||
.last()
|
|
||||||
.should('have.attr', 'data-node-name', 'Edit Fields1');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show node name and version in settings', () => {
|
|
||||||
cy.createFixtureWorkflow('Test_workflow_ndv_version.json', 'NDV test version');
|
|
||||||
|
|
||||||
workflowPage.actions.openNode('Edit Fields (old)');
|
|
||||||
ndv.actions.openSettings();
|
|
||||||
ndv.getters.nodeVersion().should('have.text', 'Set node version 2 (Latest version: 3.4)');
|
|
||||||
ndv.actions.close();
|
|
||||||
|
|
||||||
workflowPage.actions.openNode('Edit Fields (latest)');
|
|
||||||
ndv.actions.openSettings();
|
|
||||||
ndv.getters.nodeVersion().should('have.text', 'Edit Fields (Set) node version 3.4 (Latest)');
|
|
||||||
ndv.actions.close();
|
|
||||||
|
|
||||||
workflowPage.actions.openNode('Edit Fields (no typeVersion)');
|
|
||||||
ndv.actions.openSettings();
|
|
||||||
ndv.getters.nodeVersion().should('have.text', 'Edit Fields (Set) node version 3.4 (Latest)');
|
|
||||||
ndv.actions.close();
|
|
||||||
|
|
||||||
workflowPage.actions.openNode('Function');
|
|
||||||
ndv.actions.openSettings();
|
|
||||||
ndv.getters.nodeVersion().should('have.text', 'Function node version 1 (Deprecated)');
|
|
||||||
ndv.actions.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should render xml and html tags as strings and can search', () => {
|
|
||||||
cy.createFixtureWorkflow('Test_workflow_xml_output.json', 'test');
|
|
||||||
|
|
||||||
workflowPage.actions.executeWorkflow();
|
|
||||||
|
|
||||||
workflowPage.actions.openNode('Edit Fields');
|
|
||||||
|
|
||||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Table');
|
|
||||||
|
|
||||||
ndv.getters
|
|
||||||
.outputTableRow(1)
|
|
||||||
.should('include.text', '<?xml version="1.0" encoding="UTF-8"?> <library>');
|
|
||||||
|
|
||||||
cy.document().trigger('keyup', { key: '/' });
|
|
||||||
ndv.getters.searchInput().filter(':focus').type('<lib');
|
|
||||||
|
|
||||||
ndv.getters.outputTableRow(1).find('mark').should('have.text', '<lib');
|
|
||||||
|
|
||||||
ndv.getters.outputDisplayMode().find('label').eq(2).should('include.text', 'JSON');
|
|
||||||
ndv.getters
|
|
||||||
.outputDisplayMode()
|
|
||||||
.find('label')
|
|
||||||
.eq(2)
|
|
||||||
.scrollIntoView()
|
|
||||||
.should('be.visible')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
ndv.getters.outputDataContainer().find('.json-data').should('exist');
|
|
||||||
ndv.getters
|
|
||||||
.outputDataContainer()
|
|
||||||
.should(
|
|
||||||
'have.text',
|
|
||||||
'[{"body": "<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?> <library> <book> <title>Introduction to XML</title> <author>John Doe</author> <publication_year>2020</publication_year> <isbn>1234567890</isbn> </book> <book> <title>Data Science Basics</title> <author>Jane Smith</author> <publication_year>2019</publication_year> <isbn>0987654321</isbn> </book> <book> <title>Programming in Python</title> <author>Bob Johnson</author> <publication_year>2021</publication_year> <isbn>5432109876</isbn> </book> </library>"}]',
|
|
||||||
);
|
|
||||||
ndv.getters.outputDataContainer().find('mark').should('have.text', '<lib');
|
|
||||||
|
|
||||||
ndv.getters.outputDisplayMode().find('label').eq(0).should('include.text', 'Schema');
|
|
||||||
ndv.getters.outputDisplayMode().find('label').eq(0).click({ force: true });
|
|
||||||
ndv.getters
|
|
||||||
.outputDataContainer()
|
|
||||||
.findChildByTestId('run-data-schema-item-value')
|
|
||||||
.should('include.text', '<?xml version="1.0" encoding="UTF-8"?>');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should properly show node execution indicator', () => {
|
|
||||||
workflowPage.actions.addInitialCodeNodeToCanvas();
|
|
||||||
// Should not show run info before execution
|
|
||||||
ndv.getters.nodeRunSuccessIndicator().should('not.exist');
|
|
||||||
ndv.getters.nodeRunErrorIndicator().should('not.exist');
|
|
||||||
ndv.getters.nodeRunTooltipIndicator().should('not.exist');
|
|
||||||
ndv.getters.nodeExecuteButton().click();
|
|
||||||
ndv.getters.nodeRunSuccessIndicator().should('exist');
|
|
||||||
ndv.getters.nodeRunTooltipIndicator().should('exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should properly show node execution indicator for multiple nodes', () => {
|
|
||||||
workflowPage.actions.addInitialCodeNodeToCanvas();
|
|
||||||
ndv.actions.typeIntoParameterInput('jsCode', 'testets');
|
|
||||||
ndv.getters.backToCanvas().click();
|
|
||||||
workflowPage.actions.executeWorkflow();
|
|
||||||
// Manual tigger node should show success indicator
|
|
||||||
workflowPage.actions.openNode('When clicking ‘Execute workflow’');
|
|
||||||
ndv.getters.nodeRunSuccessIndicator().should('exist');
|
|
||||||
ndv.getters.nodeRunTooltipIndicator().should('exist');
|
|
||||||
// Code node should show error
|
|
||||||
ndv.getters.backToCanvas().click();
|
|
||||||
workflowPage.actions.openNode('Code');
|
|
||||||
ndv.getters.nodeRunErrorIndicator().should('exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should clear mismatched collection parameters', () => {
|
|
||||||
workflowPage.actions.addInitialNodeToCanvas('LDAP', {
|
|
||||||
keepNdvOpen: true,
|
|
||||||
action: 'Create a new entry',
|
|
||||||
});
|
|
||||||
// Add some attributes in Create operation
|
|
||||||
cy.getByTestId('parameter-item').contains('Add Attributes').click();
|
|
||||||
ndv.actions.changeNodeOperation('Update');
|
|
||||||
// Attributes should be empty after operation change
|
|
||||||
cy.getByTestId('parameter-item').contains('Currently no items exist').should('exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should keep RLC values after operation change', () => {
|
|
||||||
const TEST_DOC_ID = '1111';
|
|
||||||
workflowPage.actions.addInitialNodeToCanvas('Google Sheets', {
|
|
||||||
keepNdvOpen: true,
|
|
||||||
action: 'Append row in sheet',
|
|
||||||
});
|
|
||||||
ndv.actions.setRLCValue('documentId', TEST_DOC_ID);
|
|
||||||
ndv.actions.changeNodeOperation('Update Row');
|
|
||||||
ndv.getters.resourceLocatorInput('documentId').find('input').should('have.value', TEST_DOC_ID);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not clear resource/operation after credential change', () => {
|
|
||||||
workflowPage.actions.addInitialNodeToCanvas('Discord', {
|
|
||||||
keepNdvOpen: true,
|
|
||||||
action: 'Delete a message',
|
|
||||||
});
|
|
||||||
|
|
||||||
clickCreateNewCredential();
|
|
||||||
setCredentialValues({
|
|
||||||
botToken: 'sk_test_123',
|
|
||||||
});
|
|
||||||
|
|
||||||
ndv.getters.parameterInput('resource').find('input').should('have.value', 'Message');
|
|
||||||
ndv.getters.parameterInput('operation').find('input').should('have.value', 'Delete');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should show a notice when remote options cannot be fetched because of missing credentials', () => {
|
|
||||||
cy.intercept('POST', '/rest/dynamic-node-parameters/options', { statusCode: 403 }).as(
|
|
||||||
'parameterOptions',
|
|
||||||
);
|
|
||||||
|
|
||||||
workflowPage.actions.addInitialNodeToCanvas(NOTION_NODE_NAME, {
|
|
||||||
keepNdvOpen: true,
|
|
||||||
action: 'Update a database page',
|
|
||||||
});
|
|
||||||
|
|
||||||
ndv.actions.addItemToFixedCollection('propertiesUi');
|
|
||||||
ndv.getters
|
|
||||||
.parameterInput('key')
|
|
||||||
.find('input')
|
|
||||||
.should('have.value', 'Set up credential to see options');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should show error state when remote options cannot be fetched', () => {
|
|
||||||
cy.intercept('POST', '/rest/dynamic-node-parameters/options', { statusCode: 500 }).as(
|
|
||||||
'parameterOptions',
|
|
||||||
);
|
|
||||||
|
|
||||||
workflowPage.actions.addInitialNodeToCanvas(NOTION_NODE_NAME, {
|
|
||||||
keepNdvOpen: true,
|
|
||||||
action: 'Update a database page',
|
|
||||||
});
|
|
||||||
|
|
||||||
clickCreateNewCredential();
|
|
||||||
setCredentialValues({
|
|
||||||
apiKey: 'sk_test_123',
|
|
||||||
});
|
|
||||||
|
|
||||||
ndv.actions.addItemToFixedCollection('propertiesUi');
|
|
||||||
ndv.getters
|
|
||||||
.parameterInput('key')
|
|
||||||
.find('input')
|
|
||||||
.should('have.value', 'Error fetching options from Notion');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Correctly failing in V2 - NodeCreator is not opened after clicking on the link
|
|
||||||
it('Should open appropriate node creator after clicking on connection hint link', () => {
|
|
||||||
const nodeCreator = new NodeCreator();
|
|
||||||
const hintMapper = {
|
|
||||||
Memory: 'AI Nodes',
|
|
||||||
'Output Parser': 'AI Nodes',
|
|
||||||
'Token Splitter': 'Document Loaders',
|
|
||||||
Tool: 'AI Nodes',
|
|
||||||
Embeddings: 'Vector Stores',
|
|
||||||
'Vector Store': 'Retrievers',
|
|
||||||
};
|
|
||||||
cy.createFixtureWorkflow(
|
|
||||||
'open_node_creator_for_connection.json',
|
|
||||||
'open_node_creator_for_connection',
|
|
||||||
);
|
|
||||||
|
|
||||||
Object.entries(hintMapper).forEach(([node, group]) => {
|
|
||||||
workflowPage.actions.openNode(node);
|
|
||||||
// This fails to open the NodeCreator
|
|
||||||
cy.get('[data-action=openSelectiveNodeCreator]').contains('Insert one').click();
|
|
||||||
nodeCreator.getters.activeSubcategory().should('contain', group);
|
|
||||||
cy.realPress('Escape');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow selecting item for expressions', () => {
|
|
||||||
workflowPage.actions.visit();
|
|
||||||
|
|
||||||
cy.createFixtureWorkflow('Test_workflow_3.json', 'My test workflow 2');
|
|
||||||
workflowPage.actions.openNode('Set');
|
|
||||||
|
|
||||||
ndv.actions.typeIntoParameterInput('value', '='); // switch to expressions
|
|
||||||
ndv.actions.typeIntoParameterInput('value', '{{', {
|
|
||||||
parseSpecialCharSequences: false,
|
|
||||||
});
|
|
||||||
ndv.actions.typeIntoParameterInput('value', '$json.input[0].count');
|
|
||||||
ndv.getters.inlineExpressionEditorOutput().should('have.text', '0');
|
|
||||||
|
|
||||||
ndv.actions.expressionSelectNextItem();
|
|
||||||
ndv.getters.inlineExpressionEditorOutput().should('have.text', '1');
|
|
||||||
ndv.getters.inlineExpressionEditorItemInput().should('have.value', '1');
|
|
||||||
ndv.getters.inlineExpressionEditorItemNextButton().should('be.disabled');
|
|
||||||
|
|
||||||
ndv.actions.expressionSelectPrevItem();
|
|
||||||
ndv.getters.inlineExpressionEditorOutput().should('have.text', '0');
|
|
||||||
ndv.getters.inlineExpressionEditorItemInput().should('have.value', '0');
|
|
||||||
ndv.getters.inlineExpressionEditorItemPrevButton().should('be.disabled');
|
|
||||||
|
|
||||||
ndv.actions.expressionSelectItem(1);
|
|
||||||
ndv.getters.inlineExpressionEditorOutput().should('have.text', '1');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show data from the correct output in schema view', () => {
|
|
||||||
cy.createFixtureWorkflow('Test_workflow_multiple_outputs.json');
|
|
||||||
workflowPage.actions.zoomToFit();
|
|
||||||
|
|
||||||
workflowPage.actions.executeWorkflow();
|
|
||||||
workflowPage.actions.openNode('Only Item 1');
|
|
||||||
ndv.getters.inputPanel().should('be.visible');
|
|
||||||
ndv.getters
|
|
||||||
.inputPanel()
|
|
||||||
.find('[data-test-id=run-data-schema-item]')
|
|
||||||
.should('contain.text', 'onlyOnItem1');
|
|
||||||
ndv.actions.close();
|
|
||||||
|
|
||||||
workflowPage.actions.openNode('Only Item 2');
|
|
||||||
ndv.getters.inputPanel().should('be.visible');
|
|
||||||
ndv.getters
|
|
||||||
.inputPanel()
|
|
||||||
.find('[data-test-id=run-data-schema-item]')
|
|
||||||
.should('contain.text', 'onlyOnItem2');
|
|
||||||
ndv.actions.close();
|
|
||||||
|
|
||||||
workflowPage.actions.openNode('Only Item 3');
|
|
||||||
ndv.getters.inputPanel().should('be.visible');
|
|
||||||
ndv.getters
|
|
||||||
.inputPanel()
|
|
||||||
.find('[data-test-id=run-data-schema-item]')
|
|
||||||
.should('contain.text', 'onlyOnItem3');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should keep search expanded after Execute step node run', () => {
|
|
||||||
cy.createFixtureWorkflow('Test_ndv_search.json');
|
|
||||||
workflowPage.actions.zoomToFit();
|
|
||||||
workflowPage.actions.executeWorkflow();
|
|
||||||
workflowPage.actions.openNode('Edit Fields');
|
|
||||||
ndv.getters.outputPanel().should('be.visible');
|
|
||||||
ndv.getters.outputPanel().findChildByTestId('ndv-search').click().type('US');
|
|
||||||
ndv.getters.outputTableRow(1).find('mark').should('have.text', 'US');
|
|
||||||
|
|
||||||
ndv.actions.execute();
|
|
||||||
ndv.getters
|
|
||||||
.outputPanel()
|
|
||||||
.findChildByTestId('ndv-search')
|
|
||||||
.should('be.visible')
|
|
||||||
.should('have.value', 'US');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not show items count when seaching in schema view', () => {
|
|
||||||
cy.createFixtureWorkflow('Test_ndv_search.json');
|
|
||||||
workflowPage.actions.zoomToFit();
|
|
||||||
workflowPage.actions.openNode('Edit Fields');
|
|
||||||
ndv.getters.outputPanel().should('be.visible');
|
|
||||||
ndv.actions.execute();
|
|
||||||
ndv.actions.switchOutputMode('Schema');
|
|
||||||
ndv.getters.outputPanel().find('[data-test-id=ndv-search]').click().type('US');
|
|
||||||
ndv.getters.outputPanel().find('[data-test-id=ndv-items-count]').should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show additional tooltip when seaching in schema view if no matches', () => {
|
|
||||||
cy.createFixtureWorkflow('Test_ndv_search.json');
|
|
||||||
workflowPage.actions.zoomToFit();
|
|
||||||
workflowPage.actions.openNode('Edit Fields');
|
|
||||||
ndv.getters.outputPanel().should('be.visible');
|
|
||||||
ndv.actions.execute();
|
|
||||||
ndv.actions.switchOutputMode('Schema');
|
|
||||||
ndv.getters.outputPanel().find('[data-test-id=ndv-search]').click().type('foo');
|
|
||||||
ndv.getters
|
|
||||||
.outputPanel()
|
|
||||||
.contains('To search field values, switch to table or JSON view.')
|
|
||||||
.should('exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ADO-2931 - should handle multiple branches of the same input with the first branch empty correctly', () => {
|
|
||||||
cy.createFixtureWorkflow('Test_ndv_two_branches_of_same_parent_false_populated.json');
|
|
||||||
workflowPage.actions.zoomToFit();
|
|
||||||
workflowPage.actions.openNode('DebugHelper');
|
|
||||||
ndv.getters.inputPanel().should('be.visible');
|
|
||||||
ndv.getters.outputPanel().should('be.visible');
|
|
||||||
ndv.actions.execute();
|
|
||||||
// This ensures we rendered the inputPanel
|
|
||||||
ndv.getters
|
|
||||||
.inputPanel()
|
|
||||||
.find('[data-test-id=run-data-schema-item]')
|
|
||||||
.should('contain.text', 'a1');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
42
packages/testing/playwright/fixtures/Dummy_javascript.txt
Normal file
42
packages/testing/playwright/fixtures/Dummy_javascript.txt
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
var File = function(url, object){
|
||||||
|
File.list = Array.isArray(File.list)? File.list : [];
|
||||||
|
File.progress = File.progress || 0;
|
||||||
|
this.progress = 0;
|
||||||
|
this.object = object;
|
||||||
|
this.url = url;
|
||||||
|
};
|
||||||
|
|
||||||
|
File.indexOf = function(term){
|
||||||
|
for(var index in File.list){
|
||||||
|
var file = File.list[index];
|
||||||
|
if (file.equals(term) || file.url === term || file.object === term) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
File.find = function(term){
|
||||||
|
var index = File.indexOf(term);
|
||||||
|
return ~index && File.list[index];
|
||||||
|
};
|
||||||
|
|
||||||
|
File.prototype.equals = function(file){
|
||||||
|
var isFileType = file instanceof File;
|
||||||
|
return isFileType && this.url === file.url && this.object === file.object;
|
||||||
|
};
|
||||||
|
|
||||||
|
File.prototype.save = function(update){
|
||||||
|
update = typeof update === 'undefined'? true : update;
|
||||||
|
if(Array.isArray(File.list)){
|
||||||
|
var index = File.indexOf(this);
|
||||||
|
if(~index && update) {
|
||||||
|
File.list[index] = this;
|
||||||
|
console.warn('File `%s` has been loaded before and updated now for: %O.', this.url, this);
|
||||||
|
}else File.list.push(this);
|
||||||
|
console.log(File.list)
|
||||||
|
}else{
|
||||||
|
File.list = [this];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"name": "My workflow 8",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"operation": "getAllPeople",
|
||||||
|
"limit": 10
|
||||||
|
},
|
||||||
|
"id": "39cd80ce-5a8f-4339-b3d5-c4af969dd330",
|
||||||
|
"name": "Customer Datastore (n8n training)",
|
||||||
|
"type": "n8n-nodes-base.n8nTrainingCustomerDatastore",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [940, 680]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"values": {
|
||||||
|
"number": [
|
||||||
|
{
|
||||||
|
"name": "objectValue.prop1",
|
||||||
|
"value": 123
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"name": "objectValue.prop2",
|
||||||
|
"value": "someText"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"dotNotation": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "6e4490f6-ba95-4400-beec-2caefdd4895a",
|
||||||
|
"name": "Set",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1300, 680]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "58512a93-dabf-4584-817f-27c608c1bdd5",
|
||||||
|
"name": "When clicking ‘Execute workflow’",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [720, 680]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pinData": {},
|
||||||
|
"connections": {
|
||||||
|
"Customer Datastore (n8n training)": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Set",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"When clicking ‘Execute workflow’": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Customer Datastore (n8n training)",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active": false,
|
||||||
|
"settings": {},
|
||||||
|
"versionId": "4a4f292a-92be-427c-848a-9582527f5ed3",
|
||||||
|
"id": "8",
|
||||||
|
"meta": {
|
||||||
|
"instanceId": "032eceae7493054b723340499be69ecbf4cbe28a7ec6df676b759000750b968d"
|
||||||
|
},
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
@@ -17,6 +17,19 @@ export class CanvasPage extends BasePage {
|
|||||||
return this.page.getByTestId('node-creator-item-name').getByText(subItemText, { exact: true });
|
return this.page.getByTestId('node-creator-item-name').getByText(subItemText, { exact: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNthCreatorItem(index: number): Locator {
|
||||||
|
return this.page.getByTestId('node-creator-item').nth(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeCreatorHeader(text?: string) {
|
||||||
|
const header = this.page.getByTestId('nodes-list-header');
|
||||||
|
return text ? header.filter({ hasText: text }) : header.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
async selectNodeCreatorItemByText(nodeName: string) {
|
||||||
|
await this.page.getByText(nodeName).click();
|
||||||
|
}
|
||||||
|
|
||||||
nodeByName(nodeName: string): Locator {
|
nodeByName(nodeName: string): Locator {
|
||||||
return this.page.locator(`[data-test-id="canvas-node"][data-node-name="${nodeName}"]`);
|
return this.page.locator(`[data-test-id="canvas-node"][data-node-name="${nodeName}"]`);
|
||||||
}
|
}
|
||||||
@@ -546,6 +559,12 @@ export class CanvasPage extends BasePage {
|
|||||||
await this.getManualChatInput().fill(message);
|
await this.getManualChatInput().fill(message);
|
||||||
await this.getManualChatModal().locator('.chat-input-send-button').click();
|
await this.getManualChatModal().locator('.chat-input-send-button').click();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Get all currently selected nodes on the canvas
|
||||||
|
*/
|
||||||
|
getSelectedNodes() {
|
||||||
|
return this.page.locator('[data-test-id="canvas-node"].selected');
|
||||||
|
}
|
||||||
|
|
||||||
async openExecutions() {
|
async openExecutions() {
|
||||||
await this.page.getByTestId('radio-button-executions').click();
|
await this.page.getByTestId('radio-button-executions').click();
|
||||||
|
|||||||
@@ -23,11 +23,6 @@ export class NodeDetailsViewPage extends BasePage {
|
|||||||
return this.getContainer().locator('.parameter-item').filter({ hasText: labelName });
|
return this.getContainer().locator('.parameter-item').filter({ hasText: labelName });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill a parameter input field
|
|
||||||
* @param labelName - The label of the parameter e.g URL
|
|
||||||
* @param value - The value to fill in the input field e.g https://foo.bar
|
|
||||||
*/
|
|
||||||
async fillParameterInput(labelName: string, value: string) {
|
async fillParameterInput(labelName: string, value: string) {
|
||||||
await this.getParameterByLabel(labelName).getByTestId('parameter-input-field').fill(value);
|
await this.getParameterByLabel(labelName).getByTestId('parameter-input-field').fill(value);
|
||||||
}
|
}
|
||||||
@@ -119,21 +114,14 @@ export class NodeDetailsViewPage extends BasePage {
|
|||||||
return this.getOutputTableRow(row).locator('td').nth(col);
|
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) {
|
getOutputTbodyCell(row: number, col: number) {
|
||||||
return this.getOutputTable().locator('tbody tr').nth(row).locator('td').nth(col);
|
return this.getOutputTable().locator('tbody tr').nth(row).locator('td').nth(col);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pin data operations
|
|
||||||
async setPinnedData(data: object | string) {
|
async setPinnedData(data: object | string) {
|
||||||
const pinnedData = typeof data === 'string' ? data : JSON.stringify(data);
|
const pinnedData = typeof data === 'string' ? data : JSON.stringify(data);
|
||||||
await this.getEditPinnedDataButton().click();
|
await this.getEditPinnedDataButton().click();
|
||||||
|
|
||||||
// Wait for editor to appear and use broader selector
|
|
||||||
const editor = this.getOutputPanel().locator('[contenteditable="true"]');
|
const editor = this.getOutputPanel().locator('[contenteditable="true"]');
|
||||||
await editor.waitFor();
|
await editor.waitFor();
|
||||||
await editor.click();
|
await editor.click();
|
||||||
@@ -150,7 +138,6 @@ export class NodeDetailsViewPage extends BasePage {
|
|||||||
await editor.click();
|
await editor.click();
|
||||||
await editor.fill('');
|
await editor.fill('');
|
||||||
|
|
||||||
// Set clipboard data and paste
|
|
||||||
await this.page.evaluate(async (jsonData) => {
|
await this.page.evaluate(async (jsonData) => {
|
||||||
await navigator.clipboard.writeText(JSON.stringify(jsonData));
|
await navigator.clipboard.writeText(JSON.stringify(jsonData));
|
||||||
}, data);
|
}, data);
|
||||||
@@ -163,7 +150,6 @@ export class NodeDetailsViewPage extends BasePage {
|
|||||||
await this.getRunDataPaneHeader().locator('button:visible').filter({ hasText: 'Save' }).click();
|
await this.getRunDataPaneHeader().locator('button:visible').filter({ hasText: 'Save' }).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assignment collection methods for advanced tests
|
|
||||||
getAssignmentCollectionAdd(paramName: string) {
|
getAssignmentCollectionAdd(paramName: string) {
|
||||||
return this.page
|
return this.page
|
||||||
.getByTestId(`assignment-collection-${paramName}`)
|
.getByTestId(`assignment-collection-${paramName}`)
|
||||||
@@ -192,6 +178,10 @@ export class NodeDetailsViewPage extends BasePage {
|
|||||||
return await this.page.request.get(path);
|
return await this.page.request.get(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getWebhookUrl() {
|
||||||
|
return this.page.locator('.webhook-url').textContent();
|
||||||
|
}
|
||||||
|
|
||||||
getVisiblePoppers() {
|
getVisiblePoppers() {
|
||||||
return this.page.locator('.el-popper:visible');
|
return this.page.locator('.el-popper:visible');
|
||||||
}
|
}
|
||||||
@@ -206,104 +196,56 @@ export class NodeDetailsViewPage extends BasePage {
|
|||||||
async typeInExpressionEditor(text: string) {
|
async typeInExpressionEditor(text: string) {
|
||||||
const editor = this.getInlineExpressionEditorInput();
|
const editor = this.getInlineExpressionEditorInput();
|
||||||
await editor.click();
|
await editor.click();
|
||||||
// We have to use type() instead of fill() because the editor is a CodeMirror editor
|
|
||||||
await editor.type(text);
|
await editor.type(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get parameter input by name (for Code node and similar)
|
|
||||||
* @param parameterName - The name of the parameter e.g 'jsCode', 'mode'
|
|
||||||
*/
|
|
||||||
getParameterInput(parameterName: string) {
|
getParameterInput(parameterName: string) {
|
||||||
return this.page.getByTestId(`parameter-input-${parameterName}`);
|
return this.page.getByTestId(`parameter-input-${parameterName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get parameter input field
|
|
||||||
* @param parameterName - The name of the parameter
|
|
||||||
*/
|
|
||||||
getParameterInputField(parameterName: string) {
|
getParameterInputField(parameterName: string) {
|
||||||
return this.getParameterInput(parameterName).locator('input');
|
return this.getParameterInput(parameterName).locator('input');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Select option in parameter dropdown (improved with Playwright best practices)
|
|
||||||
* @param parameterName - The parameter name
|
|
||||||
* @param optionText - The text of the option to select
|
|
||||||
*/
|
|
||||||
async selectOptionInParameterDropdown(parameterName: string, optionText: string) {
|
async selectOptionInParameterDropdown(parameterName: string, optionText: string) {
|
||||||
const dropdown = this.getParameterInput(parameterName);
|
const dropdown = this.getParameterInput(parameterName);
|
||||||
await dropdown.click();
|
await dropdown.click();
|
||||||
|
|
||||||
// Wait for dropdown to be visible and select option - following Playwright best practices
|
|
||||||
await this.page.getByRole('option', { name: optionText }).click();
|
await this.page.getByRole('option', { name: optionText }).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Click parameter dropdown by name (test-id based selector)
|
|
||||||
* @param parameterName - The parameter name e.g 'httpMethod', 'authentication'
|
|
||||||
*/
|
|
||||||
async clickParameterDropdown(parameterName: string): Promise<void> {
|
async clickParameterDropdown(parameterName: string): Promise<void> {
|
||||||
await this.clickByTestId(`parameter-input-${parameterName}`);
|
await this.clickByTestId(`parameter-input-${parameterName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Select option from visible dropdown using Playwright role-based selectors
|
|
||||||
* This follows the pattern used in working n8n tests
|
|
||||||
* @param optionText - The text of the option to select
|
|
||||||
*/
|
|
||||||
async selectFromVisibleDropdown(optionText: string): Promise<void> {
|
async selectFromVisibleDropdown(optionText: string): Promise<void> {
|
||||||
// Use Playwright's role-based selector - this is more reliable than CSS selectors
|
|
||||||
await this.page.getByRole('option', { name: optionText }).click();
|
await this.page.getByRole('option', { name: optionText }).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill parameter input field by parameter name
|
|
||||||
* @param parameterName - The parameter name e.g 'path', 'url'
|
|
||||||
* @param value - The value to fill
|
|
||||||
*/
|
|
||||||
async fillParameterInputByName(parameterName: string, value: string): Promise<void> {
|
async fillParameterInputByName(parameterName: string, value: string): Promise<void> {
|
||||||
const input = this.getParameterInputField(parameterName);
|
const input = this.getParameterInputField(parameterName);
|
||||||
await input.click();
|
await input.click();
|
||||||
await input.fill(value);
|
await input.fill(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Click parameter options expansion (e.g. for Response Code)
|
|
||||||
*/
|
|
||||||
async clickParameterOptions(): Promise<void> {
|
async clickParameterOptions(): Promise<void> {
|
||||||
await this.page.locator('.param-options').click();
|
await this.page.locator('.param-options').click();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get visible Element UI popper (dropdown/popover)
|
|
||||||
* Ported from Cypress pattern with Playwright selectors
|
|
||||||
*/
|
|
||||||
getVisiblePopper() {
|
getVisiblePopper() {
|
||||||
return this.page.locator('.el-popper:visible');
|
return this.page.locator('.el-popper:visible');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for parameter dropdown to be visible and ready for interaction
|
|
||||||
* @param parameterName - The parameter name
|
|
||||||
*/
|
|
||||||
async waitForParameterDropdown(parameterName: string): Promise<void> {
|
async waitForParameterDropdown(parameterName: string): Promise<void> {
|
||||||
const dropdown = this.getParameterInput(parameterName);
|
const dropdown = this.getParameterInput(parameterName);
|
||||||
await dropdown.waitFor({ state: 'visible' });
|
await dropdown.waitFor({ state: 'visible' });
|
||||||
await expect(dropdown).toBeEnabled();
|
await expect(dropdown).toBeEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Click on a floating node in the NDV (for switching between connected nodes)
|
|
||||||
* @param nodeName - The name of the node to click
|
|
||||||
*/
|
|
||||||
async clickFloatingNode(nodeName: string) {
|
async clickFloatingNode(nodeName: string) {
|
||||||
await this.page.locator(`[data-test-id="floating-node"][data-node-name="${nodeName}"]`).click();
|
await this.page.locator(`[data-test-id="floating-node"][data-node-name="${nodeName}"]`).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the previous node (useful for providing input data)
|
|
||||||
*/
|
|
||||||
async executePrevious() {
|
async executePrevious() {
|
||||||
await this.clickByTestId('execute-previous-node');
|
await this.clickByTestId('execute-previous-node');
|
||||||
}
|
}
|
||||||
@@ -378,9 +320,14 @@ export class NodeDetailsViewPage extends BasePage {
|
|||||||
|
|
||||||
async setParameterDropdown(parameterName: string, optionText: string): Promise<void> {
|
async setParameterDropdown(parameterName: string, optionText: string): Promise<void> {
|
||||||
await this.getParameterInput(parameterName).click();
|
await this.getParameterInput(parameterName).click();
|
||||||
|
|
||||||
await this.page.getByRole('option', { name: optionText }).click();
|
await this.page.getByRole('option', { name: optionText }).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async changeNodeOperation(operationName: string): Promise<void> {
|
||||||
|
await this.setParameterDropdown('operation', operationName);
|
||||||
|
}
|
||||||
|
|
||||||
async setParameterInput(parameterName: string, value: string): Promise<void> {
|
async setParameterInput(parameterName: string, value: string): Promise<void> {
|
||||||
await this.fillParameterInputByName(parameterName, value);
|
await this.fillParameterInputByName(parameterName, value);
|
||||||
}
|
}
|
||||||
@@ -423,31 +370,21 @@ export class NodeDetailsViewPage extends BasePage {
|
|||||||
case 'switch':
|
case 'switch':
|
||||||
return await this.getSwitchParameterValue(parameterName);
|
return await this.getSwitchParameterValue(parameterName);
|
||||||
default:
|
default:
|
||||||
// Fallback for unknown types
|
|
||||||
return (await this.getParameterInput(parameterName).textContent()) ?? '';
|
return (await this.getParameterInput(parameterName).textContent()) ?? '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get value from a text parameter - simplified approach
|
|
||||||
*/
|
|
||||||
private async getTextParameterValue(parameterName: string): Promise<string> {
|
private async getTextParameterValue(parameterName: string): Promise<string> {
|
||||||
const parameterContainer = this.getParameterInput(parameterName);
|
const parameterContainer = this.getParameterInput(parameterName);
|
||||||
const input = parameterContainer.locator('input').first();
|
const input = parameterContainer.locator('input').first();
|
||||||
return await input.inputValue();
|
return await input.inputValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get value from a dropdown parameter
|
|
||||||
*/
|
|
||||||
private async getDropdownParameterValue(parameterName: string): Promise<string> {
|
private async getDropdownParameterValue(parameterName: string): Promise<string> {
|
||||||
const selectedOption = this.getParameterInput(parameterName).locator('.el-select__tags-text');
|
const selectedOption = this.getParameterInput(parameterName).locator('.el-select__tags-text');
|
||||||
return (await selectedOption.textContent()) ?? '';
|
return (await selectedOption.textContent()) ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get value from a switch parameter
|
|
||||||
*/
|
|
||||||
private async getSwitchParameterValue(parameterName: string): Promise<string> {
|
private async getSwitchParameterValue(parameterName: string): Promise<string> {
|
||||||
const switchElement = this.getParameterInput(parameterName).locator('.el-switch');
|
const switchElement = this.getParameterInput(parameterName).locator('.el-switch');
|
||||||
const isEnabled = (await switchElement.getAttribute('aria-checked')) === 'true';
|
const isEnabled = (await switchElement.getAttribute('aria-checked')) === 'true';
|
||||||
@@ -503,8 +440,12 @@ export class NodeDetailsViewPage extends BasePage {
|
|||||||
.first();
|
.first();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNodeInputOptions() {
|
||||||
|
return this.getInputPanel().getByTestId('ndv-input-select');
|
||||||
|
}
|
||||||
|
|
||||||
async selectInputNode(nodeName: string) {
|
async selectInputNode(nodeName: string) {
|
||||||
const inputSelect = this.getInputPanel().getByTestId('ndv-input-select');
|
const inputSelect = this.getNodeInputOptions();
|
||||||
await inputSelect.click();
|
await inputSelect.click();
|
||||||
await this.page.getByRole('option', { name: nodeName }).click();
|
await this.page.getByRole('option', { name: nodeName }).click();
|
||||||
}
|
}
|
||||||
@@ -572,6 +513,35 @@ export class NodeDetailsViewPage extends BasePage {
|
|||||||
return this.getInlineExpressionEditorInput().locator('.cm-content');
|
return this.getInlineExpressionEditorInput().locator('.cm-content');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getInlineExpressionEditorOutput() {
|
||||||
|
return this.page.getByTestId('inline-expression-editor-output');
|
||||||
|
}
|
||||||
|
|
||||||
|
getInlineExpressionEditorItemInput() {
|
||||||
|
return this.page.getByTestId('inline-expression-editor-item-input').locator('input');
|
||||||
|
}
|
||||||
|
|
||||||
|
getInlineExpressionEditorItemPrevButton() {
|
||||||
|
return this.page.getByTestId('inline-expression-editor-item-prev');
|
||||||
|
}
|
||||||
|
|
||||||
|
getInlineExpressionEditorItemNextButton() {
|
||||||
|
return this.page.getByTestId('inline-expression-editor-item-next');
|
||||||
|
}
|
||||||
|
|
||||||
|
async expressionSelectNextItem() {
|
||||||
|
await this.getInlineExpressionEditorItemNextButton().click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async expressionSelectPrevItem() {
|
||||||
|
await this.getInlineExpressionEditorItemPrevButton().click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async typeIntoParameterInput(parameterName: string, content: string): Promise<void> {
|
||||||
|
const input = this.getParameterInput(parameterName);
|
||||||
|
await input.type(content);
|
||||||
|
}
|
||||||
|
|
||||||
getInputTable() {
|
getInputTable() {
|
||||||
return this.getInputPanel().locator('table');
|
return this.getInputPanel().locator('table');
|
||||||
}
|
}
|
||||||
@@ -587,12 +557,10 @@ export class NodeDetailsViewPage extends BasePage {
|
|||||||
async toggleCodeMode(switchTo: 'Run Once for Each Item' | 'Run Once for All Items') {
|
async toggleCodeMode(switchTo: 'Run Once for Each Item' | 'Run Once for All Items') {
|
||||||
await this.getParameterInput('mode').click();
|
await this.getParameterInput('mode').click();
|
||||||
await this.page.getByRole('option', { name: switchTo }).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
|
// eslint-disable-next-line playwright/no-wait-for-timeout
|
||||||
await this.page.waitForTimeout(2500);
|
await this.page.waitForTimeout(2500);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pagination methods for output panel
|
|
||||||
getOutputPagination() {
|
getOutputPagination() {
|
||||||
return this.getOutputPanel().getByTestId('ndv-data-pagination');
|
return this.getOutputPanel().getByTestId('ndv-data-pagination');
|
||||||
}
|
}
|
||||||
@@ -616,17 +584,297 @@ export class NodeDetailsViewPage extends BasePage {
|
|||||||
return (await this.getOutputTbodyCell(row, col).textContent()) ?? '';
|
return (await this.getOutputTbodyCell(row, col).textContent()) ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set parameter input value by clearing and filling (for parameters without standard test-id)
|
|
||||||
* @param parameterName - The parameter name
|
|
||||||
* @param value - The value to set
|
|
||||||
*/
|
|
||||||
async setParameterInputValue(parameterName: string, value: string): Promise<void> {
|
async setParameterInputValue(parameterName: string, value: string): Promise<void> {
|
||||||
const input = this.getParameterInput(parameterName).locator('input');
|
const input = this.getParameterInput(parameterName).locator('input');
|
||||||
await input.clear();
|
await input.clear();
|
||||||
await input.fill(value);
|
await input.fill(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNodeRunErrorMessage() {
|
||||||
|
return this.page.getByTestId('node-error-message');
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeRunErrorDescription() {
|
||||||
|
return this.page.getByTestId('node-error-description');
|
||||||
|
}
|
||||||
|
|
||||||
|
getInputRunSelector() {
|
||||||
|
return this.getInputPanel().getByTestId('run-selector');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOutputRunSelector() {
|
||||||
|
return this.getOutputPanel().getByTestId('run-selector');
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleOutputRunLinking() {
|
||||||
|
await this.getOutputPanel().getByTestId('link-run').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleInputRunLinking() {
|
||||||
|
await this.getInputPanel().getByTestId('link-run').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
getOutputLinkRun() {
|
||||||
|
return this.getOutputPanel().getByTestId('link-run');
|
||||||
|
}
|
||||||
|
|
||||||
|
getInputLinkRun() {
|
||||||
|
return this.getInputPanel().getByTestId('link-run');
|
||||||
|
}
|
||||||
|
|
||||||
|
async isOutputRunLinkingEnabled() {
|
||||||
|
const linkButton = this.getOutputLinkRun();
|
||||||
|
const classList = await linkButton.getAttribute('class');
|
||||||
|
return classList?.includes('linked') ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async ensureOutputRunLinking(shouldBeLinked: boolean = true) {
|
||||||
|
const isLinked = await this.isOutputRunLinkingEnabled();
|
||||||
|
if (isLinked !== shouldBeLinked) {
|
||||||
|
await this.toggleOutputRunLinking();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async changeInputRunSelector(value: string) {
|
||||||
|
const selector = this.getInputRunSelector();
|
||||||
|
await selector.click();
|
||||||
|
await this.page.getByRole('option', { name: value }).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async changeOutputRunSelector(value: string) {
|
||||||
|
const selector = this.getOutputRunSelector();
|
||||||
|
await selector.click();
|
||||||
|
await this.page.getByRole('option', { name: value }).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getInputRunSelectorValue() {
|
||||||
|
return await this.getInputRunSelector().locator('input').inputValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOutputRunSelectorValue() {
|
||||||
|
return await this.getOutputRunSelector().locator('input').inputValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
getOutputDisplayMode() {
|
||||||
|
return this.getOutputPanel().getByTestId('ndv-output-display-mode');
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchemaViewItems() {
|
||||||
|
return this.getOutputPanel().locator('[data-test-id="run-data-schema-item"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchemaItem(key: string) {
|
||||||
|
return this.getSchemaViewItems().filter({ hasText: key });
|
||||||
|
}
|
||||||
|
|
||||||
|
async expandSchemaItem(itemText: string) {
|
||||||
|
const item = this.getSchemaItem(itemText);
|
||||||
|
await item.locator('.toggle').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
getPaginationContainer() {
|
||||||
|
return this.getOutputPanel().locator('[class*="_pagination"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
getExecuteNodeButton() {
|
||||||
|
return this.page.getByTestId('node-execute-button');
|
||||||
|
}
|
||||||
|
|
||||||
|
getTriggerPanelExecuteButton() {
|
||||||
|
return this.page.getByTestId('trigger-execute-button');
|
||||||
|
}
|
||||||
|
|
||||||
|
async openCodeEditorFullscreen() {
|
||||||
|
await this.page.getByTestId('code-editor-fullscreen-button').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
getCodeEditorFullscreen() {
|
||||||
|
return this.page.getByTestId('code-editor-fullscreen').locator('.cm-content');
|
||||||
|
}
|
||||||
|
|
||||||
|
getCodeEditorDialog() {
|
||||||
|
return this.page.locator('.el-dialog');
|
||||||
|
}
|
||||||
|
|
||||||
|
async closeCodeEditorDialog() {
|
||||||
|
await this.getCodeEditorDialog().locator('.el-dialog__close').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
getWebhookTriggerListening() {
|
||||||
|
return this.page.getByTestId('trigger-listening');
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeRunSuccessIndicator() {
|
||||||
|
return this.page.getByTestId('node-run-status-success');
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeRunErrorIndicator() {
|
||||||
|
return this.page.getByTestId('node-run-status-danger');
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeRunTooltipIndicator() {
|
||||||
|
return this.page.getByTestId('node-run-info');
|
||||||
|
}
|
||||||
|
|
||||||
|
async openSettings() {
|
||||||
|
await this.page.getByTestId('tab-settings').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeVersion() {
|
||||||
|
return this.page.getByTestId('node-version');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOutputSearchInput() {
|
||||||
|
return this.getOutputPanel().getByTestId('ndv-search');
|
||||||
|
}
|
||||||
|
|
||||||
|
getInputSearchInput() {
|
||||||
|
return this.getInputPanel().getByTestId('ndv-search');
|
||||||
|
}
|
||||||
|
|
||||||
|
async searchOutputData(searchTerm: string) {
|
||||||
|
const searchInput = this.getOutputSearchInput();
|
||||||
|
await searchInput.click();
|
||||||
|
await searchInput.fill(searchTerm);
|
||||||
|
}
|
||||||
|
|
||||||
|
async searchInputData(searchTerm: string) {
|
||||||
|
const searchInput = this.getInputSearchInput();
|
||||||
|
await searchInput.click();
|
||||||
|
await searchInput.fill(searchTerm);
|
||||||
|
}
|
||||||
|
|
||||||
|
getOutputItemsCount() {
|
||||||
|
return this.getOutputPanel().getByTestId('ndv-items-count');
|
||||||
|
}
|
||||||
|
|
||||||
|
getInputItemsCount() {
|
||||||
|
return this.getInputPanel().getByTestId('ndv-items-count');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type multiple values into the first available text parameter field
|
||||||
|
* Useful for testing multiple parameter changes
|
||||||
|
*/
|
||||||
|
async fillFirstAvailableTextParameterMultipleTimes(values: string[]) {
|
||||||
|
const firstTextField = this.getNodeParameters().locator('input[type="text"]').first();
|
||||||
|
await firstTextField.click();
|
||||||
|
|
||||||
|
for (const value of values) {
|
||||||
|
await firstTextField.fill(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getFloatingNodeByPosition(position: 'inputMain' | 'outputMain' | 'inputSub' | 'outputSub') {
|
||||||
|
return this.page.locator(`[data-node-placement="${position}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeNameContainer() {
|
||||||
|
return this.getContainer().getByTestId('node-title-container');
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickFloatingNodeByPosition(
|
||||||
|
position: 'inputMain' | 'outputMain' | 'inputSub' | 'outputSub',
|
||||||
|
) {
|
||||||
|
// eslint-disable-next-line playwright/no-force-option
|
||||||
|
await this.getFloatingNodeByPosition(position).click({ force: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
async navigateToNextFloatingNodeWithKeyboard() {
|
||||||
|
await this.page.keyboard.press('Shift+Meta+Alt+ArrowRight');
|
||||||
|
}
|
||||||
|
|
||||||
|
async navigateToPreviousFloatingNodeWithKeyboard() {
|
||||||
|
await this.page.keyboard.press('Shift+Meta+Alt+ArrowLeft');
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyFloatingNodeName(
|
||||||
|
position: 'inputMain' | 'outputMain' | 'inputSub' | 'outputSub',
|
||||||
|
nodeName: string,
|
||||||
|
index: number = 0,
|
||||||
|
) {
|
||||||
|
const floatingNode = this.getFloatingNodeByPosition(position).nth(index);
|
||||||
|
await expect(floatingNode).toHaveAttribute('data-node-name', nodeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFloatingNodeCount(position: 'inputMain' | 'outputMain' | 'inputSub' | 'outputSub') {
|
||||||
|
return await this.getFloatingNodeByPosition(position).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
getAddSubNodeButton(connectionType: string, index: number = 0) {
|
||||||
|
return this.page.getByTestId(`add-subnode-${connectionType}-${index}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSubNodeConnectionGroup(connectionType: string, index: number = 0) {
|
||||||
|
return this.page.getByTestId(`subnode-connection-group-${connectionType}-${index}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFloatingSubNodes(connectionType: string, index: number = 0) {
|
||||||
|
return this.getSubNodeConnectionGroup(connectionType, index).getByTestId('floating-subnode');
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodesWithIssues() {
|
||||||
|
return this.page.locator('[class*="hasIssues"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectAISubNode(connectionType: string, nodeName: string, index: number = 0) {
|
||||||
|
await this.getAddSubNodeButton(connectionType, index).click();
|
||||||
|
await this.page.getByText(nodeName).click();
|
||||||
|
await this.getFloatingNode().click();
|
||||||
|
}
|
||||||
|
|
||||||
|
getFloatingNode() {
|
||||||
|
return this.page.getByTestId('floating-node');
|
||||||
|
}
|
||||||
|
|
||||||
|
async getNodesWithIssuesCount() {
|
||||||
|
return await this.getNodesWithIssues().count();
|
||||||
|
}
|
||||||
|
|
||||||
|
async addItemToFixedCollection(collectionName: string) {
|
||||||
|
await this.page.getByTestId(`fixed-collection-${collectionName}`).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickParameterItemAction(actionText: string) {
|
||||||
|
await this.page.getByTestId('parameter-item').getByText(actionText).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
getParameterItemWithText(text: string) {
|
||||||
|
return this.page.getByTestId('parameter-item').getByText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
getParameterInputWithIssues(parameterPath: string) {
|
||||||
|
return this.page.locator(
|
||||||
|
`[data-test-id="parameter-input-field"][title*="${parameterPath}"][title*="has issues"]`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getResourceLocator(paramName: string) {
|
||||||
|
return this.page.getByTestId(`resource-locator-${paramName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getResourceLocatorInput(paramName: string) {
|
||||||
|
return this.getResourceLocator(paramName).getByTestId('rlc-input-container');
|
||||||
|
}
|
||||||
|
|
||||||
|
getResourceLocatorModeSelector(paramName: string) {
|
||||||
|
return this.getResourceLocator(paramName).getByTestId('rlc-mode-selector');
|
||||||
|
}
|
||||||
|
|
||||||
|
async setRLCValue(paramName: string, value: string): Promise<void> {
|
||||||
|
await this.getResourceLocatorModeSelector(paramName).click();
|
||||||
|
|
||||||
|
const visibleOptions = this.page.locator('.el-popper:visible .el-select-dropdown__item');
|
||||||
|
await visibleOptions.last().click();
|
||||||
|
|
||||||
|
const input = this.getResourceLocatorInput(paramName).locator('input');
|
||||||
|
await input.fill(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickNodeCreatorInsertOneButton() {
|
||||||
|
await this.page.getByText('Insert one').click();
|
||||||
|
}
|
||||||
|
|
||||||
getInputSelect() {
|
getInputSelect() {
|
||||||
return this.page.getByTestId('ndv-input-select').locator('input');
|
return this.page.getByTestId('ndv-input-select').locator('input');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ test.describe('Workflows', () => {
|
|||||||
|
|
||||||
// Search for specific workflow
|
// Search for specific workflow
|
||||||
await n8n.workflows.searchWorkflows(specificName);
|
await n8n.workflows.searchWorkflows(specificName);
|
||||||
await expect(n8n.workflows.getWorkflowItems()).toHaveCount(1);
|
|
||||||
await expect(n8n.workflows.getWorkflowByName(specificName)).toBeVisible();
|
await expect(n8n.workflows.getWorkflowByName(specificName)).toBeVisible();
|
||||||
|
|
||||||
// Search with partial term
|
// Search with partial term
|
||||||
|
|||||||
800
packages/testing/playwright/tests/ui/5-ndv.spec.ts
Normal file
800
packages/testing/playwright/tests/ui/5-ndv.spec.ts
Normal file
@@ -0,0 +1,800 @@
|
|||||||
|
import {
|
||||||
|
CODE_NODE_NAME,
|
||||||
|
CODE_NODE_DISPLAY_NAME,
|
||||||
|
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
||||||
|
} from '../../config/constants';
|
||||||
|
import { test, expect } from '../../fixtures/base';
|
||||||
|
import type { n8nPage } from '../../pages/n8nPage';
|
||||||
|
|
||||||
|
test.describe('NDV', () => {
|
||||||
|
test.beforeEach(async ({ n8n }) => {
|
||||||
|
await n8n.start.fromBlankCanvas();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show up when double clicked on a node and close when Back to canvas clicked', async ({
|
||||||
|
n8n,
|
||||||
|
}) => {
|
||||||
|
await n8n.canvas.addNode('Manual Trigger');
|
||||||
|
const canvasNodes = n8n.canvas.getCanvasNodes();
|
||||||
|
await canvasNodes.first().dblclick();
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
await n8n.ndv.clickBackToCanvasButton();
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeHidden();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show input panel when node is not connected', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.addNode('Manual Trigger');
|
||||||
|
await n8n.canvas.deselectAll();
|
||||||
|
await n8n.canvas.addNode('Edit Fields (Set)', { closeNDV: true });
|
||||||
|
const canvasNodes = n8n.canvas.getCanvasNodes();
|
||||||
|
await canvasNodes.last().dblclick();
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getInputPanel()).toContainText('Wire me up');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should test webhook node', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.addNode('Webhook', { closeNDV: false });
|
||||||
|
|
||||||
|
await n8n.ndv.execute();
|
||||||
|
|
||||||
|
const webhookUrl = await n8n.ndv.getWebhookUrl();
|
||||||
|
await expect(n8n.ndv.getWebhookTriggerListening()).toBeVisible();
|
||||||
|
const response = await n8n.ndv.makeWebhookRequest(webhookUrl as string);
|
||||||
|
expect(response.status()).toBe(200);
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getOutputPanel()).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getOutputDataContainer()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should change input and go back to canvas', async ({ n8n }) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('NDV-test-select-input.json');
|
||||||
|
await n8n.canvas.clickZoomToFitButton();
|
||||||
|
await n8n.canvas.getCanvasNodes().last().dblclick();
|
||||||
|
await n8n.ndv.execute();
|
||||||
|
|
||||||
|
await n8n.ndv.switchInputMode('Table');
|
||||||
|
|
||||||
|
await n8n.ndv.getNodeInputOptions().last().click();
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getInputPanel()).toContainText('start');
|
||||||
|
|
||||||
|
await n8n.ndv.clickBackToCanvasButton();
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeHidden();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show correct validation state for resource locator params', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.addNode('Typeform Trigger', { closeNDV: false });
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.clickBackToCanvasButton();
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Typeform Trigger');
|
||||||
|
await expect(n8n.canvas.getNodeIssuesByName('Typeform Trigger')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show validation errors only after blur or re-opening of NDV', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.addNode('Manual Trigger');
|
||||||
|
await n8n.canvas.addNode('Airtable', { closeNDV: false, action: 'Search records' });
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
await expect(n8n.canvas.getNodeIssuesByName('Airtable')).toBeHidden();
|
||||||
|
|
||||||
|
await n8n.ndv.getParameterInputField('table').nth(1).focus();
|
||||||
|
await n8n.ndv.getParameterInputField('table').nth(1).blur();
|
||||||
|
await n8n.ndv.getParameterInputField('base').nth(1).focus();
|
||||||
|
await n8n.ndv.getParameterInputField('base').nth(1).blur();
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getParameterInput('base')).toHaveClass(/has-issues|error|invalid/);
|
||||||
|
await expect(n8n.ndv.getParameterInput('table')).toHaveClass(/has-issues|error|invalid/);
|
||||||
|
|
||||||
|
await n8n.ndv.clickBackToCanvasButton();
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Search records');
|
||||||
|
await expect(n8n.canvas.getNodeIssuesByName('Search records')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show all validation errors when opening pasted node', async ({ n8n }) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('Test_workflow_ndv_errors.json');
|
||||||
|
const canvasNodes = n8n.canvas.getCanvasNodes();
|
||||||
|
await expect(canvasNodes).toHaveCount(1);
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Airtable');
|
||||||
|
await expect(n8n.canvas.getNodeIssuesByName('Airtable')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render run errors correctly', async ({ n8n }) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('Test_workflow_ndv_run_error.json');
|
||||||
|
await n8n.canvas.openNode('Error');
|
||||||
|
await n8n.ndv.execute();
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getNodeRunErrorMessage()).toHaveText(
|
||||||
|
"Paired item data for item from node 'Break pairedItem chain' is unavailable. Ensure 'Break pairedItem chain' is providing the required output.",
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getNodeRunErrorDescription()).toContainText(
|
||||||
|
"An expression here won't work because it uses .item and n8n can't figure out the matching item.",
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getNodeRunErrorMessage()).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getNodeRunErrorDescription()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should save workflow using keyboard shortcut from NDV', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.addNode('Manual Trigger');
|
||||||
|
await n8n.canvas.addNode('Edit Fields (Set)', { closeNDV: false });
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.page.keyboard.press('ControlOrMeta+s');
|
||||||
|
|
||||||
|
await expect(n8n.canvas.getWorkflowSaveButton()).toBeHidden();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('webhook should fallback to webhookId if path is empty', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.addNode('Webhook', { closeNDV: false });
|
||||||
|
|
||||||
|
await expect(n8n.canvas.getNodeIssuesByName('Webhook')).toBeHidden();
|
||||||
|
await expect(n8n.ndv.getExecuteNodeButton()).toBeEnabled();
|
||||||
|
await expect(n8n.ndv.getTriggerPanelExecuteButton()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.getParameterInputField('path').clear();
|
||||||
|
|
||||||
|
const webhookUrlsContainer = n8n.ndv.getContainer().getByText('Webhook URLs').locator('..');
|
||||||
|
const urlText = await webhookUrlsContainer.textContent();
|
||||||
|
const uuidRegex = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
|
||||||
|
expect(urlText).toMatch(uuidRegex);
|
||||||
|
|
||||||
|
await n8n.ndv.close();
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Webhook');
|
||||||
|
await n8n.ndv.fillParameterInput('path', 'test-path');
|
||||||
|
|
||||||
|
const updatedUrlText = await webhookUrlsContainer.textContent();
|
||||||
|
expect(updatedUrlText).toContain('test-path');
|
||||||
|
expect(updatedUrlText).not.toMatch(uuidRegex);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('test output schema view', () => {
|
||||||
|
const schemaKeys = [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'email',
|
||||||
|
'notes',
|
||||||
|
'country',
|
||||||
|
'created',
|
||||||
|
'objectValue',
|
||||||
|
'prop1',
|
||||||
|
'prop2',
|
||||||
|
];
|
||||||
|
|
||||||
|
const setupSchemaWorkflow = async (n8n: n8nPage) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('Test_workflow_schema_test.json');
|
||||||
|
await n8n.canvas.clickZoomToFitButton();
|
||||||
|
await n8n.canvas.openNode('Set');
|
||||||
|
await n8n.ndv.execute();
|
||||||
|
};
|
||||||
|
|
||||||
|
test('should switch to output schema view and validate it', async ({ n8n }) => {
|
||||||
|
await setupSchemaWorkflow(n8n);
|
||||||
|
await n8n.ndv.switchOutputMode('Schema');
|
||||||
|
|
||||||
|
for (const key of schemaKeys) {
|
||||||
|
await expect(n8n.ndv.getSchemaViewItems().filter({ hasText: key })).toBeVisible();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should preserve schema view after execution', async ({ n8n }) => {
|
||||||
|
await setupSchemaWorkflow(n8n);
|
||||||
|
await n8n.ndv.switchOutputMode('Schema');
|
||||||
|
await n8n.ndv.execute();
|
||||||
|
|
||||||
|
for (const key of schemaKeys) {
|
||||||
|
await expect(n8n.ndv.getSchemaViewItems().filter({ hasText: key })).toBeVisible();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should collapse and expand nested schema object', async ({ n8n }) => {
|
||||||
|
await setupSchemaWorkflow(n8n);
|
||||||
|
const expandedObjectProps = ['prop1', 'prop2'];
|
||||||
|
|
||||||
|
await n8n.ndv.switchOutputMode('Schema');
|
||||||
|
|
||||||
|
for (const key of expandedObjectProps) {
|
||||||
|
await expect(n8n.ndv.getSchemaViewItems().filter({ hasText: key })).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
const objectValueItem = n8n.ndv.getSchemaViewItems().filter({ hasText: 'objectValue' });
|
||||||
|
await objectValueItem.locator('.toggle').click();
|
||||||
|
|
||||||
|
for (const key of expandedObjectProps) {
|
||||||
|
await expect(n8n.ndv.getSchemaViewItems().filter({ hasText: key })).not.toBeInViewport();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not display pagination for schema', async ({ n8n }) => {
|
||||||
|
await setupSchemaWorkflow(n8n);
|
||||||
|
|
||||||
|
await n8n.ndv.clickBackToCanvasButton();
|
||||||
|
await n8n.canvas.deselectAll();
|
||||||
|
await n8n.canvas.nodeByName('Set').click();
|
||||||
|
await n8n.canvas.addNode('Customer Datastore (n8n training)');
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Customer Datastore (n8n training)');
|
||||||
|
|
||||||
|
await n8n.ndv.execute();
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getOutputPanel().getByText('5 items')).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.switchOutputMode('Schema');
|
||||||
|
|
||||||
|
const schemaItemsCount = await n8n.ndv.getSchemaViewItems().count();
|
||||||
|
expect(schemaItemsCount).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
await n8n.ndv.switchOutputMode('JSON');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should display large schema', async ({ n8n }) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('Test_workflow_schema_test_pinned_data.json');
|
||||||
|
await n8n.canvas.clickZoomToFitButton();
|
||||||
|
await n8n.canvas.openNode('Set');
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getOutputPanel().getByText('20 items')).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getOutputPanel().locator('[class*="_pagination"]')).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.switchOutputMode('Schema');
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getOutputPanel().locator('[class*="_pagination"]')).toBeHidden();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should display parameter hints correctly', async ({ n8n }) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('Test_workflow_3.json');
|
||||||
|
await n8n.canvas.openNode('Set1');
|
||||||
|
|
||||||
|
await n8n.ndv.getParameterInputField('value').clear();
|
||||||
|
await n8n.ndv.getParameterInputField('value').fill('=');
|
||||||
|
|
||||||
|
await n8n.ndv.getInlineExpressionEditorContent().fill('hello');
|
||||||
|
await n8n.ndv.getParameterInputField('name').click();
|
||||||
|
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('hello');
|
||||||
|
|
||||||
|
await n8n.ndv.getInlineExpressionEditorContent().fill('');
|
||||||
|
await n8n.ndv.getParameterInputField('name').click();
|
||||||
|
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('[empty]');
|
||||||
|
|
||||||
|
await n8n.ndv.getInlineExpressionEditorContent().fill(' test');
|
||||||
|
await n8n.ndv.getParameterInputField('name').click();
|
||||||
|
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText(' test');
|
||||||
|
|
||||||
|
await n8n.ndv.getInlineExpressionEditorContent().fill(' ');
|
||||||
|
await n8n.ndv.getParameterInputField('name').click();
|
||||||
|
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText(' ');
|
||||||
|
|
||||||
|
await n8n.ndv.getInlineExpressionEditorContent().fill('<div></div>');
|
||||||
|
await n8n.ndv.getParameterInputField('name').click();
|
||||||
|
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('<div></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should properly show node execution indicator', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.addNode('Manual Trigger');
|
||||||
|
await n8n.canvas.addNode('Code', { action: 'Code in JavaScript', closeNDV: false });
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getNodeRunSuccessIndicator()).toBeHidden();
|
||||||
|
await expect(n8n.ndv.getNodeRunErrorIndicator()).toBeHidden();
|
||||||
|
await expect(n8n.ndv.getNodeRunTooltipIndicator()).toBeHidden();
|
||||||
|
|
||||||
|
await n8n.ndv.execute();
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getNodeRunSuccessIndicator()).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getNodeRunTooltipIndicator()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show node name and version in settings', async ({ n8n }) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('Test_workflow_ndv_version.json');
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Edit Fields (old)');
|
||||||
|
await n8n.ndv.openSettings();
|
||||||
|
await expect(n8n.ndv.getNodeVersion()).toContainText('Set node version 2');
|
||||||
|
await expect(n8n.ndv.getNodeVersion()).toContainText('Latest version: 3.4');
|
||||||
|
await n8n.ndv.close();
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Edit Fields (latest)');
|
||||||
|
await n8n.ndv.openSettings();
|
||||||
|
await expect(n8n.ndv.getNodeVersion()).toContainText('Edit Fields (Set) node version 3.4');
|
||||||
|
await expect(n8n.ndv.getNodeVersion()).toContainText('Latest');
|
||||||
|
await n8n.ndv.close();
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Function');
|
||||||
|
await n8n.ndv.openSettings();
|
||||||
|
await expect(n8n.ndv.getNodeVersion()).toContainText('Function node version 1');
|
||||||
|
await expect(n8n.ndv.getNodeVersion()).toContainText('Deprecated');
|
||||||
|
await n8n.ndv.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not push NDV header out with a lot of code in Code node editor', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.addNode('Manual Trigger');
|
||||||
|
await n8n.canvas.addNode('Code', { action: 'Code in JavaScript', closeNDV: false });
|
||||||
|
|
||||||
|
const codeEditor = n8n.ndv.getParameterInput('jsCode').locator('.cm-content');
|
||||||
|
await codeEditor.click();
|
||||||
|
await n8n.page.keyboard.press('ControlOrMeta+a');
|
||||||
|
await n8n.page.keyboard.press('Delete');
|
||||||
|
|
||||||
|
const dummyCode = Array(50)
|
||||||
|
.fill(
|
||||||
|
'console.log("This is a very long line of dummy JavaScript code that should not push the NDV header out of view");',
|
||||||
|
)
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
await codeEditor.fill(dummyCode);
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getExecuteNodeButton()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should allow editing code in fullscreen in the code editors', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.addNode('Manual Trigger');
|
||||||
|
await n8n.canvas.addNode('Code', { action: 'Code in JavaScript', closeNDV: false });
|
||||||
|
|
||||||
|
await n8n.ndv.openCodeEditorFullscreen();
|
||||||
|
|
||||||
|
const fullscreenEditor = n8n.ndv.getCodeEditorFullscreen();
|
||||||
|
await fullscreenEditor.click();
|
||||||
|
await n8n.page.keyboard.press('ControlOrMeta+a');
|
||||||
|
await fullscreenEditor.fill('foo()');
|
||||||
|
|
||||||
|
await expect(fullscreenEditor).toContainText('foo()');
|
||||||
|
|
||||||
|
await n8n.ndv.closeCodeEditorDialog();
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getParameterInput('jsCode').locator('.cm-content')).toContainText('foo()');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should keep search expanded after Execute step node run', async ({ n8n }) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('Test_ndv_search.json');
|
||||||
|
await n8n.canvas.clickZoomToFitButton();
|
||||||
|
await n8n.workflowComposer.executeWorkflowAndWaitForNotification(
|
||||||
|
'Workflow executed successfully',
|
||||||
|
);
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Edit Fields');
|
||||||
|
await expect(n8n.ndv.getOutputPanel()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.searchOutputData('US');
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getOutputTableRow(1).locator('mark')).toContainText('US');
|
||||||
|
|
||||||
|
await n8n.ndv.execute();
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getOutputSearchInput()).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getOutputSearchInput()).toHaveValue('US');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should render xml and html tags as strings and can search', async ({ n8n }) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('Test_workflow_xml_output.json');
|
||||||
|
await n8n.workflowComposer.executeWorkflowAndWaitForNotification(
|
||||||
|
'Workflow executed successfully',
|
||||||
|
);
|
||||||
|
await n8n.canvas.openNode('Edit Fields');
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getOutputPanel().locator('[class*="active"]')).toContainText('Table');
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getOutputTableRow(1)).toContainText(
|
||||||
|
'<?xml version="1.0" encoding="UTF-8"?> <library>',
|
||||||
|
);
|
||||||
|
|
||||||
|
await n8n.page.keyboard.press('/');
|
||||||
|
|
||||||
|
const searchInput = n8n.ndv.getOutputSearchInput();
|
||||||
|
await expect(searchInput).toBeFocused();
|
||||||
|
await searchInput.fill('<lib');
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getOutputTableRow(1).locator('mark')).toContainText('<lib');
|
||||||
|
|
||||||
|
await n8n.ndv.switchOutputMode('JSON');
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getOutputDataContainer().locator('.json-data')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Run Data & Selectors - Advanced', () => {
|
||||||
|
test('can link and unlink run selectors between input and output', async ({ n8n }) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('Test_workflow_5.json');
|
||||||
|
await n8n.canvas.clickZoomToFitButton();
|
||||||
|
await n8n.workflowComposer.executeWorkflowAndWaitForNotification(
|
||||||
|
'Workflow executed successfully',
|
||||||
|
);
|
||||||
|
await n8n.canvas.openNode('Set3');
|
||||||
|
|
||||||
|
await n8n.ndv.switchInputMode('Table');
|
||||||
|
await n8n.ndv.switchOutputMode('Table');
|
||||||
|
|
||||||
|
await n8n.ndv.ensureOutputRunLinking(true);
|
||||||
|
await n8n.ndv.getInputTbodyCell(0, 0).click();
|
||||||
|
expect(await n8n.ndv.getInputRunSelectorValue()).toContain('2 of 2 (6 items)');
|
||||||
|
expect(await n8n.ndv.getOutputRunSelectorValue()).toContain('2 of 2 (6 items)');
|
||||||
|
|
||||||
|
await n8n.ndv.changeOutputRunSelector('1 of 2 (6 items)');
|
||||||
|
expect(await n8n.ndv.getInputRunSelectorValue()).toContain('1 of 2 (6 items)');
|
||||||
|
await expect(n8n.ndv.getInputTbodyCell(0, 0)).toHaveText('1111');
|
||||||
|
await expect(n8n.ndv.getOutputTbodyCell(0, 0)).toHaveText('1111');
|
||||||
|
|
||||||
|
await n8n.ndv.getInputTbodyCell(0, 0).click();
|
||||||
|
await n8n.ndv.changeInputRunSelector('2 of 2 (6 items)');
|
||||||
|
expect(await n8n.ndv.getOutputRunSelectorValue()).toContain('2 of 2 (6 items)');
|
||||||
|
|
||||||
|
await n8n.ndv.toggleOutputRunLinking();
|
||||||
|
await n8n.ndv.getInputTbodyCell(0, 0).click();
|
||||||
|
await n8n.ndv.changeOutputRunSelector('1 of 2 (6 items)');
|
||||||
|
expect(await n8n.ndv.getInputRunSelectorValue()).toContain('2 of 2 (6 items)');
|
||||||
|
|
||||||
|
await n8n.ndv.toggleOutputRunLinking();
|
||||||
|
await n8n.ndv.getInputTbodyCell(0, 0).click();
|
||||||
|
expect(await n8n.ndv.getInputRunSelectorValue()).toContain('1 of 2 (6 items)');
|
||||||
|
|
||||||
|
await n8n.ndv.toggleInputRunLinking();
|
||||||
|
await n8n.ndv.getInputTbodyCell(0, 0).click();
|
||||||
|
await n8n.ndv.changeInputRunSelector('2 of 2 (6 items)');
|
||||||
|
expect(await n8n.ndv.getOutputRunSelectorValue()).toContain('1 of 2 (6 items)');
|
||||||
|
|
||||||
|
await n8n.ndv.toggleInputRunLinking();
|
||||||
|
await n8n.ndv.getInputTbodyCell(0, 0).click();
|
||||||
|
expect(await n8n.ndv.getOutputRunSelectorValue()).toContain('2 of 2 (6 items)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Remote Options & Network', () => {
|
||||||
|
test('should not retrieve remote options when a parameter value changes', async ({ n8n }) => {
|
||||||
|
let fetchParameterOptionsCallCount = 0;
|
||||||
|
await n8n.page.route('**/rest/dynamic-node-parameters/options', async (route) => {
|
||||||
|
fetchParameterOptionsCallCount++;
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({ data: [] }),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await n8n.canvas.addNode('E2E Test', { action: 'Remote Options' });
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.fillFirstAvailableTextParameterMultipleTimes(['test1', 'test2', 'test3']);
|
||||||
|
|
||||||
|
expect(fetchParameterOptionsCallCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should show a notice when remote options cannot be fetched because of missing credentials', async ({
|
||||||
|
n8n,
|
||||||
|
}) => {
|
||||||
|
await n8n.page.route('**/rest/dynamic-node-parameters/options', async (route) => {
|
||||||
|
await route.fulfill({ status: 403 });
|
||||||
|
});
|
||||||
|
|
||||||
|
await n8n.canvas.addNode('Manual Trigger');
|
||||||
|
await n8n.canvas.addNode('Notion', { action: 'Update a database page', closeNDV: false });
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.addItemToFixedCollection('propertiesUi');
|
||||||
|
await expect(
|
||||||
|
n8n.ndv.getParameterInputWithIssues('propertiesUi.propertyValues[0].key'),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should show error state when remote options cannot be fetched', async ({ n8n }) => {
|
||||||
|
await n8n.page.route('**/rest/dynamic-node-parameters/options', async (route) => {
|
||||||
|
await route.fulfill({ status: 500 });
|
||||||
|
});
|
||||||
|
|
||||||
|
await n8n.canvas.addNode('Manual Trigger');
|
||||||
|
await n8n.canvas.addNode('Notion', { action: 'Update a database page', closeNDV: false });
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.credentials.createAndSaveNewCredential('apiKey', 'sk_test_123');
|
||||||
|
await n8n.ndv.addItemToFixedCollection('propertiesUi');
|
||||||
|
await expect(
|
||||||
|
n8n.ndv.getParameterInputWithIssues('propertiesUi.propertyValues[0].key'),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Floating Nodes Navigation', () => {
|
||||||
|
test('should traverse floating nodes with mouse', async ({ n8n }) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('Floating_Nodes.json');
|
||||||
|
await n8n.canvas.getCanvasNodes().first().dblclick();
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('inputMain')).toBeHidden();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('outputMain')).toBeVisible();
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
await n8n.ndv.clickFloatingNodeByPosition('outputMain');
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('inputMain')).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('outputMain')).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.close();
|
||||||
|
await expect(n8n.canvas.getSelectedNodes()).toHaveCount(1);
|
||||||
|
|
||||||
|
await n8n.canvas.getSelectedNodes().first().dblclick();
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
await n8n.ndv.clickFloatingNodeByPosition('outputMain');
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('inputMain')).toBeVisible();
|
||||||
|
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
await n8n.ndv.clickFloatingNodeByPosition('inputMain');
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('outputMain')).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('inputMain')).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
await n8n.ndv.clickFloatingNodeByPosition('inputMain');
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('inputMain')).toBeHidden();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('inputSub')).toBeHidden();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('outputSub')).toBeHidden();
|
||||||
|
|
||||||
|
await n8n.ndv.close();
|
||||||
|
await expect(n8n.canvas.getSelectedNodes()).toHaveCount(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should traverse floating nodes with keyboard', async ({ n8n }) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('Floating_Nodes.json');
|
||||||
|
|
||||||
|
await n8n.canvas.getCanvasNodes().first().dblclick();
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('inputMain')).toBeHidden();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('outputMain')).toBeVisible();
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
await n8n.ndv.navigateToNextFloatingNodeWithKeyboard();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('inputMain')).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('outputMain')).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.close();
|
||||||
|
await expect(n8n.canvas.getSelectedNodes()).toHaveCount(1);
|
||||||
|
|
||||||
|
await n8n.canvas.getSelectedNodes().first().dblclick();
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
await n8n.ndv.navigateToNextFloatingNodeWithKeyboard();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('inputMain')).toBeVisible();
|
||||||
|
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
await n8n.ndv.navigateToPreviousFloatingNodeWithKeyboard();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('outputMain')).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('inputMain')).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
await n8n.ndv.navigateToPreviousFloatingNodeWithKeyboard();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('inputMain')).toBeHidden();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('inputSub')).toBeHidden();
|
||||||
|
await expect(n8n.ndv.getFloatingNodeByPosition('outputSub')).toBeHidden();
|
||||||
|
|
||||||
|
await n8n.ndv.close();
|
||||||
|
await expect(n8n.canvas.getSelectedNodes()).toHaveCount(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should connect floating sub-nodes', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.addNode('AI Agent', { closeNDV: false });
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.connectAISubNode('ai_languageModel', 'Anthropic Chat Model');
|
||||||
|
await n8n.ndv.connectAISubNode('ai_memory', 'Simple Memory');
|
||||||
|
await n8n.ndv.connectAISubNode('ai_tool', 'HTTP Request Tool');
|
||||||
|
|
||||||
|
expect(await n8n.ndv.getNodesWithIssuesCount()).toBeGreaterThanOrEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should have the floating nodes in correct order', async ({ n8n }) => {
|
||||||
|
await n8n.start.fromImportedWorkflow('Floating_Nodes.json');
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Merge');
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
expect(await n8n.ndv.getFloatingNodeCount('inputMain')).toBe(2);
|
||||||
|
await n8n.ndv.verifyFloatingNodeName('inputMain', 'Edit Fields1', 0);
|
||||||
|
await n8n.ndv.verifyFloatingNodeName('inputMain', 'Edit Fields0', 1);
|
||||||
|
|
||||||
|
await n8n.ndv.close();
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Merge1');
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
expect(await n8n.ndv.getFloatingNodeCount('inputMain')).toBe(2);
|
||||||
|
await n8n.ndv.verifyFloatingNodeName('inputMain', 'Edit Fields0', 0);
|
||||||
|
await n8n.ndv.verifyFloatingNodeName('inputMain', 'Edit Fields1', 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Parameter Management - Advanced', () => {
|
||||||
|
test('Should clear mismatched collection parameters', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.addNode('Manual Trigger');
|
||||||
|
await n8n.canvas.addNode('Notion', { action: 'Create a database page', closeNDV: false });
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.addItemToFixedCollection('propertiesUi');
|
||||||
|
await n8n.ndv.changeNodeOperation('Update');
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getParameterItemWithText('Currently no items exist')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should keep RLC values after operation change', async ({ n8n }) => {
|
||||||
|
const TEST_DOC_ID = '1111';
|
||||||
|
|
||||||
|
await n8n.canvas.addNode('Manual Trigger');
|
||||||
|
await n8n.canvas.addNode('Google Sheets', { closeNDV: false, action: 'Append row in sheet' });
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.setRLCValue('documentId', TEST_DOC_ID);
|
||||||
|
await n8n.ndv.changeNodeOperation('Append or Update Row');
|
||||||
|
const input = n8n.ndv.getResourceLocatorInput('documentId').locator('input');
|
||||||
|
await expect(input).toHaveValue(TEST_DOC_ID);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should not clear resource/operation after credential change', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.addNode('Manual Trigger');
|
||||||
|
await n8n.canvas.addNode('Discord', { closeNDV: false, action: 'Delete a message' });
|
||||||
|
await expect(n8n.ndv.getContainer()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.credentials.createAndSaveNewCredential('botToken', 'sk_test_123');
|
||||||
|
const resourceInput = n8n.ndv.getParameterInputField('resource');
|
||||||
|
const operationInput = n8n.ndv.getParameterInputField('operation');
|
||||||
|
|
||||||
|
await expect(resourceInput).toHaveValue('Message');
|
||||||
|
await expect(operationInput).toHaveValue('Delete');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Node Creator Integration', () => {
|
||||||
|
test('Should open appropriate node creator after clicking on connection hint link', async ({
|
||||||
|
n8n,
|
||||||
|
}) => {
|
||||||
|
const hintMapper = {
|
||||||
|
Memory: 'AI Nodes',
|
||||||
|
'Output Parser': 'AI Nodes',
|
||||||
|
'Token Splitter': 'Document Loaders',
|
||||||
|
Tool: 'AI Nodes',
|
||||||
|
Embeddings: 'Vector Stores',
|
||||||
|
'Vector Store': 'Retrievers',
|
||||||
|
};
|
||||||
|
|
||||||
|
await n8n.canvas.importWorkflow(
|
||||||
|
'open_node_creator_for_connection.json',
|
||||||
|
'open_node_creator_for_connection',
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const [node, group] of Object.entries(hintMapper)) {
|
||||||
|
await n8n.canvas.openNode(node);
|
||||||
|
|
||||||
|
await n8n.ndv.clickNodeCreatorInsertOneButton();
|
||||||
|
await expect(n8n.canvas.getNodeCreatorHeader(group)).toBeVisible();
|
||||||
|
await n8n.page.keyboard.press('Escape');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Expression Editor Features', () => {
|
||||||
|
test('should allow selecting item for expressions', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.importWorkflow('Test_workflow_3.json', 'My test workflow 2');
|
||||||
|
|
||||||
|
await n8n.workflowComposer.executeWorkflowAndWaitForNotification(
|
||||||
|
'Workflow executed successfully',
|
||||||
|
);
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Set');
|
||||||
|
|
||||||
|
await n8n.ndv.getAssignmentValue('assignments').getByText('Expression').click();
|
||||||
|
|
||||||
|
const expressionInput = n8n.ndv.getInlineExpressionEditorInput();
|
||||||
|
await expressionInput.click();
|
||||||
|
await n8n.ndv.clearExpressionEditor();
|
||||||
|
await n8n.ndv.typeInExpressionEditor('{{ $json.input[0].count');
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getInlineExpressionEditorOutput()).toHaveText('0');
|
||||||
|
|
||||||
|
await n8n.ndv.expressionSelectNextItem();
|
||||||
|
await expect(n8n.ndv.getInlineExpressionEditorOutput()).toHaveText('1');
|
||||||
|
await expect(n8n.ndv.getInlineExpressionEditorItemInput()).toHaveValue('1');
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getInlineExpressionEditorItemNextButton()).toBeDisabled();
|
||||||
|
|
||||||
|
await n8n.ndv.expressionSelectPrevItem();
|
||||||
|
await expect(n8n.ndv.getInlineExpressionEditorOutput()).toHaveText('0');
|
||||||
|
await expect(n8n.ndv.getInlineExpressionEditorItemInput()).toHaveValue('0');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Schema & Data Views', () => {
|
||||||
|
test('should show data from the correct output in schema view', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.importWorkflow('Test_workflow_multiple_outputs.json', 'Multiple outputs');
|
||||||
|
await n8n.workflowComposer.executeWorkflowAndWaitForNotification(
|
||||||
|
'Workflow executed successfully',
|
||||||
|
);
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Only Item 1');
|
||||||
|
await expect(n8n.ndv.getInputPanel()).toBeVisible();
|
||||||
|
await n8n.ndv.switchInputMode('Schema');
|
||||||
|
await expect(n8n.ndv.getInputSchemaItem('onlyOnItem1')).toBeVisible();
|
||||||
|
await n8n.ndv.close();
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Only Item 2');
|
||||||
|
await expect(n8n.ndv.getInputPanel()).toBeVisible();
|
||||||
|
await n8n.ndv.switchInputMode('Schema');
|
||||||
|
await expect(n8n.ndv.getInputSchemaItem('onlyOnItem2')).toBeVisible();
|
||||||
|
await n8n.ndv.close();
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Only Item 3');
|
||||||
|
await expect(n8n.ndv.getInputPanel()).toBeVisible();
|
||||||
|
await n8n.ndv.switchInputMode('Schema');
|
||||||
|
await expect(n8n.ndv.getInputSchemaItem('onlyOnItem3')).toBeVisible();
|
||||||
|
await n8n.ndv.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Search Functionality - Advanced', () => {
|
||||||
|
test('should not show items count when searching in schema view', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.importWorkflow('Test_ndv_search.json', 'NDV Search Test');
|
||||||
|
await n8n.canvas.openNode('Edit Fields');
|
||||||
|
await expect(n8n.ndv.getOutputPanel()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.execute();
|
||||||
|
await n8n.ndv.switchOutputMode('Schema');
|
||||||
|
await n8n.ndv.searchOutputData('US');
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getOutputItemsCount()).toBeHidden();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show additional tooltip when searching in schema view if no matches', async ({
|
||||||
|
n8n,
|
||||||
|
}) => {
|
||||||
|
await n8n.canvas.importWorkflow('Test_ndv_search.json', 'NDV Search Test');
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('Edit Fields');
|
||||||
|
await expect(n8n.ndv.getOutputPanel()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.execute();
|
||||||
|
await n8n.ndv.switchOutputMode('Schema');
|
||||||
|
await n8n.ndv.searchOutputData('foo');
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
n8n.ndv.getOutputPanel().getByText('To search field values, switch to table or JSON view.'),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Complex Edge Cases', () => {
|
||||||
|
test('ADO-2931 - should handle multiple branches of the same input with the first branch empty correctly', async ({
|
||||||
|
n8n,
|
||||||
|
}) => {
|
||||||
|
await n8n.canvas.importWorkflow(
|
||||||
|
'Test_ndv_two_branches_of_same_parent_false_populated.json',
|
||||||
|
'Multiple Branches Test',
|
||||||
|
);
|
||||||
|
|
||||||
|
await n8n.canvas.openNode('DebugHelper');
|
||||||
|
await expect(n8n.ndv.getInputPanel()).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getOutputPanel()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.execute();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
n8n.ndv.getInputPanel().getByTestId('run-data-schema-item').filter({ hasText: 'a1' }),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Execution Indicators - Multi-Node', () => {
|
||||||
|
test('should properly show node execution indicator for multiple nodes', async ({ n8n }) => {
|
||||||
|
await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript' });
|
||||||
|
await n8n.ndv.clickBackToCanvasButton();
|
||||||
|
|
||||||
|
await n8n.workflowComposer.executeWorkflowAndWaitForNotification(
|
||||||
|
'Workflow executed successfully',
|
||||||
|
);
|
||||||
|
|
||||||
|
await n8n.canvas.openNode(MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||||
|
await expect(n8n.ndv.getNodeRunSuccessIndicator()).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getNodeRunTooltipIndicator()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.clickBackToCanvasButton();
|
||||||
|
await n8n.canvas.openNode(CODE_NODE_DISPLAY_NAME);
|
||||||
|
await expect(n8n.ndv.getNodeRunSuccessIndicator()).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
221
packages/testing/playwright/workflows/Floating_Nodes.json
Normal file
221
packages/testing/playwright/workflows/Floating_Nodes.json
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
{
|
||||||
|
"name": "Floating Nodes",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "d0eda550-2526-42a1-aa19-dee411c8acf9",
|
||||||
|
"name": "When clicking ‘Execute workflow’",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [700, 560]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "30412165-1229-4b21-9890-05bfbd9952ab",
|
||||||
|
"name": "Node 1",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 3.2,
|
||||||
|
"position": [920, 560]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "201cc8fc-3124-47a3-bc08-b3917c1ddcd9",
|
||||||
|
"name": "Node 2",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 3.2,
|
||||||
|
"position": [1100, 560]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "a29802bb-a284-495d-9917-6c6e42fef01e",
|
||||||
|
"name": "Node 3",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 3.2,
|
||||||
|
"position": [1280, 560]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "a95a72b3-8b39-44e2-a05b-d8d677741c80",
|
||||||
|
"name": "Node 4",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 3.2,
|
||||||
|
"position": [1440, 560]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "4674f10d-6144-4a17-bbbb-350c3974438e",
|
||||||
|
"name": "Chain",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.chainLlm",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1580, 560]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "58e12ea5-bd3e-4abf-abec-fcfb5c0a7955",
|
||||||
|
"name": "Model",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1600, 740]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"type": "n8n-nodes-base.merge",
|
||||||
|
"typeVersion": 3.1,
|
||||||
|
"position": [440, -140],
|
||||||
|
"id": "a00959d3-8d4b-40af-b4f2-35ca3d73fd84",
|
||||||
|
"name": "Merge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 3.4,
|
||||||
|
"position": [-20, -120],
|
||||||
|
"id": "a5cbc221-ccfd-4034-a648-6a192834af81",
|
||||||
|
"name": "Edit Fields0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 3.4,
|
||||||
|
"position": [0, 100],
|
||||||
|
"id": "d3b4c17a-bee8-418b-a721-5debafd1ce11",
|
||||||
|
"name": "Edit Fields1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"type": "n8n-nodes-base.merge",
|
||||||
|
"typeVersion": 3.1,
|
||||||
|
"position": [440, 100],
|
||||||
|
"id": "b23a2a43-ffac-41a5-a265-054e21a57d70",
|
||||||
|
"name": "Merge1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pinData": {},
|
||||||
|
"connections": {
|
||||||
|
"When clicking ‘Execute workflow’": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Node 1",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Node 1": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Node 2",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Node 3": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Node 4",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Node 2": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Node 3",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Chain": {
|
||||||
|
"main": [[]]
|
||||||
|
},
|
||||||
|
"Model": {
|
||||||
|
"ai_languageModel": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Chain",
|
||||||
|
"type": "ai_languageModel",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Node 4": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Chain",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Edit Fields0": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Merge",
|
||||||
|
"type": "main",
|
||||||
|
"index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "Merge1",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Edit Fields1": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Merge",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "Merge1",
|
||||||
|
"type": "main",
|
||||||
|
"index": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active": false,
|
||||||
|
"settings": {
|
||||||
|
"executionOrder": "v1"
|
||||||
|
},
|
||||||
|
"versionId": "2730d156-a98a-4ac8-b481-5c16361fdba2",
|
||||||
|
"id": "6bzXMGxHuxeEaqsA",
|
||||||
|
"meta": {
|
||||||
|
"instanceId": "1838be0fa0389fbaf5e2e4aaedab4ddc79abc4175b433401abb22a281001b853"
|
||||||
|
},
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"name": "ff739753-9d6e-46a7-94d3-25bd03dd4973",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "c30d1114-d7f7-44dc-b55a-15312ef2d76d",
|
||||||
|
"name": "On clicking 'execute'",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [600, 580]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "5bf514bd-65ae-4a1c-8b69-a01bfeaad411",
|
||||||
|
"name": "Set",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [820, 580]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "02bf49e9-44b2-4f4e-8cb2-8c02399208af",
|
||||||
|
"name": "Set1",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1040, 580]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pinData": {
|
||||||
|
"On clicking 'execute'": [
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"start": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"connections": {
|
||||||
|
"On clicking 'execute'": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Set",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Set": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Set1",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active": false,
|
||||||
|
"settings": {},
|
||||||
|
"hash": "abd7b28aa2605c96ba24474d72cbe610",
|
||||||
|
"id": 3,
|
||||||
|
"meta": {
|
||||||
|
"instanceId": "08a83d394781701f5c18052cde68e8d92c88b26f5cc8809eb10ad545f14015cb"
|
||||||
|
},
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
126
packages/testing/playwright/workflows/Test_ndv_search.json
Normal file
126
packages/testing/playwright/workflows/Test_ndv_search.json
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
{
|
||||||
|
"name": "NDV search bugs (introduced by schema view?)",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "55635c7b-92ee-4d2d-a0c0-baff9ab071da",
|
||||||
|
"name": "When clicking ‘Execute workflow’",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"position": [800, 380],
|
||||||
|
"typeVersion": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"operation": "getAllPeople"
|
||||||
|
},
|
||||||
|
"id": "4737af43-e49b-4c92-b76f-32605c047114",
|
||||||
|
"name": "Customer Datastore (n8n training)",
|
||||||
|
"type": "n8n-nodes-base.n8nTrainingCustomerDatastore",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1020, 380]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"assignments": {
|
||||||
|
"assignments": []
|
||||||
|
},
|
||||||
|
"includeOtherFields": true,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "8cc9b374-1856-4f3f-9315-08e6e27840d8",
|
||||||
|
"name": "Edit Fields",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 3.4,
|
||||||
|
"position": [1240, 380]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pinData": {
|
||||||
|
"Customer Datastore (n8n training)": [
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"id": "23423532",
|
||||||
|
"name": "Jay Gatsby",
|
||||||
|
"email": "gatsby@west-egg.com",
|
||||||
|
"notes": "Keeps asking about a green light??",
|
||||||
|
"country": "US",
|
||||||
|
"created": "1925-04-10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"id": "23423533",
|
||||||
|
"name": "José Arcadio Buendía",
|
||||||
|
"email": "jab@macondo.co",
|
||||||
|
"notes": "Lots of people named after him. Very confusing",
|
||||||
|
"country": "CO",
|
||||||
|
"created": "1967-05-05"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"id": "23423534",
|
||||||
|
"name": "Max Sendak",
|
||||||
|
"email": "info@in-and-out-of-weeks.org",
|
||||||
|
"notes": "Keeps rolling his terrible eyes",
|
||||||
|
"country": "US",
|
||||||
|
"created": "1963-04-09"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"id": "23423535",
|
||||||
|
"name": "Zaphod Beeblebrox",
|
||||||
|
"email": "captain@heartofgold.com",
|
||||||
|
"notes": "Felt like I was talking to more than one person",
|
||||||
|
"country": null,
|
||||||
|
"created": "1979-10-12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"id": "23423536",
|
||||||
|
"name": "Edmund Pevensie",
|
||||||
|
"email": "edmund@narnia.gov",
|
||||||
|
"notes": "Passionate sailor",
|
||||||
|
"country": "UK",
|
||||||
|
"created": "1950-10-16"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"connections": {
|
||||||
|
"When clicking ‘Execute workflow’": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Customer Datastore (n8n training)",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Customer Datastore (n8n training)": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Edit Fields",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active": false,
|
||||||
|
"settings": {
|
||||||
|
"executionOrder": "v1"
|
||||||
|
},
|
||||||
|
"versionId": "20178044-fb64-4443-88dd-e941517520d0",
|
||||||
|
"meta": {
|
||||||
|
"templateCredsSetupCompleted": true,
|
||||||
|
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||||
|
},
|
||||||
|
"id": "aBVnTRON9Y2cSmse",
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"leftValue": "",
|
||||||
|
"typeValidation": "strict",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "6f0cf983-824b-4339-a5de-6b374a23b4b0",
|
||||||
|
"leftValue": "={{ $json.a }}",
|
||||||
|
"rightValue": 3,
|
||||||
|
"operator": {
|
||||||
|
"type": "number",
|
||||||
|
"operation": "equals"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 2.2,
|
||||||
|
"position": [220, 0],
|
||||||
|
"id": "1755282a-ec4a-4d02-a833-0316ca413cc4",
|
||||||
|
"name": "If"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [0, 0],
|
||||||
|
"id": "de1e7acf-12d8-4e56-ba42-709ffb397db2",
|
||||||
|
"name": "When clicking ‘Execute workflow’"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"category": "randomData"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.debugHelper",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [580, 0],
|
||||||
|
"id": "86440d33-f833-453c-bcaa-fff7e0083501",
|
||||||
|
"name": "DebugHelper",
|
||||||
|
"alwaysOutputData": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"If": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "DebugHelper",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "DebugHelper",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"When clicking ‘Execute workflow’": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "If",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pinData": {
|
||||||
|
"When clicking ‘Execute workflow’": [
|
||||||
|
{
|
||||||
|
"a": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
258
packages/testing/playwright/workflows/Test_workflow_5.json
Normal file
258
packages/testing/playwright/workflows/Test_workflow_5.json
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"instanceId": "8147b3a74cd161276e0f3bfc17369a724afab0d377593fada8be82d34c0c6a95"
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [\n {\n id: 6666\n },\n {\n id: 3333\n },\n {\n id: 9999\n },\n {\n id: 1111\n },\n {\n id: 4444\n },\n {\n id: 8888\n },\n]"
|
||||||
|
},
|
||||||
|
"id": "5f023c7c-67ca-47a0-8a90-8227fcf29b9c",
|
||||||
|
"name": "Code",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [-520, 580]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"values": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "={{ $json.id }}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "bd454282-9dd7-465f-9b9a-654a0c8532ec",
|
||||||
|
"name": "Set2",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [-40, 780]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "ef63cdc5-50bc-4525-9873-7e7f7589a60e",
|
||||||
|
"name": "When clicking ‘Execute workflow’",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [-740, 580]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"sortFieldsUi": {
|
||||||
|
"sortField": [
|
||||||
|
{
|
||||||
|
"fieldName": "id"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "555a150c-d735-4331-b628-c1f1cfed2da1",
|
||||||
|
"name": "Sort",
|
||||||
|
"type": "n8n-nodes-base.sort",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [-280, 580]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"values": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "={{ $json.id }}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "02372cb6-aac8-45c3-8600-f699901289ac",
|
||||||
|
"name": "Set",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [-60, 580]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "00d73944-218c-4896-af68-3f2855a922d1",
|
||||||
|
"name": "Set1",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [-280, 780]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"number": [
|
||||||
|
{
|
||||||
|
"value1": "={{ $json.id }}",
|
||||||
|
"operation": "smallerEqual",
|
||||||
|
"value2": 6666
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "211a7bef-32d1-4928-9cef-3a45f2e61379",
|
||||||
|
"name": "IF",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [160, 580]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "dcbd4745-832f-43d8-8a3c-dd80e8ca2777",
|
||||||
|
"name": "Set3",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [140, 780]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [\n {\n id: 1000\n },\n {\n id: 300\n },\n {\n id: 2000\n },\n {\n id: 100\n },\n {\n id: 400\n },\n {\n id: 1300\n },\n]"
|
||||||
|
},
|
||||||
|
"id": "ec9c8f16-f3c8-4054-a6e9-4f1ebcdebb71",
|
||||||
|
"name": "Code1",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [-520, 780]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "42e89478-a53a-4d10-b20c-1dc5d5f953d5",
|
||||||
|
"name": "Set4",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [460, 460]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "5085eb1c-0345-4b9d-856a-2955279f2c5d",
|
||||||
|
"name": "Set5",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [460, 660]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"Code": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Sort",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Set2": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Set3",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"When clicking ‘Execute workflow’": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Code",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "Code1",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Sort": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Set",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "Set2",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Set": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "IF",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Set1": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Set2",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"IF": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Set4",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "Set5",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Set5",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Code1": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Set1",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
{
|
||||||
|
"name": "Multiple outputs",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "64b27674-3da6-46ce-9008-e173182efa48",
|
||||||
|
"name": "When clicking ‘Execute workflow’",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"position": [16, -32],
|
||||||
|
"typeVersion": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"rules": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"leftValue": "",
|
||||||
|
"typeValidation": "strict"
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"leftValue": "={{ $json.code }}",
|
||||||
|
"rightValue": 1,
|
||||||
|
"operator": {
|
||||||
|
"type": "number",
|
||||||
|
"operation": "equals"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"renameOutput": true,
|
||||||
|
"outputKey": "Item1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"leftValue": "",
|
||||||
|
"typeValidation": "strict"
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "a659050f-0867-471d-8914-d499b6ad7b31",
|
||||||
|
"leftValue": "={{ $json.code }}",
|
||||||
|
"rightValue": 2,
|
||||||
|
"operator": {
|
||||||
|
"type": "number",
|
||||||
|
"operation": "equals"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"renameOutput": true,
|
||||||
|
"outputKey": "Item2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"leftValue": "",
|
||||||
|
"typeValidation": "strict"
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "109fc001-53af-48f1-b79c-5e9afc8b94bd",
|
||||||
|
"leftValue": "={{ $json.code }}",
|
||||||
|
"rightValue": 3,
|
||||||
|
"operator": {
|
||||||
|
"type": "number",
|
||||||
|
"operation": "equals"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"renameOutput": true,
|
||||||
|
"outputKey": "Item3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.switch",
|
||||||
|
"position": [192, -32],
|
||||||
|
"id": "3863cc7a-8f45-46fc-a60c-36aad5b12877",
|
||||||
|
"name": "Switch",
|
||||||
|
"typeVersion": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"assignments": {
|
||||||
|
"assignments": [
|
||||||
|
{
|
||||||
|
"id": "f71bac89-8852-41b2-98dd-cb689f011dcb",
|
||||||
|
"name": "",
|
||||||
|
"value": "",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"position": [480, -192],
|
||||||
|
"id": "85940094-4656-4cdf-a871-1b3b46421de3",
|
||||||
|
"name": "Only Item 1",
|
||||||
|
"typeVersion": 3.4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"position": [480, -32],
|
||||||
|
"id": "a7f4e2b5-8cc9-4881-aa06-38601988740e",
|
||||||
|
"name": "Only Item 2",
|
||||||
|
"typeVersion": 3.4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"position": [480, 128],
|
||||||
|
"id": "7e44ad56-415a-4991-a70e-fea86c430031",
|
||||||
|
"name": "Only Item 3",
|
||||||
|
"typeVersion": 3.4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pinData": {
|
||||||
|
"When clicking ‘Execute workflow’": [
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"name": "First item",
|
||||||
|
"onlyOnItem1": true,
|
||||||
|
"code": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"name": "Second item",
|
||||||
|
"onlyOnItem2": true,
|
||||||
|
"code": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"name": "Third item",
|
||||||
|
"onlyOnItem3": true,
|
||||||
|
"code": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"connections": {
|
||||||
|
"When clicking ‘Execute workflow’": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Switch",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Switch": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Only Item 1",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Only Item 2",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Only Item 3",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active": false,
|
||||||
|
"settings": {
|
||||||
|
"executionOrder": "v1"
|
||||||
|
},
|
||||||
|
"versionId": "1e2a7b45-7730-42d6-989e-f3fa80de303e",
|
||||||
|
"meta": {
|
||||||
|
"instanceId": "27cc9b56542ad45b38725555722c50a1c3fee1670bbb67980558314ee08517c4"
|
||||||
|
},
|
||||||
|
"id": "V2ld4YU11fsHgr1z",
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"instanceId": "3204fc455f5cbeb4e71fdbd3b1dfaf0b088088dea3e639de49e61462b80ffc1d"
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"application": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "url",
|
||||||
|
"value": "",
|
||||||
|
"__regex": "https://airtable.com/([a-zA-Z0-9]{2,})"
|
||||||
|
},
|
||||||
|
"table": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "url",
|
||||||
|
"value": "",
|
||||||
|
"__regex": "https://airtable.com/[a-zA-Z0-9]{2,}/([a-zA-Z0-9]{2,})"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "e0c0cf7e-aa98-4b72-9645-6e64e2902bd1",
|
||||||
|
"name": "Airtable",
|
||||||
|
"type": "n8n-nodes-base.airtable",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [380, 180]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
{
|
||||||
|
"name": "My workflow 52",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "\nreturn [\n {\n \"field\": \"the same\"\n }\n];"
|
||||||
|
},
|
||||||
|
"id": "38c14c4a-7af1-4b04-be76-f8e474c95569",
|
||||||
|
"name": "Break pairedItem chain",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [240, 1020]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "78c4964a-c4e8-47e5-81f3-89ba778feb8b",
|
||||||
|
"name": "Edit Fields",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 3.2,
|
||||||
|
"position": [40, 1020]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "4f4c6527-d565-448a-96bd-8f5414caf8cc",
|
||||||
|
"name": "When clicking ‘Execute workflow’",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [-180, 1020]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"fields": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"stringValue": "={{ $('Edit Fields').item.json.name }}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "44f4e5da-bfe9-4dc3-8d1f-f38e9f364754",
|
||||||
|
"name": "Error",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 3.2,
|
||||||
|
"position": [460, 1020]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pinData": {
|
||||||
|
"Edit Fields": [
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"id": "23423532",
|
||||||
|
"name": "Jay Gatsby",
|
||||||
|
"email": "gatsby@west-egg.com",
|
||||||
|
"notes": "Keeps asking about a green light??",
|
||||||
|
"country": "US",
|
||||||
|
"created": "1925-04-10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"id": "23423533",
|
||||||
|
"name": "José Arcadio Buendía",
|
||||||
|
"email": "jab@macondo.co",
|
||||||
|
"notes": "Lots of people named after him. Very confusing",
|
||||||
|
"country": "CO",
|
||||||
|
"created": "1967-05-05"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"id": "23423534",
|
||||||
|
"name": "Max Sendak",
|
||||||
|
"email": "info@in-and-out-of-weeks.org",
|
||||||
|
"notes": "Keeps rolling his terrible eyes",
|
||||||
|
"country": "US",
|
||||||
|
"created": "1963-04-09"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"id": "23423535",
|
||||||
|
"name": "Zaphod Beeblebrox",
|
||||||
|
"email": "captain@heartofgold.com",
|
||||||
|
"notes": "Felt like I was talking to more than one person",
|
||||||
|
"country": null,
|
||||||
|
"created": "1979-10-12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"id": "23423536",
|
||||||
|
"name": "Edmund Pevensie",
|
||||||
|
"email": "edmund@narnia.gov",
|
||||||
|
"notes": "Passionate sailor",
|
||||||
|
"country": "UK",
|
||||||
|
"created": "1950-10-16"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"connections": {
|
||||||
|
"Break pairedItem chain": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Error",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Edit Fields": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Break pairedItem chain",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"When clicking ‘Execute workflow’": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Edit Fields",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active": false,
|
||||||
|
"settings": {
|
||||||
|
"executionOrder": "v1"
|
||||||
|
},
|
||||||
|
"versionId": "ca53267f-4eb4-481d-9e09-ecb97f6b09e2",
|
||||||
|
"meta": {
|
||||||
|
"templateCredsSetupCompleted": true,
|
||||||
|
"instanceId": "27cc9b56542ad45b38725555722c50a1c3fee1670bbb67980558314ee08517c4"
|
||||||
|
},
|
||||||
|
"id": "6fr8GiRyMlZCiDQW",
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "Node versions",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "2acca986-10a6-451e-b20a-86e95b50e627",
|
||||||
|
"name": "When clicking ‘Execute workflow’",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [460, 460]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1ea0a87c-3395-4bd9-84fb-cf8b0f769cc4",
|
||||||
|
"name": "Function",
|
||||||
|
"type": "n8n-nodes-base.function",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [960, 460]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "30bb9fab-bc89-4309-b42b-0fc586519c76",
|
||||||
|
"name": "Edit Fields (old)",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [800, 460]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "a266b96c-3539-4034-b24c-c86c6d0ca31e",
|
||||||
|
"name": "Edit Fields (no typeVersion)",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"position": [1120, 460]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "273f60c9-08e7-457e-b01d-31e16c565171",
|
||||||
|
"name": "Edit Fields (latest)",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 3.4,
|
||||||
|
"position": [640, 460]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {},
|
||||||
|
"pinData": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"name": "My workflow 8",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"operation": "getAllPeople",
|
||||||
|
"limit": 10
|
||||||
|
},
|
||||||
|
"id": "39cd80ce-5a8f-4339-b3d5-c4af969dd330",
|
||||||
|
"name": "Customer Datastore (n8n training)",
|
||||||
|
"type": "n8n-nodes-base.n8nTrainingCustomerDatastore",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [940, 680]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"values": {
|
||||||
|
"number": [
|
||||||
|
{
|
||||||
|
"name": "objectValue.prop1",
|
||||||
|
"value": 123
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"name": "objectValue.prop2",
|
||||||
|
"value": "someText"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"dotNotation": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "6e4490f6-ba95-4400-beec-2caefdd4895a",
|
||||||
|
"name": "Set",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1300, 680]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "58512a93-dabf-4584-817f-27c608c1bdd5",
|
||||||
|
"name": "When clicking ‘Execute workflow’",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [720, 680]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pinData": {},
|
||||||
|
"connections": {
|
||||||
|
"Customer Datastore (n8n training)": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Set",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"When clicking ‘Execute workflow’": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Customer Datastore (n8n training)",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active": false,
|
||||||
|
"settings": {},
|
||||||
|
"versionId": "4a4f292a-92be-427c-848a-9582527f5ed3",
|
||||||
|
"id": "8",
|
||||||
|
"meta": {
|
||||||
|
"instanceId": "032eceae7493054b723340499be69ecbf4cbe28a7ec6df676b759000750b968d"
|
||||||
|
},
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,565 @@
|
|||||||
|
{
|
||||||
|
"name": "My workflow",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"operation": "getAllPeople",
|
||||||
|
"limit": 10
|
||||||
|
},
|
||||||
|
"id": "441afcbf-a678-4463-bc89-7e0b6693af5c",
|
||||||
|
"name": "Customer Datastore (n8n training)",
|
||||||
|
"type": "n8n-nodes-base.n8nTrainingCustomerDatastore",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [720, 440]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"values": {
|
||||||
|
"number": [
|
||||||
|
{
|
||||||
|
"name": "objectValue.prop1",
|
||||||
|
"value": 123
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"name": "objectValue.prop2",
|
||||||
|
"value": "someText"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"dotNotation": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "44094a05-b3b7-49bf-bfbf-a711e6ba45d8",
|
||||||
|
"name": "Set",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1080, 440]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "3dc7cf26-ff25-4437-b9fd-0e8b127ebec9",
|
||||||
|
"name": "When clicking ‘Execute workflow’",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [500, 440]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pinData": {
|
||||||
|
"Set": [
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"key0": 0,
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 2,
|
||||||
|
"key3": 3,
|
||||||
|
"key4": 4,
|
||||||
|
"key5": 5,
|
||||||
|
"key6": 6,
|
||||||
|
"key7": 7,
|
||||||
|
"key8": 8,
|
||||||
|
"key9": 9,
|
||||||
|
"key10": 10,
|
||||||
|
"key11": 11,
|
||||||
|
"key12": 12,
|
||||||
|
"key13": 13,
|
||||||
|
"key14": 14,
|
||||||
|
"key15": 15,
|
||||||
|
"key16": 16,
|
||||||
|
"key17": 17,
|
||||||
|
"key18": 18,
|
||||||
|
"key19": 19
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"connections": {
|
||||||
|
"Customer Datastore (n8n training)": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Set",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"When clicking ‘Execute workflow’": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Customer Datastore (n8n training)",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active": false,
|
||||||
|
"settings": {},
|
||||||
|
"versionId": "",
|
||||||
|
"meta": {
|
||||||
|
"instanceId": "363581be2c2581d1b11e189456a090887e137f8393a4b5cb85641b1ee4fae479"
|
||||||
|
},
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"instanceId": "2d1cf27f75b18bb9e146336f791c37884f4fc7ddb97c2def27c0444d106778bf"
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "8108d313-8b03-4aa4-963d-cd1c0fe8f85c",
|
||||||
|
"name": "When clicking ‘Execute workflow’",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [420, 220]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"fields": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"stringValue": "<?xml version=\"1.0\" encoding=\"UTF-8\"?> <library> <book> <title>Introduction to XML</title> <author>John Doe</author> <publication_year>2020</publication_year> <isbn>1234567890</isbn> </book> <book> <title>Data Science Basics</title> <author>Jane Smith</author> <publication_year>2019</publication_year> <isbn>0987654321</isbn> </book> <book> <title>Programming in Python</title> <author>Bob Johnson</author> <publication_year>2021</publication_year> <isbn>5432109876</isbn> </book> </library>"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "45888152-7c5f-4d88-9039-660c594da084",
|
||||||
|
"name": "Edit Fields",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 3.2,
|
||||||
|
"position": [640, 220]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"When clicking ‘Execute workflow’": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Edit Fields",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pinData": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"name": "open_node_creator_for_connection",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "25ff0c17-7064-4e14-aec6-45c71d63201b",
|
||||||
|
"name": "When clicking ‘Execute workflow’",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [740, 520]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "49f376ca-845b-4737-aac0-073d0e4fa95c",
|
||||||
|
"name": "Token Splitter",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.textSplitterTokenSplitter",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1180, 540]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "d1db5111-4b01-4620-8ccb-a16ea576c363",
|
||||||
|
"name": "Memory",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.memoryXata",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"position": [940, 540],
|
||||||
|
"credentials": {
|
||||||
|
"xataApi": {
|
||||||
|
"id": "q1ckaYlHTWCYDtF0",
|
||||||
|
"name": "Xata Api account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "b08b6d3a-bef8-42ac-9cef-ec9d4e5402b1",
|
||||||
|
"name": "Output Parser",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
|
||||||
|
"typeVersion": 1.1,
|
||||||
|
"position": [1060, 540]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "ee557938-9cf1-4b78-afef-c783c52fd307",
|
||||||
|
"name": "Tool",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.toolWikipedia",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1300, 540]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "814f2e9c-cc7b-4f3c-89b4-d6eb82bc24df",
|
||||||
|
"name": "Embeddings",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.embeddingsHuggingFaceInference",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1420, 540]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"tableName": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "list",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "e8569b0b-a580-4249-9c5e-f1feed5c644e",
|
||||||
|
"name": "Vector Store",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.vectorStoreSupabase",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1540, 540]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pinData": {},
|
||||||
|
"connections": {},
|
||||||
|
"active": false,
|
||||||
|
"settings": {
|
||||||
|
"executionOrder": "v1"
|
||||||
|
},
|
||||||
|
"versionId": "8e90604c-f7e9-489d-8e43-1cc699b7db04",
|
||||||
|
"meta": {
|
||||||
|
"templateCredsSetupCompleted": true,
|
||||||
|
"instanceId": "27cc9b56542ad45b38725555722c50a1c3fee1670bbb67980558314ee08517c4"
|
||||||
|
},
|
||||||
|
"id": "L3tgfoW660UOSuX6",
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user