chore: update chrome extensions doc and tests (#34236)

This commit is contained in:
Dmitry Gozman 2025-01-08 17:24:29 +00:00 committed by GitHub
parent d6d5944797
commit 7ee7e018fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 18 additions and 52 deletions

View File

@ -9,7 +9,9 @@ title: "Chrome extensions"
Extensions only work in Chrome / Chromium launched with a persistent context. Use custom browser args at your own risk, as some of them may break Playwright functionality.
:::
The following is code for getting a handle to the [background page](https://developer.chrome.com/extensions/background_pages) of a [Manifest v2](https://developer.chrome.com/docs/extensions/mv2/) extension whose source is located in `./my-extension`:
The snippet below retrieves the [background page](https://developer.chrome.com/extensions/background_pages) of a [Manifest v2](https://developer.chrome.com/docs/extensions/mv2/) extension whose source is located in `./my-extension`.
Note the use of the `chromium` channel that allows to run extensions in headless mode. Alternatively, you can launch the browser in headed mode.
```js
const { chromium } = require('playwright');
@ -18,7 +20,7 @@ const { chromium } = require('playwright');
const pathToExtension = require('path').join(__dirname, 'my-extension');
const userDataDir = '/tmp/test-user-data-dir';
const browserContext = await chromium.launchPersistentContext(userDataDir, {
headless: false,
channel: 'chromium',
args: [
`--disable-extensions-except=${pathToExtension}`,
`--load-extension=${pathToExtension}`
@ -44,7 +46,7 @@ user_data_dir = "/tmp/test-user-data-dir"
async def run(playwright: Playwright):
context = await playwright.chromium.launch_persistent_context(
user_data_dir,
headless=False,
channel="chromium",
args=[
f"--disable-extensions-except={path_to_extension}",
f"--load-extension={path_to_extension}",
@ -78,7 +80,7 @@ user_data_dir = "/tmp/test-user-data-dir"
def run(playwright: Playwright):
context = playwright.chromium.launch_persistent_context(
user_data_dir,
headless=False,
channel="chromium",
args=[
f"--disable-extensions-except={path_to_extension}",
f"--load-extension={path_to_extension}",
@ -101,6 +103,8 @@ with sync_playwright() as playwright:
To have the extension loaded when running tests you can use a test fixture to set the context. You can also dynamically retrieve the extension id and use it to load and test the popup page for example.
Note the use of the `chromium` channel that allows to run extensions in headless mode. Alternatively, you can launch the browser in headed mode.
First, add fixtures that will load the extension:
```js title="fixtures.ts"
@ -114,7 +118,7 @@ export const test = base.extend<{
context: async ({ }, use) => {
const pathToExtension = path.join(__dirname, 'my-extension');
const context = await chromium.launchPersistentContext('', {
headless: false,
channel: 'chromium',
args: [
`--disable-extensions-except=${pathToExtension}`,
`--load-extension=${pathToExtension}`,
@ -155,7 +159,7 @@ def context(playwright: Playwright) -> Generator[BrowserContext, None, None]:
path_to_extension = Path(__file__).parent.joinpath("my-extension")
context = playwright.chromium.launch_persistent_context(
"",
headless=False,
channel="chromium",
args=[
f"--disable-extensions-except={path_to_extension}",
f"--load-extension={path_to_extension}",
@ -211,33 +215,3 @@ def test_popup_page(page: Page, extension_id: str) -> None:
page.goto(f"chrome-extension://{extension_id}/popup.html")
expect(page.locator("body")).to_have_text("my-extension popup")
```
## Headless mode
By default, Chrome's headless mode in Playwright does not support Chrome extensions. To overcome this limitation, you can run Chrome's persistent context with a new headless mode by using [channel `chromium`](./browsers.md#chromium-new-headless-mode):
```js title="fixtures.ts"
// ...
const pathToExtension = path.join(__dirname, 'my-extension');
const context = await chromium.launchPersistentContext('', {
channel: 'chromium',
args: [
`--disable-extensions-except=${pathToExtension}`,
`--load-extension=${pathToExtension}`,
],
});
// ...
```
```python title="conftest.py"
path_to_extension = Path(__file__).parent.joinpath("my-extension")
context = playwright.chromium.launch_persistent_context(
"",
channel="chromium",
args=[
f"--disable-extensions-except={path_to_extension}",
f"--load-extension={path_to_extension}",
],
)
```

View File

@ -52,19 +52,17 @@ it('should open devtools when "devtools: true" option is given', async ({ browse
await browser.close();
});
it('should return background pages', async ({ browserType, createUserDataDir, asset, isHeadlessShell }) => {
it('should return background pages', async ({ browserType, asset, isHeadlessShell }) => {
it.skip(isHeadlessShell, 'Headless Shell has no support for extensions');
const userDataDir = await createUserDataDir();
const extensionPath = asset('simple-extension');
const extensionOptions = {
headless: false,
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
],
};
const context = await browserType.launchPersistentContext(userDataDir, extensionOptions);
const context = await browserType.launchPersistentContext('', extensionOptions);
const backgroundPages = context.backgroundPages();
const backgroundPage = backgroundPages.length
? backgroundPages[0]
@ -77,13 +75,11 @@ it('should return background pages', async ({ browserType, createUserDataDir, as
expect(context.backgroundPages().length).toBe(0);
});
it('should return background pages when recording video', async ({ browserType, createUserDataDir, asset, isHeadlessShell }, testInfo) => {
it('should return background pages when recording video', async ({ browserType, asset, isHeadlessShell }, testInfo) => {
it.skip(isHeadlessShell, 'Headless Shell has no support for extensions');
const userDataDir = await createUserDataDir();
const extensionPath = asset('simple-extension');
const extensionOptions = {
headless: false,
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
@ -92,7 +88,7 @@ it('should return background pages when recording video', async ({ browserType,
dir: testInfo.outputPath(''),
},
};
const context = await browserType.launchPersistentContext(userDataDir, extensionOptions);
const context = await browserType.launchPersistentContext('', extensionOptions);
const backgroundPages = context.backgroundPages();
const backgroundPage = backgroundPages.length
? backgroundPages[0]
@ -103,23 +99,21 @@ it('should return background pages when recording video', async ({ browserType,
await context.close();
});
it('should support request/response events when using backgroundPage()', async ({ browserType, createUserDataDir, asset, server, isHeadlessShell }) => {
it('should support request/response events when using backgroundPage()', async ({ browserType, asset, server, isHeadlessShell }) => {
it.skip(isHeadlessShell, 'Headless Shell has no support for extensions');
server.setRoute('/empty.html', (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html', 'x-response-foobar': 'BarFoo' });
res.end(`<span>hello world!</span>`);
});
const userDataDir = await createUserDataDir();
const extensionPath = asset('simple-extension');
const extensionOptions = {
headless: false,
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
],
};
const context = await browserType.launchPersistentContext(userDataDir, extensionOptions);
const context = await browserType.launchPersistentContext('', extensionOptions);
const backgroundPages = context.backgroundPages();
const backgroundPage = backgroundPages.length
? backgroundPages[0]
@ -154,19 +148,17 @@ it('should support request/response events when using backgroundPage()', async (
it('should report console messages from content script', {
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32762' }
}, async ({ browserType, createUserDataDir, asset, server, isHeadlessShell }) => {
}, async ({ browserType, asset, server, isHeadlessShell }) => {
it.skip(isHeadlessShell, 'Headless Shell has no support for extensions');
const userDataDir = await createUserDataDir();
const extensionPath = asset('extension-with-logging');
const extensionOptions = {
headless: false,
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
],
};
const context = await browserType.launchPersistentContext(userDataDir, extensionOptions);
const context = await browserType.launchPersistentContext('', extensionOptions);
const page = await context.newPage();
const consolePromise = page.waitForEvent('console', e => e.text().includes('Test console log from a third-party execution context'));
await page.goto(server.EMPTY_PAGE);