Веб-приложения часто используют iframe для внедрения
стороннего контента или модульных компонентов. Для автоматизированного
тестирования такие элементы представляют отдельную сложность, так как
содержимое iframe находится в другом контексте
документа. Protractor предоставляет инструменты для
переключения контекста и взаимодействия с элементами внутри iframe.
Каждый iframe создает новый контекст DOM, отделенный
от основного документа. Любые попытки напрямую найти элемент внутри
iframe без предварительного переключения контекста приведут к ошибкам
типа NoSuchElementError.
Protractor использует WebDriver API, а для работы с iframe ключевым методом является:
browser.switchTo().frame(iframeWebElement);
Здесь iframeWebElement — элемент iframe, найденный с
помощью element() или element.all().
После переключения контекста все селекторы будут искать элементы внутри iframe, а не на основном документе.
const myIframe = element(by.css('iframe#my-frame'));
browser.switchTo().frame(myIframe.getWebElement());
Метод getWebElement() нужен, потому что
switchTo().frame() работает только с WebElement, а не с
элементами Protractor напрямую.
browser.switchTo().defaultContent();
Эта команда необходима, если нужно взаимодействовать с элементами за пределами iframe после работы внутри него.
После переключения контекста элементы можно находить и взаимодействовать с ними обычными методами Protractor:
const inputField = element(by.css('input[name="username"]'));
inputField.sendKeys('admin');
const submitButton = element(by.buttonText('Submit'));
submitButton.click();
Важно учитывать, что время ожидания загрузки iframe
может быть дольше обычного документа. Для корректной работы стоит
использовать ExpectedConditions:
const EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf(myIframe), 5000, 'iframe не загрузился');
browser.switchTo().frame(myIframe.getWebElement());
В некоторых приложениях встречаются iframe внутри iframe. В этом случае нужно последовательно переключать контексты:
const outerIframe = element(by.css('iframe#outer'));
const innerIframe = element(by.css('iframe#inner'));
browser.switchTo().frame(outerIframe.getWebElement());
browser.switchTo().frame(innerIframe.getWebElement());
Для возврата на уровень выше можно использовать
parentFrame():
browser.switchTo().parentFrame(); // возвращаемся к outerIframe
browser.switchTo().defaultContent(); // возвращаемся к основному документу
browser.wait с ExpectedConditions.Пример с проверкой видимости элемента:
const EC = protractor.ExpectedConditions;
const loginButton = element(by.css('button#login'));
browser.wait(EC.visibilityOf(loginButton), 5000, 'Кнопка login не видна в iframe');
loginButton.click();
Методы switchTo().frame() и действия над элементами
работают асинхронно, поэтому часто применяются
async/await:
it('Ввод данных в iframe', async () => {
const myIframe = element(by.css('iframe#login-frame'));
await browser.wait(EC.presenceOf(myIframe), 5000);
await browser.switchTo().frame(await myIframe.getWebElement());
const username = element(by.css('input#username'));
await username.sendKeys('admin');
const password = element(by.css('input#password'));
await password.sendKeys('12345');
const loginBtn = element(by.buttonText('Login'));
await loginBtn.click();
await browser.switchTo().defaultContent();
});
Использование async/await повышает стабильность тестов и
делает код более читаемым.
defaultContent() после завершения работы
внутри iframe.ExpectedConditions для проверки доступности
элементов.isPresent() перед switchTo().frame().Эффективное управление iframe позволяет создавать стабильные и надежные E2E-тесты, независимо от сложности DOM и количества вложенных слоев.