2021-06-10 07:48:16 -07:00
---
2021-06-10 13:43:42 -07:00
id: chrome-extensions
2023-03-06 20:52:58 +01:00
title: "Chrome extensions"
2021-06-10 07:48:16 -07:00
---
:::note
2023-01-05 22:08:16 +03:00
Extensions only work in Chrome / Chromium launched with a persistent context.
2021-06-10 07:48:16 -07:00
:::
2022-06-21 03:04:34 +01:00
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` :
2021-06-10 07:48:16 -07:00
```js
const { chromium } = require('playwright');
(async () => {
const pathToExtension = require('path').join(__dirname, 'my-extension');
const userDataDir = '/tmp/test-user-data-dir';
2023-06-29 18:26:19 +02:00
const browserContext = await chromium.launchPersistentContext(userDataDir, {
2021-06-10 07:48:16 -07:00
headless: false,
args: [
`--disable-extensions-except=${pathToExtension}` ,
`--load-extension=${pathToExtension}`
]
});
2022-06-21 03:04:34 +01:00
let [backgroundPage] = browserContext.backgroundPages();
if (!backgroundPage)
backgroundPage = await browserContext.waitForEvent('backgroundpage');
2021-06-10 07:48:16 -07:00
// Test the background page as you would any other page.
await browserContext.close();
})();
```
2021-06-28 21:26:04 +02:00
```python async
import asyncio
from playwright.async_api import async_playwright
path_to_extension = "./my-extension"
user_data_dir = "/tmp/test-user-data-dir"
async def run(playwright):
context = await playwright.chromium.launch_persistent_context(
user_data_dir,
headless=False,
args=[
f"--disable-extensions-except={path_to_extension}",
f"--load-extension={path_to_extension}",
],
)
2022-06-21 03:04:34 +01:00
if len(context.background_pages) == 0:
background_page = await context.wait_for_event('backgroundpage')
else:
background_page = context.background_pages[0]
2021-06-28 21:26:04 +02:00
# Test the background page as you would any other page.
await context.close()
async def main():
async with async_playwright() as playwright:
await run(playwright)
asyncio.run(main())
```
```python sync
2021-07-14 19:01:46 +02:00
from playwright.sync_api import sync_playwright
2021-06-28 21:26:04 +02:00
path_to_extension = "./my-extension"
user_data_dir = "/tmp/test-user-data-dir"
def run(playwright):
context = playwright.chromium.launch_persistent_context(
user_data_dir,
headless=False,
args=[
f"--disable-extensions-except={path_to_extension}",
f"--load-extension={path_to_extension}",
],
)
2022-06-21 03:04:34 +01:00
if len(context.background_pages) == 0:
background_page = context.wait_for_event('backgroundpage')
else:
background_page = context.background_pages[0]
2021-06-28 21:26:04 +02:00
# Test the background page as you would any other page.
context.close()
with sync_playwright() as playwright:
run(playwright)
```
2022-07-07 19:28:01 +01:00
## Testing
2022-10-04 06:18:01 -04:00
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.
2022-07-07 19:28:01 +01:00
2022-11-14 14:50:01 -08:00
First, add fixtures that will load the extension:
2023-05-10 23:30:51 +02:00
```js title="fixtures.ts"
2022-11-15 15:46:54 -08:00
import { test as base, expect, chromium, type BrowserContext } from '@playwright/test ';
import path from 'path';
2022-07-07 19:28:01 +01:00
export const test = base.extend< {
context: BrowserContext;
extensionId: string;
}>({
2022-07-08 13:40:49 +02:00
context: async ({ }, use) => {
2022-11-14 14:50:01 -08:00
const pathToExtension = path.join(__dirname, 'my-extension');
const context = await chromium.launchPersistentContext('', {
2022-07-07 19:28:01 +01:00
headless: false,
args: [
`--disable-extensions-except=${pathToExtension}` ,
`--load-extension=${pathToExtension}` ,
],
});
await use(context);
await context.close();
},
extensionId: async ({ context }, use) => {
/*
// for manifest v2:
let [background] = context.backgroundPages()
if (!background)
2022-11-14 14:50:01 -08:00
background = await context.waitForEvent('backgroundpage')
2022-07-07 19:28:01 +01:00
*/
// for manifest v3:
let [background] = context.serviceWorkers();
if (!background)
2022-11-14 14:50:01 -08:00
background = await context.waitForEvent('serviceworker');
2022-07-07 19:28:01 +01:00
2022-11-14 14:50:01 -08:00
const extensionId = background.url().split('/')[2];
2022-07-07 19:28:01 +01:00
await use(extensionId);
},
});
2022-11-14 14:50:01 -08:00
export const expect = test.expect;
2022-07-07 19:28:01 +01:00
```
2022-07-08 13:40:49 +02:00
2023-05-10 23:30:51 +02:00
```python title="conftest.py"
2022-07-08 13:40:49 +02:00
from typing import Generator
from pathlib import Path
from playwright.sync_api import Playwright, BrowserContext
import pytest
@pytest .fixture()
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,
args=[
f"--disable-extensions-except={path_to_extension}",
f"--load-extension={path_to_extension}",
],
)
yield context
context.close()
@pytest .fixture()
def extension_id(context) -> Generator[str, None, None]:
# for manifest v2:
# background = context.background_pages[0]
# if not background:
# background = context.wait_for_event("backgroundpage")
# for manifest v3:
background = context.service_workers[0]
if not background:
background = context.wait_for_event("serviceworker")
extension_id = background.url.split("/")[2]
yield extension_id
```
2022-11-14 14:50:01 -08:00
Then use these fixtures in a test:
2023-03-16 21:16:18 +01:00
```js
2022-11-15 15:46:54 -08:00
import { test, expect } from './fixtures';
2022-11-14 14:50:01 -08:00
2022-11-15 15:46:54 -08:00
test('example test', async ({ page }) => {
await page.goto('https://example.com');
await expect(page.locator('body')).toHaveText('Changed by my-extension');
2022-11-14 14:50:01 -08:00
});
2022-11-15 15:46:54 -08:00
test('popup page', async ({ page, extensionId }) => {
2022-11-14 14:50:01 -08:00
await page.goto(`chrome-extension://${extensionId}/popup.html` );
2022-11-15 15:46:54 -08:00
await expect(page.locator('body')).toHaveText('my-extension popup');
2022-11-14 14:50:01 -08:00
});
```
2023-05-10 23:30:51 +02:00
```python title="test_foo.py"
2022-07-08 13:40:49 +02:00
from playwright.sync_api import expect, Page
def test_example_test(page: Page) -> None:
page.goto("https://example.com")
expect(page.locator("body")).to_contain_text("Changed by my-extension")
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")
2022-10-04 06:18:01 -04:00
```
2023-01-05 22:08:16 +03:00
## 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 the following code:
2023-05-10 23:30:51 +02:00
```js title="fixtures.ts"
2023-01-05 22:08:16 +03:00
// ...
const pathToExtension = path.join(__dirname, 'my-extension');
const context = await chromium.launchPersistentContext('', {
headless: false,
args: [
2023-02-06 12:31:05 +03:00
`--headless=new` , // the new headless arg for chrome v109+. Use '--headless=chrome' as arg for browsers v94-108.
2023-01-05 22:08:16 +03:00
`--disable-extensions-except=${pathToExtension}` ,
`--load-extension=${pathToExtension}` ,
],
});
// ...
```
2023-05-10 23:30:51 +02:00
```python title="conftest.py"
2023-01-05 22:08:16 +03:00
path_to_extension = Path(__file__).parent.joinpath("my-extension")
context = playwright.chromium.launch_persistent_context(
"",
2023-02-06 12:31:05 +03:00
headless=False,
2023-01-05 22:08:16 +03:00
args=[
2023-02-06 12:31:05 +03:00
"--headless=new", # the new headless arg for chrome v109+. Use '--headless=chrome' as arg for browsers v94-108.
2023-01-05 22:08:16 +03:00
f"--disable-extensions-except={path_to_extension}",
f"--load-extension={path_to_extension}",
],
)
2023-02-06 12:31:05 +03:00
```