
Playwright WebSocketFrame 类详解捕获并解析页面 WebSocket 帧的 .NET 与 Java API【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwrightPlaywright 的WebSocketFrame类自 v1.9 起引入仅面向 .NET 与 Java 语言绑定用于表示页面内 WebSocket 连接上收发的一帧数据通过text或binary方法按帧类型分别取回文本或二进制负载。本篇结合当前仓库中的 API 文档、客户端实现源码与真实测试用例讲清楚WebSocketFrame在 WebSocket 事件模型中的位置、它与 JS/Python 版本 API 的差异以及如何围绕它写出可复现、可等待的 WebSocket 测试。WebSocketFrame 在 Playwright WebSocket 模型中的位置先回顾文档对该类的定义class-websocketframe.mdThe WebSocketFrame class represents frames sent over WebSocket connections in the page. Frame payload is returned by eithertextorbinarymethod depending on its type.也就是说WebSocketFrame是对**单个 WebSocket 帧frame**的封装每一帧要么是文本帧要么是二进制帧两类负载只能通过对应的方法获取。它不是连接本身而是 WebSocket 事件回调中交付出来的数据载体。为什么 .NET/Java 需要这个独立类型关键在于各语言绑定对frameReceived/frameSent事件参数的建模方式不同。从 class-websocket.md 可以看到同一事件在三类语言中的三种签名事件JS / 默认Python.NET / JavaframeReceivedargument: Object含payload字段string或Bufferargument: string\|Bufferpayload 直接作为参数argument: WebSocketFrameframeSentargument: Object含payload字段argument: string\|Bufferargument: WebSocketFrame也就是说JS中事件参数是一个带payload键的对象payload的运行时类型就是帧类型——文本帧为字符串、二进制帧为BufferPython中事件参数直接是 payload 本身.NET / Java中事件参数则是WebSocketFrame对象需要用text/binary方法显式取回负载。文档头部langs: csharp, java元数据明确了该类的适用语言范围JS 和 Python 绑定中并不存在这个类型。这种设计的实际价值在于强类型语言里你可以把WebSocketFrame作为方法参数、传给谓词函数或做类型化断言而不必在每个回调里重复判断 payload 到底是 string 还是 byte[]。两个核心方法binary 与 text文档定义了WebSocketFrame仅有的两个公开方法二者都是按帧类型返回负载且对类型不匹配的读取返回null文档返回类型均写作[null]|[Buffer]/[null]|[string]method: WebSocketFrame.binary自 v1.9 起可用返回类型Buffer.NET/Java 中为对应的字节数组类型或null语义返回二进制负载。当该帧是二进制帧时返回完整二进制数据否则返回null。method: WebSocketFrame.text自 v1.9 起可用返回类型string或null语义返回文本负载。当该帧是文本帧时返回字符串内容否则返回null。结合返回类型可以推断其使用模式回调里先调用一个方法若为null说明该帧是另一类型可再尝试另一个方法——这就是按类型分派的惯用法与 JS 端通过typeof payload判断字符串/Buffer 的做法一一对应。帧从浏览器到事件参数源码级链路WebSocketFrame背后对应的是 Playwright 内部 WebSocket 通道的frameSent/frameReceived事件。在 network.ts 的WebSocket客户端实现中可以看到事件的分派逻辑this._channel.on(frameSent, event { if (event.opcode 1) this.emit(Events.WebSocket.FrameSent, { payload: event.data }); else if (event.opcode 2) this.emit(Events.WebSocket.FrameSent, { payload: Buffer.from(event.data, base64) }); }); this._channel.on(frameReceived, event { if (event.opcode 1) this.emit(Events.WebSocket.FrameReceived, { payload: event.data }); else if (event.opcode 2) this.emit(Events.WebSocket.FrameReceived, { payload: Buffer.from(event.data, base64) }); });这里有几个值得注意的实现事实opcode 决定帧类型驱动层上报的原始事件携带opcode1为文本帧、2为二进制帧。JS 端据此决定 payload 是字符串还是Buffer二进制数据在通道中以 base64 编码传输见 channels.d.ts 中WebSocketFrameSentEvent/WebSocketFrameReceivedEvent类型定义。.NET/Java 绑定正是在这一层之上把{ payload }包装成WebSocketFrametext/binary的语义即由 opcode 分派结果决定控制帧被过滤源码只对 opcode 1 和 2 发射事件ping/pong/close 等控制帧opcode 8-12不会以帧事件形式到达用户回调——因此你不会收到close 帧的frameReceived事件连接关闭统一走close事件帧与连接状态分离close事件单独触发并置位isClosed()帧事件与生命周期事件互不干扰。再看驱动端各浏览器如何把 CDP/Playwright 协议事件喂进来例如 Chromium 在 crNetworkManager.ts 中监听Network.webSocketFrameSentFirefox 在 ffPage.ts 中监听Page.webSocketFrameSent/Page.webSocketFrameReceived统一汇聚到 frames.ts 的onWebSocketFrameSent(requestId, opcode, data, wallTimeMs)。这条链路解释了文档中WebSocketFrame与WebSocket事件在三种浏览器上行为一致的原因opcode payload 的语义在浏览器适配层就已归一化。事件流全景WebSocket 上还有哪些事件理解WebSocketFrame离不开它所在的完整事件模型。class-websocket.md 定义了以下事件与方法closeWebSocket 关闭时触发参数为WebSocket本身frameReceived收到一帧时触发参数为WebSocketFrame.NET/Java**frameSent发出一帧时触发参数为WebSocketFrame.NET/JavasocketError连接出现错误时触发参数为错误信息字符串isClosed()判断连接是否已关闭url()返回该 WebSocket 的 URLwaitForEvent(event, optionsOrPredicate)JS/Python等待任意事件支持predicate、timeout默认 0 表示不限时默认值受actionTimeout、setDefaultTimeout影响、signal选项。值得强调的是waitForEvent的两个拒绝语义见 network.ts 与测试验证等待期间若触发socketError会以Socket error拒绝若触发close会以Socket closed拒绝页面关闭同样会使等待失败。这些语义在 web-socket.spec.ts 中有对应测试用例逐条覆盖例如should reject waitForEvent on socket close断言错误信息包含Socket closed。在 Java 绑定中还存在两个专门围绕WebSocketFrame的便捷等待方法均自 v1.10 起文档见 class-websocket.mdwaitForFrameReceived(predicate, options)等待接收到一帧可选谓词形如predicate(WebSocketFrame): boolean谓词返回真值时结束等待在帧到达前若 WebSocket 或 Page 关闭会抛错支持timeout、signal选项。waitForFrameSent(predicate, options)语义相同针对发出帧。这两个方法本质是frameReceived/frameSent事件的类型化语法糖是 .NET/Java 中最高频的WebSocketFrame使用场景。实战围绕 WebSocketFrame 的典型测试写法仓库自带测试web-socket.spec.ts展示了帧事件的完整闭环这里把 JS 写法与 .NET/Java 的WebSocketFrame写法对照给出。场景一验证发送与接收的帧内容JS 侧的原始测试测试文件第 53-74 行监听帧事件并断言日志序列server.onceWebSocketConnection(ws { ws.on(message, () ws.send(incoming)); }); page.on(websocket, ws { ws.on(frameSent, d log.push(sent d.payload )); ws.on(frameReceived, d log.push(received d.payload )); ws.on(close, () { log.push(close); }); }); await page.evaluate(host { const ws new WebSocket(ws:// host /ws); ws.addEventListener(open, () ws.send(outgoing)); ws.addEventListener(message, () ws.close()); }, server.HOST); // 期望日志[open, sentoutgoing, receivedincoming, close]对应到 .NET/Java帧回调的参数即WebSocketFrame取回负载调用text// C#.NET 绑定 page.WebSocket (sender, webSocket) { webSocket.FrameSent (sender2, WebSocketFrame) log.Add($sent{WebSocketFrame.text}); webSocket.FrameReceived (sender2, WebSocketFrame) log.Add($received{WebSocketFrame.text}); };// Java 绑定 page.onWebSocket(ws - { ws.onFrameSent(frame - log.add(sent frame.text() )); ws.onFrameReceived(frame - log.add(received frame.text() )); });场景二二进制帧断言仓库测试should emit binary frame eventsweb-socket.spec.ts中页面同时发送一个文本帧和一个 5 字节二进制帧测试断言sent[0]为字符串text、sent[1]逐字节等于0..4。这正是.NET/Java中text与binary方法分工的用武之地ws.onFrameSent(frame - { if (frame.text() ! null) { assertThat(frame.text()).isEqualTo(text); } else { byte[] data frame.binary(); // 二进制帧 assertThat(data).isEqualTo(new byte[]{0, 1, 2, 3, 4}); } });要点对二进制帧调用text得到null对文本帧调用binary得到null——以哪个方法返回非 null作为帧类型判据即可这与 JS 端payload的运行时类型判断完全等价。场景三等待特定帧Java 的 waitForFrameReceived// 等待服务端推送的第一帧并断言其内容 WebSocketFrame frame ws.waitForFrameReceived(); assertThat(frame.text()).isEqualTo(incoming); // 带谓词只等待 payload 包含关键字的帧 WebSocketFrame frame2 ws.waitForFrameReceived(f - f.text() ! null f.text().contains(heartbeat));如果等待期间连接被对端关闭waitForFrameReceived会抛错而不是无限挂起这与 web-socket.spec.ts 中should reject waitForEvent on socket close、should reject waitForEvent on page close两个用例验证的拒绝语义一致。与 WebSocketRoute 的分工WebSocket 文档在开头提示若需要拦截或修改WebSocket 帧应使用 WebSocketRoute。二者的边界很清晰WebSocket/WebSocketFrame观察侧。拿到页面上真实收发的帧用于断言与日志属于只读语义WebSocketRoute干预侧。可以在帧到达客户端或发出前进行 fulfill / continue 等处理改变实际传输的数据。测试实践中常见组合是默认用WebSocketFrame断言真实流量只在需要 mock 或篡改协议数据时引入WebSocketRoute其拦截实现可见 network.ts 中的WebSocketRoute客户端类。适用前提与限制语言范围WebSocketFrame仅在 .NET 与 Java 绑定中存在文档元数据langs: csharp, javaJS 用{ payload }对象、Python 用裸 payload不要把 .NET/Java 的frame.text()写法照搬到 JS 测试中版本范围WebSocketFrame自 v1.9 引入Java 的waitForFrameReceived/waitForFrameSent便捷方法自 v1.10 引入当前仓库文档即按此版本标注帧类型互斥text与binary对同一帧互斥读错类型返回null回调中应做非空判断控制帧不可见从客户端实现看仅 opcode 1/2 的帧会发射事件close/ping/pong 不产生WebSocketFrame关闭状态请用close事件与isClosed()判断等待类 API 的拒绝行为等待期间发生 socket error / socket close / page close 都会使等待失败编写异步断言时按此预期处理超时与异常。小结WebSocketFrame是 Playwright 面向 .NET/Java 的 WebSocket 帧类型它通过text/binary两个方法按 opcode 分派暴露帧负载配合WebSocket的frameSent/frameReceived事件、waitForFrameReceived/waitForFrameSent等待方法构成强类型语言下完整的 WebSocket 流量断言能力。实现层面opcode 1/2 的分派发生在客户端通道层network.ts各浏览器适配器Chromium/Firefox/WebKit负责在驱动侧归一化原始帧事件其行为在 tests/library/web-socket.spec.ts 中有系统性的用例覆盖可直接作为行为契约参考。【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考