基本操作:点击/填写/清空
本教程共 59 篇 · 第 17 篇 · 更新于 2026-08-04 · 约 9 分钟阅读
本节目标:能用
click、fill、clear稳定地操作页面元素,并说清 Playwright 在动手之前到底替你等了什么。
前面几章我们一直在找元素。找到之后总得干点什么。
点击按钮、往输入框里塞文字、把旧内容清掉——这三件事占了日常测试代码的一大半。Playwright 把它们做得特别省心,省心到很多人不知道底下发生了什么。这章就把这层窗户纸捅破。
点击:click 的基本用法
最简单的形式,拿到 Locator(定位器)直接点:
await page.getByRole('button', { name: '提交' }).click();
注意 await。Playwright 的所有操作都是异步的,漏了 await 会让后面的断言跑在动作前面,测试就会莫名其妙地闪。
常见的点击变体都是同一个方法加参数:
// 双击
await page.getByText('文件夹').dblclick();
// 右键点击,弹出上下文菜单
await page.getByText('文件夹').click({ button: 'right' });
// Shift + 点击,多选场景常用
await page.getByText('第 3 项').click({ modifiers: ['Shift'] });
// Windows/Linux 上是 Ctrl,macOS 上是 Command
await page.getByText('第 3 项').click({ modifiers: ['ControlOrMeta'] });
// 点元素的左上角,而不是默认的中心点
await page.getByText('色块').click({ position: { x: 0, y: 0 } });
ControlOrMeta 这个值挺贴心。你不用自己判断操作系统,Playwright 会按平台选 Ctrl 还是 Command。
点击之前,Playwright 替你做了什么
这是 Playwright 和老一代工具最大的区别。调用 click() 之后,它不会立刻派发点击事件,而是先跑一轮可操作性检查(Actionability Checks):
- 等定位器恰好解析到一个元素(命中多个会直接报严格模式错误)。
- 等元素可见(Visible):有非空的包围盒,且计算样式不是
visibility: hidden。 - 等元素稳定(Stable):连续两帧包围盒不变,也就是动画播完了。
- 把元素滚动进视口。
- 等元素能接收指针事件(Receives Events):点击落点上没有别的元素挡着。
- 等元素处于启用状态(Enabled):没有
disabled属性。
任何一步在超时前没通过,就抛 TimeoutError。中途元素被从 DOM 里摘掉了也不怕,它会重新找一遍再来。
Note「可见」的判定有三条容易记错的细则:尺寸为 0 的元素不可见;
display: none不可见;但opacity: 0算可见。透明不等于不存在,这一条坑过不少人。
所以你几乎不用写 sleep。按钮从禁用变成可用、遮罩层淡出、列表加载完重排——这些等待 Playwright 都包了。
强制点击与程序化点击
有时候页面逻辑很刁钻。比如鼠标一悬停就浮出一层提示,正好盖住按钮。这种情况下「被遮挡」和真 bug 长得一模一样,Playwright 只能报错。
你确认这是设计如此,可以跳过非必要检查:
await page.getByRole('button').click({ force: true });
force: true 会关掉「接收事件」这类检查,直接在坐标上派发点击。
还有更极端的做法——完全不模拟真实鼠标,直接在元素上派发一个 click 事件:
await page.getByRole('button').dispatchEvent('click');
这等价于在控制台里调 HTMLElement.click()。它不走渲染层,遮挡、滚动、可见性一概不管。
Warning
force和dispatchEvent是逃生舱,不是常规武器。它们会把真实用户点不到的按钮点出来,测试通过了,线上还是坏的。用之前先问一句:真实用户能点到吗?
填写文本:fill
往输入框写内容,首选 fill():
// 普通文本框
await page.getByRole('textbox').fill('张三');
// 日期输入
await page.getByLabel('出生日期').fill('2020-02-02');
// 时间输入
await page.getByLabel('预约时间').fill('13:15');
// 本地日期时间
await page.getByLabel('本地时间').fill('2020-03-02T05:15');
fill() 做三件事:聚焦元素、把值整体设进去、触发一次 input 事件。它支持 <input>、<textarea> 和带 contenteditable 的元素。
它的可操作性检查比 click 松一些:只要求可见、启用、可编辑,不要求稳定和接收事件。毕竟填字不靠鼠标坐标。
日期和时间控件要按 HTML 标准格式给值,也就是上面那几种写法。别按界面上显示的「2020年2月2日」去填,那是渲染结果,不是元素的 value。
什么时候需要逐字输入
fill() 是「整体赋值」,不会产生 keydown、keyup 这些逐键事件。绝大多数页面无所谓,但有些组件会盯着按键做事——比如搜索框的实时联想、限制输入字符的掩码控件。
这时候用 pressSequentially(),一个字符一个字符敲:
await page.locator('#area').pressSequentially('Hello World!');
// 带延迟,更像真人打字
await page.locator('#area').pressSequentially('Hello', { delay: 100 });
它会完整发出 keydown / keypress / keyup 序列。
Tip我的经验是:默认永远用
fill。只有当测试失败,且怀疑是「页面没收到按键事件」时,才换成pressSequentially。反过来做会让整套测试慢一大截。
清空输入框:clear
清空有个专门的方法:
await page.getByRole('textbox').clear();
它等价于 fill(''),但语义更直白,可操作性检查也和 fill 一致。
新手常见的错法是模拟全选加删除:先 press('Control+a') 再 press('Backspace')。能用,但在不同平台、不同组件上行为不一致,属于自找麻烦。有 clear() 就用 clear()。
一段完整的小例子
把这章的东西串起来看:
import { test, expect } from '@playwright/test';
test('修改昵称', async ({ page }) => {
await page.goto('https://example.com/profile');
const nickname = page.getByLabel('昵称');
// 1. 清掉旧值
await nickname.clear();
// 2. 填新值
await nickname.fill('码上学');
// 3. 点保存
await page.getByRole('button', { name: '保存' }).click();
// 4. 断言结果(断言也会自动重试,第 22 章细讲)
await expect(page.getByText('保存成功')).toBeVisible();
});
整段代码里没有一行 sleep。这不是我省略了,是真的不需要。
小结与选择建议
- 点击就用
click(),双击、右键、修饰键都是它的参数,不用换 API。 - 点击前的六步检查是 Playwright 稳定性的来源,别急着用
force绕过去。 - 填文本默认
fill(),页面依赖按键事件时才换pressSequentially()。 - 清空用
clear(),别手搓全选加退格。 - 遇到「点不上」的报错,先看是不是真被遮挡了,再考虑逃生舱。
下一章预告:表单操作选择/勾选/聚焦/悬停——把表单里另一半活儿干完,顺带对比各操作的检查项差异。