diff --git a/cypress/e2e/10-undo-redo.cy.ts b/cypress/e2e/10-undo-redo.cy.ts index 3222b533ab..28c99ac313 100644 --- a/cypress/e2e/10-undo-redo.cy.ts +++ b/cypress/e2e/10-undo-redo.cy.ts @@ -116,7 +116,7 @@ describe('Undo/Redo', () => { it('should undo/redo moving nodes', () => { WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', 50, 150); + cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150]); WorkflowPage.getters .canvasNodes() .last() @@ -148,7 +148,7 @@ describe('Undo/Redo', () => { it('should undo/redo deleting a connection by moving it away', () => { WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - cy.drag('.rect-input-endpoint.jtk-endpoint-connected', 0, -100); + cy.drag('.rect-input-endpoint.jtk-endpoint-connected', [0, -100]); WorkflowPage.getters.nodeConnections().should('have.length', 0); WorkflowPage.actions.hitUndo(); WorkflowPage.getters.nodeConnections().should('have.length', 1); @@ -266,7 +266,7 @@ describe('Undo/Redo', () => { WorkflowPage.actions.hitDisableNodeShortcut(); // Move first one WorkflowPage.getters.canvasNodes().first().click(); - cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', 50, 150); + cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150]); // Delete the set node WorkflowPage.getters.canvasNodeByName(SET_NODE_NAME).click().click(); cy.get('body').type('{backspace}'); diff --git a/cypress/e2e/12-canvas.cy.ts b/cypress/e2e/12-canvas.cy.ts index 113d054170..343b63354c 100644 --- a/cypress/e2e/12-canvas.cy.ts +++ b/cypress/e2e/12-canvas.cy.ts @@ -117,7 +117,7 @@ describe('Canvas Actions', () => { WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); WorkflowPage.actions.zoomToFit(); - cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', 50, 150); + cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150]); WorkflowPage.getters .canvasNodes() .last() @@ -223,7 +223,7 @@ describe('Canvas Actions', () => { it('should delete a connection by moving it away from endpoint', () => { WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - cy.drag('.rect-input-endpoint.jtk-endpoint-connected', 0, -100); + cy.drag('.rect-input-endpoint.jtk-endpoint-connected', [0, -100]); WorkflowPage.getters.nodeConnections().should('have.length', 0); }); diff --git a/cypress/e2e/14-mapping.cy.ts b/cypress/e2e/14-mapping.cy.ts new file mode 100644 index 0000000000..6c24da45fc --- /dev/null +++ b/cypress/e2e/14-mapping.cy.ts @@ -0,0 +1,37 @@ +import { WorkflowPage, NDV, CanvasNode } from '../pages'; + +const workflowPage = new WorkflowPage(); +const ndv = new NDV(); +const canvasNode = new CanvasNode(); + +describe('Data mapping', () => { + before(() => { + cy.resetAll(); + cy.skipSetup(); + }); + + beforeEach(() => { + workflowPage.actions.visit(); + }); + + it('Should be able to map expressions from table header', () => { + cy.fixture('Test_workflow-actions_paste-data.json').then((data) => { + cy.get('body').paste(JSON.stringify(data)); + }); + canvasNode.actions.openNode('Set'); + ndv.actions.executePrevious(); + ndv.actions.switchInputMode('Table'); + ndv.getters.inputDataContainer().get('table', { timeout: 10000 }).should('exist'); + + ndv.getters.nodeParameters().find('input[placeholder*="Add Value"]').click(); + ndv.getters.nodeParameters().find('.el-select-dropdown__list li:nth-child(3)').should('have.text', 'String').click(); + ndv.getters.parameterInput('name').should('have.length', 1).find('input').should('have.value', 'propertyName'); + ndv.getters.parameterInput('value').should('have.length', 1).find('input').should('have.value', ''); + + ndv.actions.mapDataFromHeader(1, 'value'); + ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $json.timestamp }}'); + + ndv.actions.mapDataFromHeader(2, 'value'); + ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $json.timestamp }} {{ $json["Readable date"] }}'); + }); +}); diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts index d5208e60d2..c0e12a6ba2 100644 --- a/cypress/e2e/5-ndv.cy.ts +++ b/cypress/e2e/5-ndv.cy.ts @@ -42,7 +42,7 @@ describe('NDV', () => { }); }); - ndv.getters.runDataDisplayMode().should('have.length.at.least', 1).and('be.visible'); + ndv.getters.outputDisplayMode().should('have.length.at.least', 1).and('be.visible'); }); it('should change input', () => { diff --git a/cypress/pages/canvas-node.ts b/cypress/pages/canvas-node.ts new file mode 100644 index 0000000000..e0506d15f8 --- /dev/null +++ b/cypress/pages/canvas-node.ts @@ -0,0 +1,15 @@ +import { BasePage } from './base'; + +export class CanvasNode extends BasePage { + getters = { + nodes: () => cy.getByTestId('canvas-node'), + nodeByName: (nodeName: string) => + this.getters.nodes().filter(`:contains("${nodeName}")`), + }; + + actions = { + openNode: (nodeName: string) => { + this.getters.nodeByName(nodeName).dblclick(); + }, + }; +} diff --git a/cypress/pages/index.ts b/cypress/pages/index.ts index 07849aa6cc..870fbaa58c 100644 --- a/cypress/pages/index.ts +++ b/cypress/pages/index.ts @@ -8,3 +8,4 @@ export * from './modals'; export * from './settings-users'; export * from './settings-log-streaming'; export * from './ndv'; +export * from './canvas-node'; diff --git a/cypress/pages/ndv.ts b/cypress/pages/ndv.ts index 3761db326f..9842c724d0 100644 --- a/cypress/pages/ndv.ts +++ b/cypress/pages/ndv.ts @@ -11,8 +11,9 @@ export class NDV extends BasePage { inputPanel: () => cy.getByTestId('ndv-input-panel'), outputPanel: () => cy.getByTestId('output-panel'), 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'), - runDataDisplayMode: () => cy.getByTestId('ndv-run-data-display-mode'), + outputDisplayMode: () => this.getters.outputPanel().getByTestId('ndv-run-data-display-mode'), digital: () => cy.getByTestId('ndv-run-data-display-mode'), pinDataButton: () => cy.getByTestId('ndv-pin-data'), editPinnedDataButton: () => cy.getByTestId('ndv-edit-pinned-data'), @@ -22,11 +23,17 @@ export class NDV extends BasePage { outputTableRows: () => this.getters.outputDataContainer().find('table tr'), outputTableHeaders: () => this.getters.outputDataContainer().find('table thead th'), outputTableRow: (row: number) => this.getters.outputTableRows().eq(row), - outputTbodyCell: (row: number, cell: number) => - this.getters.outputTableRow(row).find('td').eq(cell), + 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), + inlineExpressionEditorInput: () => cy.getByTestId('inline-expression-editor-input'), + nodeParameters: () => cy.getByTestId('node-parameters'), parameterInput: (parameterName: string) => cy.getByTestId(`parameter-input-${parameterName}`), 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'), }; @@ -57,12 +64,33 @@ export class NDV extends BasePage { this.getters.parameterInput(parameterName).type(content); }, selectOptionInParameterDropdown: (parameterName: string, content: string) => { - this.getters.parameterInput(parameterName).find('.option-headline').contains(content).click(); + this.getters + .parameterInput(parameterName) + .find('.option-headline') + .contains(content) + .click(); }, rename: (newName: string) => { this.getters.nodeNameContainer().click(); - this.getters.nodeRenameInput().should('be.visible').type('{selectall}').type(newName); + this.getters.nodeRenameInput() + .should('be.visible') + .type('{selectall}') + .type(newName); cy.get('body').type('{enter}'); }, + executePrevious: () => { + this.getters.executePrevious().click(); + }, + mapDataFromHeader: (col: number, parameterName: string) => { + const draggable = `[data-test-id="ndv-input-panel"] [data-test-id="ndv-data-container"] table th:nth-child(${col})`; + const droppable = `[data-test-id="parameter-input-${parameterName}"]`; + cy.draganddrop(draggable, droppable); + }, + switchInputMode: (type: 'Schema' | 'Table' | 'JSON' | 'Binary') => { + this.getters.inputDisplayMode().find('label').contains(type).click(); + }, + switchOutputMode: (type: 'Schema' | 'Table' | 'JSON' | 'Binary') => { + this.getters.outputDisplayMode().find('label').contains(type).click(); + }, }; } diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index a5d9cd24a8..eade824115 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -173,7 +173,8 @@ Cypress.Commands.add('paste', { prevSubject: true }, (selector, pastePayload) => }); }); -Cypress.Commands.add('drag', (selector, xDiff, yDiff) => { +Cypress.Commands.add('drag', (selector, pos) => { + const [xDiff, yDiff] = pos; const element = cy.get(selector); element.should('exist'); @@ -188,3 +189,21 @@ Cypress.Commands.add('drag', (selector, xDiff, yDiff) => { }); element.trigger('mouseup'); }); + +Cypress.Commands.add('draganddrop', (draggableSelector, droppableSelector) => { + cy.get(draggableSelector).should('exist'); + cy.get(droppableSelector).should('exist'); + + const droppableEl = Cypress.$(droppableSelector)[0]; + const coords = droppableEl.getBoundingClientRect(); + + const pageX = coords.left + coords.width / 2; + const pageY = coords.top + coords.height / 2; + + cy.get(draggableSelector).realMouseDown(); + cy.get(droppableSelector).realMouseMove(pageX, pageY) + .realHover() + .realMouseUp(); +}); + + diff --git a/cypress/support/index.ts b/cypress/support/index.ts index abcdbd8d70..f0417ce8cc 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -32,7 +32,8 @@ declare global { grantBrowserPermissions(...permissions: string[]): void; readClipboard(): Chainable; paste(pastePayload: string): void; - drag(selector: string, xDiff: number, yDiff: number): void; + drag(selector: string, target: [number, number]): void; + draganddrop(selector: string, target: string): void; } } } diff --git a/packages/editor-ui/src/components/InputPanel.vue b/packages/editor-ui/src/components/InputPanel.vue index 07946edc47..3bb7b2458e 100644 --- a/packages/editor-ui/src/components/InputPanel.vue +++ b/packages/editor-ui/src/components/InputPanel.vue @@ -85,6 +85,7 @@ :label="$locale.baseText('ndv.input.noOutputData.executePrevious')" @execute="onNodeExecute" telemetrySource="inputs" + data-test-id="execute-previous-node" /> diff --git a/packages/editor-ui/src/components/RunData.vue b/packages/editor-ui/src/components/RunData.vue index 5ff5e96cc0..f48ee8f191 100644 --- a/packages/editor-ui/src/components/RunData.vue +++ b/packages/editor-ui/src/components/RunData.vue @@ -172,7 +172,7 @@
-
+
{{ executingMessage }}