Взаимодействие с popup расширений

Puppeteer предоставляет мощный инструмент для автоматизации браузера Chrome и Chromium, включая взаимодействие с расширениями. Одной из сложных задач в автоматизированном тестировании является работа с popup окон расширений, поскольку они отличаются от стандартных вкладок и требуют специфического подхода для доступа к их DOM и событиям.

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

Перед взаимодействием с popup необходимо запустить браузер с загруженным расширением. Для этого используется опция –disable-extensions-except и –load-extension при запуске браузера через Puppeteer:

const puppeteer = require(&

(async () => {
    const pathToExtension = '/path/to/extension';
    const browser = await puppeteer.launch({
        headless: false,
        args: [
            `--disable-extensions-except=${pathToExtension}`,
            `--load-extension=${pathToExtension}`
        ]
    });
})();

Ключевой момент: Расширения работают только в режиме не-headless, так как Chrome блокирует popup в headless-режиме.

Получение ссылки на popup окно

Popup расширения создаётся как отдельный target в браузере. Для доступа к нему используется метод browser.targets() или обработчик события targetcreated. Обычно popup открывается после взаимодействия с иконкой расширения:

const targetsBefore = await browser.targets();

// Здесь происходит клик по иконке расширения
const extensionId = 'abcdefghijklmnop'; // ID расширения
const extensionPopupUrl = `chrome-extension://${extensionId}/popup.html`;

const popupTarget = await new Promise(resolve => {
    browser.on('targetcreated', target => {
        if (target.url() === extensionPopupUrl) {
            resolve(target);
        }
    });
});

const popupPage = await popupTarget.page();

Важное замечание: URL popup известен только после анализа манифеста расширения (manifest.json) и его поля browser_action или action (в зависимости от манифеста версии 2 или 3).

Работа с DOM popup

После получения объекта Page для popup можно использовать стандартные Puppeteer методы:

  • page.() < /code > и < code > page.$()</code> — поиск элементов</li> <li><code>page.click()</code> — эмуляция клика</li> <li><code>page.type()</code> — ввод текста</li> <li><code>page.waitForSelector()</code> — ожидание появления элемента</li> </ul> <p>Пример заполнения формы внутри popup:</p> <pre class="javascript"><code>await popupPage.waitForSelector(&#39;#username&#39;); await popupPage.type(&#39;#username&#39;, &#39;testuser&#39;); await popupPage.type(&#39;#password&#39;, &#39;securepassword&#39;); await popupPage.click(&#39;#loginButton&#39;); await popupPage.waitForSelector(&#39;#successMessage&#39;, { visible: true });</code></pre> <p><strong>Совет:</strong> Popup закрывается автоматически при потере фокуса, поэтому все действия нужно выполнять сразу после его открытия.</p> <h4 id="ожидание-появления-popup">Ожидание появления popup</h4> <p>Для корректной работы с popup важно дождаться его появления. Один из подходов — постоянная проверка новых targets:</p> <pre class="javascript"><code>function waitForExtensionPopup(browser, url, timeout = 5000) { return new Promise((resolve, reject) =&gt; { const timer = setTimeout(() =&gt; reject(new Error(&#39;Popup не появился&#39;)), timeout); browser.on(&#39;targetcreated&#39;, target =&gt; { if (target.url() === url) { clearTimeout(timer); resolve(target.page()); } }); }); }</code></pre> <p>Такой метод обеспечивает надежное получение страницы popup, даже если открытие происходит с задержкой.</p> <h4 id="взаимодействие-с-background-скриптами">Взаимодействие с background скриптами</h4> <p>Для тестирования расширений важно не только работать с popup, но и проверять его связь с background-скриптами. Puppeteer позволяет подключаться к background-странице расширения через <code>targets()</code>:</p> <pre class="javascript"><code>const backgroundTarget = browser.targets().find(target =&gt; target.type() === &#39;background_page&#39; &amp;&amp; target.url().startsWith(`chrome-extension://${extensionId}/) ); const backgroundPage = await backgroundTarget.page();</code></pre> <p>Это открывает возможность:</p> <ul> <li>Отправлять сообщения через <code>chrome.runtime.sendMessage</code></li> <li>Проверять состояние background-скрипта</li> <li>Имитация событий, которые запускают popup</li> </ul> <h4 id="особенности-отладки">Особенности отладки</h4> <ul> <li><strong>Скрипты popup изолированы:</strong> Они выполняются в своем контексте, поэтому <code>window</code> и <code>document</code> не связаны с основным сайтом.</li> <li><strong>Headless режим недоступен:</strong> Все взаимодействия требуют GUI браузера.</li> <li><strong>Закрытие popup:</strong> Любое перемещение фокуса может закрыть popup, поэтому операции должны выполняться последовательно и без переключения вкладок.</li> </ul> <h4 id="рекомендации-по-стабильности-тестов">Рекомендации по стабильности тестов</h4> <ol type="1"> <li>Всегда использовать <code>await page.waitForSelector()</code> перед взаимодействием с элементами.</li> <li>Фиксировать ID элементов в расширении для надежного поиска (<code>#id</code> вместо <code>.class</code>).</li> <li>Минимизировать переключение между страницами и popup, чтобы избежать непреднамеренного закрытия окна.</li> <li>Логировать URL и target при тестировании, чтобы корректно идентифицировать popup и background страницы.</li> </ol> <h4 id="пример-комплексного-сценария">Пример комплексного сценария</h4> <pre class="javascript"><code>const extensionId = &#39;abcdefghijklmnop&#39;; const extensionPopupUrl =chrome-extension://${extensionId}/popup.html`;

    const browser = await puppeteer.launch({ headless: false, args: [ --disable-extensions-except=/path/to/extension, --load-extension=/path/to/extension ] });

    const page = await browser.newPage(); await page.goto('https://example.com');

    // Ожидание открытия popup через клик по иконке const popupPage = await waitForExtensionPopup(browser, extensionPopupUrl); await popupPage.waitForSelector('#loginButton'); await popupPage.click('#loginButton');

    // Проверка состояния background const backgroundTarget = browser.targets().find(t => t.type() === 'background_page' && t.url().startsWith(chrome-extension://${extensionId}/) ); const backgroundPage = await backgroundTarget.page(); const status = await backgroundPage.evaluate(() => window.isLoggedIn); console.log('Статус входа:', status);

    Этот подход демонстрирует полный цикл работы: запуск браузера с расширением, открытие popup, взаимодействие с его DOM и проверку состояния background скрипта.

    Работа с popup расширений требует внимания к деталям и строгого соблюдения последовательности действий, но использование Puppeteer позволяет автоматизировать сложные сценарии и интеграционные тесты для расширений Chrome.