chore: update chrome extensions doc and tests (#34236)
This commit is contained in:
parent
d6d5944797
commit
7ee7e018fa
|
|
@ -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.
|
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
|
```js
|
||||||
const { chromium } = require('playwright');
|
const { chromium } = require('playwright');
|
||||||
|
|
@ -18,7 +20,7 @@ const { chromium } = require('playwright');
|
||||||
const pathToExtension = require('path').join(__dirname, 'my-extension');
|
const pathToExtension = require('path').join(__dirname, 'my-extension');
|
||||||
const userDataDir = '/tmp/test-user-data-dir';
|
const userDataDir = '/tmp/test-user-data-dir';
|
||||||
const browserContext = await chromium.launchPersistentContext(userDataDir, {
|
const browserContext = await chromium.launchPersistentContext(userDataDir, {
|
||||||
headless: false,
|
channel: 'chromium',
|
||||||
args: [
|
args: [
|
||||||
`--disable-extensions-except=${pathToExtension}`,
|
`--disable-extensions-except=${pathToExtension}`,
|
||||||
`--load-extension=${pathToExtension}`
|
`--load-extension=${pathToExtension}`
|
||||||
|
|
@ -44,7 +46,7 @@ user_data_dir = "/tmp/test-user-data-dir"
|
||||||
async def run(playwright: Playwright):
|
async def run(playwright: Playwright):
|
||||||
context = await playwright.chromium.launch_persistent_context(
|
context = await playwright.chromium.launch_persistent_context(
|
||||||
user_data_dir,
|
user_data_dir,
|
||||||
headless=False,
|
channel="chromium",
|
||||||
args=[
|
args=[
|
||||||
f"--disable-extensions-except={path_to_extension}",
|
f"--disable-extensions-except={path_to_extension}",
|
||||||
f"--load-extension={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):
|
def run(playwright: Playwright):
|
||||||
context = playwright.chromium.launch_persistent_context(
|
context = playwright.chromium.launch_persistent_context(
|
||||||
user_data_dir,
|
user_data_dir,
|
||||||
headless=False,
|
channel="chromium",
|
||||||
args=[
|
args=[
|
||||||
f"--disable-extensions-except={path_to_extension}",
|
f"--disable-extensions-except={path_to_extension}",
|
||||||
f"--load-extension={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.
|
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:
|
First, add fixtures that will load the extension:
|
||||||
|
|
||||||
```js title="fixtures.ts"
|
```js title="fixtures.ts"
|
||||||
|
|
@ -114,7 +118,7 @@ export const test = base.extend<{
|
||||||
context: async ({ }, use) => {
|
context: async ({ }, use) => {
|
||||||
const pathToExtension = path.join(__dirname, 'my-extension');
|
const pathToExtension = path.join(__dirname, 'my-extension');
|
||||||
const context = await chromium.launchPersistentContext('', {
|
const context = await chromium.launchPersistentContext('', {
|
||||||
headless: false,
|
channel: 'chromium',
|
||||||
args: [
|
args: [
|
||||||
`--disable-extensions-except=${pathToExtension}`,
|
`--disable-extensions-except=${pathToExtension}`,
|
||||||
`--load-extension=${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")
|
path_to_extension = Path(__file__).parent.joinpath("my-extension")
|
||||||
context = playwright.chromium.launch_persistent_context(
|
context = playwright.chromium.launch_persistent_context(
|
||||||
"",
|
"",
|
||||||
headless=False,
|
channel="chromium",
|
||||||
args=[
|
args=[
|
||||||
f"--disable-extensions-except={path_to_extension}",
|
f"--disable-extensions-except={path_to_extension}",
|
||||||
f"--load-extension={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")
|
page.goto(f"chrome-extension://{extension_id}/popup.html")
|
||||||
expect(page.locator("body")).to_have_text("my-extension popup")
|
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}",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
|
||||||
|
|
@ -52,19 +52,17 @@ it('should open devtools when "devtools: true" option is given', async ({ browse
|
||||||
await browser.close();
|
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');
|
it.skip(isHeadlessShell, 'Headless Shell has no support for extensions');
|
||||||
|
|
||||||
const userDataDir = await createUserDataDir();
|
|
||||||
const extensionPath = asset('simple-extension');
|
const extensionPath = asset('simple-extension');
|
||||||
const extensionOptions = {
|
const extensionOptions = {
|
||||||
headless: false,
|
|
||||||
args: [
|
args: [
|
||||||
`--disable-extensions-except=${extensionPath}`,
|
`--disable-extensions-except=${extensionPath}`,
|
||||||
`--load-extension=${extensionPath}`,
|
`--load-extension=${extensionPath}`,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const context = await browserType.launchPersistentContext(userDataDir, extensionOptions);
|
const context = await browserType.launchPersistentContext('', extensionOptions);
|
||||||
const backgroundPages = context.backgroundPages();
|
const backgroundPages = context.backgroundPages();
|
||||||
const backgroundPage = backgroundPages.length
|
const backgroundPage = backgroundPages.length
|
||||||
? backgroundPages[0]
|
? backgroundPages[0]
|
||||||
|
|
@ -77,13 +75,11 @@ it('should return background pages', async ({ browserType, createUserDataDir, as
|
||||||
expect(context.backgroundPages().length).toBe(0);
|
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');
|
it.skip(isHeadlessShell, 'Headless Shell has no support for extensions');
|
||||||
|
|
||||||
const userDataDir = await createUserDataDir();
|
|
||||||
const extensionPath = asset('simple-extension');
|
const extensionPath = asset('simple-extension');
|
||||||
const extensionOptions = {
|
const extensionOptions = {
|
||||||
headless: false,
|
|
||||||
args: [
|
args: [
|
||||||
`--disable-extensions-except=${extensionPath}`,
|
`--disable-extensions-except=${extensionPath}`,
|
||||||
`--load-extension=${extensionPath}`,
|
`--load-extension=${extensionPath}`,
|
||||||
|
|
@ -92,7 +88,7 @@ it('should return background pages when recording video', async ({ browserType,
|
||||||
dir: testInfo.outputPath(''),
|
dir: testInfo.outputPath(''),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const context = await browserType.launchPersistentContext(userDataDir, extensionOptions);
|
const context = await browserType.launchPersistentContext('', extensionOptions);
|
||||||
const backgroundPages = context.backgroundPages();
|
const backgroundPages = context.backgroundPages();
|
||||||
const backgroundPage = backgroundPages.length
|
const backgroundPage = backgroundPages.length
|
||||||
? backgroundPages[0]
|
? backgroundPages[0]
|
||||||
|
|
@ -103,23 +99,21 @@ it('should return background pages when recording video', async ({ browserType,
|
||||||
await context.close();
|
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');
|
it.skip(isHeadlessShell, 'Headless Shell has no support for extensions');
|
||||||
|
|
||||||
server.setRoute('/empty.html', (req, res) => {
|
server.setRoute('/empty.html', (req, res) => {
|
||||||
res.writeHead(200, { 'Content-Type': 'text/html', 'x-response-foobar': 'BarFoo' });
|
res.writeHead(200, { 'Content-Type': 'text/html', 'x-response-foobar': 'BarFoo' });
|
||||||
res.end(`<span>hello world!</span>`);
|
res.end(`<span>hello world!</span>`);
|
||||||
});
|
});
|
||||||
const userDataDir = await createUserDataDir();
|
|
||||||
const extensionPath = asset('simple-extension');
|
const extensionPath = asset('simple-extension');
|
||||||
const extensionOptions = {
|
const extensionOptions = {
|
||||||
headless: false,
|
|
||||||
args: [
|
args: [
|
||||||
`--disable-extensions-except=${extensionPath}`,
|
`--disable-extensions-except=${extensionPath}`,
|
||||||
`--load-extension=${extensionPath}`,
|
`--load-extension=${extensionPath}`,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const context = await browserType.launchPersistentContext(userDataDir, extensionOptions);
|
const context = await browserType.launchPersistentContext('', extensionOptions);
|
||||||
const backgroundPages = context.backgroundPages();
|
const backgroundPages = context.backgroundPages();
|
||||||
const backgroundPage = backgroundPages.length
|
const backgroundPage = backgroundPages.length
|
||||||
? backgroundPages[0]
|
? backgroundPages[0]
|
||||||
|
|
@ -154,19 +148,17 @@ it('should support request/response events when using backgroundPage()', async (
|
||||||
|
|
||||||
it('should report console messages from content script', {
|
it('should report console messages from content script', {
|
||||||
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32762' }
|
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');
|
it.skip(isHeadlessShell, 'Headless Shell has no support for extensions');
|
||||||
|
|
||||||
const userDataDir = await createUserDataDir();
|
|
||||||
const extensionPath = asset('extension-with-logging');
|
const extensionPath = asset('extension-with-logging');
|
||||||
const extensionOptions = {
|
const extensionOptions = {
|
||||||
headless: false,
|
|
||||||
args: [
|
args: [
|
||||||
`--disable-extensions-except=${extensionPath}`,
|
`--disable-extensions-except=${extensionPath}`,
|
||||||
`--load-extension=${extensionPath}`,
|
`--load-extension=${extensionPath}`,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const context = await browserType.launchPersistentContext(userDataDir, extensionOptions);
|
const context = await browserType.launchPersistentContext('', extensionOptions);
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
const consolePromise = page.waitForEvent('console', e => e.text().includes('Test console log from a third-party execution context'));
|
const consolePromise = page.waitForEvent('console', e => e.text().includes('Test console log from a third-party execution context'));
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue