本指南介绍了如何通过测试服务构建更强大的扩展程序 使用 Puppeteer 终止工作器。已做好随时处理账号终止的准备 时间很重要,因为这可能会在没有警告的情况下 发生,导致 Service Worker。因此,扩展程序必须保存重要的状态和 如果存在以下情况,则能够在再次启动后立即处理请求: 事件进行处理。
前期准备
克隆或下载 chrome-extensions-samples 代码库。
我们将在
/functional-samples/tutorial.terminate-sw/test-extension
:用于发送消息
向 Service Worker 发送一个按钮并将文本添加到页面
如果收到响应。
您还需要安装 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": "^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;
});
请注意,我们的测试会从示例代码库加载 test-extension
。chrome.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
添加以下可终止 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
,而不是全局变量
在 Service Worker 的生命周期之间保留状态。由于只能异步访问存储空间,因此我们还会从 onMessage
监听器返回 true,以确保 sendResponse
回调保持活跃状态。
第 7 步:再次运行测试
使用 npm start
再次运行测试。现在它应该会通过。
后续步骤
现在,您可以将相同的方法应用于自己的扩展程序。请考虑以下事项:
- 构建测试套件,以支持在有或没有意外服务 worker 终止的情况下运行。然后,您可以单独运行这两种模式, 以及导致失败的原因。
- 编写代码以在测试中的随机点终止 Service Worker。 这是一种发现可能难以预测的问题的好方法。
- 从测试失败中汲取经验,并在将来尝试进行防御性编码。例如,添加 lint 规则以禁止使用全局变量,并尝试将数据移至更持久的状态。