使用pytest结合Playwright实现页面元素在两个区域间拖拽功能

 更新时间:2026年01月23日 09:53:55   作者:问道飞鱼  
本文介绍了如何使用pytest结合Playwright实现页面元素在两个区域间的拖拽操作,通过创建一个简单的HTML页面和JavaScript代码来实现拖放功能,并使用Playwright的API来模拟和验证拖拽操作,需要的朋友可以参考下

好的,下面是使用 pytest 结合 Playwright 实现页面元素在两个区域间拖拽的完整示例。

这个示例将创建一个包含两个区域(sourcetarget)和一个可拖拽区块的 HTML 页面,然后使用 Playwright 模拟将该区块从一个区域拖拽到另一个区域的操作。

示例场景

我们将创建一个简单的 HTML 页面,包含:

  1. Source Area: 包含一个可拖拽的区块(例如一个带颜色的 <div>)。
  2. Target Area: 一个空的区域,用于接收被拖拽的区块。
  3. JavaScript: 实现 HTML5 拖放 API,处理 dragstart, dragover, 和 drop 事件,以便将元素从 source 移动到 target

1. 创建示例 HTML 页面 (drag_drop_block.html)

将以下 HTML 代码保存为 drag_drop_block.html 文件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Block Drag & Drop Example</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 40px;
        }

        .area {
            width: 300px;
            height: 200px;
            border: 2px dashed #ccc;
            display: inline-block;
            vertical-align: top;
            margin: 20px;
            padding: 10px;
            position: relative;
        }

        #source-area {
            background-color: #e0f7fa; /* Light Blue */
        }

        #target-area {
            background-color: #f3e5f5; /* Light Purple */
        }

        .draggable-block {
            width: 100px;
            height: 100px;
            background-color: #f44336; /* Red */
            color: white;
            text-align: center;
            line-height: 100px; /* Vertically center text */
            cursor: move; /* Show move cursor */
            user-select: none; /* Prevent text selection */
            position: absolute; /* Position within parent */
            top: 30px;
            left: 50px;
        }

        .block-content {
            font-size: 12px;
        }

        /* Optional: Visual feedback during drag */
        .draggable-block.dragging {
            opacity: 0.5;
        }

        /* Success indicator */
        .success-indicator {
            color: green;
            font-weight: bold;
            display: none; /* Hidden initially */
        }

        #target-area.success .success-indicator {
            display: block;
        }
    </style>
</head>
<body>

    <h1>Block Drag & Drop Test</h1>

    <div id="source-area" class="area">
        <h3>Source Area</h3>
        <div id="draggable-block" class="draggable-block" draggable="true">
            <span class="block-content">Drag Me!</span>
        </div>
    </div>

    <div id="target-area" class="area">
        <h3>Target Area</h3>
        <p class="success-indicator">Block dropped successfully!</p>
    </div>

    <script>
        const draggableBlock = document.getElementById('draggable-block');
        const sourceArea = document.getElementById('source-area');
        const targetArea = document.getElementById('target-area');

        draggableBlock.addEventListener('dragstart', function(event) {
            event.dataTransfer.setData("text/plain", "block-id"); // Optional: Set data
            // Add a class for visual feedback
            this.classList.add('dragging');
        });

        draggableBlock.addEventListener('dragend', function(event) {
            // Remove visual feedback
            this.classList.remove('dragging');
        });

        targetArea.addEventListener('dragover', function(event) {
            event.preventDefault(); // Crucial: Allows dropping
        });

        targetArea.addEventListener('drop', function(event) {
            event.preventDefault(); // Crucial: Allows dropping

            // Move the block from source to target
            // We know the block is the only draggable element in source
            sourceArea.removeChild(draggableBlock);
            targetArea.appendChild(draggableBlock);

            // Optional: Add success class to target area
            targetArea.classList.add('success');
        });

        // Allow dropping back into source area too (for demo purposes)
        sourceArea.addEventListener('dragover', function(event) {
            event.preventDefault();
        });

        sourceArea.addEventListener('drop', function(event) {
            event.preventDefault();

            // Move the block from target back to source
            targetArea.removeChild(draggableBlock);
            sourceArea.appendChild(draggableBlock);

            // Remove success class from target area
            targetArea.classList.remove('success');
        });
    </script>

