مكتبة Puppetearia: النصوص البرمجية لـ Puppeteer لتسهيل الاستخدام

خليج يوهان
خليج جوهان

محرّك بحث Puppeteer وطريقته في استخدام أدوات الاختيار

Puppeteer هي مكتبة تشغيل آلي للمتصفّح لمنصة Node: تتيح لك التحكّم في المتصفّح باستخدام واجهة برمجة تطبيقات JavaScript بسيطة وحديثة.

أما أبرز مهام المتصفح، فهو بالطبع تصفُّح صفحات الويب. تعمل أتمتة هذه المهمة بشكل أساسي على أتمتة التفاعلات مع صفحة الويب.

في Puppeteer، يتحقق ذلك من خلال الاستعلام عن عناصر DOM باستخدام المحددات المستندة إلى السلسلة وتنفيذ إجراءات مثل النقر أو كتابة نص على العناصر. على سبيل المثال، نص برمجي يفتح developer.google.com ويعثر على مربّع البحث، وتظهر عمليات البحث عن puppetaria على النحو التالي:

(async () => {
   const browser = await puppeteer.launch({ headless: false });
   const page = await browser.newPage();
   await page.goto('https://developers.google.com/', { waitUntil: 'load' });
   // Find the search box using a suitable CSS selector.
   const search = await page.$('devsite-search > form > div.devsite-search-container');
   // Click to expand search box and focus it.
   await search.click();
   // Enter search string and press Enter.
   await search.type('puppetaria');
   await search.press('Enter');
 })();

وبالتالي، تشكّل كيفية تحديد العناصر باستخدام أدوات اختيار طلبات البحث جزءًا محدّدًا من تجربة Puppeteer. حتى الآن، تقتصر أدوات الاختيار في Puppeteer على أدوات اختيار CSS وXPath التي قد يكون لها عيوب، حتى وإن كانت فعّالة للغاية، من حيث التفاعل مع المتصفِّحات في النصوص البرمجية.

أدوات الاختيار النحوية مقابل الدلالية

أدوات اختيار لغة CSS هي ذات طبيعة بنية، وهي مرتبطة ارتباطًا وثيقًا بالأعمال الداخلية للتمثيل النصي لشجرة نموذج العناصر في المستند (DOM) بمعنى أنها تشير إلى المعرفات وأسماء الفئات من DOM. وعلى هذا النحو، فإنها توفر أداة متكاملة لمطوّري البرامج على الويب لتعديل أو إضافة أنماط إلى عنصر في صفحة، ولكن في هذا السياق، يمتلك المطوِّر التحكّم الكامل في الصفحة وشجرة نموذج العناصر في المستند (DOM) التابعة لها.

من ناحية أخرى، يُعدّ نص Puppeteer البرمجي مراقبًا خارجيًا للصفحة، لذا عند استخدام أدوات اختيار لغة CSS في هذا السياق، فإنّه يقدّم افتراضات خفيّة حول كيفية تنفيذ الصفحة ولا يمكن لنص برمجي مكتبة Puppeteer التحكم فيها.

ويتمثل التأثير في أن هذه النصوص البرمجية يمكن أن تكون هشة وعُرضة لتغييرات شفرة المصدر. لنفترض على سبيل المثال، أن واحدًا يستخدم نصوص Puppeteer البرمجية للاختبار الآلي لتطبيق ويب يحتوي على العُقدة <button>Submit</button> كعنصر ثانوي ثالث للعنصر body. قد يبدو مقتطف واحد من حالة الاختبار كما يلي:

const button = await page.$('body:nth-child(3)'); // problematic selector
await button.click();

ونستخدم هنا أداة الاختيار 'body:nth-child(3)' للعثور على زر الإرسال، ولكنّ هذا يرتبط ارتباطًا وثيقًا بهذا الإصدار من صفحة الويب. وإذا تمت إضافة عنصر فوق الزر لاحقًا، فلن تعمل أداة الاختيار هذه بعد ذلك!

