Для переключения между окнами браузера в Selenium WebDriver используется метод
switchTo
, также как и при работе с алертами или фреймами. Только для окон нет родительского окна, в которое можно было бы переключиться по умолчанию. Для переключения всегда используется дескриптор окна, поэтому для того, чтобы переключиться в первоначально открытое окно, нужно сначала запомнить его дескриптор.
driver.switchTo().window(windowHandler)
— переключает фокус для будущих команд драйвера в окно с заданным дескриптором.
Для получения дескриптора окна используется метод getWindowHandle()
. Он возвращает дескриптор текущего открытого окна, который уникально идентифицирует окно для этого экземпляра драйвера.
String windowHandler = driver.getWindowHandle();
Метод getWindowHandles()
возвращает набор дескрипторов окон, которые можно использовать для перебора всех открытых окон для данного экземпляра webdriver
.
Set<String> windowHandlers = driver.getWindowHandles();
Поэтому если вы не знаете имя нового открытого окна, его можно получить следующим способом:
// получаем набор дескрипторов текущих открытых окон Set<String> oldWindowsSet = driver.getWindowHandles(); // нажимаем на ссылку, которая открывает документ в новом окне driver.findElement(By.tagName("a")).click(); // здесь нужно будет дождаться открытия нового окна // получаем новый набор дескрипторов, включающий уже и новое окно Set<String> newWindowsSet = driver.getWindowHandles(); // получаем дескриптор нового окна newWindowsSet.removeAll(oldWindowsSet); String newWindowHandle = newWindowsSet.iterator().next();
Иногда открытие новых окон происходит не сразу. Для того, чтобы дождаться открытия окна достаточно перенести описанную выше логику в WebDriverWait:
// получаем набор дескрипторов текущих открытых окон Set<String> oldWindowsSet = driver.getWindowHandles(); // нажимаем на ссылку, которая открывает документ в новом окне driver.findElement(By.tagName("a")).click(); // ожидаем открытия и получаем дескриптор нового окна String newWindowHandle = (new WebDriverWait(driver, 10)) .until(new ExpectedCondition<String>() { public String apply(WebDriver driver) { Set<String> newWindowsSet = driver.getWindowHandles(); newWindowsSet.removeAll(oldWindowsSet); return newWindowsSet.size() > 0 ? newWindowsSet.iterator().next() : null; } } );
Пример:
import java.util.Set; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.WebDriverWait; public class SwitchToWindowExample { public WebDriver driver; @Before public void setUp() throws Exception { driver = new FirefoxDriver(); } @Test public void someSimpleTest() { driver.get("http://internetka.in.ua"); String originalWindow = driver.getWindowHandle(); final Set<String> oldWindowsSet = driver.getWindowHandles(); driver.findElement(By.tagName("a")).click(); String newWindow = (new WebDriverWait(driver, 10)) .until(new ExpectedCondition<String>() { public String apply(WebDriver driver) { Set<String> newWindowsSet = driver.getWindowHandles(); newWindowsSet.removeAll(oldWindowsSet); return newWindowsSet.size() > 0 ? newWindowsSet.iterator().next() : null; } } ); driver.switchTo().window(newWindow); System.out.println("New window title: " + driver.getTitle()); driver.close(); driver.switchTo().window(originalWindow); System.out.println("Old window title: " + driver.getTitle()); } @After public void tearDown() throws Exception { driver.quit(); } }
Про корректное закрытие окон можно почитать в этой статье.
Видел такой же пример только где два сета соединяются методом removeAll при котором удаляются все совпадающие элементы и остаются только те, которые разные, в итоге имеем только новое окно в сете.
В любом случае спасибо!
Кстати, способ дождаться какого-либо элемента я бы вывел в отдельную статью.
Во-первых это не очевидный способ для новичков, во-вторых достаточно большая конструкция получается чтобы просто запомнить.
+ в отдельной статье было бы полезно расписать как этот метод (а так же другие, которые часто используются) вывести в родительский класс чтобы было легко им пользоваться.
Например если вся эта ожидающая конструкция будет возвращать WebElement b вызываться одной строчкой кода из род. класса будет очень удобно обнаруживать элемент, заодно его дождавшись (например разные выпадающие медленные списки и т.д.)
достаточно писать waitFor(By.id(«some-id»)).click(); и все. Одной строкой мы дождались элемент с айдишником и кликнули на него. А еще можно в созданный нами waitFor метод добавить if который будет в случае null ( если не дождалось или не нашло) делать fail() для теста вместо обычной java ошибки, да еще и текст ошибки выводить.. короче накрутить в этот хелпер можно много!
Что касается получения нового окна и использования removeAll, то сейчас и остается только новое окно с помощью удаления совпадающих элементов в сетах, как Вы и написали. Если разбор кода вызывает затруднения, пишите, постараюсь прокомментировать.
Статья про ожидания уже существует — здесь.
Создание хелперов — личное дело каждого и достаточно объемная тема. Да и потом, не всегда нужно их создавать, можно, например, использовать Thucydides.
Добрый день. Очень обрадовался тому, что нашел вашу статью, но пока не могу понять причину ошибки компиляции.
Компилятор ругается что не находит реализацию функции apply(f), хотя я просто скопировал код из статьи.
String newWindow = (new WebDriverWait(driver, 10))
.until(new ExpectedCondition() {
public String apply(WebDriver driver) {
Set newWindowsSet = driver.getWindowHandles();
newWindowsSet.removeAll(oldWindowsSet);
return newWindowsSet.size() > 0 ?
newWindowsSet.iterator().next() : null;
}
}
);
В режиме редактирования кода подчеркнут конструктор ExpectedCondition в строке
.until(new ExpectedCondition() {
Сам я в Java не настолько силен чтобы понять в чем дело. Не могли бы вы уточнить в чем может быть причина?
При компиляции ошибка:
is not abstract and does not override abstract method apply(java.lang.Object) in com.google.common.base.Function
incompatible types
found : java.lang.Object
required: java.lang.String
Добрый день, действительно присутствует ошибка — редактор кода съел <String> при форматировании.
Должно быть так:
Исправила в примерах. Большое спасибо, что обратили внимание.