Web Bluetooth mit Puppeteer testen

François Beaufort
François Beaufort

Web Bluetooth wird seit Chrome 56 unterstützt und ermöglicht es Entwicklern, Webanwendungen zu erstellen, die direkt mit den Bluetooth-Geräten der Nutzer kommunizieren. Ein Beispiel dafür ist die Möglichkeit des Espruino-Webeditors, Code auf kompatible Bluetooth-Geräte hochzuladen. Mit Puppeteer ist es jetzt möglich, diese Anwendungen zu testen.

In diesem Blogpost wird beschrieben, wie Sie mit Puppeteer eine Bluetooth-kompatible Webanwendung bedienen und testen. Der wichtigste Teil dabei ist die Fähigkeit von Puppeteer, die Bluetooth-Geräteauswahl von Chrome zu bedienen.

Wenn Sie mit der Verwendung von Web Bluetooth in Chrome nicht vertraut sind, sehen Sie sich das folgende Video zur Bluetooth-Aufforderung im Espruino-Webeditor an:

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

Für diesen Blogpost benötigen Sie eine Bluetooth-fähige Webanwendung, ein Bluetooth-Gerät, mit dem sie kommunizieren kann, und Puppeteer v21.4.0 oder höher.

Browser starten

Wie bei den meisten Puppeteer-Scripts starten Sie den Browser mit Puppeteer.launch(). Wenn du auf Bluetooth-Funktionen zugreifen möchtest, musst du einige zusätzliche Argumente angeben:

  • Headless-Modus deaktivieren: Puppeteer öffnet dann ein sichtbares Chrome-Browserfenster, um den Test auszuführen. Verwenden Sie den neuen headless-Modus, wenn Sie ihn ohne Benutzeroberfläche ausführen möchten. Der alte headless-Modus unterstützt keine Bluetooth-Aufforderungen.
  • Zusätzliche Argumente für Chromium: Geben Sie für Linux-Umgebungen das Argument „enable Web Bluetooth“ an.
import puppeteer from 'puppeteer';

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

Beim Öffnen der ersten Seite wird in der Regel empfohlen, einen Inkognitobrowser-Kontext zu verwenden. So wird verhindert, dass Berechtigungen zwischen den mit Ihrem Script ausgeführten Tests weitergegeben werden. Es gibt jedoch einen gemeinsamen Zustand auf Betriebssystemebene, der von Puppeteer nicht verhindert werden kann. Das wird im folgenden Code veranschaulicht:

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

Sie können dann mit Page.goto() die URL der Webanwendung aufrufen, die Sie testen.

Aufforderung zum Koppeln eines Bluetooth-Geräts öffnen

Nachdem Sie die Seite der Webanwendung mit Puppeteer geöffnet haben, können Sie eine Verbindung zum Bluetooth-Gerät herstellen, um Daten zu lesen. Für den nächsten Schritt wird davon ausgegangen, dass Sie in Ihrer Webanwendung eine Schaltfläche haben, die JavaScript ausführt, einschließlich eines Aufrufs von navigator.bluetooth.requestDevice().

Verwenden Sie Page.locator().click(), um diese Schaltfläche zu drücken, und Page.waitForDevicePrompt(), um zu erkennen, wann die Bluetooth-Geräteauswahl angezeigt wird. Sie müssen waitForDevicePrompt() anrufen, bevor Sie auf die Schaltfläche klicken, da der Prompt andernfalls bereits geöffnet ist und nicht erkannt werden kann.

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

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

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

Gerät auswählen

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

DeviceRequestPrompt.waitForDevice() ruft den angegebenen Rückruf jedes Mal auf, wenn Chrome ein Bluetooth-Gerät mit einigen grundlegenden Informationen zum Gerät findet. Wenn der Callback zum ersten Mal „true“ zurückgibt, wird waitForDevice() auf den übereinstimmenden DeviceRequestPromptDevice zurückgeführt. Übergeben Sie dieses Gerät an DeviceRequestPrompt.select(), um es auszuwählen und eine Verbindung damit herzustellen.

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

Sobald DeviceRequestPrompt.select() aufgelöst ist, ist Chrome mit dem Gerät verbunden und die Webseite kann darauf zugreifen.

Vom Gerät lesen

Jetzt ist Ihre Webanwendung mit dem ausgewählten Bluetooth-Gerät verbunden und kann Informationen daraus lesen. 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ührlichere Anleitung zu dieser Abfolge von API-Aufrufen finden Sie unter Mit Bluetooth-Geräten über JavaScript kommunizieren.

Sie wissen jetzt, wie Sie mit Puppeteer die Verwendung einer Bluetooth-fähigen Webanwendung automatisieren, indem Sie den manuellen Schritt zur Auswahl eines Geräts aus dem Bluetooth-Geräteauswahlmenü ersetzen. Das ist zwar allgemein nützlich, aber es kann direkt auf das Schreiben eines End-to-End-Tests für eine solche Webanwendung angewendet werden.

Test erstellen

Das fehlende Stück, um den Code zu einem vollständigen Test zu machen, ist die Übertragung von Informationen aus der Webanwendung in Ihr Puppeteer-Script. Sobald Sie das haben, können Sie mit einer Testbibliothek wie TAP oder mocha ganz einfach prüfen, ob die richtigen Daten gelesen und erfasst wurden.

Eine der einfachsten Möglichkeiten dazu besteht darin, Daten in das DOM zu schreiben. In JavaScript gibt es viele Möglichkeiten, dies ohne zusätzliche Bibliotheken zu tun. Zurück zu Ihrer hypothetischen Webanwendung: Diese könnte die Farbe einer Statusanzeige ändern, wenn sie Daten vom Bluetooth-Gerät liest, oder die Daten in einem Feld ausdrucken. Beispiel:

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

Mit Page.$eval() in Puppeteer können Sie diese Daten aus dem DOM der Seite in ein Testscript einfügen. $eval() verwendet dieselbe Logik wie document.querySelector(), um ein Element zu finden, und führt dann die angegebene Rückruffunktion mit diesem Element als Argument aus. Sobald Sie diese Variable haben, können Sie mithilfe Ihrer Assertion-Bibliothek testen, ob die Daten den Erwartungen entsprechen.

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

Zusätzliche Ressourcen

Komplexere Beispiele zum Schreiben von Tests für Bluetooth-kompatible Web-Apps mit Puppeteer finden Sie in diesem Repository: https://github.com/WebBluetoothCG/manual-tests/. Die Web Bluetooth Community Group verwaltet diese Testsuite, die alle über einen Browser oder lokal ausgeführt werden kann. Der Test „Leseattribut“ ähnelt dem Beispiel in diesem Blogpost am ehesten.

Danksagung

Vielen Dank an Vincent Scheib, der dieses Projekt ins Leben gerufen und wertvolles Feedback zu diesem Beitrag gegeben hat.