selenium webdriver (chromedriver) and accessing shadow dom



我正在测试一个使用影子dom的新应用程序,如下所示:

 #shadow-root (open)
    <div class="th_filePicker">
        <div class="th_fp_header">
            <div class="th_fp_title" role="heading" aria-level="1" data-l10n-id="th_fp_title">Select Image</div>
                <div class="th_fp_Close"><button class="close-popup" data-l10n-id="close_popup" title="Close"></button></div>
        </div>
    </div>

有没有人知道我如何访问文件选取器控件中的元素 - 特别是关闭图标?

一种方法是使用刺穿的CSS选择器(/deep/>>>(。尽管并非所有浏览器都支持它,并且可能会在将来的版本中删除它。

这个应该给你Chrome 62的关闭按钮:

driver.findElement(By.css("* /deep/ button[title='Close']"))

您可以尝试这种"繁重"的方法(C#,但根据您的语言,它可以是这样的(:

public IWebElement DeepFind(By search)
{
    try
    {
        // search a result in the main dom
        return Driver.FindElement(search);
    }
    catch (NoSuchElementException)
    {
        // if nothing we will take a look to the shadow dom(s)
        var shadowRoots = new List<IWebElement>();
        try
        {
            // will use the recursive method that search for all shadow roots
            ListShadowRoots(search, Driver.FindElements(By.XPath("//*")), shadowRoots);
        }
        catch (NoSuchElementException)
        {
            //
        }
        // return the first element that match the By search
        return shadowRoots.FirstOrDefault(s => s.FindElement(search) != null);
    }
}
private void ListShadowRoots(By search, ReadOnlyCollection<IWebElement> elements, List<IWebElement> shadowRoots)
{
    elements.ToList().ForEach(e =>
    {
        var jsResult = (IWebElement)ExecuteJavascript("return arguments[0].shadowRoot", new object[] { e });
        if (jsResult != null)
        {
            shadowRoots.Add(jsResult);
            try
            {
                ListShadowRoots(search, jsResult.FindElements(By.XPath("//*")), shadowRoots);
            }
            catch (NoSuchElementException)
            {
                //
            }
        }
    });
}
private object ExecuteJavascript(string code, object[] args)
{
    IJavaScriptExecutor js = (IJavaScriptExecutor)Driver;
    js.ExecuteScript(code, args);
}

驱动程序是 Web 驱动程序 (IWebDriver(

表现还不错,它确实可以;)希望能有所帮助

这是可能的,但需要几个步骤。作为初步介绍,请查看此页面有关访问影子 dom 的信息。我发现它真的很有信息量。

从两种方法来获取 shadow dom 元素:

private WebElement shadowDom;
private WebElement expandRootElement(WebElement element) {
    WebElement ele = (WebElement) ((JavascriptExecutor) driver)
        .executeScript("return arguments[0].shadowRoot",element);
    return ele;
}
private void findByShadowRoot(WebDriver driver) {
    shadowDom = expandRootElement(
        driver.findElement(By.id("whatEverTheShadowDomIdIs")));
}

从那里,您可以创建方法作为伪 POM

private WebElement findByShadowButton() {
    findByShadowRoot(driver);
    return shadowDom.findElement(By.cssSelector("div.th_fp_Close"));
}

基本上,前两个方法用于创建起点,然后所有其他方法调用这些方法并说,"从这个起点开始,找到它下面的元素"。

然后你可以这样说:

findByShadowButton().click();

我从 MivaScott 的答案中获取信息并为我的解决方案创建了一个递归方法,我认为它可能对其他人有用,所以就在这里。我用它来单击视频播放器中的播放按钮。

您只需要提供影根的 CSS 选择器的字符串数组。该方法将返回最终的阴影根元素,因此您可以在末尾添加另一个选择器(在我的例子中是 svg(。请看我的例子:

玩家的影根结构

public IWebElement PlayButton {
        get {
            string[] shadowRootSelectors = { "apc-controls", "apc-control-footer", "apc-toggle-play", "apc-icon-play" };
            return FindShadowRootElementRecursive(shadowRootSelectors).FindElement(By.CssSelector("svg"));
        }
        set {
        }
}

而递归方法本身:

public IWebElement FindShadowRootElementRecursive(string[] selectors = null, IWebElement element = null) {
            IWebElement root = null;
            IWebElement selectorElement = null;
            bool baseCase = false;
            //Get the first selector from the array
            string selector = selectors[0];
            if (selectors.Length == 1)
            {
                baseCase = true;
            }
            else {
                //If there are more selectors, then remove this selector and recurse with the rest
                selectors = selectors.Where(w => w != selectors[0]).ToArray();
            }
            //If this is the first call...
            if (element == null)
            {
                //Use the driver to select the element
                selectorElement = Driver.FindElement(By.CssSelector(selector));
            }
            else {
                //Otherwise, use the previously found element
                selectorElement = element.FindElement(By.CssSelector(selector));
            }
            //Get the shadow root
            root = (IWebElement)((IJavaScriptExecutor)Driver).ExecuteScript("return arguments[0].shadowRoot", selectorElement);
            if (baseCase)
            {
                return root;
            }
            else {
                //Recurse
                root = FindShadowRootElementRecursive(selectors, root);
            }
            return root;
        }

然后我像这样点击了按钮:

PlayButton.Click();

最新更新