selenium unable to locate input object in iframe - python

My goal is to automatically fill out the form at https://mail.protonmail.com/create/new?language=en using selenium.
I am able to fill in a password but I am still struggling to fill a username in.
The username field is inside an iframe. The HTML for said iframe is the following:
<iframe title="Registration form" scrolling="no" class="top" data-name="top" sandbox="allow-scripts allow-same-origin allow-popups allow-top-navigation" src="https://secure.protonmail.com/abusev2.iframe.html?name=top">
#document
<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
<div id="app" data-name="top">
<div class="formList">
<div class="field field-usernameInput">
<div class="group-username">
<label for="username">Choose username</label>
<input placeholder="Choose username" required="" name="username" messages="[object Object]" iframename="top" pattern=".{1,40}" id="username" class="input">
<div class="field field-select"><label for="domain">Select a domain</label><div class="select-mask"><select component="domains" name="domain" id="domain"><option value="protonmail.com">protonmail.com</option><option value="protonmail.ch">protonmail.ch</option></select><i>▼</i></div></div>
</div>
</div>
</div>
</div>
</iframe>
In order to switch selenium to the iframe I used
driver.switch_to.frame(driver.find_element_by_tag_name('iframe'))
That does not give me an exception.
However I am now unable to find the input object to do .send_keys().
I've tried
driver.find_element_by_id('username')
driver.find_element_by_class_name('input')
driver.find_element_by_tag_name('input')
driver.find_element_by_name('username')
driver.find_element_by_xpath('//input[#id="username"]')
driver.find_element_by_css_selector('#username')
but every time I get the same exception:
selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: *whatever I searched for*
When I tried
driver.find_element_by_id('app') #first div object in the iframe
I didn't get any exceptions.
If I don't switch to the iframe I cannot find this which leads me to believe that switching to the iframe worked.
I am however unable to find any of the other div objects with any of the mentioned methods.
To conclude: I have no idea why I am unable to find the input object with selenium to automatically put in a username afterwords and would greatly appreciate ideas on what I am doing wrong.

That would get the first iframe use a more targeted xpath.
driver.switch_to.frame(driver.find_element_by_xpath("(//iframe[#title='Registration form'])[1]"))
So to target the first iframe and wait for the username and send a key to it.
wait = WebDriverWait(driver, 10)
driver.get('https://mail.protonmail.com/create/new?language=en')
wait.until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,"(//iframe[#title='Registration form'])[1]")))
wait.until(EC.element_to_be_clickable((By.XPATH,"(//div[#class='group-username'])[1]/input"))).send_keys('a')
Import
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

Related

Selenium: Cant select/click button in modal. Modal appears after try:except

