Testar o encerramento do service worker com o Puppeteer

Este guia explica como criar extensões mais robustas testando a interrupção do worker de serviço usando o Puppeteer. É importante estar preparado para processar a interrupção a qualquer momento, porque isso pode acontecer sem aviso, resultando na perda de qualquer estado não persistente no service worker. Consequentemente, as extensões precisam salvar o estado importante e ser capazes de processar solicitações assim que forem iniciadas novamente quando houver um evento para processar.

Antes de começar

Clone ou faça o download do repositório chrome-extensions-samples. Usaremos a extensão de teste /functional-samples/tutorial.terminate-sw/test-extension, que envia uma mensagem ao service worker sempre que um botão for clicado e adicionar texto à página se uma resposta for recebida.

Você também precisa instalar o Node.JS, que é o ambiente de execução em que o Puppeteer é criado.

Etapa 1: iniciar seu projeto Node.js

Crie os seguintes arquivos em um novo diretório. Juntos, eles criam um novo projeto Node.js e fornecem a estrutura básica de um conjunto de testes do Puppeteer usando o Jest como um executor de teste. Consulte Teste as extensões do Google Chrome com o Puppeteer para saber mais sobre essa configuração com mais detalhes.

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; }); 

Observe que nosso teste carrega o test-extension do repositório de exemplos. O gerenciador de chrome.runtime.onMessage depende do estado definido no gerenciador para o evento chrome.runtime.onInstalled. Como resultado, o conteúdo de data serão perdidos quando o service worker for encerrado e responder a do Google Cloud vão falhar. Vamos corrigir isso depois de programar o teste.

service-worker-broken.js:

let data;  chrome.runtime.onInstalled.addListener(() => {   data = { version: chrome.runtime.getManifest().version }; });  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {   sendResponse(data.version); }); 

Etapa 2: instalar dependências

Execute npm install para instalar as dependências necessárias.

Etapa 3: escrever um teste básico

Adicione o teste abaixo à parte de baixo de index.test.js. Isso abre o teste página da nossa extensão de teste, clica no elemento de botão e aguarda uma resposta do 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'); }); 

Você pode executar o teste com npm start e conferir se ele foi concluído com sucesso.

Etapa 4: encerrar o service worker

Adicione a seguinte função auxiliar que encerra o 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(); } 

Por fim, atualize o teste com o código abaixo. Agora encerre o service worker e clique no botão novamente para verificar se você recebeu uma resposta.

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'); }); 

Etapa 5: executar o teste

Execute npm start. O teste vai falhar, o que indica que o worker do serviço não respondeu após ser encerrado.

Etapa 6: corrigir o service worker

Em seguida, corrija o worker de serviço removendo a dependência do estado temporário. Atualize a extensão de teste para usar o código abaixo, que é armazenado em service-worker-fixed.js no repositório.

service-worker-fixed.js:

chrome.runtime.onInstalled.addListener(() => {   chrome.storage.local.set({ version: chrome.runtime.getManifest().version }); });  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {   chrome.storage.local.get('version').then((data) => {     sendResponse(data.version);   });   return true; }); 

Aqui, salvamos a versão em chrome.storage.local em vez de uma variável global para manter o estado entre os ciclos de vida do worker do serviço. Como o armazenamento só pode seja acessado de forma assíncrona, também retornamos "true" do listener onMessage para garanta que o callback sendResponse permaneça ativo.

Etapa 7: executar o teste novamente

Execute o teste novamente com npm start. Agora ele será aprovado.

Próximas etapas

Agora você pode aplicar a mesma abordagem à sua extensão. Considere o seguinte:

  • Crie seu pacote de testes para dar suporte à execução com ou sem serviço inesperado de funcionários. Em seguida, execute os dois modos individualmente para esclarecer a causa da falha.
  • Escreva o código para encerrar o service worker em pontos aleatórios de um teste. Essa pode ser uma boa maneira de descobrir problemas que podem ser difíceis de prever.
  • Aprenda com as falhas de teste e tente codificar defensivamente no futuro. Para exemplo, adicione uma regra de inspeção para desencorajar o uso de variáveis globais e tente migrar os dados para um estado mais persistente.