Playwright Python — справочникНа главный сайт

Обзор

Playwright поддерживает загрузку расширений Chrome (CRX) при запуске браузера. Это полезно для тестирования ваших собственных расширений или проверки совместимости с популярными дополнениями.

Важно: Расширения Chrome работают только в Chromium с параметром headless=False. Режим headless не поддерживает расширения. Также рекомендуется указывать channel="chrome" для использования системного Chrome.

Запуск Chromium с расширением

Путь к расширению — это директория, содержащая файл manifest.json:

from playwright.sync_api import sync_playwright

EXT_PATH = "/path/to/my-extension"

with sync_playwright() as p:
    browser = p.chromium.launch(
        headless=False,
        channel="chrome",
        args=[
            f"--disable-extensions-except={EXT_PATH}",
            f"--load-extension={EXT_PATH}"
        ]
    )
    context = browser.new_context()
    page = context.new_page()
    page.goto("https://example.com")

Структура расширения

Минимальный manifest.json:

{
    "manifest_version": 3,
    "name": "My Extension",
    "version": "1.0",
    "permissions": ["storage", "activeTab"],
    "background": {
        "service_worker": "background.js"
    },
    "action": {
        "default_popup": "popup.html"
    }
}

Получение background page / Service Worker

У контекста есть свойство background_page, которое указывает на Service Worker расширения (Manifest V3):

browser = p.chromium.launch(
    headless=False,
    channel="chrome",
    args=[
        f"--disable-extensions-except={EXT_PATH}",
        f"--load-extension={EXT_PATH}"
    ]
)
context = browser.new_context()

# Ждём загрузки Service Worker расширения
background_page = context.background_page
if background_page:
    print("Service Worker загружен:", background_page.url)
Примечание: context.background_page доступен только после полной инициализации расширения. Если расширение использует Manifest V2 и background page, она также будет доступна через это свойство. Для Manifest V3 возвращается Service Worker.

Доступ к chrome.* API через page.evaluate()

На страницах расширения API chrome.* доступен напрямую. На обычных веб-страницах он отсутствует, но вы можете взаимодействовать с расширением через его Service Worker:

# Открыть popup расширения
page.goto("chrome-extension://EXTENSION_ID/popup.html")

# Вызвать chrome API внутри popup
result = page.evaluate("""() => {
    return new Promise((resolve) => {
        chrome.storage.local.get(['key'], (data) => {
            resolve(data.key);
        });
    });
}""")

Тестирование расширения: открыть popup

import re

EXT_ID = "abcdefghijklmnopabcdefghijklmn"

# Открываем popup расширения
page = context.new_page()
page.goto(f"chrome-extension://{EXT_ID}/popup.html")

# Взаимодействуем с popup
page.fill("#input", "test")
page.click("#save")
assert page.locator(".status").text_content() == "Сохранено"
Совет: ID расширения можно получить из URL при открытии chrome://extensions/ или из chrome.runtime.id через evaluate. Если ID неизвестен, используйте context.pages после открытия popup.

Проверка контента на странице с расширением

# Загружаем страницу, на которой расширение добавляет элементы
page.goto("https://example.com")

# Проверяем, что расширение внедрило свой контент
injected = page.locator(".extension-injected-element")
assert injected.is_visible()

Полный пример

from playwright.sync_api import sync_playwright
import os

EXT_PATH = os.path.abspath("./my-extension")

def test_extension():
    with sync_playwright() as p:
        browser = p.chromium.launch(
            headless=False,
            channel="chrome",
            args=[
                f"--disable-extensions-except={EXT_PATH}",
                f"--load-extension={EXT_PATH}"
            ]
        )
        context = browser.new_context()

        # Ждём Service Worker
        background = context.background_page
        assert background is not None, "Расширение не загружено"

        # Открываем popup
        page = context.pages[0]
        page.goto(f"chrome-extension://{background.url.split('/')[2]}/popup.html")

        # Проверяем функциональность
        page.fill("#search-input", "playwright")
        page.click("#search-btn")
        assert page.locator(".results").is_visible()

        # Вызов chrome API через background page
        storage_data = background.evaluate("""() => {
            return new Promise((resolve) => {
                chrome.storage.local.get(null, resolve);
            });
        }""")
        print("Данные хранилища:", storage_data)

        browser.close()

test_extension()