I am running into an issue where I am unable to select a button in the modal. The modal only appears when running my selenium script and does not appear when manually traversing the webpage. Interestingly enough the modal does consistently appear when my script runs try/except as shown below, but the program advances to the next step and is not able to get the element to click. Trying to get the element after this try:except also fails. The modal does not show up otherwise.
try:
element=driver.find_element_by_xpath('//div[contains(#href,"appointment")]')
element.click()
print("Looking for the modal button")
except NoSuchElementException:
print("*****Did not find the modal button")
I have tried the following with no success:
WebDriverWait(driver, 300).until(EC.element_to_be_clickable((By.XPATH, "//button[#class='btn btn-default btn-ok'][#data-dismiss='modal']"))).click() This simply times out.
Different methods to get the element:
element = driver.find_element_by_class_name("btn.btn-default.bt-ok")
and
element=driver.find_element_by_xpath('//div[contains(#href,"appointment")]')
In both cases the element is not found.
Tried running with cookies, and the result is the same.
Tried various sleep timers, but the modal does not appear during this sleep period
Here is the HTML Snippet of this section. Not sure if <div class =schedule-modal is a hint that there is some logic behind how/when the modal shows up?
<div class="schedule-modal modal fade in" tabindex="-1" role="dialog" aria-hidden="false" style="display: block;">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-body">
<p><strong> Text
</p>
</div>
<div class="modal-footer">
<a class="btn btn-default btn-ok" href="/appointment">Start Appointment</a>
</div>
</div>
</div>
</div>
Thanks in advance for your help!
Update
Looks like the above code exists HTML code exists on the source page before the modal pops up which is why I was not able to access the element. My next question is what is this schedule-modal function doing:
function showNotice(dom) {
var $scheduleModal = $(".schedule-modal");
if ($scheduleModal.length) {
var link = $(dom).attr('href');
$scheduleModal.find('.btn-ok').attr('href', link);
$scheduleModal.modal('toggle');
return false;
}
it looks like you are trying to find a div element with an href attribute containing appointment. you'll want to select the a element, which is the element that actually contains the reference you are trying to select. you have two options:
driver.find_element_by_xpath('//a[contains(#href,"appointment")]')
or
driver.find_element_by_xpath('//div/a[contains(#href,"appointment")]')
both will achieve the same results. but you may want to refine the selection just in case there are multiple a tags that have an href attribute contianing appointment
If you are unsure if the modal element would appear or not you can wrap up the click to the modal button within a try-catch{} block inducing WebDriverWait for the element_to_be_clickable() and catching TimeoutException and you can use the following Locator Strategy:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
try:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//*[contains(., 'login or register')]"))).click()
print("Modal button clicked")
except TimeoutException:
print("Modal button was not found")

Unable to Locate Element for Selenium Webdriver Path in Python

I've been trying to reference the two inputs in the following HTML code using Selenium's get_element_by_...() with the purpose of automatically entering a username and password into the fields. I'm running into trouble with accessing the proper HTML elements.
<body>
<div id="app">
<div>
<section id="root" class="page-login">
<section class="main">
<section class="auth-page register-fill">
<div class="page-center">
<a class="switch-lang" href="/cn/login">cn</a>
<a class="btn-back" href="en/login">
<span class="isvg loaded">
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 24 24">
<path class="a" d="M0,0H24V24H0Z"></path>
<path class="b" d="M20,11H7.83l5.59-5.59L12,4,4,12l8,8,1.41-1.41L7.83,13H20Z"></path>
</svg>
</span>
</a>
<div class="auth-page-wrapper">
<h1 class="auth-page-title">Welcome Back</h1>
<h2 class="auth-page-subtitle">Login to get started</h2>
<div class="auth-page-fields">
<div class="custom-input">
<input type="text" placeholder="Email">
<label>Email</label>
</div>
<div class="custom-input custom-input-password">
<a class="forgot-link" href="en/forgot-password">Forgot Password?</a>
<input type="password" placeholder="Minimum 6 characters">
<label>Password</label>
</div>
</div>
Note: The HTML may have some mistakes, I just quickly formatted it, but the mistakes should not affect the question.
Tried:
I've tried using all the different functions of the 'form find_element_by_...' and referencing a variety of things, but all tend to return "no such element: unable to locate element ..."
I've done absolute xpaths, relative xpaths, css selectors, ids, and combinations of these.
Some examples:
username = browser.find_element_by_xpath("//input[1]")
username = browser.find_element_by_xpath("//html/body/div/div/section/.../input")
The error begins at this point in the path:
username = browser.find_element_by_xpath("//html/body/div/div/section")
I'm likely making a stupid mistake, so sorry if this question isn't great, but other StackOverflow answers all have ids and such that are referable on the input field. (eg. <input id=username ...>)
To identify the Email field you have to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following Locator Strategies:
Using CSS_SELECTOR:
username = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "input[placeholder='Email']")))
Using XPATH:
username = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//input[#placeholder='Email']")))
Note : You have to add the following imports:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
There are more than one way to do this. But, this should work browser.find_element_by_css_selector('[placeholder="Email"]')

Can find element within an iframe using Firefox but not Chrome

