En esta guía, se explica cómo crear extensiones más sólidas mediante la prueba del servicio y la finalización de los trabajadores con Puppeteer. Es importante estar preparado para controlar la finalización en cualquier momento, ya que esto puede ocurrir sin advertencia, lo que provocará la pérdida de cualquier estado no persistente en el trabajador de servicio. Por lo tanto, las extensiones deben guardar el estado importante y las solicitudes se inician de nuevo cuando evento que manejar.
Antes de comenzar
Clona o descarga el repositorio chrome-extensions-samples.
Usaremos la extensión de prueba en /functional-samples/tutorial.terminate-sw/test-extension
, que envía un mensaje al trabajador de servicio cada vez que se hace clic en un botón y agrega texto a la página si se recibe una respuesta.
También deberás instalar Node.JS, que es el entorno de ejecución en el que se basa Puppeteer.
Paso 1: Inicia tu proyecto de Node.js
Crea los siguientes archivos en un directorio nuevo. Juntos, crean un proyecto de Node.js nuevo y proporcionan la estructura básica de un paquete de pruebas de Puppeteer con Jest como ejecutor de pruebas. Consulta Cómo probar extensiones de Chrome con Puppeteer para obtener más información sobre esta configuración.
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;
});
Ten en cuenta que nuestra prueba carga test-extension
desde el repositorio de muestras.
El controlador de chrome.runtime.onMessage
se basa en el estado establecido en el controlador
para el evento chrome.runtime.onInstalled
. Como resultado, el contenido de data
se perderán cuando el service worker finalice y responda
los mensajes fallarán. Corregiremos esto después de escribir la prueba.
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);
});
Paso 2: Instala las dependencias
Ejecuta npm install
para instalar las dependencias requeridas.
Paso 3: Escribe una prueba básica
Agrega la siguiente prueba a la parte inferior de index.test.js
. Se abrirá la prueba
de nuestra extensión de prueba, hace clic en el elemento del botón y espera una respuesta.
del 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');
});
Puedes ejecutar tu prueba con npm start
y deberías ver que se completa
correctamente.
Paso 4: Finaliza el trabajador de servicio
Agrega la siguiente función auxiliar que finaliza tu trabajador de servicio:
/**
* 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();
}
Por último, actualiza la prueba con el siguiente código. Ahora finaliza el servicio y vuelve a hacer clic en el botón para comprobar que recibiste una respuesta.
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');
});
Paso 5: Ejecuta la prueba
Ejecuta npm start
. La prueba debería fallar, lo que indica que el trabajador de servicio no respondió después de que se cerró.
Paso 6: Corrige el service worker
A continuación, corrige el trabajador de servicio quitando su dependencia del estado temporal. Actualiza
la extensión de prueba para usar el siguiente código, que se almacena en
service-worker-fixed.js
en el repositorio.
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;
});
Aquí, guardamos la versión en chrome.storage.local
en lugar de una variable global para conservar el estado entre los ciclos de vida del trabajador de servicio. Como solo se puede acceder al almacenamiento de forma asíncrona, también mostramos "true" desde el objeto de escucha onMessage
para asegurarnos de que la devolución de llamada de sendResponse
permanezca activa.
Paso 7: Vuelve a ejecutar la prueba
Vuelve a ejecutar la prueba con npm start
. Ahora debería aprobarse.
Próximos pasos
Ahora puedes aplicar el mismo enfoque a tu propia extensión. Ten en cuenta lo siguiente:
- Compila tu suite de pruebas para admitir la ejecución con o sin el cierre inesperado de un trabajador del servicio. Luego, puedes ejecutar ambos modos de forma individual para aclarar cuál fue la causa de la falla.
- Escribir código para finalizar el service worker en puntos aleatorios dentro de una prueba. Esta puede ser una buena manera de descubrir problemas que pueden ser difíciles de predecir.
- Aprende de las pruebas fallidas e intenta programar de forma defensiva en el futuro. Para ejemplo, agrega una regla de análisis con lint para desalentar el uso de variables globales y trata de mover los datos a un estado más persistente.