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

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

What is this about?

La transition de Manifest V2 vers Manifest V3 s'accompagne d'un changement fondamental. Dans Manifest V2, les extensions se trouvaient sur une page en arrière-plan. Les pages en arrière-plan géraient la communication entre les extensions et les pages Web. Le fichier manifeste V3 utilise plutôt des services workers.

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

Qui sommes-nous ?

eyeo est une entreprise qui s'engage à favoriser un échange de valeur en ligne équilibré et durable pour les utilisateurs, les navigateurs, les annonceurs et les éditeurs. Plus de 300 millions d'utilisateurs dans le monde entier ont activé le filtrage des annonces et autorisent la diffusion d'annonces acceptables, une norme publicitaire dérivée de manière indépendante qui 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 de blocage d'annonces les plus populaires sur le marché, comme AdBlock et Adblock Plus, qui comptent plus de 110 millions d'utilisateurs dans le monde. De plus, nous proposons cette technologie en tant que bibliothèque Open Source, ce qui la rend disponible pour d'autres extensions de navigateur de filtrage des annonces.

Qu'est-ce qu'un service worker ?

Les service workers d'extension sont le gestionnaire d'événements central d'une extension de navigateur. Elles s'exécutent indépendamment en arrière-plan. Dans l'ensemble, cela ne pose pas de problème. Nous pouvons effectuer la plupart des tâches que nous devons effectuer sur une page en arrière-plan dans le nouveau service worker. Toutefois, il existe quelques différences par rapport aux pages en arrière-plan:

  • Les services workers s'arrêtent lorsqu'ils ne sont pas utilisés. Pour ce faire, nous devons conserver les états de l'application au lieu de nous appuyer sur des variables globales. Cela signifie que tous les points d'entrée de notre système doivent être prêts à être appelés avant l'initialisation du système.
  • Les écouteurs d'événements doivent être associés avant d'attendre des rappels asynchrones. Les services workers suspendus peuvent toujours recevoir les événements auxquels ils sont abonnés. Si l'écouteur de l'événement n'est pas enregistré lors du premier tour de la boucle d'événements, il ne recevra pas l'événement s'il a réveillé le service worker.
  • L'arrêt en veille peut interrompre les minuteurs avant leur exécution.

Quand les services workers sont-ils suspendus ?

Pour Chrome 119, nous avons constaté que les services workers étaient suspendus:

  • Après 30 secondes sans recevoir d'événements ni appeler d'API d'extension.
  • Jamais si les outils pour les développeurs sont ouverts ou si vous utilisez une bibliothèque de test basée sur ChromeDriver (voir la demande de fonctionnalité).
  • Si vous cliquez sur Arrêter dans chrome://serviceworker-internals.

Pour en savoir plus, consultez la page Cycle de vie des services workers.

Pourquoi est-ce un problème de tester cela ?

Idéalement, il aurait été utile de disposer de conseils officiels sur la façon de tester efficacement les services workers ou d'exemples de tests fonctionnels. Lors de nos tests de service workers, nous avons rencontré quelques difficultés:

  • Nous avons un état dans notre extension de test. Lorsque le service worker s'arrête, nous perdons son état et ses événements enregistrés. Comment conserver les données dans notre flux de test ?
  • Si les services workers peuvent être suspendus à tout moment, nous devons tester que toutes les fonctionnalités fonctionnent si elles sont interrompues.
  • Même si nous introduisons un mécanisme dans nos tests qui suspend de manière aléatoire les services workers, aucune API du navigateur ne permet de les suspendre facilement. Nous avons demandé à l'équipe du W3C d'ajouter cette fonctionnalité, mais nous attendons toujours une réponse.

Tester la suspension du service worker

Nous avons essayé plusieurs approches pour déclencher la suspension du service worker lors des tests:

Méthode Problèmes liés à l'approche
Attendre une durée arbitraire (par exemple, 30 secondes) Cela rend les tests lents et peu fiables, en particulier lorsque vous en exécutez plusieurs. Cela ne fonctionne pas avec WebDriver, car WebDriver utilise l'API DevTools de Chrome et le service worker n'est pas suspendu lorsque DevTools est ouvert. Même si nous pouvions contourner ce problème, nous devrions tout de 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 Selon la spécification, cela peut entraîner l'arrêt, en fonction de la façon dont le navigateur implémente cette fonctionnalité. Chrome ne met pas fin au service worker dans ce cas. Nous ne pouvons donc pas tester le scénario lorsque le service worker est suspendu.
Insérer un message dans le service worker pour vérifier s'il a été suspendu L'envoi d'un message réveille le service worker. Vous pouvez l'utiliser pour vérifier si le service worker était en veille, mais cela entraîne des résultats incorrects pour les tests qui doivent effectuer des vérifications immédiatement après la suspension du service worker.
Arrêter 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. Si vous arrêtez ce processus à l'aide de chrome.process.terminate() ou de l'IUG du gestionnaire de processus de Chrome, vous arrêtez non seulement le service worker, mais aussi toutes les pages de l'extension.

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

