Cómo probar la conexión Bluetooth web con Puppeteer

François Beaufort
François Beaufort

El Bluetooth web es compatible desde Chrome 56 y permite a los desarrolladores escribir apps web que se comuniquen directamente con los dispositivos Bluetooth de los usuarios. La capacidad del editor web de Espruino de subir código a dispositivos Bluetooth compatibles es un ejemplo de ello. Ahora es posible probar estas aplicaciones con Puppeteer.

En esta entrada de blog, se explica cómo usar Puppeteer para operar y probar una app web compatible con Bluetooth. La parte clave de esto es la capacidad de Puppeteer para operar el selector de dispositivos Bluetooth de Chrome.

Si no conoces el uso de Bluetooth web en Chrome, en el siguiente video, se muestra el mensaje de Bluetooth en el editor web de Espruino:

El usuario selecciona un dispositivo Bluetooth Puck.js en el editor web de Espruino.

Para seguir esta entrada de blog, necesitarás una app web compatible con Bluetooth, un dispositivo Bluetooth con el que pueda comunicarse y usar Puppeteer v21.4.0 o una versión posterior.

Inicia el navegador

Al igual que con la mayoría de las secuencias de comandos de Puppeteer, comienza por iniciar el navegador con Puppeteer.launch(). Para acceder a las funciones de Bluetooth, debes proporcionar algunos argumentos adicionales:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({
  headless: false,
  args: ["--enable-features=WebBluetooth"],
});

Cuando abres la primera página, generalmente se recomienda usar un contexto de navegador en incógnito. Esto ayuda a evitar la filtración de permisos entre las pruebas que se ejecutan con tu secuencia de comandos (aunque hay un estado compartido a nivel del SO que Puppeteer no puede evitar). Esto se demuestra en el siguiente código:

const browserContext = await browser.createIncognitoBrowserContext();
const page = await browserContext.newPage();

Luego, puedes navegar a la URL de la app web que estás probando con Page.goto().

Abre el mensaje del dispositivo Bluetooth

Una vez que hayas usado Puppeteer para abrir la página de la app web con Puppeteer, puedes conectarte al dispositivo Bluetooth para leer los datos. En el siguiente paso, se supone que tienes un botón en tu app web que ejecuta código JavaScript, incluida una llamada a navigator.bluetooth.requestDevice().

Usa Page.locator().click() para presionar ese botón y Page.waitForDevicePrompt() para reconocer cuándo aparece el selector de dispositivos Bluetooth. Debes llamar a waitForDevicePrompt() antes de hacer clic en el botón. De lo contrario, el mensaje ya se habrá abierto y no podrá detectarlo.

Dado que ambos métodos de Puppeteer devuelven promesas, Promise.all() es una forma conveniente de llamarlos en el orden correcto juntos:

const [devicePrompt] = await Promise.all([
  page.waitForDevicePrompt(),
  page.locator("#start-test-button").click(),
]);

La promesa que muestra waitForDevicePrompt() se resuelve en un objeto DeviceRequestPrompt que usarás a continuación para seleccionar el dispositivo Bluetooth al que deseas conectarte.

Seleccionar un dispositivo

Usa DeviceRequestPrompt.waitForDevice() y DeviceRequestPrompt.select() para encontrar el dispositivo Bluetooth correcto y conectarte a él.

DeviceRequestPrompt.waitForDevice() llama a la devolución de llamada proporcionada cada vez que Chrome encuentra un dispositivo Bluetooth con información básica sobre el dispositivo. La primera vez que la devolución de llamada muestra un valor verdadero, waitForDevice() se resuelve en el DeviceRequestPromptDevice coincidente. Pasa ese dispositivo a DeviceRequestPrompt.select() para seleccionarlo y conectarte a él.

const bluetoothDevice = await devicePrompt.waitForDevice(
  (d) => d.name == wantedDeviceName,
);
await devicePrompt.select(bluetoothDevice);

Una vez que se resuelve DeviceRequestPrompt.select(), Chrome se conecta al dispositivo y la página web puede acceder a él.

Cómo leer desde el dispositivo

En este punto, tu app web se conectará al dispositivo Bluetooth elegido y podrá leer información de él. El aspecto podría ser el siguiente:

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();

Para obtener una explicación más detallada de esta secuencia de llamadas a la API, consulta Cómo comunicarse con dispositivos Bluetooth a través de JavaScript.

En este punto, sabes cómo usar Puppeteer para automatizar el uso de una app web habilitada para Bluetooth reemplazando el paso manual de seleccionar un dispositivo del menú del selector de dispositivos Bluetooth. Si bien esto puede ser útil en general, se aplica directamente a la escritura de una prueba de extremo a extremo para una aplicación web de este tipo.

Crea una prueba

Lo que falta para llevar el código hasta escribir una prueba completa es obtener información de la app web y llevarla a tu secuencia de comandos de Puppeteer. Una vez que tengas esto, es bastante sencillo usar una biblioteca de pruebas (como TAP o mocha) para verificar que se hayan leído y registrado los datos correctos.

Una de las formas más sencillas de hacerlo es escribir datos en el DOM. JavaScript tiene muchas formas de hacerlo sin bibliotecas adicionales. Siguiendo con tu app web hipotética, podría cambiar el color de un indicador de estado cuando lee datos del dispositivo Bluetooth o imprime los datos literales en un campo. Por ejemplo:

const dataDisplayElement = document.querySelector('#data-display');
dataDisplayElement.innerText = dataView.getUint8();

En Puppeteer, Page.$eval() te brinda una forma de extraer estos datos del DOM de la página y colocarlos en una secuencia de comandos de prueba. $eval() usa la misma lógica que document.querySelector() para encontrar un elemento y, luego, ejecuta la función de devolución de llamada proporcionada con ese elemento como argumento. Una vez que tengas esto como una variable, usa tu biblioteca de aserciones para probar si los datos son los que esperamos.

const dataText = await page.$eval('#data-display', (el) => el.innerText);
equal(17, dataText);

Recursos adicionales

Para ver ejemplos más complejos de cómo escribir pruebas para apps web compatibles con Bluetooth con Puppeteer, consulta este repositorio: https://github.com/WebBluetoothCG/manual-tests/. El Web Bluetooth Community Group mantiene este paquete de pruebas, que se pueden ejecutar desde un navegador o de forma local. La prueba"Característica de solo lectura" es la más similar al ejemplo que se usa en esta entrada de blog.

Agradecimientos

Gracias a Vincent Scheib por iniciar este proyecto y proporcionar comentarios valiosos sobre esta publicación.