Di cosa si tratta?
La transizione da Manifest V2 a Manifest V3 comporta un cambiamento fondamentale. In Manifest V2, le estensioni si trovavano in una pagina in background. Le pagine in background gestivano la comunicazione tra le estensioni e le pagine web. La versione 3 del file manifest utilizza invece i service worker.
In questo post, analizziamo il problema del test dei worker dei servizi di estensione. In particolare, diamo un'occhiata a come assicurarci che il nostro prodotto funzioni correttamente nel caso in cui un worker di servizio venga sospeso.
Chi siamo?
eyeo è un'azienda impegnata a promuovere uno scambio di valore online equilibrato e sostenibile per utenti, browser, inserzionisti e publisher. Abbiamo più di 300 milioni di utenti di filtri degli annunci a livello globale che consentono la visualizzazione di Annunci accettabili, uno standard degli annunci indipendente che determina se un annuncio è accettabile e non invadente.
Il nostro team di Extension Engine fornisce la tecnologia di filtro degli annunci che alimenta alcune delle estensioni del browser di blocco degli annunci più popolari sul mercato, come AdBlock e Adblock Plus, con oltre 110 milioni di utenti in tutto il mondo. Inoltre, offriamo questa tecnologia come libreria open source, rendendola disponibile per altre estensioni del browser per il filtro degli annunci.
Che cos'è un worker di servizio?
I worker di servizio dell'estensione sono il gestore degli eventi centrali di un'estensione del browser. Vengono eseguite in modo indipendente in background. In linea di massima, non c'è problema. Possiamo fare la maggior parte delle cose che dobbiamo fare in una pagina di sfondo nel nuovo service worker. Tuttavia, ci sono alcune differenze rispetto alle pagine di sfondo:
- I worker di servizio si arrestano quando non sono in uso. Ciò richiede di mantenere invariati gli stati dell'applicazione anziché fare affidamento su variabili globali. Ciò significa che tutti i punti di contatto con il nostro sistema devono essere preparati per essere chiamati prima dell'inizializzazione del sistema.
- I listener di eventi devono essere collegati prima di attendere eventuali callback asincroni. I service worker sospesi possono comunque ricevere gli eventi a cui sono iscritti. Se l'ascoltatore dell'evento non è registrato nel primo passaggio del loop di eventi, non lo riceverà se l'evento ha riattivato il service worker.
- L'interruzione in caso di inattività può interrompere i timer prima del completamento.
Quando vengono sospesi i worker di servizio?
In Chrome 119, abbiamo riscontrato che i worker di servizio sono sospesi:
- Dopo 30 secondi senza aver ricevuto eventi o chiamato API di estensione.
- Mai se gli Strumenti per sviluppatori sono aperti o se utilizzi una libreria di test basata su ChromeDriver (vedi richiesta di funzionalità).
- Se fai clic su Interrompi in chrome://serviceworker-internals.
Per informazioni più recenti, consulta la sezione Ciclo di vita dei service worker.
Perché è un problema testarlo?
Idealmente, sarebbe stato utile avere indicazioni ufficiali su "come testare i service worker in modo efficiente" o esempi di test funzionanti. Durante le nostre avventure nel testare i worker di servizio, abbiamo riscontrato alcune difficoltà:
- Abbiamo lo stato nella nostra estensione di test. Quando il worker del servizio si arresta, perdiamo il suo stato e gli eventi registrati. Come possiamo mantenere i dati nel flusso di test?
- Se i worker di servizio possono essere sospesi in qualsiasi momento, dobbiamo verificare che tutte le funzionalità funzionino se vengono interrotte.
- Anche se introducessimo nei nostri test un meccanismo che sospende in modo casuale i service worker, non esiste un'API nel browser per sospenderli facilmente. Abbiamo chiesto al team del W3C di aggiungere questa funzionalità, ma la discussione è ancora in corso.
Testare la sospensione del service worker
Abbiamo provato diversi approcci per attivare la sospensione del servizio worker durante i test:
Approccio | Problemi con l'approccio |
Attendi un periodo di tempo arbitrario (ad esempio 30 secondi) | Ciò rende i test lenti e inaffidabili, soprattutto se ne esegui più di uno. Non funziona quando si utilizza WebDriver, poiché WebDriver utilizza l'API DevTools di Chrome e il service worker non viene sospeso quando DevTools è aperto. Anche se potessimo aggirarlo, dovremmo comunque verificare se il service worker è stato sospeso e non abbiamo un modo per farlo. |
Esegui un loop infinito nel service worker | Secondo le specifiche, questo può comportare l'interruzione del servizio a seconda di come il browser implementa questa funzionalità. In questo caso Chrome non termina il service worker, quindi non possiamo testare lo scenario in cui il service worker viene sospeso. |
Avere un messaggio nel service worker per verificare se è stato sospeso | L'invio di un messaggio riattiva il service worker. Questo può essere utilizzato per verificare se il servizio inattivo era inattivo, ma interrompe i risultati dei test che devono eseguire controlli immediatamente dopo la sospensione del servizio inattivo. |
Uccidere il processo del service worker utilizzando chrome.processes.terminate() | Il service worker dell'estensione condivide un processo con altre parti dell'estensione, quindi l'interruzione di questo processo utilizzando chrome.process.terminate() o la GUI del gestore dei processi di Chrome interrompe non solo il service worker, ma anche le pagine dell'estensione. |
Abbiamo eseguito un test che controlla la risposta del nostro codice alla sospensione del servizio worker facendo in modo che Selenium WebDriver apra chrome://serviceworker-internals/ e faccia clic sul pulsante "Interrompi" per il servizio worker.
Questa è la soluzione migliore finora, ma non è ideale perché i nostri test Mocha (che vengono eseguiti in una pagina dell'estensione) non possono farlo autonomamente, quindi devono comunicare di nuovo con il nostro programma del nodo WebDriver. Ciò significa che questi test non possono essere eseguiti utilizzando solo l'estensione, ma devono essere attivati utilizzando Selenium WebDriver.
Di seguito è riportato un diagramma che mostra come comunichiamo con l'API del browser tramite diversi flussi e come l'aggiunta del meccanismo di "sospensione dei worker di servizio" influisce su questo processo.
In un nuovo flusso che sospende i worker di servizio (blu), abbiamo aggiunto Selenium WebDriver per "fare clic" sulla sospensione tramite l'interfaccia utente, che attiva un'azione nell'API del browser.
Vale la pena ricordare che esisteva un bug di Chrome per cui questa operazione con Selenium WebDriver impediva al servizio worker di riavviarsi. Il problema è stato risolto in Chrome 116 e, fortunatamente, esiste anche una soluzione alternativa: se imposti Chrome in modo che apra automaticamente DevTools in ogni scheda, il servizio worker si avvia correttamente.
Questo è l'approccio che utilizziamo durante i test, anche se non è ideale perché fare clic sul pulsante potrebbe non essere un'API stabile e l'apertura di DevTools (per i browser meno recenti) sembra avere un costo in termini di prestazioni.
Come copriamo l'intera funzionalità? Test di fuzz
Una volta creato un meccanismo per testare la sospensione, abbiamo dovuto decidere come collegarlo alle nostre suite di test di automazione. Abbiamo eseguito i nostri test standard in un ambiente in cui, prima di ogni interazione con la pagina in background, il service worker viene sospeso da WebDriver facendo clic su Interrompi nella pagina chrome://serviceworker-internals/.
Eseguiamo la maggior parte dei test, ma non tutti, perché il meccanismo di sospensione non è completamente stabile e a volte causa instabilità. Inoltre, l'esecuzione di tutte le suite di test in modalità fuzz richiede molto tempo. Pertanto, anziché coprire tutti i casi "simili", abbiamo scelto i percorsi più critici per i test in modalità fuzz. Vale la pena ricordare che l'esecuzione di test funzionali in modalità "fuzz" ha comportato l'aumento dei timeout dei test perché la sospensione e il riavvio dei service worker richiedono più tempo.
Questi test sono utili come primo passaggio a livello granulare, che evidenzia molti punti in cui il codice non funziona, ma potrebbe non rilevare necessariamente tutti i modi sottili in cui la sospensione del servizio worker potrebbe causare errori.
All'interno, chiamiamo questi tipi di test "test di fuzz". Tradizionalmente, il test di fuzz consiste nell'eseguire un input non valido nel programma e assicurarsi che risponda in modo ragionevole o almeno che non abbia arresti anomali. Nel nostro caso, l'"input non valido" è il servizio worker che viene sospeso in qualsiasi momento e il "comportamento ragionevole" che ci aspettiamo è che la nostra funzionalità di filtro degli annunci continui a funzionare come prima. Non si tratta di un input non valido, poiché si tratta di un comportamento previsto in Manifest V3, ma non sarebbe stato valido in Manifest V2, quindi sembra una terminologia ragionevole.
Riepilogo
I worker di servizio sono una delle principali modifiche in Manifest V3 (oltre alle regole declarativeNetRequest). La migrazione a Manifest V3 potrebbe richiedere molte modifiche al codice delle estensioni del browser e nuovi approcci ai test. Inoltre, richiede agli sviluppatori di estensioni con stato persistente di preparare le estensioni per gestire in modo corretto la sospensione imprevista dei worker di servizio.
Purtroppo non esiste un'API per gestire la sospensione in modo semplice e adatto al nostro caso d'uso. Poiché volevamo testare la robustezza della base di codice della nostra estensione rispetto ai meccanismi di sospensione in una fase iniziale, abbiamo dovuto trovare una soluzione. Altri sviluppatori di estensioni che si trovano ad affrontare problemi simili possono utilizzare questa soluzione alternativa, che, sebbene richieda tempo nella fase di sviluppo e manutenzione, vale la pena di essere utilizzata per garantire che le nostre estensioni possano funzionare correttamente in un ambiente in cui i service worker vengono regolarmente sospesi.
Anche se esiste già un supporto di base per testare la sospensione dei worker di servizio, vorremmo davvero che in futuro fosse disponibile un supporto della piattaforma migliore per testare i worker di servizio dalle estensioni, in quanto ciò potrebbe ridurre notevolmente i tempi di esecuzione dei test e le attività di manutenzione.