Reputation: 11
I am using Robot Framework to do test automation and would like to know if there is a way for me to extend the Selenium Webdriver to handle a loading element universally. For reference when I say loading element I am referring to a custom Angular element.
I understand that I can do a Wait Until Element Not Visible
, but what I really want to do is create a constant listener on the Selenium Webdriver to halt whenever this loading element is visible. I would like to not constantly use a robot keyword to check if the element is visible, and be forced to implement arbitrary-ish sleeps.
If my question is still not clear, below is some pseudocode / psuedoflow I was thinking of. I'm assuming Selenium is single threaded, otherwise this idea would be fruitless.
Selenium Webdriver:
AddListener(new LoadingListener)
LoadingListener
While ( LoadingElement is Visible)
Sleep 1s
Please respond with any feedback. If this isn't possible let me know, If you have any ideas also let me know.
Upvotes: 1
Views: 1289
Reputation: 11
Below is the solution I ended up using :) Worked well, thank you for all comments and help!
from robot.libraries.BuiltIn import BuiltIn
from datetime import datetime
class LoaderListener(object):
ROBOT_LISTENER_API_VERSION = 2
ROBOT_LIBRARY_SCOPE = "GLOBAL"
def __init__(self):
self.ROBOT_LIBRARY_LISTENER = self
self.selib = BuiltIn().get_library_instance("SeleniumLibrary")
def _start_suite(self, name, attrs):
BuiltIn().log_to_console("Loader Listener Has Been Attached.")
def start_keyword(self, name, attrs):
try:
# If this doesn't fail we have the loader
self.selib.find_element("id=loader")
# Take time stamp for when we begin waiting
BuiltIn().log_to_console("The Page Is Loading.")
start_time = datetime.now()
# Wait Until Element Not Visible
# Sometimes this will fail, because by the time we get here the loader is gone.
# In that case, just continue as normal
try:
self.selib.wait_until_element_is_not_visible("id=loader")
except:
pass
# Take time stamp for when we have stopped waiting
end_time = datetime.now()
# Calculate time elapsed
elapsed_time = (end_time-start_time)
# Create output message and log to conosle
output_message = "The Page Has Finished Loading in {0}".format(elapsed_time)
BuiltIn().log_to_console(output_message)
except:
# We don't have the element...
pass
Upvotes: 0
Reputation: 6971
Another direction you could take is to use the SeleniumLibrary keyword Execute Async Javascript
.
The document states:
Executes asynchronous JavaScript code.
Similar to Execute Javascript except that scripts executed with this keyword must explicitly signal they are finished by invoking the provided callback. This callback is always injected into the executed function as the last argument.
Scripts must complete within the script timeout or this keyword will fail
With the following code example from the docs:
${result} = Execute Async JavaScript
... var callback = arguments[arguments.length - 1];
... function answer(){callback("text");};
... window.setTimeout(answer, 2000);
Should Be Equal ${result} text
Upvotes: 0
Reputation: 386210
I haven't ever done this, but you might be able to use the listener interface to call a function before or after every keyword. For example, the listener might look something like this (untested):
from robot.libraries.BuiltIn import BuiltIn
xpath = //*[@id='loading_element']
class CustomListener(object):
ROBOT_LISTENER_API_VERSION = 2
def start_keyword(self, name, attrs):
selib = BuiltIn().get_library_instance("SeleniumLibrary")
selib.wait_until_element_is_not_visible(xpath)
This may throw an exception if the element doesn't exist on the current page, and you also need to add code to handle the case where a keyword runs before the browser is open, but hopefully it gives the general idea.
For more information about the listener interface see the section titled Listener Interface in the Robot Framework User Guide.
Upvotes: 1