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