pytest 结合 Playwright 实现页面元素在两个区域间拖拽
-
-
- [✅ 示例场景](#✅ 示例场景)
- [1. 创建示例 HTML 页面 (`drag_drop_block.html`)](#1. 创建示例 HTML 页面 (
drag_drop_block.html)) - [2. Pytest + Playwright 测试代码 (`test_block_drag_drop.py`)](#2. Pytest + Playwright 测试代码 (
test_block_drag_drop.py)) - [3. 运行测试](#3. 运行测试)
- 关联文档
-
好的,下面是使用 pytest 结合 Playwright 实现页面元素在两个区域间拖拽的完整示例。
这个示例将创建一个包含两个区域(source 和 target)和一个可拖拽区块的 HTML 页面,然后使用 Playwright 模拟将该区块从一个区域拖拽到另一个区域的操作。
✅ 示例场景
我们将创建一个简单的 HTML 页面,包含:
- Source Area : 包含一个可拖拽的区块(例如一个带颜色的
<div>)。 - Target Area: 一个空的区域,用于接收被拖拽的区块。
- JavaScript : 实现 HTML5 拖放 API,处理
dragstart,dragover, 和drop事件,以便将元素从source移动到target。
1. 创建示例 HTML 页面 (drag_drop_block.html)
将以下 HTML 代码保存为 drag_drop_block.html 文件。
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)
首先,确保安装了必要的库:
bash
pip install pytest playwright
playwright install
然后,将以下 Python 代码保存为 test_block_drag_drop.py。
python
# 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.py 和 drag_drop_block.html 的目录,然后运行:
bash
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)都成功通过了。
关联文档
【Python知识】使用 Playwright for Python 从零开始搭建自动化测试方案