تتوفّر تقنية Web Bluetooth منذ الإصدار 56 من Chrome، وهي تتيح للمطوّرين كتابة تطبيقات ويب تتواصل مباشرةً مع أجهزة Bluetooth الخاصة بالمستخدمين. ومن الأمثلة على ذلك قدرة محرر الويب Espruino على تحميل الرمز البرمجي إلى أجهزة البلوتوث المتوافقة. أصبح من الممكن الآن اختبار هذه التطبيقات باستخدام Puppeteer.
توضِّح مشاركة المدونة هذه كيفية استخدام Puppeteer لتشغيل تطبيق ويب مزوّد بتقنية البلوتوث واختباره. ويتمثل الجزء الرئيسي من ذلك في قدرة Puppeteer على تشغيل أداة اختيار أجهزة البلوتوث في Chrome.
إذا لم تكن على دراية باستخدام Web Bluetooth في Chrome، يعرض الفيديو التالي طلب البلوتوث في محرِّر ويب Espruino:
لمتابعة هذه المشاركة في المدونة، ستحتاج إلى تطبيق ويب مزوّد بتقنية البلوتوث وجهاز بلوتوث يمكنه التواصل معه، ويجب استخدام Puppeteer v21.4.0 أو إصدار أحدث.
افتح المتصفّح.
كما هو الحال مع معظم النصوص البرمجية في Puppeteer، ابدأ بتشغيل المتصفّح باستخدام Puppeteer.launch()
. للوصول إلى ميزات البلوتوث، عليك تقديم بعض الوسيطات الإضافية:
- إيقاف وضع "التشغيل بلا واجهة مستخدم": يعني ذلك أنّ Puppeteer سيفتح نافذة متصفّح Chrome مرئية لإجراء الاختبار. استخدِم وضع التشغيل بلا واجهة مستخدم الجديد إذا كنت تفضّل تشغيله بدون واجهة مستخدم. لا يتيح وضع التشغيل بلا واجهة مستخدم رسومية القديم عرض الطلبات من خلال البلوتوث.
- وسيطات إضافية لخدمة Chromium: يمكنك تمرير وسيطة"enable Web Bluetooth" في بيئات Linux.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({
headless: false,
args: ["--enable-features=WebBluetooth"],
});
عند فتح الصفحة الأولى، يُنصح بشكل عام باستخدام سياق متصفّح التصفّح المتخفي. يساعد ذلك في منع تسرُّب الأذونات بين الاختبارات التي يتم تشغيلها باستخدام النص البرمجي (على الرغم من أنّ هناك بعض الحالات المشتركة على مستوى نظام التشغيل لا يمكن منع Puppeteer من الوصول إليها). يوضِّح الرمز البرمجي التالي ذلك:
const browserContext = await browser.createIncognitoBrowserContext();
const page = await browserContext.newPage();
يمكنك بعد ذلك الانتقال إلى عنوان URL لتطبيق الويب الذي تختبره باستخدام Page.goto()
.
فتح طلب جهاز البلوتوث
بعد استخدام Puppeteer لفتح صفحة تطبيق الويب باستخدام Puppeteer، يمكنك الاتصال بجهاز Bluetooth لقراءة البيانات. تفترض هذه الخطوة التالية أنّ لديك زرًا في تطبيق الويب الخاص بك يشغّل بعض 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()
للعثور على جهاز البلوتوث الصحيح والاتصال به.
يُطلِق DeviceRequestPrompt.waitForDevice()
دالة الاستدعاء المقدَّمة في كل مرة يعثر فيها Chrome على جهاز بلوتوث يتضمّن بعض المعلومات الأساسية عن الجهاز. في المرة الأولى التي يعود فيها معاودة الاتصال إلى القيمة 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();
للحصول على جولة تفصيلية حول هذا التسلسل من طلبات البيانات من واجهة برمجة التطبيقات، يُرجى الاطّلاع على التواصل مع الأجهزة التي تتضمّن بلوتوث من خلال JavaScript.
في هذه المرحلة، تعرَّفت على كيفية استخدام Puppeteer لبرمجة استخدام تطبيق ويب مزوّد بتقنية البلوتوث من خلال استبدال خطوة اختيار جهاز من قائمة اختيار أجهزة البلوتوث. على الرغم من أنّ هذا قد يكون مفيدًا بشكل عام، إلا أنّه يمكن تطبيقه مباشرةً على كتابة اختبار شامل لتطبيق ويب من هذا النوع.
إنشاء اختبار
إنّ الخطوة غير المُستخدَمة حتى الآن في كتابة اختبار كامل هي الحصول على المعلومات من التطبيق الإلكتروني وإدخالها في نص Puppeteer البرمجي. بعد إجراء ذلك، من السهل إلى حدٍ ما استخدام مكتبة اختبار (مثل TAP أو mocha) للتأكّد من قراءة البيانات الصحيحة والإبلاغ عنها.
وإحدى أسهل طرق القيام بذلك هي كتابة البيانات في 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/. تحافظ مجموعة منتدى Web Bluetooth على هذه المجموعة من الاختبارات التي يمكن تنفيذها كلها من متصفّح أو على الجهاز. يُعدّ اختبار"السمة للقراءة فقط" هو الأقرب إلى المثال المستخدَم في مشاركة المدوّنة هذه.
خدمات الإقرار
نشكر "فينسنت شيب" على بدء هذا المشروع وتقديم ملاحظات قيّمة حول هذه المشاركة.