Extensions Chrome: parcours d'eyeo pour tester la suspension d'un service worker

Aga Czyżewska
Aga Czyżewska
Rowan Deysel
Rowan Deysel

De quoi s'agit-il ?

La transition de Manifest V2 vers Manifest V3 s'accompagne d'un changement fondamental. Dans Manifest V2, les extensions se trouvaient dans une page en arrière-plan. Les pages en arrière-plan géraient la communication entre les extensions et les pages Web. Manifest V3 utilise des service workers à la place.

Dans cet article, nous nous penchons sur le problème du test des service workers d'extension. Nous allons en particulier voir comment nous assurer que notre produit fonctionne correctement en cas de suspension d'un service worker.

Qui sommes-nous ?

eyeo est une entreprise dont la mission est d'offrir aux utilisateurs, aux navigateurs, aux annonceurs et aux éditeurs un échange de valeur en ligne équitable et durable. À travers le monde, plus de 300 millions d'utilisateurs du filtrage des annonces autorisent la diffusion d'annonces autorisées. Cette norme indépendante détermine si une annonce est acceptable et non intrusive.

Notre équipe Extension Engine fournit une technologie de filtrage des annonces qui alimente certaines des extensions de navigateur les plus populaires du marché, telles qu'AdBlock et Adblock Plus, qui comptent plus de 110 millions d'utilisateurs à travers le monde. Nous proposons également cette technologie sous la forme d'une bibliothèque Open Source que nous mettons à la disposition d'autres extensions de navigateur pour le filtrage des annonces.

Qu'est-ce qu'un service worker ?

Les service workers d'extension sont le gestionnaire central des événements d'une extension de navigateur. Elles s'exécutent de manière indépendante en arrière-plan. De manière générale, cela ne pose pas de problème. Nous pouvons effectuer la plupart des choses que nous devons faire sur une page en arrière-plan dans le nouveau service worker. Il existe toutefois quelques différences par rapport aux pages en arrière-plan:

  • Les service workers s'arrêtent lorsqu'ils ne sont pas utilisés. Pour ce faire, nous devons persister les états de l'application au lieu d'utiliser des variables globales. Cela signifie que tous les points d'entrée dans notre système doivent être préparés pour être appelés avant l'initialisation du système.
  • Les écouteurs d'événements doivent être associés avant d'attendre les rappels asynchrones. Les service workers suspendus peuvent toujours recevoir les événements auxquels ils sont abonnés. Si l'écouteur de l'événement n'est pas enregistré dans le premier tour de la boucle d'événements, il ne recevra pas l'événement s'il a activé le service worker.
  • Les arrêts inactifs peuvent interrompre les minuteurs avant qu'ils ne se terminent.

Quand les service workers sont-ils suspendus ?

Pour Chrome 119, nous avons constaté que les service workers sont suspendus:

  • Après ne pas avoir reçu d'événements ni avoir appelé les API d'extension pendant 30 secondes.
  • jamais si les outils pour les développeurs sont ouverts ou si vous utilisez une bibliothèque de tests basée sur ChromeDriver (voir la demande de fonctionnalité).
  • Si vous cliquez sur Arrêter dans chrome://serviceworker-internals.

Pour obtenir des informations plus récentes, consultez Cycle de vie des service workers.

Pourquoi le test est-il problématique ?

Dans l'idéal, il aurait été utile de disposer de directives officielles sur la façon de tester les service workers de manière efficace ou de disposer d'exemples de tests fonctionnels. Au cours de nos expériences de test des service workers, nous avons été confrontés à quelques défis:

  • Nous avons l'état dans notre extension de test. Lorsque le service worker s'arrête, nous perdons son état et les événements enregistrés. Comment pouvons-nous conserver les données dans notre flux de test ?
  • Si les service workers peuvent être suspendus à tout moment, nous devons vérifier que toutes les fonctionnalités fonctionnent en cas d'interruption.
  • Même si nous introduisons dans nos tests un mécanisme permettant de suspendre les service workers de manière aléatoire, il n'existe aucune API dans le navigateur pour le suspendre facilement. Nous avons demandé à l'équipe du W3C d'ajouter cette fonctionnalité, mais il s'agit d'un processus continu.

Tester la suspension d'un service worker

Nous avons essayé plusieurs approches pour déclencher la suspension d'un service worker pendant les tests:

Méthode Problèmes liés à l'approche
Attendez un laps de temps arbitraire (30 secondes, par exemple). Par conséquent, les tests sont lents et peu fiables, en particulier lorsque vous exécutez plusieurs tests. Elle ne fonctionne pas avec WebDriver, car celui-ci utilise l'API DevTools de Chrome, et le service worker n'est pas suspendu lorsque les outils de développement sont ouverts. Même si nous pouvions le contourner, nous devrions quand même vérifier si le service worker a été suspendu, et nous n'avons aucun moyen de le faire.
Exécuter une boucle infinie dans le service worker D'après les spécifications, cette opération peut entraîner l'arrêt de la connexion selon la façon dont le navigateur met en œuvre cette fonctionnalité. Dans ce cas, Chrome n'arrête pas le service worker. Nous ne pouvons donc pas tester le scénario lorsque le service worker est suspendu.
Avoir un message au service worker pour vérifier s'il a été suspendu L'envoi d'un message réveille le service worker. Cela permet de vérifier si le service worker était endormi, mais annule les résultats des tests qui doivent effectuer des vérifications immédiatement après la suspension du service worker.
Arrêtez le processus du service worker à l'aide de chrome.processes.terminate(). Le service worker de l'extension partage un processus avec d'autres parties de l'extension. Par conséquent, si vous arrêtez ce processus à l'aide de chrome.process.terminate() ou de l'IUG du gestionnaire de processus Chrome, non seulement le service worker, mais également toutes les pages d'extension seront supprimés.

