/query-selector-shadow-dom

querySelector that can pierce Shadow DOM roots without knowing the path through nested shadow roots. Useful for automated testing of Web Components. Production use is not advised, this is for test environments/tools such as Web Driver

Primary LanguageJavaScript

Build Status npm version codecov

query-selector-shadow-dom

querySelector that can pierce Shadow DOM roots without knowing the path through nested shadow roots. Useful for automated testing of Web Components e.g. with Selenium, Puppeteer.

// available as an ES6 module for importing in Browser environments

import { querySelectorAllDeep, querySelectorDeep } from 'query-selector-shadow-dom';

Examples

Puppeteer (ES5 script on the window)

/**
 * @name get list of links which may be in the shadow dom
 *
 */
const puppeteer = require('puppeteer');
const path = require('path');
(async() => {
    try {
        const browser = await puppeteer.launch()
        const page = await browser.newPage()
        await page.goto('https://www.polymer-project.org/2.0/docs/upgrade')
        await page.addScriptTag({
            path: path.join(__dirname, 'node_modules/query-selector-shadow-dom/dist/querySelectorShadowDom.js')
        });

        // execute standard javascript in the context of the page.
        const downloads = await page.evaluate(() => {
            const anchors = Array.from(querySelectorShadowDom.querySelectorAllDeep('a'))
            return anchors.map(anchor => anchor.href)
        })
        console.log(downloads)
        await browser.close()
    } catch (e) {
        console.error(e);
    }

})()

Provide alternative node

    // query from another node
    querySelectorShadowDom.querySelectorAllDeep('child', document.querySelector('#startNode'));
    // query an iframe
    querySelectorShadowDom.querySelectorAllDeep('child', iframe.contentDocument);

This library does not allow you to query across iframe boundaries, you will ned to get a reference to the iframe you want to interact with.
If your iframe is inside of a shadow root you could cuse querySelectorDeep to find the iframe, then pass the contentDocument into the 2nd argument of querySelectorDeep or querySelectorAllDeep.

Chrome downloads page

In the below examples the components being searched for are nested within web components shadowRoots.

// Download and Paste the lib code in dist into chrome://downloads console to try it out :)

console.log(querySelectorShadowDom.querySelectorAllDeep('downloads-item:nth-child(4) #remove'));
console.log(querySelectorShadowDom.querySelectorAllDeep('#downloads-list .is-active a[href^="https://"]'));
console.log(querySelectorShadowDom.querySelectorDeep('#downloads-list div#title-area + a'));

Shady DOM

If using the polyfills and shady DOM, this library will still work.

Importing

  • Shipped as an ES6 module to be included using a bundler of your choice (or not).
  • ES5 version bundled ontop the window as window.querySelectorShadowDom available for easy include into a test framework

Running the code locally

npm install

Running the tests

npm test

Running the tests in watch mode

npm run watch

Running the build

npm run build