Questa guida spiega come creare estensioni più robuste testando l'interruzione del service worker utilizzando Puppeteer. È importante essere preparati a gestire l'interruzione in qualsiasi momento, perché può verificarsi senza preavviso, con conseguente perdita di qualsiasi stato non persistente nel service worker. Di conseguenza, le estensioni devono salvare lo stato importante ed essere in grado di gestire le richieste non appena vengono riavviate quando si verifica un evento da gestire.
Prima di iniziare
Clona o scarica il repository chrome-extensions-samples.
Utilizzeremo l'estensione di test in
/functional-samples/tutorial.terminate-sw/test-extension, che invia un messaggio
al service worker ogni volta che viene fatto clic su un pulsante e aggiunge testo alla pagina
se viene ricevuta una risposta.
Dovrai anche installare Node.JS, il runtime su cui è basato Puppeteer.
Passaggio 1: avvia il progetto Node.js
Crea i seguenti file in una nuova directory. Insieme creano un nuovo progetto Node.js e forniscono la struttura di base di una suite di test Puppeteer utilizzando Jest come test runner. Per saperne di più su questa configurazione, consulta Testare le estensioni di Chrome con 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;
});
Tieni presente che il nostro test carica test-extension dal repository di esempi.
Il gestore per browser.runtime.onMessage si basa sullo stato impostato nel gestore
per l'evento browser.runtime.onInstalled. Di conseguenza, i contenuti di data
andranno persi quando il service worker viene terminato e la risposta a eventuali messaggi futuri
non andrà a buon fine. Risolviamo il problema dopo aver scritto il test.
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);
});
Passaggio 2: installa le dipendenze
Esegui npm install per installare le dipendenze richieste.
Passaggio 3: scrivi un test di base
Aggiungi il seguente test in fondo a index.test.js. Si apre la pagina di test
dalla nostra estensione di test, si fa clic sull'elemento pulsante e si attende una risposta
dal 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');
});
Puoi eseguire il test con npm start e dovresti vedere che viene completato
correttamente.
Passaggio 4: termina il service worker
Aggiungi la seguente funzione helper che termina il 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();
}
Infine, aggiorna il test con il seguente codice. Ora termina il service worker e fai di nuovo clic sul pulsante per verificare di aver ricevuto una risposta.
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');
});
Passaggio 5: esegui il test
Esegui npm start. Il test dovrebbe non riuscire, il che indica che il service worker
non ha risposto dopo la sua terminazione.
Passaggio 6: correggi il service worker
Successivamente, correggi il service worker rimuovendo la sua dipendenza dallo stato temporaneo. Aggiorna
l'estensione di test in modo che utilizzi il seguente codice, archiviato in
service-worker-fixed.js nel repository.
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;
});
Qui salviamo la versione in browser.storage.local anziché in una variabile globale
per mantenere lo stato tra i cicli di vita dei service worker. Poiché è possibile accedere allo spazio di archiviazione solo in modo asincrono, restituiamo anche true dal listener onMessage per assicurarci che il callback sendResponse rimanga attivo.
Passaggio 7: esegui di nuovo il test
Esegui di nuovo il test con npm start. Ora dovrebbe essere approvato.
Passaggi successivi
Ora puoi applicare lo stesso approccio alla tua estensione. Considera quanto segue:
- Crea la tua suite di test per supportare l'esecuzione con o senza interruzione imprevista del service worker. Puoi quindi eseguire entrambe le modalità singolarmente per capire meglio la causa di un errore.
- Scrivi codice per terminare il service worker in punti casuali all'interno di un test. Questo può essere un buon modo per scoprire problemi difficili da prevedere.
- Impara dagli errori dei test e cerca di programmare in modo difensivo in futuro. Ad esempio, aggiungi una regola di linting per scoraggiare l'utilizzo di variabili globali e prova a spostare i dati in uno stato più persistente.