如果 Angular 正在做......从硒中检测

Posted

技术标签:

【中文标题】如果 Angular 正在做......从硒中检测【英文标题】:Detect from selenium if angular is doing ... stuff 【发布时间】:2015-11-27 12:23:03 【问题描述】:

为什么driver.findElement(<some static element ng-if=simplebool>static text</some>).getText() 总是返回“”?

我有一个 Angular 应用程序,我正在通过 chromedriver 通过 osx 上的 java 使用 selenium 进行测试。

我有一些看起来像这样的标记:

<h1 id="my-unique-id" ng-if="model.shouldDisplayThisAttribute">static text</h1>

我经常得到:

assert(driver.findElement(By.id("my-unique-id").getText().contains("static text");

屈服:

java.lang.AssertionError: Not true that <""> contains <"static text">

比如,30% 的时间。

我不明白该元素的 .getText() 如何评估为“”,所以我假设 angular 是 $digesting 或 $compiling 页面。没关系。没关系。没关系。

我想知道 Angular 什么时候完成了 $compile 和 $digesting 和 $watching,所以我可以查看页面看看是什么。

如果我添加:

(function() 
   function angularAppearsToBeIdle(callback) 
     var untilNextDigest,
        isIdle = angular.element(document.body).scope().$watch(function () 
           clearTimeout(untilNextDigest);
           untilNextDigest = setTimeout(function () 
           isIdle();
           callback('done');
         , 100);
      );
    
 angularAppearsToBeIdle(console.log.bind(console));
());

在我的页面上,我在预期的时间看到了 console.log 消息。

如果我将其粘贴到控制台中:

(function() 
   function angularAppearsToBeIdle(callback) 
       var untilNextDigest,
       isIdle = angular.element(document.body).scope().$watch(function () 
           clearTimeout(untilNextDigest);
           untilNextDigest = setTimeout(function () 
               isIdle();
               callback('done');
           , 100);
       );
   
   angularAppearsToBeIdle(console.log.bind(console));
());

我得到“未定义”。

最终,我想做的是来自 Java:

@Test
public void clickOnSomethingThatIsProbablyNotGoingToChange(driver.findElement(By.id("some-id"));

private WebElement idleElement() 

    new WebDriverWait(driver, 30).until(new Predicate<WebDriver>() 
        @Override
        public boolean apply(WebDriver input) 
            Object result = ((javascriptExecutor) driver).executeScript("return window.angularAppearsToBeIdle;");
            return result.equals("idle");
        
   

我尝试了以下方法,但没有 Selenium 执行此类操作的示例:

public void waitForAngular() 

    ((JavascriptExecutor) driver).executeScript(
            " window.angularAppearsToBeIdle = 'not idle'; ");

    ((JavascriptExecutor) driver).executeScript(
            " window.signalThatAngularAppearsToBeIdle = function(signal)  " +
            " var untilNextDigest, " +
            " isIdle = angular.element(document.body).scope().$watch(function ()  " +
            "    clearTimeout(untilNextDigest); " +
            "    untilNextDigest = setTimeout(function ()  " +
            "       isIdle(); " +
            "       signal = 'idle'; " +
            "    , 100); " +
            "  ); " +
            "  ");

    driver.manage().timeouts().setScriptTimeout(10, TimeUnit.SECONDS);

    ((JavascriptExecutor) driver).executeAsyncScript(
            " var callback = arguments[arguments.length - 1]; " +
                    " signalThatAngularAppearsToBeIdle(callback) "
            ,
            " window.angularAppearsToBeIdle ");

    new WebDriverWait(driver, 30).until(new Predicate<WebDriver>() 
        @Override
        public boolean apply(WebDriver input) 
            Object result = ((JavascriptExecutor) driver).executeScript("return window.angularAppearsToBeIdle;");
            return result.equals("idle");

        
    );

如果 Angular 忙于做事,我如何从 Selenium 中检测到?

【问题讨论】:

首先请更正您的以下代码assert(driver.findElement(By.id("my-unique-id").getText().contains("expected"); 即使没有括号也正确匹配。否则会误导其他人。 我严重怀疑任何人都不会理解我的问题。如果人们不明白,我会更新我的问题...... 请更新问题标题,使其更能描述实际问题。 Protractor.js 这样做github.com/angular/protractor/blob/…,Ardesco 的回答是对 Selenium 的一个很好的翻译。 【参考方案1】:

您将需要编写一个自定义的 ExpectedCondition 来查询 Angular 以查看它是否已完成。这是我之前准备的:

    public static ExpectedCondition angularHasFinishedProcessing() 
        return new ExpectedCondition<Boolean>() 
            @Override
            public Boolean apply(WebDriver driver) 
                String hasAngularFinishedScript = "var callback = arguments[arguments.length - 1];\n" +
                        "var el = document.querySelector('html');\n" +
                        "if (!window.angular) \n" +
                        "    callback('false')\n" +
                        "\n" +
                        "if (angular.getTestability) \n" +
                        "    angular.getTestability(el).whenStable(function()callback('true'));\n" +
                        " else \n" +
                        "    if (!angular.element(el).injector()) \n" +
                        "        callback('false')\n" +
                        "    \n" +
                        "    var browser = angular.element(el).injector().get('$browser');\n" +
                        "    browser.notifyWhenNoOutstandingRequests(function()callback('true'));\n" +
                        "";

                JavascriptExecutor javascriptExecutor = (JavascriptExecutor) driver;
                String isProcessingFinished = javascriptExecutor.executeAsyncScript(hasAngularFinishedScript).toString();

                return Boolean.valueOf(isProcessingFinished);
            
        ;
    

这是假设您在 html 元素上定义了 ng-app。如果你在 DOM 的其他地方定义它,你需要更新上面的代码。这几乎是从量角器偷来的,它正在检查是否有任何未完成的 ajax 请求并检查 Angular 是否认为它处于可测试状态。

重要的部分是将这个 JavaScript sn-p 作为异步脚本执行,因为 Angular 使用 Promise 在准备好时触发回调。如果您不将其作为异步脚本运行,它将无法工作。

您还需要在驱动程序对象上设置脚本超时,这是 selenium 在抛出超时异常之前等待回调完成的时间量。可以这样设置:

driver.manage().timeouts().setScriptTimeout(15, TimeUnit.SECONDS);

然后就像将这个 ExpectedCondition 插入到一个等待中一样简单:

WebDriverWait wait = new WebDriverWait(driver, 15, 100);
wait.until(angularHasFinishedProcessing());

希望这会有所帮助。

【讨论】:

【参考方案2】:

我正在用 C#.Net 编写,所以我不得不翻译 Ardesco 的答案:

static class HelperFunctions


    public static bool AngularHasFinishedProcessing(IWebDriver driver)
    
        string HasAngularFinishedScript = 
            @"var callback = arguments[arguments.length - 1];
            var el = document.querySelector('html');
            if (!window.angular) 
                callback('False')
            
            if (angular.getTestability) 
                angular.getTestability(el).whenStable(function()callback('True'));
             else 
                if (!angular.element(el).injector()) 
                    callback('False')
                
                var browser = angular.element(el).injector().get('$browser');
                browser.notifyWhenNoOutstandingRequests(function()callback('True'));
            ";
        IJavaScriptExecutor javascriptExecutor = (IJavaScriptExecutor)driver;
        return Convert.ToBoolean(javascriptExecutor.ExecuteAsyncScript(HasAngularFinishedScript));
    

为了称呼它,我用:

WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until((d) =>  return HelperFunctions.AngularHasFinishedProcessing(driver); );

【讨论】:

【参考方案3】:

混合使用 Python 2.7:

class angular_is_ready(object):
    script = """var callback = arguments[arguments.length - 1];
    var el = document.querySelector('html');
    if (!window.angular) 
        callback(false)
    
    if (angular.getTestability) 
        angular.getTestability(el).whenStable(function()callback(true));
     else 
        if (!angular.element(el).injector()) 
            callback(false)
        
        var browser = angular.element(el).injector().get('$browser');
        browser.notifyWhenNoOutstandingRequests(function()callback(true));
    ;"""

    def __call__(self, driver):
        try:
            return driver.execute_async_script(self.script)
        except:
            return False

# Define driver and timeout,
# then use the condition like so:
WebDriverWait(driver, timeout).until(angular_is_ready)

【讨论】:

以上是关于如果 Angular 正在做......从硒中检测的主要内容,如果未能解决你的问题,请参考以下文章

检测声音是不是在硒中播放

如何在geckodriver中永久安装扩展程序

检查元素在硒中是不是可点击

如何正确模拟硒中的拖放

如何从硒的跨度中获取文本

如何滚动到硒中的特定元素?