Get hands-on training for JIRA Software, Confluence, and more at Atlassian Summit Europe. Register now ›

Or Selenium – everything is obvious in retrospect.

I spent some of my 20% time trying to figure out why simulating typing or pushing keys in selenium is such a pain for us and has been for so long.

Here is what I have found out:

Finding 1: Javascript events are first class citizens in Firefox and cordoned off in IE

In Firefox if you fire a keyboard keypress event at a textfield, Firefox will obligingly type the appropriate letter in that field. And then fire any handlers related to the keyPressEvent.

IE took the more conservative approach that a generated javascript event is just meant for javascript and will only fire the handlers (it will not type the appropriate letter in the field).

Selenium’s type function is actually javascript’s replaceText function and hence will push the string directly into the text area without firing any keyboard events, and will overwrite any existing text in the field.

This is why we created typeWithFullKeyEvents in our seleniumClient. For Firefox it will only send the events. For IE it will put all the text in the field and then fire the events for each letter (it probably should only put the text up to that letter into the field but that is a minor problem).

The problem with typeWithFullKeyEvents is that for IE it will always call the type function. If the locator is pointing to something that cannot receive text the result of this function are “undefined”.

Finding 2: Javascript keyDown and keyUp are not the same as the keyPress event

When pressing the key on a real keyboard, three events are (usually) fired at the currently focussed element of the page.

keyDown

keyPress

keyUp

 (The one exception I have found so far is the tab key which moves focus hence the element which has focus will usually only receive the keyDown event, and on some browsers the keyPress event).

However, if you look at the definition for these events, the fields that are set are actually subtly different:

For keyPress the charCode and the which parameter are set (to the same value) this is the ASCII value of the character we want to “type”. The keyCode parameter may or may not be set depending on the browser.

For keyDown and keyUp events the keyCode and the which parameter are set (to the same value) but this is NOT the ASCII value of the character we want to type. It is actually the Virtual keyCode and usually (but not always) equals the character code of character that would by typed if no modifier keys are being depressed (like the shift or the option key). For the . character even though you are not holding down any modifier buttons a different code is sent on keyDown and keyUp to the character code.

In fact if you are typing a ( you fire 5 events: keyDown for the shiftKey, keyDown for the 9 key, keyPress for the ( character, keyUp for the 9 key, keyUp for the shiftKey. (and the keyDown, keyPress, and keyUp events should have the shiftKey boolean set to true).

Why is this a big deal?

Because we have been naïvely sending through Java’s ASCII character code on the keyDown and keyUp, and these are interpreted by Firefox (because javascript events are first class citizens), and so Firefox attempts to do what it would normally do on a keyDown or keyUp for the keyCode, now for instance a ( is characterCode 40, but keyCode 40 is actually the up arrow key on the keyboard. Similarly, the . has characterCode 46 but keycode 46 is the forward delete key.

Hence we have been getting unexpected behaviour when testing in Firefox, the other problem being our current hacks around this issue will not work properly in IE.

How to make this problem less painful.

I plan to add the following methods to our selenium client, then push the changes up to the selenium project

keyDownEventsForCharacter(String locator, Character character)
keyUpEventsForCharacter(String locator, Character character)
keyPressEventsForCharacter(String locator, Character character)
simulateKeyPressForCharacter(String locator, Character character)

And rewrite the

typeWithFullKeyEvents(String locator, String string, boolean reset)

to be more correct, with comments on this event to say it should only be called on with a locator that finds elements that can actually have text typed into them.

For a very in depth look at keyboard events in javascript on different browsers see here

Fresh ideas, announcements, and inspiration for your team, delivered weekly.

Subscribe now

Fresh ideas, announcements, and inspiration for your team, delivered weekly.

Subscribe now