第 10 章 · 表单与复杂组件交互
本章目标:掌握下拉框、复选框、文件上传、原生对话框等复杂控件的自动化方法,分清
fill与press_sequentially的适用边界,学会处理日期控件这类"难点专业户"。
10.1 下拉与选择类控件
原生 <select> 用 select_option 处理,支持按 value、label 或 index 定位,也支持一次多选:
# 单选:值或 label 匹配即可
page.get_by_label("Choose a color").select_option("blue")
page.get_by_label("Choose a color").select_option(label="Blue")
# 多选:传入列表
page.get_by_label("Choose multiple colors").select_option(["red", "green", "blue"])
# 按 index 选择
page.get_by_label("Choose a color").select_option(index=2)复选框和单选钮统一用 check() / uncheck() / set_checked(bool)——set_checked 同时适用于 input[type=checkbox]、input[type=radio] 和 [role=checkbox]:
page.get_by_label("I agree to the terms above").check()
expect(page.get_by_label("Subscribe to newsletter")).to_be_checked()
page.get_by_label("XL").set_checked(True) # 幂等:已是该状态则不动作10.2 文件上传的三种姿势
方式一(最常用):直接对 <input type="file"> 设置文件,无需打开系统对话框:
# 单文件
page.get_by_label("Upload file").set_input_files("myfile.pdf")
# 多文件
page.get_by_label("Upload files").set_input_files(["file1.txt", "file2.txt"])
# 上传整个目录
page.get_by_label("Upload directory").set_input_files("mydir")
# 清空已选文件
page.get_by_label("Upload file").set_input_files([])方式二:从内存上传 Buffer,测试"生成报告再上传"这类流程时特别有用:
page.get_by_label("Upload file").set_input_files(
files=[{"name": "test.txt", "mimeType": "text/plain", "buffer": b"hello e2e"}]
)方式三:input 是点击后才动态创建的,用 expect_file_chooser 接住系统文件选择器事件:
with page.expect_file_chooser() as fc_info:
page.get_by_text("选择文件").click()
fc_info.value.set_files("myfile.pdf") # 同样可以传列表/Buffer10.3 原生对话框:alert / confirm / prompt
Playwright 默认自动 dismiss 所有对话框。要接受输入或点确定,必须在触发动作之前注册 dialog 处理器:
# 自动接受所有 confirm/alert
page.on("dialog", lambda dialog: dialog.accept())
# prompt 需要带输入文本;也可以按类型分别处理
def handle(dialog):
if dialog.type == "prompt":
dialog.accept("张三")
else:
dialog.accept()
page.on("dialog", handle)
page.get_by_role("button", name="删除").click()监听器必须真正处理对话框
官方文档特别警告:page.on("dialog") 的回调如果只 print 不 accept/dismiss,后续动作会永久卡死——Web 对话框是模态的,会阻塞页面执行。只读 dialog.message 而不处理是最常见的翻车点。
10.4 fill、press_sequentially 与键盘操作
fill 直接设置值并触发 input 事件(清空原有内容),适用于 <input>、<textarea> 和 [contenteditable],是表单填充的默认选择。需要逐键模拟真实敲击时(依赖 keydown 计数的自动补全、掩码输入框),用 press_sequentially——它是旧 type() 方法的继任者:
page.get_by_role("textbox").fill("Peter") # 一次性填入
page.locator("#area").press_sequentially("Hello!", delay=50) # 逐键输入
# 组合键与功能键
page.get_by_text("Submit").press("Enter")
page.get_by_role("textbox").press("Control+ArrowRight")
# 悬停与拖拽
page.get_by_text("Item").hover()
source.drag_to(page.get_by_role("listitem", name="done")) # locator.drag_tofill 与 press_sequentially 的本质区别:前者是"设值",后者是完整的键盘事件序列(keydown/keypress/keyup 全套)。给普通输入框逐键打字只会白白拖慢测试。
10.5 日期控件的处理策略
日期控件分两类,策略完全不同:
- 原生
<input type="date">:直接fill标准格式字符串即可,不要去点日历面板:pythonpage.get_by_label("Birth date").fill("2020-02-02") page.get_by_label("Appointment time").fill("13:15") # 时间 page.get_by_label("Local time").fill("2020-03-02T05:15") # 本地日期时间 - JS 日历组件(Element UI/AntD 等):优先看它是否支持在 input 里键入(可聚焦后
fill);必须点选时,定位到年月切换按钮循环点击到目标月份,再get_by_role("cell", name=...)点具体日期。切忌用坐标硬算位置——分辨率一变就碎。
本章小结
<select>用select_option(value/label/index),多选传列表;- 文件上传首选
set_input_files(含目录、Buffer、清空三种形态),动态创建的 input 用expect_file_chooser; - 对话框默认自动 dismiss;自定义监听器必须 accept/dismiss 收尾否则卡死;
fill是设值、press_sequentially才是逐键模拟(替代已弃用的type);- 日期控件:原生直接 fill,组件化日历走定位点击。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. locator.type() 方法在当前版本中的状态是?
2. 没有注册 page.on("dialog") 监听器时,页面弹出 alert 会怎样?
3. 关于 dialog 监听器的正确写法是?
4. 测试"生成内容后上传"的场景,最方便的上传方式是?
🛠️ 动手实践
- 编写测试:一个 multi-select 下拉中选中三个城市并断言提交后的请求体(结合第 8 章的 expect_response)。
- 用 Buffer 方式构造一个 3MB 的假 CSV 上传,断言服务端返回的解析行数。
- 为一个带 confirm 的删除按钮写测试:分别验证"确认删除"与"取消删除"两种路径的界面结果。