'Selenium-chromedriver: Cannot construct KeyEvent from non-typeable key
I updated my Chrome and Chromedriver to the latest version yesterday, and since then I get the following error messages when running my Cucumber features:
....
unknown error: Cannot construct KeyEvent from non-typeable key
(Session info: chrome=98.0.4758.80) (Selenium::WebDriver::Error::UnknownError)
#0 0x55e9ce6a4093 <unknown>
#1 0x55e9ce16a648 <unknown>
#2 0x55e9ce1a9866 <unknown>
#3 0x55e9ce1cbd29 <unknown>
.....
I try to fill a text field with Capybara's fill_in method. While debugging I noticed that Capybara has problems especially with the symbols @ and \. Every other character can be written into the text field without any problems.
The code that triggers the error looks like this
def sign_in(user)
visit new_sign_in_path
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
click_button 'Sign in'
end
user.email contains a string like "[email protected]".
I work with Rails 6.1.3.1, Cucumber 5.3.0, Chromedriver 98.0.4758.48, capybara 3.35.3
The error only occurs on features that are tagged with @javascript
Do you have any ideas what causes this error or how to fix it?
Solution 1:[1]
For now the easiest is to pin to an earlier version of the chrome driver, so add this to your capybara config
In ruby
# /test/support/system/capybara_config.rb
require 'webdrivers/chromedriver'
Webdrivers::Chromedriver.required_version = '97.0.4692.71'
Hopefully this issue will be addressed in future chromedriver releases, it has been raised and is discussed here
I also played around with overriding the fill_in method.
This is less than ideal, and actually OS dependent, so please provide better solution or update this answer. I will try to update as my research progresses.
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
# overriding the `fill_in` helper for filling in strings with an `@` symbol
def fill_in(locator = nil, with:, currently_with: nil, fill_options: {}, **find_options)
return super unless with.include? "@"
find_options[:with] = currently_with if currently_with
find_options[:allow_self] = true if locator.nil?
element = find(:fillable_field, locator, **find_options)
email_front, email_back = with.split("@")
element.send_keys(email_front)
page.driver.browser.action
.key_down(Selenium::WebDriver::Keys[:alt])
.send_keys('g')
.key_up(Selenium::WebDriver::Keys[:alt])
.perform
element.send_keys(email_back)
end
end
Solution 2:[2]
It seems something has changed in the new version of ChromeDriver and it is no longer possible to send some special chars directly using send_keys method.
In this link you will see how it is solved (in C#) --> Selenium - SendKeys("@") write an "à"
And regarding python implementation, check this out --> https://www.geeksforgeeks.org/special-keys-in-selenium-python/
Specifically, my implementation was (using MAC):
driver.find_element('.email-input', '[email protected]')
Now I had to change it by:
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
emailParts = '[email protected]'.split('@')
emailElement = driver.find_element('.email-input')
emailElement.send_keys(emailParts[0])
action = ActionChains(driver)
action.key_down(Keys.ALT).send_keys('2').key_up(Keys.ALT).perform()
emailElement.send_keys(emailParts[1])
Solution 3:[3]
I had the same problem with Python, and it seems that using only ActionChains solves the problem in an easy way.
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
def safe_send_keys(
driver,
input_selector: str,
input_text: str,
selector_type = By.CSS_SELECTOR
):
driver.find_element(selector_type, input_selector).click()
action = ActionChains(driver)
action.send_keys(input_text)
action.perform()
# Example
driver = webdriver.Chrome()
# ... Go to your web page
email = "[email protected]"
selector = "input_selector"
safe_send_keys(driver, selector, email)
Solution 4:[4]
This answer is for Java and WebDriverManager users.
You can specify chrome driver version like this before start chrome.
WebDriverManager.chromedriver().browserVersion("97").setup();
You can work around this problem.
Update
Solution 5:[5]
Just use JavascriptExecutor, for example:
JavascriptExecutor jse = (JavascriptExecutor) getDriver();
jse.executeScript("arguments[0].setAttribute('value', arguments[1])", googleEmailWebElement, email);
Solution 6:[6]
We had the same issue with my co-worker today.
What worked for us was removing any language from the Chrome settings, but the English (I'm using the Polish language as well and it seems like it's not causing the issue).
Machine: MacBook Pro with macOS Monterey 12.1; Test Framework: Java 11/Selenium
Solution 7:[7]
I was getting the same error for python. If Google chrome automatically updated to version 98 don't update your chromedriver to 98 also, use 97 instead. This solved my issue.
Solution 8:[8]
I am using Selenium with Java. I have been automating login functions without any problem until the Google Chrome update. After the update, I started getting the same error as well. I just changed my keyboard language to "US" and the problem is solved.
Update: Recently (mid-February) my ChromeDriver stopped giving the same error. I am not sure if there has been a fix as I didn't see a new release since I started having problems but my Java/Selenium automation codes run without any error regardless of the keyboard language and send characters like '@'. So, no need to switch the keyboard language to "US" anymore.
Solution 9:[9]
I have same problem. I solved it for Selenium java like:
String arroba = Keys.chord(Keys.ALT, "2");
String[] mailSplit = mail.split("@");
userInput.sendKeys(mailSplit[0]);
userInput.sendKeys(arroba);
userInput.sendKeys(Keys.BACK_SPACE);
userInput.sendKeys(mailSplit[1]);
Solution 10:[10]
This is hopefully a temporary problem that will be solved in a later release https://github.com/SeleniumHQ/selenium/issues/10318
A test macro using JS to fill in the field solved my needs (Ruby) for basic scenarios like
fill_in :user_email, with: user.email
Test macro:
def fill_in_chrome98_tmp_fix(locator, with:)
element = find(:fillable_field, locator)
dom_id = element.native.dom_attribute("id")
raise "No DOM ID" if dom_id.blank?
page.execute_script("document.getElementById('#{dom_id}').value = '#{with}'")
end
Solution 11:[11]
This issue affected me as well. This answer is for Python. This is how I currently set up my selenium tests in django. My chromium browser is snap version 98.0.4758.80.
@classmethod
def setUpClass(cls):
super().setUpClass()
options = webdriver.ChromeOptions()
options.add_argument('--no-sandbox') # Must be the very first option
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.add_argument('--disable-software-rasterizer')
options.add_argument('--disable-dev-shm-usage')
options.add_argument("--remote-debugging-port=9222")
options.add_argument("--no-default-browser-check")
options.add_argument("--no-first-run")
options.add_argument("--disable-default-apps")
s = Service(
ChromeDriverManager(
# version='98.0.4758.80',
# version='98.0.4758.48',
version='97.0.4692.71',
log_level=logging.WARNING,
chrome_type=ChromeType.CHROMIUM).install())
cls.selenium = webdriver.Chrome(
service=s,
options=options)
cls.selenium.implicitly_wait(1)
Solution 12:[12]
Update of Google Chrome from the previous Version: 97.0.4692.99-1 to the newest one Version: 98.0.4758.80-1 also affect my code. I am using Python and I couldn't send any more characters as @, ^. For now, I simply downgrade the version of Google Chrome. It affected on such a structure: self.driver.find_element_by_id('id_password').send_keys(password), where e.g. password contains ^.
Link to a simple downgrade of chrome version on linux machines: https://makandracards.com/makandra/486433-how-to-downgrade-google-chrome-in-ubuntu
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
