Shadow-dom support for selenium

Viewed 15018

I am working on an automation project which uses shadow DOMs extensively. I use the execute_script function to access shadow root elements.

For example:

root = driver.execute_script('return document.querySelector(".flex.vertical.layout").shadowRoot')

Then I use the root element to access the elements within. Since we have shadow root elements at many levels, this is annoying me a lot. Is there any better solution to access elements within shadow root elements?

I am using Chrome 2.20 driver.

7 Answers

Trying to have this automated on Chrome I came up with an inelegant solution of recursively searching through each shadow dom explicitly using:

driver.executeScript(scriptToRun, cssSelector);

Here's the javascript (passed as a string):

function recursiveSearch(element, target) {
    let result = element.querySelector(target);
    if (result) { return result; }
    let subElements = element.querySelectorAll("*");
    for (let i = 0; i < subElements.length; i++) {
        let subElement = subElements[i];
        if (subElement && subElement.shadowRoot) {
            let result = recursiveSearch(subElement.shadowRoot, target);
            if (result) return result;
        }
    }
}
return recursiveSearch(document, arguments[0]);

Since the contents of a shadowRoot might be empty initially one can use driver.wait and until.elementIsVisible to avoid returning a null element.

Async example:

return await driver.wait(until.elementIsVisible(await driver.wait(async () => {
           return await driver.executeScript(scriptToRun, cssSelector);
       }, timeOut)));

Alternatively

My previous solution was to traverse the elements with shadowdoms explicitly, but is less autonomous. Same as above but with this script:

let element = document.querySelector(arguments[0][0]);
let selectors = arguments[0].slice(1);
for (i = 0; i < selectors.length; i++) {
    if (!element || !element.shadowRoot) {return false;}
        element = element.shadowRoot.querySelector(selectors[i]);
    }
return element;

Where selectors would be something like:

['parentElement1', 'parentElement2', 'targetElement']

Sidenote

I found that running my automation tests on Firefox Quantum 57.0 doesn't suffer from hidden shadow doms, and any element can be found with a simple:

driver.findElement(searchQuery);

Maybe you may use IJavaScriptExecutor?

IWebDriver driver;
IJavaScriptExecutor jsExecutor = (IJavaScriptExecutor)driver;
jsExecutor.ExecuteScript('yourShadowDom.func()');

Not sure it works in all browsers, but for me ::shadow works fine in chromedriver 2.38 For example:

div::shadow div span::shadow a
Related