Frame API

Фреймы (frames) представляют собой отдельные контексты внутри веб-страницы, которые позволяют загружать независимые документы HTML. В тестировании часто приходится взаимодействовать с содержимым iframe, поэтому понимание Frame API является ключевым для автоматизации сложных веб-приложений.


Получение фреймов на странице

Каждая страница в Playwright может содержать один или несколько фреймов. Основной (главный) фрейм доступен через метод page.mainFrame(), а дополнительные можно получить из списка всех фреймов:

const mainFrame = page.mainFrame();
const allFrames = page.frames();
console.log(allFrames.length); // количество фреймов на странице
  • mainFrame() возвращает главный фрейм страницы.
  • frames() возвращает массив всех фреймов, включая главный.

Доступ к дочернему фрейму

Дочерние фреймы можно идентифицировать по имени, URL или другим свойствам. Например:

const frame = page.frame({ name: 'iframeName' });
const frameByUrl = page.frame({ url: /example\.com/ });
  • Аргумент name — значение атрибута name у тега <iframe>.
  • Аргумент url может быть строкой, шаблоном или регулярным выражением, соответствующим URL фрейма.

Взаимодействие с элементами внутри фрейма

Фрейм ведет себя почти как страница. Можно использовать те же методы, что и для page:

await frame.click('button#submit');
await frame.fill('input[name="username"]', 'admin');
const text = await frame.textContent('.message');

Ключевые моменты:

  • Методы click, fill, textContent, waitForSelector и другие работают непосредственно внутри фрейма.
  • Селекторы интерпретируются относительно содержимого фрейма.

Ожидание загрузки фрейма

Фреймы могут загружаться асинхронно. Для корректной работы рекомендуется явно ожидать их появление:

const frame = await page.waitForSelector('iframe#myFrame').then(f => f.contentFrame());
await frame.waitForSelector('button#submit');
  • waitForSelector('iframe') позволяет дождаться, пока iframe появится на странице.
  • contentFrame() возвращает объект Frame, с которым можно работать.

Работа с вложенными фреймами

Фреймы могут содержать другие фреймы. Для поиска вложенного фрейма используется метод frame.childFrames():

const parentFrame = page.frame({ name: 'parentFrame' });
const nestedFrame = parentFrame.childFrames()[0];
await nestedFrame.click('#nestedButton');
  • childFrames() возвращает массив прямых дочерних фреймов.
  • Для глубоких деревьев можно рекурсивно обходить childFrames().

Выполнение скриптов внутри фрейма

Для получения данных или выполнения действий можно использовать метод evaluate:

const result = await frame.evaluate(() => {
    return document.querySelector('#data').textContent;
});

Особенности:

  • Код внутри evaluate выполняется в контексте фрейма.
  • Можно передавать аргументы из Node.js:
const result = await frame.evaluate((selector) => {
    return document.querySelector(selector).value;
}, 'input#name');

Навигация фрейма

Фреймы могут изменять свой URL независимо от основной страницы. Методы навигации аналогичны странице:

await frame.goto('https://example.com/form');
await frame.reload();
  • goto — переход к новому URL внутри фрейма.
  • reload — перезагрузка содержимого фрейма.
  • Поддерживаются параметры таймаута и ожидания события load.

Обработка событий фрейма

Фреймы генерируют события, на которые можно подписаться через объект page:

page.on('frameattached', frame => {
    console.log('Фрейм присоединён:', frame.url());
});

page.on('framenavigated', frame => {
    console.log('Фрейм перешёл на новый URL:', frame.url());
});

page.on('framedetached', frame => {
    console.log('Фрейм удалён:', frame.url());
});
  • frameattached — срабатывает при добавлении нового фрейма.
  • framenavigated — при смене URL фрейма.
  • framedetached — при удалении фрейма из DOM.

Отладка и логирование

Для сложных тестов полезно проверять структуру фреймов:

function printFrames(frame, level = 0) {
    console.log(' '.repeat(level * 2) + frame.url());
    frame.childFrames().forEach(child => printFrames(child, level + 1));
}

printFrames(page.mainFrame());
  • Рекурсивно выводит дерево фреймов с их URL.
  • Помогает визуализировать вложенность и отладить селекторы.

Практические рекомендации

  • Всегда проверять наличие фрейма перед взаимодействием.
  • Для динамически подгружаемых фреймов использовать waitForSelector и contentFrame.
  • Предпочтительно использовать frame({ name }) или frame({ url }) вместо индексов массива, так как порядок фреймов может изменяться.
  • Для сложных вложенных структур рекурсивно обходить childFrames() вместо ручного угадывания.

Frame API в Playwright обеспечивает полный контроль над любыми фреймами на странице, позволяя писать стабильные и надежные тесты даже для сложных веб-приложений с множественными iframe.