Specify wait time between Actions when using Selenium Actionchains - python

The ActionChains is a very handy method when using Selenium.
It works really well, only thing i am missing is how to insert wait times between the Actions.
I will take the same example from the official google Selenium Documentation.
https://selenium.googlecode.com/git/docs/api/py/webdriver/selenium.webdriver.common.action_chains.html
menu = driver.find_element_by_css_selector(".nav")
hidden_submenu = driver.find_element_by_css_selector(".nav #submenu1")
ActionChains(driver).move_to_element(menu).click(hidden_submenu).perform()
What i am looking for is a way to insert wait times between the two actions
ActionChains(driver).move_to_element(menu)**(..wait some seconds)**.click(hidden_submenu).perform()
Thanks!

I tried this and seems working
from selenium import webdriver
action = webdriver.ActionChains(driver)
action.pause(3)
action.perform()
driver.close()

Note: Better answer
RobZ's answer is sufficient. The ActionChains class has a pause method that will do what was asked in the original question without requiring any subclassing or custom code.
Original answer
Here's a Python example based on Kim Homann's tip. It extends the ActionChains Selenium class to add a wait action.
import time
from selenium.webdriver import ActionChains
class Actions(ActionChains):
def wait(self, time_s: float):
self._actions.append(lambda: time.sleep(time_s))
return self
Then your test becomes:
Actions(driver) \
.move_to_element(menu) \
.wait(2) \
.click(hidden_submenu) \
.perform()

I don't know Python, but I think it's the same as in C#. I hope my code is readable for you.
You can create your own class ActionsEx deriving from Actions. Then you declare a method public Actions Wait(TimeSpan duration). Inside this method, you call AddAction(new SleepAction(duration));. AddAction() is a protected method of Selenium's Actions class, which is accessible only if you derive from this class.
SleepAction is a class implementing the IAction interface, which you have to create. It can look like this example:
public class SleepAction : IAction
{
public SleepAction(TimeSpan duration)
{
_duration = duration;
}
private TimeSpan _duration;
void IAction.Perform()
{
ToolBox.Sleep((int) _duration.TotalMilliseconds);
}
}
ActionsEx class:
public class ActionsEx : Actions
{
public ActionsEx(IWebDriver driver) : base(driver)
{
}
public Actions Wait(TimeSpan duration)
{
AddAction(new SleepAction(duration));
return this;
}
}
Then you can call an action chain like this:
var actions = new ActionsEx(driver);
var duration = TimeSpan.FromSeconds(1);
((ActionsEx)actions
.Wait(duration)
.MoveToElement(element))
.Wait(duration)
.Click()
.Build()
.Perform();

Simply import the time modul and use sleep whenever you need it:
from time import sleep
action = webdriver.ActionChains(driver)
action.move_to_element(menu)
sleep(5)
action.click(hidden_submenu).perform()
Hope this helps you a bit.

I believe the issue is that a delay executed outside of the ActionChain will be ignored once perform is called. Think of the chain like a queue of actions in sched: you could wait for hours and hours between adding items to the queue, but once you call run, each task will execute in simple sequence without a delay.
So, to create a delay inside the chain, I would use Selenium's pause method.
Docs here: http://selenium-python.readthedocs.io/api.html

Related

How to keep selenium webdriver running as part of before_tag hook using fixture in behave framework?

I am using behave framework for testing and some features files do not require a browser to run against. So I am trying to use fixtures with tags as described in https://behave.readthedocs.io/en/latest/tutorial.html#controlling-things-with-tags. The problem is that the CLEANUP-FIXTURE PART ran right after the driver is started.
environment.py
#fixture
def selenium_webdriver(context):
# -- SETUP-FIXTURE PART:
context.driver = webdriver.Remote(
command_executor='http://my-selenium-server:4444/wd/hub',
desired_capabilities=DesiredCapabilities.CHROME)
context.driver.set_page_load_timeout(30)
context.driver.maximize_window()
yield context.driver
# -- CLEANUP-FIXTURE PART:
context.driver.quit()
fixture_registry1 = {
"fixture.selenium_webdriver": selenium_webdriver
}
def before_tag(context, tag):
if tag.startswith("fixture.selenium_webdriver"):
return use_fixture_by_tag(tag, context, fixture_registry1)
test.feature
#fixture.selenium_webdriver
Feature: Test
Scenario: My Scenario
Given Something
When I do anything
Then Something happens
During scenarios execution, the driver starts successfully, however, it ends right after. I have tried to comment the lines under CLEANUP-FIXTURE PART: to keep it running, but the behave kind of lost connection with the selenium session. Any thoughts on how to only starts selenium webdriver for specifics tags and features?
Ok, reading again the documentation I found a solution that worked for me. Here is what I did:
environment.py
#fixture
def selenium_webdriver(context):
# -- SETUP-FIXTURE PART:
context.driver = webdriver.Remote(
command_executor='http://my-selenium-server:4444/wd/hub',
desired_capabilities=DesiredCapabilities.CHROME)
context.driver.set_page_load_timeout(30)
context.driver.maximize_window()
yield context.driver
# -- CLEANUP-FIXTURE PART:
context.driver.quit()
fixture_registry1 = {
"fixture.selenium_webdriver": selenium_webdriver
}
def before_feature(context, feature):
if "fixture.selenium_webdriver" in feature.tags:
use_fixture(selenium_webdriver, context)
test.feature
#fixture.selenium_webdriver
Feature: Test
Scenario: My Scenario
Given Something
When I do anything
Then Something happens
Using before_feature hook instead of before_tag did the trick.