Nous avons fini avec un test qui vérifie comment notre code réagit à la suspension d'un service worker en demandant à Selenium WebDriver d'ouvrir chrome://serviceworker-internals/ et de cliquer sur le bouton "Stop" (Arrêter) pour le service worker.

Il s'agit de la meilleure option jusqu'à présent, mais elle n'est pas idéale, car nos tests Mocha (qui s'exécutent sur une page d'extension) ne peuvent pas le faire eux-mêmes. Ils doivent donc communiquer avec notre programme de nœuds WebDriver. Cela signifie que ces tests ne peuvent pas être exécutés uniquement avec l'extension. Ils doivent être déclenchés à l'aide de Selenium WebDriver.

Voici un schéma montrant comment nous communiquons avec l'API du navigateur via différents flux et comment l'ajout du mécanisme de suspension des service workers affecte cette fonctionnalité.

Diagramme illustrant le flux de test
Flux de test avec suspension du service worker

Dans un nouveau flux qui suspend les service workers (en bleu), nous avons ajouté Selenium WebDriver à la fonctionnalité de clic sur la suspension via l'interface utilisateur, ce qui déclenche une action dans l'API du navigateur.

Il est intéressant de noter qu'en raison d'un bug dans Chrome, le service worker ne parvenait pas à redémarrer après avoir effectué cette opération avec Selenium WebDriver. Ce problème a été résolu dans Chrome 116. Heureusement, il existe également une solution de contournement: si vous configurez Chrome pour qu'il ouvre automatiquement les outils de développement dans chaque onglet, le service worker démarre correctement.

Nous utilisons cette approche pour les tests, même si elle n'est pas idéale. En effet, cliquer sur le bouton n'est pas nécessairement une API stable et ouvrir les outils de développement (pour les navigateurs plus anciens) semble avoir un coût en termes de performances.

Comment couvrir l'ensemble des fonctionnalités ? Tests Fuzz

Une fois que nous avons eu un mécanisme de test de la suspension, nous avons dû décider comment l'intégrer à nos suites de tests d'automatisation. Nous avons effectué nos tests standards dans un environnement où, avant chaque interaction avec la page en arrière-plan, le service worker est suspendu par un WebDriver et cliquait sur Arrêter sur la page chrome://serviceworker-internals/.

Exemple d'exécution d'un test à données aléatoires
Image présentant la configuration actuelle des tests.

Nous exécutons la plupart des tests, mais pas tous, car le mécanisme de suspension n'est pas totalement stable et, parfois, provoque des fragilités. De plus, l'exécution de toutes les suites de tests en mode aléatoire prend beaucoup de temps. Ainsi, au lieu de couvrir tous les cas "similaires", nous avons choisi les chemins les plus critiques pour les tests en mode fuzz. Il est intéressant de noter que l'exécution de tests fonctionnels en mode "fuzz" signifie que nous avons dû augmenter les délais avant expiration des tests, car la suspension et le redémarrage des service workers prennent plus de temps.

Ces tests sont utiles en tant que premier pass plus précis, qui mettent en évidence de nombreux endroits où le code échoue, mais ne révèlent pas nécessairement toutes les raisons subtiles de la suspension d'un service worker pouvant entraîner des dysfonctionnements.

En interne, nous appelons ces types de tests les "tests Fuzz". Traditionnellement, les tests à données aléatoires consistent à envoyer des entrées non valides dans votre programme et à vous assurer qu'il répond raisonnablement, ou au moins ne plante pas. Dans notre cas, la "entrée non valide" désigne le service worker suspendu à tout moment, et le "comportement raisonnable" attendu est que notre fonctionnalité de filtrage des annonces doit continuer à fonctionner comme avant. Cette entrée n'est pas vraiment incorrecte, car il s'agit d'un comportement attendu dans Manifest V3, mais cela aurait été non valide dans Manifest V2. Cela semble donc raisonnable.

Résumé

Les service workers représentent l'un des changements les plus importants dans Manifest V3 (en plus des règles declarativeNetRequest). La migration vers Manifest V3 peut nécessiter de nombreuses modifications du code dans les extensions de navigateur et de nouvelles approches de test. Les développeurs d'extensions à état persistant doivent également les préparer à gérer la suspension inattendue d'un service worker de manière optimale.

Malheureusement, il n'existe pas d'API permettant de gérer facilement la suspension d'une manière adaptée à notre cas d'utilisation. Puisque nous voulions tester la robustesse du codebase de notre extension par rapport aux mécanismes de suspension, nous avons dû contourner ce problème. D'autres développeurs d'extensions confrontés à des défis similaires peuvent utiliser cette solution de contournement. Bien que fastidieuse en phase de développement et de maintenance, elle nous permet de nous assurer que nos extensions peuvent fonctionner correctement dans un environnement où les service workers sont régulièrement suspendus.

Bien qu'il existe déjà une prise en charge de base pour tester la suspension des service workers, une meilleure prise en charge de la plate-forme pour tester les service workers depuis les extensions est quelque chose que nous aimerions vraiment voir à l'avenir, car cela pourrait réduire considérablement les temps d'exécution des tests et les efforts de maintenance.