В этом руководстве объясняется, как создавать более надежные расширения, тестируя завершение сервисного работника с помощью Puppeteer. Важно быть готовым к завершению в любой момент, поскольку это может произойти без предупреждения, что приведет к потере любого непостоянного состояния сервисного работника. Следовательно, расширения должны сохранять важное состояние и иметь возможность обрабатывать запросы, как только они снова запускаются, когда возникает событие, которое нужно обработать.
Прежде чем начать
Клонируйте или загрузите репозиторий chrome-extensions-samples . Мы будем использовать тестовое расширение в /functional-samples/tutorial.terminate-sw/test-extension
, которое отправляет сообщение сервисному работнику каждый раз при нажатии кнопки и добавляет текст на страницу, если получен ответ.
Вам также потребуется установить Node.JS — среду выполнения, на которой построен Puppeteer.
Шаг 1. Запустите проект Node.js.
Создайте следующие файлы в новом каталоге. Вместе они создают новый проект Node.js и обеспечивают базовую структуру набора тестов Puppeteer, используя Jest в качестве средства запуска тестов. См. раздел «Тестирование расширений Chrome с помощью Puppeteer», чтобы узнать об этой настройке более подробно.
пакет.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"
}
}
индекс.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
будет потеряно при завершении работы сервисного работника, и ответ на любые будущие сообщения не удастся. Мы исправим это после написания нашего теста.
сервис-работник-сломанный.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
. При этом открывается тестовая страница нашего тестового расширения, нажимается элемент кнопки и ожидается ответ от сервис-воркера.
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. Завершите работу сервисного работника
Добавьте следующую вспомогательную функцию, которая завершает работу вашего сервис-воркера:
/**
* 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-fixed.js
в репозитории.
сервис-работник-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
вместо глобальной переменной, чтобы сохранить состояние между сроками службы сервис-воркера. Поскольку доступ к хранилищу возможен только асинхронно, мы также возвращаем true из прослушивателя onMessage
чтобы убедиться, что обратный вызов sendResponse
остается активным.
Шаг 7. Запустите тест еще раз
Запустите тест еще раз с помощью npm start
. Теперь это должно пройти.
Следующие шаги
Теперь вы можете применить тот же подход к своему собственному расширению. Учтите следующее:
- Создайте свой набор тестов для поддержки работы с неожиданным завершением работы Service Worker или без него. Затем вы можете запустить оба режима по отдельности, чтобы прояснить причину сбоя.
- Напишите код для завершения работы сервис-воркера в случайных точках теста. Это может быть хорошим способом обнаружить проблемы, которые трудно предсказать.
- Учитесь на неудачных тестах и старайтесь писать код в будущем. Например, добавьте правило проверки, чтобы препятствовать использованию глобальных переменных, и попытайтесь перевести данные в более устойчивое состояние.