diff --git a/cypress/e2e/12-canvas-actions.cy.ts b/cypress/e2e/12-canvas-actions.cy.ts index d336294f48..3f23f90484 100644 --- a/cypress/e2e/12-canvas-actions.cy.ts +++ b/cypress/e2e/12-canvas-actions.cy.ts @@ -78,6 +78,42 @@ describe('Canvas Actions', () => { WorkflowPage.getters.nodeConnections().should('have.length', 1); }); + it('should add a connected node dragging from node creator', () => { + WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); + cy.get('.plus-endpoint').should('be.visible').click(); + WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible'); + WorkflowPage.getters.nodeCreatorSearchBar().type(CODE_NODE_NAME); + cy.drag( + WorkflowPage.getters.nodeCreatorNodeItems().first(), + [100, 100], + { + realMouse: true, + abs: true + } + ); + cy.get('body').type('{esc}'); + WorkflowPage.getters.canvasNodes().should('have.length', 2); + WorkflowPage.getters.nodeConnections().should('have.length', 1); + }); + + it('should open a category when trying to drag and drop it on the canvas', () => { + WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); + cy.get('.plus-endpoint').should('be.visible').click(); + WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible'); + WorkflowPage.getters.nodeCreatorSearchBar().type(CODE_NODE_NAME); + cy.drag( + WorkflowPage.getters.nodeCreatorActionItems().first(), + [100, 100], + { + realMouse: true, + abs: true + } + ); + WorkflowPage.getters.nodeCreatorCategoryItems().its('length').should('be.gt', 0); + WorkflowPage.getters.canvasNodes().should('have.length', 1); + WorkflowPage.getters.nodeConnections().should('have.length', 0); + }); + it('should add disconnected node if nothing is selected', () => { WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); // Deselect nodes diff --git a/cypress/pages/workflow.ts b/cypress/pages/workflow.ts index 2176d9b91e..31f509e3a8 100644 --- a/cypress/pages/workflow.ts +++ b/cypress/pages/workflow.ts @@ -97,6 +97,9 @@ export class WorkflowPage extends BasePage { nodeCredentialsCreateOption: () => cy.getByTestId('node-credentials-select-item-new'), nodeCredentialsEditButton: () => cy.getByTestId('credential-edit-button'), nodeCreatorItems: () => cy.getByTestId('item-iterator-item'), + nodeCreatorNodeItems: () => cy.getByTestId('node-creator-node-item'), + nodeCreatorActionItems: () => cy.getByTestId('node-creator-action-item'), + nodeCreatorCategoryItems: () => cy.getByTestId('node-creator-category-item'), ndvParameters: () => cy.getByTestId('parameter-item'), nodeCredentialsLabel: () => cy.getByTestId('credentials-label'), getConnectionBetweenNodes: (sourceNodeName: string, targetNodeName: string) => diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 6c7adaea8f..812404ff7a 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -103,19 +103,31 @@ Cypress.Commands.add('paste', { prevSubject: true }, (selector, pastePayload) => Cypress.Commands.add('drag', (selector, pos, options) => { const index = options?.index || 0; const [xDiff, yDiff] = pos; - const element = cy.get(selector).eq(index); + const element = typeof selector === 'string' ? cy.get(selector).eq(index) : selector; element.should('exist'); - const originalLocation = Cypress.$(selector)[index].getBoundingClientRect(); + element.then(([$el]) => { + const originalLocation = $el.getBoundingClientRect(); + const newPosition = { + x: options?.abs ? xDiff : originalLocation.x + xDiff, + y: options?.abs ? yDiff : originalLocation.y + yDiff, + } - element.trigger('mousedown', { force: true }); - element.trigger('mousemove', { - which: 1, - pageX: options?.abs ? xDiff : originalLocation.right + xDiff, - pageY: options?.abs ? yDiff : originalLocation.top + yDiff, - force: true, + if(options?.realMouse) { + element.realMouseDown(); + element.realMouseMove(newPosition.x, newPosition.y); + element.realMouseUp(); + } else { + element.trigger('mousedown', {force: true}); + element.trigger('mousemove', { + which: 1, + pageX: newPosition.x, + pageY: newPosition.y, + force: true, + }); + element.trigger('mouseup', {force: true}); + } }); - element.trigger('mouseup', { force: true }); }); Cypress.Commands.add('draganddrop', (draggableSelector, droppableSelector) => { diff --git a/cypress/support/index.ts b/cypress/support/index.ts index 196a14d9ec..f1602b3e06 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -31,7 +31,7 @@ declare global { grantBrowserPermissions(...permissions: string[]): void; readClipboard(): Chainable; paste(pastePayload: string): void; - drag(selector: string, target: [number, number], options?: {abs?: true, index?: number}): void; + drag(selector: string | Cypress.Chainable>, target: [number, number], options?: {abs?: boolean, index?: number, realMouse?: boolean}): void; draganddrop(draggableSelector: string, droppableSelector: string): void; } } diff --git a/packages/editor-ui/src/components/CodeNodeEditor/CodeNodeEditor.vue b/packages/editor-ui/src/components/CodeNodeEditor/CodeNodeEditor.vue index 32d22e6b9a..6e21e94099 100644 --- a/packages/editor-ui/src/components/CodeNodeEditor/CodeNodeEditor.vue +++ b/packages/editor-ui/src/components/CodeNodeEditor/CodeNodeEditor.vue @@ -24,7 +24,7 @@ import type { PropType } from 'vue'; import { mapStores } from 'pinia'; import type { LanguageSupport } from '@codemirror/language'; -import type { Extension } from '@codemirror/state'; +import type { Extension, Line } from '@codemirror/state'; import { Compartment, EditorState } from '@codemirror/state'; import type { ViewUpdate } from '@codemirror/view'; import { EditorView } from '@codemirror/view'; @@ -154,18 +154,29 @@ export default defineComponent({ changes: { from: 0, to: this.content.length, insert: this.placeholder }, }); }, - highlightLine(line: number | 'final') { + line(lineNumber: number): Line | null { + try { + return this.editor?.state.doc.line(lineNumber) ?? null; + } catch { + return null; + } + }, + highlightLine(lineNumber: number | 'final') { if (!this.editor) return; - if (line === 'final') { + if (lineNumber === 'final') { this.editor.dispatch({ selection: { anchor: this.content.length }, }); return; } + const line = this.line(lineNumber); + + if (!line) return; + this.editor.dispatch({ - selection: { anchor: this.editor.state.doc.line(line).from }, + selection: { anchor: line.from }, }); }, trackCompletion(viewUpdate: ViewUpdate) { diff --git a/packages/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue b/packages/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue index 9e871ad35d..849c647380 100644 --- a/packages/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue +++ b/packages/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue @@ -9,6 +9,7 @@ :title="displayName" :show-action-arrow="showActionArrow" :is-trigger="isTrigger" + :data-test-id="dataTestId" >