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.