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": "^22.1.0"
},
"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,
args: [
`--disable-extensions-except=${EXTENSION_PATH}`,
`--load-extension=${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.