news 2026/9/15 13:54:25

WebdriverIO Bidi 实战指南:实时浏览器日志捕获与预加载脚本注入

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WebdriverIO Bidi 实战指南:实时浏览器日志捕获与预加载脚本注入

WebdriverIO Bidi 实战指南:实时浏览器日志捕获与预加载脚本注入

【免费下载链接】webdriverioNext-gen browser and mobile automation test framework for Node.js项目地址: https://gitcode.com/GitHub_Trending/we/webdriverio

本文基于 WebdriverIO 仓库自带的 examples/bidi 示例,讲解如何通过 WebDriver Bidi 协议在浏览器自动化中实时订阅控制台日志(log.entryAdded)与网络响应事件(network.responseCompleted),并向页面注入预加载脚本(scriptAddPreloadScript)。读完本文,你将掌握webSocketUrl: true的启用原理、sessionSubscribe/browser.on()的事件订阅模式,以及如何把这些能力复用到自己的测试或脚本中。

什么是 WebDriver Bidi,为什么用webSocketUrl

传统 WebDriver 协议基于 HTTP 请求/响应,客户端与浏览器之间只能一问一答,无法实时获取浏览器内部事件(如控制台输出、网络请求状态)。WebDriver Bidi(双向协议)则在浏览器与客户端之间建立一条 WebSocket 长连接,让浏览器可以主动把事件推送给客户端,从而实现真正的实时监听与双向控制。

在 WebdriverIO 中开启 Bidi 非常简单——只需要在创建会话时把webSocketUrl置为true

const browser = await remote({ logLevel: 'error', capabilities: { webSocketUrl: true, browserName: 'firefox' } })

从源码层面看,webSocketUrl是否生效直接决定了 WebdriverIO 是否注册整套 Bidi 会话管理能力。在 packages/webdriverio/src/session/index.ts 中,registerSessionManager只有在typeof instance.capabilities.webSocketUrl === 'string'时,才会初始化 Polyfill、ShadowRoot、Network、Dialog 等基于 Bidi 的会话管理器:

/** * only register session manager for sessions that appear to support * WebDriver Bidi */ if (typeof instance.capabilities.webSocketUrl === 'string') { initializationPromises.push( getPolyfillManager(instance).initialize(), getShadowRootManager(instance).initialize(), getNetworkManager(instance).initialize(), getDialogManager(instance).initialize() ) }

也就是说,webSocketUrl一旦开启,WebdriverIO 会自动通过 Bidi 事件来驱动内部的网络拦截(mock命令)、Shadow DOM 穿透等功能——示例中的事件订阅只是你能够直接使用的一小部分能力。

环境准备与运行方式

examples/bidi目录下的示例脚本可以直接用 Node 运行(示例脚本均为 ESM 格式,且依赖仓库内的webdriverio包)。示例包 examples/bidi/package.json 声明了"type": "module",并在依赖中引入"webdriverio": "workspace:*"

根据仓库根目录 examples/README.md 的说明,运行这些示例的标准流程是:先克隆仓库、安装依赖并完成构建,然后进入对应目录用 Node 执行脚本:

git clone git@github.com:webdriverio/webdriverio.git cd ./webdriverio pnpm install pnpm run setup # 进入 Bidi 示例目录运行 cd ./examples/bidi node logging.js node scripting.js

示例脚本 examples/bidi/logging.js 与 examples/bidi/scripting.js 都要求本机存在对应的浏览器(logging.js中第一个会话用 Firefox、第二个会话与scripting.js用 Chrome),运行时浏览器会真实弹出。

提示:仓库自带的示例不包含dev-dependencies,如果你要在自己的工程里复刻这些能力,建议基于 website/docs/GettingStarted.md 搭建一个标准 WebdriverIO 项目,再把示例中的remote调用集成进去。

实时捕获浏览器日志

示例的第一种典型场景是实时监听浏览器产生的日志事件,包含两类:控制台日志网络事件

订阅控制台日志log.entryAdded

import { remote } from 'webdriverio' const browser = await remote({ logLevel: 'error', capabilities: { webSocketUrl: true, browserName: 'firefox' } }) await browser.sessionSubscribe({ events: ['log.entryAdded'] }) browser.on('log.entryAdded', (entryAdded) => console.log(JSON.stringify(entryAdded, null, 4))) await browser.execute(() => console.log('Hello Bidi')) await browser.deleteSession()

