يوضّح هذا الدليل كيفية إنشاء إضافات أكثر فعالية من خلال اختبار إنهاء عامل الخدمة باستخدام Puppeteer. من المهم أن تكون مستعدًا للتعامل مع عملية الإنهاء في أي وقت، لأنّ ذلك قد يحدث بدون سابق إنذار، ما يؤدي إلى فقدان أي حالة غير ثابتة في عامل الخدمة. وبالتالي، يجب أن تحفظ الإضافات الحالة المهمة وأن تكون قادرة على التعامل مع الطلبات فور إعادة تشغيلها عند توفّر حدث يجب التعامل معه.
قبل البدء
استنسِخ مستودع chrome-extensions-samples أو نزِّله.
سنستخدم الإضافة التجريبية في
/functional-samples/tutorial.terminate-sw/test-extension التي ترسل رسالة
إلى عامل الخدمة في كل مرة يتم فيها النقر على زر، وتضيف نصًا إلى الصفحة
في حال تلقّي ردّ.
عليك أيضًا تثبيت Node.JS وهو وقت التشغيل الذي تم إنشاء Puppeteer عليه.
الخطوة 1: بدء مشروع Node.js
أنشئ الملفات التالية في دليل جديد. ويعملان معًا على إنشاء مشروع جديد في Node.js وتوفير البنية الأساسية لمجموعة اختبارات Puppeteer باستخدام Jest كأداة لتنفيذ الاختبارات. يمكنك الاطّلاع على مقالة اختبار إضافات Chrome باستخدام Puppeteer لمعرفة المزيد من التفاصيل حول عملية الإعداد هذه.
package.json:
{
"name": "puppeteer-demo",
"version": "1.0",
"dependencies": {
"jest": "^29.7.0",
"puppeteer": "^24.8.1"
},
"scripts": {
"start": "jest ."
},
"devDependencies": {
"@jest/globals": "^29.7.0"
}
}
index.test.js:
const puppeteer = require('puppeteer');
const SAMPLES_REPO_PATH = 'PATH_TO_SAMPLES_REPOSITORY';
const EXTENSION_PATH = `${SAMPLES_REPO_PATH}/functional-samples/tutorial.terminate-sw/test-extension`;
const EXTENSION_ID = 'gjgkofgpcmpfpggbgjgdfaaifcmoklbl';
let browser;
beforeEach(async () => {
browser = await puppeteer.launch({
// Set to 'new' to hide Chrome if running as part of an automated build.
headless: false,
pipe: true,
enableExtensions: [EXTENSION_PATH]
});
});
afterEach(async () => {
await browser.close();
browser = undefined;
});
يُرجى العِلم أنّ الاختبار يحمّل test-extension من مستودع النماذج.
يعتمد معالج browser.runtime.onMessage على الحالة التي تم ضبطها في معالج الحدث browser.runtime.onInstalled. نتيجةً لذلك، سيتم فقدان محتوى data
عند إيقاف عامل الخدمة، وسيتعذّر الردّ على أي رسائل مستقبلية. سنحلّ هذه المشكلة بعد كتابة الاختبار.
service-worker-broken.js:
let data;
browser.runtime.onInstalled.addListener(() => {
data = { version: browser.runtime.getManifest().version };
});
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse(data.version);
});
الخطوة 2: تثبيت التبعيات
نفِّذ الأمر npm install لتثبيت التبعيات المطلوبة.
الخطوة 3: كتابة اختبار أساسي
أضِف الاختبار التالي إلى أسفل index.test.js. يؤدي ذلك إلى فتح صفحة الاختبار من إضافة الاختبار، والنقر على عنصر الزر، وانتظار رد من عامل الخدمة.
test('can message service worker', async () => {
const page = await browser.newPage();
await page.goto(`chrome-extension://${EXTENSION_ID}/page.html`);
// Message without terminating service worker
await page.click('button');
await page.waitForSelector('#response-0');
});
يمكنك تشغيل الاختبار باستخدام npm start، ومن المفترض أن تلاحظ اكتماله بنجاح.
الخطوة 4: إنهاء عمل عامل الخدمة
أضِف دالة المساعد التالية التي توقف عامل الخدمة:
/**
* Stops the service worker associated with a given extension ID. This is done
* by creating a new Chrome DevTools Protocol session, finding the target ID
* associated with the worker and running the Target.closeTarget command.
*
* @param {Page} browser Browser instance
* @param {string} extensionId Extension ID of worker to terminate
*/
async function stopServiceWorker(browser, extensionId) {
const host = `chrome-extension://${extensionId}`;
const target = await browser.waitForTarget((t) => {
return t.type() === 'service_worker' && t.url().startsWith(host);
});
const worker = await target.worker();
await worker.close();
}
أخيرًا، عدِّل الاختبار باستخدام الرمز التالي. الآن، أوقِف عامل الخدمة وانقر على الزر مرة أخرى للتأكّد من تلقّي ردّ.
test('can message service worker when terminated', async () => {
const page = await browser.newPage();
await page.goto(`chrome-extension://${EXTENSION_ID}/page.html`);
// Message without terminating service worker
await page.click('button');
await page.waitForSelector('#response-0');
// Terminate service worker
await stopServiceWorker(page, EXTENSION_ID);
// Try to send another message
await page.click('button');
await page.waitForSelector('#response-1');
});
الخطوة 5: تنفيذ الاختبار
تشغيل npm start يجب أن يتعذّر إجراء الاختبار، ما يشير إلى أنّ عامل الخدمة لم يستجب بعد إيقافه.
الخطوة 6: إصلاح عامل الخدمة
بعد ذلك، عليك إصلاح عامل الخدمة من خلال إزالة اعتماده على الحالة المؤقتة. عدِّل
الإضافة الاختبارية لاستخدام الرمز التالي المخزَّن في
service-worker-fixed.js في المستودع.
service-worker-fixed.js:
browser.runtime.onInstalled.addListener(() => {
browser.storage.local.set({ version: browser.runtime.getManifest().version });
});
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
browser.storage.local.get('version').then((data) => {
sendResponse(data.version);
});
return true;
});
هنا، نحفظ الإصدار في browser.storage.local بدلاً من متغير عام
للحفاظ على الحالة بين مدة بقاء عامل الخدمة. بما أنّه لا يمكن الوصول إلى مساحة التخزين إلا بشكل غير متزامن، نعرض أيضًا القيمة "صحيح" من أداة معالجة الحدث onMessage للتأكّد من أنّ عملية الاسترجاع sendResponse تظل نشطة.
الخطوة 7: إعادة تشغيل الاختبار
أعِد إجراء الاختبار باستخدام npm start. من المفترض أن يتم اجتياز الاختبار الآن.
الخطوات التالية
يمكنك الآن تطبيق النهج نفسه على الإضافة الخاصة بك. يُرجى مراعاة ما يلي:
- أنشئ مجموعة الاختبارات لتتيح تشغيلها مع إنهاء غير متوقّع لبرنامج عامل الخدمة أو بدونه. يمكنك بعد ذلك تشغيل كلا الوضعين بشكل منفصل لتوضيح سبب حدوث الخطأ.
- اكتب رمزًا برمجيًا لإنهاء عامل الخدمة في نقاط عشوائية ضمن الاختبار. يمكن أن تكون هذه طريقة جيدة لاكتشاف المشاكل التي قد يصعب التنبؤ بها.
- التعلم من حالات فشل الاختبار ومحاولة الترميز بشكل دفاعي في المستقبل على سبيل المثال، أضِف قاعدة تدقيق أسلوب البرمجة لتثبيط استخدام المتغيرات العامة وحاوِل نقل البيانات إلى حالة أكثر ثباتًا.