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
Related
I have the following code:
for button in buttons:
ActionChains(driver).move_to_element(button).perform()
time.sleep(2)
button.click()
time.sleep(2)
try:
wait_button.until(EC.presence_of_element_located((By.XPATH,'//div/h2')))
time.sleep(2)
name = driver.find_element_by_xpath('//div/h2').text
except:
wait_button.until(EC.presence_of_element_located((By.XPATH,'//span[#id="chat-header-title"]')))
time.sleep(2)
name = driver.find_element_by_xpath('//span[#id="chat-header-title"]').text
def pull_ul() -> list:
chat_frame = driver.find_element_by_xpath("//iframe[starts-with(#id, 'experience-container-')]")
driver.switch_to.frame(chat_frame)
wait_button.until(EC.presence_of_element_located((By.XPATH,'//ul[#aria-label="Chat content"]')))
the_ul =driver.find_element(By.XPATH,'//ul[#aria-label="Chat content"]')
new_lis =the_ul.find_elements(By.TAG_NAME,'li')
return new_lis
def pull_ul_again() -> list:
the_ul =driver.find_element(By.XPATH,'//ul[#aria-label="Chat content"]')
new_lis_2 =the_ul.find_elements(By.TAG_NAME,'li')
return new_lis_2
lis = pull_ul()
print(f"Archiving Chat with {name} ...\n")
print("this is len lis: ",len(lis), "for " + name)
And here is what the terminal shows:
As you can see, the code actually does run past the line that threw up the error, how is this possible? Also, why would this be happening , I successfully ran the code multiple times and suddenly it starts throwing the follwing error?
The loop that you're doing,
for button in buttons:
ActionChains(driver).move_to_element(button).perform()
...
is causing your StaleElementReferenceException because inside that you have driver.switch_to.frame(chat_frame), but you never switch back to default content or to the parent frame, which means that your buttons won't exist in that level, and so Selenium throws StaleElementReferenceException as a result. That could also happen if you navigate away from the page (which might also be happening depending on the button clicks).
If you ever switch frames or leave pages, you have to re-find elements in order to interact with them.
This question already has answers here:
How to specify multiple return types using type-hints
(4 answers)
Closed last year.
I want to make use of the hint (or maybe also called annotation?) of python to make easier my coding getting the functions of a class. Therefore I was using something like the following:
def convert_to_string(my_integer_number: int) -> str:
return str(my_integer_number)
But now I'm working with selenium and I have multiple classes that can be accepted for the same code, for example:
import webdriver
def get_driver(browser = DEFAULT_BROWSER, headless=False) -> \
webdriver.Chrome or webdriver.Firefox or webdriver.Edge:
"""
Returns a selenium webdriver:
* browser: Available options are 'chrome', 'edge' and 'firefox'
"""
if browser == 'chrome':
return webdriver.Chrome(
#....
)
if browser == 'firefox':
return webdriver.Firefox(
...
)
if browser == 'edge':
return webdriver.Edge(
...
)
And then in the functions where make use of one of the returned drivers:
def access_login_page(driver: webdriver.Chrome or webdriver.Firefox or webdriver.Edge) -> None:
"""
Access the login page using the header menu
"""
ELEMENT_ID = 'header-sign-in'
try:
element: WebElement = WebDriverWait(driver, timeout=30).until(
EC.element_to_be_clickable((By.ID, ELEMENT_ID))
)
This looks like a lot of overhead but at the same time I find really usefull to have this information. Is this the correct approach to do this?
Use the typing module:
from typing import Union
import webdriver
...
def get_driver(browser = DEFAULT_BROWSER, headless=False) -> \
Union[webdriver.Chrome, webdriver.Firefox, webdriver.Edge]:
...
The typing module contains thin wrappers for built-in types and also things like Optional[<type>] (which is basically Union[<type>, None]), Any, Callable, etc.
I'm learning pytest and I get an error trying to run a test that worked fine before I added pytest-bdd(feature file, step file is the one I'm trying to run)
This is my code:
import pytest
import time
from selenium import webdriver
from pytest_bdd import scenario, given, when, then
#scenario('../feature/learn.feature', 'learning pytest-bdd')
#when('Does work-->website open, find element')
def does_work():
driver = webdriver.Edge(r"C:/Users/artri/Downloads/edgedriver_win64/msedgedriver.exe")
driver.get('https://www.example.com/')
driver.maximize_window()
print(driver.title)
time.sleep(5)
element = driver.find_element_by_xpath("//button[#class='btn btn-outline-light btn-lg btn-close']")
element.click()
name = driver.find_element_by_xpath("//a[#href='/items/something/']").text
driver.quit()
return name
#then('Get text "something"')
def test_work():
assert does_work() == 'something'
It finds the element and checks if it's value is right. I would like to know why after adding steps and making the feature file it doesn't work. Other test in the same steps file works just like they did before.
The error massage:
#then('Get text "something"')
def test_work():
> assert does_work() == 'something'
E TypeError: scenario_wrapper() missing 1 required positional argument: 'request'
I looked at a couple similar questions, but non were understandable for me.
If anyone has any idea where the issue might be or some links would be very appreciated!
You have decorator without function def.
From documentation at: https://github.com/pytest-dev/pytest-bdd
#scenario('publish_article.feature', 'Publishing the article')
def test_publish():
pass
You need to decorate empty function.
Is it possible to call a function outside of the WebDriver in the .until? No matter what I try, I get the exception:
Exception: 'WebDriver' object has no attribute 'verifyObj_tag'.
I have a class called 'ad_selenium' and all calls to selenium are encapsulated within the library. The explicitWait function I wrote is trying to use another class method in the .until:
def explicitWait(self,tag_name,search_for,element=None,compare='contains',seconds=20):
element = WebDriverWait(self.__WD, seconds).until( lambda self: \
self.verifyObj_tag(tag_name,search_for,element=element,compare=compare))
I've tried all sorts of combinations of lambda functions and function varaibles like:
def explicitWait(self,tag_name,search_for,element=None,compare='contains',seconds=20):
x = self.verifyObj_tag
element = WebDriverWait(self.__WD, seconds).until( lambda x: \
x(tag_name,search_for,element=element,compare=compare))
Looking at the code inside selenium/webdriver/support/wait.py, it looks like it always passes webriver to the method passed in the until:
def until(self, method, message=''):
while(True):
try:
value = method(self._driver) #<<--webdriver passed here
if value:
return value
except self._ignored_exceptions:
pass
Any ideas on how to make that work?
You need to let it pass the driver as an argument:
element = WebDriverWait(self.__WD, seconds).until(lambda driver: \
self.verifyObj_tag(tag_name, search_for, element=element, compare=compare))
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