Решил создать тему для описания разных ситуаций где и как можно избавится от Thread.sleep() используя более умные методы, а где вовсе нельзя
Ситуация 1:
driver.findElement(By.id("new")).click(); //осуществляется переход на новую страницу
Thread.sleep(5000);
driver.switchTo().defaultContent();
WebDriverWait wait = new WebDriverWait(driver, 20);
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("contentIFrame1"));
wait.until(ExpectedConditions.elementToBeClickable(By.id("itemtype"))).click();
Проблема:
После нажатия “New” драйвер уже успевает переключится на “contentIFrame1” (который есть на обоих страницах) хотя страница еще не успела поменятся. Вследствие этого элемент “itemtype” не находится к тому времени как новая страница полностю загрузилась (20сек). Как можно избавится в этом случае от Thread.sleep(5000); ?
Странно, при переходе на другую страницу по идее сам Selenium должен получить статус страницы ready и потом локатить элементы (хотя там немного другой механизм но принцип такой)
Что-то я не совсем понял вашего флоу…
Вы кликаете #new
, что якобы тригерит переход на некий новый фрейм. Затем переключаетесь на дефолт контент и запускаете ExpectedCondition
-переключатель фрейма. Подозреваю, что вот этим:
driver.switchTo().defaultContent();
-> вы могли переключиться обратно на “старый” контекст, в котором собственно и нет #itemtype
кнопки.
- Я нажимаю на кнопку “New” которая находится в фрейме "“contentIFrame2”
- осуществляется загрузка новой страницы
- я начинаю все с нуля- возвращяю в исходное положение driver.switchTo().defaultContent(); (выхожу из всех фреймов) и выбираю нужный фрейм - “contentIFrame1”, затем нужный элемент “itemtype”.
У меня есть подозрение что без хардкодной задержки драйвер выполняет иногда шаг 3 раньше шага 2. т.к. страница не успевает поменятся а фрейм “contentIFrame1” есть и на стар ой и на новой странице и он ищет элемент все еще в старой странице? я не могу ето никак отследить
Ну исходя из описания прототипа defaultContent, наверное не зря разработчики задали WebDriver
в качестве возвращаемого типа. Значит все же скрытый сакральный смысл в этом есть:
Returns:
This driver focused on the top window/first frame.
Из приведенного вами кода следует, что новое состояние драйвера вы никак не хэндлите. Может все же следует передавать именно обновленный драйвер в WebDriverWait
?
wait = new WebDriverWait(driver.switchTo().defaultContent(), 20);
Как так-то?
В каком плане как?
defaultContent()
возвращает драйвер, WebDriverWait
принимает.
В том плане, что в defaultContent()
стоит return this
, а писателя джавадоков - в угол.
Ну так перед тем, как он возвратит себя, он то свитчится в “null
” frame.
public WebDriver defaultContent() {
Map<String, Object> frameId = Maps.newHashMap();
frameId.put("id", null);
execute(DriverCommand.SWITCH_TO_FRAME, frameId);
return RemoteWebDriver.this;
}
Код из RemoteWebDriver, все драйвера его наследуют, и насколько я вижу, никто не переопределяет этот метод.
Ну и что? Вы хотите сказать, что эти куски кода не эквивалентны?
driver.switchToDefaultContent();
wait = new WebDriverWait(driver, 20);
driver = driver.switchToDefaultContent();
wait = new WebDriverWait(driver, 20);
wait = new WebDriverWait(driver.switchToDefaultContent(), 20);
itemtype не находится:
driver.findElement(By.id("new")).click(); //осуществляется переход на новую страницу
driver.switchTo().frame("contentIFrame1");
Thread.sleep(5000);
driver.findElement(By.id("itemtype")).click();
itemtype находится:
driver.findElement(By.id("new")).click(); //осуществляется переход на новую страницу
Thread.sleep(5000);
driver.switchTo().frame("contentIFrame1");
driver.findElement(By.id("itemtype")).click();
Хотя, да, вы правы. Драйвер уже должен обновить свое состояние до непосредственной передачи вейту.
Ок, давайте поразмышляем… Вот так происходит переключение на фрейм:
public WebDriver frame(String frameName) {
String name = frameName.replaceAll("(['\"\\\\#.:;,!?+<>=~*^$|%&@`{}\\-/\\[\\]\\(\\)])", "\\\\$1");
List<WebElement> frameElements = RemoteWebDriver.this.findElements(
By.cssSelector("frame[name='" + name + "'],iframe[name='" + name + "']"));
if (frameElements.size() == 0) {
frameElements = RemoteWebDriver.this.findElements(
By.cssSelector("frame#" + name + ",iframe#" + name));
}
if (frameElements.size() == 0) {
throw new NoSuchFrameException("No frame element found by name or id " + frameName);
}
return frame(frameElements.get(0));
}
Исходя из описанного вами поведения, findElements
находит фреймы старой пейджи. У вас после клика вся пейджа прегружается или кусок? Используются ли еще какие-то доп. вейты?
Можно конечно попробовать считерить: задать кондишен - invisibilityOfElementLocated(By.id("contentIFrame2"))
, тем самым вы подождете, пока ваш изначальный фрейм не исчезнет (его то надеюсь нет на новой странице?). По идее он должен исчезнуть вместе со старым contentIFrame1
. И затем уже пробовать свитчиться при помощи frameToBeAvailableAndSwitchToIt("contentIFrame1")
.
wait.until invisibilityOfElementLocated отличная идея! У меня же тем еще Loading и гифка, буду ждать пока она пропадёт