本指南說明如何使用 Puppeteer 測試服務工作人員終止作業,建構更穩固的擴充功能。請務必隨時準備好處理終止作業,因為終止作業可能會在沒有警告的情況下發生,導致 Service Worker 中的任何非持續性狀態遺失。因此,擴充功能必須儲存重要狀態,並在有待處理的事件時,能夠在重新啟動後立即處理要求。
事前準備
複製或下載 chrome-extensions-samples 存放區。
我們將使用測試擴充功能,在每次點選按鈕時,將訊息傳送至 Service Worker,並在收到回應時,將文字新增至頁面。/functional-samples/tutorial.terminate-sw/test-extension
您也需要安裝 Node.JS,這是 Puppeteer 建構的執行階段。
步驟 1:啟動 Node.js 專案
在新的目錄中建立下列檔案。兩者會共同建立新的 Node.js 專案,並使用 Jest 做為測試執行工具,提供 Puppeteer 測試套件的基本架構。如要進一步瞭解這項設定,請參閱「使用 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-extension。browser.runtime.onMessage 的處理常式會依據 browser.runtime.onInstalled 事件處理常式中設定的狀態。因此,Service Worker 終止時,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);
});
步驟 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
新增下列輔助函式,終止 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。 測試應會失敗,表示 Service Worker 終止後未回應。
步驟 6:修正 Service Worker
接著,請移除服務工作人員對暫時狀態的依賴,修正服務工作人員。更新測試擴充功能,使用儲存在存放區 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,而不是全域變數,以便在 Service Worker 生命週期之間保留狀態。由於只能以非同步方式存取儲存空間,我們也會從 onMessage 監聽器傳回 true,確保 sendResponse 回呼保持運作。
步驟 7:再次執行測試
再次執行測試,並使用 npm start。現在應該會通過測試。
後續步驟
現在您可以將相同方法套用至自己的擴充功能。請考量下列事項:
- 建構測試套件,支援在服務工作人員意外終止時執行,或不執行。接著,你可以個別執行這兩種模式,更清楚瞭解導致失敗的原因。
- 編寫程式碼,在測試中的隨機時間點終止 Service Worker。 這有助於找出難以預測的問題。
- 從測試失敗中學習,並嘗試在未來編寫防禦性程式碼。舉例來說,您可以新增 Lint 規則,避免使用全域變數,並嘗試將資料移至更持久的狀態。