Puppeteer を使用して Service Worker の終了をテストする

このガイドでは、サービスをテストすることで、より堅牢な拡張機能を作成する方法について説明します 終了させることもできます。終結処理の準備 警告なしに実行され、結果整合性のない状態になってしまうこともあるため、 発生しがちですそのため、拡張機能は重要な状態を保存し、処理するイベントがあるときに再起動後すぐにリクエストを処理できるようにする必要があります。

始める前に

chrome-extensions-samples リポジトリのクローンを作成するか、ダウンロードします。/functional-samples/tutorial.terminate-sw/test-extension でテスト拡張機能を使用して、ボタンがクリックされるたびにサービス ワーカーにメッセージを送信し、レスポンスが受信された場合はページにテキストを追加します。

また、Puppeteer が構築されているランタイムである Node.JS もインストールする必要があります。

ステップ 1: Node.js プロジェクトを開始する

新しいディレクトリに次のファイルを作成します。これらを組み合わせて新しい Node.js プロジェクトを作成し、テストランナーとして Jest を使用する Puppeteer テストスイートの基本構造を用意します。詳しくは、 この設定について詳しくは、Puppeteer を使用した Chrome 拡張機能のテストをご覧ください。 詳しく説明します。

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

このテストでは、サンプル リポジトリから test-extension が読み込まれます。 chrome.runtime.onMessage のハンドラは、chrome.runtime.onInstalled イベントのハンドラで設定された状態に依存します。その結果、data のコンテンツ 破棄され、以降のレスポンスに応答すると、データは失われ、 エラーが発生します。この問題は、テストの作成後に修正します。

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

ステップ 2: 依存関係をインストールする

npm install を実行して、必要な依存関係をインストールします。

ステップ 3: 基本的なテストを作成する

index.test.js の末尾に次のテストを追加します。これにより、テスト拡張機能のテストページが開き、ボタン要素がクリックされ、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');
});

npm start でテストを実行すると、テストが正常に完了します。

ステップ 4: Service Worker を終了する

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

最後に、次のコードでテストを更新します。サービスを終了する 再びボタンをクリックして、レスポンスが届いていることを確認します。

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

ステップ 5: テストを実行する

npm start を実行します。テストは失敗します。これは、サービス ワーカーが終了後に応答しなかったことを示します。

ステップ 6: Service Worker を修正する

次に、一時的な状態に依存しないようにして、サービス ワーカーを修正します。リポジトリの service-worker-fixed.js に保存されている次のコードを使用するように、test-extension を更新します。

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

ここでは、バージョンをグローバル変数ではなく chrome.storage.local に保存して、サービス ワーカーの存続期間の間に状態を保持します。ストレージは 場合、onMessage リスナーから true を返し、 sendResponse コールバックが存続していることを確認します。

ステップ 7: テストをもう一度実行する

npm start を使用してテストを再度実行します。合格するはずです。

次のステップ

同じ方法を独自の拡張機能に適用できるようになりました。次の点を考慮してください。

  • 予期しないサービスの有無にかかわらず実行をサポートするテストスイートを構築する 終了です。両方のモードを個別に実行すれば、より明確に 原因を特定します
  • テスト内のランダムなポイントで Service Worker を終了するコードを記述します。 これは、予測が難しい問題を発見するのに適した方法です。
  • テストの失敗から学び、今後は防御的なコードを記述するようにしてください。たとえば、リンティング ルールを追加してグローバル変数の使用を抑制し、データをより永続的な状態に移行するようにします。