From b8980f6118cb7afabcbb628800958b7d9c791603 Mon Sep 17 00:00:00 2001 From: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> Date: Thu, 9 Feb 2023 17:59:01 +0300 Subject: [PATCH] test: Add more data mapping tests (#5389) * test: Add more data mapping tests * test: add tests for preview mapping * test: update wording * test: add more tests * test: fix up prev node test * test: stop popup * test: add mapping test for paths * test: revert back param changes * test: fix mapping tests * test: reset db * test: fix up mapping tests * test: fix up mapping tests * test: update tests to be more stable * chore: clean up unused command * fix: fix up before unload bug * fix: fix data transformation tests * test: fix up flaky webhook tests * test: fix up flaky webhook tests * test: fix up flaky dt tests --- .../14-data-transformation-expressions.cy.ts | 31 ++- cypress/e2e/14-mapping.cy.ts | 196 +++++++++++++++++- cypress/e2e/16-webhook-node.cy.ts | 49 ++--- cypress/fixtures/Test_workflow_3.json | 121 +++++++++++ cypress/pages/canvas-node.ts | 2 +- cypress/pages/ndv.ts | 35 +++- cypress/support/commands.ts | 14 +- cypress/support/index.ts | 2 +- .../src/components/ParameterInputWrapper.vue | 3 +- packages/editor-ui/src/views/NodeView.vue | 8 +- 10 files changed, 393 insertions(+), 68 deletions(-) create mode 100644 cypress/fixtures/Test_workflow_3.json diff --git a/cypress/e2e/14-data-transformation-expressions.cy.ts b/cypress/e2e/14-data-transformation-expressions.cy.ts index ab59eef826..6f6b802926 100644 --- a/cypress/e2e/14-data-transformation-expressions.cy.ts +++ b/cypress/e2e/14-data-transformation-expressions.cy.ts @@ -4,15 +4,18 @@ const wf = new WorkflowPage(); const ndv = new NDV(); describe('Data transformation expressions', () => { - before(() => { + beforeEach(() => { cy.resetAll(); cy.skipSetup(); + wf.actions.visit(); cy.waitForLoad(); + + cy.window() + // @ts-ignore + .then(win => win.onBeforeUnload && win.removeEventListener('beforeunload', win.onBeforeUnload)); }); it('$json + native string methods', () => { - wf.actions.visit(); - wf.actions.addInitialNodeToCanvas('Schedule Trigger', { keepNdvOpen: true }); ndv.actions.setPinnedData([{ myStr: 'Monday' }]); ndv.actions.close(); @@ -23,12 +26,10 @@ describe('Data transformation expressions', () => { ndv.getters.inlineExpressionEditorInput().clear().type(input); ndv.actions.execute(); - ndv.getters.outputDataContainer().contains(output).should('be.visible'); + ndv.getters.outputDataContainer().should('be.visible').contains(output); }); it('$json + n8n string methods', () => { - wf.actions.visit(); - wf.actions.addInitialNodeToCanvas('Schedule Trigger', { keepNdvOpen: true }); ndv.actions.setPinnedData([{ myStr: 'hello@n8n.io is an email' }]); ndv.actions.close(); @@ -39,12 +40,10 @@ describe('Data transformation expressions', () => { ndv.getters.inlineExpressionEditorInput().clear().type(input); ndv.actions.execute(); - ndv.getters.outputDataContainer().contains(output).should('be.visible'); + ndv.getters.outputDataContainer().should('be.visible').contains(output); }); it('$json + native numeric methods', () => { - wf.actions.visit(); - wf.actions.addInitialNodeToCanvas('Schedule Trigger', { keepNdvOpen: true }); ndv.actions.setPinnedData([{ myNum: 9.123 }]); ndv.actions.close(); @@ -55,12 +54,10 @@ describe('Data transformation expressions', () => { ndv.getters.inlineExpressionEditorInput().clear().type(input); ndv.actions.execute(); - ndv.getters.outputDataContainer().contains(output).should('be.visible'); + ndv.getters.outputDataContainer().should('be.visible').contains(output); }); it('$json + n8n numeric methods', () => { - wf.actions.visit(); - wf.actions.addInitialNodeToCanvas('Schedule Trigger', { keepNdvOpen: true }); ndv.actions.setPinnedData([{ myStr: 'hello@n8n.io is an email' }]); ndv.actions.close(); @@ -71,12 +68,10 @@ describe('Data transformation expressions', () => { ndv.getters.inlineExpressionEditorInput().clear().type(input); ndv.actions.execute(); - ndv.getters.outputDataContainer().contains(output).should('be.visible'); + ndv.getters.outputDataContainer().should('be.visible').contains(output); }); it('$json + native array methods', () => { - wf.actions.visit(); - wf.actions.addInitialNodeToCanvas('Schedule Trigger', { keepNdvOpen: true }); ndv.actions.setPinnedData([{ myArr: [1, 2, 3] }]); ndv.actions.close(); @@ -87,12 +82,10 @@ describe('Data transformation expressions', () => { ndv.getters.inlineExpressionEditorInput().clear().type(input); ndv.actions.execute(); - ndv.getters.outputDataContainer().contains(output).should('be.visible'); + ndv.getters.outputDataContainer().should('be.visible').contains(output); }); it('$json + n8n array methods', () => { - wf.actions.visit(); - wf.actions.addInitialNodeToCanvas('Schedule Trigger', { keepNdvOpen: true }); ndv.actions.setPinnedData([{ myArr: [1, 2, 3] }]); ndv.actions.close(); @@ -103,7 +96,7 @@ describe('Data transformation expressions', () => { ndv.getters.inlineExpressionEditorInput().clear().type(input); ndv.actions.execute(); - ndv.getters.outputDataContainer().contains(output).should('be.visible'); + ndv.getters.outputDataContainer().should('be.visible').contains(output); }); }); diff --git a/cypress/e2e/14-mapping.cy.ts b/cypress/e2e/14-mapping.cy.ts index 6c24da45fc..ade387e454 100644 --- a/cypress/e2e/14-mapping.cy.ts +++ b/cypress/e2e/14-mapping.cy.ts @@ -5,16 +5,18 @@ const ndv = new NDV(); const canvasNode = new CanvasNode(); describe('Data mapping', () => { - before(() => { + beforeEach(() => { cy.resetAll(); cy.skipSetup(); - }); - - beforeEach(() => { workflowPage.actions.visit(); + cy.waitForLoad(); + + cy.window() + // @ts-ignore + .then(win => win.onBeforeUnload && win.removeEventListener('beforeunload', win.onBeforeUnload)) }); - it('Should be able to map expressions from table header', () => { + it('maps expressions from table header', () => { cy.fixture('Test_workflow-actions_paste-data.json').then((data) => { cy.get('body').paste(JSON.stringify(data)); }); @@ -34,4 +36,188 @@ describe('Data mapping', () => { ndv.actions.mapDataFromHeader(2, 'value'); ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $json.timestamp }} {{ $json["Readable date"] }}'); }); + + it('maps expressions from table json, and resolves value based on hover', () => { + cy.fixture('Test_workflow_3.json').then((data) => { + cy.get('body').paste(JSON.stringify(data)); + }); + + canvasNode.actions.openNode('Set'); + ndv.actions.switchInputMode('Table'); + ndv.getters.inputDataContainer().get('table', { timeout: 10000 }).should('exist'); + + ndv.getters.parameterInput('name').should('have.length', 1).find('input').should('have.value', 'other'); + ndv.getters.parameterInput('value').should('have.length', 1).find('input').should('have.value', ''); + + ndv.getters.inputTbodyCell(1, 0).find('span').contains('count').trigger('mousedown', {force: true}); + ndv.actions.mapToParameter('value'); + + ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $json.input[0].count }}'); + ndv.getters.parameterExpressionPreview('value').should('include.text', '0') + + ndv.getters.inputTbodyCell(1, 0).realHover(); + ndv.getters.parameterExpressionPreview('value') + .should('include.text', '0') + .invoke('css', 'color') + .should('equal', 'rgb(125, 125, 135)'); + + ndv.getters.inputTbodyCell(2, 0).realHover(); + ndv.getters.parameterExpressionPreview('value') + .should('include.text', '1') + .invoke('css', 'color') + .should('equal', 'rgb(125, 125, 135)'); + + ndv.actions.execute(); + + ndv.getters.outputTbodyCell(1, 0).realHover(); + ndv.getters.parameterExpressionPreview('value') + .should('include.text', '0') + .invoke('css', 'color') + .should('equal', 'rgb(125, 125, 135)'); // todo update color + + ndv.getters.outputTbodyCell(2, 0).realHover(); + ndv.getters.parameterExpressionPreview('value') + .should('include.text', '1') + .invoke('css', 'color') + .should('equal', 'rgb(125, 125, 135)'); + }); + + it('maps expressions from json view', () => { + cy.fixture('Test_workflow_3.json').then((data) => { + cy.get('body').paste(JSON.stringify(data)); + }); + + canvasNode.actions.openNode('Set'); + ndv.actions.switchInputMode('JSON'); + + ndv.getters.inputDataContainer().should('exist').find('.json-data') + .should('have.text', '[{"input":[{"count":0,"with space":"!!","with.dot":"!!","with"quotes":"!!"}]},{"input":[{"count":1}]}]') + .find('span').contains('"count"') + .realMouseDown(); + + ndv.actions.mapToParameter('value'); + ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $json.input[0].count }}'); + ndv.getters.parameterExpressionPreview('value') + .should('include.text', '0'); + + ndv.getters.inputDataContainer().find('.json-data') + .find('span').contains('"input"') + .realMouseDown(); + + ndv.actions.mapToParameter('value'); + ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $json.input[0].count }} {{ $json.input }}'); + ndv.getters.parameterExpressionPreview('value') + .should('include.text', '0 [object Object]'); + }); + + it('maps expressions from schema view', () => { + cy.fixture('Test_workflow_3.json').then((data) => { + cy.get('body').paste(JSON.stringify(data)); + }); + + canvasNode.actions.openNode('Set'); + ndv.actions.clearParameterInput('value'); + cy.get('body').type('{esc}'); + + ndv.getters.inputDataContainer() + .should('exist') + .find('span').contains('count') + .realMouseDown(); + + + ndv.actions.mapToParameter('value'); + ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $json.input[0].count }}'); + ndv.getters.parameterExpressionPreview('value') + .should('include.text', '0'); + + ndv.getters.inputDataContainer() + .find('span').contains('input') + .realMouseDown(); + + ndv.actions.mapToParameter('value'); + ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $json.input[0].count }} {{ $json.input }}'); + ndv.getters.parameterExpressionPreview('value') + .should('include.text', '0 [object Object]'); + }); + + it('maps expressions from previous nodes', () => { + cy.createFixtureWorkflow('Test_workflow_3.json', `My test workflow`); + canvasNode.actions.openNode('Set1'); + + ndv.actions.selectInputNode('Schedule Trigger'); + + ndv.getters.inputDataContainer() + .find('span').contains('count') + .realMouseDown(); + + ndv.actions.mapToParameter('value'); + ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $node["Schedule Trigger"].json.input[0].count }}'); + ndv.getters.parameterExpressionPreview('value') + .should('not.exist'); + + ndv.actions.switchInputMode('Table'); + ndv.actions.mapDataFromHeader(1, 'value'); + ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $node["Schedule Trigger"].json.input[0].count }} {{ $node["Schedule Trigger"].json.input }}'); + ndv.getters.parameterExpressionPreview('value') + .should('not.exist'); + + ndv.actions.selectInputNode('Set'); + + ndv.actions.executePrevious(); + ndv.getters.executingLoader().should('not.exist'); + ndv.getters.inputDataContainer().should('exist'); + ndv.getters.parameterExpressionPreview('value') + .should('include.text', '0 [object Object]'); + + ndv.getters.inputTbodyCell(2, 0).realHover(); + ndv.getters.parameterExpressionPreview('value') + .should('include.text', '1 [object Object]'); + }); + + it('maps keys to path', () => { + workflowPage.actions.addInitialNodeToCanvas('Manual Trigger', {keepNdvOpen: true}); + + ndv.actions.setPinnedData([ + { + input: [ + { + "hello.world": { + "my count": 0, + }, + } + ] + }, + { + input: [ + { + "hello.world": { + "my count": 1, + } + } + ] + }, + ]); + + ndv.actions.close(); + + workflowPage.actions.addNodeToCanvas('Item Lists'); + canvasNode.actions.openNode('Item Lists'); + + ndv.getters.parameterInput('operation') + .click() + .find('li').contains('Sort') + .click(); + + ndv.getters.nodeParameters().find('button').contains('Add Field To Sort By').click(); + + ndv.getters.inputDataContainer() + .find('span').contains('my count') + .realMouseDown(); + + ndv.actions.mapToParameter('fieldName'); + + ndv.getters.inlineExpressionEditorInput().should('have.length', 0); + ndv.getters.parameterInput('fieldName') + .find('input').should('have.value', 'input[0]["hello.world"]["my count"]'); + }); }); diff --git a/cypress/e2e/16-webhook-node.cy.ts b/cypress/e2e/16-webhook-node.cy.ts index 119ebdd3b4..07495cc4b0 100644 --- a/cypress/e2e/16-webhook-node.cy.ts +++ b/cypress/e2e/16-webhook-node.cy.ts @@ -1,13 +1,11 @@ -import { WorkflowPage, WorkflowsPage, NDV, CredentialsModal } from '../pages'; +import { WorkflowPage, NDV, CredentialsModal } from '../pages'; import { v4 as uuid } from 'uuid'; import { cowBase64 } from '../support/binaryTestFiles'; -const workflowsPage = new WorkflowsPage(); const workflowPage = new WorkflowPage(); const ndv = new NDV(); const credentialsModal = new CredentialsModal(); -const webhookWorkflowName = 'Webhook Workflow'; const waitForWebhook = 500; interface SimpleWebhookCallOptions { @@ -21,7 +19,7 @@ interface SimpleWebhookCallOptions { } const simpleWebhookCall = (options: SimpleWebhookCallOptions) => { - const { + const { authentication, method, webhookPath, @@ -31,10 +29,6 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => { executeNow = true, } = options; - cy.visit(workflowsPage.url); - - workflowsPage.actions.createWorkflowFromCard(); - workflowPage.actions.renameWorkflow(webhookWorkflowName); workflowPage.actions.addInitialNodeToCanvas('Webhook'); workflowPage.actions.openNode('Webhook'); @@ -49,7 +43,7 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => { .find('input') .clear() .type(webhookPath); - + if (authentication) { cy.getByTestId('parameter-input-authentication').click(); cy.getByTestId('parameter-input-authentication') @@ -66,7 +60,7 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => { .clear() .type(responseCode.toString()); } - + if (respondWith) { cy.getByTestId('parameter-input-responseMode').click(); cy.getByTestId('parameter-input-responseMode') @@ -85,7 +79,7 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => { .click(); } - if (executeNow) { + if (executeNow) { ndv.actions.execute(); cy.wait(waitForWebhook); @@ -97,14 +91,15 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => { }; describe('Webhook Trigger node', async () => { - before(() => { + beforeEach(() => { cy.resetAll(); cy.skipSetup(); - }); + workflowPage.actions.visit(); + cy.waitForLoad(); - afterEach(() => { - cy.visit(workflowsPage.url); - workflowsPage.actions.deleteWorkFlow(webhookWorkflowName); + cy.window() + // @ts-ignore + .then(win => win.onBeforeUnload && win.removeEventListener('beforeunload', win.onBeforeUnload)); }); it('should listen for a GET request', () => { @@ -138,7 +133,7 @@ describe('Webhook Trigger node', async () => { }); ndv.getters.backToCanvas().click(); - + workflowPage.actions.addNodeToCanvas('Set'); workflowPage.actions.openNode('Set'); cy.get('.add-option').click(); @@ -146,12 +141,12 @@ describe('Webhook Trigger node', async () => { cy.get('.fixed-collection-parameter').getByTestId('parameter-input-name').clear().type('MyValue'); cy.get('.fixed-collection-parameter').getByTestId('parameter-input-value').clear().type('1234'); ndv.getters.backToCanvas().click(); - + workflowPage.actions.addNodeToCanvas('Respond to Webhook'); workflowPage.actions.executeWorkflow(); cy.wait(waitForWebhook); - + cy.request('GET', '/webhook-test/'+ webhookPath).then((response) => { expect(response.status).to.eq(200); expect(response.body.MyValue).to.eq(1234); @@ -169,7 +164,7 @@ describe('Webhook Trigger node', async () => { ndv.actions.execute(); cy.wait(waitForWebhook); - + cy.request('GET', '/webhook-test/'+ webhookPath).then((response) => { expect(response.status).to.eq(201); }); @@ -184,7 +179,7 @@ describe('Webhook Trigger node', async () => { respondWith: 'Last Node', }); ndv.getters.backToCanvas().click(); - + workflowPage.actions.addNodeToCanvas('Set'); workflowPage.actions.openNode('Set'); cy.get('.add-option').click(); @@ -192,10 +187,10 @@ describe('Webhook Trigger node', async () => { cy.get('.fixed-collection-parameter').getByTestId('parameter-input-name').clear().type('MyValue'); cy.get('.fixed-collection-parameter').getByTestId('parameter-input-value').clear().type('1234'); ndv.getters.backToCanvas().click(); - + workflowPage.actions.executeWorkflow(); cy.wait(waitForWebhook); - + cy.request('GET', '/webhook-test/'+ webhookPath).then((response) => { expect(response.status).to.eq(200); expect(response.body.MyValue).to.eq(1234); @@ -212,7 +207,7 @@ describe('Webhook Trigger node', async () => { responseData: 'First Entry Binary', }); ndv.getters.backToCanvas().click(); - + workflowPage.actions.addNodeToCanvas('Set'); workflowPage.actions.openNode('Set'); cy.get('.add-option').click(); @@ -220,7 +215,7 @@ describe('Webhook Trigger node', async () => { cy.get('.fixed-collection-parameter').getByTestId('parameter-input-name').clear().type('data'); cy.get('.fixed-collection-parameter').getByTestId('parameter-input-value').clear().find('input').invoke('val', cowBase64).trigger('blur'); ndv.getters.backToCanvas().click(); - + workflowPage.actions.addNodeToCanvas('Move Binary Data'); workflowPage.actions.zoomToFit(); @@ -233,10 +228,10 @@ describe('Webhook Trigger node', async () => { .contains('JSON to Binary') .click(); ndv.getters.backToCanvas().click(); - + workflowPage.actions.executeWorkflow(); cy.wait(waitForWebhook); - + cy.request('GET', '/webhook-test/'+ webhookPath).then((response) => { expect(response.status).to.eq(200); expect(Object.keys(response.body).includes('data')).to.be.true; diff --git a/cypress/fixtures/Test_workflow_3.json b/cypress/fixtures/Test_workflow_3.json new file mode 100644 index 0000000000..846b50cf19 --- /dev/null +++ b/cypress/fixtures/Test_workflow_3.json @@ -0,0 +1,121 @@ +{ + "name": "My workflow", + "nodes": [ + { + "parameters": { + "rule": { + "interval": [ + {} + ] + } + }, + "id": "0f7d87ee-19c6-4576-bdff-1f3c4739392c", + "name": "Schedule Trigger", + "type": "n8n-nodes-base.scheduleTrigger", + "typeVersion": 1, + "position": [ + 720, + 300 + ] + }, + { + "parameters": { + "values": { + "string": [ + { + "name": "other", + "value": "" + } + ] + }, + "options": {} + }, + "id": "2dfc690a-95cf-48c2-85a6-2b3bb8cd1d1d", + "name": "Set", + "type": "n8n-nodes-base.set", + "typeVersion": 1, + "position": [ + 920, + 300 + ] + }, + { + "id": "9bee04af-1bfc-4be2-a704-e975cb887ced", + "name": "Set1", + "type": "n8n-nodes-base.set", + "typeVersion": 1, + "position": [ + 1120, + 300 + ], + "parameters": { + "values": { + "string": [ + { + "name": "other", + "value": "" + } + ] + }, + "options": {} + } + } + ], + "pinData": { + "Schedule Trigger": [ + { + "json": { + "input": [ + { + "count": 0, + "with space": "!!", + "with.dot": "!!", + "with\"quotes": "!!" + } + ] + } + }, + { + "json": { + "input": [ + { + "count": 1 + } + ] + } + } + ] + }, + "connections": { + "Schedule Trigger": { + "main": [ + [ + { + "node": "Set", + "type": "main", + "index": 0 + } + ] + ] + }, + "Set": { + "main": [ + [ + { + "node": "Set1", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": {}, + "versionId": "c26af749-dacb-45ef-8071-98aba8688075", + "id": "1", + "meta": { + "instanceId": "fe45a93dd232270eb40d3ba1f7907ad3935bbd72ad5e4ee09ff61e96674f9aef" + }, + "tags": [] +} \ No newline at end of file diff --git a/cypress/pages/canvas-node.ts b/cypress/pages/canvas-node.ts index e0506d15f8..b0d0032320 100644 --- a/cypress/pages/canvas-node.ts +++ b/cypress/pages/canvas-node.ts @@ -9,7 +9,7 @@ export class CanvasNode extends BasePage { actions = { openNode: (nodeName: string) => { - this.getters.nodeByName(nodeName).dblclick(); + this.getters.nodeByName(nodeName).eq(0).dblclick(); }, }; } diff --git a/cypress/pages/ndv.ts b/cypress/pages/ndv.ts index 74febf8c00..9d15a836c3 100644 --- a/cypress/pages/ndv.ts +++ b/cypress/pages/ndv.ts @@ -10,6 +10,7 @@ export class NDV extends BasePage { inputOption: () => cy.getByTestId('ndv-input-option'), inputPanel: () => cy.getByTestId('ndv-input-panel'), outputPanel: () => cy.getByTestId('output-panel'), + executingLoader: () => cy.getByTestId('ndv-executing'), inputDataContainer: () => this.getters.inputPanel().findChildByTestId('ndv-data-container'), inputDisplayMode: () => this.getters.inputPanel().getByTestId('ndv-run-data-display-mode'), outputDataContainer: () => this.getters.outputPanel().findChildByTestId('ndv-data-container'), @@ -24,18 +25,18 @@ export class NDV extends BasePage { outputTableHeaders: () => this.getters.outputDataContainer().find('table thead th'), outputTableRow: (row: number) => this.getters.outputTableRows().eq(row), outputTbodyCell: (row: number, col: number) => this.getters.outputTableRow(row).find('td').eq(col), - inputTableRows: () => this.getters.outputDataContainer().find('table tr'), - inputTableHeaders: () => this.getters.outputDataContainer().find('table thead th'), - inputTableRow: (row: number) => this.getters.outputTableRows().eq(row), - inputTbodyCell: (row: number, col: number) => this.getters.outputTableRow(row).find('td').eq(col), + inputTableRows: () => this.getters.inputDataContainer().find('table tr'), + inputTableHeaders: () => this.getters.inputDataContainer().find('table thead th'), + inputTableRow: (row: number) => this.getters.inputTableRows().eq(row), + inputTbodyCell: (row: number, col: number) => this.getters.inputTableRow(row).find('td').eq(col), inlineExpressionEditorInput: () => cy.getByTestId('inline-expression-editor-input'), nodeParameters: () => cy.getByTestId('node-parameters'), parameterInput: (parameterName: string) => cy.getByTestId(`parameter-input-${parameterName}`), + parameterExpressionPreview: (parameterName: string) => this.getters.nodeParameters().find(`[data-test-id="parameter-input-${parameterName}"] + [data-test-id="parameter-expression-preview"]`), nodeNameContainer: () => cy.getByTestId('node-title-container'), nodeRenameInput: () => cy.getByTestId('node-rename-input'), executePrevious: () => cy.getByTestId('execute-previous-node'), httpRequestNotice: () => cy.getByTestId('node-parameters-http-notice'), - inlineExpressionEditorInput: () => cy.getByTestId('inline-expression-editor-input'), nthParam: (n: number) => cy.getByTestId('node-parameters').find('.parameter-item').eq(n), }; @@ -46,6 +47,9 @@ export class NDV extends BasePage { editPinnedData: () => { this.getters.editPinnedDataButton().click(); }, + savePinnedData: () => { + this.getters.savePinnedDataButton().click(); + }, execute: () => { this.getters.nodeExecuteButton().first().click(); }, @@ -64,7 +68,10 @@ export class NDV extends BasePage { editor.type(`{selectall}{backspace}`); editor.type(JSON.stringify(data).replace(new RegExp('{', 'g'), '{{}')); - this.getters.savePinnedDataButton().click(); + this.actions.savePinnedData(); + }, + clearParameterInput: (parameterName: string) => { + this.getters.parameterInput(parameterName).type(`{selectall}{backspace}`); }, typeIntoParameterInput: (parameterName: string, content: string) => { this.getters.parameterInput(parameterName).type(content); @@ -92,11 +99,23 @@ export class NDV extends BasePage { const droppable = `[data-test-id="parameter-input-${parameterName}"]`; cy.draganddrop(draggable, droppable); }, + mapToParameter: (parameterName: string) => { + const droppable = `[data-test-id="parameter-input-${parameterName}"]`; + cy.draganddrop('', droppable); + }, switchInputMode: (type: 'Schema' | 'Table' | 'JSON' | 'Binary') => { - this.getters.inputDisplayMode().find('label').contains(type).click(); + this.getters.inputDisplayMode().find('label').contains(type).click({force: true}); }, switchOutputMode: (type: 'Schema' | 'Table' | 'JSON' | 'Binary') => { - this.getters.outputDisplayMode().find('label').contains(type).click(); + this.getters.outputDisplayMode().find('label').contains(type).click({force: true}); + }, + selectInputNode: (nodeName: string) => { + this.getters.inputSelect().find('.el-select').click(); + this.getters.inputOption().contains(nodeName).click(); + }, + addDefaultPinnedData: () => { + this.actions.editPinnedData(); + this.actions.savePinnedData(); }, }; } diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 571c240827..ff64f69599 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -243,7 +243,9 @@ Cypress.Commands.add('drag', (selector, pos) => { }); Cypress.Commands.add('draganddrop', (draggableSelector, droppableSelector) => { - cy.get(draggableSelector).should('exist'); + if (draggableSelector) { + cy.get(draggableSelector).should('exist'); + } cy.get(droppableSelector).should('exist'); cy.get(droppableSelector) @@ -254,12 +256,16 @@ Cypress.Commands.add('draganddrop', (draggableSelector, droppableSelector) => { const pageX = coords.left + coords.width / 2; const pageY = coords.top + coords.height / 2; - // We can't use realMouseDown here because it hangs headless run - cy.get(draggableSelector).trigger('mousedown'); + if (draggableSelector) { + // We can't use realMouseDown here because it hangs headless run + cy.get(draggableSelector).trigger('mousedown'); + } // We don't chain these commands to make sure cy.get is re-trying correctly cy.get(droppableSelector).realMouseMove(pageX, pageY); cy.get(droppableSelector).realHover(); cy.get(droppableSelector).realMouseUp(); - cy.get(draggableSelector).realMouseUp(); + if (draggableSelector) { + cy.get(draggableSelector).realMouseUp(); + } }); }); diff --git a/cypress/support/index.ts b/cypress/support/index.ts index 8eee49e10f..93927c98e8 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -48,7 +48,7 @@ declare global { readClipboard(): Chainable; paste(pastePayload: string): void; drag(selector: string, target: [number, number]): void; - draganddrop(selector: string, target: string): void; + draganddrop(draggableSelector: string, droppableSelector: string): void; } } } diff --git a/packages/editor-ui/src/components/ParameterInputWrapper.vue b/packages/editor-ui/src/components/ParameterInputWrapper.vue index d5735f03df..0fc39cefdd 100644 --- a/packages/editor-ui/src/components/ParameterInputWrapper.vue +++ b/packages/editor-ui/src/components/ParameterInputWrapper.vue @@ -16,16 +16,17 @@ :isForCredential="isForCredential" :eventSource="eventSource" :expressionEvaluated="expressionValueComputed" + :data-test-id="`parameter-input-${parameter.name}`" @focus="onFocus" @blur="onBlur" @drop="onDrop" @textInput="onTextInput" @valueChanged="onValueChanged" - :data-test-id="`parameter-input-${parameter.name}`" /> diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 6f82056eb0..51c79d15e2 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -2536,7 +2536,10 @@ export default mixins( this.uiStore.nodeViewInitialized = true; document.addEventListener('keydown', this.keyDown); document.addEventListener('keyup', this.keyUp); - window.addEventListener('beforeunload', (e) => { + + // allow to be overriden in e2e tests + // @ts-ignore + window.onBeforeUnload = (e) => { if (this.isDemo) { return; } else if (this.uiStore.stateIsDirty) { @@ -2549,7 +2552,8 @@ export default mixins( this.startLoading(this.$locale.baseText('nodeView.redirecting')); return; } - }); + }; + window.addEventListener('beforeunload', window.onBeforeUnload); }, getOutputEndpointUUID(nodeName: string, index: number): string | null { const node = this.workflowsStore.getNodeByName(nodeName);