Draggable actions

A draggable element can be selected by the user with a mouse, dragged to a droppable element, and dropped by releasing the mouse button.

Draggable element declaration

A draggable element must include draggable in its type property. In this example, one element is just draggable, but usually an element is both clickable and draggable because a user needs to click before they can drag.

{
    "elements": [
        {
            "name": "onlyDraggable",
            "type": ["draggable"],
            "selector": {
                "css": ".drag-me"
            },
            "public": true
        },
        {
            "name": "myDraggable",
            "type": ["clickable", "draggable"],
            "selector": {
                "css": ".drag-me-too"
            },
            "public": true
        }
    ]
}

DragAndDrop API

The draggable type exposes the following API for an element:

A second optional parameter defines a hold duration in seconds that is applied after the click and before the drag. This hold duration can be useful to simulate a real user in some cases.

Usage in a JavaScript test:

const source = await pageObject.getMyDraggable();
const target = await pageObject.getDropTarget();
// drag and drop without hold
await source.dragAndDrop(target);

Usage in a Java test:

Draggable source = pageObject.getMyDraggable();
BasicElement target = pageObject.getDropTarget();
// drag and drop with 3 sec hold
source.dragAndDrop(target, 3);

Usage in a JavaScript test:

const source = await pageObject.getMyDraggable();
// drag and drop with 3 sec hold
await source.dragAndDropByOffset(100, 100, 3);

Usage in a Java test:

Draggable source = pageObject.getMyDraggable();
// drag and drop without hold
source.dragAndDropByOffset(100, 100);

Compose dragAndDrop

It's a good practice to compose a UI interaction as a public method inside a page object. Here are some examples of how to compose dragAndDrop and dragAndDropByOffset and how to provide a target element as a parameter.

Drop at a known target element

The most common use case is to drop an element at a known target element location. To do that, we pass elementReference to a known element as an argument:

{
    "elements": [
        {
            "name": "myDraggable",
            "type": ["draggable"],
            "selector": { "css": ".foo" }
        },
        {
            "name": "dropTarget",
            "selector": { "css": ".moo" }
        }
    ],
    "methods": [
        {
            "name": "dragAndDropAtTarget",
            "compose": [
                {
                    "element": "myDraggable",
                    "apply": "dragAndDrop",
                    "args": [
                        {
                            "type": "elementReference",
                            "value": "dropTarget"
                        }
                    ]
                }
            ]
        }
    ]
}

The generated method declarations have no parameters because the target element is referenced inside the statement of the dragAndDropAtTarget method.

Generated JavaScript method declaration:

dragAndDropAtTarget(): Promise<unknown>;

Generated Java method declaration:

public void dragAndDropAtTarget();

Drop at a known target element with hardcoded arguments

It's possible that the target element needs arguments for the selector or filter. Those arguments can be hardcoded in a compose statement. This example passes a hardcoded myClass value to the dropTarget in the dragAndDropAtTarget method.

{
    "elements": [
        {
            "name": "myDraggable",
            "type": ["draggable"],
            "selector": { "css": ".foo" }
        },
        {
            "name": "dropTarget",
            "selector": {
                "css": "target[class='%s']",
                "args": [
                    {
                        "name": "classStr",
                        "type": "string"
                    }
                ]
            }
        }
    ],
    "methods": [
        {
            "name": "dragAndDropAtTarget",
            "compose": [
                {
                    "element": "myDraggable",
                    "apply": "dragAndDrop",
                    "args": [
                        {
                            "type": "elementReference",
                            "value": "dropTarget",
                            "args": [{ "value": "myClass" }]
                        }
                    ]
                }
            ]
        }
    ]
}

If we omitted the "value" : "myClass" hardcoded value for the dropTarget element, it would be inferred and added to the method parameters.

JavaScript example:

dragAndDropAtTarget(classStr: string): Promise<unknown>;

Java example:

public void dragAndDropAtTarget(String classStr);

Drop by offset inside a frame

It's always more reliable to dragAndDrop with a target element as shown in the previous section. However, that's not possible if the target element is inside a frame. Here's an example for Java:

// get source element coordinates
ElementRectangle source = sourceElement.getRect();

// enter frame and get target coordinates
MyPageObject targetPageObjectInsideFrame = utam.enterFrameAndLoad(frameElement, MyPageObject.class);
ElementRectangle target = targetPageObjectInsideFrame.getTargetElement().getRect();
utam.exitFrame();

// offset is a difference between element centers
int xOffset = (target.getX() + target.getWidth()/2) - (source.getX() + source.getWidth()/2);
int yOffset = (target.getY() + target.getHeight()/2) - (source.getY() + source.getHeight()/2);

// drag and drop with a 5 second pause
source.dragAndDropByOffset(xOffset, yOffset, 5);

We don't show JavaScript code here but it's similar and uses the same methods.