资讯动态

Puppeteer BluetoothEmulation 接口详解:page.bluetooth 模拟蓝牙适配器与外设的完整指南

发布时间:2026/9/6 21:46:36 来源:尧图企业网站定制
Puppeteer BluetoothEmulation 接口详解page.bluetooth 模拟蓝牙适配器与外设的完整指南【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer本文基于 Puppeteer 的BluetoothEmulation接口文档展开系统讲解如何通过page.bluetooth模拟蓝牙适配器状态、注入预连接外设并结合 CDP 与 WebDriver BiDi 两套底层实现、以及仓库内的端到端测试说明其调用链路与使用限制。读完本文你将能够理解emulateAdapter、simulatePreconnectedPeripheral、disableEmulation三个方法的确切语义掌握配合waitForDevicePrompt完成 Web Bluetooth 设备请求自动化的完整流程并明确该功能在 Chromium 中的隔离边界与实验性约束。一、BluetoothEmulation 接口概述BluetoothEmulation是 Puppeteer 暴露的蓝牙模拟能力接口官方文档描述其为 Exposes the bluetooth emulation abilities暴露蓝牙模拟能力通过page.bluetooth属性访问。接口签名如下export interface BluetoothEmulation该接口定义了三个方法全部标记为Experimental实验性方法说明disableEmulation()禁用已模拟的蓝牙适配器。对应 Web Bluetooth 规范中的bluetooth.disableSimulation命令emulateAdapter(state, leSupported)模拟蓝牙适配器是所有蓝牙模拟操作的前提。对应规范中的bluetooth.simulateAdapter命令simulatePreconnectedPeripheral(preconnectedPeripheral)模拟一个预连接的蓝牙外设。对应规范中的bluetooth.simulatePreconnectedPeripheral命令由于三个方法均为实验性 API接口在源码中同样以experimental标注见 接口定义文件。这意味着 API 签名可能随版本演进调整生产环境使用时应关注版本变更。作用域限制浏览器上下文级而非页面级接口文档中最重要的 Remarks 指出Web Bluetooth specification requires the emulated adapters should be isolated per top-level navigable. However, at the moment Chromiums bluetooth emulation implementation is tight to the browser context, not the page. This means the bluetooth emulation exposed from different pages of the same browser context would interfere their states.即规范要求模拟器按顶层可导航对象页面隔离但当前 Chromium 的实现将模拟绑定在浏览器上下文browser context层面。同一个浏览器上下文中的不同页面会互相干扰蓝牙模拟状态。这一限制在 CDP 实现中有直接体现。从 CdpPage 构造函数 可以看到// Use browser contexts connection, as current Bluetooth emulation in Chromium is // implemented on the browser context level, and not tight to the specific tab. this.#cdpBluetoothEmulation new CdpBluetoothEmulation( this.#primaryTargetClient.connection(), );源码注释明确说明CDP 蓝牙模拟命令通过浏览器上下文级别的连接而非标签页会话发送。因此编写测试时若要避免状态串扰应使用独立浏览器上下文browser.createBrowserContext()隔离各用例。二、核心类型定义接口涉及三个公共类型全部定义在 packages/puppeteer-core/src/api/BluetoothEmulation.tsAdapterState适配器状态export type AdapterState absent | powered-off | powered-on;模拟的蓝牙适配器支持三种状态详见 AdapterState 文档取值含义absent设备不存在蓝牙适配器powered-off适配器存在但已关闭powered-on适配器存在且已开启进行蓝牙模拟的前提BluetoothManufacturerData厂商数据export interface BluetoothManufacturerData { /** * The company identifier, as defined by the Bluetooth SIG. */ key: number; /** * The manufacturer-specific data as a base64-encoded string. */ data: string; }key蓝牙 SIG 定义的公司标识符company identifierdata厂商特定数据必须以base64 编码字符串传入。类型定义见 BluetoothManufacturerData 文档。PreconnectedPeripheral预连接外设export interface PreconnectedPeripheral { address: string; name: string; manufacturerData: BluetoothManufacturerData[]; knownServiceUuids: string[]; }四个字段均有实际类型约束见 PreconnectedPeripheral 文档字段说明address外设蓝牙地址如09:09:09:09:09:09name外设名称如SOME_NAMEmanufacturerData厂商数据数组每项含key/dataknownServiceUuids已知服务 UUID 列表如[12345678-1234-5678-9abc-def123456789]三、三个方法的签名与参数emulateAdapter(state, leSupported)interface BluetoothEmulation { emulateAdapter(state: AdapterState, leSupported?: boolean): Promisevoid; }stateAdapterState期望的适配器状态leSupportedboolean可选标记该适配器是否支持低功耗蓝牙LE。从源码签名emulateAdapter(state: AdapterState, leSupported true)可见默认值为true返回Promisevoid。该方法是所有蓝牙模拟操作的前置条件必须先让适配器处于开启状态页面中的navigator.bluetoothAPI 才能发现设备。simulatePreconnectedPeripheral(preconnectedPeripheral)interface BluetoothEmulation { simulatePreconnectedPeripheral( preconnectedPeripheral: PreconnectedPeripheral, ): Promisevoid; }preconnectedPeripheralPreconnectedPeripheral要模拟的外设对象返回Promisevoid。调用后该外设会以已发现状态出现在页面的设备选择提示中可供 DeviceRequestPrompt.select() 选中。disableEmulation()interface BluetoothEmulation { disableEmulation(): Promisevoid; }无参数返回Promisevoid用于结束模拟、恢复浏览器真实蓝牙状态避免影响后续用例。四、完整使用示例文档给出的标准用法与源码 JSDoc 中example一致await page.bluetooth.emulateAdapter(powered-on); await page.bluetooth.simulatePreconnectedPeripheral({ address: 09:09:09:09:09:09, name: SOME_NAME, manufacturerData: [ { key: 17, data: AP8BAX8, }, ], knownServiceUuids: [12345678-1234-5678-9abc-def123456789], }); await page.bluetooth.disableEmulation();调用顺序即完整生命周期开启适配器 → 注入预连接外设 → 在页面中触发navigator.bluetooth.requestDevice()完成断言 → 关闭模拟。示例中key: 17与data: AP8BAX8分别演示了公司标识符数字和 base64 编码数据两种取值形态。五、底层实现CDP 与 WebDriver BiDi 双通道Puppeteer 对该接口提供了两套实现均位于packages/puppeteer-core/src/下CDP 实现CdpBluetoothEmulationCdpBluetoothEmulation 通过Connection直接发送 CDP 命令async emulateAdapter(state: AdapterState, leSupported true): Promisevoid { // Bluetooth spec requires overriding the existing adapter (step 6). From the CDP // perspective, it means disabling the emulation first. await this.#connection.send(BluetoothEmulation.disable); await this.#connection.send(BluetoothEmulation.enable, { state, leSupported, }); } async disableEmulation(): Promisevoid { await this.#connection.send(BluetoothEmulation.disable); } async simulatePreconnectedPeripheral( preconnectedPeripheral: PreconnectedPeripheral, ): Promisevoid { await this.#connection.send( BluetoothEmulation.simulatePreconnectedPeripheral, preconnectedPeripheral, ); }两个值得注意的实现细节emulateAdapter会先发送BluetoothEmulation.disable再发送enable。源码注释解释这是规范要求的行为——Web Bluetooth 规范的simulateAdapter命令第 6 步要求覆盖override已存在的适配器因此在 CDP 层面需要先禁用再启用。这也意味着连续调用emulateAdapter是幂等安全的。构造函数接收的是浏览器上下文级连接见上文CdpPage中的构造位置这解释了文档 Remarks 中上下文级隔离的限制来源。BiDi 实现BidiBluetoothEmulationBidiBluetoothEmulation 面向 WebDriver BiDi 协议所有命令都显式携带context上下文 ID将模拟作用域限定在指定浏览器上下文async emulateAdapter(state: AdapterState, leSupported true): Promisevoid { await this.#session.send(bluetooth.simulateAdapter, { context: this.#contextId, state, leSupported, }); }BiDi 版simulatePreconnectedPeripheral会显式解构外设对象将address、name、manufacturerData、knownServiceUuids逐字段展平后发送bluetooth.simulatePreconnectedPeripheral命令。对比两套实现可以看到CDP 版整体透传preconnectedPeripheralBiDi 版按协议 schema 逐字段映射——接口抽象层Page.bluetooth 抽象 getter保证了用户代码在两种协议间无需改动。六、端到端实战配合 waitForDevicePrompt 完成设备选择仓库测试 test/src/bluetooth-emulation.test.ts 展示了模拟蓝牙后与页面交互的完整闭环其中包含几个文档示例未覆盖的关键前提1. 浏览器启动参数。测试通过setupSeparateTestBrowserHooks指定args: [ --enable-featuresWebBluetoothNewPermissionsBackend, --enable-featuresWebBluetooth, ], acceptInsecureCerts: true,即需要WebBluetoothNewPermissionsBackend与WebBluetooth两个 feature flag并且页面必须运行在安全上下文测试使用httpsServer.EMPTY_PAGE。2. 标准交互流程。以选择设备用例为例await page.goto(httpsServer.EMPTY_PAGE); await page.bluetooth.emulateAdapter(powered-on); await page.bluetooth.simulatePreconnectedPeripheral(SIMULATED_PERIPHERAL); const devicePromptPromise page.waitForDevicePrompt(); const navigatorRequestDevicePromise page.evaluate( triggerBluetoothDevicePrompt, // 内部调用 navigator.bluetooth.requestDevice ); // 等待设备提示出现然后选中模拟设备 const devicePrompt await devicePromptPromise; await devicePrompt.select(devicePrompt.devices[0]!); // 断言requestDevice 解析为模拟外设名称 expect(await navigatorRequestDevicePromise).toEqual(DEVICE_NAME);其中DeviceRequestPrompt由 page.waitForDevicePrompt() 返回定义见 DeviceRequestPrompt 文档核心成员有devicesreadonly当前可选设备列表select(device)选中提示列表中的某个设备cancel()取消提示测试用例验证了取消后requestDevice会 rejectwaitForDevice(filter, options)等待并解析第一个匹配过滤条件的设备。典型写法是将waitForDevicePrompt()与触发请求的动作用Promise.all配对例如点击页面上的连接蓝牙按钮const [devicePrompt] Promise.all([ page.waitForDevicePrompt(), page.click(#connect-bluetooth), ]); await devicePrompt.select( await devicePrompt.waitForDevice(({name}) name.includes(My Device)), );Page类对waitForDevicePrompt的注释特别提醒该方法必须在设备请求发起之前调用否则无法返回提示对象。3. 模拟数据的复用。测试中的SIMULATED_PERIPHERAL常量与文档示例逐字段一致地址09:09:09:09:09:09、名称SOME_NAME、厂商数据{key: 17, data: AP8BAX8}、服务 UUID12345678-1234-5678-9abc-def123456789可直接作为复制模板。七、小结BluetoothEmulation接口以三个实验性方法提供了完整的 Web Bluetooth 模拟生命周期用emulateAdapter设定absent/powered-off/powered-on三态适配器LE 支持默认开启用simulatePreconnectedPeripheral注入含地址、名称、base64 厂商数据与服务 UUID 的虚拟外设再用disableEmulation收尾。理解它需要抓住三层事实接口层定义于 packages/puppeteer-core/src/api/BluetoothEmulation.ts实现层由 CDP 通道先 disable 后 enable 覆盖旧适配器与 BiDi 通道按 context 限定作用域分别落地使用层则需配合waitForDevicePrompt与浏览器 feature flag 完成端到端断言。同时务必记住文档强调的约束——Chromium 当前的模拟绑定在浏览器上下文而非页面同上下文多页面间的模拟状态会互相干扰测试编排时宜用独立上下文隔离。【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价