هذه ليست أخبارًا بالنسبة إلى اختبار المؤلفين: يحاول مستخدمو الدمى من الآن اختيار أدوات اختيار مناسبة لهذه التغييرات. من خلال Puppetaria، نمنح المستخدمين أداة جديدة في هذه المهمة.

يوفّر Puppeteer الآن معالج طلب بحث بديلاً استنادًا إلى طلب البحث في شجرة تسهيل الاستخدام بدلاً من الاعتماد على أدوات اختيار لغة CSS. تتمثل الفلسفة الأساسية هنا في أنه إذا لم يتغير العنصر الملموس الذي نريد تحديده، فلن يكون ينبغي أن تتغير عقدة إمكانية الوصول المقابلة أيضًا.

نُطلق على هذه الأدوات اسم "أدوات اختيار ARIA"، ونتيح إجراء طلبات البحث عن اسم ودور شجرة تسهيل الاستخدام الذي تم احتسابه. مقارنةً بأدوات اختيار لغة CSS، تتسم هذه السمات بطبيعتها الدلالية. وهي غير مرتبطة بالخصائص النحوية لـ DOM ولكن بدلاً من ذلك واصفات لكيفية مراقبة الصفحة من خلال التكنولوجيا المساعِدة مثل برامج قراءة الشاشة.

في مثال النص البرمجي للاختبار أعلاه، يمكننا بدلاً من ذلك استخدام المحدِّد aria/Submit[role="button"] لاختيار الزر المطلوب، حيث يشير Submit إلى اسم العنصر الذي يمكن الوصول إليه:

const button = await page.$('aria/Submit[role="button"]');
await button.click();

والآن، إذا قرّرنا في وقت لاحق تغيير المحتوى النصي للزر من Submit إلى Done، سيتعذّر إجراء الاختبار مرة أخرى، ولكن في هذه الحالة يكون ذلك أمرًا مرغوبًا، إذ يؤدي تغيير اسم الزر إلى تغيير محتوى الصفحة بعكس طريقة عرضها المرئي أو كيفية تنظيمها في DOM. ويجب أن تحذّرنا اختباراتنا من هذه التغييرات لضمان أن تكون هذه التغييرات مقصودة.

بالعودة إلى المثال الأكبر باستخدام شريط البحث، يمكننا الاستفادة من معالج aria الجديد واستبدال

const search = await page.$('devsite-search > form > div.devsite-search-container');

مع

const search = await page.$('aria/Open search[role="button"]');

لتحديد موقع شريط البحث!

نعتقد بشكل عام أنّ استخدام أدوات اختيار ARIA هذه يمكن أن يوفّر الفوائد التالية لمستخدمي Puppeteer:

  • جعل أدوات الاختيار في النصوص البرمجية للاختبار أكثر مرونة في التعامل مع تغييرات رمز المصدر
  • تسهيل قراءة النصوص البرمجية للاختبار (الأسماء التي يمكن الوصول إليها هي واصفات دلالية).
  • تحفيز الممارسات الجيدة لتعيين خصائص إمكانية الوصول للعناصر.

تتعمق بقية هذه المقالة في التفاصيل حول كيفية تنفيذ مشروع Puppetaria.

عملية التصميم

الخلفية

كما هو دافع أعلاه، نريد تمكين عناصر الاستعلام حسب اسمها ودورها الذي يمكن الوصول إليه. هذه هي خصائص شجرة تسهيل الاستخدام، وهي مزدوجة لشجرة نموذج العناصر في المستند (DOM) المعتادة، وتستخدمها أجهزة مثل برامج قراءة الشاشة لعرض صفحات الويب.

من خلال النظر في مواصفات حساب الاسم الذي يمكن الوصول إليه، يتضح أن حساب اسم عنصر ما هو مهمة غير بسيطة، لذلك قررنا من البداية أننا أردنا إعادة استخدام البنية الأساسية الحالية لـ Chromium لإجراء ذلك.

كيفية تعاملنا مع عملية التنفيذ

