I am trying to send an email with Python Selenium on Protonmail server. Everything works fine until until I get to the email message.
To get to the email message field I use the following xpath: "//div[@id='rooster-editor']/div[1]"
. It gives a unique element when searching manually through the DOM (Chromium gives the same xpath as well).
But within a script this XPath throws a 'no such element' exception. I first thought the error was caused by the fact the page was not completely loaded as explained in this post: Selenium Webdriver - NoSuchElementExceptions.
To avoid this issue, I inserted a 20 seconds implicitly_wait
, but the issue remains.
I do not think this feature would solve the issue either:
EC.presence_of_element_located(By.XPATH,"//div[@id='rooster-editor']/div[1]"
because my snippet is slower than sending an email manually.
To summarize, here is where I introduced some 'wait' or 'time.sleep' in the snippet: From click on 'New Message' button -> implicit wait 20 sec -> fill recipient email -> 2 sec sleep -> email subject -> 3 sec sleep -> then email message. I therefore dont think the problem could be caused by a not completely loaded page.
So my question is: What could bring such an exception?
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
class FindProton():
def test(self, recipient, subject, msg):
self.recipient = recipient
self.subject = subject
self.msg = msg
baseUrl = 'https://www.protonmail.com'
driver = webdriver.Firefox()
driver.get(baseUrl)
driver.maximize_window()
driver.implicitly_wait(5)
time.sleep(2)
#SIGN IN
elementXpath = driver.find_element(By.XPATH,"//span[text()='Sign in']")
elementXpath.click()
#USERNAME
time.sleep(3)
#elementId_0 = driver.find_element(By.ID,'username').click()
elementId_0 = driver.find_element(By.ID,'username')
#elementId_0.send_keys(self.user)
elementId_0.send_keys('xxxxxxx')
#PASSWORD
time.sleep(1)
elementId_1 = driver.find_element(By.ID,'password')
#elementId_1.send_keys(self.passwd)
elementId_1.send_keys('xxxxxx')
time.sleep(2)
elementId_2 = driver.find_element(By.XPATH,"//button[text()='Sign in']")
elementId_2.click()
#my router is the hell slow so I introduce a long implicit wait...
driver.implicitly_wait(20)
#time.sleep(1)
elementId_3 = driver.find_element(By.XPATH,"//button[text()='New message']")
elementId_3.click()
driver.implicitly_wait(15)
#recipient ; part of id=dynamic, so use id contains
element_4 = driver.find_element(By.XPATH,"//input[contains(@id,'to-composer-')]")
element_4.send_keys(self.recipient)
time.sleep(2)
#subject, part of id = dynamic, so use id contains
element_5 = driver.find_element(By.XPATH,"//input[contains(@id,'subject-composer-')]")
element_5.send_keys(self.subject)
time.sleep(3)
#email message: BUG HERE !
element_6 = driver.find_element(By.XPATH,"//div[@id='rooster-editor']/div[1]").click()
ff=FindProton()
ff.test('unknown0j0@gmail.com','no subject email','this is a short message')
Message area is placed in iframe with selector [data-testid=rooster-iframe]
.
So to access the element you need to switch to iframe content first.
Also be aware, that message is not input field, to type you should click on it and emulate keyboard actions first.
After typing / any other actions in iframe, to access previous context you should switch to default context.
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
import time
#your code
action_chains = ActionChains(driver)
message_frame = driver.find_element(By.CSS_SELECTOR, '[data-testid=rooster-iframe]')
driver.switch_to.frame(message_frame)
element_6 = driver.find_element(By.XPATH, "//div[@id='rooster-editor']/div[1]")
element_6.click()
action_chains.send_keys(msg).perform()
# if you finished with actions inside iframe - add line below
driver.switch_to.default_content()