如何修改Playwright请求追踪类:仅存在XHR请求时等待其完成
问题描述
我写了一个基于Playwright InflightRequests类的辅助方法,用来等待所有XHR请求完成,但在没有XHR请求的场景下,它还是会进入等待逻辑。我需要修改这个类,实现「有XHR请求就等全部完成,没有就直接跳过等待」的逻辑。另外想问问当前方法是否正确,以及有没有优化建议。
原代码
import {Page } from "@playwright/test"; export class InflightRequests { page: Page; requests: Set<unknown>; constructor(page:Page) { this.page = page; this.requests = new Set(); this._onStarted = this._onStarted.bind(this); this._onFinished = this._onFinished.bind(this); this.page.on('request', this._onStarted); this.page.on('requestfinished', this._onFinished); this.page.on('requestfailed', this._onFinished); } _onStarted(request:any) { this.requests.add(request); } _onFinished(request:any) { this.requests.delete(request); } inflightRequests() { return Array.from(this.requests); } dispose() { this.page.removeListener('request', this._onStarted); this.page.removeListener('requestfinished', this._onFinished); this.page.removeListener('requestfailed', this._onFinished); } }
当前测试使用方式
expect.poll(() => this.tracker.inflightRequests().filter((request: any) => request.resourceType() === 'xhr').length).toBe(0)
解决方案与优化建议
一、修改类实现目标逻辑
当前问题核心是expect.poll会强制轮询直到条件满足,哪怕初始状态就没有XHR请求。我们可以通过单独跟踪XHR请求、封装智能等待方法来解决这个问题:
修改后的代码
import { Page, Request } from "@playwright/test"; export class InflightRequests { private page: Page; private allRequests: Set<Request>; private xhrRequests: Set<Request>; // 单独维护XHR请求集合 constructor(page: Page) { this.page = page; this.allRequests = new Set(); this.xhrRequests = new Set(); this._onStarted = this._onStarted.bind(this); this._onFinished = this._onFinished.bind(this); this.page.on('request', this._onStarted); this.page.on('requestfinished', this._onFinished); this.page.on('requestfailed', this._onFinished); } private _onStarted(request: Request) { this.allRequests.add(request); // 仅跟踪XHR类型请求 if (request.resourceType() === 'xhr') { this.xhrRequests.add(request); } } private _onFinished(request: Request) { this.allRequests.delete(request); this.xhrRequests.delete(request); } // 获取当前活跃的XHR请求 inflightXhrRequests(): Request[] { return Array.from(this.xhrRequests); } // 智能等待方法:无XHR直接返回,有则等待全部完成 async waitForXhrComplete(timeout = 5000) { if (this.xhrRequests.size === 0) { return; } await expect.poll(() => this.inflightXhrRequests().length, { timeout }) .toBe(0); } inflightRequests(): Request[] { return Array.from(this.allRequests); } dispose() { this.page.removeListener('request', this._onStarted); this.page.removeListener('requestfinished', this._onFinished); this.page.removeListener('requestfailed', this._onFinished); } }
修改后的使用方式
无需再手动过滤和写poll逻辑,直接调用封装好的方法即可:
await this.tracker.waitForXhrComplete();
二、原代码正确性分析
原代码的基础逻辑是可行的:通过监听请求生命周期事件跟踪活跃请求,dispose方法也正确移除了监听器避免内存泄漏。但存在明显不足:
- 未区分请求类型,每次都需要在外部做filter,效率低且冗余
- 无法直接判断是否需要等待,导致无XHR场景下仍会触发轮询,浪费测试时间
三、额外优化建议
- 类型安全强化:把原代码中的
request: any替换为Playwright提供的Request类型,避免类型模糊导致的潜在错误 - 超时可配置:在等待方法中增加超时参数,防止极端情况下无限等待
- 扩展多类型支持:如果后续需要等待其他类型请求(如fetch、image),可以扩展方法支持自定义资源类型:
async waitForRequestsComplete(resourceType: string = 'xhr', timeout = 5000) { const targetRequests = Array.from(this.allRequests).filter(req => req.resourceType() === resourceType ); if (targetRequests.length === 0) return; await expect.poll(() => Array.from(this.allRequests).filter(req => req.resourceType() === resourceType).length, { timeout } ).toBe(0); } - 防止重复监听:可以在构造函数中添加校验,避免同一Page实例被多次初始化该类(可选)
内容的提问来源于stack exchange,提问作者YusufOzt
相关产品推荐
相关产品推荐

