Article summary
Since the summer of 2019, WebdriverIO has fully supported the W3C WebDriver protocol (announced in this post). As part of upgrading to the latest v5 version of WebdriverIO, I decided to also switch over to using the W3C protocol for our tests that use Chrome / ChromeDriver.
After switching to the W3C protocol, we found failing tests that were interacting with the page while a modifier key (like Shift) was held down. For example, this issue affected tests for multi-column sorting. In the application, if a column header is clicked while Shift is held down, that column becomes a secondary sort for the table.
I eventually figured out why this was happening and created a workaround. I want to share it here in case others run into the same issue.
Enable W3C Protocol
To ensure that Chrome is running in W3C mode, specified the following in the capabilities section the WebdriverIO config:
...
capabilities: [{
browserName: 'chrome',
'goog:chromeOptions': {
w3c: true,
}
}],
...
The Problem
Our test suite was using the browser.keys
function to hit the Shift key before clicking on a column header. The documentation for the keys
function states:
Modifier like Ctrl, Shift, Alt and Meta will stay pressed, so you need to trigger them again to release them.
This worked exactly as specified when using the JSON Wire protocol, but it seemed that the Shift key was not being held down when using the W3C protocol.
After further searching turned up nothing, I started poking around in the WebdriverIO source, where I found the following snippet in the implementation of the browser.keys()
function:
/**
* W3C way of handle it key actions
*/
const keyDownActions = keySequence.map((value) => ({ type: 'keyDown', value }))
const keyUpActions = keySequence.map((value) => ({ type: 'keyUp', value }))
return this.performActions([{
type: 'key',
id: 'keyboard',
actions: [...keyDownActions, ...keyUpActions]
}]).then(() => this.releaseActions())
The important part here is that it’s sending both the keyDown
and the keyUp
actions. That means the Shift key is definitely not being held down they way the docs say that it is.
Hold Down and Release
To fix my tests, I implemented two new helper functions:
export function holdDownKey(character: string) {
browser.performActions([{
type: 'key',
id: 'keyboard',
actions: [{ type: 'keyDown', value: character }],
}]);
}
export function releaseKey(character: string) {
const keyAction = ;
browser.performActions([{
type: 'key',
id: 'keyboard',
actions: [{ type: 'keyUp', value: character }],
}]);
}
So instead of this:
browser.keys('Shift');
// Click column headers
browser.keys('Shift');
It now looks like this:
holdDownKey('Shift');
// Click column headers
releaseKey('Shift');
Hopefully, this comes in handy for anyone else that runs into this same issue.
Your holdDownKey(‘Shift’); Did it ever work? I mean in the latest webdriverio (6.10.5) it throws an error ‘invalid argument: ‘value’ must be a single Unicode code point’.
I.e. you may not pass the ‘Shift’ string to the performActions([{ /*skipped stuff*/ {type: ‘keyDown’, value: ‘Shift’} /*skipped stuff*/}]) as your example does.
It requires a unicode code point, and there is NONE, lol, for the Shift key (AFAIK). So how to press and hold a Shift key that is the question indeed.
Good God, it uses the (\uE008) character for a shift press, so one need to use a js string like ‘\uE008’ for the value of the value property in the comment above. Is this specified anywhere? No mention in the docs apparently
Glad you were able to figure it out. holdDownKey(‘Shift’) was definitely working for the version I was using at the time I wrote this. Looks like, as always, things have changed….
Dont work for me with holdDownKey(‘\uE008’) webdriwerio: 6.4.6
I can confirm that this works in Devtools protocol, using ‘Shift’. But with Chromedriver protocol, even using the proper UNICODE character for Shift key does not seem to keep it pressed, although there is no error when it runs.