Menguji penghentian pekerja layanan dengan Puppeteer

Panduan ini menjelaskan cara membuat ekstensi yang lebih andal dengan menguji penghentian pekerja layanan menggunakan Puppeteer. Bersiap untuk menangani penghentian kapan pun adalah hal penting karena hal ini dapat terjadi tanpa peringatan yang dapat mengakibatkan hilangnya status non-persisten di pekerja layanan. Akibatnya, ekstensi harus menyimpan status penting dan dapat menangani permintaan segera setelah dimulai lagi saat ada peristiwa yang perlu ditangani.

Sebelum memulai

Clone atau download repositori chrome-extensions-samples. Kita akan menggunakan ekstensi pengujian di /functional-samples/tutorial.terminate-sw/test-extension yang mengirim pesan ke pekerja layanan setiap kali tombol diklik dan menambahkan teks ke halaman jika respons diterima.

Anda juga harus menginstal Node.JS yang merupakan runtime yang mendasari Puppeteer.

Langkah 1: Mulai project Node.js Anda

Buat file berikut di direktori baru. Bersama-sama, mereka membuat project Node.js baru dan menyediakan struktur dasar paket pengujian Puppeteer menggunakan Jest sebagai runner pengujian. Lihat Menguji Ekstensi Chrome dengan Puppeteer untuk mempelajari penyiapan ini secara lebih mendetail.

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;
});

Perhatikan bahwa pengujian kita memuat test-extension dari repositori contoh. Pengendali untuk chrome.runtime.onMessage bergantung pada status yang ditetapkan dalam pengendali untuk peristiwa chrome.runtime.onInstalled. Akibatnya, konten data akan hilang saat pekerja layanan dihentikan dan tidak dapat merespons pesan mendatang. Kita akan memperbaiki masalah ini setelah menulis pengujian.

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);
});

Langkah 2: Menginstal dependensi

Jalankan npm install untuk menginstal dependensi yang diperlukan.

Langkah 3: Tulis pengujian dasar

Tambahkan pengujian berikut ke bagian bawah index.test.js. Tindakan ini akan membuka halaman pengujian dari ekstensi pengujian kita, mengklik elemen tombol, dan menunggu respons dari pekerja layanan.

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

Anda dapat menjalankan pengujian dengan npm start dan akan melihat bahwa pengujian berhasil diselesaikan.

Langkah 4: Hentikan pekerja layanan

Tambahkan fungsi bantuan berikut yang menghentikan pekerja layanan Anda:

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

Terakhir, update pengujian Anda dengan kode berikut. Sekarang hentikan pekerja layanan dan klik tombol lagi untuk memeriksa apakah Anda menerima respons.

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

Langkah 5: Jalankan pengujian

Jalankan npm start. Pengujian Anda akan gagal, yang menunjukkan bahwa pekerja layanan tidak merespons setelah dihentikan.

Langkah 6: Perbaiki pekerja layanan

Selanjutnya, perbaiki pekerja layanan dengan menghapus ketergantungannya pada status sementara. Update ekstensi pengujian untuk menggunakan kode berikut, yang disimpan di service-worker-fixed.js dalam repositori.

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;
});

Di sini, kita menyimpan versi ke chrome.storage.local, bukan variabel global, untuk mempertahankan status di antara masa aktif pekerja layanan. Karena penyimpanan hanya dapat diakses secara asinkron, kami juga menampilkan true dari pemroses onMessage untuk memastikan callback sendResponse tetap aktif.

Langkah 7: Jalankan pengujian lagi

Jalankan lagi pengujian dengan npm start. Seharusnya sekarang lulus.

Langkah berikutnya

Sekarang Anda dapat menerapkan pendekatan yang sama ke ekstensi Anda sendiri. Pertimbangkan hal berikut:

  • Bangun rangkaian pengujian Anda agar dapat berjalan dengan atau tanpa penghentian pekerja layanan yang tidak terduga. Anda kemudian dapat menjalankan kedua mode tersebut satu per satu untuk memperjelas penyebab kegagalan.
  • Tulis kode untuk menghentikan pekerja layanan pada titik acak dalam pengujian. Langkah ini dapat menjadi cara yang efektif untuk menemukan masalah yang mungkin sulit diprediksi.
  • Belajar dari kegagalan uji dan cobalah membuat kode defensif di masa mendatang. Misalnya, tambahkan aturan lint untuk mencegah penggunaan variabel global dan cobalah memindahkan data ke status yang lebih persisten.