这段代码的关键步骤:

  1. 开启 Bidicapabilities.webSocketUrl: true让 WebdriverIO 通过 WebSocket 连接浏览器;
  2. 订阅事件sessionSubscribe({ events: ['log.entryAdded'] })告诉浏览器只推送日志新增类事件(log.entryAdded对应 Bidi 协议中的Log.entryAdded);
  3. 监听事件browser.on('log.entryAdded', ...)为事件注册回调,回调参数是结构化的日志条目对象,包含日志来源、级别、文本等字段;
  4. 触发日志browser.execute(() => console.log('Hello Bidi'))在页面上下文执行一段脚本触发console.log,随后该日志事件会通过 WebSocket 推送到客户端并打印出来;
  5. 清理会话browser.deleteSession()关闭会话,避免残留浏览器进程。

订阅网络响应事件network.responseCompleted

网络事件的订阅方式与日志完全一致,只是事件名不同:

import { remote } from 'webdriverio' const browser = await remote({ logLevel: 'error', capabilities: { webSocketUrl: true, browserName: 'chrome' } }) await browser.sessionSubscribe({ events: ['network.responseCompleted'] }) browser.on('network.responseCompleted', (networkResponse) => console.log(JSON.stringify(networkResponse, null, 4))) await browser.url('https://webdriver.io') await browser.deleteSession()

这里在sessionSubscribe中订阅network.responseCompleted,然后用browser.on('network.responseCompleted', ...)监听。回调中拿到的networkResponse对象包含响应状态、URL、响应体、时间戳等字段。随后browser.url('https://webdriver.io')导航到页面,页面加载过程中的每一个已完成响应都会实时推送并打印。

注意:sessionSubscribe是可以多次调用的,examples/bidi/logging.js 中就把日志与网络两类事件放在同一个会话里先后订阅,互不冲突:

await browser.sessionSubscribe({ events: ['log.entryAdded'] }) browser.on('log.entryAdded', (logEntry) => console.log(JSON.stringify(logEntry, null, 4))) await browser.sessionSubscribe({ events: ['network.responseCompleted'] }) browser.on('network.responseCompleted', (networkResponse) => console.log(JSON.stringify(networkResponse, null, 4)))

事件订阅的底层实现

sessionSubscribe是 WebdriverIO 通过 Bidi 协议暴露出来的标准命令,其协议定义位于 packages/wdio-protocols/src/protocols/webdriverBidi.ts。WebdriverIO 内部自己也在使用同样的机制:在 packages/webdriverio/src/session/networkManager.ts 中,网络管理器初始化时会一次性订阅network.responseCompletednetwork.beforeRequestSentnetwork.fetchErrorbrowsingContext.navigationStarted等事件,并把这些事件关联到内部监听器,进而支撑起 WebdriverIO 的mock网络拦截能力:

this.#initialize = this.#browser.sessionSubscribe({ events: [ 'browsingContext.navigationStarted', 'network.beforeRequestSent', 'network.responseCompleted', ... ] }) this.#browser.on('network.responseCompleted', this.#responseCompletedListener) this.#browser.on('network.beforeRequestSent', this.#beforeRequestSentListener)

可以推断,sessionSubscribe本质上就是向浏览器发送 Bidi 命令并返回订阅确认,而browser.on()则是 WebdriverIO 对 WebSocket 事件流的封装。你在示例中使用的 API 与 WebdriverIO 内部使用的完全是同一套通道,因此它既稳定又具备完整的协议能力。

通过预加载脚本注入页面逻辑

Bidi 的另一个实用能力是在任意页面加载前注入脚本。所谓“预加载脚本”(preload script),是指注册到浏览器会话中、在目标页面脚本执行之前就被注入并运行的脚本,适用于注入全局变量、拦截 API、埋点等场景。

import { remote } from 'webdriverio' const browser = await remote({ logLevel: 'error', capabilities: { webSocketUrl: true, browserName: 'chrome' } }) await browser.scriptAddPreloadScript({ functionDeclaration: `() => { window.bar='foo'; }` }) await browser.url('https://webdriver.io') await browser.pause(2000) console.log(await browser.execute(() => window.bar)) await browser.deleteSession()

这段代码的执行流程:

  1. scriptAddPreloadScript注册一个匿名函数,其函数体window.bar='foo'会在每个新页面的脚本环境初始化时执行;
  2. browser.url(...)打开目标页面,预加载脚本自动注入;
  3. browser.pause(2000)等待页面完成加载;
  4. browser.execute(() => window.bar)在页面上下文读取window.bar,打印出foo,从而验证预加载脚本确实在页面加载前生效;
  5. browser.deleteSession()结束会话。

