Web Bluetooth mit Puppeteer testen

François Beaufort
François Beaufort

Web Bluetooth wird seit Chrome 56 unterstützt und ermöglicht Entwicklern, Web-Apps zu erstellen, die direkt mit den Bluetooth-Geräten der Nutzer kommunizieren. Ein Beispiel dafür ist die Fähigkeit des Espruino Web Editors, Code auf kompatible Bluetooth-Geräte hochzuladen. Das Testen dieser Anwendungen ist jetzt mit Puppeteer möglich.

In diesem Blogpost wird erläutert, wie Sie mit Puppeteer eine Bluetooth-fähige Webanwendung betreiben und testen. Der entscheidende Teil dabei ist die Fähigkeit von Puppeteer, die Bluetooth-Geräteauswahl von Chrome zu steuern.

Wenn Sie mit der Verwendung von Web Bluetooth in Chrome nicht vertraut sind, sehen Sie im folgenden Video die Bluetooth-Aufforderung im Espruino Web Editor:

Der Nutzer wählt im Espruino-Webeditor ein Bluetooth-Gerät von Puck.js aus.

Wenn Sie diesem Blogpost folgen möchten, benötigen Sie eine Bluetooth-fähige Web-App, ein Bluetooth-Gerät, mit dem sie kommunizieren kann, und Puppeteer v21.4.0 oder höher.

Browser starten

Wie bei den meisten Puppeteer-Skripts starten Sie zuerst den Browser mit Puppeteer.launch(). Um auf Bluetooth-Funktionen zugreifen zu können, müssen Sie ein paar zusätzliche Argumente angeben:

  • Headless-Modus deaktivieren: Das bedeutet, dass Puppeteer ein sichtbares Chrome-Browserfenster öffnet, um den Test auszuführen. Verwenden Sie den neuen monitorlosen Modus, wenn Sie ihn lieber ohne Benutzeroberfläche ausführen möchten. Im alten monitorlosen Modus können keine Bluetooth-Aufforderungen angezeigt werden.
  • Zusätzliche Argumente für Chromium: Übergeben Sie bei Linux-Umgebungen das Argument „Web Bluetooth aktivieren“.
import puppeteer from 'puppeteer';

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

Beim Öffnen der ersten Seite empfiehlt es sich im Allgemeinen, einen Inkognito-Browserkontext zu verwenden. So wird verhindert, dass Berechtigungen zwischen den Tests, die mit Ihrem Skript ausgeführt werden, verloren gehen (auch wenn es einen gemeinsamen Status auf Betriebssystemebene gibt, der von Puppeteer nicht verhindert werden kann). Der folgende Code veranschaulicht dies:

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

Anschließend können Sie mit Page.goto() die URL der Web-App aufrufen, die Sie testen.

Aufforderung für das Bluetooth-Gerät öffnen

Nachdem Sie mit Puppeteer die Seite der Webanwendung mit Puppeteer geöffnet haben, können Sie eine Verbindung zum Bluetooth-Gerät herstellen, um Daten zu lesen. Bei diesem nächsten Schritt wird davon ausgegangen, dass sich in Ihrer Web-App eine Schaltfläche befindet, auf der JavaScript ausgeführt wird und die ein Aufruf von navigator.bluetooth.requestDevice() enthält.

Drücke Page.locator().click(), um diese Taste zu drücken, und Page.waitForDevicePrompt(), um zu erkennen, wann die Bluetooth-Geräteauswahl angezeigt wird. Sie müssen waitForDevicePrompt() aufrufen, bevor Sie auf die Schaltfläche klicken. Andernfalls wurde die Eingabeaufforderung bereits geöffnet und kann nicht erkannt werden.

Da beide Puppeteer-Methoden Promise zurückgeben, ist Promise.all() eine bequeme Möglichkeit, sie in der richtigen Reihenfolge aufzurufen:

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

