تتوفّر تقنية 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 على جهاز بلوتوث يتضمّن بعض المعلومات الأساسية عن الجهاز. في المرة الأولى التي يعرض فيها ردّ الاتصال قيمة صحيحة، يتمّ تحويل 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 على هذه المجموعة من الاختبارات التي يمكن تنفيذها كلها من متصفّح أو على الجهاز. يُعدّ اختبار"السمة للقراءة فقط" هو الأقرب إلى المثال المستخدَم في مشاركة المدوّنة هذه.
خدمات الإقرار
نشكر "فينسنت شيب" على بدء هذا المشروع وتقديم ملاحظات قيّمة حول هذه المشاركة.