scriptAddPreloadScript同样定义在 packages/wdio-protocols/src/protocols/webdriverBidi.ts。除了functionDeclaration(函数体声明)之外,它还支持通过arguments参数向脚本传入额外的值(默认是空数组),这对于注入可配置的初始化数据非常有用。WebdriverIO 官方的addInitScript命令(见 packages/webdriverio/src/commands/browser/addInitScript.ts)底层也是基于scriptAddPreloadScript封装的,可以理解为 Bidi 预加载脚本能力的命令化入口。

注意事项与最佳实践

  • 浏览器支持:Bidi 协议要求浏览器具备相应的 WebDriver BiDi 实现。示例中 Firefox 与 Chrome 均可用,若使用不支持 Bidi 的浏览器或驱动版本,sessionSubscribe等命令可能不可用。从 packages/webdriverio/src/session/index.ts 的实现看,会话管理器仅在capabilities.webSocketUrl为字符串时才注册,说明该能力由浏览器端能力决定。
  • 事件名与回调sessionSubscribe订阅的事件名与browser.on监听的事件名必须完全一致(如log.entryAddednetwork.responseCompleted),回调参数是结构化的 Bidi 事件对象,建议用JSON.stringify(entry, null, 4)打印观察其字段。
  • 会话生命周期:示例中都显式调用browser.deleteSession()清理会话,在真实测试框架中应把订阅放在before/after钩子中,避免事件泄漏或浏览器进程残留。
  • 订阅尽量精准:只订阅自己关心的事件(示例中为log.entryAdded/network.responseCompleted),减少不必要的事件流量,保持回调逻辑轻量,以免阻塞 WebSocket 事件处理。

借助 WebdriverIO 的 Bidi 支持,你可以在不额外安装 Chrome DevTools 相关依赖的前提下,用统一的remoteAPI 完成实时日志采集、网络监听与脚本注入。如需进一步探索,可继续阅读 packages/webdriverio/src/session/networkManager.ts 了解网络拦截的完整实现,或直接运行仓库中的 examples/bidi/logging.js 与 examples/bidi/scripting.js 体验真实效果。

【免费下载链接】webdriverioNext-gen browser and mobile automation test framework for Node.js项目地址: https://gitcode.com/GitHub_Trending/we/webdriverio

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 13:54:10

CMake报错CMakeTestCCompiler.cmake broken?从原理到实践彻底排查

你正打算好好编译一个项目,或者刚踩进 CMake 这个坑,控制台里出现了这么一条报错:CMake Error at .../CMakeTestCCompiler.cmake:52 (message),再往上翻,还有一句扎心的-- Check for working C compiler: ... -- broke…

作者头像 李华
网站建设 2026/9/15 13:53:17

DocsGPT 的 Artifact 工具怎么生成并迭代编辑 PPT、Word 和 PDF 文档

DocsGPT 的 Artifact 工具怎么生成并迭代编辑 PPT、Word 和 PDF 文档 【免费下载链接】DocsGPT Private AI platform for agents, assistants and enterprise search. Built-in Agent Builder, Deep research, Document analysis, Multi-model support, and API connectivity f…

作者头像 李华
网站建设 2026/9/15 13:51:27

OpenHarmony上跑通Flutter:环境搭建完整实战指南

宠辱不惊地讲,在 OpenHarmony 生态还没完全“傻瓜化”的今天,能把 Flutter 和 OHOS 的这套工具链从零拼起来,本身就是一场跟版本、签名、构建缓存斗智斗勇的过程。我这次踩的版本是 oh-3.44.9-dev ,算是 Flutter 对 OpenHarmony…

作者头像 李华
网站建设 2026/9/15 13:51:13

Flink StandAlone模式作业提交全流程:打包、提交与排坑指南

你有没有遇到过这种情况:本地开发环境里跑得好好的Flink任务,一拿到测试环境就各种幺蛾子,要么类冲突,要么提交后TaskManager一直连不上,要么作业运行几分钟就内存溢出。折腾一圈下来,发现大多数问题都不是…

作者头像 李华
网站建设 2026/9/15 13:49:57

Unity移动端录屏实战:Natcorder实现录屏、拍照与GIF生成

前几天有个朋友在群里问:Unity 游戏要上线应用商店,商店需要演示视频,有没有能在移动端直接录屏、还能顺手生成 GIF 的插件?我第一反应就是 Natcorder。这个插件我用了快两年,录屏、拍照、GIF 三件事全都能干&#xff…

作者头像 李华