Service-Worker-Beendigung mit Puppeteer testen

In diesem Leitfaden erfahren Sie, wie Sie robustere Erweiterungen erstellen, indem Sie die Beendigung von Dienstarbeitern mit Puppeteer testen. Es ist wichtig, jederzeit auf die Beendigung vorbereitet zu sein, da dies ohne Warnung passieren kann und nicht persistente Status im Dienstarbeiter verloren gehen. Daher müssen Erweiterungen wichtigen Status speichern und Anfragen verarbeiten können, sobald sie wieder gestartet werden, wenn ein Ereignis verarbeitet werden muss.

Vorbereitung

Klonen Sie das Repository chrome-extensions-samples oder laden Sie es herunter. Wir verwenden die Testerweiterung in /functional-samples/tutorial.terminate-sw/test-extension, die jedes Mal, wenn auf eine Schaltfläche geklickt wird, eine Nachricht an den Service Worker sendet und der Seite Text hinzufügt, wenn eine Antwort empfangen wird.

Außerdem müssen Sie Node.JS installieren, die Laufzeit, auf der Puppeteer basiert.

Schritt 1: Node.js-Projekt starten

Erstellen Sie die folgenden Dateien in einem neuen Verzeichnis. Gemeinsam erstellen sie ein neues Node.js-Projekt erstellen und die Grundstruktur einer Puppeteer-Testsuite bereitstellen Verwenden Sie Jest als Test-Runner. Weitere Informationen zu dieser Konfiguration finden Sie unter Chrome-Erweiterungen mit Puppeteer testen.

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

Beachten Sie, dass in unserem Test die test-extension aus dem Samples-Repository geladen wird. Der Handler für chrome.runtime.onMessage basiert auf dem Status, der im Handler festgelegt wurde für das chrome.runtime.onInstalled-Ereignis. Daher wird der Inhalt von data gehen verloren, wenn der Service Worker beendet wird und auf zukünftige Anfragen schlagen fehl. Wir beheben das Problem, nachdem wir den Test geschrieben haben.

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

Schritt 2: Abhängigkeiten installieren

Führen Sie npm install aus, um die erforderlichen Abhängigkeiten zu installieren.

Schritt 3: Einen einfachen Test schreiben

Fügen Sie den folgenden Test am Ende von index.test.js hinzu. Damit wird der Test geöffnet von unserer Testerweiterung aus, klickt auf das Schaltflächenelement und wartet auf eine Antwort vom Service Worker erhalten.

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

Sie können den Test mit npm start ausführen und sehen, dass er abgeschlossen ist. erfolgreich war.

Schritt 4: Dienstarbeiter beenden

Fügen Sie die folgende Hilfsfunktion hinzu, die Ihren Service Worker beendet:

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

Aktualisieren Sie den Test abschließend mit dem folgenden Code. Dienst beenden und klicken Sie erneut auf die Schaltfläche, um zu überprüfen, ob Sie eine Antwort erhalten haben.

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

Schritt 5: Test ausführen

Führen Sie npm start aus. Der Test sollte fehlschlagen, was darauf hinweist, dass der Dienst-Worker nach dem Beenden nicht geantwortet hat.

Schritt 6: Service Worker korrigieren

Als Nächstes beheben Sie den Dienst-Worker, indem Sie die Abhängigkeit vom temporären Status entfernen. Aktualisieren die Testerweiterung, um den folgenden Code zu verwenden, der in folgendem Verzeichnis gespeichert ist: service-worker-fixed.js im Repository.

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

Hier speichern wir die Version in chrome.storage.local anstelle einer globalen Variablen, um den Status zwischen den Lebensdauern von Dienst-Workern beizubehalten. Da nur asynchron auf den Speicher zugegriffen werden kann, geben wir auch über den onMessage-Listener den Wert „true“ zurück, damit der sendResponse-Callback aktiv bleibt.

Schritt 7: Test noch einmal ausführen

Führen Sie den Test noch einmal mit npm start aus. Es sollte jetzt funktionieren.

Nächste Schritte

Diesen Ansatz können Sie jetzt auch auf Ihre eigene Erweiterung anwenden. Beachten Sie dabei Folgendes:

  • Erstelle deine Testsuite, um die Ausführung mit oder ohne unerwarteten Diensten zu unterstützen Beendigung von Arbeitskräften. Sie können dann beide Modi einzeln ausführen, um besser nachvollziehen zu können, was zu einem Fehler geführt hat.
  • Schreiben Sie Code, um den Dienst-Worker an zufälligen Stellen innerhalb eines Tests zu beenden. So können Sie Probleme erkennen, die möglicherweise schwer vorhersehbar sind.
  • Lernen Sie aus den Testfehlern und versuchen Sie, in Zukunft sicherer zu programmieren. Fügen Sie beispielsweise eine Lint-Regel hinzu, um die Verwendung globaler Variablen zu vermeiden, und versuchen Sie, Daten in einen dauerhafteren Zustand zu verschieben.