DEV Community

Cover image for How to handle drag and drop with Cypress in Workflow Builder
Daniil
Daniil

Posted on

How to handle drag and drop with Cypress in Workflow Builder

Drag’n’drop is one of those UI interactions that used to feel painful to automate.
Rarely used, often avoided… but very real in modern products, especially a Agentic Workflow Builders like Opus or n8n, OpenAI, Monday.com, etc.

Workflow builders, canvas editors, node-based UIs — like Opus — rely on it heavily.

The good news: Cypress (with the right approach) handles this surprisingly well.

Two main ways to do drag’n’drop in Cypress

Native browser events

.trigger("mousedown")
.trigger("mousemove")
.trigger("mouseup")

This gives you full control over coordinates and is perfect for:

  • canvas-based UIs
  • node connectors
  • custom drag logic

Cypress-real-events plugin

Closer to real user behavior and great for classic UI drag cases.

Real-world example: connecting nodes on a canvas.

This is how I automate workflow connections in Opus

// Cypress Custom Command to connect workflow units - Tasks(Nodes).

Cypress.Commands.add("connectNodes", (outputSelector, inputSelector, numberOfEdges) => {
  function getCenterCoords($el) {
    const rect = $el[0].getBoundingClientRect();
    return {
      x: rect.x + rect.width / 2,
      y: rect.y + rect.height / 2,
    };
  }

  cy.get(outputSelector).then(($out) => {
    const { x: outX, y: outY } = getCenterCoords($out);

    cy.get(inputSelector).then(($in) => {
      const { x: inX, y: inY } = getCenterCoords($in);

      cy.wrap($out)
        .trigger("mousedown", { button: 0, clientX: outX, clientY: outY, force: true })
        .trigger("mousemove", { clientX: inX, clientY: inY, force: true });

      cy.wrap($in).trigger("mouseup", { force: true });

      cy.get("[data-test-id='edge']")
        .should("exist")
        .and("have.length", numberOfEdges);
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

This approach lets tests build workflows automatically, just like real users do.

And bonus: file uploads.
While it seems to be the same method, it really depends on which packages your product is using. So there is 2 approaches:

  • .selectFile() - built-in way to select files in an HTML5 input element & simulate dragging files into a browser with the .selectFile()
  • Native browser events:

.trigger("dragenter")
.trigger("dragover")
.trigger("drop")

// Cypress Custom Command to drag'n'drop file into upload area, e.g. React Aria DropZone

Cypress.Commands.add(
  "dragAndDropUploadFile",
  (fileName, targetSelector, mimeType = "application/json") => {
    cy.intercept("POST", "/job/file/download").as("fileDownload");

    cy.fixture(`sampleFiles/${fileName}`, "base64").then((fileContent) => {
      const blob = Cypress.Blob.base64StringToBlob(fileContent, mimeType);
      const testFile = new File([blob], fileName, { type: mimeType });

      const dataTransfer = new DataTransfer();
      dataTransfer.items.add(testFile);

      cy.get(targetSelector)
        .trigger("dragenter", { dataTransfer })
        .trigger("dragover", { dataTransfer })
        .trigger("drop", { dataTransfer });
    });
    // Verify status of the upload process with custom command
    cy.verifyApiStatus("@fileDownload", 201);
  },
);
Enter fullscreen mode Exit fullscreen mode

Key takeaway

Drag’n’drop isn’t just about moving pixels.
It’s about testing user intent, state transitions, and real AI workflows.

Once you stop avoiding it — Cypress gives you everything you need.

Next up: more canvas, workflow, and agentic UI testing patterns 👀

How do you handle drag’n’drop in your tests?

Top comments (0)