So I'm trying to write a test for a webpage which has some elements within an iframe. I've been able to successfully run the test using webdriver.Firefox() without any problems but if I switch it over to webdriver.Chrome() I get a timeout exception on the following lines of code:
self.driver.switch_to.frame(0)
self.activity_status = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, '#overview > div.details.w-66 > div > div.duration-and-status > span.status.stat_incomplete#')))
It'd be great to get a solution to this as I'm all out of ideas.
Thanks for your help.
edit, partial html for the page:
<iframe id="iframe_course_details" allowfullscreen="" src="../Course/Details.aspx?HidePageNav=true&IsInIframe=true"></iframe>
Close
Edit (Inactive)
Edit
<span id="ctl00_cph_main_content_area_ucCourseDetails_spn_favourite" class="favourite button tooltipstered" style="display: none;">Favourite</span>
<span id="ctl00_cph_main_content_area_ucCourseDetails_spn_basket_dull" class="add-to-basket button delete tooltipstered" style="display: none;">Enrolled (Remove From Enrolments)</span>
<span id="ctl00_cph_main_content_area_ucCourseDetails_spn_basket" class="add-to-basket button tooltipstered">Add to Enrolments</span>
<span id="ctl00_cph_main_content_area_ucCourseDetails_spn_print" class="print button tooltipstered">Print</span>
</div>
<section id="overview" style="opacity: 1;">
<div id="fullname" class="fullname w-100" style="display: none;">
</div>
<div class="image w-33" style="cursor: pointer;">
<div style="background-image:url(/App_Themes/MainTheme-responsive/Images/Course/webcast.jpg);"></div></div>
<div class="details w-66">
<div class="inner">
<h2>testing activity</h2>
<div class="star-rating-num-ratings">
<div class="star-rating">
<span></span><span></span><span></span><span></span><span></span>
</div>
<span class="num-of-ratings">0 Ratings</span>
</div>
<div class="duration-and-status">
<span class="duration">
<label>
Duration:
</label>
<span>0</span>
</span>
<span class="status stat_incomplete">Started</span>
</div>
Edit 2:
So we've managed to find a solution to this and its even more confusing than the original problem
WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.ID, 'iframe_course_details')))
time.sleep(0)
self.activity_status = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[#id="overview"]/div[3]/div/div[2]/span[2]')))
I'd be really curious to hear some theories on why this works, it times out without the 'time.sleep(0).
If you reference the iframe directly rather then an integer that will work between Firefox/Chrome.
self.driver.switch_to.frame(driver.find_element_by_name("iframe"))
You can find the iframe element any way you wish e.g by css/xpath etc
As the the desired element is within an <iframe> so to invoke click() on the element you have to:
Induce WebDriverWait for the desired frame to be available and switch to it.
Code Block:
# as per your comment assuming -> there is only one frame on the page
WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.TAG_NAME,"iframe")))
self.element = self.activity_status = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, '#overview > div.details.w-66 > div > div.duration-and-status > span.status.stat_incomplete#')))
Note : You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
Reference
You can find a relevant detailed discussion in:
Ways to deal with #document under iframe

Send_keys() doesn´t send the full string

