Playwright 提供 API 来监控和修改浏览器网络流量,包括 HTTP 和 HTTPS。页面发出的任何请求,包括 XHR 和 fetch 请求,都可以被跟踪、修改和处理。
模拟 API
Section titled “模拟 API”查看我们的 API 模拟指南,了解更多关于如何:
- 模拟 API 请求并且永远不真正访问 API
- 执行 API 请求并修改响应
- 使用 HAR 文件模拟网络请求。
你不需要配置任何内容即可模拟网络请求。只需定义一个自定义的 Route,为浏览器上下文模拟网络。
example.spec.ts
import { test, expect } from '@playwright/test';
test.beforeEach(async ({ context }) => { // 阻止此文件中每个测试的任何 css 请求。 await context.route(/.css$/, route => route.abort());});
test('loads page without css', async ({ page }) => { await page.goto('https://playwright.dev'); // ... 测试写在这里});或者,你可以使用 page.route() 在单个页面中模拟网络。
example.spec.ts
import { test, expect } from '@playwright/test';
test('loads page without images', async ({ page }) => { // 阻止 png 和 jpeg 图片。 await page.route(/(png|jpeg)$/, route => route.abort()); await page.goto('https://playwright.dev'); // ... 测试写在这里});HTTP 身份验证
Section titled “HTTP 身份验证”执行 HTTP 身份验证。
playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({ use: { httpCredentials: { username: 'bill', password: 'pa55w0rd', } }});Library
Section titled “Library”const context = await browser.newContext({ httpCredentials: { username: 'bill', password: 'pa55w0rd', },});const page = await context.newPage();await page.goto('https://example.com');HTTP 代理
Section titled “HTTP 代理”你可以配置页面通过 HTTP(S) 代理或 SOCKSv5 加载。代理既可以为整个浏览器全局设置,也可以为每个浏览器上下文单独设置。
你可以选择性地为 HTTP(S) 代理指定用户名和密码,也可以指定要绕过代理的主机。
下面是一个全局代理的示例:
playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({ use: { proxy: { server: 'http://myproxy.com:3128', username: 'usr', password: 'pwd' } }});Library
Section titled “Library”const browser = await chromium.launch({ proxy: { server: 'http://myproxy.com:3128', username: 'usr', password: 'pwd' }});也可以按上下文指定:
example.spec.ts
import { test, expect } from '@playwright/test';
test('should use custom proxy on a new context', async ({ browser }) => { const context = await browser.newContext({ proxy: { server: 'http://myproxy.com:3128', } }); const page = await context.newPage();
await context.close();});Library
Section titled “Library”const browser = await chromium.launch();const context = await browser.newContext({ proxy: { server: 'http://myproxy.com:3128' }});你可以监控所有 Request 和 Response:
// 订阅 'request' 和 'response' 事件。page.on('request', request => console.log('>>', request.method(), request.url()));page.on('response', response => console.log('<<', response.status(), response.url()));
await page.goto('https://example.com');或者,在按钮点击后使用 page.waitForResponse() 等待网络响应:
// 使用 glob URL 模式。注意没有 await。const responsePromise = page.waitForResponse('**/api/fetch_data');await page.getByText('Update').click();const response = await responsePromise;使用 page.waitForResponse() 等待 Response:
// 使用 RegExp。注意没有 await。const responsePromise = page.waitForResponse(/\.jpeg$/);await page.getByText('Update').click();const response = await responsePromise;
// 使用一个接收 Response 对象的谓词。注意没有 await。const responsePromise = page.waitForResponse(response => response.url().includes(token));await page.getByText('Update').click();const response = await responsePromise;await page.route('**/api/fetch_data', route => route.fulfill({ status: 200, body: testData,}));await page.goto('https://example.com');你可以通过在 Playwright 脚本中处理网络请求来模拟 API 端点。
使用 browserContext.route() 在整个浏览器上下文上设置路由,或使用 page.route() 在页面上设置路由。它会应用于弹出窗口和打开的链接。
await browserContext.route('**/api/login', route => route.fulfill({ status: 200, body: 'accept',}));await page.goto('https://example.com');// 删除标头await page.route('**/*', async route => { const headers = route.request().headers(); delete headers['X-Secret']; await route.continue({ headers });});
// 继续请求并将其作为 POST。await page.route('**/*', route => route.continue({ method: 'POST' }));你可以在修改后继续请求。上面的示例会从传出请求中移除一个 HTTP 标头。
你可以使用 page.route() 和 route.abort() 中止请求。
await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
// 基于请求类型中止await page.route('**/*', route => { return route.request().resourceType() === 'image' ? route.abort() : route.continue();});要修改响应,请使用 APIRequestContext 获取原始响应,然后将该响应传递给 route.fulfill()。你可以通过选项覆盖响应上的单个字段:
await page.route('**/title.html', async route => { // 获取原始响应。 const response = await route.fetch(); // 给 title 添加前缀。 let body = await response.text(); body = body.replace('<title>', '<title>My prefix:'); await route.fulfill({ // 传递响应中的所有字段。 response, // 覆盖响应正文。 body, // 强制内容类型为 html。 headers: { ...response.headers(), 'content-type': 'text/html' } });});Glob URL 模式
Section titled “Glob URL 模式”Playwright 在 page.route() 或 page.waitForResponse() 等网络拦截方法中使用简化的 glob 模式进行 URL 匹配。这些模式支持基本通配符:
- 星号:
- 单个
*匹配除/之外的任何字符 - 双星号
**匹配包括/在内的任何字符
- 单个
- 问号
?只匹配问号?。如果你想匹配任意字符,请改用*。 - 花括号
{}可用于匹配由逗号,分隔的选项列表 - 反斜杠
\可用于转义任何特殊字符(注意要转义反斜杠本身,请写成\\)
示例:
https://example.com/*.js匹配https://example.com/file.js,但不匹配https://example.com/path/file.jshttps://example.com/?page=1匹配https://example.com/?page=1,但不匹配https://example.com**/*.js同时匹配https://example.com/file.js和https://example.com/path/file.js**/*.{png,jpg,jpeg}匹配所有图片请求
重要说明:
- glob 模式必须匹配整个 URL,而不仅仅是其中一部分。
- 使用 glob 进行 URL 匹配时,请考虑完整的 URL 结构,包括协议和路径分隔符。
- 对于更复杂的匹配需求,请考虑使用
RegExp而不是 glob 模式。
WebSockets
Section titled “WebSockets”Playwright 开箱即支持 WebSockets 检查、模拟和修改。请参阅我们的 API 模拟指南,了解如何模拟 WebSockets。
每次创建 WebSocket 时,都会触发 page.on('websocket') 事件。此事件包含 WebSocket 实例,用于进一步检查 WebSocket 帧:
page.on('websocket', ws => { console.log(`WebSocket opened: ${ws.url()}>`); ws.on('framesent', event => console.log(event.payload)); ws.on('framereceived', event => console.log(event.payload)); ws.on('close', () => console.log('WebSocket closed'));});缺失网络事件和 Service Workers
Section titled “缺失网络事件和 Service Workers”Playwright 内置的 browserContext.route() 和 page.route() 允许你的测试原生地路由请求并执行模拟和拦截。
如果你正在使用 Playwright 原生的 browserContext.route() 和 page.route(),并且看起来缺失了网络事件,请通过将 serviceWorkers 设置为 'block' 来禁用 Service Workers。
可能是你正在使用 Mock Service Worker(MSW)这样的模拟工具。虽然此工具可以开箱即用地模拟响应,但它会添加自己的 Service Worker 来接管网络请求,因此会使这些请求对 browserContext.route() 和 page.route() 不可见。如果你同时对网络测试和模拟感兴趣,请考虑使用内置的 browserContext.route() 和 page.route() 进行响应模拟。
如果你感兴趣的不只是使用 Service Workers 进行测试和网络模拟,而是路由和监听 Service Workers 自身发出的请求,请参阅 本指南。