حتى عندما نحصر أنفسنا باستخدام شجرة تسهيل الاستخدام في Chromium، هناك بعض الطرق التي يمكننا من خلالها تنفيذ استعلام ARIA في Puppeteer. لمعرفة السبب، لنرَ أولًا كيف يتحكم تطبيق Puppeteer في المتصفح.

يعرض المتصفّح واجهة تصحيح الأخطاء عبر بروتوكول يُسمى بروتوكول Chrome DevTools (CDP). يعرض ذلك وظائف مثل "إعادة تحميل الصفحة" أو "تنفيذ هذا الجزء من JavaScript في الصفحة وإعادة النتيجة" من خلال واجهة لا تستخدم اللغة.

يستخدم كل من الواجهة الأمامية لـ DevTools وأداة Puppeteer لغة CDP للتواصل مع المتصفح. ولتنفيذ أوامر بروتوكول "أدوات مطوّري البرامج في Chrome"، تتوفّر البنية الأساسية لأدوات مطوّري البرامج في جميع مكوّنات Chrome: سواء في المتصفّح أو في العارض وما إلى ذلك. يتولّى CDP توجيه الأوامر إلى المكان الصحيح.

يتم تنفيذ إجراءات محرّك الدمى، مثل تعبيرات طلب البحث والنقر والتقييم، من خلال الاستفادة من أوامر CDP، مثل Runtime.evaluate، التي تقيّم JavaScript مباشرةً في سياق الصفحة وتعرض النتيجة. تستخدم إجراءات مكتبة الدمى الأخرى مثل محاكاة نقص رؤية الألوان أو التقاط لقطات شاشة أو تسجيل آثار الأنشطة، بروتوكول CDP للتواصل مباشرةً مع عملية عرض Blink.

مركز CDP

وهذا يتركنا بالفعل مسارين لتنفيذ وظيفة الاستعلام؛ يمكننا:

  • كتابة منطق طلب البحث بلغة JavaScript وإدخال ذلك في الصفحة باستخدام Runtime.evaluate، أو
  • استخدِم نقطة نهاية CDP التي يمكنها الوصول إلى شجرة تسهيل الاستخدام والاستعلام عنها مباشرةً في عملية Blink.

قمنا بتنفيذ 3 نماذج أوّلية:

  • اجتياز JS DOM - استنادًا إلى إدخال JavaScript في الصفحة
  • اجتياز Puppeteer AXTree: استنادًا إلى استخدام إمكانية وصول CDP الحالية إلى شجرة تسهيل الاستخدام
  • اجتياز CDP DOM: باستخدام نقطة نهاية CDP جديدة مصمّمة لغرض معيّن لإجراء طلبات بحث في شجرة تسهيل الاستخدام.

اجتياز JavaScript DOM

يُجري هذا النموذج الأولي عملية فحص نموذج كائن المستند (DOM) بالكامل ويستخدم element.computedName وelement.computedRole، التي يتم ربطها في علامة تشغيل ComputedAccessibilityInfo، لاسترداد اسم ودور كل عنصر أثناء الاجتياز.

اجتياز اختبار Puppeteer AXTree

هنا، نسترجع بدلاً من ذلك شجرة إمكانية الوصول الكاملة من خلال CDP ونجتازها في Puppeteer. بعد ذلك، يتم ربط عُقد إمكانية الوصول الناتجة بعُقد DOM.

اجتياز CDP DOM

بالنسبة لهذا النموذج الأولي، قمنا بتنفيذ نقطة نهاية CDP جديدة خصيصًا للاستعلام عن شجرة إمكانية الوصول. وبهذه الطريقة، يمكن أن يحدث الاستعلام في الخلفية من خلال تنفيذ C++ بدلاً من سياق الصفحة عبر JavaScript.

مقياس أداء اختبار الوحدة

يقارن الشكل التالي إجمالي وقت التشغيل للاستعلام عن أربعة عناصر 1000 مرة للنماذج الأوّلية الثلاثة. تم تنفيذ مقياس الأداء من خلال 3 إعدادات مختلفة تختلف في حجم الصفحة وما إذا تم تفعيل التخزين المؤقت لعناصر تسهيل الاستخدام أم لا.

