Playwright
1. 框架介绍
Playwright Test 是一个适用于现代 Web 应用的端到端测试框架。它打包了测试运行器、断言、隔离、并行化和丰富的工具。
2. 安装playwright
cmd
npm init playwright@latest
3. 运行示例测试
安装浏览器(不用电脑上的浏览器)
npx playwright install
也可以只下载一个浏览器
cmd
npx playwright install chromium # Expecting one of: chromium, chromium-headless-shell, chromium-tip-of-tree-headless-shell, chrome, chrome-beta, msedge, msedge-beta, msedge-dev, bidi-chromium, firefox, webkit, webkit-wsl
运行测试
cmd
npx playwright test
运行项目中的命令,可以自动安装浏览器

4. 页面等待
tsx
test("页面等待测试", async ({ page }) => {
await page.goto("https://www.baidu.com/");
await page.waitForTimeout(5000);
await expect(page).toHaveTitle(/百度一下/);
});
5. 自动化代码助手
cmd
npx playwright codegen

代码助手可以完成一些功能(人对页面的输入操作),但是对于一些断言什么需要自己写
代码助手产生代码时,能使用role定位的,会优先使用后role定位
6. 跟踪功能
playwright有个特色功能: 跟踪
启用跟踪功能后,可以在执行自动化后,通过记录的跟踪数据文件,回看自动化过程中的每个细节。
tsx
export default defineConfig({
use: {
// 策略选项:
// - "on" → 始终生成
// - "off" → 禁用
// - "on-first-retry" → 首次重试失败时生成(默认)
// - "retain-on-failure" → 失败时保留(即使后续重试成功)
trace: "on-first-retry", // 推荐生产环境配置
},
});

执行完毕后,我们可以看到trace.zip文件
怎么查看这个跟踪文件呢?
-
执行命令
cmdnpx playwright show-trace test-results/example--chromium/trace.zip
7. css定位
7.1 验证css选择器

7.2 元素匹配
-
如果一个locator匹配多个元素,要获取所有元素的locator对象,如下:
tsxtest("匹配多个元素", async ({ page }) => { await page.goto("https://www.baidu.com/"); const lcs = await page.locator("div").all(); console.log(lcs); console.log("--------------------------------------"); const fist = await page.locator("div").first(); console.log(await fist.innerHTML()); console.log("--------------------------------------"); const last = await page.locator("div").last(); console.log(await last.innerHTML()); console.log("--------------------------------------"); const nth = await page.locator("div").nth(1); console.log(await nth.innerHTML()); await expect(page).toHaveTitle(/百度一下/); });
如果只需要数量的话,我们可以使用
tsx
const lcs = await page.locator("div").count();
如果我们只需要第一个或者最后一个元素,可以使用
tsx
const first = await page.locator("div").first();
const last = await page.locator("div").last();
- 选择直接子元素和后代元素
直接子元素
tsx
const span = await page.locator("div>span").first();

后代元素
tsx
const span = await page.locator("div span").first();

-
属性选择器
tsxconst a = await page.locator('[href="http://news.baidu.com"]');
如果属性包含某个字符串的元素
tsx
const a1 = await page.locator('[href*="http"]').first();
属性值以http开头的
ts
const a2 = await page.locator('[href^="http"]').first();
以什么结尾
tsx
const a3 = await page.locator('[href$="top"]').first();
-
联合使用
tsconst a4 = await page .locator('div#head a[href="http://news.baidu.com"]') .first();如果是或的关系,用,隔开
-
按次序选择子结点
tsx
const a5 = await page.locator("div#s-top-left a:nth-child(2)").first();

奇数结点
tsx
const even = await page.locator("a:nth-child(even)").first();
-
兄弟结点
相邻兄弟结点的选择
h3 + sapn
后续所有兄弟结点的选择
h3 ~ span
8. xptah选择
playwright推荐的是xpath定位,它推荐从用户角度视觉呈现的定位。
因为它觉得用户的角度相对固定,不容易便,而html页面写法容易变化
8.1 根据文本内容定位
tsx
const element1 = await page.getByText("百度一下").first();
8.2 根据role进行定位
- 根据元素的角色定位

