I want to find the element using ID, but it seems that the web is sometimes changing the ID.
There are 2 IDs, the first ID will make me possible to enter the email directly, but if the second ID occurs, I need to click a button first before entering my email. I’ve tried using if
and else
logic but it shows some errors.
Here is my code:
@pytest.mark.parametrize('a,b', key0) def test_login_successful(self, a,b): time.sleep(5) if WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'login-find-account-form'))) == True: pass else: # WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'login-form'))) WebDriverWait(driver, 20).until(EC.element_to_be_clickable( (By.XPATH, '//*[@id="login-form"]/div[1]/div/div/div/a'))).click() email_box = '//*[@id="user_name"]' email_input = driver.find_element_by_xpath(email_box) email_input.send_keys(a) submit = driver.find_element_by_xpath('//*[@id="submit_button"]').click()
If you have any idea to do this I would appreciate it.
Thanks
Advertisement
Answer
You could use CSS
selector instead, something like this:
ID_1 = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.CSS, '#login-form,#login-find-account-form')))
The comma works as an OR
operator for CSS, if it doesn’t find the first one then it will attempt to find the second one. It is important that you know that this selector doesn’t work as an AND
meaning that it won’t return two results.
Edit:
There is an easy way to achieve this (not the proper one).
Easy way to achieve what you need:
@pytest.mark.parametrize('a,b', key0) def test_login_successful(self, a,b): time.sleep(5) # The element to be found on the second condition element_of_interest = driver.find_elements_by_xpath('//*[@id="login-form"]/div[1]/div/div/div/a') # element_of_interest is a list, if is empty this condition # is not valid and then it won't attempt to click on it if element_of_interest: element_of_interest[0].click() email_box = '//*[@id="user_name"]' email_input = driver.find_element_by_xpath(email_box) email_input.send_keys(a) submit = driver.find_element_by_xpath('//*[@id="submit_button"]').click()
A few comments on here. it looks like you don’t really need the first condition as you are not doing anything there. Therefore I added just the second condition.
If you take a look at the solution you will find that is using find_elements_by_xpath
(in plural) instead of find_element_by_xpath
the reason of this is because if the element is not found then find_elments_by_xpath
will return an empty list.
if you need to wait 10 seconds until one of these element appear, then you could do something like this:
@pytest.mark.parametrize('a,b', key0) def test_login_successful(self, a,b): time.sleep(5) # The element to be found on the second condition element1 = driver.find_elements_by_id('login-find-account-form') element2 = driver.find_elements_by_xpath('//*[@id="login-form"]/div[1]/div/div/div/a') # Waits up to 10 seconds to find either element 1 or 2 retries = 10 while not element1 or not element2: time.sleep(1) retries -= 1 if retries < 0: raise Exception("some exception in here") element1 = driver.find_elements_by_id('login-find-account-form') element2 = driver.find_elements_by_xpath('//*[@id="login-form"]/div[1]/div/div/div/a') # element2 is a list, if is empty this condition # is not valid and then it won't attempt to click on it if element2: element2[0].click() email_box = '//*[@id="user_name"]' email_input = driver.find_element_by_xpath(email_box) email_input.send_keys(a) submit = driver.find_element_by_xpath('//*[@id="submit_button"]').click()
There is a better implementation for this I suggest to take a look to the Page object model documentation for selenium, you could benefit a lot from that design pattern: https://www.selenium.dev/documentation/guidelines/page_object_models/