Beëindiging van servicemedewerkers testen met Puppeteer

In deze handleiding wordt uitgelegd hoe u robuustere extensies kunt bouwen door de beëindiging van servicemedewerkers te testen met behulp van Puppeteer. Het is belangrijk dat u op elk moment voorbereid kunt zijn op beëindiging, omdat dit zonder waarschuwing kan gebeuren, waardoor een niet-persistente toestand van de servicemedewerker verloren gaat. Bijgevolg moeten extensies belangrijke statussen opslaan en verzoeken kunnen afhandelen zodra ze opnieuw worden opgestart als er een gebeurtenis moet worden afgehandeld.

Voordat je start

Kloon of download de chrome-extensions-samples- repository. We gebruiken de testextensie in /functional-samples/tutorial.terminate-sw/test-extension , die elke keer dat er op een knop wordt geklikt een bericht naar de servicemedewerker stuurt en tekst aan de pagina toevoegt als er een antwoord wordt ontvangen.

Je moet ook Node.JS installeren, de runtime waarop Puppeteer is gebouwd.

Stap 1: Start uw Node.js-project

Maak de volgende bestanden in een nieuwe map. Samen creëren ze een nieuw Node.js-project en bieden ze de basisstructuur van een Puppeteer-testsuite met Jest als testrunner. Zie Chrome-extensies testen met Puppeteer voor meer informatie over deze configuratie.

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

Merk op dat onze test de test-extension uit de voorbeeldrepository laadt. De handler voor chrome.runtime.onMessage is afhankelijk van de status die is ingesteld in de handler voor de gebeurtenis chrome.runtime.onInstalled . Als gevolg hiervan gaat de inhoud van data verloren wanneer de servicemedewerker wordt beëindigd en zal het reageren op toekomstige berichten mislukken. We zullen dit oplossen na het schrijven van onze test.

service-werker-broken.js:

let data;

chrome.runtime.onInstalled.addListener(() => {
  data = { version: chrome.runtime.getManifest().version };
});

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

Stap 2: Afhankelijkheden installeren

Voer npm install om de vereiste afhankelijkheden te installeren.

Stap 3: Schrijf een basistest

Voeg de volgende test toe aan de onderkant van index.test.js . Deze opent de testpagina van onze testextensie, klikt op het knopelement en wacht op een reactie van de servicemedewerker.

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

U kunt uw test uitvoeren met npm start en u zult zien dat deze met succes wordt voltooid.

Stap 4: Beëindig de servicemedewerker

Voeg de volgende helperfunctie toe die uw servicemedewerker beëindigt:

/**
 * 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();
}

Werk ten slotte uw test bij met de volgende code. Beëindig nu de servicemedewerker en klik nogmaals op de knop om te controleren of u een reactie heeft ontvangen.

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

Stap 5: Voer uw test uit

Voer npm start . Uw test zou moeten mislukken, wat aangeeft dat de servicemedewerker niet heeft gereageerd nadat deze was beëindigd.

Stap 6: Repareer de servicemedewerker

Corrigeer vervolgens de servicemedewerker door zijn afhankelijkheid van de tijdelijke status weg te nemen. Werk de testextensie bij om de volgende code te gebruiken, die is opgeslagen in service-worker-fixed.js in de repository.

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

Hier slaan we de versie op in chrome.storage.local in plaats van een globale variabele om de status tussen de levens van servicemedewerkers te behouden. Omdat opslag alleen asynchroon toegankelijk is, retourneren we ook true van de onMessage listener om ervoor te zorgen dat de sendResponse callback actief blijft.

Stap 7: Voer uw test opnieuw uit

Voer de test opnieuw uit met npm start . Het zou nu voorbij moeten gaan.

Volgende stappen

U kunt nu dezelfde aanpak toepassen op uw eigen extensie. Stel je de volgende situatie voor:

  • Bouw uw testsuite ter ondersteuning van uitvoering met of zonder onverwachte beëindiging van de servicemedewerker. Vervolgens kunt u beide modi afzonderlijk uitvoeren om duidelijker te maken wat de oorzaak van een fout is.
  • Schrijf code om de servicemedewerker op willekeurige punten binnen een test te beëindigen. Dit kan een goede manier zijn om problemen te ontdekken die misschien moeilijk te voorspellen zijn.
  • Leer van testfouten en probeer in de toekomst defensief te coderen. Voeg bijvoorbeeld een lintingregel toe om het gebruik van globale variabelen te ontmoedigen en te proberen gegevens in een meer persistente staat te brengen.