Add examples for testing service worker termination (#1078)

* Add examples for testing service worker termination

* Add example of failing case to tutorial
This commit is contained in:
Oliver Dunk
2024-02-08 18:04:49 +00:00
committed by GitHub
parent b5e36835a3
commit c5287cde2c
14 changed files with 17570 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
# Testing service worker termination
## Overview
**Note:** The test extension is intentionally broken as part of a tutorial in
our documentation. See [Test service worker termination with Puppeteer](https://developer.chrome.com/docs/extensions/how-to/test/test-serviceworker-termination-with-puppeteer).
Sample code to show how to terminate a service worker in Puppeteer or Selenium.
## Running the tests
1. Install [Node.JS](https://nodejs.org/).
2. Change to the `puppeteer` or `selenium` directory.
3. Run `npm install`.
4. Run `npm start`.

View File

@@ -0,0 +1,7 @@
{
"extends": ["../../../.eslintrc"],
"plugins": ["jest"],
"env": {
"jest/globals": true
}
}

View File

@@ -0,0 +1,86 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// eslint-disable-next-line no-undef
const puppeteer = require('puppeteer');
const EXTENSION_PATH = '../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;
});
/**
* 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} page Puppeteer page that CDP session can be started from
* @param {string} extensionId Extension ID of worker to terminate
*/
async function stopServiceWorker(page, extensionId) {
const host = `chrome-extension://${extensionId}`;
// Create a new CDP session
const client = await page.target().createCDPSession();
// Find the extension service worker
const targets = await client.send('Target.getTargets');
const worker = targets.targetInfos.find(
(t) => t.type === 'service_worker' && t.url.startsWith(host)
);
if (!worker) {
throw new Error(`No worker found for ${host}`);
}
// Terminate the service worker
await client.send('Target.closeTarget', {
targetId: worker.targetId
});
// End the CDP session
await client.detach();
}
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');
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
{
"name": "puppeteer-demo",
"version": "1.0",
"dependencies": {
"jest": "^29.7.0",
"puppeteer": "^21.3.6"
},
"scripts": {
"start": "jest ."
},
"devDependencies": {
"@jest/globals": "^29.7.0"
}
}

View File

@@ -0,0 +1,7 @@
{
"extends": ["../../../.eslintrc"],
"plugins": ["jest"],
"env": {
"jest/globals": true
}
}

View File

@@ -0,0 +1,87 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// eslint-disable-next-line no-undef
const { Builder, Browser, By, until } = require('selenium-webdriver');
// eslint-disable-next-line no-undef
const { Options } = require('selenium-webdriver/chrome');
const EXTENSION_PATH = '../test-extension';
const EXTENSION_ID = 'gjgkofgpcmpfpggbgjgdfaaifcmoklbl';
let driver;
let cdpConnection;
beforeEach(async () => {
driver = await new Builder()
.forBrowser(Browser.CHROME)
.setChromeOptions(
new Options().addArguments([
`--disable-extensions-except=${EXTENSION_PATH}`,
`--load-extension=${EXTENSION_PATH}`
])
)
.build();
// Create this here, since there is one connection per driver and we want to
// avoid repeatedly recreating it.
cdpConnection = await driver.createCDPConnection('page');
});
afterEach(async () => {
await driver.quit();
driver = undefined;
});
/**
* 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 {Driver} driver Driver that CDP session can be started from
* @param {string} extensionId Extension ID of worker to terminate
*/
async function stopServiceWorker(driver, extensionId) {
const host = `chrome-extension://${extensionId}`;
// Find the extension service worker
const response = await cdpConnection.send('Target.getTargets', {});
const worker = response.result.targetInfos.find(
(t) => t.type === 'service_worker' && t.url.startsWith(host)
);
if (!worker) {
throw new Error(`No worker found for ${host}`);
}
// Terminate the service worker
await cdpConnection.send('Target.closeTarget', {
targetId: worker.targetId
});
}
test('can message service worker when terminated', async () => {
await driver.get(`chrome-extension://${EXTENSION_ID}/page.html`);
// Message without terminating service worker
await driver.wait(until.elementLocated(By.css('button'))).click();
await driver.wait(until.elementLocated(By.css('#response-0')));
// Terminate service worker
await stopServiceWorker(driver, EXTENSION_ID);
// Try to send another message
await driver.wait(until.elementLocated(By.css('button'))).click();
await driver.wait(until.elementLocated(By.css('#response-1')));
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
{
"name": "puppeteer-demo",
"version": "1.0",
"dependencies": {
"jest": "^29.7.0",
"selenium-webdriver": "^4.17.0"
},
"scripts": {
"start": "jest ."
},
"devDependencies": {
"@jest/globals": "^29.7.0"
}
}

View File

@@ -0,0 +1,10 @@
{
"name": "Hello World",
"version": "0.1",
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr1Cssen3kE1Kzw8x2vhc0neVV3Im5HhQ28cl2zptYPZVLMkT4RuVAbSu8/CnlqyfYMoHSSUKgo/kaEOW+g9aoGjPyKYfmy5FJmIZnWfbKP+qLRcnX8XrgX6NM8XxAalzrq/a8DeaFTCtLyVEX+Xw5uzjJapXfaiFdwPhAV6TR0GPgB6pSvkcRiPTTcctqQ64/FSbNQutcVRA0bH8d8tnIDQZ9nm/9/4A0njq1Xujf/NnhldWjlKilQPLZh/2bXrThlWDLal3LJbMsvWolFbQWpGIPw4ti13bhBFp9A7jFUt89wBnwEPhViMEjatOJzx72karMqkAFEOfPiXsytrNYQIDAQAB",
"manifest_version": 3,
"description": "Basic Hello World Extension",
"background": {
"service_worker": "service-worker-broken.js"
}
}

View File

@@ -0,0 +1,9 @@
<html>
<head>
<title>Test Page</title>
</head>
<body>
<button id="get-response" type="button">Get Response</button>
<script src="page.js"></script>
</body>
</html>

View File

@@ -0,0 +1,15 @@
const GET_RESPONSE_BUTTON = document.getElementById('get-response');
let counter = 0;
GET_RESPONSE_BUTTON.addEventListener('click', async () => {
const response = await chrome.runtime.sendMessage('ping');
if (response) {
const element = document.createElement('p');
element.id = `response-${counter}`;
element.innerText = `Response ${counter}: ${response}`;
document.body.appendChild(element);
counter++;
}
});

View File

@@ -0,0 +1,9 @@
let data;
chrome.runtime.onInstalled.addListener(() => {
data = { version: chrome.runtime.getManifest().version };
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse(data.version);
});

View File

@@ -0,0 +1,3 @@
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse(chrome.runtime.getManifest().version);
});