Detection from selenium if angular does ... stuff

Why is driver.findElement(<some static element ng-if=simplebool>static text</some>).getText() ever returned? "?

I have an angular application that I am testing with selenium via chromedriver via java on osx.

I have a markup that looks like this:

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

Very often I get:

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

getting:

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

For example, 30% of the time.

I do not understand how this .getText () element could be evaluated as "", so I assume that angular is either $ digesting or $ compilation of the page. It's great. This is normal. This is normal.

I want to know when angular finished compiling $ and $ digesting and $ watch, so I can look at the page and see what is.

If I add:

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

to my page, I see console.log messages while I am expecting them.

If I paste this into the console:

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

I get 'undefined'.

Ultimately, what I would like to do is 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"); } } 

I tried the following, but there are no examples when Selenium does something like this:

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

How can I detect from Selenium if angular is busy with work?

+5
source share
4 answers

You need to write a custom ExpectedCondition that queries angular to see if this is done. Here I prepared earlier:

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

This assumes you have a ng application defined for your html element. If you define it elsewhere in the DOM, you will need to update the code above. This is pretty much stolen from the protractor, checking to see if there are any outstanding ajax requests and checking whether angular counts it in the checked state.

The important part is to execute this JavaScript snippet as an async script, because angular uses promises to trigger a callback when it is ready. If you do not run this as an async script, this will not work.

You will also need to set the script timeout for the driver object, this is the amount of time that selenium will wait for the callback to complete before throwing a timeout exception. You can install it as follows:

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

It is as simple as connecting this ExpectedCondition to a wait like this:

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

Hope this helps.

+6
source

I write in C # .Net, and so I had to translate Ardesco's answer:

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

To call it, I used:

 WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10)); wait.Until((d) => { return HelperFunctions.AngularHasFinishedProcessing(driver); }); 
+2
source

Drop Python 2.7 into the mix:

 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) 
+2
source

The reason I think the test failed 30% of the time is because it runs after AngularJS is executed, but before the initialization of your model (nothing to do with the imho digest cycle).

If AngularJS is not loaded yet, the element will exist and be visible (because you did not use ng-cloak ). After that, AngularJS will destroy the #my-unique-id element because initially there is no state, so the ng-if expression will give false .

Then your model is set up and AngularJS processes it in the next digest cycle and modifies the DOM.

So, to fix this, I would add the ng-cloak directive to the element, so it is initially invisible, even before loading AngularJS. Now, if you use webdriver to wait for the element to become visible first, your test should work.

I usually run all Selenium tests, waiting for certain data to load, I try not to rely on AngularJS internal stuff to check this out. For example (using Fluentlenium):

 await() .atMost(5, TimeUnit.SECONDS) .until("h1#my-unique-id") .areDisplayed(); // Assertion 
0
source

All Articles