Plugin for webdriver.io to transparently make CSS selectors "just work" with shadow DOM

Usage no npm install needed!

<script type="module">
  import wdioShadowdomService from 'https://cdn.skypack.dev/wdio-shadowdom-service';


wdio-shadowdom-service build status

This is a plugin for WebDriverIO that transparently makes CSS selectors "just work" with the shadow DOM.

With this plugin, APIs like $('.foo') and $('.foo')will automatically query inside the shadow DOM to find elements. This can help avoid complicated or hard-to-maintain test code.


// 😞
const element = $('.foo')


// 🥳
const element = $('.quux') 


  • APIs like $, $, and even some basic usage of execute all "just work" with the shadow DOM.
  • Doesn't override the global document.querySelector or document.querySelectorAll. Only touches your test code, not your production code.
  • Uses kagekiri under the hood – a rigorously-tested utility containing a full CSS selector parser.


npm install wdio-shadowdom-service



Modify your wdio.conf.js like so:

const ShadowDomService = require('wdio-shadowdom-service')

exports.config = {
    // ...
    services: [ [ShadowDomService, {}] ],
    // ...

Use the webdriver protocol

Due to an open bug on WebDriverIO, you will also need to use the webdriver protocol, not the devtools protocol. Set this in your wdio.conf.js:

exports.config = {
  // ...
  automationProtocol: 'webdriver',
  path: '/wd/hub',
  // ...


Now you can use selector queries that pierce the shadow DOM:

const element = await browser.$('.foo')
const elements = await browser.$('.foo')

Some simple usages of document.querySelector/querySelectorAll are also supported:

const element = await browser.execute(() => document.querySelector('.foo'))
const elements = await browser.execute(() => document.querySelectorAll('.foo'))

All selectors are able to pierce the shadow DOM, including selectors like '.outer .inner' where .outer is in the light DOM and .inner is in the shadow DOM. See kagekiri for more details on how it works.

Supported APIs

* execute and executeAsync only work with simple usages of document.querySelector/querySelectorAll or element.querySelector/querySelectorAll.

Currently, WebDriverIO v6 and v7 are supported.


To lint:

npm run lint

To fix most lint issues automatically:

npm run lint:fix

To run the tests:

npm test

To run the tests in debug mode:

DEBUG=true npm test

Then open chrome:inspector in Chrome and open the dedicated DevTools for Node.