using selenium with python to handle confirmation alerts when they appear

I am using selenium with python for the website automation,and that includes navigation and file downloads.If the page is idle for around 15 mins(roughly,not exact),confirmation pop up appears warning the session timeout and it asks to click "OK" to continue.
I know I can use the following code to deal with the confirmation pop up
driver.switch_to.alert.accept()
But how can I click "OK"in the confirmation popup whenever it appears?Do I have hto keep checking every 30 seconds if the confirmation popup exists?
It looks like someone here had a similar question as you: How to check if an alert exists using WebDriver?
If you run driver.switch_to.alert when there is no alert actually present, you will receive a NoAlertPresentException. So, the basic idea is to write a method with a try / catch block that attempts driver.switch_to.alert, and returns true or false based on the presence of an exception.
Here's an example:
public boolean doesAlertExist()
{
try
{
driver.switch_to.alert();
return true;
}
catch (NoAlertPresentException e)
{
return false;
}
}
As far as your timing issue goes (alert pops up every 15ish minutes or so) -- you can try to write a wrapper method for driver.findElement() and element.click() that checks for the presence of the alert implicitly. Specific details about the method will depend on the project, but here's an example of something simple:
public IWebElement findElementWrapped(By by)
{
if (doesAlertExist())
{
driver.switch_to.alert().accept(); // accept the alert
return driver.findElement(by); // use selenium's standard findElement
}
else
{
// no alert exists, just find the element
return driver.findElement(by);
}
}
With this code, you can check for the alert every time you try to find an element on the page, but you only have to write the line of code once. You can use this method in action like this:
// check for alert, accept alert if it exists, get the desired web element
IWebElement myElement = driver.findElementWrapped(by);

clickAndHold is not working in Selenium webdriver (python)

I have a function with actions like click, focus, etc. and works fine, but I need the function clickAndHold and returns an error when I try to run test.
This is a piece of code of my function:
def start_action(self, selector, action, value):
browser = self.d
element = browser.find_element_by_xpath(selector)
if action == 'clickAndHold':
actions = ActionChains(browser)
actions.clickAndHold(element)
actions.perform()
And this is the error:
AttributeError: 'ActionChains' object has no attribute 'clickAndHold'
Please help me!
In Python this method called click_and_hold(). Try to use it instead of clickAndHold()
Note that in Python in most cases snake_case used instead of camelCase

Wrapping Selenium "Expected Conditions" Python

