问题
I am using mui-rte rich text editor (https://github.com/niuware/mui-rte) in a react project. I am not able to figure out how to input text to the rte input area when writing a selenium webdriver integration test.
As I understand correctly, mui-rte is a materia-ui wrapper of draftjs. The react code is simply:
<MUIRichTextEditor
onChange={onChange}
value={initial}
{...rest}
/ >
This generates the following html elements:
<div id="mui-rte-container" class="MUIRichTextEditor-container-73">
<div id="mui-rte-toolbar" class="MUIRichTextEditor-toolbar-84">
...
</div>
<div id="mui-rte-editor" class="MUIRichTextEditor-editor-75">
<div id="mui-rte-editor-container" class="MUIRichTextEditor-hidePlaceholder-79 MUIRichTextEditor-editorContainer-76">
<div class="DraftEditor-root">
<div class="DraftEditor-editorContainer">
<div aria-describedby="placeholder-9mnek" class="notranslate public-DraftEditor-content" role="textbox" spellcheck="false" style="outline:none;user-select:text;-webkit-user-select:text;white-space:pre-wrap;word-wrap:break-word" contenteditable="true">
<div data-contents="true"><div class="" data-block="true" data-editor="7kjuh" data-offset-key="8a2rc-0-0">
<div data-offset-key="8a2rc-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr">
<span data-offset-key="8a2rc-0-0">
<br data-text="true">
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
I can easily find any of the element but when I try this for example:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait # available since 2.4.0
from selenium.webdriver.support import expected_conditions as EC # available since 2.26.0
from selenium.webdriver.common.by import By
rte_editor = WebDriverWait(self.driver, 2).until(
EC.presence_of_element_located((By.ID, id))
)
rte_input = bio_rte.find_element_by_xpath("//div[@role='textbox']")
rte_input.send_keys("Hello")
I get:
E selenium.common.exceptions.ElementNotInteractableException: Message: Element <div class="notranslate public-DraftEditor-content"> is not reachable by keyboard
With all elements that I have tried.
What is the correct way to input text into draft.js rte with selenium-webdriver in python? I am quite new to selenium+webdriver and any help will be appreciated, be it in python, JavaScript or other flavor of selenium-webdriver API.
I have a sample project here: https://github.com/vessper/formik-mui-rte-example
update:
Including the stack trace from the error:
self = <test.TestBase testMethod=test_input_text_in_rte>
def test_input_text_in_rte(self):
rte_input = WebDriverWait(self.driver, 20).until(
EC.element_to_be_clickable(
> (By.XPATH, '//div[@class="notranslate public-DraftEditor-content" and @role="textbox"]'))
)
test.py:25:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <selenium.webdriver.support.wait.WebDriverWait (session="38c21bf5-27ea-499d-9831-e8755a10f57a")>
method = <selenium.webdriver.support.expected_conditions.element_to_be_clickable object at 0x7f7115fe7198>, message = ''
def until(self, method, message=''):
"""Calls the method provided with the driver as an argument until the \
return value is not False."""
screen = None
stacktrace = None
end_time = time.time() + self._timeout
while True:
try:
value = method(self._driver)
if value:
return value
except self._ignored_exceptions as exc:
screen = getattr(exc, 'screen', None)
stacktrace = getattr(exc, 'stacktrace', None)
time.sleep(self._poll)
if time.time() > end_time:
break
> raise TimeoutException(message, screen, stacktrace)
E selenium.common.exceptions.TimeoutException: Message:
../../../.virtualenvs/ml2/lib/python3.7/site-packages/selenium/webdriver/support/wait.py:80: TimeoutException
================================================================= 1 failed in 24.70s ==================================================================
回答1:
In my case it was a draft.js rich text editor with a contenteditable div
async sendKeysToContentEditableDiv(element, text) {
const script = `var txtarea = arguments[0],txt = arguments[1];
txtarea.dispatchEvent(new Event("focus"));
var txtEvt = document.createEvent("TextEvent");
txtEvt.initTextEvent("textInput", true, true, null, txt);
txtarea.dispatchEvent(txtEvt);
txtarea.dispatchEvent(new Event("blur"));`;
await this.driver.executeScript(script, element, text);
}
回答2:
The desired element is a ReactJS enabled element so to locate and send a character sequence to the element you have to induce WebDriverWait for the element_to_be_clickable()
and you can use either of the following solutions:
Using
CSS_SELECTOR
:WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div.notranslate.public-DraftEditor-content[role='textbox']"))).send_keys("Vess_Perfanov")
Using
XPATH
:WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//div[@class='notranslate public-DraftEditor-content' and @role='textbox']"))).send_keys("Vess_Perfanov")
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
Update
As the click()
is still not invoked with WebDriverWait you can use ActionChains as follows:
Using
CSS_SELECTOR
:element = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div.notranslate.public-DraftEditor-content[role='textbox']"))).send_keys("Vess_Perfanov") ActionChains(driver).move_to_element(element).click(element).send_keys("Vess_Perfanov").perform()
Using
XPATH
:element = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//div[@class='notranslate public-DraftEditor-content' and @role='textbox']"))).send_keys("Vess_Perfanov") ActionChains(driver).move_to_element(element).click(element).send_keys_to_element(element, "Vess_Perfanov").perform()
回答3:
There is an inline label element from Material-UI that needs to be clicked first to uncover the underlying text field. So the following work for me.
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
import unittest
class TestBase(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
self.driver.get(URL)
def tearDown(self):
self.driver.close()
def get_by_id(self, id):
return WebDriverWait(self.driver, 5).until(
EC.presence_of_element_located((By.ID, id))
)
def test_input_text_in_rte(self):
description_msg = "Hello There"
rte_container = self.get_by_id('mui-rte-container')
rte_label = rte_container.find_element_by_xpath("//*[contains(text(), 'Description:')]")
actions = ActionChains(self.driver)
actions.move_to_element(rte_label)
actions.click(rte_label)
actions.perform()
rte_input = WebDriverWait(self.driver, 5).until(
EC.presence_of_element_located((By.XPATH,
'//div[@class="notranslate public-DraftEditor-content" and @role="textbox"]'))
)
rte_input.send_keys(description_msg)
Thanks to @DebanjanB for the suggestions that are incorporated in the code above.
来源:https://stackoverflow.com/questions/59120039/how-to-set-content-into-mui-rte-with-selenium-webdriver-in-python