使用 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"],
});

開啟第一個網頁時,通常建議使用無痕模式的瀏覽器內容。這有助於避免使用指令碼執行的測試之間洩露權限 (但 Puppeteer 無法防止某些 OS 層級的共用狀態)。請參考下列程式碼:

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

然後,您就可以前往目前使用 Page.goto() 測試的網頁應用程式網址。

開啟藍牙裝置提示

使用 Puppeteer 開啟網頁應用程式的 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/網路藍牙社群群組負責維護這一系列的測試,而所有測試都可以透過瀏覽器或本機執行。「唯讀特性」test 與這篇網誌文章所使用的範例最為相似。

特別銘謝

感謝 Vincent Scheib 開始進行這項專案,並為這篇文章提供寶貴意見。