Избавляемся от Thread.sleep() - разные ситуации

Решил создать тему для описания разных ситуаций где и как можно избавится от 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 кнопки.

1 лайк
  1. Я нажимаю на кнопку “New” которая находится в фрейме "“contentIFrame2”
  2. осуществляется загрузка новой страницы
  3. я начинаю все с нуля- возвращяю в исходное положение driver.switchTo().defaultContent(); (выхожу из всех фреймов) и выбираю нужный фрейм - “contentIFrame1”, затем нужный элемент “itemtype”.

У меня есть подозрение что без хардкодной задержки драйвер выполняет иногда шаг 3 раньше шага 2. т.к. страница не успевает поменятся а фрейм “contentIFrame1” есть и на стар ой и на новой странице и он ищет элемент все еще в старой странице? я не могу ето никак отследить

Ну исходя из описания прототипа defaultContent, наверное не зря разработчики задали WebDriver в качестве возвращаемого типа. Значит все же скрытый сакральный смысл в этом есть:

Returns:
This driver focused on the top window/first frame.

Из приведенного вами кода следует, что новое состояние драйвера вы никак не хэндлите. Может все же следует передавать именно обновленный драйвер в WebDriverWait? :wink:

wait = new WebDriverWait(driver.switchTo().defaultContent(), 20);

Как так-то?

В каком плане как? :blush:

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 и гифка, буду ждать пока она пропадёт :thumbsup: