简体   繁体   English

Java - 使用 Selenium 等待 JavaScript 事件(例如 onchange)完成

[英]Java - Wait for JavaScript event (e.g. onchange) to complete using Selenium

A set of logically related input fields have an onchange event.一组逻辑相关的input字段有一个onchange事件。 After iterating over the fields and modifying their values some get updated correctly and some don't because of the stuff done by onchange event.在迭代字段并修改它们的值之后,有些会正确更新,有些则不会,因为onchange事件所做的事情。

The moment the onchange event triggers on a field, it starts some processing (involving other related fields), stores the value somewhere and clears other related fields if they weren't previously processed by their own onchange event. onchange事件在一个字段上触发的那一刻,它开始一些处理(涉及其他相关字段),将值存储在某处并清除其他相关字段(如果它们之前没有被它们自己的onchange事件处理)。

I could put the thread to sleep for an arbitrary amount of time but that doesn't look good.我可以让线程休眠任意时间,但这看起来不太好。 It would be just guessing how much time is going to take the processing and choosing between wasting time in a generous sleep or having a script that can abort due timeouts.这只是猜测处理需要多少时间,并在大量睡眠中浪费时间或拥有可以因超时而中止的脚本之间进行选择。

Is there a way to know when the JavaScript code (which is called by the onchange event) has finished doing its work?有没有办法知道 JavaScript 代码(由onchange事件调用)何时完成工作?

Original Code原始代码

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);
}

Output输出

With sleep: Field1 : _w_ ... Field2 : _x_ ... Field3 : _y_ ... FieldN : _z_睡眠时: Field1 : _w_ ... Field2 : _x_ ... Field3 : _y_ ... FieldN : _z_

Without sleep: Field1 : _w_ ... Field2 : ___ ... Field3 : _y_ ... FieldN : ___没有睡眠: Field1 : _w_ ... Field2 : ___ ... Field3 : _y_ ... FieldN : ___

Notes:笔记:

I experienced some issues in the way so I just think it's worth mentioning the lessons learned in brief notes:我在此过程中遇到了一些问题,所以我认为值得一提的是在简要说明中学到的经验教训:

WARNING : Do not mix implicit and explicit waits.警告:不要混合隐式和显式等待。

Use WebDriverWait (specialization of FluentWait ) instead of FluentWait , unless you have a very specific requirement.除非您有非常具体的要求,否则请使用WebDriverWaitFluentWait专业化)而不是FluentWait Eg, WebDriverWait ignores NotFoundException ( NoSuchElementException 's superclass) by default.例如, WebDriverWait默认忽略NotFoundExceptionNoSuchElementException的超类)。 See recomendation .推荐

After serious refactoring and some research, I finally made it.经过认真的重构和一些研究,我终于做到了。 The onchange event fires when the value of the input field is changed and the element loses focus.input字段的值改变并且元素失去焦点时触发onchange事件。 Manipulating the fields with WebElement (eg sendKeys() ) is not an option because you are not in control of the background processing, so using JavascriptExecutor is the choice.使用WebElement (例如sendKeys() )操作字段不是一种选择,因为您无法控制后台处理,因此使用JavascriptExecutor是一种选择。 First, I updated the value of the field using JavaScript (which does NOT trigger the event), and after that, I triggered the onchange event, also using JavaScript:首先,我使用 JavaScript 更新了字段的值(不会触发事件),之后,我也使用 JavaScript 触发了onchange事件:

//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));
}

There are some key aspects here.这里有一些关键方面。

  • Do NOT mix implicit and explicit waits (I learnt it the hard way), it will cause unexpected results.不要混合隐式和显式等待(我是通过艰难的方式学会的),它会导致意想不到的结果。
  • Use WebDriverWait (specialization of FluentWait ) instead of its superclass, unless you have a very specific requirement.除非您有非常具体的要求,否则请使用WebDriverWaitFluentWait )而不是其超类。 If you use FluentWait make sure to ignore appropriate exceptions;如果您使用FluentWait确保忽略适当的异常; otherwise you will start getting NoSuchElementException .否则你会开始得到NoSuchElementException
  • The onchange event fires when the value of the input field is changed and the element loses focus.input字段的值改变并且元素失去焦点时触发onchange事件。
  • dispatchEvent() dispatches an Event at the specified EventTarget, ( synchronously ) invoking the affected EventListeners in the appropriate order. dispatchEvent()在指定的 EventTarget 分派一个 Event,(同步)以适当的顺序调用受影响的 EventListeners。 This applies for custom events as well.这也适用于自定义事件 This is a very nice post for events.这是一个非常好的活动帖子

