Determine using Selenium whether Angular is performing various actions

Why does

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

I am currently working on an angular application and testing it using selenium with chromedriver in java on a mac operating system.

The markup I am dealing with looks like this:

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

Many times, when I run the assertion:

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

I encounter the error:

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

This happens around 30% of the time.

I find it puzzling that the .getText() of that element could be evaluated to "", so my assumption is that angular might be $digesting or $compiling the page. This theory seems plausible to me. It's ok. It's fine. Everything will be fine.

I wish to determine when angular has completed $compiling and $digesting, so I can examine the final state of the page.

If I insert the following code snippet into my page:

(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 observe the console.log messages at the anticipated times.

When I paste the same code 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 receive 'undefined' as the output.

In essence, what I want to achieve is to do the following from 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 have attempted the following approach, but there are no existing examples of Selenium performing such tasks:

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

        }
    });

Is there a way for me to determine from Selenium whether angular is actively processing operations?

Answer №1

To ensure that your web page has finished processing Angular, it is recommended to create a custom ExpectedCondition. Here is an example you can use:

    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 code assumes that the 'ng-app' attribute is defined on your html element. If it is located elsewhere in the DOM, make sure to adjust the code accordingly. The logic behind this script is borrowed from Protractor and checks for pending ajax requests along with Angular's testable state.

Execute the JavaScript snippet as an asynchronous script since Angular uses promises for triggering callbacks upon readiness. Failure to run it asynchronously will lead to errors.

Add a timeout value to the driver object to specify how long Selenium should wait for the callback to complete before raising a timeout exception:

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

Integrate this ExpectedCondition into a WebDriverWait instance like this:

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

Implementing these steps should help ensure smooth synchronization with Angular.

Answer №2

In my C#.Net project, I needed to adapt Ardesco's solution for my needs:

static class HelperFunctions
{

    public static bool CheckAngularProcessingStatus(IWebDriver driver)
    {
        string ScriptForCheckingAngular = 
            @"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 jsExecutor = (IJavaScriptExecutor)driver;
        return Convert.ToBoolean(jsExecutor.ExecuteAsyncScript(ScriptForCheckingAngular));
    }
}

To utilize this function, the following code was used:

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

Answer №3

Introducing Python 2.7 into the equation:

class check_angular_status(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

# Setting up the driver and timeout parameters,
# and utilizing the condition in this manner:
WebDriverWait(driver, timeout).until(check_angular_status)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Implement Angular.js on a fresh DOM element within a Content Script in a Chrome Extension

After spending quite some time on this, I still haven't made much progress. I'm attempting to incorporate angular functionality (such as ng-repeats and data binding) into the red box that pops up when text is double-clicked on any webpage. Unfor ...

Navigating the file paths for Vue.js assets while utilizing the v-for directive

Recently, I started working with Vue.js and found it simple enough to access the assets folder for static images like my logo: <img src="../assets/logo.png"> However, when using v-for to populate a list with sample data, the image paths se ...

Is the display of the password for `m.Password` shown on the screen using `@Html.PasswordFor`

I'm currently using the following code to handle password input: @Html.PasswordFor(m => m.Password) on my webpage. There seems to be an issue with German Umlauts not being properly processed. I need a way to display the entered password on the sc ...

The execution of Node.js callback function is not triggered

After setting up an API that queries a MongoDB and returns JSON Objects, I decided to modularize the code. I moved the MongoDB functionality to a separate file called queryMongo.js, which is now called in the POST request of main.js. Here is the main.js co ...

Balancing scrolling status in sibling components within React/Redux

Is it possible for a React component to control the scroll position of another sibling component? The main component, known as Parent, includes two child components: List (which contains a scrollable div) and Actions with a button that is meant to control ...

tips for displaying an array of responsejson in a flatlist

I am new to React-Native and would appreciate an explanation. Here is what my response.json() looks like: { "1": {name: "bob", class: 12}, "2": {name: "anax", class: 123} } Can someone guide me on how to render this data into a FlatList? ...

Invoke the C# function in the code-behind using an AJAX call

I have been attempting to encrypt two variables and return them as query strings using a code-behind function, but I have not been successful. This is my first time trying Ajax. Here is the method in my code-behind: [WebMethod] public static string Encri ...

Click on the 'Login' button on the webpage if it is available

Here is what I need: If the 'Login' button is displayed, click on it and proceed with the use case. If the 'Login' button is not present, go ahead with the use case directly (no need to click on 'Login' button). Based on t ...

What is the best way to add unique styles to multiple tags with the same class designation?

Looking for suggestions on styling two tags with the same class differently? I have 2 tables: <table id="tab1" class=".ui-jqgrid .ui-jqgrid-hbox"> <table id="tab2" class=".ui-jqgrid .ui-jqgrid-hbox"> The first table is a jqgrid on the page, ...

What version of Selenium JAR is suitable for use with either Mozilla Firefox version 52 or the most up-to-date

Until recently, I had been utilizing Selenium version 2.53 alongside Firefox version 46.1. However, Firefox has now undergone updates and I am attempting to work with the latest version. Could you please inform me which Selenium JAR is compatible with Moz ...

Is it possible to delay the execution of the code until the ajax function has completed

After executing the fillContent function, I need to call beginEditingQuestion. fillContent(cid, "questions"); beginEditingQuestion(qid); The issue is that beginEditingQuestion can't be executed until all the ajax requests in fillContent are complete ...

Learn the steps to invoke a JavaScript function from a <td> element within an ng-repeat loop

How can I call an Angular function inside ng-repeat td and replace the value from the function with the value in the td element? The code snippet provided below is not functioning as expected. Instead of getting the date, the same getCSTDateTime function i ...

Retrieve a Kendo UI model by utilizing drag and drop functionality on a non-Kendo UI element

I'm currently developing a prototype that utilizes Kendo UI Pro with a heavy reliance on the TreeListView and Drag and Drop functionalities. I have implemented a custom drop location using an Angular 1.x custom directive. Within this directive's ...

Reducing an array group using index in JavaScript: A beginner's guide

Do you have coding questions? Check out this sample array group: myArray = { tab1 : [], tab2 : [], tab3 : [], tab4 : [] } I'm looking to always retain the first tab (tab1) and an additional tab based on an index (ranging from 2 to 4) For instance, ...

Creating the data type for the input file's state: React with Typescript

Encountering an error when attempting to define the type of a file object within state: Argument of type 'null' is not assignable to parameter of type 'File | (()=> File)'.ts. Currently working on an upload component that allows for ...

What is the best way to instantiate a dictionary/JSON-like data structure in JSP?

In my dataset, I have the following information: { "2022-37" : "2022-09-17 00:00:00.0", "2022-38" : "2022-09-24 00:00:00.0", "2022-39" : "2022-10-01 00:00:00.0", "2022-40" ...

Unable to reach the sub-component of the JSON object that was returned

For some reason, I can't seem to access a sub object of a returned $Resource object that fetched a group of JSON objects. It's baffling me. Resource > $resolved: true > $then: function (b, g) {var j=e(),h= > data: Object > 519bc5f6 ...

Is the "json_encode" function dropping the '+' character when using "json.parse"?

Seeking Assistance I have a question regarding the functionality of php function json_encode and js JSON.parse. I seem to be encountering an issue where the '+' character is being dropped somewhere in the process, particularly when dealing with ...

A guide to navigating nested JSON arrays in React Native

Can anyone help me fetch this JSON data? JSON [ "got_new_event", { "type": "exchange", "data": [ { "m": 0.1556, "c": "USGLDKG", "l": 39264, "a": 39264, "t": "085 ...

What is the optimal Selenium standalone version to use with Firefox version 58.0?

Which Selenium standalone jars are compatible with Firefox version 58.0? I attempted using selenium jars version 2.46.0 and encountered the following exception: org.openqa.selenium.WebDriverException: Unable to bind to locking port 7054 within 45000 ms ...