Selenium "Wait Until Presence of Element Located By" not behaving as expected - python

I'm running a Python script that uses Selenium + Chromedriver to automate a series of actions on a web app. Several of these actions causes the page or frame to reload. Since Selenium does not have a built-in "wait for page load" feature and I would rather not use time.sleep(x) if I can avoid it, I have used this combo frequently:
wait = WebDriverWait(driver, 10)
driver.find_element_by_id("sysverb_update_and_stay").click() #causes form to submit and data to load
element = wait.until(EC.presence_of_element_located((By.ID, "view.change_task.parent")))
element = wait.until(EC.element_to_be_clickable((By.ID, "view.change_task.parent")))
Based on the documentation I have read (and the name of the attribute itself), I expected that the script would wait until an element with that ID was present before continuing, and that if it did not find the element within the script's built-in timeout, only then would the script crash out and throw an exception. However, as soon as the script hits the the first wait line, it crashes and throws the following exception:
Traceback (most recent call last):
File "servicenow.py", line 137, in test_change_control
element = wait.until(EC.presence_of_element_located((By.ID, "view.change_task.parent")
))
...
selenium.common.exceptions.WebDriverException: Message: unknown error: unhandled inspec
tor error: {"code":-32000,"message":"Cannot find context with specified id"}
If it can't find that element by the ID, isn't it supposed to, you know, wait for it to be present? What am I missing here?
EDIT: Added wait in code above, and HTML for element below:
<a id="view.change_task.parent" name="view.change_task.parent" style="" class="btn btn-default btn-ref icon icon-info" data-type="reference_popup" data-table="change_task" data-form="task.do" data-ref="change_task.parent" data-ref-key="null" data-view="" tabindex="-1"><span class="sr-only">View - Opens reference record in current window</span></a>

This appears to be an issue with earlier versions of Chromedriver -- updating to the latest version appears to have solved this.

Related

Understanding selenium move_to_element behaviour + StaleElementReference exception