</body>
</html>

说明:

  • draggable="true": 必须在可拖拽的元素上设置此属性。
  • CSS: 设置了 position: absolute 以便在容器内精确定位区块。
  • JavaScript:
    • dragstart: 设置拖拽数据(可选),添加视觉反馈类。
    • dragend: 移除视觉反馈类。
    • dragover必须调用 event.preventDefault(),否则 drop 事件不会触发。
    • drop必须调用 event.preventDefault()。然后执行元素的移动逻辑(removeChild + appendChild)。
    • 为了演示双向拖拽,source-area 也监听了 dragover 和 drop

2. Pytest + Playwright 测试代码 (test_block_drag_drop.py)

首先,确保安装了必要的库:

pip install pytest playwright
playwright install

然后,将以下 Python 代码保存为 test_block_drag_drop.py

# test_block_drag_drop.py
import pytest
from playwright.sync_api import Page, expect

# Pytest fixture to provide a fresh browser page for each test
@pytest.fixture(scope="function")
def page(context):
    """Creates a new page for each test function."""
    page = context.new_page()
    yield page
    page.close()

def test_drag_block_from_source_to_target(page: Page):
    """
    Tests dragging a block from the source area to the target area.
    """
    # 1. Navigate to the HTML file
    # Update the path to point to where you saved the HTML file
    page.goto("file:///absolute/path/to/your/drag_drop_block.html")

    # 2. Define selectors for the draggable block and the target area
    source_area_selector = "#source-area"
    target_area_selector = "#target-area"
    draggable_block_selector = "#draggable-block"

    # 3. Verify initial state: block is in source area
    expect(page.locator(source_area_selector + " " + draggable_block_selector)).to_be_attached()
    expect(page.locator(target_area_selector + " " + draggable_block_selector)).not_to_be_attached()
    expect(page.locator(f"{target_area_selector} .success-indicator")).not_to_be_visible()

    # 4. Perform the drag and drop operation
    page.drag_and_drop(draggable_block_selector, target_area_selector)

    # 5. Verify final state: block is in target area
    expect(page.locator(target_area_selector + " " + draggable_block_selector)).to_be_attached(message="Block should be in target area after drop.")
    expect(page.locator(source_area_selector + " " + draggable_block_selector)).not_to_be_attached(message="Block should not be in source area after drop.")
    expect(page.locator(f"{target_area_selector} .success-indicator")).to_be_visible(message="Success indicator should be visible in target area.")

def test_drag_block_back_to_source(page: Page):
    """
    Tests dragging the block back from the target area to the source area.
    """
    # 1. Navigate to the HTML file
    page.goto("file:///absolute/path/to/your/drag_drop_block.html")

    # 2. Define selectors
    source_area_selector = "#source-area"
    target_area_selector = "#target-area"
    draggable_block_selector = "#draggable-block"

    # 3. First, move the block to the target area (using the previous test's logic or just do it here)
    # Initial state check
    expect(page.locator(source_area_selector + " " + draggable_block_selector)).to_be_attached()
    expect(page.locator(target_area_selector + " " + draggable_block_selector)).not_to_be_attached()

    # Drag to target first
    page.drag_and_drop(draggable_block_selector, target_area_selector)

    # Confirm it's in target
    expect(page.locator(target_area_selector + " " + draggable_block_selector)).to_be_attached()
    expect(page.locator(source_area_selector + " " + draggable_block_selector)).not_to_be_attached()
    expect(page.locator(f"{target_area_selector} .success-indicator")).to_be_visible()

    # 4. Now, drag the block back from target to source
    page.drag_and_drop(draggable_block_selector, source_area_selector)

    # 5. Verify final state: block is back in source area
    expect(page.locator(source_area_selector + " " + draggable_block_selector)).to_be_attached(message="Block should be back in source area after second drop.")
    expect(page.locator(target_area_selector + " " + draggable_block_selector)).not_to_be_attached(message="Block should not be in target area after second drop.")
    expect(page.locator(f"{target_area_selector} .success-indicator")).not_to_be_visible(message="Success indicator should be hidden after moving block back to source.")

