Reputation: 481
I'm having a very weird problem with WebDriver for Python. Basically, I have a block of actions which, when run on their own, work perfectly:
driver.find_element_by_id("lbAdd").click()
driver.find_element_by_id("lblAutoAssignAdvId").click()
driver.find_element_by_id("txtCampaignName").clear()
driver.find_element_by_id("txtCampaignName").send_keys("Campaign X")
Select(driver.find_element_by_id("ddlCampaignTypes")).select_by_visible_text("Type 1")
driver.find_element_by_id("btnCampaignSave").click()
but when I try to do it more than one i.e. put it into a loop:
x=1
while (x < 3):
driver.find_element_by_id("lbAdd").click()
driver.find_element_by_id("lblAutoAssignAdvId").click()
driver.find_element_by_id("txtCampaignName").clear()
driver.find_element_by_id("txtCampaignName").send_keys("Campaign X")
Select(driver.find_element_by_id("ddlCampaignTypes")).select_by_visible_text("Type 1")
driver.find_element_by_id("btnCampaignSave").click()
x=x+1
I keep getting this error:
Unable to locate element: {"method":"id","selector":"lblAutoAssignAdvId"}
It seems that the loop is trying to just loop the first action
driver.find_element_by_id("lbAdd").click()
Without going on to the rest of them. Has anyone else experienced this? Thanks
Here is most of the whole thing. The domain is login-protected so I can't include base_url. Other than that everything else is here. I will add another observation too: I tried including the loop earlier, to encompass everything I've pasted below, including the actions to open the browser. What happened was that the program just opened the browser on a blank page a bunch of times. This is what led me to the theory that the loop was just looping through the first action, and not the whole block of them. I thought ti might be a simple syntax error (Something like {} around the block I wanted to loop but alas, no luck on that either)
#Open Firefox
driver = webdriver.Firefox()
#Login in to Database
driver.get(base_url + "/Login.aspx?return=Default.aspx")
driver.find_element_by_id("txtUsername").clear()
driver.find_element_by_id("txtUsername").send_keys(username)
driver.find_element_by_id("txtPassword").clear()
driver.find_element_by_id("txtPassword").send_keys(password)
driver.find_element_by_id("btnLogin").click()
#Waaaaaaait for it
time.sleep(10)
#Select Campaign Table
Select(driver.find_element_by_id("ddlTables")).select_by_visible_text("Campaign")
driver.find_element_by_css_selector("option[value=\"Campaign\"]").click()
#Fill in Camapign Info Fields & Save
driver.find_element_by_id("lbAdd").click()
driver.find_element_by_id("lblAutoAssignAdvId").click()
driver.find_element_by_id("txtCampaignName").clear()
driver.find_element_by_id("txtCampaignName").send_keys("Campaign X")
Select(driver.find_element_by_id("ddlCampaignTypes")).select_by_visible_text("Type 1")
driver.find_element_by_id("btnCampaignSave").click()
Thanks to German for the solution. I guess webdriver needs some periodic breaks for stuff to load before moving on to the next step, hence the failure of the loop. This code worked:
for x in range(0,1000):
driver.find_element_by_id("lbAdd").click()
time.sleep(1)
driver.find_element_by_id("lblAutoAssignAdvId").click()
time.sleep(1)
driver.find_element_by_id("txtCampaignName").clear()
driver.find_element_by_id("txtCampaignName").send_keys("Campaign X")
Select(driver.find_element_by_id("ddlCampaignTypes")).select_by_visible_text("Type 1")
driver.find_element_by_id("btnCampaignSave").click()
time.sleep(2)
Upvotes: 2
Views: 3548
Reputation: 36
Here's an alternative to German's answer (from the comments above) that will greatly reduce the amount of time waiting:
Define this method which waits for the element and returns it as soon as it is available:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
def wait_for_element_by_id(self, element_id, timeout):
try:
return WebDriverWait(driver, timeout).until(EC.presence_of_element_located((By.ID, element_id)))
except TimeoutException:
return None
# or do something else here
Then replace
time.sleep(1)
driver.find_element_by_id("lblAutoAssignAdvId").click()
with
wait_for_element_by_id("lblAutoAssignAdvId", 1).click()
or just
WebDriverWait(driver, 1).until(EC.presence_of_element_located((By.ID, "lblAutoAssignAdvId")))
if you don't care to define the function.
I've found it is generally good practice to wait for conditions rather than to wait for an explicit amount of time and doing so will make your automation must faster. You may run into the case where although the DOM element you are waiting for is present, it may not be displayed yet and trying to call click()
on it will raise an ElementNotVisibleException
. In this case you may want to make use of expected_conditions.visibility_of()
before you click.
Upvotes: 1