I´m trying to login to a website with selenium and Chrome in Python. I´m able to locate the user and password field, and the button. The only thing I´m not capable of is filling the password field with all the string. It writes a substring, with varying lengths.
My code
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
browser=webdriver.Chrome(r"C:\Users\Visente\Desktop\Pablito Python\chromedriver.exe")
urlbet='https://www.bet365.es/?&cb=103265469#/HO/'
browser.get(urlbet)
sport=WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.XPATH,'//*[#id="dv1"]/a')))
sport.click()
user=WebDriverWait(browser,
10).until(EC.presence_of_element_located((
By.XPATH,'/html/body/div[1]/div/div[1]/div/div[1]/div[2]/div/div[1]/input')))
passw=browser.find_element_by_xpath(
'/html/body/div[1]/div/div[1]/div/div[1]/div[2]/div/div[2]/input[1]')
user.send_keys('my_user')
passw.send_keys('password')
submit=browser.find_element_by_xpath(
'/html/body/div[1]/div/div[1]/div/div[1]/div[2]/div/div[2]/button')`
submit.click()
So, in short, the line where y use send_keys() on variable passw is not writing the full length of the password and I don´t know why.
By the way, how do I get back the text I just sent with sed_keys() in order to know what is being passed and what is missing from the string?
I had the same problem, I first click in the element then I send the text, it solved the problem.
el_search_wo.driver = driver.find_element_by_xpath("//*[#id='arid_WIN_0_302258625']")
el_search_wo.click()
el_search_wo.send_keys('TEXT HERE')
You're not supposed to type the password in the field you've selected. As you've noted in the comments, the xpath switches from input[1] to input[2]. The latter is where you're supposed to put the password. I don't understand the mechanism by which the xpath changes when you try to send keys there, but it does. Depending on how quickly it changes, anything from 0 to a few characters will be sent to the wrong input, which explains why the password field ends up with a substring of your password.
From the html, we can see there are 3 input fields, all belonging to the class 'hm-Login_InputField'.
<div class="hm-Login ">
<div class="hm-Login_UserNameWrapper ">
<input type="text" class="hm-Login_InputField ">
<div class="hm-Login_InputText " style="">
Registrarse
</div>
</div>
<div class="hm-Login_PasswordWrapper ">
<input type="text" class="hm-Login_InputField Hidden ">
<input type="password" class="hm-Login_InputField ">
<button tabindex="0" class="hm-Login_LoginBtn ">
IR
</button>
<div class="hm-Login_InputText ">
Recordar contraseña
</div>
</div>
</div>
If you collect them all, from top to bottom, they are the username, the field you tried to put the password in and the password field.
Now, if you try to send the keys directly to the password field, input[2], you will get an error that the element is not visible. You have to first click on input[1] in order to make input[2] visible.
You can easily do it with xpaths, but I find the following approach cleaner.
login = WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, 'hm-Login')))
fields = login.find_elements_by_css_selector('.hm-Login_InputField')
button = login.find_element_by_css_selector('.hm-Login_LoginBtn')
fields[0].send_keys('user_name')
fields[1].click()
fields[2].send_keys('password')
button.click()

Selenium with Python: First instance of the element is identified but the next occurance is ElementNotVisibleException

I have the following Selenium Test for Python/Django application:
class EmailRecordsTest(StaticLiveServerTestCase):
def test_can_store_email_and_retrieve_it_later(self):
self.browser.get(self.live_server_url)
emailbox = self.browser.find_element_by_xpath("//form[#class='pma-subscribe-form']/input[1]")
self.assertEqual(emailbox.get_attribute("placeholder"), 'Enter your Email')
print("tested until here")
print("The placeholder: ", emailbox.get_attribute("placeholder"))
print(emailbox)
emailbox.send_keys('vio#mesmerizing.com')
First occurance of emailbox is clearly identified as seen from the print runs and assert Equal for placeholder. The last instance of emailbox.send_keys throws following error:
selenium.common.exceptions.ElementNotVisibleException: Message:
Element is not currently visible and so may not be interacted with
Cannot find why the same element become Not Visible when using with send_keys.
The Html code being tested is as below:
<!-- Start footer -->
<footer id="pma-footer">
<!-- start footer top -->
<div class="pma-footer-top">
<div class="container">
<div class="pma-footer-top-area">
<div class="row">
<div class="col-lg-3 col-md-3 col-sm-3">
<div class="pma-footer-widget">
<h4>News letter</h4>
<p>Get latest update, news & offers</p>
<form class="pma-subscribe-form">
<input id="subscribe-email" type="email" placeholder="Enter your Email">
<button class="btn btn-danger btn-md" type="submit">Subscribe!</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- end footer top -->
Kindly help.
Actually find_element returns element which would be present on the DOM no matter it's visible or not and you can get attribute of this element as well but send_keys does an action on element and selenium does action only visible element, So you need to be sure before doing action on element that it's visible using WebDriverWait as below :-
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
wait = WebDriverWait(driver, 10)
emailbox = wait.until(EC.visibility_of_element_located((By.ID, "subscribe-email")))
#do your all stuff before send keys
# now use send_keys
emailbox.send_keys('vio#mesmerizing.com')
Edited :- If you are still unable to interact with element try using execute_script() to set value as below :-
emailbox = wait.until(EC.presence_of_element_located((By.ID, "subscribe-email")))
#do your all stuff before send keys
# now use execute_script
driver.execute_script("arguments[0].value = 'vio#mesmerizing.com'", emailbox)
Another option which worked in this case is that you scroll to the specific element (which was at the bottom of the page)and then use send_keys it works.
emailbox = self.browser.find_element_by_xpath("//form[#class='mu-subscribe-form']/input[1]")
self.browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
emailbox.send_keys('vio#mesmerizing.com')

Categories

Resources