Redsauce's software and cybersecurity blog

Drag and Drop Between Tabs with Playwright

Posted by Alejandro Rey

{1570}

A few days ago, I encountered a use case that required a relatively simple functionality: performing a drag from an element in one tab to a drop in a location in another tab. It was the first time in my five years of QA experience that I had to tackle such a use case. Personally, I don’t think it’s the best user experience, but the application is already developed, and we need to solve the problem!

The Challenge

Automating E2E tests is complex on its own, but when it comes to interacting between browser tabs via Drag and Drop, the challenge is amplified. Major automation frameworks like Playwright, Cypress, and WebdriverIO don’t support this natively. Therefore, to tackle this type of interaction, we had to look for “alternative” solutions.

Understanding the Mechanism with DevTools

To address the issue, we first turned to the browser’s DevTools to get as much detail as possible about what was happening. We used Event Listener Breakpoints to intercept and handle the key Drag and Drop events: dragstart, dragover, drop, among others. During this process, we discovered that when the dragstart event was fired, the program added a series of items to the event.DataTransfer, which were contained in the draggable element itself, in the form of custom attributes. Additionally, this information was stored in localStorage, a technique widely used before to maintain the state of dragged elements across sessions.

Implementing the Solution with Injected JavaScript

We couldn’t perform Drag and Drop between tabs directly with the automation tools. So, we decided to perform Drag and Drop within the same tab that contains the element to be dragged, thus ensuring that this part of the code was actually executed by the web application.


To do this, we copied the element with all its children and, using JavaScript, added it to the tab containing the drop target element. In this tab, we added an Event Listener to the newly created element so that, when dragging the element, it reads the previously created information from localStorage and adds it to the event.DataTransfer, just as the application does during real use. With everything configured, we performed a Drag and Drop using the native methods of the automation framework, in this case, Playwright. We had to implement a double locator.hover() to ensure that the dragover event was fired correctly across all browsers, following the sequence: hover over the drag element, mouse down, hover over the drop target twice, and then mouse up, as indicated in the official documentation in version 1.46.


Here’s a generalized version of our solution:

export async function customDragAndDrop(page: Page, page2: Page) { 
const dragHtml = await page.waitForSelector("[draggable="true"]", { timeout: 5000 })
.then(async element => await element.evaluate(node => node.outerHTML))
.catch(() => { throw new Error("No draggable element found on the page."); });

await page.locator("span[draggable="true"]").dragTo(page.getByText("Refresh"));

await page2.bringToFront();

await page2.evaluate((dragHtml) => {
const targetContainer = document.querySelector("#wrapper");
const newDiv = document.createElement("div");
newDiv.innerHTML = dragHtml;
targetContainer.appendChild(newDiv);
newDiv.addEventListener("dragstart", (event) => {
const local = localStorage.getItem("ngStorage-clipboard");
event.dataTransfer.setData("element-items", local);
});
}, dragHtml);

await page2.locator("span[draggable="true"]").hover();
await page2.mouse.down();
await page2.locator("#droppable").hover();
await page2.locator("#droppable").hover();
await page2.mouse.up();

await expect(page2.getByText("Action completed successfully.")).toBeVisible({timeout: 30000});
}

Importance of Mastering Web APIs

This achievement highlights the importance of the automation team understanding and mastering Web APIs. It’s not just about knowing automation tools but also about understanding how web applications work at a deeper level. In this case, although we couldn’t interact exactly as a real user would due to the limitations of automation tools, we executed the minimal JavaScript code necessary to closely respect the original implementation and behavior of the application.

Skills and Collaboration in QA

Once we clearly understood the application behavior and had a solution, we communicated the proposal to the development team. Collaborating with them was crucial at this stage to ensure that our approach was correct and aligned with the original application implementation. This allowed us to move forward with confidence towards the solution.


At Redsauce, we understand that a good QA should be able to develop or, at least, understand the concepts behind web development. This understanding is crucial for tackling complex problems and finding innovative solutions. Additionally, integrating the QA role as part of the development team is essential for facilitating collaboration, support, and understanding of the application. All of this is vital when facing complex issues.

Conclusion

Automating a Drag and Drop scenario between browser tabs wasn’t an easy task, but with creativity and technical knowledge, we were able to handle it. This achievement underscores the importance of a comprehensive approach in development and test automation.


This experience reinforces our belief in the importance of deeply understanding web technologies and working closely with developers to overcome unique and complex challenges.

About us

You have reached the blog of Redsauce, a team of experts in QA and software development. Here we will talk about agile testing, automation, programming, cybersecurity… Welcome!