Playwright 高级页面操作

本章节我们将介绍 Playwright 高级页面操作,这部分会更贴近真实测试需求,覆盖 等待机制、表单处理、弹窗对话框 等高级功能。


等待机制

Playwright 最大的优势之一就是 智能等待(auto-waiting):大部分操作(如 click, fill)会自动等待元素可见且可操作。但在复杂场景中,仍需要手动控制等待。

1. 隐式等待(Auto-waiting)

Playwright 内置的等待机制:

  • 元素可见
  • 可点击 / 可交互
  • 页面加载完成

实例

await page.click('#submit'); // 自动等待按钮可见并可点击

2. 显式等待

显式调用等待方法,例如等待某个选择器、文本或请求。

实例

await page.waitForSelector('#result', { state: 'visible' });

常见 state 值:

  • 'attached' → 元素出现在 DOM 中
  • 'visible' → 元素在页面可见
  • 'hidden' → 元素隐藏
  • 'detached' → 元素被移除

3. 等待元素出现 / 消失

// 等待元素出现
await page.waitForSelector('#loading', { state: 'visible' });

// 等待元素消失
await page.waitForSelector('#loading', { state: 'hidden' });

4. 等待网络请求完成

等待某个请求完成,常用于 AJAX 请求或 API 调用。

// 等待指定 URL 的请求完成
await page.waitForResponse(response => 
  response.url().includes('/api/login') && response.status() === 200
);

5. 自定义等待条件

通过 page.waitForFunction 执行自定义等待逻辑。

// 等待页面上计数器达到 10
await page.waitForFunction(() => {
  return document.querySelector('#count').innerText === '10';
});

表单处理

表单是测试中最常见的交互场景。Playwright 提供了丰富的 API 来操作表单控件。

1. 输入框操作

await page.fill('#username', 'admin');  // 清空并输入
await page.type('#search', 'Playwright', { delay: 100 }); // 模拟逐字输入

2. 下拉菜单选择

// 通过 value
await page.selectOption('#country', 'china');

// 通过标签文本
await page.selectOption('#country', { label: 'United States' });

// 通过 index
await page.selectOption('#country', { index: 2 });

3. 复选框和单选框

// 勾选复选框
await page.check('#agree');

// 取消勾选
await page.uncheck('#subscribe');

// 点击单选框
await page.check('input[type="radio"][value="male"]');

4. 文件上传

// 直接上传文件
await page.setInputFiles('#upload', 'example.png');

// 上传多个文件
await page.setInputFiles('#upload', ['a.png', 'b.png']);

// 清空文件
await page.setInputFiles('#upload', []);

5. 表单提交

提交表单有两种方式:

  • 点击提交按钮
  • 直接触发表单提交事件
await page.click('button[type="submit"]');
// 或者
await page.press('#form input[name="email"]', 'Enter');

弹窗和对话框

Playwright 可以捕获并处理 浏览器原生弹窗(alert, confirm, prompt),以及自定义模态框。

1. Alert 处理

page.once('dialog', async dialog => {
  console.log(dialog.message()); // 打印弹窗内容
  await dialog.accept(); // 点击 "确定"
});

await page.click('#show-alert');

2. Confirm 处理

page.once('dialog', async dialog => {
  console.log(dialog.message());
  await dialog.dismiss(); // 点击 "取消"
});

await page.click('#show-confirm');

3. Prompt 处理

page.once('dialog', async dialog => {
  console.log(dialog.message());
  await dialog.accept('Playwright'); // 输入内容并确认
});

await page.click('#show-prompt');

4. 自定义模态框

自定义模态框(如 Bootstrap / Ant Design)其实就是普通 DOM 元素,可以直接用 定位 + 交互 的方式关闭或输入。

await page.click('#open-modal');
await page.fill('.modal input[name="name"]', 'Test User');
await page.click('.modal button.confirm');

实战案例 —— 注册表单自动化测试

下面的示例会综合使用 等待机制、表单操作、文件上传、对话框处理

实例

// register-test.js
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ headless: false, slowMo: 500 });
  const context = await browser.newContext();
  const page = await context.newPage();

  // 1. 打开注册页面
  await page.goto('https://example.com/register', { waitUntil: 'domcontentloaded' });

  // 2. 等待表单出现
  await page.waitForSelector('#register-form', { state: 'visible' });

  // 3. 输入基本信息
  await page.fill('#username', 'testuser');
  await page.fill('#email', 'test@example.com');
  await page.fill('#password', 'Password123');

  // 4. 下拉选择国家
  await page.selectOption('#country', { label: 'China' });

  // 5. 勾选条款
  await page.check('#agree');

  // 6. 上传头像
  await page.setInputFiles('#avatar', 'avatar.png');

  // 7. 点击提交
  page.once('dialog', async dialog => {
    console.log('弹窗内容:', dialog.message());
    await dialog.accept(); // 点击确定
  });
  await page.click('button[type="submit"]');

  // 8. 等待提交完成后的跳转
  await page.waitForLoadState('networkidle');

  // 9. 断言结果(是否包含欢迎文字)
  const successText = await page.textContent('.welcome-message');
  console.log('注册结果:', successText);

  // 10. 截图保存
  await page.screenshot({ path: 'register-success.png' });

  await browser.close();
})();

运行后效果:

  • 自动填写表单、选择国家、上传头像
  • 自动处理弹窗
  • 提交后截图并验证欢迎信息

完整 API

以下整理了 页面导航、等待、表单、交互、弹窗 的常用 API:

分类 方法 / 属性 说明
页面导航 page.goto(url[, options]) 打开页面
page.reload([options]) 刷新页面
page.goBack([options]) 后退
page.goForward([options]) 前进
page.waitForLoadState([state, options]) 等待加载完成
等待机制 page.waitForSelector(selector[, options]) 等待元素出现/消失
page.waitForResponse(fn[, options]) 等待指定请求响应
page.waitForFunction(pageFn[, arg, options]) 自定义等待条件
元素定位 page.locator(selector) 通用定位器
page.getByRole(role, options) 根据角色定位
page.getByText(text) 根据文本定位
page.getByPlaceholder(text) 输入框 placeholder
page.getByLabel(text) 表单 label
表单操作 page.fill(selector, value) 输入框(清空后填入)
page.type(selector, text[, options]) 模拟逐字输入
page.press(selector, key) 在元素上按键
page.selectOption(selector, values) 下拉菜单选择
page.check(selector[, options]) 勾选复选框/单选框
page.uncheck(selector[, options]) 取消勾选
page.setInputFiles(selector, files) 文件上传
基础交互 page.click(selector[, options]) 点击元素
page.dblclick(selector[, options]) 双击
page.hover(selector[, options]) 悬停
page.mouse.move(x, y) 鼠标移动
page.mouse.click(x, y[, options]) 鼠标点击
page.dragAndDrop(src, dest[, options]) 拖拽元素
弹窗处理 page.on('dialog', handler) 监听对话框事件
dialog.message() 获取弹窗文本
dialog.accept([promptText]) 确认(可输入内容)
dialog.dismiss() 取消
截图与断言 page.screenshot({ path }) 截图保存
page.textContent(selector) 获取文本内容
expect(locator).toHaveText(text) 断言文本(需用 @playwright/test)