Il s'agit de la meilleure option à ce jour, 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œud WebDriver. Cela signifie que ces tests ne peuvent pas être exécutés uniquement à l'aide de l'extension. Ils doivent être déclenchés à l'aide de Selenium WebDriver.

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

Schéma illustrant le flux de test
Flux de test avec suspension du service worker.

Dans un nouveau flux qui suspend les service workers (bleu), nous avons ajouté Selenium WebDriver pour "cliquer" sur la suspension via l'UI, ce qui déclenche une action dans l'API du navigateur.

Il est à noter qu'un bug Chrome empêchait le service worker de redémarrer lorsque cette opération était effectuée avec Selenium WebDriver. Ce problème a été corrigé dans Chrome 116. Heureusement, il existe également une solution de contournement: configurer Chrome pour qu'il ouvre automatiquement les outils pour les développeurs dans chaque onglet permet au service worker de démarrer correctement.

C'est l'approche que nous utilisons lors des tests, même si elle n'est pas idéale, car cliquer sur le bouton peut ne pas être une API stable et l'ouverture de DevTools (pour les anciens navigateurs) semble avoir un coût en termes de performances.

Comment couvrir l'ensemble de la fonctionnalité ? Tests de fuzz

Une fois que nous avons mis en place 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 exécuté nos tests standards dans un environnement où, avant chaque interaction avec la page en arrière-plan, le service worker est suspendu par WebDriver qui clique sur Stop (Arrêter) sur la page chrome://serviceworker-internals/.

Exemple d'exécution d'un test de fuzz
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 peut parfois entraîner des erreurs. De plus, exécuter toutes les suites de tests en mode fuzz prend beaucoup de temps. Au lieu de couvrir tous les cas "similaires", nous avons choisi les chemins les plus critiques à tester en mode fuzz. Il est à noter que l'exécution de tests fonctionnels en mode "fuzz" nous a obligés à augmenter les délais avant expiration des tests, car la suspension et le redémarrage des service workers prennent du temps.

Ces tests sont utiles en tant que première analyse à gros grain, qui met en évidence de nombreux endroits où le code échoue, mais qui ne révèle pas nécessairement toutes les façons subtiles dont la suspension du service worker peut entraîner des erreurs.

En interne, nous appelons ces types de tests "tests fuzz". Traditionnellement, le test fuzz consiste à envoyer des entrées non valides à votre programme et à vous assurer qu'il répond de manière raisonnable ou, du moins, qu'il ne plante pas. Dans notre cas, l'entrée incorrecte correspond à la suspension du service worker à tout moment. Le "comportement raisonnable" que nous attendons est que notre fonctionnalité de filtrage des annonces continue de fonctionner comme avant. Il ne s'agit pas vraiment d'une entrée non valide, car il s'agit d'un comportement attendu dans Manifest V3. Toutefois, il aurait été non valide dans Manifest V2. Il semble donc que cette terminologie soit raisonnable.

Résumé

Les services workers constituent l'une des plus grandes modifications apportées au fichier manifeste V3 (en plus des règles declarativeNetRequest). La migration vers Manifest V3 peut nécessiter de nombreuses modifications de code dans les extensions de navigateur et de nouvelles approches de test. Les développeurs d'extensions avec état persistant doivent également préparer leurs extensions à gérer de manière élégante la suspension inattendue du service worker.

Malheureusement, il n'existe aucune API permettant de gérer facilement la suspension en fonction de notre cas d'utilisation. Comme nous voulions tester la robustesse du code de notre extension par rapport aux mécanismes de suspension dès une phase précoce, nous avons dû contourner ce problème. Les autres développeurs d'extensions confrontés à des défis similaires peuvent utiliser cette solution de contournement, qui, bien que longue à mettre en œuvre pendant la phase de développement et de maintenance, est utile pour s'assurer que nos extensions peuvent fonctionner dans un environnement où les services workers sont régulièrement suspendus.

Même si la suspension des services workers peut déjà être testée de manière basique, nous aimerions que la plate-forme soit mieux compatible avec les tests des services workers à partir des extensions à l'avenir, car cela pourrait considérablement réduire les temps d'exécution des tests et les efforts de maintenance.