使用 Puppeteer 測試 Service Worker 終止事宜

本指南將說明如何使用 Puppeteer 測試服務 worker 終止作業,以便建構更健全的擴充功能。隨時準備處理終止作業非常重要,因為這可能發生在沒有警告的情況下,導致服務工作站中任何非持久狀態都會遺失。因此,擴充功能必須儲存重要狀態,並在有事件需要處理時,一經重新啟動,就能夠處理要求。

事前準備

複製或下載 chrome-extensions-samples 存放區。 我們會使用 /functional-samples/tutorial.terminate-sw/test-extension 中的測試擴充功能,每次按下按鈕時,該擴充功能都會向服務工作者傳送訊息,並在收到回應時在頁面上新增文字。

您也需要安裝 Node.JS,這是 Puppeteer 的執行階段。

步驟 1:開始 Node.js 專案

在新目錄中建立下列檔案。協助他們建立 Node.js 專案,並提供 Puppeteer 測試套件的基本結構 以 Jest 做為測試執行工具。詳情請見 如要瞭解這項設定,請使用 Puppeteer 測試 Chrome 擴充功能 一起來看看吧

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-extensionchrome.runtime.onMessage 的處理常式會依賴 chrome.runtime.onInstalled 事件處理常式中設定的狀態。因此,當服務工作站終止時,data 的內容就會遺失,且無法回應任何未來的訊息。我們會在編寫測試後修正這個問題。

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

新增下列輔助函式,用於終止服務工作者:

/**  * 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。 您的測試應會失敗,這表示服務工作者在終止後未回應。

步驟 6:修正 Service Worker

接著,移除對暫時狀態的依賴,藉此修正 Service Worker。最新消息 測試擴充功能,以便使用下列程式碼 (儲存在 存放區中的 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 而非全域變數,以便在服務工作站生命週期之間保留狀態。由於儲存空間只能以非同步方式存取,我們也會從 onMessage 事件監聽器傳回 true,確保 sendResponse 回呼持續運作。

步驟 7:再次執行測試

使用 npm start 再次執行測試。現在應該會通過。

後續步驟

您現在可以將相同做法套用至自己的擴充功能。假設 包括:

  • 建構測試套件,以便在發生或未發生服務 worker 意外終止的情況下執行。你可以分別執行兩種模式 造成失敗的原因
  • 撰寫程式碼,在測試中的隨機位置終止 Service Worker。 這樣就能輕鬆發現難以預測的問題。
  • 從測試失敗中學習,日後嘗試執行防禦性程式碼。舉例來說,您可以新增 linting 規則,避免使用全域變數,並嘗試將資料移至更持久的狀態。