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

François Beaufort
François Beaufort

Web Bluetooth es compatible desde Chrome 56 y permite que los desarrolladores escriban aplicaciones 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, se pueden probar estas aplicaciones con Puppeteer.

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

Si no estás familiarizado con el uso de Web Bluetooth en Chrome, el siguiente video muestra el mensaje de Bluetooth en el editor web Espruino:

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

Para seguir esta entrada de blog, necesitarás una aplicación web con Bluetooth habilitado y un dispositivo Bluetooth con el que se pueda comunicar, además de que debe utilizar 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 iniciando 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"],
});

Al abrir la primera página, generalmente se recomienda usar un contexto de navegador en modo Incógnito. Esto ayuda a evitar que se filtren permisos entre las pruebas que se ejecutan con tu secuencia de comandos (aunque hay algún 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().

Abrir el mensaje del dispositivo Bluetooth

Una vez que hayas utilizado Puppeteer para abrir la página de la aplicación web con Puppeteer, podrás 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.

Como ambos métodos de Puppeteer muestran promesas, Promise.all() es una forma conveniente de llamarlas en el orden correcto:

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 buscar 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 la DeviceRequestPromptDevice coincidente. Pasa ese dispositivo a DeviceRequestPrompt.select() para seleccionar ese dispositivo Bluetooth y conectarte a él.

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

Una vez que se resuelva DeviceRequestPrompt.select(), Chrome se conectará al dispositivo y la página web podrá acceder a él.

Cómo leer desde el dispositivo

En este punto, tu aplicación web se conectará al dispositivo Bluetooth seleccionado y podrá leer la información de este. 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 Comunicación con dispositivos Bluetooth a través de JavaScript.

En este punto, ya sabes cómo usar Puppeteer para automatizar el uso de una aplicación web con Bluetooth habilitado mediante el reemplazo del paso humano de seleccionar un dispositivo en el menú del selector de dispositivos Bluetooth. Si bien esto podría ser útil en términos generales, se puede aplicar directamente a la escritura de una prueba de extremo a extremo para este tipo de app web.

Cómo crear una prueba

Falta completar el código para escribir una prueba completa y obtener información de la app web en la secuencia de comandos de Puppeteer. Una vez que tengas esto, será bastante sencillo usar una biblioteca de pruebas (como TAP o mocha) para verificar que se hayan leído e informado los datos correctos.

Una de las formas más fáciles de hacerlo es escribir datos en el DOM. JavaScript tiene muchas maneras de hacer esto sin bibliotecas adicionales. Volviendo a la app web hipotética, esta 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 ofrece una manera de extraer estos datos del DOM de la página y 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 escritura de pruebas para apps web compatibles con Bluetooth con Puppeteer, consulta el siguiente repositorio: https://github.com/WebBluetoothCG/manual-tests/. El grupo comunitario de Web Bluetooth mantiene este conjunto de pruebas, que pueden ejecutarse desde un navegador o de manera local. La prueba de"Característica de solo lectura" es más similar al ejemplo que se usa en esta entrada de blog.

Agradecimientos

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