I am writing a test, where i create an ip pool for the application , and then in the next step, delete it.
The function is as follows:
def remove_passed_ip(self, ip):
""" Find and ip and delete it
Args:
ip (str): the name of ip to click and delete
Returns:
webelement
"""
index = -1
try:
ipDelBtnList = self.wait_for_presence_of_all_elements(self.onboarding_select_address_pools_delete_btn_list)
ipList = self.wait_for_presence_of_all_elements(self.onboarding_select_address_pools_list)
time.sleep(5)
except:
self.log.info("dasdda")
else:
for ips in ipList:
index +=1
temp = ips.text.split('/')
if str(ip)==str(temp[0]):
ipHandle = ipDelBtnList[index]
time.sleep(5)
ipHandle.click()
time.sleep(15)
The delete action works fine and the created ip is deleted, but after this when the test ends, it gives the error as
Message: Element is no longer attached to the DOM
Please provide with any pointers to resolve this issue. If any other clarification is required regarding the question, please let me know.
This is because after the delete the DOM changes and all 'references' that Selenium has with the browser are lost.
To solve this, you need to get the element/s again from the page after the previous one is deleted (or any other action that determines the DOM to be changed).
Related
So I have this project: It is a website with multiple WebElements on it. I find those WebElements by their class name (they all have the same one obviously) and then iterate through them to click on each of them. After clicking on them I have to click on another button "next". Some of them then open a website in a new tab (others don't). I then immediately close the newly opened tab and try to iterate through the next element when I get the StaleElementReferenceException.
Don't get me wrong here, I know what a StaleElementReferenceException is, I just don't know why it occurs. The DOM of the initial website doesn't seem to change and more importantly: The WebElement I'm trying to reach in the next iteration is still known so I can print it out, but not click on it.
I have tried working around this issue by creating a new class CustomElement to permanently "save" the found WebElements to be able to reach them after the DOM has changed but that also doesn't seem to be working.
Whatever here's some code for you guys:
def myMethod():
driver.get("https://initialwebsite.com")
time.sleep(1)
scrollToBottom() #custom Method to scroll to the bottom of the website to make sure I find all webelemnts
ways = driver.find_elements_by_class_name("sc-dYzWWc")
waysCounter = 1
for way in ways:
# print("clicking: " + str(way)) ##this will get executed even if there was a new tab opened in the previous iteration....
driver.execute_script("arguments[0].click();", way)
# print("clicked: " + str(way)) ##...but this won't get executed
try:
text = driver.find_element_by_xpath("/html/body/div[1]/div/div[2]/div/div[1]/div/div[7]/div[2]/div[" + str(entryWaysCounter) + "]/div[1]/div/div/div[1]").text
except:
waysCounter += 1
text = driver.find_element_by_xpath("/html/body/div[1]/div/div[2]/div/div[1]/div/div[7]/div[2]/div[" + str(entryWaysCounter) + "]/div[1]/div/div/div[1]").text
methode = None
#big bunch of if and else statements to give methode a specific number based on what text reads
print(methode)
weiterButton = driver.find_element_by_xpath(
"/html/body/div[1]/div/div[2]/div/div[1]/div/div[7]/div[2]/div[" + str(
entryWaysCounter) + "]/div[2]/div/div/div/div/div/div[2]/button[2]")
try:
driver.execute_script("arguments[0].click();", weiterButton)
except:
pass
if (methode == 19):
time.sleep(0.2)
try:
driver.switch_to.window(driver.window_handles[1])
driver.close()
time.sleep(0.5)
driver.switch_to.window(driver.window_handles[0])
time.sleep(0.5)
except:
pass
waysCounter += 1
time.sleep(0.5)
And for those who are curious here's the workaround class I set up:
class CustomElement:
def __init__(self, text, id, location):
self.text = text
self.id = id
self.location = location
def __str__(self):
return str(str(self.text) + " \t" + str(self.id) + " \t" + str(self.location))
def storeWebElements(seleniumElements):
result = []
for elem in seleniumElements:
result.append(CustomElement(elem.text, elem.id, elem.location))
return result
I tried then working with the id and "re-finding" the WebElement ("way") by id but apparently the id that gets saved is a completely different id.
So what can I say I really tried my best, searched nearly every forum but didn't come up with a good soluation, I really hope you got one for me :)
Thanks!
Are you crawling links? If so then you want to save the destination, not the element.
Otherwise you could force the link to open in a new window (perhaps like https://stackoverflow.com/a/19152396/1387701), switch to that wind, parse the page, close the page and still have the original window open.
I have the following program in python 2.7 to launch a web page and do an activity. I have parsed the HTML DOM of a web page and was able to make this work when launched via PyCharm IDE or via an Exe created with PyInstaller or via a bat file that wraps an Exe.
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.common.by import By
import time
import sys
def main():
try:
mgmt_console_server_name = sys.argv[1]
user_session_to_terminate = sys.argv[2]
# Phase 1: Launch a browser and open the management console page
# Using IE to access web
driver = webdriver.Ie('IEDriverServer32.exe')
return_code = 0
# Open the management console page.
driver.get("http://" + mgmt_console_server_name + ":8183/mgmt/security/login.html")
# Phase 2: Enter the LDAP credentials and hit enter
# Select the username and the password text boxes
username = driver.find_element_by_class_name('gwt-TextBox')
password = driver.find_element_by_class_name('gwt-PasswordTextBox')
# Send username information
username.send_keys('sa-tcnxbot-gds')
# Send password information
password.send_keys('zzzzzzzzz')
# Login to Teamcenter management console with the above set credentials.
signinbutton = driver.find_element_by_class_name('GFHWBTICII')
signinbutton.click()
# Phase 3: Click on the 'TcServer' component to reach the UI where we can find and kill the user sessions.
# We are now in the main page. We need to wait till we find an XML element by the ID
# 'Console'. This signifies that the page has loaded. And we wait at the maximum of 20 seconds.
WebDriverWait(driver, 20).until(ec.presence_of_element_located((By.ID, "Console")))
# We find the XML element whose InnerHTML is 'Tc Server'.
tcserverelement = driver.find_element_by_xpath("//div[text()='Tc Server']")
# We then find its immediate parent, which is the clickable element and finally click it.
tcserverclickableelement = tcserverelement.find_element_by_xpath('..')
tcserverclickableelement.click()
# Phase 4: We enter the user id whose session is to be terminated in the search field and hit the search.
WebDriverWait(driver, 20).until(ec.presence_of_element_located((By.XPATH, "//div[text()='Assigned User:']")))
# We are at the HTML element that has the text 'Assigned User:'
assigneduserelement = driver.find_element_by_xpath("//div[text()='Assigned User:']")
# print(assigneduserelement)
# An immediate parent of this HTML element is the TD element.
assigneduserelementparent = assigneduserelement.find_element_by_xpath('..')
# print(assigneduserelementparent)
# An immediate sibling of this TD HTML element is another TD element which contains INPUT HTML element.
assigneduserelementparent.find_element_by_xpath("//TD/following-sibling::TD")
# print(assigneduserelementparent_sibling)
# assignedusertextbox = assigneduserelementparent_sibling.find_elements_by_xpath(".//*")
# print(assignedusertextbox[0])
# Send username information
driver.find_elements_by_class_name('gwt-TextBox')[1].send_keys(user_session_to_terminate)
search_button = driver.find_element_by_xpath("//button[text()='Search']")
# We then find its immediate parent, which is the Search button and finally click it.
search_button.click()
time.sleep(4)
# There would be three cases here.
# Case 1: The input user has no sessions on his name.
# Case 2: The input user has one session on his name.
# Case 3: The input user has more than one sessions on his name.
# Case 1: This should be one of the table header elements containing the list of servers assigned to the user.
assigned_server_rows = driver.find_elements_by_xpath("//div[text()='Assigned']")
if len(assigned_server_rows) == 0:
return_code = 4
else:
# Case 2 and Case 3:
for each_assigned_server in assigned_server_rows:
print each_assigned_server.tag_name
assigned_server_parent_td = each_assigned_server.find_element_by_xpath('..') # td
print assigned_server_parent_td.tag_name
assigned_server_parent_td.click()
# assigned_server_parent_tr = assigned_server_parent_td.find_element_by_xpath('..') #tr
# print assigned_server_parent_tr.tag_name
# assigned_server_parent_tr.click()
shutdown_button_element = driver.find_element_by_xpath("//button[text()='Shutdown Server']")
shutdown_button_element.click()
time.sleep(2)
main_page = driver.window_handles[0]
alert_window = driver.switch_to.alert
alert_window.accept()
driver.switch_to.window(main_page)
except:
return_code = 1
finally:
driver.quit()
if return_code == 0:
print('=> Successfully terminated sessions for the user ' + user_session_to_terminate)
elif return_code == 4:
print('=> There are no sessions assigned to the user ' + user_session_to_terminate)
else:
print('=> An error occurred while tying to terminate sessions for the user ' + user_session_to_terminate)
return return_code
if __name__ == "__main__":
main()
But as part of Azure Chatbot, when we tried to plug it in NodeJS code, we are seeing this problem. Is this really some problem with my code?. How should I correctly correct it.
I read on scoping of python but in this case everything is inside the main function and got to know that python try-catches do not add new block scope. So my expectation is that everything should have worked. Please let me know what I am missing.
PyAutomationScript Error:Error: Command failed: C:\tcnxbot_data\automated_script\auto.exe localhost pdittaka
[9904] Failed to execute script auto
Traceback (most recent call last):
File "auto.py", line 131, in <module>
File "auto.py", line 118, in main
UnboundLocalError: local variable 'driver' referenced before assignment
Thanks,
Pavan.
This might be an issue with the line inside the finally function. What you are essentially saying is irrespective of whether there is an error or not (whether it goes into try or except) execute driver.quit().
But if your code runs into and error inside the try before or during the driver is defined, it goes into the except, then the finally where you are attempting to close the driver. However, this is not possible because the driver hasn't been defined yet. What you can do is add this at the beginning of the main function:
driver = None
and change the code inside the finally: to be this:
if driver is not None:
driver.quit()
The idea is to create a bot to read message from a chat, and all message are in a ul>li(not neccesary to write message), e.g
<ul class="message-list">
<li class="message">
Hello There
</li>
<li class="message">
Hello
</li>
</ul>
I found expected_conditions from Selenium, to handle if element exist/found, but it's always true, because there is a message but I don't want to continue the code if a message was already in chat, that's not the main idea. And after that, I found EventFiringWebDriver, AbstractEventListener and nothing.
from selenium import webdriver as wb
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support.events import EventFiringWebDriver, AbstractEventListener
from selenium.webdriver.common.keys import Keys
class MyListener(AbstractEventListener):
def before_navigate_to(self, url, driver):
print("Before navigate to %s" % url)
def after_navigate_to(self, url, driver):
print("After navigate to %s" % url)
driver = wb.Chrome('C:\python\selenium\chromedriver.exe')
wait = WebDriverWait(driver, 300)
# ef_driver = EventFiringWebDriver(driver, MyListener())
driver.implicitly_wait(15)
driver.get('https://socket.io/demos/chat/')
driver.switch_to.frame(driver.find_element_by_class_name('iframe-class'))
InputName = driver.find_element_by_xpath('/html/body/ul/li[2]/div/input')
InputName.send_keys('MyNameIsJeff')
InputName.send_keys(Keys.ENTER)
I think you can write a loop and inside the loop, get the number of "li"s inside the desired "ul". if the number of "li"s increased, you have a new message.
# get list of li inside of ul
ulList = driver.find_elements_by_xpath("//ul[#class='message-list']")
lenOfLis = len(ulList)
# wait for new message...
while (true):
ulList = driver.find_elements_by_xpath("//ul[#class='message-list']")
if (len(ulList) > lenOfLis): # you have new message
for (i in range(len(ulList)-lenOfLis)):
yourLastMessage = ulList[-1-(i-1)]
# you can do whatever you want with your last messages
LenOfLis = len(ulList) # update length of ul
# wait 15 seconds
sleep(5)
You can do some kind of listener in a loop that checks the text of your message elements to determine whether or not it is a new message, you just have to determine a set time frame that you want to wait between 'checks' -- 10 seconds, 30 seconds, 1 minute, etc.
I don't think you need an event listener for this. You can just grab the latest message, then keep checking it to see if it's different than the previous value.
from time import sleep
# get list of currently displayed messages
messages = driver.find_elements_by_xpath("//li[#class='message']")
# get text from most recent message
# depending on element order on the page, might need to do messages.last
last_message_text = messages[0].text
# do something here to trigger a new message coming in?
# wait for last_message_text to be something different
while (true):
# get list of currently displayed messages
messages = driver.find_elements_by_xpath("//li[#class='message']")
# new message has arrived if we hit this statement
if (messages[0].text != last_message_text) break;
# wait 15 seconds
sleep(15)
This example will grab the list of messages currently displayed. Then, it grabs the last received message. The code enters a loop and re-fetches the list of messages, checks the last received message, and compares its text to the last message value we saved earlier.
If the last received message is different from saved value, the loop will break, meaning a new message has arrived.
There's a few unclear things here -- the most recent message may be either the first element, or last element, in messages list. The other issue -- are you doing something on your end to trigger a new message appearing? Such as InputName.send_keys('MyNameIsJeff')? If this is the case, the order of the code may need to change a bit.
Lastly -- in your comments, you mentioned sometimes the ul is empty. I'm not really sure what this means, but if some messages are appearing and there is no HTML on the page for them, then this solution won't really work.
I'm trying to create a program that signs up for instagram with a new account, I've got the emails and the rest generated, when I go ahead and send_keys to the appropriate fields, it does it just fine. I wanted to implement a retry function, which would clear the email field and try with a different mail. However this does not work, even though send_keys to it worked previously? Snippet of my code below.
driver.get('https://www.instagram.com')
driver.find_element_by_xpath("//*[contains(#aria-label,'Email')]").send_keys(mail)
driver.find_element_by_xpath("//*[contains(#aria-label,'Full')]").send_keys(name + lastname)
driver.find_element_by_xpath("//*[contains(#aria-label,'User')]").send_keys(namae+lastonamae+pamae2)
driver.find_element_by_xpath("//*[contains(#aria-label,'Password')]").send_keys(password)
driver.find_element_by_xpath("//*[contains(#type,'submit')]").click()
This attempts to create a new account with the appropriate credentials, however when it fails, I want it to try to look for an element that is only present when it fails, and if it finds that, it should clear the email field and retry with a different one. Code below.
driver.find_element_by_xpath('//*[#id="react-root"]/section/main/article/div[2]/div[1]/div/form/div[3]/div/div[2]/span') #this looks for the element only present on the fail page
driver.find_element_by_xpath("//*[contains(#aria-label,'Email')]").clear()
driver.find_element_by_xpath("//*[contains(#aria-label,'Email')]").send_keys(mail2)
It doesn't clear the field, but doesn't raise an error either. It then proceeds to type the 2nd email with no problems. I appreciate any help on the matter.
EDIT: Posting a bigger chunk of the code.
def signup():
driver.get('https://www.instagram.com')
time.sleep(7)
if trycounter < 3: #this is almost always true, just a failsafe
driver.find_element_by_xpath("//*[contains(#aria-label,'Email')]").send_keys(mail1)
driver.find_element_by_xpath("//*[contains(#aria-label,'Full')]").send_keys(name + ' ' + lastname)
driver.find_element_by_xpath("//*[contains(#aria-label,'User')]").send_keys(name+lastname+extension)
driver.find_element_by_xpath("//*[contains(#aria-label,'Password')]").send_keys(password)
driver.find_element_by_xpath("//*[contains(#type,'submit')]").click()
time.sleep(7)
try: #this only executes if a popup that wants you to confirm your age pops up
driver.find_element_by_xpath('//*[#id="igCoreRadioButtonageRadioabove_18"]').click()
driver.find_element_by_xpath('/html/body/div[3]/div/div[3]/div/button').click()
time.sleep(5)
except:
pass
try:
randomgen() #generates the mail,password and name
driver.find_element_by_xpath('//*[#id="react-root"]/section/main/article/div[2]/div[1]/div/form/div[3]/div/div[2]/span')
time.sleep(1)
driver.find_element_by_xpath("//*[contains(#aria-label,'Email')]").clear()
time.sleep(1)
driver.find_element_by_xpath("//*[contains(#aria-label,'Email')]").send_keys(mail2)
driver.find_element_by_xpath("//*[contains(#aria-label,'User')]").send_keys(username)
driver.find_element_by_xpath("//*[contains(#type,'submit')]").click()
time.sleep(7)
You can use following code as alternative for clear method:
from selenium.webdriver.common.keys import Keys
email_element = driver.find_element_by_xpath("//*[contains(#aria-label,'Email')]")
email_element.send_keys(Keys.CONTROL, 'a')
email_element.send_keys(mail1)
fullname_element = driver.find_element_by_xpath("//*[contains(#aria-label,'Full')]")
fullname_element.send_keys(Keys.CONTROL, 'a')
fullname_element.send_keys(name + ' ' + lastname)
# do it for other field as well
So this will definitely work as a workaround. I just tried it on instagram. Although there was no field with an aria label called Email for me. It was aria-label "Mobile Number or Email" for me.
driver.execute_script("$(\"input[aria-label='Email']"\").value = '';");
I will keep looking at it to see why the clear command didn't work though.
You can try something like this to delete mail1.
driver.find_element_by_xpath("//*[contains(#aria-label,'Email')]").send_keys(Keys.chord(Keys.CONTROL,"a"))
driver.find_element_by_xpath("//*[contains(#aria-label,'Email')]").send_keys(Keys.DELETE)
driver.find_element_by_xpath("//*[contains(#aria-label,'Email')]").send_keys(mail2)
I am hoping someone may be able to point out the error I am making; it is probably very straight forward!
What I am trying to do is run some code previous what I have shown below, then when I get to this point I need to get it to hold for the 600 seconds and then reload the download page:
try:
# Clicks OK in Download Requested window
driver.implicitly_wait(10)
ClickOkay = driver.find_element_by_css_selector("._42ft._42fu.layerCancel.uiOverlayButton.selected._42g-._42gy")
ClickOkay.click()
except:
print("error 2")
# Wait Time
# time.sleep(600) # Allow Facebook to compile the archive
# Reload Settings page
GoToURL('https://www.facebook.com/' + 'settings', driver)
# Goes back through to Download page
link = driver.find_element_by_link_text('Download a copy')
link.click()
At this point if the archive has finished being created then the button changes from Start Archive to Download Archive. However depending on the size of the profile the time taken to compile the archive varies. so what i was attempting (with the code below and a couple of attempts with the if and while arguments) was to get it to check if the button exists and if not go back and wait 300 seconds before trying again. Once the button appears it will then continue on to download using additional code.
try:
print("Checking if button exists")
DownloadArchive = driver.find_elements_by_css_selector("._42ft._42fu.selected._42gz._42gy")
print(DownloadArchive.count())
if(DownloadArchive.count() > 0):
print("button exists")
else:
print("button does not exist")
# Button to initiate password entry popup window
#driver.implicitly_wait(10)
#while (DownloadArchive = driver.find_element_by_css_selector("._42ft._42fu.selected._42gz._42gy")):
# if (DownloadArchive = True):
# DownloadArchive.click()
# print("wait")
# else:time.sleep(300)
Thanks in advance, James
You're mixing the assignment operator (=) with the equal operator (==).
So it should be:
while (DownloadArchive == driver.find_element_by_css_selector("._42ft._42fu.selected._42gz._42gy")):
if (DownloadArchive == True):
Or just:
while DownloadArchive == driver.find_element_by_css_selector("._42ft._42fu.selected._42gz._42gy"):
Hope it helps!