.click() and .send_keys() methods not being recognized Python Selenium - python

I'm using Selenium in Python to click a text entry field and write some text into it. Neither the .click() nor .send_keys() methods are being recognized. Can someone help with this?
Also, is there a way to stop Selenium from printing to the console automatically? My program is console-based and Selenium is writing things to an input() that I gave because it prints to the console.
Here is a code snippet:
url = "https://weather.com/weather/today/l/69ef4b6e85ca2de422bea7adf090b06c1516c53e3c4302a01b00ba763d49be65"
browser = webdriver.Edge("Web Scrapers\msedgedriver.exe")
browser.get(url)
textbox = browser.find_element_by_id("LocationSearch_input")
textbox.click()
textbox.send_keys(zipcode)
textbox.send_keys(Keys.ENTER)

you could try the explicitWait hope this will work for you
WebDriverWait(browser,10).until(EC.element_to_be_clickable((By.XPATH,"//input[#type='text']"))).send_keys("20874",Keys.RETURN)

I would suggest you to do it with explicit wait.
I am giving this answer, cause none of the answer's are really using Explicit waits with ID attribute :
driver.maximize_window()
driver.implicitly_wait(30)
driver.get("https://weather.com/weather/today/l/69ef4b6e85ca2de422bea7adf090b06c1516c53e3c4302a01b00ba763d49be65")
wait = WebDriverWait(driver, 10)
wait.until(EC.element_to_be_clickable((By.ID, "LocationSearch_input"))).send_keys('60007' + Keys.RETURN)
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div[class^='CurrentConditions--dataWrapperInner']"))).click()
Imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

From what I can tell, everything is working fine here, however you are pointing your textbox variable to the wrong HTML element.
LocationSearch_input is a hidden label that isn't directly attached to the searchbox. I would try pointing it to
SearchInput--SearchInput--M7lKl SearchInput--enableSearchIcon--1Ugsx, one of the parent elements.

At least testing in firefox, it's a timing thing. The click works. The error of element not interactable (not reachable by keyboard) comes off of the send_keys line. If we wait after the click(), the element becomes reachable by keyboard.
The following could probably be refined (really don't like sleep, but sometimes it works), but works for me:
url = "https://weather.com/weather/today/l/69ef4b6e85ca2de422bea7adf090b06c1516c53e3c4302a01b00ba763d49be65"
browser.get(url)
browser.find_element_by_id('LocationSearch_input').click()
time.sleep(5)
browser.find_element_by_id('LocationSearch_input').send_keys('Boston')
At that point you need to click on whichever of the 10 options is what you really want.

Related

Error: Stale element is not attached to the page, after try statement

I have the following try statement, that basically finds a button that resets the current page I am in. In summary the page reloads,
try:
reset_button = D.find_element(By.XPATH,"//button[starts-with(#class,'resetBtn rightActionBarBtn ng-star-inserted')]")
reset_button.click()
D.implicitly_wait(5)
ok_reset_botton = D.find_element(By.ID,'okButton')
D.implicitly_wait(5)
print(ok_reset_botton)
ok_reset_botton.click()
D.implicitly_wait(5)
# Trying to reset current worksheet
except:
pass
print(D.current_url)
grupao_ab = D.find_element(By.XPATH,'//descendant::div[#class="slicer-restatement"][1]')
D.implicitly_wait(5)
grupao_ab.click()
The weird thing is every time that try statement get executed, I get the following log of error
selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: element is not attached to the page document
Which happens in the a following line of code according to the log
grupao_ab.click()
When I took a look at the reason given by selenium it say it is because the element is no longer on the given DOM, but the element grupao_ab, is not even being defined in that page so why it is giving me that error? If any extra information is needed just comment.
First of all, StaleElementReferenceException means that the web element reference you trying to access is no more valid. This normally happens after the page was reloaded. This is exactly what happens here.
What happened is as following: you clicked on reset button and immediately after that you collecting the grupao_ab element and shortly after that trying to click it. But between the moment you located the grupao_ab element with grupao_ab = D.find_element(By.XPATH,'//descendant::div[#class="slicer-restatement"][1]') and the line where you trying to click it, reloading started. So that previously collected web element, that actually is a reference to a physical element on the DOM, no more pointing to that web element.
What you can to do here is: after clicking on the refresh button set a short delay so that refreshing will start and after that wait for grupao_ab element to become clickable. WebDriverWait expected_conditions explicit waits should be used for that.
Also, you should understand that D.implicitly_wait(5) is not a pause command. It sets the timeout for find_element and find_elements methods to wait for presence of the searching element. Normally we never set this timeout at all since it's better to use WebDriverWait expected_conditions explicit waits, not implicitly_wait implicitly waits. And you should never mix these two types of waits.
And even if you want to set implicitly_wait to some value normally no need to set it again, this setting is applied to the entire driver session.
Please try changing your code as following:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 20)
try:
wait.until(EC.element_to_be_clickable((By.XPATH, "//button[starts-with(#class,'resetBtn rightActionBarBtn ng-star-inserted')]"))).click()
wait.until(EC.element_to_be_clickable((By.ID, "okButton"))).click()
print(ok_reset_botton)
time.sleep(0.5) # a short pause to make reloading started
except:
pass
print(D.current_url)
#wait for the element on refreshed page to become clickable
wait.until(EC.element_to_be_clickable((By.XPATH, '//descendant::div[#class="slicer-restatement"][1]'))).click()

