End-to-end Testing
How-tos & Guides
12 min read

How to test drag-and-drop interactions in Playwright

Learn everything you need to know to test drag-and-drop interaction with the Playwright testing framework

Antonello Zanini
Published October 3, 2022
AI Assistant for Playwright
Code AI-powered test steps with the free ZeroStep JavaScript library
Learn more

The way we interact with devices is constantly evolving. The mouse and then touchscreens introduced new forms of interaction, and one of the most popular is drag and drop. This involves dragging an object on the screen from point X to point Y.

Two phenomena have led to an increased prevalence of drag-and-drop interactions:

  1. As web applications continue to replace traditional desktop apps, they have taken on more of the interactive features of their predecessors, including more drag-and-drop functionality.
  2. The web is increasing mobile-first, which makes it critical to support mobile-native interactions such as swiping.

In this article, you will learn how to test drag-and-drop interactions with Playwright. In detail, you will see how to test drag and drop in a TypesScript script, both with Playwright’s drag-and-drop API as well as with custom logic.

What is Playwright?

Playwright is a new test automation framework that supports end-to-end testing for any kind of web app. This open-source technology was released by Microsoft in 2020 and has built-in support for Firefox, WebKit, and Chromium-based browsers. Playwright provides a broad range of cross-browser testing features, and can be used across a variety of programming languages including TypeScript, JavaScript, Python, Java, and C#.

Playwright has everything you need to develop and execute UI tests. Plus, you can run Playwright headless or headed tests on Linux, Windows, and macOS, as well as run it locally or on a Continous Integration server.

What is a drag-and-drop interaction?

“Drag and drop” refers to a type of interaction where a user drags a source element on the screen and releases it onto another element (sometimes called the “drop target” or simply “target”). Drag and drop by definition requires a mouse or touchscreen interaction, however supporting equivalent actions may exist via keyboard shortcuts for accessibility reasons.

Drag–and–drop interactions consist of the following three steps:

  1. The user starts the interaction by grabbing an object using the mouse or a touch gesture.
  2. While keeping the object selected, the user moves it with their pointing device to the desired target. This is what is called the “drag” operation.
  3. The user deselects the source object, which remains in the target location. This is what is called the “drop” operation.

Now, let’s see when and where the drag-and-drop interaction is used.

Which web elements use drag-and-drop?

There are several cases where you need to use drag and drop to implement optimal user interaction. Let’s now focus on the top five components where and when drag-and-drop interaction is necessary.

Sliders

Sliders allow you to select a value from a range through drag-and-drop interaction, as below:

Example of a slider component

Example of a slider component

Range Sliders

Range sliders are a special type of slider that allows you to select a range of values via drag-and-drop interaction, as follows:

A slider component in action

A slider component in action

Canvases

Canvas components allow you to draw something with your mouse or finger through drag-and-drop interaction. These elements have become popular for e-signatures, enabling users to sign documents online.

Drawing ‘Hi!’ in a canvas element

Drawing ‘Hi!’ in a canvas element

Toggles

Toggles enable you to select one of several states for a given element, with the most common configuration being a boolean “on” / “off” state. These stylized versions of the native <input type="checkbox"> element will almost always be toggleable with a single click, but may also be toggled via a slide-right or slide-left action.

An on/off swipe component

An on/off swipe component

Custom drag-and-drop components

In this category of components, you can find all custom components that explicitly require dragging and dropping. Included are components in which you can move an element from one list to another, components within a game in which you can move an element or character from one position to another like in chess, or sorting components in which you can manually sort items in a list.

How to perform drag-and-drops with Playwright

Let’s learn how to test drag-and-drop interactions with Playwright in a TypeScript script.

Prerequisites

To get started with Playwright, you only need the following prerequisite:

If you do not have Node.js, download and install the latest LTS version by clicking the link above.

Setting up your Playwright project

Create a playwright-dnd-example directory and access it in the terminal with the commands below:

1
2
    mkdir playwright-dnd-example
    cd playwright-dnd-example

Then, initialize your Playwright project with the following command:

1
    npm init playwright@latest

As stated in the official documentation, this will install Playwright and set up a new project.

During the process, you will have to answer a few questions. Here, let’s provide the default answers by pressing Enter on each question:

1
2
3
4
5
6
    Getting started with writing end-to-end tests with Playwright:
    Initializing project in '.'
    √ Do you want to use TypeScript or JavaScript? · TypeScript
    √ Where to put your end-to-end tests? · tests
    √ Add a GitHub Actions workflow? (y/N) · N
    Initializing NPM project (npm init -y)

