آزمایش فسخ کارگزار سرویس با Puppeteer

این راهنما توضیح می‌دهد که چگونه با آزمایش کردن خاتمه کارگر سرویس بااستفاده از Puppeteer، افزونه‌های قوی‌تری بسازید. آماده بودن برای رسیدگی به فسخ در هر زمانی مهم است زیرا این اتفاق می‌تواند بدون هشدار رخ دهد و منجر به ازدست رفتن هرگونه وضعیت غیرپایدار در سرویس‌کار شود. درنتیجه، افزونه‌ها باید وضعیت مهم را ذخیره کنند و بتوانند درخواست‌ها را به‌محض راه‌اندازی مجدد درصورت وجود رویدادی برای رسیدگی، مدیریت کنند.

قبل‌از شروع

مخزن chrome-extensions-samples را شبیه‌سازی یا بارگیری کنید. از افزونه آزمایشی در /functional-samples/tutorial.terminate-sw/test-extension استفاده خواهیم کرد که هربار روی دکمه‌ای کلیک می‌شود، پیامی به کارگر سرویس ارسال می‌کند و اگر پاسخی دریافت شود، نوشتاری به صفحه اضافه می‌کند.

همچنین باید Node.JS را نصب کنید که زمان اجرای Puppeteer بر آن ساخته شده است.

مرحله ۱: شروع پروژه 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);
});

مرحله ۲: نصب وابستگی‌ها

برای نصب وابستگی‌های لازم، npm install را اجرا کنید.

مرحله ۳: نوشتن آزمایش پایه

آزمایش زیر را به پایین 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 اجرا کنید و باید ببینید که باموفقیت تکمیل می‌شود.

مرحله ۴: خاتمه دادن به کارگر سرویس

تابع کمکی زیر را که به کارگر سرویس شما خاتمه می‌دهد اضافه کنید:

/**
 * 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');
});

مرحله ۵: اجرای آزمایش

‫npm start را اجرا کنید. آزمایش شما باید ناموفق باشد که نشان می‌دهد کارمند خدمات پس‌از خاتمه یافتن پاسخ نداده است.

مرحله ۶: رفع مشکل کارگر خدمات

سپس، با حذف وابستگی کارگر سرویس به وضعیت موقت، آن را اصلاح کنید. افزونه آزمایشی را به‌روز کنید تا از کد زیر استفاده کند، که در 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 فعال می‌ماند.

مرحله ۷: اجرای مجدد آزمایش

آزمایش را دوباره با npm start اجرا کنید. اکنون باید قبول شود.

مراحل بعدی

اکنون می‌توانید همین رویکرد را برای افزونه خودتان اعمال کنید. موارد زیر را درنظر بگیرید:

  • مجموعه آزمون خود را طوری بسازید که از اجرا با یا بدون توقف غیرمنتظره سرویس‌گر پشتیبانی کند. سپس می‌توانید هر دو حالت را به‌طور جداگانه اجرا کنید تا مشخص‌تر شود چه چیزی باعث خرابی شده است.
  • کدی بنویسید که کارگر سرویس را در نقاط تصادفی درطول آزمایش خاتمه دهد. این کار می‌تواند روش خوبی برای کشف مشکلاتی باشد که پیش‌بینی آن‌ها دشوار است.
  • از آزمایش‌های ناموفق درس بگیرید و سعی کنید در آینده کدنویسی تدافعی داشته باشید. برای مثال، یک قانون linting اضافه کنید تا استفاده از متغیرهای سراسری را کاهش دهید و سعی کنید داده‌ها را به حالت پایدارتری منتقل کنید.