Das von waitForDevicePrompt() zurückgegebene Promise wird in ein DeviceRequestPrompt-Objekt aufgelöst, mit dem du das Bluetooth-Gerät auswählen kannst, mit dem du eine Verbindung herstellen möchtest.

Gerät auswählen

Verwende DeviceRequestPrompt.waitForDevice() und DeviceRequestPrompt.select(), um das richtige Bluetooth-Gerät zu finden und eine Verbindung herzustellen.

DeviceRequestPrompt.waitForDevice() ruft den bereitgestellten Callback immer dann auf, wenn Chrome ein Bluetooth-Gerät mit einigen grundlegenden Informationen über das Gerät findet. Wenn der Callback zum ersten Mal „true“ zurückgibt, wird waitForDevice() in den übereinstimmenden DeviceRequestPromptDevice aufgelöst. Übergib das Gerät an DeviceRequestPrompt.select(), um es auszuwählen und eine Verbindung mit ihm herzustellen.

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

Sobald DeviceRequestPrompt.select() aufgelöst wurde, wird Chrome mit dem Gerät verbunden und die Webseite kann auf das Gerät zugreifen.

Vom Gerät aus lesen

Ihre Web-App ist nun mit dem ausgewählten Bluetooth-Gerät verbunden und kann Informationen von diesem Gerät abrufen. Das könnte so aussehen:

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

Eine ausführliche Anleitung zu dieser Abfolge von API-Aufrufen finden Sie unter Kommunikation mit Bluetooth-Geräten über JavaScript.

Sie wissen jetzt, wie Sie mit Puppeteer die Nutzung einer Bluetooth-fähigen Web-App automatisieren, indem Sie die manuelle Auswahl eines Geräts aus dem Bluetooth-Geräteauswahlmenü ersetzen. Dies mag zwar allgemein nützlich sein, eignet sich jedoch direkt für das Schreiben eines End-to-End-Tests für eine solche Webanwendung.

Test erstellen

Wenn Sie den Code bis zum Schreiben eines vollständigen Tests noch nicht fertigstellen, müssen Sie Informationen aus der Webanwendung in Ihr Puppeteer-Skript abrufen. Danach können Sie ganz einfach mithilfe einer Testbibliothek (z. B. TAP oder Mocha) prüfen, ob die korrekten Daten gelesen und gemeldet wurden.

Eine der einfachsten Möglichkeiten, dies zu tun, besteht darin, Daten in das DOM zu schreiben. JavaScript bietet viele Möglichkeiten, dies zu erreichen, ohne zusätzliche Bibliotheken. Gehen Sie zurück zu Ihrer hypothetischen Web-App. Möglicherweise ändert sie die Farbe einer Statusanzeige, wenn Daten vom Bluetooth-Gerät gelesen werden, oder die Literaldaten in einem Feld auszugeben. Beispiel:

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

In Puppeteer können Sie mit Page.$eval() diese Daten aus dem DOM der Seite in ein Testskript übertragen. $eval() verwendet dieselbe Logik wie document.querySelector(), um ein Element zu finden, und führt dann die bereitgestellte Callback-Funktion mit diesem Element als Argument aus. Sobald Sie diese als Variable haben, verwenden Sie die Assertion-Bibliothek, um zu testen, ob die Daten unseren Erwartungen entsprechen.

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

Zusätzliche Ressourcen

Komplexere Beispiele für das Schreiben von Tests für Bluetooth-fähige Webanwendungen mit Puppeteer finden Sie in diesem Repository: https://github.com/WebBluetoothCG/manual-tests/. Die Web Bluetooth Community Group verwaltet diese Reihe von Tests, die alle über einen Browser oder lokal ausgeführt werden können. Der Test der schreibgeschützten Eigenschaft ist dem Beispiel in diesem Blogpost am ähnlichsten.

Danksagung

Vielen Dank an Vincent Scheib für den Start dieses Projekts und für das wertvolle Feedback zu diesem Beitrag.