【问题标题】:WebDriver executeAsyncScript vs executeScriptWebDriver executeAsyncScript 与 executeScript
【发布时间】:2012-11-07 07:36:40
【问题描述】:

executeAsyncScript 和 executeScript 有什么区别?如何使用诸如 window.onload 之类的事件?我试过这样的事情

((JavascriptExecutor) driver).executeAsyncScript("window.onload = function() {alert('Hello')}"); 

但当然它没有工作...... 所以如果有人知道它是如何工作的,请写一个例子

【问题讨论】:

  • 它们之间的主要区别在于,使用异步执行的脚本必须通过调用提供的回调显式表示它们已完成。这个回调总是作为最后一个参数注入到执行的函数中。
  • 感谢您的反馈!
  • @sphair 你应该已经提交了一个答案,它简洁正确,筛选下面的一些垃圾很难。
  • 查看seleniumhq.github.io/selenium/docs/api/java CTRL+F 并查找“JavaScriptExecutor”并点击它。

标签: javascript testing selenium webdriver qa


【解决方案1】:

(保持它简单,并且正确。)

execteScriptexecuteAsyncScript 之间的相关区别是这样的:

使用executeAsyncScript 调用的函数将“完成回调”作为最后一个参数,必须调用该参数以表明脚本已完成执行。

这允许它与仅在使用回调时“完成”的代码一起使用 - 例如。 setTimeout 或异步 XHR。如果在超时限制内未调用“完成回调”,则返回的承诺将被拒绝。

根据webdriver.WebDriver.executeAsyncScript 文档:

与使用#executeScript 执行同步JavaScript 不同,使用[#executeAsyncScript] 执行的脚本必须通过调用提供的回调来明确表示它们已完成。此回调将始终作为最后一个参数注入到执行的函数中..

也就是说,这两个函数都会阻塞 WebDriver 控制流,直到它们完成 - 要么在 executeScript 的代码末尾运行,要么在调用 'done 回调时' 带有executeAsyncScript: 名称中的“async”表示使用的信号机制,并不意味着/暗示 JavaScript 代码实际上是相对于 WebDriver 异步执行的。