Selenium: How to click a label

I'm trying to click this object with selenium (pyhton) using the code:
driver.find_element_by_('publicForm_customFields').click()
But I'm receiving this error:
id="publicForm_customFields" tabindex="0" type="radio" value="value"> is not clickable at point (480, 98). Other element would receive the click: value
"Other element would receive the click" means that you have another element over your element. There are a couple of options to get around this:
Try to find another element above in the DOM tree and click on it.
Use js click, you need to write a function like this:
def js_click(self, element):
element = driver.find_element_by_id("myid")
driver.execute_script("arguments[0].click();", element)
js script will click on the element even if it is intersected by another
There might be two possibilities:
1- Your locator to find element might be wrong or not unique.
2- You need to apply explicit wait till element is ready [load successfully] to be clickable.
Hope above possibilities might help you out, Or you share the link of the sight so I might debug correctly.
You are not passing any find method to the driver. Try refactoring the code to:
driver.find_element_by_id('publicForm_customFields').click()
Also, try using some sort of wait so the driver does not click before the page/element is loaded.
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
wait.until(EC.element_to_be_clickable((By.ID, 'publicForm_customFields'))).click()

Selenium can't find radio button

I'm automating the testing process we have in servicenow, but I can't select a radio button in a form. I've used multiple selectors to fix this but nothing has worked.
ServiceNow HTML:
these are the selectors I have used:
driver.find_element_by_css_selector('input[value="Add"]').click()
driver.find_element_by_xpath('//input[#value="Add"]').click()
None of these have worked and there's not iframe tag in the body to swicth to. Thank you!
The following are a few reasons why I couldn't find an element.
Currently on the wrong window/frame.
Synchronization issues - script is looking before the element has loaded. Solved with explicit waits. (see #cruisepandey's answer)
The element was different due to mobile vs desktop. Should be noticable if you disable headless. I advise dumping the HTML from selenium just to be sure.
When I'm unsure I'll start trying to find it's parent element, walking up the scope until I'm able to get a proper element. That's usually where I figure out what the problem was.
Can I find the parent label element?
Can I find the grandparent div element?
Can I find the greatgrandparent fieldset?
And so on...
You can try with expicit wait :
wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.XPATH, "//input[#value='Add']")))
element.click()
Imports :
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
Update 1 :
wait = WebDriverWait(driver, 10)
radioBtn = wait.until(EC.element_to_be_clickable((By.XPATH, "//input[#value='Add']")))
driver.execute_script("arguments[0].checked = true;", radioBtn)

How to handle Python Selenium recurring loading modals and stay DRY?

