Selenium and AngularJS patiently wait before carrying out specific actions

I have been using selenium to test an AngularJS application and I am encountering an issue where I am unable to perform any actions on the page until it is fully loaded. Although I have considered using Thread.sleep(), I am aware that this is not the most efficient solution. I have researched and tried multiple methods to wait for the page to load, but none have been successful so far. Once I navigate to a webpage in my application, it appears visually loaded, yet I still cannot interact with it until it has fully loaded (which takes about 1 second).

If anyone could share their implementation of how they successfully solved this issue, I would greatly appreciate it.

Here is a snippet of my code:

public By ngWait(final By by) {
    return new FluentBy() {
        @Override
        public void beforeFindElement(WebDriver driver) {
            driver.manage().timeouts().setScriptTimeout(30, TimeUnit.SECONDS);
            ((JavascriptExecutor) driver).executeAsyncScript("var callback = arguments[arguments.length - 1];" +
                    "angular.element(document.body).injector().get('$browser').notifyWhenNoOutstandingRequests(callback);");
            super.beforeFindElement(driver);
        }

        @Override
        public List<WebElement> findElements(SearchContext context) {
            return by.findElements(context);
        }

        @Override
        public WebElement findElement(SearchContext context) {
            return by.findElement(context);
        }

        @Override
        public String toString() {
            return "ngWait(" + by.toString() + ")";
        }
    };
}

https://i.sstatic.net/yEzxA.png

https://i.sstatic.net/lwS5g.jpg

Answer №1

During my experience with writing tests for an Angular app using Selenium, I encountered a similar challenge. The page skeleton loads instantly in the Angular app, but it continuously makes background $http requests to fetch data. The view is only rendered once these calls are completed, making traditional Selenium waits like waitUntilPageToBeLoad or waitUntilElementToBeClickable ineffective.

One workaround is using Thread.Sleep(), but that is not considered a smart wait strategy. A more efficient approach is to implement a custom wait method to ensure that all $http calls have finished in the background. Here is a sample wait method that worked well for me:

public void untilAngularFinishHttpCalls() {
    final String javaScriptToLoadAngular =
        "var injector = window.angular.element('body').injector();" + 
        "var $http = injector.get('$http');" + 
        "return ($http.pendingRequests.length === 0)";

    ExpectedCondition<Boolean> pendingHttpCallsCondition = new ExpectedCondition<Boolean>() {
        public Boolean apply(WebDriver driver) {
            return ((JavascriptExecutor) driver).executeScript(javaScriptToLoadAngular).equals(true);
        }
    };
    WebDriverWait wait = new WebDriverWait(driver, 20); // timeout = 20 secs
    wait.until(pendingHttpCallsCondition);
}

Answer №2

Unfortunately, I am unable to leave a comment at the moment, so I will provide my assistance in the form of an answer.

Regarding the issue with the comments on Priyanshu Shekhar's response, it appears that jqLite (Angular's simplified version of jQuery) may not have the necessary functionality for the task attempted by the code.

To rectify this issue, consider integrating the complete jQuery library into your webpage.

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

Reset the text input field when the dropdown menu is set to 'no/other'

CURRENT: Choosing from a dropdown menu with options 'Yes' or 'No'. If 'Yes' is chosen: Display additional dropdowns/inputs for entry If 'No' is chosen: Conceal additional dropdowns/inputs WANT: I am looking to imp ...

Encountering an ActionController::RoutingError when attempting to send data to a Rails server using AngularJS, as it is unable to find a route for sending a POST request to "/users

Encountering a problem when attempting to send data from my AngularJS side to the Rails server. The error message from the server: ActionController::RoutingError (No route matches [OPTIONS] "/users"): actionpack (4.1.9) lib/action_dispatch/middleware/ ...

Issues with premature execution of Angular JS code have been observed at times

Currently facing an issue with my code where the updateProduct() function call is happening before the forEach loop begins running on about 1 out of every 10 page loads. Not sure why this is occurring or how to solve it. Any insights on what might be causi ...

Is there a tool available that can convert a cron schedule into a more user-friendly, easy-to-read format?

My search for an NPM package that can handle this task has been fruitless so far. I am in need of a cron library that can convert a monthly cron job into easily readable text. For example, inputting 0 0 14 * * should output "Monthly", rather than the curre ...

