В современных веб-приложениях часто встречаются страницы, содержащие iframe или даже несколько уровней вложенных фреймов. Для корректного тестирования взаимодействия с элементами внутри таких фреймов необходимо понимать, как Protractor работает с контекстом окна и как переключаться между фреймами.
В Protractor для взаимодействия с элементами внутри iframe
используется метод
browser.switchTo().frame(). Этот метод
позволяет переключить контекст выполнения с основного документа на
указанный фрейм.
Синтаксис:
browser.switchTo().frame(iframeWebElement);
iframeWebElement — объект типа WebElement,
указывающий на нужный фрейм.element() или
by будут относиться к содержимому этого фрейма.Чтобы вернуться в основной документ, используется:
browser.switchTo().defaultContent();
Iframe можно найти стандартными селекторами Protractor. Например:
let myFrame = element(by.css('iframe#myFrame'));
Для вложенных фреймов необходимо последовательно переключаться на каждый уровень:
let outerFrame = element(by.id('outerFrame'));
let innerFrame = element(by.id('innerFrame'));
browser.switchTo().frame(outerFrame.getWebElement());
browser.switchTo().frame(innerFrame.getWebElement());
После выполнения теста важно возвращаться в основной контекст:
browser.switchTo().defaultContent();
browser.switchTo().frame() принимает только
WebElement или индекс, нельзя напрямую передавать объект
element() без вызова .getWebElement().async/await или подход с
then().Пример с async/await:
it('должен заполнить форму внутри вложенного фрейма', async () => {
let outerFrame = element(by.id('outerFrame'));
let innerFrame = element(by.id('innerFrame'));
await browser.switchTo().frame(await outerFrame.getWebElement());
await browser.switchTo().frame(await innerFrame.getWebElement());
let input = element(by.name('username'));
await input.sendKeys('testuser');
await browser.switchTo().defaultContent();
});
Помимо передачи WebElement, метод
frame() поддерживает:
browser.switchTo().frame(0) — первый фрейм на
странице.browser.switchTo().frame('frameName').При наличии нескольких вложенных фреймов использование индексов требует аккуратного подсчета:
browser.switchTo().frame(0); // Внешний фрейм
browser.switchTo().frame(1); // Внутренний фрейм внутри первого
После вызова browser.switchTo().defaultContent()
контекст возвращается на основной документ. Любые обращения к элементам
внутри фреймов требуют нового переключения.
Для безопасного кода рекомендуется использовать обертки:
async function withFrame(frameElement, callback) {
await browser.switchTo().frame(await frameElement.getWebElement());
await callback();
await browser.switchTo().defaultContent();
}
let frame = element(by.id('innerFrame'));
await withFrame(frame, async () => {
await element(by.name('password')).sendKeys('secret');
});
element.all(by.tagName('iframe')).count() позволяет
убедиться, что нужный фрейм существует.browser.driver.getTitle() или
browser.getCurrentUrl() внутри фрейма для проверки
правильного переключения.NoSuchElementError: чаще всего
появляются из-за того, что контекст не был переключен на нужный
фрейм.Protractor изначально ориентирован на Angular, но содержимое iframe
может быть как Angular, так и обычным HTML. Для Angular внутри iframe
можно продолжать использовать стандартные селекторы
by.binding или by.model. Если iframe содержит
не-Angular страницу, нужно отключить ожидание Angular:
await browser.waitForAngularEnabled(false);
После работы с не-Angular фреймом необходимо снова включить ожидание:
await browser.waitForAngularEnabled(true);
element()..getWebElement().defaultContent().Использование этих методов позволяет стабильно взаимодействовать с многоуровневыми фреймами в Protractor, обеспечивая надежное выполнение тестов независимо от сложности структуры документа.