C# 如何使用Selenium中的CSS选择器查找非根元素的直接后代?

C# 如何使用Selenium中的CSS选择器查找非根元素的直接后代?,c#,css,selenium,C#,Css,Selenium,我正在尝试为下面的重用找到的WebElement,以搜索其子代和直接子代: ... 福 ... ... 这个表可能包含嵌套的表,也可能不包含嵌套的表,我只是不想涉猎整个过程,我宁愿使用一个更具体的选择器,它可以保证做我想做的事情。我需要的一些元素没有合理的ID,我想使用选择器查找它们: #tbl1>tbody>tr:first child>td:first child>div #tbl1>tbody>tr>td>button.btnDefault 为了减少代码的重复性,甚至更快,我想缓存元

我正在尝试为下面的
重用找到的WebElement,以搜索其子代和直接子代:


...
福
...
...
这个表可能包含嵌套的表,也可能不包含嵌套的表,我只是不想涉猎整个过程,我宁愿使用一个更具体的选择器,它可以保证做我想做的事情。我需要的一些元素没有合理的ID,我想使用选择器查找它们:

#tbl1>tbody>tr:first child>td:first child>div
#tbl1>tbody>tr>td>button.btnDefault
为了减少代码的重复性,甚至更快,我想缓存
元素:

var table=driver.FindElement(By.Id(“tbl”);
var div=table.FindElement(By.CssSelector(“>tbody>tr:first-child>td:first-child>div”);
var button=table.FindElement(由.CssSelector(“>tbody>tr>td>button.btnDefault”);

在第二个和第三个查询中,这会导致CSS选择器无效。这是正确的,但同样,标准CSS语法并不意味着要从特定的范围进行搜索。这里是否有某种构造可以用作查询的根?

定义页面类时,为什么不使用:

public IWebDriver WebDriver { get; set; }

[FindsBy(How = How.ClassName, Using = "btnDefault")] 
private IWebElement button { get; set; }

实例化该类时,您将获得作为WebElement的按钮。

因为您是从table元素调用FindElement,那么这应该可以工作:

var div = table.FindElement(By.CssSelector("tbody > tr:first-child > td:first-child > div"));
var button = table.FindElement(By.CssSelector("tbody > tr > td > button.btnDefault"));

这删除了开头的“>”。

如果有多个按钮具有相同的类名。当我建议识别按钮所在的单元格时。可能是以下代码将起作用:

public IWebDriver WebDriver { get; set; }

IWebElement tableElement = WebDriver.FindElement(By.Id(tableName));
            IWebElement tbodyElement = tableElement.FindElement(By.TagName("tbody"));
            List<IWebElement> rows = new List<IWebElement>(tbodyElement.FindElements(By.TagName("tr")));
            foreach (var row in rows)
            {
                if (row.GetAttribute("row") != "-1" && (!string.IsNullOrWhiteSpace(row.GetAttribute("row"))))
                {
                    List<IWebElement> cells = new List<IWebElement>(row.FindElements(By.TagName("td")));
                    if (cells.Count != 0)
                    {
                        if (cells[0].Text == columnName) //Identify the cell, may use other attribute
                        {
                            IWebElement btn = cells[0].FindElement(By.ClassName("btnDefault")) as IWebElement;
                            return btn;

                        }
                    }
                }
            }
public-IWebDriver-WebDriver{get;set;}
IWebElement tableElement=WebDriver.FindElement(By.Id(tableName));
IWebElement tbodyElement=tableElement.FindElement(按.TagName(“tbody”));
列表行=新列表(tbodyElement.FindElements(按.TagName(“tr”));
foreach(行中的变量行)
{
if(row.GetAttribute(“row”)!=“-1”&(!string.IsNullOrWhiteSpace(row.GetAttribute(“row”)))
{
列表单元格=新列表(row.FindElements(按.TagName(“td”));
如果(cells.Count!=0)
{
如果(单元格[0].Text==columnName)//标识该单元格,则可以使用其他属性
{
IWebElement btn=单元格[0]。将FindElement(By.ClassName(“btnDefault”))作为IWebElement;
返回btn;
}
}
}
}

一个选项是使用伪类
:scope
引用当前元素

此伪类存在于中,并且当前受Firefox和Chrome支持。但是一些浏览器可能不支持它

var table = driver.FindElement(By.Id("tbl"));
var div = table.FindElement(By.CssSelector(":scope > tbody > tr:first-child > td:first-child > div"));
var button = table.FindElement(By.CssSelector(":scope > tbody > tr > td > button.btnDefault"));

请注意,使用像“直接子对象”这样的强依赖性通常会导致脆弱的选择器,这将增加测试维护成本。

我不确定这是如何解决我的问题的,除了可能是一个轻微的清晰度改进。(可能不是因为有不止一个这样的按钮,我正在测试一个多步骤工作流,其中包括一个AJAX弹出窗口,也是多步骤的。)这只是解决了语法问题,但不能保证匹配的
tbody
将是表的直接后代。也就是说,我没有意识到这是毫无意义的,因为
findelelement
无论如何都应该抓取第一个找到的元素,当从
表中搜索时,
将是它对应的
tbody
。啊,我看到了问题。我摆弄着缓存“table#tbl>tbody”,对“div”稍作修改就可以了,但这并没有让我在“button”上走得更远。这无助于改变性能,但为了减少重复,您可以在页面上创建一个方法来与这些元素进行交互(假设您使用的是页面对象模型),这似乎相当于查询
表tbody tr td
,因此,比我已经拥有的更不具体。这是我一直在寻找的,谢谢!不幸的是,代码库是一个遗留的怪物,我将接受带有误报的脆弱测试,而不是完全没有。