真实场景
Day 19 - Uploads, Downloads, Dialogs, and Frames
今日目标
- 掌握常见复杂页面交互。
- 能处理文件、弹窗、iframe。
学习时间安排(60–120 分钟)
| 时间 | 模块 | 做什么 |
|---|---|---|
| 0-20 分钟 | 核心概念与词汇 | 读概念表,重点理解 download 事件和 frameLocator。 |
| 20-45 分钟 | 官方文档阅读 | 读 Uploads、Downloads、Dialogs、Frames 相关文档。 |
| 45-75 分钟 | 实操练习 | 分别练习上传、下载、dialog、iframe 四类交互。 |
| 75-105 分钟 | 示例代码改写 | 把四类交互整理成模板代码笔记。 |
| 105-120 分钟 | 复盘与作业 | 完成今日问题,标记最容易 flaky 的点。 |
核心概念与词汇
| English | 中文 | 场景用法 |
|---|---|---|
setInputFiles() | 设置上传文件 | Use it when you attach files to a file input. |
waitForEvent('download') | 等待下载事件 | Use it when you capture the download promise before clicking. |
download.saveAs() | 保存下载文件 | Use it when you write the downloaded file to disk for assertions. |
| dialog | 原生弹窗 | Use it when you handle alert, confirm, and prompt dialogs. |
page.on('dialog') | 弹窗监听 | Use it when you register a handler before the dialog appears. |
frameLocator() | Frame 定位器 | Use it when you interact with elements inside an iframe. |
| iframe | 内嵌框架 | Use it when part of the page comes from another document. |
dialog.accept() / dismiss() | 接受/取消弹窗 | Use it when you click OK or Cancel on a dialog. |
| multipart/form-data | 表单文件上传 | Use it when you explain how file inputs send content. |
| download path | 下载路径 | Use it when you decide where downloaded files land, especially in CI. |
学习材料
- 必读:[Uploads](https://playwright.dev/docs/input#upload-files)
- 必读:[Downloads](https://playwright.dev/docs/downloads)
- 必读:[Dialogs](https://playwright.dev/docs/dialogs)
- 必读:[Frames](https://playwright.dev/docs/frames)
重点理解
上传文件:
await page.getByLabel('Upload file').setInputFiles('tests/fixtures/file.pdf');
下载文件:
const downloadPromise = page.waitForEvent('download');
await page.getByRole('button', { name: 'Download' }).click();
const download = await downloadPromise;
await download.saveAs('downloads/report.pdf');
Dialog:
page.on('dialog', async dialog => {
expect(dialog.message()).toContain('Are you sure');
await dialog.accept();
});
Frame:
const frame = page.frameLocator('iframe[name="payment"]');
await frame.getByLabel('Card number').fill('4242424242424242');
实操步骤
分别练习:
- 上传一个文件。
- 下载一个文件。
- 处理 confirm dialog。
- 定位 iframe 中的元素。
示例代码
import { test, expect } from '@playwright/test';
test('upload and download report', async ({ page }) => {
await page.goto('/reports');
// 上传
await page.getByLabel('Upload file').setInputFiles('tests/fixtures/sample.csv');
// 下载:先建 promise 再点击
const downloadPromise = page.waitForEvent('download');
await page.getByRole('button', { name: 'Generate report' }).click();
const download = await downloadPromise;
await download.saveAs('downloads/report.csv');
await expect(page.getByText('Report ready')).toBeVisible();
});
test('confirm deletion dialog', async ({ page }) => {
page.on('dialog', async dialog => {
expect(dialog.type()).toBe('confirm');
await dialog.accept();
});
await page.goto('/orders');
await page.getByRole('button', { name: 'Delete order #42' }).click();
await expect(page.getByText('Order #42 deleted')).toBeVisible();
});
常见坑
- 先点击下载按钮再等 download 事件,事件已经错过。
- dialog 监听注册在点击之后,弹窗没人处理导致测试挂起。
- 直接
page.getByLabel()找 iframe 里的元素——必须先 frameLocator。 - CI 里
saveAs('downloads/...')目录不存在或没有写权限。 - 上传路径用绝对路径,换机器/CI 环境后文件找不到。
今日产出
- 一个复杂交互测试文件。
- 记录每类交互的模板代码。
今日问题
- 文件下载为什么要先创建
waitForEventpromise? - Dialog 监听应该在点击前还是点击后?
- FrameLocator 和普通 Locator 有什么区别?
- 文件路径在 CI 中可能有什么问题?
- 复杂交互最容易 flaky 的点是什么?
复盘要点
- 事件类交互的口诀:先监听、后触发(download、dialog、popup 全部如此)。
- iframe 是页面里的页面:先 frameLocator 切入,再沿用你会的所有 locator 技巧。
- 复杂交互的 flaky 大多来自时序——事件错过、弹窗未处理、文件未落盘。
AI 时代扩展:AI 辅助 Playwright 测试
新增概念
| English | 中文 |
|---|---|
| event timing | 事件时序 |
| cross-frame interaction | 跨 frame 交互 |
| file handling | 文件处理 |
适用场景
让 AI 解释“为什么要先建 promise 再点击”,或者审查你的复杂交互测试里的时序问题。iframe 内的 locator 建议也可以让 AI 先给草稿。
可复用表达 / 提示词
请解释 Playwright 处理 download 事件时为什么必须「先 waitForEvent 再点击」,不这样做会怎样。
这是 iframe 的 HTML 片段,请给出 frameLocator 切入后的元素定位草稿。
追问加练
- AI 建议用 waitForTimeout 等弹窗出现,正确做法是什么?
- dialog 没被处理时测试会卡住还是失败?你遇到过吗?
- 多级嵌套 iframe 怎么一层层切入?
今日作业
- 完成四类交互各 1 条测试并跑通。
- 整理“复杂交互模板库”:上传、下载、dialog、iframe、popup 各一段可复制代码。
- 让 AI 审查你的模板库,找出时序问题并修正。