从 Chrome 56 开始,网络蓝牙就一直受到支持,这使得开发者能够编写 Web 应用,直接与用户的设备通信蓝牙设备。例如,Espruino 网页编辑器能够将代码上传到兼容的蓝牙设备。现在可使用 Puppeteer 测试这些应用。
这篇博文详细介绍了如何使用 Puppeteer 操作和测试启用了蓝牙的 Web 应用。其中的关键在于 Puppeteer 能够操作 Chrome 的蓝牙设备选择器。
如果您不熟悉如何在 Chrome 中使用网络蓝牙,请观看以下视频,了解 Espruino 网页编辑器中的蓝牙提示:
<ph type="x-smartling-placeholder">要关注此博文,您需要一个支持蓝牙的 Web 应用、一个可以与其通信的蓝牙设备,并使用 Puppeteer v21.4.0 或更高版本。
启动浏览器
与大多数 Puppeteer 脚本一样,首先使用 Puppeteer.launch()
启动浏览器。要访问蓝牙功能,您需要提供一些额外的参数:
- 停用无头模式:这表示 Puppeteer 将打开一个可见的 Chrome 浏览器窗口来运行测试。如果您希望在没有界面的情况下运行该模式,请使用新的无头模式。旧版无头模式不支持显示蓝牙提示。
- Chromium 的其他参数:传递“启用网络蓝牙”参数。
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({
headless: false,
args: ["--enable-features=WebBluetooth"],
});
打开第一个网页时,通常建议使用浏览器的无痕模式。这有助于防止在使用脚本运行的测试之间泄露权限(尽管 Puppeteer 无法阻止某些操作系统级别的共享状态)。以下代码对此进行了演示:
const browserContext = await browser.createIncognitoBrowserContext();
const page = await browserContext.newPage();
然后,您可以前往使用 Page.goto()
测试的 Web 应用的网址。
打开蓝牙设备提示
使用 Puppeteer 通过 Puppeteer 打开 Web 应用的页面后,您可以连接到蓝牙设备以读取数据。接下来的这个步骤假定您的 Web 应用中有一个按钮,用于运行某些 JavaScript(包括对 navigator.bluetooth.requestDevice()
的调用)。
使用 Page.locator().click()
可按下该按钮,使用 Page.waitForDevicePrompt()
则可识别蓝牙设备选择器何时出现。您必须在点击该按钮前调用 waitForDevicePrompt()
,否则,提示符已打开,无法检测到。
由于这两种 Puppeteer 方法都会返回 promise,因此可以方便地使用 Promise.all()
以正确的顺序将它们一起调用:
const [devicePrompt] = await Promise.all([
page.waitForDevicePrompt(),
page.locator("#start-test-button").click(),
]);
waitForDevicePrompt()
返回的 promise 会解析为 DeviceRequestPrompt
对象,接下来您将使用该对象选择要连接的蓝牙设备。
选择设备
使用 DeviceRequestPrompt.waitForDevice()
和 DeviceRequestPrompt.select()
查找并连接正确的蓝牙设备。
每当 Chrome 发现包含设备基本信息的蓝牙设备时,DeviceRequestPrompt.waitForDevice()
都会调用所提供的回调。回调第一次返回 true 时,waitForDevice()
会解析为匹配的 DeviceRequestPromptDevice
。将该设备传递给 DeviceRequestPrompt.select()
,以便选择并连接到该蓝牙设备。
const bluetoothDevice = await devicePrompt.waitForDevice(
(d) => d.name == wantedDeviceName,
);
await devicePrompt.select(bluetoothDevice);
解析 DeviceRequestPrompt.select()
后,Chrome 就会连接到设备,然后该网页就可以访问该设备了。
从设备读取内容
此时,您的 Web 应用将连接到所选的蓝牙设备,并能够从该设备读取信息。相应版本代码可能类似如下:
const serviceId = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
const device = await navigator.bluetooth.requestDevice({
filters: [{ services: [serviceId] }],
});
const gattServer = await device.gatt.connect();
const service = await gattServer.getPrimaryService(serviceId);
const characteristic = await service.getCharacteristic(
"0b30afd0-193e-11eb-adc1-0242ac120002",
);
const dataView = await characteristic.readValue();
有关此 API 调用序列的更深入演示,请参阅通过 JavaScript 与蓝牙设备通信。
至此,您已经知道如何使用 Puppeteer 自动使用支持蓝牙的 Web 应用,而不必费时费力从蓝牙设备选择器菜单中选择设备。虽然这通常很有用,但它直接适用于为此类 Web 应用编写端到端测试。
创建测试
到目前为止,从获取代码到编写完整测试,缺少的部分是从 Web 应用获取信息到 Puppeteer 脚本中。完成上述操作后,使用测试库(如 TAP 或 mocha)来验证读取和报告的数据是否正确,就非常简单了。
其中一种最简单的方法就是将数据写入 DOM。JavaScript 提供了多种实现这一点的方法,而无需其他库。回到您假设的 Web 应用,当它从蓝牙设备读取数据或输出某个字段中的文本数据时,它可能会改变状态指示器的颜色。例如:
const dataDisplayElement = document.querySelector('#data-display');
dataDisplayElement.innerText = dataView.getUint8();
在 Puppeteer 中,Page.$eval()
可让您将这些数据从页面的 DOM 中提取到测试脚本中。$eval()
使用与 document.querySelector()
相同的逻辑来查找元素,然后以该元素作为参数运行所提供的回调函数。将其作为变量后,请使用断言库测试数据是否符合我们的预期。
const dataText = await page.$eval('#data-display', (el) => el.innerText);
equal(17, dataText);
其他资源
如需查看使用 Puppeteer 为启用蓝牙的 Web 应用编写测试的更复杂的示例,请参阅以下代码库:https://github.com/WebBluetoothCG/manual-tests/。Web Bluetooth 社区小组负责维护这套测试,所有这些测试都可以通过浏览器或在本地运行。“只读特性”test”这一示例与这篇博文中使用的示例最为相似。
致谢
感谢 Vincent Scheib 启动这个项目并为这篇帖子提供宝贵的反馈意见。