Puppeteer でウェブ Bluetooth をテストする

François Beaufort
François Beaufort

ウェブ Bluetooth は Chrome 56 以降でサポートされており、デベロッパーはユーザーの Bluetooth デバイスと直接通信するウェブアプリを作成できます。互換性のある Bluetooth デバイスにコードをアップロードできる Espruino ウェブ エディタの機能がその一例です。これらのアプリケーションのテストは、Puppeteer で可能になりました。

このブログ投稿では、Puppeteer を使用して Bluetooth 対応ウェブアプリを操作してテストする方法について説明します。この方法の重要な部分は、Puppeteer が Chrome の Bluetooth デバイス選択ツールを操作できることです。

Chrome で Web Bluetooth の使用に慣れていない場合は、次の動画で Espruino ウェブ エディタの Bluetooth プロンプトをご覧ください。

ユーザーが Espruino ウェブ エディタで Puck.js Bluetooth デバイスを選択します。

このブログ投稿に沿って操作するには、Bluetooth 対応のウェブアプリ、通信できる Bluetooth デバイス、Puppeteer v21.4.0 以降が必要です。

ブラウザを起動する

ほとんどの Puppeteer スクリプトと同様に、まず Puppeteer.launch() でブラウザを起動します。Bluetooth 機能にアクセスするには、いくつかの追加引数を指定する必要があります。

import puppeteer from 'puppeteer';

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

通常、最初のページを開くときは、シークレット ブラウザ コンテキストを使用することをおすすめします。これにより、スクリプトで実行されるテスト間で権限が漏洩するのを防ぐことができます(ただし、Puppeteer で防ぐことができない OS レベルの共有状態もあります)。次のコードは、このことを示しています。

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

その後、Page.goto() を使用して、テストするウェブアプリの URL に移動します。

Bluetooth デバイスのプロンプトを開く

Puppeteer を使用してウェブアプリのページを開いたら、Bluetooth デバイスに接続してデータを読み取ることができます。次のステップでは、navigator.bluetooth.requestDevice() の呼び出しを含む JavaScript を実行するボタンがウェブアプリにあることを前提としています。

Page.locator().click() を使用してそのボタンを押します。Page.waitForDevicePrompt() を使用して、Bluetooth デバイス選択ツールが表示されたときに認識します。ボタンをクリックする前に waitForDevicePrompt() を呼び出す必要があります。呼び出さないと、プロンプトがすでに開いているため、検出できません。

これらの Puppeteer メソッドはどちらも Promise を返すため、Promise.all() を使用すると、適切な順序で一緒に呼び出すことができます。

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

waitForDevicePrompt() によって返された Promise は DeviceRequestPrompt オブジェクトに解決されます。このオブジェクトは、次に接続する Bluetooth デバイスを選択するために使用します。

デバイスを選択する

DeviceRequestPrompt.waitForDevice()DeviceRequestPrompt.select() を使用して、適切な Bluetooth デバイスを探して接続します。

DeviceRequestPrompt.waitForDevice() は、Chrome が Bluetooth デバイスを検出するたびに、指定されたコールバックを呼び出し、デバイスに関する基本情報を返します。コールバックが初めて true を返すと、waitForDevice() は一致した DeviceRequestPromptDevice に解決されます。そのデバイスを DeviceRequestPrompt.select() に渡して、その Bluetooth デバイスを選択して接続します。

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

DeviceRequestPrompt.select() が解決すると、Chrome はデバイスに接続され、ウェブページはデバイスにアクセスできるようになります。

デバイスから読み取る

この時点で、選択した Bluetooth デバイスにウェブアプリが接続され、デバイスから情報を読み取ることができるようになります。次に例を示します。

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

この一連の API 呼び出しの詳細なチュートリアルについては、JavaScript を介した Bluetooth デバイスとの通信をご覧ください。

ここまでで、Puppeteer を使用して Bluetooth 対応ウェブアプリの使用を自動化する方法を学びました。これは、Bluetooth デバイス選択ツールのメニューからデバイスを選択する手順を自動化することで実現できます。これは一般に有用ですが、このようなウェブアプリのエンドツーエンド テストの作成に直接適用できます。

テストの作成

ここまでのコードから完全なテストを作成するには、ウェブアプリから Puppeteer スクリプトに情報を取得する必要があります。準備ができたら、テスト ライブラリ(TAPmocha など)を使用して、正しいデータが読み取られ、報告されたことを簡単に確認できます。

最も簡単な方法の一つは、DOM にデータを書き込むことです。JavaScript では、追加のライブラリを使用せずに、さまざまな方法でこれを行うことができます。前述の仮想ウェブアプリでは、Bluetooth デバイスからデータを読み取ったときにステータス インジケーターの色を変更したり、フィールドにリテラル データを出力したりすることがあります。例:

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

Puppeteer の Page.$eval() では、このデータを取得してページの DOM からテスト スクリプトに取り込めます。$eval()document.querySelector() と同じロジックを使用して要素を見つけ、その要素を引数として指定されたコールバック関数を実行します。これを変数として取得したら、アサーション ライブラリを使用して、データが想定どおりかどうかをテストします。

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

参考情報

Puppeteer を使用して Bluetooth 対応ウェブアプリのテストを作成する複雑な例については、https://github.com/WebBluetoothCG/manual-tests/ のリポジトリをご覧ください。このテストスイートは Web Bluetooth コミュニティ グループが管理しており、すべてブラウザまたはローカルで実行できます。「読み取り専用」特性テストは、このブログ投稿で使用した例と非常によく似ています。

謝辞

このプロジェクトを開始し、この投稿に関する貴重なフィードバックをいただいた Vincent Scheib 氏に感謝いたします。