I am using Python3.9+Selenium to write a small script that fills an online form for me.
A bit of context: the webpage contains a field (locationField) expecting a street address as input, and located on top of some sort of "google maps wrapper".
When typing in the field, it loads a drop-down list of one element (locationField_sugg) with the compatible complete address, and when this option is selected the map zooms-in on the chosen part of the city.
Other than this, there is a descriptionField to be filled with some random text, and then a submitButton to be clicked in order to send the form.
I noticed that if I use actionChains.move_to_element(locationField_sugg).click().perform() to click on the address in the drop-down list, then submitButton throws a StaleElementReference exception, while if I just use locationField_sugg.click() this is not the case, and the code proceeds as it should.
I've been reading through many Q/A about this notorious exception handling, but none of them seemed to explain the reason why this happens in my code.
For me it seems to be related to the behaviour of move_to_element() in combination with the "map wrapper" (?) but I do not understand why, since this function is just supposed to move the mouse in the middle of a given element.
No reload nor other changes in the webpage seem to happen (I verified that if I query the button at the beginning and re-query at the end of the script, I get the same exact instance representation string).
Besides, I query the submitButton right before performing an action on it, and I assume it is properly found since it can be printed.
Below there is a snippet of my code and of the output I get (Note: the code works properly if I use the alternative commented option, but I am curious to understand what I am missing)
CODE SNIPPET
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
websiteUrl = "https://mywebsite"
option = webdriver.ChromeOptions()
option.add_argument("-incognito")
browser = webdriver.Chrome(executable_path="/Applications/chromedriver", options=option)
browser.get(websiteUrl)
actionChains = ActionChains(browser)
# Write and select complete address
locationField = browser.find_element_by_id("location")
print("locationField = ", locationField)
locationField.send_keys("my location")
locationField_sugg = WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.ID, "as-listbox")))
print("locationField_sugg = ", locationField_sugg)
#
# this throws stale element reference exception:
actionChains.move_to_element(locationField_sugg).click().perform()
#
# this does not:
# locationField_sugg.click()
# Write description
descriptionField = browser.find_element_by_id("description")
print("descriptionField = ", descriptionField)
descriptionField.send_keys("my description")
# Submit form
submitButton = WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "span.value")))
print("submitButton = ", submitButton)
actionChains.move_to_element(submitButton).click().perform()
OUTPUT
locationField = <selenium.webdriver.remote.webelement.WebElement (session="e10dc716790c61a0c160599624dd30c6", element="e75539a7-ebd0-4090-9c04-0c4994afe03f")>
locationField_sugg = <selenium.webdriver.remote.webelement.WebElement (session="e10dc716790c61a0c160599624dd30c6", element="53ef0269-aceb-451a-b559-d9e5e7aa7851")>
descriptionField = <selenium.webdriver.remote.webelement.WebElement (session="e10dc716790c61a0c160599624dd30c6", element="dce478de-a23d-4ca2-817c-81ee5ce0c232")>
nowButton = <selenium.webdriver.remote.webelement.WebElement (session="e10dc716790c61a0c160599624dd30c6", element="f2036f1c-d164-4211-a37c-2ee50e5c55c1")>
Traceback (most recent call last):
File "/Users/alice/Desktop/wasteComplaints_selenium.py", line 44, in <module>
actionChains.move_to_element(nowButton).click().perform()
File "/Users/alice/miniconda3/lib/python3.9/site-packages/selenium/webdriver/common/action_chains.py", line 80, in perform
self.w3c_actions.perform()
File "/Users/alice/miniconda3/lib/python3.9/site-packages/selenium/webdriver/common/actions/action_builder.py", line 76, in perform
self.driver.execute(Command.W3C_ACTIONS, enc)
File "/Users/alice/miniconda3/lib/python3.9/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
self.error_handler.check_response(response)
File "/Users/alice/miniconda3/lib/python3.9/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: element is not attached to the page document
(Session info: chrome=92.0.4515.131)
I'm not sure I know why this occurs without debugging it. Maybe the suggested address is disappearing at the moment when focus is removed from the input field? If so, when you insert the address string to the input field and then instantly clicking on the suggested address it works correct, but if after inserting the input address string and the suggested address appears you are applying actionChains.move_to_element this moves the mouse from it's initial position to the suggested address element. So the focus is moved from input field to the mouse cursor. This causes the suggested address to disappear and this is why StaleElementReference exception is thrown.

IF ELSE Statement in python selenium webdriver not executing Else when element is not there

I am using an automation script with selenium web driver. It usually works fine, but sometimes the content on a page is changing.
If the element it is looking for is not there, it is crashing instead of executing the else statement.
I Tried to use another function with try and NoSuchElementException, but I do get another error about NoSuchElementException.
This is the if else statement:
#Look for link text "Stores"
find_store = driver.find_element_by_link_text('Stores')
if find_store:
find_store.click()
else:
driver.get("https://example.com/myaccount")
time.sleep(5)
This is the try statement:
try:
find_store = driver.find_element_by_link_text('Stores')
if find_store.is_displayed():
find_store.click() # this will click the element if it is there
print("Store found, all good.")
except NoSuchElementException:
driver.get("https://example.com/myaccount")
print("Store was not found, visiting base URL to search for store")
time.slep(5)
In the first script I expect it to search for the element. If its not there, then visit the URL so I can search for the element there. But it never opens the URL and I get this error:
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element:
In the second script I expect to look for the element. If the element is not there, it should visit the base url and try again later in the code. However here I get this error:
except NoSuchElementException:
NameError: name 'NoSuchElementException' is not defined
I would like to learn how I can avoid the script crashing and actually execute an alternative action if the element is not there. Thank you.
Use Following :
if len(driver.find_elements_by_link_text('Stores'))> 0:
driver.find_element_by_link_text('Stores').click()
else:
driver.get("https://example.com/myaccount")
time.sleep(5)