【讨论】:

    【解决方案2】:

    我使用executeScript。提供的示例:

    String cssSelector="...blablabla...";
    JavascriptExecutor js = (JavascriptExecutor) driver;
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("document.getElementById(\'"+cssSelector +"\').click();");
    js.executeScript(stringBuilder.toString());
    

    关于警报的详细信息,存在已知问题。您可以获取详细信息here

    按照文档不同的是:

    执行脚本

    public java.lang.Object executeScript(java.lang.String script,
                                 java.lang.Object... args)
    

    从接口复制的描述:JavascriptExecutor 在当前选定的框架或窗口的上下文中执行 JavaScript。提供的脚本片段将作为主体执行 一个匿名函数。在脚本中,使用 document 来引用 当前文件。请注意,局部变量一次将不可用 脚本已完成执行,但全局变量将 坚持。如果脚本有返回值(即如果脚本包含 return 语句),然后将采取以下步骤:

    • 对于 HTML 元素,此方法返回 WebElement
    • 对于小数,返回 Double
    • 对于非十进制数,返回 Long
    • 对于布尔值,返回一个布尔值
    • 对于所有其他情况,返回一个字符串。
    • 对于数组,返回一个列表,其中每个对象都遵循上述规则。我们支持嵌套列表。
    • 除非值为null或者没有返回值,否则返回null

    参数必须是数字、布尔值、字符串、WebElement 或列表 以上任意组合。如果 论据不符合这些标准。将提出论据 通过“arguments”魔术变量可用于 JavaScript,就好像 该函数是通过“Function.apply”调用的

    指定者:接口JavascriptExecutor中的executeScript 参数: script - 要执行的 JavaScript args - 要执行的参数 剧本。可能为空 返回:Boolean、Long、String、List 之一 或网页元素。或为空。

    执行AsyncScript

    public java.lang.Object executeAsyncScript(java.lang.String script,
                                      java.lang.Object... args)
    

    从接口复制的描述:JavascriptExecutor 在当前选定的框架或窗口的上下文中执行一段异步的 JavaScript。与执行同步不同 JavaScript,使用此方法执行的脚本必须显式发出信号 它们通过调用提供的回调来完成。这个回调是 总是作为最后一个参数注入到执行的函数中。 传递给回调函数的第一个参数将用作 脚本的结果。该值的处理方式与 同步案例。

    示例 #1:在被测浏览器中执行休眠。

     long start = System.currentTimeMillis();
       ((JavascriptExecutor) driver).executeAsyncScript(
           "window.setTimeout(arguments[arguments.length - 1], 500);");
       System.out.println(
           "Elapsed time: " + (System.currentTimeMillis() - start));  
    

    示例 #2:将测试与 AJAX 应用程序同步:

     WebElement composeButton = driver.findElement(By.id("compose-button"));
       composeButton.click();
       ((JavascriptExecutor) driver).executeAsyncScript(
           "var callback = arguments[arguments.length - 1];" +
           "mailClient.getComposeWindowWidget().onload(callback);");
       driver.switchTo().frame("composeWidget");
       driver.findElement(By.id("to")).sendKeys("bog@example.com");
    

    示例 #3:注入 XMLHttpRequest 并等待结果:

     Object response = ((JavascriptExecutor) driver).executeAsyncScript(
           "var callback = arguments[arguments.length - 1];" +
           "var xhr = new XMLHttpRequest();" +
           "xhr.open('GET', '/resource/data.json', true);" +
           "xhr.onreadystatechange = function() {" +
           "  if (xhr.readyState == 4) {" +
           "    callback(xhr.responseText);" +
           "  }" +
           "}" +
           "xhr.send();");
       JSONObject json = new JSONObject((String) response);
       assertEquals("cheese", json.getString("food"));
    

    脚本参数必须是数字、布尔值、字符串、WebElement、 或以上任意组合的列表。会抛出异常 如果论点不符合这些标准。论据将是 通过“arguments”变量提供给 JavaScript。

    指定者:接口JavascriptExecutor中的executeAsyncScript 参数: script - 要执行的 JavaScript。 args - 参数 到脚本。可能是空的。返回: Boolean、Long、String 之一, List、WebElement 或 null。

    详细文档是here

    【讨论】:

    • 上面的“描述”(从界面文档复制)和链接到的文档已经过时了。 “对象”列表将作为 List> 返回。其中,每个键都是对象的一个​​属性。我不知道如何获得这方面的任何细节,但我读完这篇文章后就被烧死了,所以我想我会做出贡献的。
    • @eugene.polschikov 我指的是您的示例 3,当我在控制台中执行该行时,我得到“未捕获的 ReferenceError:参数未在 :1:16 处定义”。我对 JS 完全陌生,非常感谢您的帮助。
    • 文档链接已损坏。
    【解决方案3】:

    它们之间的主要区别在于,使用 async 执行的脚本必须通过调用提供的回调显式地表示它们已完成。这个回调总是作为最后一个参数注入到执行的函数中。

    【讨论】:

    • 为什么糟糕的文档不这么说?文档只说:“在当前选定的框架或窗口的上下文中异步执行 JavaScript。”
    【解决方案4】:
    ((JavascriptExecutor) driver).executeScript("alert('Hello');"); 
    

    将显示警报:

    ((JavascriptExecutor) driver).executeAsyncScript() 在 JS 需要时间执行时使用,例如在 Web 服务调用中。

    window.onload 确保页面加载完成时执行 JS。

    【讨论】:

      【解决方案5】:

      我花了很多时间来理解这个功能,最后我得到了它。以下代码将有很大帮助:

      /**
       * executeAsyncScript document mentioned callback is a browser intrinsic function for returning deferred value (e.g 123 in example) from
       * js environment to Java environment
       * 
       */
      @Test
      public void testAsyncScript() {
          webDriver.manage().timeouts().setScriptTimeout(1, TimeUnit.SECONDS);
          Integer a = 23;
          TestUtil.elapse("first", () -> {
              Object value = getJsExecutor().executeAsyncScript("window.setTimeout(arguments[arguments.length - 1](123), 500);", a);
              // following code should be executed after 500ms timeout
              System.out.println("a is " + a); // a has nothing to do with the documented "callback"
              assertEquals(123, value);
          });
      
      }
      

      【讨论】:

        【解决方案6】:

        这个简单的异步脚本监听 DOM 上点击的元素并通过回调解决 executeAsyncScript

          var clicked_element = await driver.executeAsyncScript('var callback=arguments[arguments.length - 1]; window.document.addEventListener("click", function(e){ var element = event.srcElement ||event.target; callback(element); });'); 
        

        【讨论】:

          【解决方案7】:

          你可以这样做:

          JavascriptExecutor js=(JavascriptExecutor)driver;
          String javascript="window.location='https://google.com';window.onload=alert('hello'); var callback=arguments[arguments.length-1]; callback();";
                  js.executeAsyncScript(javascript);
              
          

          正如其他人所说,executeAsyncScript() 使用称为回调的信号机制来向主函数/代码发出信号,表明 executeAsyncScript 中的 javascript 已完成。回调被称为参数[arguments.length-1]。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-04-22
            • 2020-01-21
            • 1970-01-01
            • 1970-01-01
            • 2016-04-29
            • 2014-09-24
            • 1970-01-01
            • 2018-06-03
            相关资源
            最近更新 更多