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) {
  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.