Files
2026-07-13 21:36:47 +08:00

747 lines
27 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 调试 Playwright 测试
> **使用场景**:测试失败,你需要理解原因——选择器错误、时序问题、网络故障或意外的应用程序状态。
## 快速参考
| 工具 | 命令 | 最适合的场景 |
| -------------------------- | ----------------------------------------------- | ---------------------------------------------------------------- |
| UI 模式 | `npx playwright test --ui` | 交互式探索、可视化时间线、重新运行测试 |
| Playwright Inspector | `PWDEBUG=1 npx playwright test` | 逐步调试、选择器试验场 |
| Trace Viewer(追踪查看器) | `npx playwright show-trace trace.zip` | CI 失败的事后分析 |
| 有头模式 | `npx playwright test --headed` | 在测试执行期间观察浏览器 |
| 慢速模式 | `npx playwright test --headed --slow-mo=500` | 可视地跟踪快速交互 |
| `page.pause()` | 插入测试代码中 | 在精确位置暂停以检查状态 |
| 详细 API 日志 | `DEBUG=pw:api npx playwright test` | 查看每个 Playwright API 调用及其耗时 |
| VS Code 扩展 | Playwright Test for VS Code | 断点、逐步执行、拾取定位器 |
## 系统化调试工作流
按此顺序执行。不要直接跳到第 5 步——大多数问题在第 2 步即可解决。
```
1. 阅读完整的错误信息
└─ 查阅 troubleshooting/error-index.md 了解已知模式
2. 使用 --ui 运行以直观查看发生了什么
└─ 时间线显示每个操作,失败点附有截图
3. 如果尚未开启追踪,先启用
└─ 在配置中临时使用 use: { trace: 'on' }
4. 在追踪的网络标签页中检查 API 故障
└─ 缺失的响应、4xx/5xx、CORS 错误
5. 在失败点插入 page.pause()
└─ 检查实时 DOM,在控制台中尝试选择器
6. 检查浏览器控制台中的 JavaScript 错误
└─ page.on('console') 或追踪中的控制台标签页
```
## 模式
### 模式 1:使用 UI 模式进行交互式调试
**适用场景**:开发新测试、本地调查失败原因、探索应用程序行为。
**避免场景**CI 环境(改用追踪)。
UI 模式提供可视化时间线、每一步的 DOM 快照、网络瀑布图,以及重新运行单个测试的能力。
**TypeScript**
```typescript
// 从终端启动 UI 模式:
// npx playwright test --ui
// 在 UI 模式下运行特定测试文件:
// npx playwright test tests/checkout.spec.ts --ui
// playwright.config.ts — 为 UI 模式便利性配置
import { defineConfig } from "@playwright/test"
export default defineConfig({
use: {
// 在 UI 模式下,追踪始终可用,不受此设置影响,
// 但此设置确保 CI 失败时也能捕获追踪
trace: "on-first-retry",
},
})
```
**JavaScript**
```javascript
// 从终端启动 UI 模式:
// npx playwright test --ui
// 在 UI 模式下运行特定测试文件:
// npx playwright test tests/checkout.spec.js --ui
// playwright.config.js
const { defineConfig } = require("@playwright/test")
module.exports = defineConfig({
use: {
trace: "on-first-retry",
},
})
```
### 模式 2:使用 PWDEBUG 的 Playwright Inspector
**适用场景**:需要逐步执行每个操作、交互式测试选择器,或查看每个操作前后的精确状态。
**避免场景**:错误信息或追踪本身已能显示失败原因。
**TypeScript**
```typescript
// 从终端启动 Inspector
// PWDEBUG=1 npx playwright test tests/login.spec.ts
// Windows PowerShell
// $env:PWDEBUG=1; npx playwright test tests/login.spec.ts
// Windows CMD
// set PWDEBUG=1 && npx playwright test tests/login.spec.ts
// Inspector 自动打开。使用以下控件:
// - "Step over"(单步跳过)按钮:每次执行一个操作
// - "Pick locator"(拾取定位器)按钮:悬停元素以查看最佳定位器
// - "Resume"(继续)按钮:运行到下一个 page.pause() 或结束
import { test, expect } from "@playwright/test"
test("调试登录流程", async ({ page }) => {
await page.goto("/login")
// 当 PWDEBUG=1 时,Inspector 会在每个操作前暂停
await page.getByLabel("Email").fill("user@example.com")
await page.getByLabel("Password").fill("password123")
await page.getByRole("button", { name: "Sign in" }).click()
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible()
})
```
**JavaScript**
```javascript
// 从终端启动 Inspector
// PWDEBUG=1 npx playwright test tests/login.spec.js
const { test, expect } = require("@playwright/test")
test("调试登录流程", async ({ page }) => {
await page.goto("/login")
await page.getByLabel("Email").fill("user@example.com")
await page.getByLabel("Password").fill("password123")
await page.getByRole("button", { name: "Sign in" }).click()
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible()
})
```
### 模式 3:使用 Trace Viewer 分析 CI 失败
**适用场景**:测试在 CI 中失败,你需要理解发生了什么,而无需在本地重新运行。
**避免场景**:你能在本地复现失败(改用 UI 模式或 Inspector)。
**TypeScript**
```typescript
// playwright.config.ts — 追踪配置
import { defineConfig } from "@playwright/test"
export default defineConfig({
retries: process.env.CI ? 2 : 0,
use: {
// 'on-first-retry' — 仅在首次重试时捕获追踪(推荐用于 CI)
// 'on' — 每次运行都捕获(临时用于顽固失败)
// 'retain-on-failure' — 每次运行都捕获,仅保留失败的追踪
trace: "on-first-retry",
},
})
// CI 失败后,下载追踪产物并打开:
// npx playwright show-trace test-results/tests-login-Login-test-chromium/trace.zip
// 或者通过 URL 打开:
// npx playwright show-trace https://ci.example.com/artifacts/trace.zip
// 或者使用 trace.playwright.dev 在浏览器中查看追踪——拖放 zip 文件即可
```
**JavaScript**
```javascript
// playwright.config.js
const { defineConfig } = require("@playwright/test")
module.exports = defineConfig({
retries: process.env.CI ? 2 : 0,
use: {
trace: "on-first-retry",
},
})
// CI 失败后,下载追踪产物并打开:
// npx playwright show-trace test-results/tests-login-Login-test-chromium/trace.zip
```
**阅读追踪——按顺序检查的内容:**
1. **Actions(操作)标签页**——查看每个 Playwright 操作及其前后截图
2. **Console(控制台)标签页**——浏览器控制台输出(错误、警告、日志)
3. **Network(网络)标签页**——每个 HTTP 请求及其状态、耗时、请求/响应体
4. **Source(源码)标签页**——高亮显示失败行的测试源代码
5. **Call(调用)标签页**——每个 Playwright 调用的精确参数和返回值
### 模式 4:有头模式加慢速
**适用场景**:希望观察浏览器执行过程,而不想承受完整的 Inspector 开销。
**避免场景**:即使使用慢速模式,测试运行仍然太快而难以跟踪(改用 Inspector)。
**TypeScript**
```typescript
// 从终端——快速可视化调试:
// npx playwright test tests/checkout.spec.ts --headed --slow-mo=500
// playwright.config.ts — 为本地开发配置有头模式
import { defineConfig } from "@playwright/test"
export default defineConfig({
use: {
// 不要提交这些设置——改用 CLI 标志或环境变量检查
headless: !process.env.HEADED,
launchOptions: {
slowMo: process.env.SLOW_MO ? parseInt(process.env.SLOW_MO) : 0,
},
},
})
// 然后运行:
// HEADED=1 SLOW_MO=500 npx playwright test tests/checkout.spec.ts
```
**JavaScript**
```javascript
// 从终端:
// npx playwright test tests/checkout.spec.js --headed --slow-mo=500
// playwright.config.js
const { defineConfig } = require("@playwright/test")
module.exports = defineConfig({
use: {
headless: !process.env.HEADED,
launchOptions: {
slowMo: process.env.SLOW_MO ? parseInt(process.env.SLOW_MO) : 0,
},
},
})
```
### 模式 5VS Code 集成
**适用场景**:你更喜欢基于 IDE 的调试,包括断点、变量检查和集成测试运行。
**避免场景**:你在调试 CI 独有的失败,且该失败无法在本地复现。
安装 **Playwright Test for VS Code** 扩展(`ms-playwright.playwright`)。
**关键功能:**
- **运行/调试单个测试**——点击任意 `test()` 旁的绿色播放按钮
- **设置断点**——点击左侧装订线设置断点;测试会自动在断点处暂停
- **拾取定位器**——使用 "Pick locator" 命令悬停在元素上以获取最佳选择器
- **显示浏览器**——在测试侧边栏中勾选 "Show Browser" 以在测试执行期间查看浏览器
- **监听模式**——启用后可在保存文件时重新运行测试
**TypeScript**
```typescript
// 在 VS Code 中调试时,使用 test.only() 聚焦单个测试,
// 而不是通过调试器运行整个套件
import { test, expect } from "@playwright/test"
test.only("调试这个特定测试", async ({ page }) => {
await page.goto("/products")
// 在这一行设置 VS Code 断点,然后在调试面板中检查 `page`
const productCard = page.getByRole("listitem").filter({ hasText: "Widget" })
await expect(productCard).toBeVisible()
await productCard.getByRole("button", { name: "Add to cart" }).click()
await expect(page.getByTestId("cart-count")).toHaveText("1")
})
```
**JavaScript**
```javascript
const { test, expect } = require("@playwright/test")
test.only("调试这个特定测试", async ({ page }) => {
await page.goto("/products")
const productCard = page.getByRole("listitem").filter({ hasText: "Widget" })
await expect(productCard).toBeVisible()
await productCard.getByRole("button", { name: "Add to cart" }).click()
await expect(page.getByTestId("cart-count")).toHaveText("1")
})
```
### 模式 6:捕获浏览器控制台日志
**适用场景**:怀疑存在 JavaScript 错误、客户端 API 调用失败,或应用程序级别的日志能解释失败原因。
**避免场景**:问题显然是追踪中可见的选择器或时序问题。
**TypeScript**
```typescript
import { test, expect } from "@playwright/test"
test("捕获控制台输出", async ({ page }) => {
// 收集所有控制台消息
const consoleLogs: string[] = []
page.on("console", (msg) => {
consoleLogs.push(`[${msg.type()}] ${msg.text()}`)
})
// 捕获未捕获的异常
page.on("pageerror", (error) => {
console.error("页面错误:", error.message)
})
await page.goto("/dashboard")
await page.getByRole("button", { name: "Load data" }).click()
await expect(page.getByRole("table")).toBeVisible()
// 失败时打印收集的日志以提供调试上下文
console.log("浏览器控制台输出:", consoleLogs)
})
// 用于所有测试的控制台日志记录可复用 fixture
import { test as base } from "@playwright/test"
type ConsoleFixtures = {
consoleMessages: string[]
}
export const test = base.extend<ConsoleFixtures>({
consoleMessages: async ({ page }, use) => {
const messages: string[] = []
page.on("console", (msg) => messages.push(`[${msg.type()}] ${msg.text()}`))
page.on("pageerror", (err) => messages.push(`[pageerror] ${err.message}`))
await use(messages)
},
})
```
**JavaScript**
```javascript
const { test, expect } = require("@playwright/test")
test("捕获控制台输出", async ({ page }) => {
const consoleLogs = []
page.on("console", (msg) => {
consoleLogs.push(`[${msg.type()}] ${msg.text()}`)
})
page.on("pageerror", (error) => {
console.error("页面错误:", error.message)
})
await page.goto("/dashboard")
await page.getByRole("button", { name: "Load data" }).click()
await expect(page.getByRole("table")).toBeVisible()
console.log("浏览器控制台输出:", consoleLogs)
})
// 用于控制台日志记录的可复用 fixture
const { test: base } = require("@playwright/test")
const test = base.extend({
consoleMessages: async ({ page }, use) => {
const messages = []
page.on("console", (msg) => messages.push(`[${msg.type()}] ${msg.text()}`))
page.on("pageerror", (err) => messages.push(`[pageerror] ${err.message}`))
await use(messages)
},
})
module.exports = { test }
```
### 模式 7:失败时截图
**适用场景**:你需要在失败发生的精确时刻获取可视化快照。
**避免场景**:已启用追踪(追踪已经在每一步包含了截图)。
**TypeScript**
```typescript
// playwright.config.ts — 失败时自动截图
import { defineConfig } from "@playwright/test"
export default defineConfig({
use: {
// 'off' — 不截图(默认)
// 'on' — 每次测试后截图
// 'only-on-failure' — 仅在测试失败时截图(推荐)
screenshot: "only-on-failure",
},
})
```
```typescript
// 在特定点手动截图
import { test, expect } from "@playwright/test"
test("调试视觉状态", async ({ page }) => {
await page.goto("/checkout")
await page.getByLabel("Promo code").fill("SAVE20")
await page.getByRole("button", { name: "Apply" }).click()
// 在断言前捕获截图以进行调试
await page.screenshot({ path: "test-results/before-discount.png", fullPage: true })
await expect(page.getByTestId("discount-amount")).toHaveText("-$20.00")
})
```
**JavaScript**
```javascript
// playwright.config.js
const { defineConfig } = require("@playwright/test")
module.exports = defineConfig({
use: {
screenshot: "only-on-failure",
},
})
```
```javascript
// 手动截图
const { test, expect } = require("@playwright/test")
test("调试视觉状态", async ({ page }) => {
await page.goto("/checkout")
await page.getByLabel("Promo code").fill("SAVE20")
await page.getByRole("button", { name: "Apply" }).click()
await page.screenshot({ path: "test-results/before-discount.png", fullPage: true })
await expect(page.getByTestId("discount-amount")).toHaveText("-$20.00")
})
```
### 模式 8:网络调试
**适用场景**:怀疑 API 失败、请求负载错误、缺少认证头,或响应缓慢导致超时。
**避免场景**:追踪的网络标签页已显示问题所在。
**TypeScript**
```typescript
import { test, expect } from "@playwright/test"
test("调试网络请求", async ({ page }) => {
// 记录所有请求
page.on("request", (request) => {
console.log(`>> ${request.method()} ${request.url()}`)
})
// 记录所有响应及其状态码
page.on("response", (response) => {
console.log(`<< ${response.status()} ${response.url()}`)
})
// 记录失败的请求(网络错误,而非 HTTP 错误)
page.on("requestfailed", (request) => {
console.log(`失败: ${request.url()} ${request.failure()?.errorText}`)
})
await page.goto("/dashboard")
await page.getByRole("button", { name: "Refresh" }).click()
await expect(page.getByRole("table")).toBeVisible()
})
// 等待特定 API 响应并检查它
test("检查 API 响应", async ({ page }) => {
await page.goto("/products")
const responsePromise = page.waitForResponse(
(resp) => resp.url().includes("/api/products") && resp.status() === 200
)
await page.getByRole("button", { name: "Load products" }).click()
const response = await responsePromise
const body = await response.json()
console.log("API 响应:", JSON.stringify(body, null, 2))
await expect(page.getByRole("listitem")).toHaveCount(body.products.length)
})
```
**JavaScript**
```javascript
const { test, expect } = require("@playwright/test")
test("调试网络请求", async ({ page }) => {
page.on("request", (request) => {
console.log(`>> ${request.method()} ${request.url()}`)
})
page.on("response", (response) => {
console.log(`<< ${response.status()} ${response.url()}`)
})
page.on("requestfailed", (request) => {
console.log(`失败: ${request.url()} ${request.failure()?.errorText}`)
})
await page.goto("/dashboard")
await page.getByRole("button", { name: "Refresh" }).click()
await expect(page.getByRole("table")).toBeVisible()
})
test("检查 API 响应", async ({ page }) => {
await page.goto("/products")
const responsePromise = page.waitForResponse(
(resp) => resp.url().includes("/api/products") && resp.status() === 200
)
await page.getByRole("button", { name: "Load products" }).click()
const response = await responsePromise
const body = await response.json()
console.log("API 响应:", JSON.stringify(body, null, 2))
await expect(page.getByRole("listitem")).toHaveCount(body.products.length)
})
```
### 模式 9:详细 API 日志
**适用场景**:你需要查看每个 Playwright API 调用及其耗时,以确定测试在何处花费时间或卡住。
**避免场景**:你已经知道哪个操作正在失败(改用 Inspector 或 `page.pause()`)。
```bash
# 查看所有 Playwright API 调用及时间戳
DEBUG=pw:api npx playwright test tests/slow-test.spec.ts
# 查看浏览器协议消息(非常详细——谨慎使用)
DEBUG=pw:protocol npx playwright test tests/slow-test.spec.ts
# 组合多个调试通道
DEBUG=pw:api,pw:browser npx playwright test tests/slow-test.spec.ts
# Windows PowerShell
$env:DEBUG="pw:api"; npx playwright test tests/slow-test.spec.ts
# Windows CMD
set DEBUG=pw:api && npx playwright test tests/slow-test.spec.ts
```
### 模式 10`page.pause()` — 内联断点
**适用场景**:你需要在精确位置暂停执行,以检查实时 DOM、尝试定位器或检查应用程序状态。
**避免场景**:你可以使用 `PWDEBUG=1`,它会自动在每个步骤暂停。
**TypeScript**
```typescript
import { test, expect } from "@playwright/test"
test("使用暂停进行调试", async ({ page }) => {
await page.goto("/checkout")
await page.getByLabel("Email").fill("user@example.com")
// 执行在此处暂停——Inspector 会打开,提供:
// - 实时 DOM 检查
// - 选择器试验场(在控制台中尝试定位器)
// - 逐步执行剩余操作
await page.pause()
// 这些操作会等待你在 Inspector 中点击 "Resume"
await page.getByRole("button", { name: "Continue" }).click()
await expect(page.getByText("Order confirmed")).toBeVisible()
})
```
**JavaScript**
```javascript
const { test, expect } = require("@playwright/test")
test("使用暂停进行调试", async ({ page }) => {
await page.goto("/checkout")
await page.getByLabel("Email").fill("user@example.com")
// 执行在此处暂停——Inspector 会打开
await page.pause()
await page.getByRole("button", { name: "Continue" }).click()
await expect(page.getByText("Order confirmed")).toBeVisible()
})
```
**重要提示**:在提交前删除 `page.pause()`。它会在 CI 中无限挂起。使用此 lint 规则或 CI 检查:
```bash
# 添加到 pre-commit 钩子或 CI 流水线
grep -r "page.pause()" tests/ && echo "错误:提交前请移除 page.pause()" && exit 1
```
## 决策指南
使用此表格根据失败类型选择正确的工具。
| 失败类型 | 首选工具 | 原因 |
| ------------------------------------ | -------------------------------------------- | ------------------------------------------------------------------------------ |
| **元素未找到**(选择器错误) | UI 模式(`--ui`) | 在失败时刻查看 DOM,在 Pick Locator 中尝试选择器 |
| **元素未找到**(时序问题) | Trace Viewer — Actions(操作)标签页 | 比较前后截图,查看元素是否在超时后才出现 |
| **文本/值错误** | Trace Viewer — Actions(操作)标签页 | 检查每个操作步骤的实际 DOM 内容 |
| **测试挂起/超时** | `DEBUG=pw:api` | 查看哪个 API 调用正在等待且从未完成 |
| **网络/API 失败** | Trace Viewer — Network(网络)标签页 | 查看请求/响应状态码、负载、耗时 |
| **认证/会话问题** | 网络调试(`page.on('response')`) | 检查 401/403 响应、缺少的 cookie/token |
| **视觉渲染错误** | `--headed --slow-mo=500` | 在浏览器中观察实际渲染 |
| **应用中的 JavaScript 错误** | 控制台日志记录(`page.on('console')`) | 捕获未捕获的异常和错误日志 |
| **仅 CI 失败** | Trace Viewer(来自 CI 产物) | 无需在本地运行即可重现精确的 CI 状态 |
| **不稳定/间歇性失败** | 每次运行追踪(`trace: 'on'`)加重试 | 并排比较通过和失败的追踪 |
| **状态污染** | 使用 `test.only()` 运行单个测试 | 与其他测试隔离;如果单独通过,则状态来自其他测试的泄漏 |
## 反模式
### 使用 `waitForTimeout` 修复时序问题
```typescript
// 错误——任意延迟掩盖了真正的问题,使测试变得缓慢且不稳定
await page.getByRole("button", { name: "Submit" }).click()
await page.waitForTimeout(3000) // "加上这个延迟就能工作"
await expect(page.getByText("Success")).toBeVisible()
// 正确——等待实际条件
await page.getByRole("button", { name: "Submit" }).click()
await expect(page.getByText("Success")).toBeVisible() // 自动重试最长 5 秒
```
如果默认超时不够,请调查操作缓慢的_原因_,然后要么:
- 修复应用程序性能
- 增加特定断言的超时时间:`await expect(locator).toBeVisible({ timeout: 15000 })`
- 等待前置条件:`await page.waitForResponse('**/api/submit')`
### 注释掉测试以隔离失败
```typescript
// 错误——注释掉测试以找出哪个导致失败
// test('test A', ...);
// test('test B', ...);
test("test C — 这个会失败", async ({ page }) => {
/* ... */
})
// 正确——使用 .only 运行单个测试
test.only("test C — 这个会失败", async ({ page }) => {
/* ... */
})
// 正确——使用 grep 运行匹配模式的测试
// npx playwright test --grep "test C"
```
### 不阅读完整的错误信息
Playwright 错误信息包括:
- **预期值**与**实际值**
- 使用的**定位器**
- **调用日志**,显示 Playwright 在超时前尝试的操作
- 测试中的**行号**
阅读全部内容。仅凭调用日志通常就能精确显示问题所在(例如,元素存在但被隐藏时显示"等待选择器可见")。
### 在没有追踪的情况下调试 CI 失败
```typescript
// 错误——CI 中没有追踪,无法调试失败
export default defineConfig({
use: {
trace: "off",
},
})
// 正确——始终在 CI 失败时捕获追踪
export default defineConfig({
retries: process.env.CI ? 2 : 0,
use: {
trace: "on-first-retry",
},
})
```
### 使用 `console.log` 代替正确的调试工具
```typescript
// 错误——到处散布 console.log
test("使用日志调试", async ({ page }) => {
await page.goto("/dashboard")
console.log("页面已加载")
const el = page.getByRole("button", { name: "Save" })
console.log("按钮已找到:", await el.isVisible())
console.log("按钮文本:", await el.textContent())
// ...另外 20 个 console.log 调用
// 正确——在感兴趣的点使用 page.pause()
await page.goto("/dashboard")
await page.pause() // 交互式检查所有内容
})
```
### 提交包含 `page.pause()` 或 `test.only()` 的代码
```typescript
// 错误——这些永远不应进入 CI
test.only("聚焦测试", async ({ page }) => {
// 跳过所有其他测试
await page.goto("/")
await page.pause() // 在 CI 中永远挂起
})
// 如果在开发中需要,添加 CI 防护
if (!process.env.CI) {
await page.pause()
}
```
## 故障排查
| 症状 | 可能原因 | 修复方法 |
| ----------------------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Inspector 在 `PWDEBUG=1` 时不打开 | 在无头模式下运行,或 workers 大于 1 | 使用 `--headed``--workers=1` 运行 |
| 追踪为空或缺失 | 配置中 `trace: 'off'`,或测试未重试 | 临时设置为 `trace: 'on'`,或 `trace: 'retain-on-failure'` |
| UI 模式显示过时的测试结果 | 文件监听器未检测到更改 | 停止 UI 模式,清除 `test-results/`,重新启动 |
| `page.pause()` 无效果 | 未设置 `PWDEBUG` 且在无头模式下运行 | 使用 `--headed` 运行或设置 `PWDEBUG=1` |
| 截图空白或尺寸错误 | 未设置视口或测试在错误的浏览器项目中运行 | 在配置中设置 `viewport`;检查哪个浏览器项目运行了 |
| 详细日志信息量过大 | 使用了 `DEBUG=pw:protocol` | 使用 `DEBUG=pw:api` 以获得可管理的详细级别 |
| 追踪文件过大 | 对所有测试都开启 `trace: 'on'`,包括通过的测试 | 切换到 `trace: 'on-first-retry'``trace: 'retain-on-failure'` |
| VS Code 未检测到测试 | `testDir``testMatch` 配置错误 | 确保配置路径匹配,且扩展设置指向你的 `playwright.config` |
| 网络事件未触发 | 请求在监听器附加之前已发出 | 在 `page.goto()` 之前附加 `page.on('request')``page.on('response')` |
## 相关文档
- [core/error-index.md](error-index.md) — 查询特定错误信息
- [core/flaky-tests.md](flaky-tests.md) — 间歇性失败模式及修复
- [core/common-pitfalls.md](common-pitfalls.md) — 常见初学者错误
- [core/assertions-and-waiting.md](assertions-and-waiting.md) — 以 Web 为先的断言和自动等待
- [core/configuration.md](configuration.md) — 追踪、截图和重试配置
- [ci/reporting-and-artifacts.md](../ci/reporting-and-artifacts.md) — 用于追踪和截图的 CI 产物收集配置