I'm using python 3.6 and selenium 3.8.1, Chrome browser to simulate users entering an order. The app we use has a particularly frustrating implementation for automation - a loading modal will pop up whenever a filter for a product is loading, but it does not truly cover elements underneath it. Additionally, load time fluctuates wildly, but with an upper bound. If I don't use excessive sleep statements, selenium will either start clicking wildly before the correct objects are loaded or clicks on the element but, of course, hits the loading modal. (Fun side note, the loading modal only fills the screen view, so selenium is also able to interact with items below the fold. :P)
To get around this:
def kill_evil_loading_modal(self):
# i pause for a second to make sure the loader has a chance to pop
time.sleep(1)
# pulling locator type and from another file: ("id","locator_id")
loading_modal = ProductsLocators.loading_modal_selector
# call a function that returns true/false for object if exists
check_for_evil = self.is_element_exist(*loading_modal)
while check_for_evil == True:
check_for_evil = self.is_element_exist(*loading_modal)
This works great! Where I had a ton of evil time.sleep(x) statements to avoid the loading modal, I'm now catching it and waiting until it's gone to move forward.
If I only had to deal with that two or three times, I would move on. Sadly, this loading modal hits after every click ... so this is what my main script looks like now:
new_quote02_obj.edit_quote_job(**data)
new_quote03_obj.kill_evil_loading_modal()
new_quote03_obj.click_product_dropdown()
new_quote03_obj.kill_evil_loading_modal()
new_quote03_obj.click_product_dropdown_link()
new_quote03_obj.kill_evil_loading_modal()
new_quote03_obj.select_category_dropdown(scenario_data['category_name'])
new_quote03_obj.kill_evil_loading_modal()
new_quote03_obj.select_market_dropdown(scenario_data['local_sales_market'])
new_quote03_obj.kill_evil_loading_modal()
new_quote03_obj.add_products_job(scenario_data['product_list_original'])
new_quote03_obj.kill_evil_loading_modal()
new_quote03_obj.click_done_btn()
new_quote03_obj.kill_evil_loading_modal()
new_quote03_obj.next_btn_page()
How can I refactor to stay DRY?
If you want to wait until modal disappeared and avoid using time.sleep() you can try ExplicitWait:
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait as wait
wait(driver, 10).until_not(EC.visibility_of_element_located(("id", "locator_id")))
or
wait(driver, 10).until(EC.invisibility_of_element_located(("id", "locator_id")))
This should allow you to wait up to 10 seconds (increase delay if needed) until element with specified selector ("id", "locator_id") will become invisible
If modal appears after each click you can implement your own click method, like
def click_n_wait(by, value, timeout=10):
wait(driver, timeout).until(EC.element_to_be_clickable((by, value))).click()
wait(driver, timeout).until(EC.invisibility_of_element_located(("id", "locator_id")))
and use it as
click_n_wait("id", "button_id")
As you mentioned in your question a loading modal will pop up whenever a filter for a product is loading irespective of the loader cover elements underneath it or not you can simply wait for the next intended element with which you want to interact with. Following this approach you can completely get rid of the function kill_evil_loading_modal() which looks to me as a overhead. As a replacement to kill_evil_loading_modal() function you have to invoke WebDriverWait() method along with proper expected_conditions as required as follows :
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# other code
WebDriverWait(driver, 2).until(EC.element_to_be_clickable((By.XPATH, "xpath_of_element_A"))).click()
WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.XPATH, "xpath_of_element_B"))).click()
WebDriverWait(driver, 3).until(EC.element_to_be_clickable((By.XPATH, "xpath_of_element_C"))).click()

Send keys without specifying element in python selenium webdriver

I have a page whose source code is not available, but there is a input box where cursor is blinking.
Can i write something into the text box without finding the element. I mean, some way where send key can automatically look for focused inputbox and type input to it.
My code does not work obviously
driver.send_keys("testdata")
Solved it
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(self.driver)
actions.send_keys('dummydata')
actions.perform()
If you get error about 'self' in this code:
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(self.driver)
actions.send_keys('dummydata')
actions.perform()
just use:
actions = ActionChains(driver)
I don't have comment rights that's why I put this as answer
Edit: Added this enhancement as a comment on the original answer.
This worked for me:
driver.find_element_by_tag_name('body').send_keys(' ')
(Which I used to use a space character to scroll through a page)

Categories

Resources