tsx
const element2 = await page.getByRole("alert");
html元素中,有些特定语义元素被API规范认为自身就包含ARIA role信息,并不需要我们明显的加上ARIA role属性设置。
比如
progress,role为progressbar
再比如type为search的input,role就为searchbox
通常使用代码助手产生的,大家知道意思就行。
- ARIA规范除了可以给元素添加ARIA role ,还可以添加其他ARIA属性(如果你根据role,不足以去定位的话,可以添加其他属性),比如
html
<div role="heading" aria-level="1">1</div>
<div role="heading" aria-level="2">2</div>
playwright对常见的ARIA属性,有额外的参数对应,比如 aria-checked/aria-disabled/aria-level等等
比如,上述元素中怎么定位
tsx
const element3 = await page.getByRole("heading", {
level: 2
});
- 根据name定位
往往根据role和ARIA role属性,往往不能唯一定位元素
role定位最常见的组合是ARIA role和Accessible Name
因为,Accessible Name就像元素的名字一样,往往可以唯一定位
html
<a name="xinwen" href="http://news.baidu.com">新闻</a>
定位:
ts
const element4 = await page.getByRole("link", {
name: 'xinwen',
});
9. 缺省等待时间
Playwright中,当我们定位元素(比如,通过locator/get_by_text等方法)后,对元素进行操作(比如click,fill)
如果根据定位条件,找不到这个元素,playwright并不会立即抛出错误,而是缺省等待元素时间为30秒,如果30秒内元素出现了,就立即操作返回成功。
就比如我们点击save时候,根据后台的接口,成功后弹出提示信息。我们定位到这个提示信息后,操作返回成功,但是这个提示信息不是立即点击save后就出现了,是根据后台接口的返回,playwright等待30s
tsx
test("缺省等待时间", async ({ page, context }) => {
await page.goto("https://www.baidu.com/");
context.setDefaultTimeout(60000);
const element1 = await page.getByTitle("xxx");
console.log(await element1.innerHTML());
await expect(page).toHaveTitle(/百度一下/);
});
10. 元素通用操作
ts
test("元素通用操作", async ({ page }) => {
await page.goto("https://www.baidu.com/");
await page.getByRole("textbox").click();
await page.getByRole("textbox").fill("你好");
console.log(
await page.getByRole("button", { name: "百度一下" }).getAttribute("id")
);
console.log(await page.getByRole("button", { name: "百度一下" }).innerHTML());
await page.getByRole("button", { name: "百度一下" }).click();
await page.getByRole("button", { name: "百度一下" }).dblclick(); // 双击
await page.getByRole("button", { name: "百度一下" }).hover(); // 悬停
await expect(page).toHaveTitle(/百度一下/);
});
10.1 等待元素可见
前面说过,playwright通过locator对元素进行操作时,缺省就会等待30秒。
但是,有时我们的代码并不是要操作这个元素,而是要等待这个元素出现后,进行别的操作
这时,可以使用Locator对象的waitFor方法
ts
await page.getByRole("button", { name: "百度一下" }).waitFor({
timeout: 500000
});
10.2 判断元素是否可见
ts
await page.getByRole("button", { name: "百度一下" }).isVisible();
10.3 文本框
文本框输入
单行文本框input或者多行文本框textarea都可以使用Locator的fill方法进行输入
文本框清空
单行文本框input或者多行文本框textarea都可以使用Locator的clear方法进行清除
获取输入框的内容
通过inputValue方法
文件输入框
ts
const lc = await page.locator('input[type="file"]');
await lc.setInputFiles(["d:/1.png"]);
10.4 radio单选/checkbox多选
常见的选择框包括: radio框、checkbox框、select框
如果要点选radio框,可以使用Locator的check方法
如果要取消选择radio框,可以使用Locator对象的uncheck方法
如果我们要判断radio框是否选中,可以使用Locator对象的isChecked方法
checkbox和radio一样
10.5 select元素
要选择选项,可以使用select 元素对应的Locator对象的select_option方法
可以根据索引,或者通过value去选择

11. 网页Page操作
打开网址/刷新/后退,可以分别调用对象的goto/reload/go_back/go_forward方法
获取网页html,可以调用对象的content方法
获取网页的title,可以调用页面的title
设置网页宽度,高度的大小: setViewportSize
ts
test("网页Page操作", async ({ page }) => {
await page.goto("https://www.baidu.com/");
console.log(await page.context());
console.log(await page.title());
await page.setViewportSize({ width: 1920, height: 1280 });
});
12. frame切换
页面嵌入一个iframe的时候,你要获取里面iframe的元素,
要使用Page或者Locator对象的frame_locator方法定位到你要操作的frame
这个方法会产生一个FrameLocator对象,后续的定位,就使用这个对象,在其内部进行定位。
13. 窗口切换
在网页上操作的时候,我们经常遇到过,点击一个链接或按钮,就会打开一个新窗口
ts
test("窗口切换", async ({ page, context }) => {
//...
const new_page = await context.pages[0];
});
14. 冻结页面
在开发者工具栏console里面执行如下js代码:
ts
setTimeout(() => { debugger }, 5000)
15. 截屏
tsx
test("截屏", async ({ page }) => {
await page.goto("https://www.baidu.com/");
await page.screenshot({
path: "s1.png",
});
// 截屏完整页面,页面内容超过窗口高度时,包括不可见部分
await page.screenshot({
path: "s1.png",
fullPage: true,
});
// 也可以对某个元素的显示内容进行截屏
await page.locator("div").first().screenshot({
path: "s2.png",
});
});
16. 弹出框
alert、confirm、prompt
ts
test("弹出框", async ({ page }) => {
await page.goto("https://www.baidu.com/");
function handleDiglof(dialog) {
page.waitForTimeout(1000);
dialog.appect();
console.log(dialog.message);
}
page.on("dialog", handleDiglof);
page.locator("#alert-button").click();
});
dismiss相当于点击了取消按钮
注册的处理函数中一定要调用accept或者dismiss方法,让对话框消失
否则当对话框弹出时,后续任何代码都不会执行,并且会有超时错误。
Playwright在页面有弹出框时,如果发现没有任何注册的处理函数,会自动点击取消
16. 弹出框
alert、confirm、prompt
ts
test("弹出框", async ({ page }) => {
await page.goto("https://www.baidu.com/");
function handleDiglof(dialog) {
page.waitForTimeout(1000);
dialog.appect();
console.log(dialog.message);
}
page.on("dialog", handleDiglof);
page.locator("#alert-button").click();
});
dismiss相当于点击了取消按钮
注册的处理函数中一定要调用accept或者dismiss方法,让对话框消失
否则当对话框弹出时,后续任何代码都不会执行,并且会有超时错误。
Playwright在页面有弹出框时,如果发现没有任何注册的处理函数,会自动点击取消