Extensões do Chrome: a jornada da Eo' para testar a suspensão do service worker

Qual é o motivo do contato?

A transição do Manifest V2 para o Manifesto V3 vem com uma mudança fundamental. No Manifest V2, as extensões ficavam em uma página em segundo plano. As páginas de plano de fundo gerenciavam a comunicação entre extensões e páginas da Web. O Manifest V3 usa service workers.

Nesta postagem, analisamos o problema ao testar os service workers de extensão. Particularmente, vamos aprender a garantir que nosso produto funcione corretamente caso um service worker seja suspenso.

Quem somos?

A eyeo é uma empresa dedicada a capacitar uma troca de valor on-line equilibrada e sustentável para usuários, navegadores, anunciantes e editores. Temos mais de 300 milhões de usuários no mundo todo que usam filtros de anúncios que permitem a exibição de anúncios aceitáveis, um padrão derivado de maneira independente que determina se um anúncio é aceitável ou não intrusivo.

Nossa equipe do Extension Engine fornece uma tecnologia de filtragem de anúncios que é usada em algumas das extensões de navegador de bloqueio de anúncios mais conhecidas do mercado, como o AdBlock e o Adblock Plus, com mais de 110 milhões de usuários em todo o mundo. Além disso, oferecemos essa tecnologia como uma biblioteca de código aberto, disponibilizando-a para outras extensões de navegador com filtragem de anúncios.

O que é um service worker?

Service workers de extensão são o manipulador de eventos central de uma extensão do navegador. Eles são executados de forma independente em segundo plano. De modo geral, tudo bem. Podemos fazer a maioria das coisas que precisamos fazer em uma página em segundo plano no novo service worker. No entanto, há algumas mudanças em comparação com as páginas em segundo plano:

  • Os service workers são encerrados quando não estão em uso. Com isso, precisamos manter os estados do aplicativo em vez de depender de variáveis globais. Isso significa que todos os pontos de entrada no sistema precisam estar preparados para serem chamados antes da inicialização do sistema.
  • Os listeners de eventos precisam ser anexados antes de aguardar callbacks assíncronos. Service workers suspensos ainda podem receber eventos em que se inscreveram. Se o listener do evento não estiver registrado na primeira curva do loop de eventos, ele não receberá o evento se esse evento tiver ativado o service worker.
  • O encerramento de inatividade pode interromper os timers antes que sejam concluídos.

Quando os service workers são suspensos?

No Chrome 119, observamos que os service workers são suspensos:

  • Após 30 segundos sem receber eventos ou chamar APIs de extensão.
  • Nunca se as ferramentas para desenvolvedores estiverem abertas ou se você estiver usando uma biblioteca de testes baseada no ChromeDriver. Consulte a solicitação de recurso.
  • Se você clicar em Parar em chrome://serviceworker-internals.

Para informações mais recentes, consulte Ciclo de vida dos service workers.

Por que testar isso é um problema?

Idealmente, teria sido útil ter orientações oficiais sobre “como testar os service workers de forma eficiente” ou exemplos de testes de trabalho. Durante nossas aventuras no teste dos service workers, encontramos alguns desafios:

  • Há um estado na extensão de teste. Quando o service worker é interrompido, perdemos o estado e os eventos registrados. Como manteremos os dados no nosso fluxo de teste?
  • Se os service workers puderem ser suspensos a qualquer momento, precisaremos testar se todos os recursos funcionam, caso sejam interrompidos.
  • Mesmo se introduzirmos um mecanismo em nossos testes que suspende aleatoriamente os service workers, não há uma API no navegador para suspendê-lo facilmente. Pedimos à equipe do W3C para adicionar esse recurso, mas essa conversa ainda está em andamento.

Como testar a suspensão do service worker

Tentamos várias abordagens para acionar a suspensão do service worker durante os testes:

Abordagem Problemas com a abordagem
Aguardar um período arbitrário (por exemplo, 30 segundos) Isso torna o teste lento e não confiável, especialmente ao realizar vários testes. Ela não funciona com o WebDriver, já que ele usa a API DevTools do Chrome, e o service worker não é suspenso quando o DevTools está aberto. Mesmo se pudéssemos ignorar isso, ainda teríamos que verificar se o service worker foi suspenso e não temos uma maneira de fazer isso.
Executar um loop infinito no service worker De acordo com a especificação, isso pode levar ao encerramento, dependendo de como o navegador implementa essa funcionalidade. O Chrome não encerra o service worker nesse caso, então não podemos testar o cenário quando o service worker é suspenso.
Ter uma mensagem no service worker para verificar se ele foi suspenso O envio de uma mensagem ativa o service worker. Isso pode ser usado para verificar se o service worker estava em suspensão, mas interrompe os resultados de testes que precisam fazer verificações imediatamente após a suspensão do service worker.
Encerra o processo do service worker usando chrome.processes.terminate() O service worker da extensão compartilha um processo com outras partes da extensão, portanto, encerrar esse processo usando chrome.process.terminate() ou a GUI do gerenciador de processos do Chrome elimina não apenas o service worker, mas também todas as páginas de extensão.

