Menguji penghentian pekerja layanan dengan Puppeteer

Panduan ini menjelaskan cara membuat ekstensi yang lebih andal dengan menguji penghentian service worker menggunakan Puppeteer. Penting untuk bersiap menangani penghentian kapan saja karena hal ini dapat terjadi tanpa peringatan sehingga menyebabkan hilangnya status non-persisten di service worker. Oleh karena itu, ekstensi harus menyimpan status penting dan dapat menangani permintaan segera setelah dimulai kembali saat ada peristiwa yang harus ditangani.

Sebelum memulai

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

Anda juga perlu menginstal Node.JS yang merupakan runtime tempat Puppeteer dibangun.

Langkah 1: Mulai project Node.js Anda

Buat file berikut di direktori baru. Bersama-sama, keduanya akan membuat project Node.js baru dan menyediakan struktur dasar rangkaian 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": "^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;
});

Perhatikan bahwa pengujian kita memuat test-extension dari repositori contoh. Handler untuk browser.runtime.onMessage bergantung pada status yang ditetapkan di handler untuk peristiwa browser.runtime.onInstalled. Akibatnya, konten data akan hilang saat service worker dihentikan dan merespons pesan di masa mendatang akan gagal. Kita akan memperbaikinya setelah menulis pengujian.

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

Langkah 2: Instal 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 kami, mengklik elemen tombol, dan menunggu respons dari 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');
});

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

Langkah 4: Hentikan pekerja layanan

Tambahkan fungsi bantuan berikut yang menghentikan service worker 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, perbarui pengujian Anda dengan kode berikut. Sekarang, hentikan service worker 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 service worker tidak merespons setelah dihentikan.

Langkah 6: Perbaiki service worker

Selanjutnya, perbaiki pekerja layanan dengan menghapus ketergantungannya pada status sementara. Perbarui test-extension untuk menggunakan kode berikut, yang disimpan di service-worker-fixed.js di repositori.

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

Di sini, kita menyimpan versi ke browser.storage.local, bukan variabel global untuk mempertahankan status di antara masa aktif service worker. Karena penyimpanan hanya dapat diakses secara asinkron, kita juga menampilkan nilai benar dari pemroses onMessage untuk memastikan callback sendResponse tetap aktif.

Langkah 7: Jalankan pengujian lagi

Jalankan pengujian lagi dengan npm start. Sekarang seharusnya berhasil.

Langkah berikutnya

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

  • Bangun suite pengujian Anda untuk mendukung pengujian dengan atau tanpa penghentian service worker yang tidak terduga. Kemudian, Anda dapat menjalankan kedua mode secara terpisah untuk memperjelas penyebab kegagalan.
  • Tulis kode untuk menghentikan service worker di titik acak dalam pengujian. Cara ini dapat menjadi cara yang baik untuk menemukan masalah yang mungkin sulit diprediksi.
  • Belajarlah dari kegagalan pengujian dan cobalah untuk membuat kode secara defensif di masa mendatang. Misalnya, tambahkan aturan linting untuk mencegah penggunaan variabel global dan coba pindahkan data ke status yang lebih persisten.