pytest 结合 Playwright 实现页面元素在两个区域间拖拽
- ✅ 示例场景
- 1. 创建示例 HTML 页面 (`drag_drop_block.html`)
- 2. Pytest + Playwright 测试代码 (`test_block_drag_drop.py`)
- 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文件。
<!DOCTYPEhtml><htmllang="en"><head><metacharset="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><divid="source-area"class="area"><h3>Source Area</h3><divid="draggable-block"class="draggable-block"draggable="true"><spanclass="block-content">Drag Me!</span></div></div><divid="target-area"class="area"><h3>Target Area</h3><pclass="success-indicator">Block dropped successfully!</p></div><script>constdraggableBlock=document.getElementById('draggable-block');constsourceArea=document.getElementById('source-area');consttargetArea=document.getElementById('target-area');draggableBlock.addEventListener('dragstart',function(event){event.dataTransfer.setData("text/plain","block-id");// Optional: Set data// Add a class for visual feedbackthis.classList.add('dragging');});draggableBlock.addEventListener('dragend',function(event){// Remove visual feedbackthis.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 sourcesourceArea.removeChild(draggableBlock);targetArea.appendChild(draggableBlock);// Optional: Add success class to target areatargetArea.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 sourcetargetArea.removeChild(draggableBlock);sourceArea.appendChild(draggableBlock);// Remove success class from target areatargetArea.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)
首先,确保安装了必要的库:
pipinstallpytest playwright playwrightinstall然后,将以下 Python 代码保存为test_block_drag_drop.py。
# test_block_drag_drop.pyimportpytestfromplaywright.sync_apiimportPage,expect# Pytest fixture to provide a fresh browser page for each test@pytest.fixture(scope="function")defpage(context):"""Creates a new page for each test function."""page=context.new_page()yieldpage page.close()deftest_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 filepage.goto("file:///absolute/path/to/your/drag_drop_block.html")# 2. Define selectors for the draggable block and the target areasource_area_selector="#source-area"target_area_selector="#target-area"draggable_block_selector="#draggable-block"# 3. Verify initial state: block is in source areaexpect(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 operationpage.drag_and_drop(draggable_block_selector,target_area_selector)# 5. Verify final state: block is in target areaexpect(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.")deftest_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 filepage.goto("file:///absolute/path/to/your/drag_drop_block.html")# 2. Define selectorssource_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 checkexpect(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 firstpage.drag_and_drop(draggable_block_selector,target_area_selector)# Confirm it's in targetexpect(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 sourcepage.drag_and_drop(draggable_block_selector,source_area_selector)# 5. Verify final state: block is back in source areaexpect(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的目录,然后运行:
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 从零开始搭建自动化测试方案
【自动化测试】Playwright for Python 与 Allure 集成完整指南
【自动化测试】pytest 语法与核心概念