使用 Puppeteer 測試網路藍牙

François Beaufort
François Beaufort

網頁藍牙自 Chrome 56 版起開始支援,可讓開發人員編寫網頁應用程式,直接與使用者的藍牙裝置交談。舉例來說,Espruino 網頁編輯器可將程式碼上傳到相容的藍牙裝置。現在您可以使用 Puppeteer 測試這些應用程式。

這篇網誌文章將詳細介紹如何使用 Puppeteer 來操作及測試支援藍牙的網頁應用程式。關鍵在於 Puppeteer 可以操作 Chrome 的藍牙裝置選擇工具。

如果您不熟悉如何在 Chrome 中使用網路藍牙,以下影片會顯示 Espruino 網頁編輯器中的藍牙提示:

使用者在 Espruino 網頁編輯器中選取 Puck.js 藍牙裝置。

如要追蹤這篇網誌文章,您需要支援藍牙的網頁應用程式、可與藍牙通訊的藍牙裝置,以及 Puppeteer v21.4.0 以上版本。

啟動瀏覽器

與大多數 Puppeteer 指令碼相同,請先使用 Puppeteer.launch() 啟動瀏覽器。如要使用藍牙功能,您必須提供幾個額外的引數:

import puppeteer from 'puppeteer';

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

開啟第一頁時,我們通常會建議你使用無痕模式瀏覽器環境。這有助於防止使用指令碼執行測試時的權限外洩 (不過某些 OS 層級的共用狀態無法被 Puppeteer 防止)。以下程式碼示範了這項做法:

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

接著,您就可以前往使用 Page.goto() 進行測試的網頁應用程式網址。

開啟藍牙裝置提示

使用 Puppeteer 開啟網頁應用程式頁面後,您就能連線到藍牙裝置來讀取資料。下一個步驟假設您是網頁應用程式中有按鈕,該按鈕可以執行部分 JavaScript,包括呼叫 navigator.bluetooth.requestDevice()

使用 Page.locator().click() 按下按鈕,Page.waitForDevicePrompt() 則可辨識藍牙裝置的選擇器顯示的時間。您必須在點選按鈕前呼叫 waitForDevicePrompt(),否則提示會已開啟,而不會偵測到。

由於這兩種 Puppeteer 方法都會傳回承諾,因此 Promise.all() 可以方便地以正確的順序呼叫這些方法:

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

waitForDevicePrompt() 傳回的承諾會解析為 DeviceRequestPrompt 物件,您接下來將使用這個物件選取要連線的藍牙裝置。

選取裝置

使用「DeviceRequestPrompt.waitForDevice()」和「DeviceRequestPrompt.select()」尋找並連線到正確的藍牙裝置。

每當 Chrome 找到具備裝置基本資訊的藍牙裝置時,DeviceRequestPrompt.waitForDevice() 就會呼叫提供的回呼。第一次回呼傳回 true 時,waitForDevice() 會解析為相符的 DeviceRequestPromptDevice。將裝置交給「DeviceRequestPrompt.select()」,即可選取並連線到該藍牙裝置。

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

DeviceRequestPrompt.select() 解析後,Chrome 就會連上該裝置,網頁也能存取裝置。

讀取裝置內容

此時,您的網頁應用程式將連上所選藍牙裝置,並可從該裝置讀取資訊。看起來會像這樣:

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 與藍牙裝置通訊

此時,您已經瞭解如何使用 Puppeteer 自動使用支援藍牙的網頁應用程式,取代從藍牙裝置選擇工具選單中選取裝置的人為步驟。雖然這種做法通常很有用,但直接適合為這類網頁應用程式撰寫端對端測試。

建立測試

目前為止,從編寫完整程式碼到撰寫完整測試的過程,少了從網頁應用程式和 Puppeteer 指令碼擷取資訊。取得設定檔後,使用測試程式庫 (例如 TAPMocha) 來驗證系統讀取和回報正確的資料十分簡單。

最簡單的做法是將資料寫入 DOM。JavaScript 有很多方法可執行此操作,而且不需要額外的程式庫。返回假設的網頁應用程式,當狀態指標從藍牙裝置讀取資料時,或列印欄位中的文字資料,狀態指標的顏色可能會有所變更。例如:

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 為支援藍牙的網頁應用程式撰寫測試,請參閱這個存放區:https://github.com/WebBluetoothCG/manual-tests/網路藍牙社群群組負責維護這組測試,所有測試均可透過瀏覽器或本機執行。「唯讀特性」測試與這篇網誌文章使用的範例最相似。

特別銘謝

感謝 Vincent Scheib 展開這項專案計畫,針對這篇文章提供寶貴的意見回饋。