Playwright 操作(Actions)
Playwright 操作(Actions) 本章介绍 Playwright 中与导航和点击相关的操作,以及操作背后的自动等待机制。 什么是 Action Action(操作)是 Playwright 中用来模拟用户行为的方法,比如点击按钮、输入文字、选择下拉框。 每个 Action 在执行之前,Playwright 会自动等待目标元素变为可操作状态。 Actionability 可操作性检查 在执行每个操作前,Playwrig..
教程详情页默认 noindex。
Playwright 操作(Actions)
本章介绍 Playwright 中与导航和点击相关的操作,以及操作背后的自动等待机制。
什么是 Action
Action(操作)是 Playwright 中用来模拟用户行为的方法,比如点击按钮、输入文字、选择下拉框。
每个 Action 在执行之前,Playwright 会自动等待目标元素变为可操作状态。
Actionability 可操作性检查
在执行每个操作前,Playwright 会对目标元素进行一系列检查:
| 步骤 | 检查条件 | 说明 |
|---|---|---|
| 1. Attached | 元素已挂载到 DOM 树 | 元素存在于页面中 |
| 2. Visible | 元素可见(非 display:none 或 visibility:hidden) | 用户能看见元素 |
| 3. Stable | 元素位置稳定(动画结束) | 避免动画期间的误操作 |
| 4. Receives Events | 元素没有被其他元素遮挡 | 确保点击能被元素接收 |
| 5. Enabled | 元素未被禁用(非 disabled) | 用户可以与之交互 |
任意一步未通过,Playwright 都会等待并重试,直到超时。
这 5 步检查是 Playwright 自动完成的,你不需要写任何额外的等待代码。这也是 Playwright 能够大幅减少不稳定测试的根本原因。
强制操作 { force: true }
有时你需要绕过可操作性检查,例如点击一个被遮罩隐藏的按钮或测试不可见元素的状态。
实例
await page.getByRole('button', { name: '提交' }).click({ force: true });
force: true是测试真实用户做不到的操作,绝大多数情况下不应使用。如果你的测试需要force: true,先检查是否有交互设计问题。
导航操作
page.goto() 页面跳转
实例
await page.goto('https://www.runoob.com/');
// 等待 DOMContentLoaded(页面结构加载完成)
await page.goto('https://www.runoob.com/', { waitUntil: 'domcontentloaded' });
// 等待网络空闲(所有异步数据加载完毕)
await page.goto('https://www.runoob.com/', {
waitUntil: 'networkidle', // 网络空闲
timeout: 60000, // 超时 60 秒
referer: 'https://google.com', // Referer 来源
});
page.goBack() / goForward() / reload()
实例
await page.goBack();
// 前进一页
await page.goForward();
// 刷新页面
await page.reload();
点击操作
locator.click() 单击
实例
await page.getByRole('button', { name: '登录' }).click();
// 带选项的点击
await page.getByRole('button', { name: '登录' }).click({
button: 'left', // 鼠标按键:'left' | 'right' | 'middle'
clickCount: 1, // 点击次数
delay: 100, // 按下与松开之间的延迟(毫秒)
timeout: 5000, // 超时时间
modifiers: [], // 修饰键:['Alt', 'Control', 'Meta', 'Shift']
});
locator.dblclick() 双击
实例
await page.getByText('双击编辑').dblclick();
右键点击
实例
await page.getByText('右键菜单').click({ button: 'right' });
组合键点击
实例
await page.getByRole('option').click({ modifiers: ['Control'] });
// Shift + 点击(范围选择 / 新窗口打开)
await page.getByRole('link').click({ modifiers: ['Shift'] });
坐标点击
实例
await page.locator('canvas').click({
position: { x: 100, y: 50 },
});
locator.hover() 悬停
实例
await page.getByRole('link', { name: '教程' }).hover();
// 悬停后点击弹出的子菜单
await page.getByRole('link', { name: 'Playwright 教程' }).click();
点击 vs hover 的场景差异
| 操作 | 适用场景 |
|---|---|
click() | 按钮点击、链接跳转、复选框切换 |
dblclick() | 编辑模式进入、双击选择文本 |
click({ button: 'right' }) | 打开浏览器原生上下文菜单 |
hover() | 触发悬停菜单、Tooltip 提示 |
click({ position }) | Canvas 操作、自定义图形交互 |
文本输入操作
locator.fill() 清空后填入
fill() 会先清空输入框,再填入新值,是输入操作中最常用和最推荐的方法。
实例
await page.getByLabel('用户名').fill('runoob_user');
await page.getByLabel('密码').fill('secure_password');
await page.getByPlaceholder('搜索RUNOOB教程').fill('Playwright');
locator.type() 逐字输入
type() 会逐个字符输入,模拟真实的键盘敲击,每次按键之间可设置间隔。
实例
await page.getByLabel('搜索').type('Playwright', { delay: 100 });
fill vs type 的区别
| 方法 | 行为 | 适用场景 |
|---|---|---|
fill() | 清空后一次性填入 | 绝大多数场景(推荐) |
type() | 逐字符输入,触发每次按键事件 | 需要触发输入联想、实时搜索 |
大部分情况下使用
fill()即可。只有当输入框有实时搜索(keydown/keyup 事件)时,才需要使用type()。
locator.clear() 清空
实例
await page.getByLabel('用户名').clear();
选择操作
locator.check() / uncheck() 复选框
实例
await page.getByLabel('我同意服务协议').check();
// 取消勾选
await page.getByLabel('接收邮件通知').uncheck();
// 设置勾选状态
await page.getByLabel('记住我').setChecked(true); // 确认为勾选
await page.getByLabel('记住我').setChecked(false); // 确认为未勾选
locator.selectOption() 下拉框选择
实例
await page.getByLabel('城市').selectOption('beijing');
// 通过显示文本选择
await page.getByLabel('城市').selectOption({ label: '上海' });
// 多选下拉框
await page.getByLabel('兴趣标签').selectOption([
{ label: '编程' },
{ value: 'design' },
]);
// 通过索引选择(第 0 项、第 2 项)
await page.getByLabel('城市').selectOption({ index: 0 });
键盘操作
page.keyboard.press() 按键
实例
await page.keyboard.press('Enter');
// 全选(Control + A)
await page.keyboard.press('Control+A');
// 复制粘贴
await page.keyboard.press('Control+C');
await page.keyboard.press('Control+V');
// 撤销
await page.keyboard.press('Control+Z');
page.keyboard.down() / up() 按下/松开
实例
await page.keyboard.down('Shift');
// 在此期间按其他键(相当于按住 Shift 再按其他键)
await page.keyboard.press('ArrowRight'); // Shift + 右箭头(选中文本)
await page.keyboard.press('ArrowRight');
// 松开 Shift
await page.keyboard.up('Shift');
特殊键名称
| 键名 | 对应按键 |
|---|---|
Enter | 回车 |
Escape | Esc 退出 |
Tab | Tab 切换焦点 |
Backspace | 退格删除 |
Delete | 删除键 |
ArrowUp/Down/Left/Right | 方向键 |
PageUp/PageDown | 翻页键 |
Home/End | 行首/行尾 |
Control/Alt/Shift/Meta | 修饰键 |
page.keyboard.type() 键盘输入文本
实例
await page.keyboard.type('RUNOOB 教程', { delay: 50 });
page.keyboard.insertText() 直接插入
实例
await page.keyboard.insertText('快速插入的文本');
鼠标操作
坐标点击与移动
实例
await page.mouse.click(100, 200);
// 在坐标处双击
await page.mouse.dblclick(100, 200);
// 移动鼠标到坐标
await page.mouse.move(100, 200);
// 按下 / 松开
await page.mouse.down();
await page.mouse.up();
鼠标滚轮
实例
await page.mouse.wheel(0, 500);
// 向左滚动 200px
await page.mouse.wheel(200, 0);
locator.dragTo() 拖拽
实例
const source = page.getByText('拖拽我');
const target = page.getByTestId('drop-zone');
await source.dragTo(target);
// 带选项的拖拽
await source.dragTo(target, {
sourcePosition: { x: 0, y: 0 }, // 拖拽起始点(相对于源元素)
targetPosition: { x: 10, y: 10 }, // 拖拽目标点(相对于目标元素)
});
page.mouse vs locator.click 的选择
| 方式 | 适用场景 |
|---|---|
locator.click() | 点击页面元素(绝大多数场景) |
page.mouse.click() | 点击特定坐标(Canvas、图表等无 DOM 的场景) |
文件上传
locator.setInputFiles() 单文件/多文件
实例
await page
.getByLabel('选择文件')
.setInputFiles('path/to/document.pdf');
// 上传多个文件
await page
.getByLabel('选择文件')
.setInputFiles([
'path/to/file1.png',
'path/to/file2.png',
]);
// 清除已选文件
await page.getByLabel('选择文件').setInputFiles([]);
文件路径是相对于当前工作目录(项目根目录)的路径。
文件下载
实例
const downloadPromise = page.waitForEvent('download');
// 执行触发下载的操作
await page.getByRole('button', { name: '下载报告' }).click();
// 等待下载完成
const download = await downloadPromise;
// 获取建议的文件名
console.log(download.suggestedFilename());
// 保存到指定路径
await download.saveAs('downloads/report.pdf');