网络 (Network)
Playwright 提供 API 来 监控 和 修改 网络流量,包括 HTTP 和 HTTPS。页面执行的任何请求,包括 XHRs 和 fetch 请求,都可以被跟踪、修改和处理。
HTTP 身份验证
使用 browser.newContext([options]) 执行 HTTP 身份验证。
const context = await browser.newContext({
httpCredentials: {
username: 'bill',
password: 'pa55w0rd',
},
});
const page = await context.newPage();
await page.goto('https://example.com');
HTTP 代理
您可以配置页面通过 HTTP(S) 代理或 SOCKSv5 加载。代理可以为整个浏览器全局设置,也可以为每个浏览器上下文单独设置。
您可以选择为 HTTP(S) 代理指定用户名和密码,也可以指定要绕过代理的主机。
这是一个全局代理的示例:
const browser = await chromium.launch({
proxy: {
server: 'http://myproxy.com:3128',
username: 'usr',
password: 'pwd'
}
});
当为每个上下文单独指定代理时,Windows 上的 Chromium 需要一个提示,表明将设置代理。这是通过将非空代理服务器传递给浏览器本身来完成的。这是一个特定于上下文的代理示例:
const browser = await chromium.launch({
// Windows 上的 Chromium 需要浏览器代理选项。
proxy: { server: 'per-context' }
});
const context = await browser.newContext({
proxy: { server: 'http://myproxy.com:3128' }
})
网络事件
const { chromium, webkit, firefox } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
// 订阅 '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');
await browser.close();
})();
或者在按钮点击后使用 page.waitForResponse(urlOrPredicate[, options]) 等待网络响应:
// 使用 glob URL 模式
const [response] = await Promise.all([
page.waitForResponse('**/api/fetch_data'),
page.getByText('Update').click(),
]);
变体
使用 page.waitForResponse(urlOrPredicate[, options]) 等待 Response
// 使用 RegExp
const [response] = await Promise.all([
page.waitForResponse(/\.jpeg$/),
page.getByText('Update').click(),
]);
// 使用接受 Response 对象的谓词
const [response] = await Promise.all([
page.waitForResponse(response => response.url().includes(token)),
page.getByText('Update').click(),
]);
处理请求
await page.route('**/api/fetch_data', route => route.fulfill({
status: 200,
body: testData,
}));
await page.goto('https://example.com');
您可以通过处理 Playwright 脚本中的网络请求来 mock API 端点。
变体
使用 browserContext.route(url, handler[, options]) 在整个浏览器上下文上设置路由,或使用 page.route(url, handler[, options]) 在页面上设置路由。它将应用于弹出窗口和打开的链接。
await browserContext.route('**/api/login', route => route.fulfill({
status: 200,
body: 'accept',
}));
await page.goto('https://example.com');
修改请求
// 删除 header
await page.route('**/*', route => {
const headers = route.request().headers();
delete headers['X-Secret'];
route.continue({headers});
});
// 继续请求作为 POST。
await page.route('**/*', route => route.continue({method: 'POST'}));
您可以继续请求并进行修改。上面的示例从传出请求中删除了 HTTP header。
中止请求
您可以使用 page.route(url, handler[, options]) 和 route.abort([errorCode]) 中止请求。
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([options])。您可以通过选项覆盖响应上的各个字段:
await page.route('**/title.html', async route => {
// 获取原始响应。
const response = await page.request.fetch(route.request());
// 向标题添加前缀。
let body = await response.text();
body = body.replace('<title>', '<title>My prefix:');
route.fulfill({
// 传递响应中的所有字段。
response,
// 覆盖响应体。
body,
// 强制内容类型为 html。
headers: {
...response.headers(),
'content-type': 'text/html'
}
});
});
录制和重放请求
您可以将网络活动录制为 HTTP Archive 文件 (HAR)。稍后,此存档可用于 mock 网络请求的响应。您需要:
- 录制 HAR 文件。
- 将 HAR 文件与测试一起提交。
- 在测试中使用保存的 HAR 文件路由请求。
使用 CLI 录制 HAR
使用 Playwright CLI 打开浏览器并传递 --save-har 选项以生成 HAR 文件。或者,使用 --save-har-glob 仅保存您感兴趣的请求,例如 API 端点。如果 har 文件名以 .zip 结尾,则工件将作为单独的文件写入,并全部压缩到一个 zip 中。
# 将来自 example.com 的 API 请求保存为 "example.har" 存档。
npx playwright open --save-har=example.har --save-har-glob="**/api/**" https://example.com
使用脚本录制 HAR
或者,您可以以编程方式录制 HAR,而不是使用 CLI。在使用 browser.newContext([options]) 创建 BrowserContext 时传递 har 选项以创建存档。如果 har 文件名以 .zip 结尾,则工件将作为单独的文件写入,并全部压缩到一个 zip 中。
const context = await browser.newContext({
recordHar: { path: 'example.har', urlFilter: '**/api/**' }
});
// ... 执行操作 ...
// 关闭上下文以确保 HAR 保存到磁盘。
await context.close();
从 HAR 重放
使用 page.routeFromHAR(har[, options]) 或 browserContext.routeFromHAR(har[, options]) 从 HAR 文件提供匹配的响应。
// 从 HAR 重放 API 请求。
// 使用 HAR 中的匹配响应,
// 或者如果没有匹配项则中止请求。
await page.routeFromHAR('example.har');
HAR 重放严格匹配 URL 和 HTTP 方法。对于 POST 请求,它还严格匹配 POST 有效负载。如果多个录制匹配一个请求,则选择具有最多匹配 header 的那个。导致重定向的条目将自动跟随。
与录制时类似,如果给定的 HAR 文件名以 .zip 结尾,则将其视为包含 HAR 文件以及存储为单独条目的网络有效负载的存档。您也可以解压缩此存档,手动编辑有效负载或 HAR 日志,并指向解压缩的 har 文件。所有有效负载都将相对于文件系统上的解压缩 har 文件进行解析。
WebSockets
Playwright 开箱即用地支持 WebSockets 检查。每次创建 WebSocket 时,都会触发 page.on('websocket') 事件。此事件包含 WebSocket 实例,用于进一步检查 web socket 帧:
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
Playwright 的内置 browserContext.route(url, handler[, options]) 和 page.route(url, handler[, options]) 允许您的测试原生路由请求并执行 mock 和拦截。
- 如果您使用的是 Playwright 的原生 browserContext.route(url, handler[, options]) 和 page.route(url, handler[, options]),并且似乎缺少网络事件,请通过将
Browser.newContext.serviceWorkers设置为'block'来禁用 Service Workers。 - 您可能正在使用 mock 工具,例如 Mock Service Worker (MSW)。虽然此工具开箱即用地用于 mock 响应,但它添加了自己的 Service Worker 来接管网络请求,从而使它们对 browserContext.route(url, handler[, options]) 和 page.route(url, handler[, options]) 不可见。如果您对网络测试和 mock 都感兴趣,请考虑使用内置的 browserContext.route(url, handler[, options]) 和 page.route(url, handler[, options]) 进行 响应 mock。
- 如果您不仅对使用 Service Workers 进行测试和网络 mock 感兴趣,而且对路由和监听 Service Workers 本身发出的请求感兴趣,请参阅 此实验性功能。