尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Puppeteer DeviceRequestPromptDevice 接口详解:Web Bluetooth 设备选择弹窗中的设备模型

Puppeteer DeviceRequestPromptDevice 接口详解:Web Bluetooth 设备选择弹窗中的设备模型 Puppeteer DeviceRequestPromptDevice 接口详解Web Bluetooth 设备选择弹窗中的设备模型【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteerDeviceRequestPromptDevice是 Puppeteer 中描述设备选择弹窗Device Request Prompt内单个候选设备的接口。当页面通过 Web Bluetooth如navigator.bluetooth.requestDevice()请求连接蓝牙设备时Puppeteer 可借助该接口描述的设备信息来识别、过滤并自动选中目标设备从而在自动化测试与脚本中完全接管浏览器原生的设备选择交互。读完本文你将掌握该接口两个字段的语义、其与DeviceRequestPrompt/Page.waitForDevicePrompt()的协作方式以及 CDP 与 WebDriver BiDi 两套协议下的底层实现差异。背景Puppeteer 如何接管设备选择弹窗真实浏览器中网页调用 Web Bluetooth 等 Web API 请求设备时会弹出系统级/浏览器级的设备选择界面这一步无法用普通的click、type模拟。Puppeteer 通过监听浏览器协议中的设备请求事件把弹窗抽象为DeviceRequestPrompt对象把弹窗里出现的每一个候选设备抽象为本文主角DeviceRequestPromptDevice从而允许脚本以编程方式完成浏览设备列表 → 选择设备 / 取消整个交互闭环。接口定义与字段语义依据 官方 API 文档 与 源码声明该接口签名如下export interface DeviceRequestPromptDevice { id: string; name: string; }它只包含两个字符串字段语义如下表属性类型说明idstring设备在本次请求弹窗期间的设备 IDDevice id during a prompt。注意它是本次弹窗期间的标识而非蓝牙 MAC 等永久标识仅用于在本次 prompt 生命周期内唯一定位候选设备namestring设备名称即设备在弹窗中展示给用户的名字Device name as it appears in a prompt例如某耳机的广播名称id与name都是只读的纯数据字段接口本身不提供任何方法。对象的增删改查、选择等行为全部由持有设备列表的 DeviceRequestPrompt 类 完成。DeviceRequestPromptDevice 与 DeviceRequestPrompt 的协作关系两者定义在同一个模块中。抽象类 DeviceRequestPrompt 是承载设备列表与交互行为的容器只读属性devices: DeviceRequestPromptDevice[]—— 当前可选择设备列表。从源码结构看该列表随协议事件实时增量追加不会因设备离开而自动移除waitForDevice(filter, options)—— 等待并返回第一个满足过滤条件的DeviceRequestPromptDeviceselect(device)—— 在弹窗中选择某个设备对象cancel()—— 取消本次弹窗。也就是说DeviceRequestPromptDevice是数据单元DeviceRequestPrompt是围绕这批数据的操作句柄二者共同支撑处理设备请求弹窗这一能力。如何获取设备对象Page.waitForDevicePrompt() 与官方示例DeviceRequestPrompt实例其内部持有DeviceRequestPromptDevice列表通过 Page.waitForDevicePrompt() 获取。方法签名abstract waitForDevicePrompt( options?: WaitTimeoutOptions, ): PromiseDeviceRequestPrompt;官方在 DeviceRequestPrompt 文档 与 waitForDevicePrompt 文档 中给出了一致的完整示例它演示了从触发请求到过滤并选中设备的全过程const [devicePrompt] Promise.all([ page.waitForDevicePrompt(), page.click(#connect-bluetooth), ]); await devicePrompt.select( await devicePrompt.waitForDevice(({name}) name.includes(My Device)), );解读这段官方范式page.waitForDevicePrompt()与触发动作此处是点击某个连接蓝牙按钮通过Promise.all并发执行因为协议要求先注册等待再触发请求。方法文档还特别用:::caution警示必须在设备请求发生之前调用它不会返回一个已经处于活动状态的弹窗见 文档原文devicePrompt.waitForDevice(({name) name.includes(My Device))的解构入参就是DeviceRequestPromptDevice这里用字段name做模糊匹配拿到匹配的设备对象后调用select(device)完成选择返回的 Promise 在目标设备出现后 resolve。如果页面可能先后弹出多次且希望仅用name前缀判断而忽略固件/系统附加的后缀可以直接把多个相关对象放入数组用Array.prototype.some组合成一次过滤这是对filter回调自由扩展的常见写法。CDP 实现DeviceAccess 域与增量设备列表ChromeCDP路径的实现位于 packages/puppeteer-core/src/cdp/DeviceRequestPrompt.ts对理解字段来源最有价值的部分是#updateDevices第 54–77 行与构造函数第 33–52 行每个CdpDeviceRequestPrompt构造时会记住本次 prompt 的id并订阅DeviceAccess.deviceRequestPrompted事件协议事件负载Protocol.DeviceAccess.DeviceRequestPromptedEvent.devices中每个原始设备包含协议层面的id与name源码第 68 行const newDevice {id: rawDevice.id, name: rawDevice.name}将其映射为DeviceRequestPromptDevice追加前会先按device.id去重第 60–65 行因此devices数组只增不减新设备加入后会立即驱动所有正在等待的waitForDevice观察者第 71–75 行逐一执行过滤回调命中则 resolve——这正是设备列表动态刷新、过滤器即时响应的实现机理每次事件都会比对event.id ! this.#id第 55–57 行确保只处理属于本弹窗的设备测试 should not list devices from events of another prompt 专门验证了这一点。协议层面等待与处理分别对应 CDPDeviceAccess域的四个调用/事件DeviceAccess.enable开启监听、DeviceAccess.deviceRequestPrompted弹窗与设备列表事件、DeviceAccess.selectPrompt {id, deviceId}选择见 select 实现 第 114–126 行、DeviceAccess.cancelPrompt {id}取消见 第 128–136 行。也就是说select时协议只需要prompt id deviceId两个标识DeviceRequestPromptDevice.id正是这里的deviceId。同样值得注意CDP 实现会保证同一弹窗只能被处理一次——select与cancel都会断言prompt 尚未被处理防止重复选择或选择后取消且每次select/cancel后即销毁协议订阅#subscriptions.dispose()。WebDriver BiDi 实现蓝牙域与取消选择的差异在 Firefox / WebDriver BiDi 路径下实现位于 packages/puppeteer-core/src/bidi/DeviceRequestPrompt.ts机制不同但DeviceRequestPromptDevice的字段完全一致BidiDeviceRequestPromptManager.waitForDevicePrompt会订阅bluetooth.requestDevicePromptUpdated事件第 31–39 行收到与当前 context 匹配的事件后构造BidiDeviceRequestPrompt构造时把协议负载Bidi.Bluetooth.RequestDeviceInfo[]映射为设备列表值得留意第 112 行当设备没有名字时被填充为字符串UNKNOWNname: d.name ?? UNKNOWN。这说明name字段在个别平台可能并非总是真实设备名自动化断言时需要考虑此边界情况select通过发送bluetooth.handleRequestDevicePrompt {accept: true, device: device.id}完成第 126–133 行cancel发送同一命令但accept: false第 118–124 行关键差异BiDi 实现中waitForDevice()直接抛出UnsupportedOperation第 135–137 行即该过滤等待能力目前仅 CDP 路径可用。从源码结构推断跨浏览器使用统一脚本时应先判断运行环境是否为 Firefox/BiDi再决定是否依赖waitForDevice。字段级实战建议与边界情况综合两套实现与 单元测试 中定义的模拟设备DEVICE_0 {id: 00000000, name: Device 0}等可以归纳出若干可直接落地的工程建议用name做展示匹配、用id做协议级回传。过滤、日志、断言偏向name真正传给select()的对象必须来自devicePrompt.devices或waitForDevice()的返回值因为 CDP 实现会断言this.devices.includes(device)Cannot select unknown device!见 select 实现自己 new 一个同名对象是无效的设备名可能为空或UNKNOWNBiDi 下用?? UNKNOWN兜底按name匹配时建议同时兼容模糊匹配与空名旁路同一设备可能被多次重复播报但devices按id去重无需担心数组重复waitForDevice支持超时与中断参数options: WaitTimeoutOptions支持timeout默认继承页面默认超时设置与signalAbortSignal。测试 should respect timeout 与 should be able to abort 验证了超时抛TimeoutError、abort 会以signal.reason拒绝同时测试也确认显式timeout优先于默认超时timeout: 0表示永不超时等语义过滤器在等待期间即生效即使设备列表已非空只要目标还没出现waitForDevice就会继续等待后续到达的设备测试 should return first matching device from already known devices 说明已存在设备会被优先即时命中。完整可运行示例综合以上信息一个更完整的 Web Bluetooth 自动化用例可写成假定页面按钮#connect-bluetooth会触发navigator.bluetooth.requestDevice(...)import puppeteer from puppeteer; const browser await puppeteer.launch(); const page await browser.newPage(); await page.goto(https://example.com/bluetooth-demo); // 1. 先注册等待弹窗再触发蓝牙连接顺序不可颠倒 const [devicePrompt] await Promise.all([ page.waitForDevicePrompt({timeout: 30_000}), page.click(#connect-bluetooth), ]); // 2. 打印当前已知设备展示 DeviceRequestPromptDevice 对象结构 console.log(devicePrompt.devices.map(d ({id: d.id, name: d.name}))); // 3. 等待目标设备出现按名称过滤返回 DeviceRequestPromptDevice const target await devicePrompt.waitForDevice( device device.name.startsWith(My-Device-), {timeout: 10_000}, ); // 4. 用返回的对象完成选择 await devicePrompt.select(target); // 5. 需要中止场景可调用 await devicePrompt.cancel(); await browser.close();单元测试如何背书该接口行为packages/puppeteer-core/src/cdp/DeviceRequestPrompt.test.ts 用MockCDPSession模拟协议事件完整覆盖了devices随DeviceAccess.deviceRequestPrompted事件逐批增量增长、不同 prompt 事件互不串扰lists devices as they arrivewaitForDevice从动态到达的设备、已缓存设备中返回首个匹配项且多个观察者可同时命中同一设备超时/默认超时/显式超时优先级、timeout: 0永不超时、AbortSignal 中断等全部边界。结语DeviceRequestPromptDevice是 Puppeteer 设备请求弹窗能力的最小数据契约id用于协议往返与唯一性判重name用于人类可读的过滤与展示。把它放进DeviceRequestPrompt.devices、配合Page.waitForDevicePrompt()与select()使用即可在 CDPChrome 系下完整实现触发 → 等待 → 过滤 → 选择的蓝牙设备自动化闭环而在 WebDriver BiDiFirefox下应直接遍历devices后调用select。相关声明与实现的更多细节可继续查阅 接口文档、DeviceRequestPrompt 类文档 以及 API 层源码。【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表