المعيار: إجمالي وقت التشغيل للاستعلام عن أربعة عناصر 1000 مرة

من الواضح تمامًا أن هناك فجوة كبيرة في الأداء بين آلية الاستعلام المستندة إلى CDP والآلية الأخرى المنفذة في Puppeteer فقط، ويبدو أن الفرق النسبي يزداد بشكل كبير مع حجم الصفحة. من المثير للاهتمام إلى حد ما أن نرى أنّ النموذج الأولي لاجتياز JavaScript لـ JS DOM يستجيب بشكل جيد للغاية لتمكين التخزين المؤقت الخاص بإمكانية الوصول. عند إيقاف التخزين المؤقت، يتم حساب شجرة تسهيل الاستخدام عند الطلب ويتم تجاهل الشجرة بعد كل تفاعل في حال إيقاف النطاق. يؤدي تفعيل النطاق إلى تخزين Chromium مؤقتًا في الشجرة المحسوبة بدلاً من ذلك.

بالنسبة إلى اجتياز JavaScript DOM، نطلب الاسم والدور المتاحَين لكل عنصر أثناء الاجتياز، ولذلك إذا كان التخزين المؤقت غير مفعّل، سيحسب Chromium شجرة تسهيل الاستخدام لكل عنصر ننتقل إليه ويتجاهله. من ناحية أخرى، بالنسبة للمناهج المستندة إلى CDP، يتم تجاهل الشجرة فقط بين كل استدعاء إلى CDP، أي لكل استعلام. تستفيد هذه الأساليب أيضًا من تفعيل التخزين المؤقت، حيث تستمر شجرة تسهيل الاستخدام في جميع استدعاءات CDP، ولكن تعزيز الأداء أصغر نسبيًا.

فعلى الرغم من أن تمكين التخزين المؤقت يبدو مرغوبًا هنا، إلا أنه يتضمن تكلفة استخدام إضافية للذاكرة. بالنسبة إلى نصوص Puppeteer البرمجية مثل تسجيل ملفات التتبع، قد يكون ذلك مشكلة. لذلك، قررنا عدم تفعيل التخزين المؤقت لشجرة تسهيل الاستخدام تلقائيًا. يمكن للمستخدمين تفعيل ميزة التخزين المؤقت بأنفسهم من خلال تفعيل نطاق تسهيل الاستخدام في CDP.

مقياس أداء حزمة اختبار أدوات مطوّري البرامج

أظهر المعيار السابق أن تنفيذ آلية الاستعلام لدينا في طبقة CDP يمنح تعزيز الأداء في سيناريو اختبار الوحدة السريري.

لمعرفة ما إذا كان الفرق واضحًا بما فيه الكفاية بحيث يمكن ملاحظته في سيناريو أكثر واقعية لتشغيل مجموعة اختبار كاملة، أجرينا تصحيحًا على حزمة الاختبار الشامل في أدوات مطوّري البرامج للاستفادة من النماذج الأولية المستندة إلى JavaScript وCDP ومقارنة أوقات التشغيل. في مقياس الأداء هذا، غيّرنا إجمالي 43 أداة اختيار من [aria-label=…] إلى معالج طلبات البحث المخصّص aria/…، وطبّقنا بعد ذلك باستخدام كل نموذج من النماذج الأولية.

تُستخدَم بعض أدوات الاختيار عدّة مرات في النصوص البرمجية للاختبار، لذا كان العدد الفعلي لعمليات التنفيذ لمعالج طلبات البحث aria هو 113 لكل عملية تشغيل للمجموعة. كان العدد الإجمالي لتحديدات الاستعلام 2253، لذلك لم يحدث سوى جزء من اختيارات الاستعلام من خلال النماذج الأولية.

مقياس الأداء: حزمة اختبار e2e

كما هو موضح في الشكل أعلاه، هناك فرق واضح في إجمالي وقت التشغيل. البيانات مزعجة جدًا بحيث لا يمكن استنتاج أي شيء محدد، ولكن من الواضح أن فجوة الأداء بين النموذجين الأوليين تظهر في هذا السيناريو أيضًا.

