我正在测试一个使用阴影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>
有没有人知道我如何访问文件选择器控件中的元素 - 特别是关闭图标?
答案 0 :(得分:2)
一种方法是使用穿孔CSS选择器(/deep/
或>>>
)。虽然并非所有浏览器都支持它,但可能会在将来的版本中将其删除。
这个应该给你关于Chrome 62的关闭按钮:
driver.findElement(By.css("* /deep/ button[title='Close']"))
答案 1 :(得分:2)
这是可能的,但需要几步。作为初步,请查看有关访问影子dom的this page。我发现它确实很有用。
从两个方法开始获取阴影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"));
}
基本上前两种方法用于创建一个起点,然后所有其他方法都调用这些方法并说出,从这个起点开始,找到它下面的元素&#34;。
然后你可以这样说:
findByShadowButton().click();
答案 2 :(得分:2)
你可以尝试这种“沉重”的方法(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)
表演并不是那么糟糕,它可以胜任这项工作;) 希望它可以提供帮助
答案 3 :(得分:0)
我从MivaScott的答案中获取了信息,并为我的解决方案创建了一个递归方法,我认为它可能对其他人有用,所以在这里。我用它来单击视频播放器中的播放按钮。
您需要提供的是shadow-root的CSS选择器的字符串数组。该方法将返回最终的阴影根元素,因此您可以在末尾添加另一个选择器(在我的情况下为svg)。请看我的例子:
The Player's Shadow-Root Structure
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();