במדריך הזה נסביר איך לפתח תוספים חזקים יותר על ידי בדיקת סיום הפעילות של עובדי השירות באמצעות Puppeteer. חשוב להיות מוכנים לטפל בסיום הפעילות בכל שלב, כי זה יכול לקרות ללא אזהרה, וכתוצאה מכך כל מצב לא עקבי ב-service worker יאבד. לכן, התוספים חייבים לשמור את המצב החשוב שלהם ולהיות מסוגלים לטפל בבקשות ברגע שהם מופעלים מחדש, כשיש אירוע שצריך לטפל בו.
לפני שמתחילים
מעתיקים (clone) או מורידים את המאגר chrome-extensions-samples.
נשתמש בתוסף הבדיקה
/functional-samples/tutorial.terminate-sw/test-extension
כדי לשלוח הודעה
ל-Service Worker בכל פעם שלוחצים על לחצן ומוסיף טקסט לדף
אם מתקבלת תשובה.
תצטרכו גם להתקין את 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": "^22.1.0"
},
"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,
args: [
`--disable-extensions-except=${EXTENSION_PATH}`,
`--load-extension=${EXTENSION_PATH}`
]
});
});
afterEach(async () => {
await browser.close();
browser = undefined;
});
שימו לב שהבדיקה שלנו טוענת את test-extension
ממאגר הדוגמאות.
הטיפול באירוע chrome.runtime.onMessage
מסתמך על המצב שהוגדר בטיפול באירוע chrome.runtime.onInstalled
. כתוצאה מכך, התוכן של data
ילך לאיבוד כשה-service worker יסתיים, והתגובה להודעות עתידיות תיכשל. נתקן את הבעיה אחרי שנכתב את הבדיקה שלנו.
service-worker-broken.js:
let data;
chrome.runtime.onInstalled.addListener(() => {
data = { version: chrome.runtime.getManifest().version };
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse(data.version);
});
שלב 2: יחסי תלות של התקנות
מריצים את הפקודה npm install
כדי להתקין את יחסי התלות הנדרשים.
שלב 3: כותבים בדיקה בסיסית
מוסיפים את הבדיקה הבאה לתחתית הקובץ index.test.js
. הבדיקה תיפתח
מתוסף הבדיקה שלנו, לוחץ על אלמנט הלחצן וממתין לתגובה
מ-Service Worker.
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: סיום הפעילות של ה-service worker
מוסיפים את פונקציית העזרה הבאה שמפסיקה את הפעילות של ה-service worker:
/**
* 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
. הבדיקה אמורה להיכשל, מה שמציין ש-service worker לא הגיב אחרי שהסתיים.
שלב 6: תיקון של ה-service worker
בשלב הבא, מתקנים את ה-service worker על ידי הסרת התלות שלו במצב זמני. מעדכנים את test-extension כך שישתמש בקוד הבא, שמאוחסן ב-service-worker-fixed.js
במאגר.
service-worker-fixed.js:
chrome.runtime.onInstalled.addListener(() => {
chrome.storage.local.set({ version: chrome.runtime.getManifest().version });
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
chrome.storage.local.get('version').then((data) => {
sendResponse(data.version);
});
return true;
});
כאן אנחנו שומרים את הגרסה כ-chrome.storage.local
במקום במשתנה גלובלי
כדי לשמור על המצב בין משך החיים של קובץ השירות. מכיוון שאפשר לגשת לאחסון רק באופן אסינכרוני, אנחנו גם מחזירים את הערך true מהמאזין onMessage
כדי לוודא שהקריאה החוזרת (callback) של sendResponse
תישאר פעילה.
שלב 7: מריצים שוב את הבדיקה
מריצים את הבדיקה שוב עם npm start
. עכשיו היא אמורה לעבור.
השלבים הבאים
עכשיו אפשר להחיל את אותה גישה על התוסף שלכם. כדאי להביא בחשבון את הבאים:
- כדאי לבנות את חבילת הבדיקות כדי שתתמוך בהרצה עם שירות לא צפוי או בלעדיו פיטורין של עובדים. לאחר מכן אפשר להריץ את שני המצבים בנפרד כדי שיהיה ברור יותר מה גרם לכשל.
- כתיבת קוד לסיום של Service Worker בנקודות אקראיות בבדיקה. זו דרך טובה לגלות בעיות שקשה לחזות מראש.
- לומדים מכישלונות בבדיקה ומנסים לכתוב קוד באופן הגנתי בעתיד. עבור למשל, הוסיפו כלל איתור שגיאות בקוד כדי להימנע משימוש במשתנים גלובליים ולנסות להעביר נתונים למצב קבוע יותר.