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
Related
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);
My selenium code checks for a completed subroutine to be done by waiting on the site's title to change which worked perfectly. Code looks like this:
waitUntilDone = WebDriverWait(session, 15).until(EC.title_contains(somestring))
However, this can fail sometimes since the site's landing page changes after manual website visits. The server remembers where you left off. This forces me to check for an alternate condition (website title = "somestring2).
Here is what I came up with so far (also works as far as I can tell):
try:
waitUntilDone = WebDriverWait(session, 15).until(EC.title_contains(somestring)) # the old condition
except:
try:
waitUntilDone = WebDriverWait(session, 15).until(EC.title_contains(somestring2)) # the new other condition which is also valid
except:
print "oh crap" # we should never reach this point
Either one of these conditions is always true. I don't know which one thou.
Is there any way to include an "OR" inside these waits or make the try/except block look nicer?
Looks like selenium will let you do this by creating your own class. Check out the documentation here: http://selenium-python.readthedocs.io/waits.html
Here's a quick example for your case. Note the key is to have a method named __call__ in your class that defines the check you want. Selenium will call that function every 500 milliseconds until it returns True or some not null value.
class title_is_either(object):
def __init__(self, locator, string1, string2):
self.locator = locator
self.string1 = string1
self.string2 = string2
def __call__(self, driver):
element = driver.find_element(*self.locator) # Finding the referenced element
title = element.text
if self.string1 in title or self.string2 in title
return element
else:
return False
# Wait until an element with id='ID-of-title' contains text from one of your two strings
somestring = "Title 1"
somestring2 = "Title 2"
wait = WebDriverWait(driver, 10)
element = wait.until(title_is_either((By.ID, 'ID-of-title'), somestring, somestring2))
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
I am trying access js variables from a website and I am doing something like this:
log = driver.execute_script(""" return v1,v2; """)
print log
where driver = webdriver.Chrome()
It is returning only the value for v2 variable but not the v1.
Is there any way I can return more than one variable and store it into a list ?
You need to wrap the values with brackets
log = driver.execute_script(""" return [v1, v2]; """)
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