Beëindiging van service workers testen met Puppeteer

In deze handleiding wordt uitgelegd hoe je robuustere extensies maakt door de beëindiging van serviceworkers te testen met Puppeteer. Het is belangrijk dat je op elk moment voorbereid bent op beëindiging, omdat dit zonder waarschuwing kan gebeuren. Hierdoor gaat elke niet-permanente status in de servicemedewerker verloren. Daarom moeten extensies belangrijke statussen opslaan en verzoeken kunnen verwerken zodra ze opnieuw worden opgestart als er een gebeurtenis is die moet worden verwerkt.

Voordat je begint

Kloon of download de repository chrome-extensions-samples. We gebruiken de testextensie in /functional-samples/tutorial.terminate-sw/test-extension. Deze stuurt een bericht naar de servicemedewerker telkens wanneer er op een knop wordt geklikt en voegt tekst toe aan de pagina als er een reactie wordt ontvangen.

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

Stap 1: Start je Node.js-project

Maak de volgende bestanden in een nieuwe directory. Samen maken ze een nieuw Node.js-project en bieden ze de basisstructuur van een Puppeteer-testpakket met Jest als testrunner. Ga naar Chrome-extensies testen met Puppeteer voor meer informatie over deze instelling.

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

Onze test laadt de test-extension uit de opslagplaats met voorbeelden. De handler voor browser.runtime.onMessage is afhankelijk van de status die is ingesteld in de handler voor de gebeurtenis browser.runtime.onInstalled. Daarom gaat de content van data verloren als de servicemedewerker wordt beëindigd en mislukt de reactie op toekomstige berichten. We lossen dit op nadat we onze test hebben geschreven.

service-worker-broken.js:

let data;

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

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

Stap 2: Installeer afhankelijkheden

Voer npm install uit om de vereiste afhankelijkheden te installeren.

Stap 3: Schrijf een basistest

Voeg de volgende test toe aan de onderkant van index.test.js. Hiermee wordt de testpagina van onze testextensie geopend, wordt op het knopelement geklikt en wordt gewacht op een reactie van de servicemedewerker.

test('can message service worker', as>ync () = {
  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');
});

Je kunt je test uitvoeren met npm start en zou moeten zien dat deze is afgerond.

Stap 4: Beëindig de serviceworker

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_work&&er'  t.url().startsWith(host);
  });

  const worker = await target.worker();
  await worker.close();
}

Update tot slot uw test met de volgende code. Beëindig nu de serviceworker en klik opnieuw op de knop om te checken of je een reactie hebt gekregen.

test('can message service worker when terminated', as>ync () = {
  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 de test uit

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

Stap 6: Los het probleem met de serviceworker op

Los daarna het probleem met de serviceworker op door de afhankelijkheid van de tijdelijke status te verwijderen. Update de testextensie om de volgende code te gebruiken, die is opgeslagen in service-worker-fixed.js in de repository.

service-worker-fixed.js:

browser.runtime.onInstalled.addListener(() => {
  browser.storage.local.set({ version: browser.runtime.getManifest().version });
});

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

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

Stap 7: Voer de test opnieuw uit

Voer de test opnieuw uit met npm start. De betaling zou nu moeten lukken.

Volgende stappen

Je kunt nu dezelfde aanpak toepassen op je eigen extensie. Denk daarbij aan het volgende:

  • Maak je testpakket zo dat het kan worden uitgevoerd met of zonder onverwachte beëindiging van de serviceworker. Je kunt beide modi dan afzonderlijk uitvoeren om duidelijker te maken wat de oorzaak van een fout is.
  • Schrijf code om de servicemedewerker op willekeurige punten in een test te beëindigen. Dit kan een goede manier zijn om problemen te ontdekken die moeilijk te voorspellen zijn.
  • Leer van mislukte tests en probeer in de toekomst defensief te coderen. Voeg bijvoorbeeld een lintingregel toe om het gebruik van globale variabelen te ontmoedigen en probeer gegevens naar een meer permanente status te verplaatsen.