By default, Playwright will create a TypeScript project.

At the end of the process, your playwright-dnd-example folder should contain the following files:

1
2
3
4
5
6
7
    playwright.config.ts
    package.json
    package-lock.json
    tests/
      example.spec.ts
    tests-examples/
      demo-todo-app.spec.ts

playwright.config contains the Playwright configs, while in the tests folder you can find some basic tests. By default, Playwright initializes the playwright.config file to run each test on Firefox, Webkit, and Chromium at the same time. Also, tests will be run in headless mode. This means that no browser window will be displayed while running the tests.

You are now ready to write your first testing script in Playwright. To do so, initialize the example.spec.ts test file as follows:

1
2
3
4
5
6
// tests/example.spec.ts
import { test, expect } from "@playwright/test";

test("drag-and-drop test", async ({ page }) => {
  // test logic ...
});

As you can see, the Playwright test() function takes a title and a function. The title is used in the logs and HTML report to identify the test. The test function gives you access to the Playwright page variable, which provides all the testing features you can use to implement the test logic. In the body of this function, you will use the Playwright expect() function to check assertions.

Before testing a web page, you need to connect to it. In this tutorial, you will see how to test the Ant Design Slider component from the https://x2f9rh.csb.app/ sandbox webpage.

The https://x2f9rh.csb.app/ app contains only two sliders. The first one goes from 1 to 20 and will be the target of the test. You can instruct Playwright to navigate to https://x2f9rh.csb.app/ using the page.goto() function as follows:

1
await page.goto("https://x2f9rh.csb.app/");

This webpage is generated by CodeSandbox and may take a while to load. To avoid timeout errors, extend the timeout config value to 20000 milliseconds in the playwright.config file as below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const config: PlaywrightTestConfig = {
  // ...
  expect: {
    /**
     * Maximum time expect() should wait for the condition to be met.
     * For example in `await expect(locator).toHaveText();`
     */
    timeout: 20000,
  },
  // ...
};

export default config;

Your new test will now look as follows:

1
2
3
4
5
6
7
8
9
// tests/example.spec.ts
import { test, expect } from "@playwright/test";

test("drag-and-drop test", async ({ page }) => {
  // navigating to the Ant slider component test page
  await page.goto("https://x2f9rh.csb.app/");

  // test logic ...
});

Launch your Playwright tests with the command below:

1
    npx playwright test

Once completed, type the following command to get access to the Playwright HTML report.

1
    npx playwright show-report

This report is automatically generated by Playwright and contains all the info collected during the execution of your tests. Plus, you can use this HTML report to filter the results by browsers and by passed, failed, skipped, and flaky tests.

Approach #1: Using the Playwright dragTo function

Playwright currently supports drag-and-drop interactions via the locator.dragTo() function. You can use it as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// tests/example.spec.ts
import { test, expect } from "@playwright/test";

test("drag-and-drop test", async ({ page }) => {
  // navigating to the Ant slider component test page
  await page.goto("https://x2f9rh.csb.app/");

  // the value corresponding to the 100% of the slider
  const maxValue = 20;
  // drag-and-drop target value in percentage
  const targetValue = 0.4; // 40%

  // retrieving the slider handle HTML element
  const sliderHandle = page.locator(".ant-slider-handle").first();
  // retrieving the slider HTML element
  const slider = page.locator(".ant-slider.ant-slider-horizontal").first();

  // getting the slider bounding box size
  const sliderBoundingBox = await slider.boundingBox();

  // performing the drag-and-drop interaction
  await sliderHandle.dragTo(sliderHandle, {
    force: true,
    targetPosition: {
      // moving the slider to the target value in %
      x: sliderBoundingBox.width * targetValue,
      y: 0,
    },
  });

  // retrieving the input HTML element
  const input = page.locator(".ant-input-number-input").first();
  // getting the "value" HTML attribute
  const value = await input.getAttribute("value");

  // calculating the expected value
  const expectedValue = `${maxValue * targetValue}`;

  expect(value).toEqual(expectedValue);
});

This script selects the first slider, drag-and-dropping its moving element to 40% of the slider total width, and verifies that the input value matches the expected value.

To make the script works, you have to call the dragTo() function on the same source and target sliderHandle element. Also, the flag value to bypass the Playwright actionability checks must be true.

You can call the boundingBox() function on the slider parent div to get its dimensions and be able to calculate the position where to move sliderHandle to. Note that the webpage contains only x-based sliders, so the y coordinate can remain 0.

Et voilà! You just learned how to test drag-and-drop interaction with the Playwright API!

