Java - дождаться завершения события JavaScript (например, onchange) с помощью Selenium - PullRequest
0 голосов
/ 08 ноября 2018

Набор логически связанных полей input имеет событие onchange. После перебора полей и изменения их значений некоторые обновляются корректно, а некоторые нет из-за того, что сделано onchange event.

В тот момент, когда событие onchange запускается в поле, оно начинает некоторую обработку (включая другие связанные поля), сохраняет где-то значение и очищает другие связанные поля, если они не были ранее обработаны их собственным событием onchange.

Я мог бы положить поток в спящий режим на произвольное количество времени, но это не выглядит хорошо. Было бы просто угадывать, сколько времени уйдет на обработку, и выбирать между тратой времени в щедром сне или наличием сценария, который может прервать истечение времени ожидания.

Есть ли способ узнать, когда код JavaScript (который вызывается событием onchange) завершил свою работу?

Оригинальный код

Wait<WebDriver> wait = new WebDriverWait(driver, 25, 500);
for(int i = 1; i <= fieldCount; i++) {
    elementId = "field$" + i;
    wait.until(ExpectedConditions.elementToBeClickable(By.id(elementId)));
    driver.findElementById(elementId).sendKeys(data);
    //The mess happens if I don't sleep
    Thread.sleep(3000);
}

выход

со сном: Field1: _w_ ... Field2: _x_ ... Field3: _y_ ... FieldN: _z_

Без сна: Field1: _w_ ... Field2: ___ ... Field3: _y_ ... FieldN: ___

Примечания:

Я столкнулся с некоторыми проблемами, поэтому думаю, что стоит упомянуть уроки, извлеченные в кратких заметках:

ПРЕДУПРЕЖДЕНИЕ : не смешивать неявные и явные ожидания.

Используйте WebDriverWait (специализация FluentWait) вместо FluentWait, если только у вас нет особых требований. Например, WebDriverWait игнорирует NotFoundException (суперкласс NoSuchElementException) по умолчанию. См. Рекомендация .

Ответы [ 4 ]

0 голосов
/ 11 ноября 2018

После серьезного рефакторинга и некоторых исследований, я наконец сделал это. Событие onchange возникает, когда значение поля input изменяется и элемент теряет фокус. Управлять полями с помощью WebElement (например, sendKeys()) нельзя, поскольку вы не контролируете фоновую обработку, поэтому использование JavascriptExecutor является выбором. Сначала я обновил значение поля с помощью JavaScript (который НЕ вызывает событие), а после этого я вызвал событие onchange, также используя JavaScript:

//Setting implicit wait to 0 to avoid mess with explicit waits
driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
//Use WebDriverWait instead of FluentWait (it's superclass)
Wait<WebDriver> wait = new WebDriverWait(driver, 25, 500);
for(int i = 1; i <= fieldCount; i++) {
    String elementId = "field$" + i;
    String javaScript = String.format("document.getElementById('%s').value='%s';", elementId , myValue);
    Object jsResult = wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(javaScript));
    javaScript = String.format("return document.getElementById('%s').dispatchEvent(new Event('change'));", elementId);
    jsResult = wait.until(ExpectedConditions.jsReturnsValue(javaScript));
}

Здесь есть несколько ключевых аспектов.

  • НЕ смешивайте неявные и явные ожидания (я научился этому нелегко), это приведет к неожиданным результатам.
  • Используйте WebDriverWait (специализация FluentWait) вместо его суперкласса, если только у вас нет особых требований. Если вы используете FluentWait, обязательно игнорируйте соответствующие исключения; в противном случае вы начнете получать NoSuchElementException.
  • Событие onchange происходит, когда значение поля input изменяется и элемент теряет фокус.
  • dispatchEvent() отправляет Событие по указанной EventTarget, ( синхронно ), вызывая задействованные EventListeners в соответствующем порядке. Это относится и к настраиваемым событиям . Это очень хороший пост для событий.

Я использовал следующий код, чтобы лучше понять ExpectedConditions.javaScriptThrowsNoExceptions и ExpectedConditions.jsReturnsValue. Это вызов функции JavaScript, который просто удерживает движок на несколько секунд. Таким образом, вы можете увидеть взаимодействие явного ожидания с JavaScript и проверить возвращаемые значения. Обратите внимание, что код JS немного отличается для каждого ExpectedCondition:

//ExpectedConditions.jsReturnsValue
String javaScript = "(function watcher(ms){var start=new Date().getTime();var end = start;while(end<start+ms){end=new Date().getTime();};return 'complete';})(5000);return 'success';";
log.trace("javaScript={}", javaScript);
Object jsResult = wait.until(ExpectedConditions.jsReturnsValue(javaScript));
log.trace("jsResult={}", jsResult);

//ExpectedConditions.javaScriptThrowsNoExceptions
javaScript = "(function watcher(ms){var start=new Date().getTime();var end = start;while(end<start+ms){end=new Date().getTime();};return 'complete';})(5000);";
log.trace("javaScript={}", javaScript);
jsResult = wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(javaScript));
log.trace("jsResult={}", jsResult);
0 голосов
/ 08 ноября 2018

Вы можете попробовать этот метод, который ждет, пока JQuery не станет неактивным:

/**
 * Wait until JQuery is inactive
 * @author Bill Hileman
 */
public void waitForJQueryToBeInactive() {

    Boolean isJqueryUsed = (Boolean) ((JavascriptExecutor) driver)
            .executeScript("return (typeof(jQuery) != 'undefined')");

    if (isJqueryUsed) {
        while (true) {
            // JavaScript test to verify jQuery is active or not
            Boolean ajaxIsComplete = (Boolean) (((JavascriptExecutor) driver)
                    .executeScript("return jQuery.active == 0"));
            if (ajaxIsComplete)
                break;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
        }
    }

}
0 голосов
/ 08 ноября 2018

Следующая подпрограмма проверяет состояние готовности документа на «завершено» и возвращает его по завершении загрузки / изменения страницы.

/**
 * Wait for the web page to finish loading
 * @author Bill Hileman
 */
public void waitForPageToLoad() {

    WebDriverWait wait = new WebDriverWait(driver, 10);
    wait.until(new ExpectedCondition<Boolean>() {

        public Boolean apply(WebDriver wdriver) {
            return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
        }
    });
}
0 голосов
/ 08 ноября 2018

Насколько мне известно, вы не можете ждать асинхронного события с помощью Selenium Web Driver.Однако вы можете ожидать воздействия этого события, используя класс WebDriverWait.Если события, о которых Вы упомянули, вносят некоторые изменения в DOM, вы можете обнаружить эти изменения, используя выбранные ExpectedConditions.

   Wait<WebDriver> wait = new WebDriverWait(driver, 15, 500);

   // here you make some change to the input

   wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//input")));

Этот простой пример будет ждать, пока кнопка не станет активной.Если ожидаемые условия не будут выполнены в следующие 15 секунд, будет сгенерировано исключение.

В случае, если предоставленный набор ExpectedConditions недостаточен, вы всегда можете создать свой собственный, реализовав интерфейс ExpectedCondition.Для получения дополнительной информации посетите документацию.

https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/package-summary.html https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/package-summary.html

...