说明:

  • Fixture page: 为每个测试函数提供一个新的浏览器页面实例,并在测试结束后自动关闭,确保测试隔离。
  • page.goto(): 导航到你的本地 HTML 文件。务必更新 file:///... 后面的路径为你实际存放 drag_drop_block.html 的绝对路径。
  • page.drag_and_drop(source, target): Playwright 提供的核心方法,用于模拟拖放操作。它会处理底层的鼠标事件序列。
  • expect(...): Playwright 的断言库,用于验证 DOM 状态(元素是否存在、是否可见等)。它具有内置的智能等待机制。

3. 运行测试

在终端中,切换到包含 test_block_drag_drop.pydrag_drop_block.html 的目录,然后运行:

pytest test_block_drag_drop.py -v
  • -v 选项提供更详细的输出。

如果配置正确,你应该会看到类似以下的输出,并且浏览器窗口会短暂出现以执行测试:

============================= test session starts ==============================
platform linux -- Python 3.x.y, pytest-x.x.x, pluggy-x.x.x
rootdir: /path/to/your/test/directory
collected 2 items

test_block_drag_drop.py::test_drag_block_from_source_to_target PASSED    [ 50%]
test_block_drag_drop.py::test_drag_block_back_to_source PASSED           [100%]

============================== 2 passed in 3.12s ===============================

这表明两个测试(从 source 到 target,以及从 target 回到 source)都成功通过了。

以上就是使用pytest结合Playwright实现页面元素在两个区域间拖拽功能的详细内容,更多关于pytest Playwright页面元素拖拽的资料请关注脚本之家其它相关文章!

相关文章

  • TensorFlow实现打印每一层的输出

    TensorFlow实现打印每一层的输出

    今天小编就为大家分享一篇TensorFlow实现打印每一层的输出,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-01-01
  • 使用pipenv管理python虚拟环境的全过程

    使用pipenv管理python虚拟环境的全过程

    pipenv 是Kenneth Reitz大神的作品,能够有效管理Python多个环境,各种包,接下来通过本文给大家分享使用pipenv管理python虚拟环境的全过程,感兴趣的朋友一起看看吧
    2021-09-09
  • 深入了解Python的继承

    深入了解Python的继承

    这篇文章主要为大家介绍了Python 继承,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-12-12
  • python序列类型的实现示例

    python序列类型的实现示例

    Python序列类型分为不可变序列和可变序列,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-12-12
  • wxpython 最小化到托盘与欢迎图片的实现方法

    wxpython 最小化到托盘与欢迎图片的实现方法

    这篇文章主要分享一个python实例代码,使用wxpython实现最小化到托盘与欢迎图片,需要的朋友可以参考下
    2014-06-06
  • python 根据pid杀死相应进程的方法

    python 根据pid杀死相应进程的方法

    下面小编就为大家带来一篇python 根据pid杀死相应进程的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • Python实现批量将MP3音频转为WAV格式详解

    Python实现批量将MP3音频转为WAV格式详解

    这篇文章主要介绍了通过Python实现将MP3音频转为WAV格式的方法,文中的示例代码讲解详细,对我们学习Python有一定帮助,感兴趣的可以了解一下
    2021-12-12
  • Python使用Socket(Https)Post登录百度的实现代码

    Python使用Socket(Https)Post登录百度的实现代码

    以前都是用一些高级模块,封装的比较好,今天尝试使用socket模块登录百度,弄了半天才弄好,主要由于百度在登陆页使用了https,我们需要对socket进行一定处理
    2012-05-05
  • 关于python -m http.server的一些安全问题详解

    关于python -m http.server的一些安全问题详解

    这篇文章主要介绍了关于python -m http.server的一些安全问题的相关资料,文章还列举了该模块已知的安全漏洞,并建议在生产环境中使用专业的服务器替代,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2026-01-01
  • Python实现删除windows下的长路径文件

    Python实现删除windows下的长路径文件

    这篇文章主要为大家详细介绍一下如何利用Python语言实现删除windows下的长路径文件功能,文中的示例代码讲解详细,具有一定参考借鉴价值,感兴趣的可以了解一下
    2022-07-07

最新评论