I used the following code to get a better grasp of ExpectedConditions.javaScriptThrowsNoExceptions and ExpectedConditions.jsReturnsValue .我使用以下代码来更好地掌握ExpectedConditions.javaScriptThrowsNoExceptionsExpectedConditions.jsReturnsValue It is a JavaScript function call that just keeps the engine busy for few seconds.这是一个 JavaScript 函数调用,它只会让引擎忙碌几秒钟。 That way you can see the interaction of the explicit wait with JavaScript and inspect the return values.这样你就可以看到显式等待与 JavaScript 的交互并检查返回值。 Notice that the JS code is slightly different for each ExpectedCondition :请注意,每个ExpectedCondition的 JS 代码略有不同:

//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);

You might try this method which waits until JQuery is set to inactive:您可以尝试这种方法,它会一直等到 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) {
            }
        }
    }

}

To my best of knowledge, you can't wait for an asynchronous event using Selenium Web Driver.据我所知,您不能等待使用 Selenium Web Driver 的异步事件。 You may, however, be able to wait for effects of that event using WebDriverWait class.但是,您可以使用 WebDriverWait 类等待该事件的影响。 If events You mentioned, make some changes in the DOM, you can detect those changes by using selected ExpectedConditions.如果您提到的事件,在 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")));

This simple example would wait until a button is active.这个简单的例子会一直等到按钮处于活动状态。 If the expected conditions are not met in the next 15s exception will be thrown.如果在接下来的 15 秒内未满足预期条件,则会抛出异常。

In case, that provided set of ExpectedConditions is insufficient you can always create your own by implementing ExpectedCondition interface.如果提供的一组 ExpectedConditions 不够用,您可以通过实现 ExpectedCondition 接口来创建自己的。 For more information visit the documentation.有关更多信息,请访问文档。

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 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

The following routine checks the document ready state for "complete" and returns when the page is finished loading/changing.以下例程检查文档就绪状态是否为“完成”,并在页面加载/更改完成后返回。

/**
 * 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");
        }
    });
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用JavaScript在onchange事件中等待诺言完成? - How to wait for promises to complete within the onchange event using JavaScript? 使用javax.script或Rhino在带有浏览器上下文(例如envjs)的Java中运行javascript? - Using javax.script or Rhino to run javascript in Java with browser context (e.g. envjs)? 使用 JavaScript 以编程方式调度键盘(例如按键)事件 - Programmatically dispatch keyboard (e.g. keypress) events using JavaScript 将javascript代码(例如函数,而不是完整文件)发送到浏览器的最佳实践? - Best practice to send javascript code (e.g. a function, not a complete file) to the browser? Selenium会等待JavaScript完成吗? - Does Selenium wait for JavaScript to complete? 用javascript控制视频(例如streamcloud) - Control Videos with javascript (e.g. streamcloud) 使用带有副作用的 React 自定义 Hook 进行事件处理(例如 OnClick) - Using A React Custom Hook With Side Effects For Event Handling (e.g. OnClick) 如何使用例如event.target和element.parentNode配置ESLint? - How to configure ESLint for using e.g. event.target and element.parentNode? 通过 Dropzone 以编程方式上传/添加文件,例如 Selenium - Programmatically upload / add file via Dropzone e.g. by Selenium 在JavaScript中使用正则表达式检查模式重用(例如,在字符串中声明两次的相同字符序列) - Using regular expressions in JavaScript for checking pattern reuse (e.g. the same sequence of characters declared twice in string)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM