تست خاتمه کارگر خدمات با Puppeteer

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

قبل از شروع

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

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

مرحله ۱: پروژه Node.js خود را شروع کنید

فایل‌های زیر را در یک دایرکتوری جدید ایجاد کنید. این فایل‌ها با هم یک پروژه جدید Node.js ایجاد می‌کنند و ساختار اولیه یک مجموعه تست Puppeteer را با استفاده از Jest به عنوان یک اجراکننده تست ارائه می‌دهند. برای کسب اطلاعات بیشتر در مورد این تنظیمات، به بخش «تست افزونه‌های کروم با Puppeteer» مراجعه کنید.

بسته.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 با خاتمه یافتن سرویس ورکر از بین می‌روند و پاسخ به هرگونه پیام بعدی با شکست مواجه خواهد شد. ما این مشکل را پس از نوشتن تست خود برطرف خواهیم کرد.

سرویس-ورکر-خراب.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 در مخزن ذخیره شده است، استفاده کند.

سرویس-ورکر-ثابت.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 ذخیره می‌کنیم تا وضعیت بین طول عمر سرویس ورکر حفظ شود. از آنجایی که دسترسی به فضای ذخیره‌سازی فقط به صورت ناهمگام امکان‌پذیر است، ما همچنین مقدار true را از شنونده onMessage برمی‌گردانیم تا مطمئن شویم که فراخوانی sendResponse فعال می‌ماند.

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

تست را دوباره با npm start اجرا کنید. اکنون باید با موفقیت انجام شود.

مراحل بعدی

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

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