Accessing a local MS SQL database hosted in a Docker container on macOS Monterey is a simple process that can

Currently, as I work my way through a programming guide (specifically from the "Learn with" series by Jeffry Houser), I am encountering a challenge when it comes to accessing the database that powers a Java/Jersey REST service. The database in question is ...

How do I prevent a specific word from being removed in a contenteditable div using JavaScript?

Attempting to create a terminal-like experience in JS, I am looking to generate the word 'current source, current location' (e.g., admin@ubuntuTLS~$: ~/Desktop) at the beginning which cannot be removed. Also, I want to prevent the caret from bein ...

Tips for sending an optional parameter to @Directives in Angular 2 using TypeScript

Here is a helpful guide on passing parameters to Angular 2 directives. <p [gridGroup]="gridGroup"></p> My goal is to have the parameter as optional so that it doesn't have to be included in every class referencing the html source. Curre ...

Processing hover attributes in Tailwind-styled-components

I'm currently working on a website that features a dark mode, and I want to utilize the dark prop in tailwind-styled-components. The props work fine in all instances except for actions like hover, active, focus, etc. When attempting to use hover and t ...

Use $parse to extract the field names that include the dot character

Suppose I have an object with a field that contains a dot character, and I want to parse it using $parse. For instance, the following code currently logs undefined - var getter = $parse('IhaveDot.here'); var context = {"IhaveDot.here": 'Th ...

Navigating through the various iterations of jQuery

Unique Case Study I'm currently facing a dilemma involving the integration of jstree and jquery-ui datepicker. The scenario is this: I am attempting to utilize jquery-ui-datepicker on an input element that is dynamically inserted into the DOM after ...

Exploring the Ins and Outs of Debugging JavaScript in Visual Studio Using

I encountered a peculiar issue while testing some code. When the program is executed without any breakpoints, it runs smoothly. However, if I introduce a breakpoint, it halts at a certain point in the JSON data and does not allow me to single-step through ...

WebApp specifically designed for iPads that mimics the functionality of a swipe

I am in the process of developing a full-screen web application for an iPad that will showcase a series of images in a slider format. The users should be able to swipe between the images and click on one to view it in detail. Below is an example showcasin ...

Get rid of the delay when using ng-change

I am currently facing a challenge with two select boxes. Specifically, I need to hide the "Canada" selection in one box when it is selected in the other box. To address this issue, I have successfully implemented a solution on JSFiddle: https://jsfiddle.n ...

Maintaining the user interface state while utilizing $resources in AngularJS

For my app, users have the ability to create and delete items. I've implemented $resources for this functionality, which is working really well. However, I'd like to implement a loading screen that appears whenever a request is being processed. ...

After setting up a Mongoose schema for authentication, how can I effectively perform database queries with MongoDB?

After successfully setting up authentication for my node.js (Express) app using Passport-local and Mongoose schema, I organized the folder structure as follows: app - app.js - config - auth.js - keys.js - passport.js - models - User.js - ...

Creating a CSS animation to repeat at regular intervals of time

Currently, I am animating an SVG element like this: .r1 { transform-box: fill-box; transform-origin: 50% 50%; animation-name: simpleRotation,xRotation; animation-delay: 0s, 2s; animation-duration: 2s; animation-iterat ...

Restricting Meteor Publish to specific user (admin) for all collections

Is there a method to exclusively publish all meteor collections to users with the role of {role: "admin"}? The meteor autopublish package grants database access to all clients. Are there any techniques to utilize the autopublish package while implementing ...

Having trouble with Javascript/jQuery not functioning in IE8?

My current project involves a website that functions smoothly in Chrome, but encounters issues with running JavaScript/jQuery scripts in IE8. Even a simple alert on page load fails to execute. Admittedly, my approach is a bit messy as I have mixed CSS an ...

What causes Chrome Extension Images to appear broken when they are inserted into the DOM?

Currently working on a Chrome extension, I am attempting to insert a div with a background image into the DOM using a content script. The CSS is loading correctly, and upon inspecting the Developer Tools, the image URL appears to be correct. $('.clos ...

Combine an array of arrays with its elements reversed within the same array

I am working with an array of numbers that is structured like this: const arrayOfArrays: number[][] = [[1, 2], [1, 3]]; The desired outcome is to have [[1, 2], [2, 1], [1, 3], [3, 1]]. I found a solution using the following approach: // initialize an e ...