Holding a Key Down with W3C WebdriverIO

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) {
    type: 'key',
    id: 'keyboard',
    actions: [{ type: 'keyDown', value: character }],

export function releaseKey(character: string) {
  const keyAction = ;
    type: 'key',
    id: 'keyboard',
    actions: [{ type: 'keyUp', value: character }],

So instead of this:


// Click column headers


It now looks like this:


// Click column headers


Hopefully, this comes in handy for anyone else that runs into this same issue.

  • D says:

    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.

  • D says:

    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

    • Patrick Bacon Patrick Bacon says:

      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….

  • sytolk says:

    Dont work for me with holdDownKey(‘\uE008’) webdriwerio: 6.4.6

  • Juan P says:

    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.

  • Comments are closed.