操作交互
简介
Playwright 可以与 HTML 输入元素进行交互,包括文本输入框、复选框、单选按钮、选择选项、鼠标点击、字符输入、按键和快捷键操作,以及文件上传和元素聚焦等功能。
文本输入
使用 locator.fill() 是填充表单字段最简单的方式。该方法会聚焦元素并触发带有输入文本的 input
事件。适用于 <input>
、<textarea>
和 [contenteditable]
元素。
// 文本输入
await page.getByRole('textbox').fill('Peter');
// 日期输入
await page.getByLabel('Birth date').fill('2020-02-02');
// 时间输入
await page.getByLabel('Appointment time').fill('13:15');
// 本地日期时间输入
await page.getByLabel('Local time').fill('2020-03-02T05:15');
复选框和单选按钮
使用 locator.setChecked() 是勾选或取消勾选复选框和单选按钮最简单的方式。此方法适用于 input[type=checkbox]
、input[type=radio]
和 [role=checkbox]
元素。
// 勾选复选框
await page.getByLabel('I agree to the terms above').check();
// 验证选中状态
expect(page.getByLabel('Subscribe to newsletter')).toBeChecked();
// 选择单选按钮
await page.getByLabel('XL').check();
选择选项
使用 locator.selectOption() 在 <select>
元素中选择一个或多个选项。可以通过指定选项的 value
或 label
进行选择。支持多选操作。
// 通过值或标签匹配进行单选
await page.getByLabel('选择颜色').selectOption('blue');
// 通过标签匹配进行单选
await page.getByLabel('选择颜色').selectOption({ label: '蓝色' });
// 多选操作
await page.getByLabel('选择多个颜色').selectOption(['red', 'green', 'blue']);
鼠标点击
模拟真实用户的点击行为。
// 普通点击
await page.getByRole('button').click();
// 双击
await page.getByText('项目').dblclick();
// 右键点击
await page.getByText('项目').click({ button: 'right' });
// Shift + 点击
await page.getByText('项目').click({ modifiers: ['Shift'] });
// Windows/Linux系统下 Ctrl + 点击
// macOS系统下 Meta + 点击
await page.getByText('项目').click({ modifiers: ['ControlOrMeta'] });
// 鼠标悬停
await page.getByText('项目').hover();
// 点击左上角位置
await page.getByText('项目').click({ position: { x: 0, y: 0 } });
底层实现中,该方法及其他指针相关操作会:
- 等待指定选择器的元素出现在DOM中
- 等待元素变为可见状态(非空、无
display:none
、无visibility:hidden
) - 等待元素停止移动(例如CSS过渡动画完成)
- 将元素滚动至可视区域
- 等待元素在操作点可接收指针事件(例如等待元素不再被其他元素遮挡)
- 在上述任何检查过程中如果元素被移除,会自动重试操作
强制点击
有时,应用程序会使用复杂的逻辑,当悬停在元素上时会用另一个拦截点击的元素覆盖它。这种行为与元素被覆盖导致点击被分发到其他位置的 bug 难以区分。如果你确认这种情况正在发生,可以绕过可操作性检查并强制点击:
await page.getByRole('button').click({ force: true });
编程式点击
如果你不关心在真实条件下测试应用程序,而是想通过任何可能的方式模拟点击,可以通过locator.dispatchEvent()直接在元素上触发HTMLElement.click()
行为:
await page.getByRole('button').dispatchEvent('click');
逐个字符输入
大多数情况下,你应该使用locator.fill()来输入文本。请参阅上文的文本输入部分。只有当页面有特殊键盘处理时,才需要逐个字符输入。
使用locator.pressSequentially()逐个字符输入到字段中,就像用户使用真实键盘一样。
// 逐个按键输入
await page.locator('#area').pressSequentially('Hello World!');
此方法会触发所有必要的键盘事件,包括所有keydown
、keyup
、keypress
事件。你甚至可以指定按键之间的可选delay
来模拟真实用户行为。
按键与快捷键
// 按下 Enter 键
await page.getByText('Submit').press('Enter');
// 发送 Control+右箭头组合键
await page.getByRole('textbox').press('Control+ArrowRight');
// 在键盘上按下 $ 符号键
await page.getByRole('textbox').press('$');
locator.press() 方法会使选定元素获得焦点并触发单个按键事件。它接受键盘事件 keyboardEvent.key 属性中定义的逻辑键名:
Backquote, Minus, Equal, Backslash, Backspace, Tab, Delete, Escape,
ArrowDown, End, Enter, Home, Insert, PageDown, PageUp, ArrowRight,
ArrowUp, F1 - F12, Digit0 - Digit9, KeyA - KeyZ, 等
- 也可以直接指定要触发的单个字符,如
"a"
或"#"
- 支持以下修饰键组合:
Shift, Control, Alt, Meta
简单版本触发单个字符。字符区分大小写,因此 "a"
和 "A"
会产生不同结果。
// <input id=name>
await page.locator('#name').press('Shift+A');
// <input id=name>
await page.locator('#name').press('Shift+ArrowLeft');
同时也支持如 "Control+o"
或 "Control+Shift+T"
这样的组合快捷键。当指定修饰键时,修饰键会被按下并保持,同时触发后续按键。
请注意在 Shift-A
中仍需指定大写 A
才能生成大写字符。Shift-a
会产生小写字符,就像启用了 CapsLock
一样。
文件上传
您可以使用 locator.setInputFiles() 方法选择要上传的输入文件。该方法期望第一个参数指向一个类型为 "file"
的 input 元素。可以通过数组传递多个文件。如果某些文件路径是相对路径,它们将相对于当前工作目录进行解析。空数组会清除已选择的文件。
// 选择单个文件
await page.getByLabel('Upload file').setInputFiles(path.join(__dirname, 'myfile.pdf'));
// 选择多个文件
await page.getByLabel('Upload files').setInputFiles([
path.join(__dirname, 'file1.txt'),
path.join(__dirname, 'file2.txt'),
]);
// 选择整个目录
await page.getByLabel('Upload directory').setInputFiles(path.join(__dirname, 'mydir'));
// 移除所有已选文件
await page.getByLabel('Upload file').setInputFiles([]);
// 从内存上传缓冲区
await page.getByLabel('Upload file').setInputFiles({
name: 'file.txt',
mimeType: 'text/plain',
buffer: Buffer.from('this is test')
});
如果您没有现成的 input 元素(它是动态创建的),可以处理 page.on('filechooser') 事件,或者在操作时使用相应的等待方法:
// 在点击前开始等待文件选择器。注意不需要 await。
const fileChooserPromise = page.waitForEvent('filechooser');
await page.getByLabel('Upload file').click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(path.join(__dirname, 'myfile.pdf'));
元素聚焦
对于处理焦点事件的动态页面,您可以使用 locator.focus() 方法将焦点设置到指定元素。
await page.getByLabel('Password').focus();
拖放操作
您可以使用 locator.dragTo() 方法执行拖放操作。该方法会:
- 悬停在待拖动元素上
- 按下鼠标左键
- 移动鼠标到接收放置的元素
- 释放鼠标左键
await page.locator('#item-to-be-dragged').dragTo(page.locator('#item-to-drop-at'));
手动拖放
如果需要更精确地控制拖放操作,可以使用底层方法如 locator.hover()、mouse.down()、mouse.move() 和 mouse.up()。
await page.locator('#item-to-be-dragged').hover();
await page.mouse.down();
await page.locator('#item-to-drop-at').hover();
await page.mouse.up();
如果您的页面依赖 dragover
事件触发,在所有浏览器中都需要至少两次鼠标移动才能触发该事件。为了可靠地执行第二次鼠标移动,请重复调用 mouse.move() 或 locator.hover() 两次。操作顺序应为:悬停拖动元素 → 鼠标按下 → 悬停放置元素 → 再次悬停放置元素 → 鼠标释放。
滚动操作
大多数情况下,Playwright 在执行任何操作前会自动进行滚动。因此,您通常不需要显式地执行滚动操作。
// 自动滚动使按钮可见
await page.getByRole('button').click();
但在极少数情况下,您可能需要手动滚动。例如,您可能希望强制"无限列表"加载更多元素,或者为特定截图定位页面位置。这种情况下,最可靠的方法是找到一个您希望显示在底部的元素,并将其滚动到视图中。
// 将页脚滚动到视图中,强制"无限列表"加载更多内容
await page.getByText('Footer text').scrollIntoViewIfNeeded();
如果您需要更精确地控制滚动,可以使用 mouse.wheel() 或 locator.evaluate():
// 定位鼠标并用滚轮滚动
await page.getByTestId('scrolling-container').hover();
await page.mouse.wheel(0, 10);
// 或者以编程方式滚动特定元素
await page.getByTestId('scrolling-container').evaluate(e => e.scrollTop += 100);