Acabamos com um teste que verifica como nosso código responde quando o service worker é suspenso, fazendo com que o Selenium WebDriver abra chrome://serviceworker-internals/ e clique no botão "stop" do service worker.

Essa é a melhor opção até o momento, mas não é a ideal, porque nossos testes Mocha (executados em uma página de extensão) não conseguem fazer isso por conta própria. Portanto, eles precisam se comunicar com nosso programa de nós do WebDriver. Isso significa que esses testes não podem ser executados usando apenas a extensão. Eles precisam ser acionados usando o Selenium WebDriver (em inglês).

Aqui está um diagrama de como nos comunicamos com a API do navegador por meio de diferentes fluxos e como a adição do mecanismo de "service workers de suspensão" o afeta.

Diagrama mostrando o fluxo de teste
Fluxo de teste com a suspensão do service worker.

Em um novo fluxo que suspende os service workers (azul), adicionamos o Selenium WebDriver para fazer a suspensão por meio da interface do usuário, o que aciona uma ação na API do navegador.

Vale a pena mencionar que houve um bug do Chrome em que fazer isso com o Selenium WebDriver fez com que o service worker não conseguisse reiniciar. Isso foi corrigido no Chrome 116. Felizmente, também há uma solução alternativa: configurar o Chrome para abrir o DevTools automaticamente em todas as guias faz com que o service worker inicie corretamente.

Essa é a abordagem que estamos usando nos testes, mesmo que não seja ideal, já que clicar no botão pode não ser uma API estável e abrir o DevTools (para navegadores mais antigos) parece ter um custo de desempenho.

Como é possível cobrir toda a funcionalidade? Testes de fuzz

Depois que tínhamos um mecanismo para testar a suspensão, tivemos que decidir como incluí-lo nos nossos conjuntos de testes de automação. Executamos nossos testes padrão em um ambiente em que, antes de cada interação com a página de segundo plano, o service worker é suspenso quando o WebDriver clica em Parar na página chrome://serviceworker-internals/.

Um exemplo de execução de teste de fuzz
Imagem com a configuração atual dos testes.

Realizamos a maioria dos testes, e não todos, porque o mecanismo de suspensão não é totalmente estável e, às vezes, causa inconsistência. Além disso, executar todos os pacotes de testes no modo fuzz leva muito tempo. Portanto, em vez de abordar todos os casos "semelhantes", escolhemos os caminhos mais críticos para testes no modo fuzz. Vale a pena mencionar que executar testes funcionais no modo “fuzz” significa que tivemos que aumentar os tempos limite dos testes porque suspender e reiniciar os service workers leva mais tempo.

Esses testes são valiosos como uma primeira passagem de alta granularidade, que destaca muitos locais em que o código falha, mas não necessariamente revelam todas as maneiras sutis pelas quais a suspensão do service worker pode causar falhas.

Internamente, chamamos esses tipos de "testes de fuzz". Tradicionalmente, o teste de fuzz acontece quando você lança uma entrada inválida no programa e garante que ele responda razoavelmente ou, pelo menos, não falhe. No nosso caso, a "entrada inválida" significa que o service worker é suspenso a qualquer momento, e o "comportamento razoável" que esperamos é que nossa funcionalidade de filtragem de anúncios continue funcionando como antes. Essa entrada não é realmente inválida, já que é um comportamento esperado no Manifesto V3, mas seria inválida no Manifesto V2, então parece uma terminologia razoável.

Resumo

Os service workers são uma das maiores mudanças no Manifesto V3 (além das regras declarativeNetRequest). A migração para o Manifest V3 pode exigir muitas mudanças de código nas extensões do navegador e novas abordagens de testes. Além disso, os desenvolvedores de extensões com estado persistente precisam preparar as extensões para lidar com a suspensão inesperada do service worker de maneira eficiente.

Infelizmente, não existe uma API para lidar com a suspensão de uma forma fácil que se encaixe no nosso caso de uso. Como queríamos testar a robustez da base de código da nossa extensão em relação aos mecanismos de suspensão em uma fase inicial, tivemos que contornar isso. Outros desenvolvedores de extensões que enfrentam desafios semelhantes podem usar essa solução alternativa. Embora seja demorada na fase de desenvolvimento e manutenção, ela vale a pena para garantir que nossas extensões possam funcionar em um ambiente em que os service workers sejam suspensos regularmente.

Embora já exista um suporte básico para o teste de suspensão de service workers, um melhor suporte de plataforma para testar service workers de dentro das extensões é algo que realmente gostaríamos de ver no futuro, porque isso poderia reduzir muito os tempos de execução dos testes e o esforço de manutenção.