Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force Selenium to wait for AngularJS

How can I force python Selenium wait for a second until AngularJS completes page parsing and loads some stuff that it needs.

Or how can I force Selenium to wait for 1 second after button click, which causes ajax request to the server, handled by AngularJS. I need server side actions to take place before navigating to other page.

like image 958
Simanas Avatar asked Dec 27 '13 19:12

Simanas


3 Answers

If some asynchronous behavior in your application is important enough that a real user should wait for it, then you should tell them. Similarly, your script can wait for that same indication before proceeding.

For example, if a user clicks a button that triggers an API call to create a record, and the user needs to wait for that record to be created, you should show them a message indicating when it completes successfully, e.g., "Record created successfully." Your script can then wait for that same text to appear, just as a user would.

Importantly, it shouldn't matter how your application is implemented. What matters is that your users can use your application—not that it calls certain AngularJS APIs or React APIs, etc.

1. Using Selenium

Selenium includes WebDriverWait and the expected_conditions module to help you wait for particular conditions to be met:

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

TIMEOUT = 5

# ...

WebDriverWait(driver, TIMEOUT).until(
    EC.text_to_be_present_in_element(
        [By.CLASS_NAME, "alert"],
        "Record created successfully"))

2. Using Capybara (which uses Selenium)

As you can see above, bare Selenium is complicated and finicky. capybara-py abstracts most of it away:

from capybara.dsl import page

# ...

page.assert_text("Record created successfully")
like image 138
Ian Lesperance Avatar answered Sep 29 '22 21:09

Ian Lesperance


Access AngularJS scope through Selenium - most likely that state is already held in Scope/IsolatedScope .

I built a few extensions to help with this that can be translated to python.

webDriver.NgWaitFor(productDiv, "scope.Data.Id != 0");
webDriver.NgWaitFor(partialElement, "scope.IsBusyLoadingTemplate == false");

https://github.com/leblancmeneses/RobustHaven.IntegrationTests/blob/master/NgExtensions/NgWebDriverExtensions.cs

to deal with ajax request when your working with both angularjs $http and jquery I use:

webDriver.WaitFor("window.isBrowserBusy() == false");

requires you to setup intercepts in both angularjs and jquery to manage the count of the xhr requests.

Here is the framework we are using in our project: (you might want to extract more pieces from it)

https://github.com/leblancmeneses/RobustHaven.IntegrationTests

like image 45
Leblanc Meneses Avatar answered Sep 29 '22 20:09

Leblanc Meneses


I had the same problem this is the way I solved it

from datetime import datetime, timedelta
from time import sleep

from selenium import webdriver
from selenium.common.exceptions import WebDriverException


class MyDriver(webdriver.Chrome):
    def __init__(self, executable_path="chromedriver", port=0,
                 chrome_options=None, service_args=None,
                 desired_capabilities=None, service_log_path=None):
        super().__init__(executable_path, port, chrome_options, service_args,
                         desired_capabilities, service_log_path)

    def wait_until_angular(self, seconds: int = 10) -> None:
        java_script_to_load_angular = "var injector = window.angular.element('body').injector(); " \
                                      "var $http = injector.get('$http');" \
                                      "return ($http.pendingRequests.length === 0);"
        end_time = datetime.utcnow() + timedelta(seconds=seconds)
        print("wait for Angular Elements....")
        while datetime.utcnow() < end_time:
            try:
                if self.execute_script(java_script_to_load_angular):
                    return
            except WebDriverException:
                continue
            sleep(1)
        raise TimeoutError("waiting for angular elements for too long")

It worked for me Hope this helps you!!!

like image 39
Moshe Slavin Avatar answered Sep 29 '22 21:09

Moshe Slavin