نقطة نهاية CDP جديدة

في ضوء المعايير المذكورة أعلاه، وبما أنّ نهج الإطلاق المستند إلى العلامات لم يكن مرغوبًا فيه بشكل عام، قرّرنا المضي قدمًا في تنفيذ أمر CDP جديد للاستعلام عن شجرة تسهيل الاستخدام. أما الآن، فينبغي علينا اكتشاف واجهة نقطة النهاية الجديدة هذه.

بالنسبة إلى حالة الاستخدام في Puppeteer، نحتاج إلى نقطة النهاية لاستخدام ما يُسمّى RemoteObjectIds كوسيطة، ولتمكيننا من العثور على عناصر DOM المقابلة بعد ذلك، يجب أن تعرض نقطة النهاية قائمة بالكائنات التي تحتوي على backendNodeIds لعناصر DOM.

وكما هو موضّح في الرسم البياني أدناه، جربنا عددًا قليلاً من الأساليب التي تلبي هذه الواجهة. بعد ذلك، تبيَّن لنا أنّ حجم العناصر المعروضة، أي ما إذا كنا قد عرضنا العُقد الكاملة لإمكانية الوصول أو العنصر backendNodeIds فقط، لم يكن هناك أي فرق واضح. من ناحية أخرى، وجدنا أنّ استخدام NextInPreOrderIncludingIgnored الحالي كان اختيارًا سيئًا لتنفيذ منطق الاجتياز هنا، لأنّ ذلك أدّى إلى تباطؤ ملحوظ.

المقارنة: مقارنة نماذج اجتياز AXTree المستندة إلى CDP

إنهاء كل شيء

والآن، بعدما طبّقنا نقطة نهاية CDP، نفّذنا معالج طلب البحث على جانب Puppeteer. تمثّلت صعوبة العمل هنا في إعادة هيكلة رمز معالجة طلبات البحث للسماح بحل طلبات البحث مباشرةً من خلال CDP بدلاً من إجراء طلبات بحث من خلال JavaScript تم تقييمه في سياق الصفحة.

ما هي الخطوات التالية؟

تم شحن معالج aria الجديد مع Puppeteer v5.4.0 كمعالج مُدمَج لطلبات البحث. نحن نتطلع إلى معرفة كيفية استخدام المستخدمين لهذه الميزة في نصوصهم البرمجية التجريبية، ولا يسعنا الانتظار لسماع أفكارك حول كيفية جعل ذلك أكثر فائدة.

تنزيل قنوات المعاينة

يمكنك استخدام إصدار Canary أو إصدار مطوّري البرامج أو الإصدار التجريبي من Chrome ليكون متصفِّح التطوير التلقائي. تتيح لك قنوات المعاينة هذه الوصول إلى أحدث ميزات أدوات مطوّري البرامج، واختبار واجهات برمجة التطبيقات المتطورة للأنظمة الأساسية للويب، والعثور على المشاكل في موقعك الإلكتروني قبل أن يتمكّن المستخدمون من ذلك.

التواصل مع فريق "أدوات مطوري البرامج في Chrome"

يمكنك استخدام الخيارات التالية لمناقشة الميزات والتغييرات الجديدة في المشاركة، أو أي موضوع آخر مرتبط بـ "أدوات مطوري البرامج".

  • يمكنك إرسال اقتراحات أو ملاحظات إلينا عبر crbug.com.
  • يمكنك الإبلاغ عن مشكلة في "أدوات مطوري البرامج" باستخدام خيارات إضافية   المزيد > مساعدة > الإبلاغ عن مشاكل في "أدوات مطوري البرامج" في "أدوات مطوري البرامج".
  • نشر تغريدة على @ChromeDevTools
  • يمكنك إضافة تعليقات على الميزات الجديدة في الفيديوهات على YouTube في "أدوات مطوري البرامج" أو الفيديوهات على YouTube التي تتضمّن نصائح حول أدوات مطوّري البرامج.