Test Web Bluetooth met Poppenspeler

François Beaufort
François Beaufort

Web Bluetooth wordt ondersteund sinds Chrome 56 en stelt ontwikkelaars in staat webapps te schrijven die rechtstreeks met de Bluetooth-apparaten van gebruikers praten. De mogelijkheid van de Espruino-webeditor om code te uploaden naar compatibele Bluetooth-apparaten is zo'n voorbeeld. Het testen van deze applicaties is nu mogelijk met Puppeteer .

In deze blogpost wordt uitgelegd hoe u Puppeteer kunt gebruiken om een ​​Bluetooth-compatibele webapp te bedienen en te testen. Het belangrijkste onderdeel hiervan is de mogelijkheid van Puppeteer om de Bluetooth-apparaatkiezer van Chrome te bedienen.

Als u niet bekend bent met het gebruik van Web Bluetooth in Chrome, toont de volgende video de Bluetooth-prompt in de Espruino-webeditor:

Gebruiker selecteert een Puck.js Bluetooth-apparaat in de Espruino-webeditor.

Om deze blogpost te volgen heb je een webapp met Bluetooth nodig, een Bluetooth-apparaat waarmee het kan communiceren en gebruik je Puppeteer v21.4.0 of hoger.

Start de browser

Zoals bij de meeste Puppeteer-scripts begint u met het starten van de browser met Puppeteer.launch() . Om toegang te krijgen tot Bluetooth-functies moet je een paar extra argumenten opgeven:

  • Schakel de headless-modus uit: Dit betekent dat Puppeteer een zichtbaar Chrome-browservenster opent om de test uit te voeren. Gebruik de nieuwe headless-modus als u deze liever zonder gebruikersinterface uitvoert. De oudere headless-modus biedt geen ondersteuning voor het weergeven van Bluetooth-prompts.
  • Aanvullende argumenten voor Chromium: geef een argument 'Web Bluetooth inschakelen' door voor Linux-omgevingen.
import puppeteer from 'puppeteer';

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

Bij het openen van de eerste pagina wordt over het algemeen aanbevolen om een ​​incognito browsercontext te gebruiken. Dit helpt voorkomen dat er machtigingen lekken tussen de tests die met uw script worden uitgevoerd (hoewel er een gedeelde status op besturingssysteemniveau is die niet door Puppeteer kan worden voorkomen). De volgende code demonstreert dit:

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

Vervolgens kunt u met Page.goto() naar de URL van de web-app die u test, navigeren.

Open de prompt voor het Bluetooth-apparaat

Zodra u Puppeteer hebt gebruikt om de pagina van de webapp met Puppeteer te openen, kunt u verbinding maken met het Bluetooth-apparaat om gegevens te lezen. Bij deze volgende stap wordt ervan uitgegaan dat u een knop in uw webapp heeft die JavaScript uitvoert, inclusief een aanroep naar navigator.bluetooth.requestDevice() .

Gebruik Page.locator().click() om op die knop te drukken, en Page.waitForDevicePrompt() om te herkennen wanneer de Bluetooth-apparaatkiezer verschijnt. U moet waitForDevicePrompt() aanroepen voordat u op de knop klikt, anders is de prompt al geopend en kan deze niet worden gedetecteerd.

Omdat beide Puppeteer-methoden beloften retourneren, is Promise.all() een handige manier om ze in de juiste volgorde samen aan te roepen:

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

De belofte die wordt geretourneerd door waitForDevicePrompt() wordt omgezet in een DeviceRequestPrompt object dat u vervolgens gaat gebruiken om het Bluetooth-apparaat te selecteren waarmee u verbinding wilt maken.

Selecteer een apparaat

Gebruik DeviceRequestPrompt.waitForDevice() en DeviceRequestPrompt.select() om het juiste Bluetooth-apparaat te vinden en er verbinding mee te maken.

DeviceRequestPrompt.waitForDevice() roept de opgegeven callback aan telkens wanneer Chrome een Bluetooth-apparaat vindt met wat basisinformatie over het apparaat . De eerste keer dat de callback true retourneert, wordt waitForDevice() omgezet in het overeenkomende DeviceRequestPromptDevice . Geef dat apparaat door aan DeviceRequestPrompt.select() om dat Bluetooth-apparaat te selecteren en er verbinding mee te maken.

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

Zodra DeviceRequestPrompt.select() is opgelost, is Chrome verbonden met het apparaat en heeft de webpagina er toegang toe.

Lees vanaf het apparaat

Op dit punt wordt uw webapp verbonden met het gekozen Bluetooth-apparaat en kan u er informatie van lezen. Dit kan er als volgt uitzien:

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

Zie Communiceren met Bluetooth-apparaten via JavaScript voor een uitgebreider overzicht van deze reeks API-aanroepen.

Op dit punt weet je hoe je Puppeteer kunt gebruiken om het gebruik van een Bluetooth-compatibele webapp te automatiseren door de menselijke stap van het selecteren van een apparaat uit het Bluetooth-apparaatkiezermenu te vervangen. Hoewel dit over het algemeen nuttig kan zijn, is het direct van toepassing op het schrijven van een end-to-end-test voor een dergelijke web-app.

Maak een toets

Het ontbrekende stukje bij het schrijven van de code tot nu toe en het schrijven van een volledige test is het verkrijgen van informatie uit de webapp en in uw Puppeteer-script. Als je dit eenmaal hebt, is het vrij eenvoudig om een ​​testbibliotheek (zoals TAP of mokka ) te gebruiken om te verifiëren dat de juiste gegevens zijn gelezen en gerapporteerd.

Een van de gemakkelijkste manieren om dit te doen is door gegevens naar de DOM te schrijven. JavaScript heeft tal van manieren om dit te doen zonder extra bibliotheken. Als u teruggaat naar uw hypothetische webapp, kan deze de kleur van een statusindicator veranderen wanneer deze gegevens van het Bluetooth-apparaat leest of de letterlijke gegevens in een veld afdrukt. Bijvoorbeeld:

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

Vanuit Puppeteer biedt Page.$eval() u een manier om deze gegevens uit de DOM van de pagina te halen en in een testscript te plaatsen. $eval() gebruikt dezelfde logica als document.querySelector() om een ​​element te vinden en voert vervolgens de meegeleverde callback-functie uit met dat element als argument. Zodra u dit als variabele heeft, gebruikt u uw beweringenbibliotheek om te testen of de gegevens zijn wat we verwachten.

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

Aanvullende bronnen

Voor meer complexe voorbeelden van het schrijven van tests voor Bluetooth-compatibele web-apps met Puppeteer, zie deze repository: https://github.com/WebBluetoothCG/manual-tests/ . De Web Bluetooth Community Group onderhoudt deze reeks tests, die allemaal vanuit een browser of lokaal kunnen worden uitgevoerd. De test 'Alleen-lezen-kenmerk' lijkt het meest op het voorbeeld dat in deze blogpost wordt gebruikt.

Dankbetuigingen

Met dank aan Vincent Scheib voor het starten van dit project en het geven van waardevolle feedback op dit bericht.