Approach #2: Using custom JavaScript to emulate mouse actions

Drag-and-drop interaction can be implemented either through the native HTML Drag and Drop API or custom JavaScript logic. In this last scenario, the Playwright locator.dragTo() function may not work.

You can still use Playwright to test the drag-and-drop interaction, but this time you need to implement custom logic. Consider the dragAndDrop() function in the example below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// tests/utils/utils.ts
import { Locator, Page } from "@playwright/test";

export async function dragAndDrop(
  page: Page,
  dragLocator: Locator,
  dropLocator: Locator,
  targetPosition?: { x: number; y: number }
) {
  const dragBoundingBox = await dragLocator.boundingBox();
  const dropBoundingBox = await dropLocator.boundingBox();

  // moving the mouse to the center of the drag HTML element
  await page.mouse.move(dragBoundingBox.x + dragBoundingBox.width / 2, dragBoundingBox.y + dragBoundingBox.height / 2);

  // activating the drag action
  await page.mouse.down();

  // if targetPosition is undefined, defining the center of the
  // drop HTML element as the target position
  const targetX = targetPosition?.x || dropBoundingBox.x + dropBoundingBox.width / 2;
  const targetY = targetPosition?.y || dropBoundingBox.y + dropBoundingBox.height / 2;

  // moving the mouse to the (targetX, targetY) coordinates of the
  // drop element
  await page.mouse.move(targetX, targetY);

  // releasing the mouse and terminating the drop option
  await page.mouse.up();
}

This function manually implements the drag-and-drop logic by performing every mouse action necessary to simulate such interaction.

Then, you can use it as below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// tests/example.spec.ts
import { test, expect } from "@playwright/test";
import { dragAndDrop } from "./utils/utils";

test("drag-and-drop test", async ({ page }) => {
  // navigating to the Ant slider component test page
  await page.goto("https://x2f9rh.csb.app/");

  // the value corresponding to the 100% of the slider
  const maxValue = 20;
  // drag-and-drop target value in percentage
  const targetValue = 0.4; // 40%

  // retrieving the slider handle HTML element
  const sliderHandle = page.locator(".ant-slider-handle").first();
  // retrieving the slider HTML element
  const slider = page.locator(".ant-slider.ant-slider-horizontal").first();

  // getting the slider bounding box size
  const sliderBoundingBox = await slider.boundingBox();

  // performing the drag-and-drop interaction
  await dragAndDrop(page, sliderHandle, sliderHandle, { x: sliderBoundingBox.width * targetValue, y: 0 });

  // retrieving the input HTML element
  const input = page.locator(".ant-input-number-input").first();
  // getting the "value" HTML attribute
  const value = await input.getAttribute("value");

  // calculating the expected value
  const expectedValue = `${maxValue * targetValue}`;

  expect(value).toEqual(expectedValue);
});

Again, this test works just like the previous one, but it does not rely on the Playwright Drag-and-Drop API.

Congrats! You just saw how to test drag-and-drop interaction with custom logic in Playwright!

Drag-and-drop testing made easier with Reflect

Testing drag-and-drop in Playwright with the built-in dragTo() function may not work. As a result, you might end up writing custom logic. This is time-consuming, especially considering how popular drag-and-drop elements are. Plus, your custom logic may not work perfectly in every situation and make your tests less reliable. Avoid all this with a complete, advanced, easy-to-use testing tool like Reflect!

Reflect is a no-code testing tool that can test virtually any action you can take on a browser. Anyone, even non-technical users, can use it to define tests involving clicks, taps, hovers, file uploads, and more. Reflect also supports complex interactions, such as drag-and-drop.

As you can see from the video above, creating tests has never been easier. All you have to do is take some actions on the target website in the browser. Reflect will record those actions and automatically translate them into a repeatable test that you can run at any time. Try Reflect for free and take your test coverage to the next level!

Conclusion

In this article, you learned what Playwright is, what drag-and-drop is, how common this is, and you can use this type of interaction in testing. In particular, you had the opportunity to take a look at how to use the Playwright testing framework to test a website containing components requiring drag-and-drop interaction. Here, you learned everything you need to know to write tests for drag and drop with Playwright, both with the official API and custom logic. Also, you saw why that approach to drag-and-drop testing may not be the best option. Save time and energy with a more powerful testing tool, such as Reflect.

Get started with Reflect today

Create your first test in 2 minutes, no installation or setup required. Accelerate your testing efforts with fast and maintainable test suites without writing a line of code.

Copyright © Reflect Software Inc. All Rights Reserved.