Following my usecase in the OP, 
I have copied the CSS selector of the span element and tried this:
sendButton = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "body > div.app-root > div.composer-container > div > div > div > footer > div > div.button-group.button-group-solid-norm.button-group-medium > button > span")))
sendButton.click()
I get the following error:
selenium.common.exceptions.NoSuchWindowException: Message: Browsing context has been discarded
I am not sure, if I targeted the right element for what I want to achieve. I often struggle to find the right strategy to target an element. What is the best way if there is no id element oder a nice and short css selector? I checked the Selenium docs (https://selenium-python.readthedocs.io/locating-elements.html) but hesitated choosing one of the suggested methods.
Original code:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
myPassword = 'XXXXXXXXXXXXXX!'
browser = webdriver.Firefox() # Opens Firefox webbrowser
browser.get('https://protonmail.com/') # Go to protonmail website
loginButton = WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "a.btn-ghost:nth-child(1)")))
loginButton.click()
usernameElem = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "#username")))
usernameElem.send_keys("[email protected]")
passwordElem = browser.find_element_by_css_selector("#password")
passwordElem.send_keys(myPassword)
anmeldenButton = browser.find_element_by_css_selector(".button")
anmeldenButton.click()
newMessage = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.XPATH, "/html/body/div[1]/div[3]/div/div/div[1]/div[2]/button")))
newMessage.click()
addressElem = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "input[id^='to-composer']")))
addressElem.send_keys('[email protected]')
subjectElem = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "input[id^='subject-composer']")))
subjectElem.send_keys('anySubject')
WebDriverWait(browser, 20).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, "iframe[title='Editor']")))
WebDriverWait(browser, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div#squire"))).send_keys('Test message')
sendButton = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "body > div.app-root > div.composer-container > div > div > div > footer > div > div.button-group.button-group-solid-norm.button-group-medium > button > span")))
sendButton.click()
browser.switch_to.default_content()
CodePudding user response:
Some initial comments...
You don't need to keep using
WebDriverWait(browser, 10). Instead declare a variable to store theWebDriverWaitinstance,wait = WebDriverWait(browser, 10), and then use the variable everywhere, e.g.loginButton = WebDriverWait(browser, 10).until()would become
loginButton = wait.until()It removes unnecessary instantiations and makes the code easier to read.
Unless you are going to reuse the variable, just one-line the
WebDriverWait()and action, e.g.loginButton = wait.until() loginButton.click()becomes
wait.until().click()It reduces the number of local variables you have, especially when you aren't using them and also makes the code easier to read.
The main answer...
I don't have a protonmail account but my guess is that your last few lines of code are the issue. It looks like after switching to an IFRAME, the IFRAME is no longer available... maybe due to the .send_keys()? Try moving the browser.switch_to.default_content() before the final .click() line or you may even have to switch back to default and then switch into a new IFRAME. That should at least get you pointed in the right direction. Updated code below.
wait.until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, "iframe[title='Editor']")))
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div#squire"))).send_keys('Test message')
browser.switch_to.default_content()
# may need to switch into the correct IFRAME again here
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "body > div.app-root > div.composer-container > div > div > div > footer > div > div.button-group.button-group-solid-norm.button-group-medium > button > span"))).click()
CodePudding user response:
As per the screenshot:

to identify the element you need to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following Locator Strategies:
Using
CSS_SELECTOR:WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div.button-group.button-group-solid-norm.button-group-medium > button span"))).click()
CodePudding user response:
I would try to select the button by xpath. I think its way more accurate. If your on chrome or fire fox
right click on the an element on the browser
right click and copy and copy by full xpath
//*[@id="hireme"]/div/ul/li[4]/div/a