I am trying to create my own Selenium class with custom functions so that test scripting will become a bit more intuitive and more robust in some scenarios, for my taste at least. One of my current tasks is to wrap all Selenium expected conditions (described here) so that eventually I will have a single function that looks something like that:
def waitForElement(self, elementName, expectedCondition, searchBy)
Where:
elementName - the name of the element I am looking for. That could be id, name, xpath, css, etc...
expectedCondition - this is where the Selenium expected condition is set. So that can be: element_to_be_clickable, visibility_of_element_located, etc...
The above function internally implements the standard Selenium WebDriverWait as follows:
try:
if expectedCondition == "element_to_be_clickable":
element = WebDriverWait(self.driver, defaultWait).until(EC.element_to_be_clickable((searchBy, elementName)))
elif expectedCondition == "visibility_of_element_located":
element = WebDriverWait(self.driver, defaultWait).until(EC.visibility_of_element_located((searchBy, elementName)))
All is good but I have a bit of trouble with passing the searchBy as a parameter. To remind, searchBy can be one of the following:
By.ID
By.NAME
By.CLASS_NAME
...
When I call this wrapper function from the main code, I do it with the below line:
self.waitForElement("elementName", "element_to_be_clickable", "By.NAME", "test")
So all parameters are passed as strings which is fine for everything except of the searchBy part.
So my question is: How can I pass the By.X part as a parameter to my function?
Hopefully I was able to describe my situation well. If I wasn't I will be happy to clarify.
Eventually I was able to solve this problem after asking this question. So in order to obtain the desired functionality, the above-mentioned method will look like this:
def waitForElement(self, elementName, expectedCondition, searchBy):
try:
if expectedCondition == "element_to_be_clickable":
element = WebDriverWait(self.driver, self.defaultWait).until(EC.element_to_be_clickable((getattr(By, searchBy), elementName)))
elif expectedCondition == "visibility_of_element_located":
element = WebDriverWait(self.driver, self.defaultWait).until(EC.visibility_of_element_located((getattr(By, searchBy), elementName)))
. . .
So it can be called like this:
self.waitForElement("elementName", "element_to_be_clickable", "NAME")
You can start like this:
Create main findElement method, that accepts By instance:
WebElement findElement(By by) {
try {
return driver.findElement(by);
} catch (NoSuchElementException e) {
logException("ERROR: Could not find - '" + by + "' on page " + driver.getCurrentUrl());
throw e;
}
Then create wait method, that uses the findElement method:
WebElement findElementAndWaitElementToPresent(By by, int timeoutInSeconds) {
try {
WebDriverWait wait = new WebDriverWait(driver, timeoutInSeconds);
wait.until(ExpectedConditions.presenceOfElementLocated(by));
return findElement(by);
} catch (TimeoutException e) {
logException("ERROR: Element is not present: " + by + " on page " + driver.getCurrentUrl());
throw e;
}
}
And pass By instance to the findElementAndWaitElementToPresent method:
findElementAndWaitElementToPresent(By.xpath("//your_xpath"), 10);
or
findElementAndWaitElementToPresent(By.name("name"), 10);
something like this is done in the framework i am working on/with
Issue with your code is that you are converting "By" datatype to string.
Simple way would be to pass it without quotes like below:
self.waitForElement("elementName", "element_to_be_clickable", By.NAME, "test")
Only additional thing you need to do is to add import as below for By in python module from where you are calling above method:
from selenium.webdriver.common.by import By

Getting console.log output from Chrome with Selenium Python API bindings

I'm using Selenium to run tests in Chrome via the Python API bindings, and I'm having trouble figuring out how to configure Chrome to make the console.log output from the loaded test available. I see that there are get_log() and log_types() methods on the WebDriver object, and I've seen Get chrome's console log which shows how to do things in Java. But I don't see an equivalent of Java's LoggingPreferences type in the Python API. Is there some way to accomplish what I need?
Ok, finally figured it out:
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
# enable browser logging
d = DesiredCapabilities.CHROME
d['loggingPrefs'] = { 'browser':'ALL' }
driver = webdriver.Chrome(desired_capabilities=d)
# load the desired webpage
driver.get('http://foo.com')
# print messages
for entry in driver.get_log('browser'):
print(entry)
Entries whose source field equals 'console-api' correspond to console messages, and the message itself is stored in the message field.
Starting from chromedriver, 75.0.3770.8, you have to use goog:loggingPrefs instead of loggingPrefs:
d['goog:loggingPrefs'] = { 'browser':'ALL' }
To complete the answer: starting from chromedriver 75.0.3770.8, you have to use goog:loggingPrefs instead of loggingPrefs.
See Chromedriver changelog: http://chromedriver.chromium.org/downloads or this bug: https://bugs.chromium.org/p/chromedriver/issues/detail?id=2976
if you are using the python logging module (and you should be)... here is a way to add the selenium browser logs to the python logging system..
the get_browser_log_entries() function grabs the logs from eth provded driver, emits them to the python logging module as chrome. (ie chrome.console-api, chrome.network etc..) using the timestamp from the browser.(in case there is a delay before you call get_log)
it could probably do with some better exception handling (like if logging is not turned on ) etc.. but it works most of the time..
hop
import logging
from selenium import webdriver
def get_browser_log_entries(driver):
"""get log entreies from selenium and add to python logger before returning"""
loglevels = { 'NOTSET':0 , 'DEBUG':10 ,'INFO': 20 , 'WARNING':30, 'ERROR':40, 'SEVERE':40, 'CRITICAL':50}
#initialise a logger
browserlog = logging.getLogger("chrome")
#get browser logs
slurped_logs = driver.get_log('browser')
for entry in slurped_logs:
#convert broswer log to python log format
rec = browserlog.makeRecord("%s.%s"%(browserlog.name,entry['source']),loglevels.get(entry['level']),'.',0,entry['message'],None,None)
rec.created = entry['timestamp'] /1000 # log using original timestamp.. us -> ms
try:
#add browser log to python log
browserlog.handle(rec)
except:
print(entry)
#and return logs incase you want them
return slurped_logs
def demo():
caps = webdriver.DesiredCapabilities.CHROME.copy()
caps['goog:loggingPrefs'] = { 'browser':'ALL' }
driver = webdriver.Chrome(desired_capabilities=caps )
driver.get("http://localhost")
consolemsgs = get_browser_log_entries(driver)
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s:%(levelname)7s:%(message)s')
logging.info("start")
demo()
logging.info("end")
Note that calling driver.get_log('browser') will cause the next call to return nothing until more logs are written to the console.
I would suggest saving the logs to a variable first. For example below logs_2 will equal [].
If you need something in the console to test you can use:
self.driver.execute_script("""
function myFunction() {
console.log("Window loaded")
}
if(window.attachEvent) {
window.attachEvent('onload', myFunction());
} else {
if(window.onload) {
var curronload = window.onload;
var newonload = function(evt) {
curronload(evt);
myFunction(evt);
};
window.onload = newonload;
} else {
window.onload = myFunction();
}
}
""")
logs_1 = driver.get_log('browser')
print("A::", logs_1 )
logs_2 = driver.get_log('browser')
print("B::", logs_2 )
for entry in logs_1:
print("Aa::",entry)
for entry in logs_2:
print("Bb::",entry)
See the answer from msridhar for what should go above my example code.

Categories

Resources