Python selenium: Unable to locate the element (//input[#type='file']')

I am trying to upload a file using python automation.
While I try to execute the code below python selenium throws an error.
Even I tried waiting for 10 seconds to avoid synchronisation issues.
driver.execute_script('window.open("https://ocr.space/" , "new window")')
Imagepath = r"C:\User\Desktop\banner.png"
field=driver.find_element_by_xpath('//input[#type="file"]')
field.send_keys(Imagepath)
NoSuchElementException: Message: no such element: Unable to locate
element: {"method":"xpath","selector":"//input[#type="file"]"}
Website url:
https://ocr.space/
HTML snippet:
<div class="span8">
<input type="file" id="imageFile" class="form-control choose valid">
</div>
Changing the code to launch the url with get seems to solve the issue.
from selenium import webdriver
driver = webdriver.Chrome("./chromedriver")
driver.get("https://ocr.space/")
image = r"C:\Users\Thanthu Nair\Desktop\soc360.png"
field=driver.find_element_by_xpath('//input[#type="file"]')
field.send_keys(image)
Also make sure the path provided C:\User\Desktop\banner.png is correct, otherwise you'll get another exception. It is just my assumption that this path might be wrong because usually Desktop folder is inside folder with user's name which is inside the User folder. In this case you've Desktop folder is inside User folder according to the path you've give.
To solve your problem, simply replace new window with _self in the below line of your code :
driver.execute_script('window.open("https://ocr.space/" , "_self")')
Your code is working fine but the reason for an error is, after running your code it launches browser with two tabs nothing but windows and the page will be launched in the second window so you need to switch to that window before uploading an image.
You can use window handles for switching to that window. Below is the code in Java, you can try doing same using Python :
// Using JavaScriptExecutor to launch the browser
JavascriptExecutor jse = (JavascriptExecutor) driver;
jse.executeScript("window.open(\"https://ocr.space/\" , \"new window\")");
// Fetching window handles and switching to the last window
Set<String> handles = driver.getWindowHandles();
for(String handle : handles) {
driver.switchTo().window(handle);
}
// Printing window title
System.out.println(driver.getTitle());
// Uploading an image
WebElement field = driver.findElement(By.xpath("//input[#type='file']"));
String imagePath = "some image";
field.sendKeys(imagePath);
If you use window.open() to launch an URL then it will do two things, first it will launch browser with default window then it will open URL in new tab even if you don't provide new window argument in your JavaScript function. You need to switch to a particular window to perform any operations on it if you choose this way.
To avoid an above problem, simply you can use driver.get(URL) or driver.navigate().to(URL) which launches the browser and navigates to a particular URL in the same launched browser window.
If you want to use JavaScriptExecutor only without doing switching, you can pass _self as a second argument to the JavaScript function like below instead of new window which avoids switching and launches an URL in the same window :
JavascriptExecutor jse = (JavascriptExecutor) driver;
jse.executeScript("window.open(\"https://ocr.space/\" , \"_self\")");
System.out.println(driver.getTitle());
WebElement field = driver.findElement(By.xpath("//input[#type='file']"));
String imagePath = "some image";
field.sendKeys(imagePath);
I hope it helps...
Generally, when the file upload related <input> tag contains the attribute type as file you can invoke send_keys() to populate the relevant text field with a character sequence. However, in your usecase the <input> tag though having type="file" but the class attributes are form-control choose which is as follows:
<input type="file" id="imageFile" class="form-control choose">
So, you may not able to able to send a character sequence invoking send_keys().
In these cases you need to use Auto IT based solutions. You can find a couple of relevant discussion in:
How to upload a file in Selenium with no text box

Script can't parse title from a redirected url

I've written a script in python with selenium to get the header address from a webpage. The url I used within my script gets redirected automatically within seconds. This is where my script encounters an error. I'm pasting a portion of that error to give you an idea.
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host
During handling of the above exception, another exception occurred:
Link to that url which gets redirected to another page
Script I've tried with:
from contextlib import closing
from selenium import webdriver
from selenium.webdriver.support import ui
url = "https://www.rightmove.co.uk/propertyMedia/redirect.html?propertyId=30578943&contentId=1625965454&index=1"
with closing(webdriver.Chrome()) as wd:
wait = ui.WebDriverWait(wd, 10)
wd.get(url)
item = wait.until(lambda driver: driver.find_element_by_css_selector("h1.header_address__title")).text
print(item)
This is the output I would like to have from that page:
Park View Back Road, Locharbriggs, Dumfries, DG1
This is what I see before that error:
You might need to replace
item = wait.until(lambda driver: driver.find_element_by_css_selector("h1.header_address__title")).text
that means wait for specific element to appear in DOM and immediately get its currently visible text (might return empty string)
with
item = wait.until(lambda driver: driver.find_element_by_css_selector("h1.header_address__title").text)
that means wait for specific element and return its visible text once it's NOT AN EMPTY STRING
But IMHO you can simply do
item = driver.find_element_by_css_selector("h1.header_address__title").get_attribute('textContent')
to get the text value even if that text is currently not displayed on page
As for your chromedriver that stops working issue: try to update both Chrome and chromedriver to last versions

Selenium error "Element is no longer attached to the DOM" while scraping data

for i in driver.find_elements_by_class_name("endorse-count"):
try:
i.click()
except:
continue
elem = WebDriverWait(driver, 100).until(EC.presence_of_element_located((By.CLASS_NAME, "dialog-window")))
src = elem.get_attribute("innerHTML")
add_skill(name, src)
WebDriverWait(driver, timeout=10)
I'm getting the following error while running the above code -
selenium.common.exceptions.StaleElementReferenceException: Message: u'Element is no longer attached to the DOM' ; Stacktrace:
at fxdriver.cache.getElementAt (resource://fxdriver/modules/web_element_cache.js:7646)
for line -
src = elem.get_attribute("innerHTML")
I'm running this code on LinkedIn user profile page, after logging in.
I tried putting the following line of code after "i.click()" -
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
But then I see that function "add_skill(name, src)" is not called and none of the code after driver.manage() is called, though for loop and further i.click() work fine.
Selenium is trying to complete actions (such as clicking a button or link) before verifying that the target element has rendered on the page. Selenium can be more patient, but you have to explicitly ask him to be.
For example, if you are testing something that makes an AJAX request, you can try something like this (in Ruby):
# timeout is in seconds
def wait_for_ajax(timeout=x)
time_limit, interval = (Time.now + timeout), 0.5
loop do
break if #driver.execute_script "return jQuery.active == 0"
sleep interval
raise "Wait for AJAX timed out after waiting for #{timeout} seconds" if Time.now > time_limit
end
end
To ensure your tests are fully comprehensive, always make Selenium waits for elements to load before running a task.
I had faced a similar issue and tried refreshing the page before finding that element, and it worked...
driver.navigate().refresh();
Though I couldnt reason out how this worked.
If this works for you as well, please let me know. I just want to learn more about this exception.
you can refer this page to learn about a similar issue
I had a similar problem when trying to execute some javascript (IJavaScripExecutor). I created an IWebElement and passed that to the JSE and that failed for me. When I moved the driver.FindElement(BySelector) into my JSE call, then it worked. (C# code ahead.)
Instead of:
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
IWebElement tableEl = driver.FindElement(selector);
js.ExecuteScript(script, tableEl);
I had to do:
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
js.ExecuteScript(script, driver.FindElement(selector));
You may have to do something similar: move your selector or element creation onto the same line as what you are trying to do. Or, maybe, in your case:
src = driver.find_element_by_class_name("dialog-window").get_attribute("innerHTML")
Upon closer inspection, that's what looks to be your problem, there's a stale web element object when you try to use the get_attribute method.

Categories