如何确定一个元素是否与CSS选择器匹配?

时间:2015-04-01 15:40:25

标签: c# selenium selenium-webdriver

给定一个Selenium WebDriver元素实例,我想检查该元素是否与给定的CSS选择器匹配。该功能类似于jQuery的is()函数。

我正在使用.NET绑定。

示例(假设该方法将被称为Is

var links = _driver.FindElements(By.CssSelector("a"));
foreach (var link in links) 
{
  if (link.Is(".myclass[myattr='myvalue']"))
    // ... do something
  else 
    // ... do some other thing
}

是否有任何内置的东西来实现这一目标,或者如果没有,是否有人可以建议实施?我是Selenium的新手,还不知道。

更新

我发现没有内置方法可以做到这一点。我的最终目标是实现jQuery的parents(selector)closest(selector)等方法,因此任何建议都会对这个更特殊的案例表示赞赏。

3 个答案:

答案 0 :(得分:2)

没有Selenium方法可以完全相当于jQuery' s $(...).is(...)。但是,DOM提供matches。它不是$(...).is(...)的完全替代品,因为它不支持jQuery对CSS选择器语法的扩展,但是,Selenium也不支持这些扩展。

您可以通过将要测试的元素传递给传递给ExecuteScript的JavaScript脚本来使用它。此元素将显示为脚本中arguments的第一个元素。您只需在其上调用matches并返回值即可。我不做C#,但根据文档,我相信它在C#中看起来像这样:

bool isIt = (bool)(driver as IJavaScriptExecutor).ExecuteScript(
    "return arguments[0].matches(\".myclass[myattr='myvalue']\")", element);

isIt包含测试结果。 element是您要测试的元素,driver是您已经创建的驱动程序。有关兼容性,请参阅caniuse。在我的应用程序中,我使用polyfill在我关心的所有平台上提供matches


这就是说我不建议循环遍历元素并逐个测试。问题是每个ExecuteScriptGetAttribute调用都是您的脚本和浏览器之间的往返。当您运行完整的测试套件时,这会增加,特别是如果浏览器在远离脚本的服务器场上运行。然后真的广告。我会构建您的测试,以便查询所有a元素的列表以及所有a.myclass[myattr='myvalue']元素的列表。这归结为两次往返。从这两个列表中,您可以获得进行if()测试所需的所有信息。

答案 1 :(得分:1)

通常,为了比较硒中的元素,您可以比较它们的outerHTMLinnerHTML表示。它不是防弹的,但应该在实践中起作用:

IWebElement elm = _driver.FindElement(By.CssSelector(".myclass[myattr='myvalue']"));

string linkHtml = link.GetAttribute("outerHTML");
string elmHtml = elm.GetAttribute("outerHTML");

if (linkHtml == elmHtml) {
    ...
}

请注意,在您的情况下,看起来您只需使用class检查myattrGetAttribute()属性的值:

string linkClass = link.GetAttribute("class");
string linkMyAttr = link.GetAttribute("myattr");

if (linkClass.Contains("myclass") && linkMyAttr == "myvalue") {
    ...
}

答案 2 :(得分:0)

我的最终解决方案是这三种方法:Parent, Parents(selector), Closest(selector)。在此之后,可以很容易地实现其他几个类似jQuery的帮助器方法。希望将来帮助某人。

    public static IWebElement Parent(this IWebElement elem)
    {
        var script = new[]
        {
            "return",
            "  (function(elem) {",
            "     return elem.parentNode;",
            "   })(arguments[0]);"
        };
        var remoteWebElement = elem as RemoteWebElement;
        if (remoteWebElement == null)
            throw new NotSupportedException("This method is only supported on RemoteWebElement instances. Got: {0}".FormatWith(elem.GetType().Name));

        var scriptTxt = script.Implode(separator: " ");
        var scriptExecutor = remoteWebElement.WrappedDriver as IJavaScriptExecutor;
        if (scriptExecutor == null)
            throw new NotSupportedException("This method is only supported on drivers implementing IJavaScriptExecutor interface. Got: {0}".FormatWith(elem.GetType().Name));

        return scriptExecutor.ExecuteScript(scriptTxt, elem) as IWebElement;
    }

    public static ReadOnlyCollection<IWebElement> Parents(this IWebElement elem, string selector = null)
    {
        var script = new[]
        {
            "return",
            "  (function(elem) {",
            //"     console.log(elem);",
            "     var result = [];",
            "     var p = elem.parentNode;",
            "     while (p && p != document) {",
            //"       console.log(p);",
            (string.IsNullOrWhiteSpace(selector) ? null :
                "     if (p.matches && p.matches('" + selector + "'))"),
            "          result.push(p);",
            "       p = p.parentNode;",
            "     }",
            "     return result;",
            "   })(arguments[0]);"
        };
        var remoteWebElement = elem as RemoteWebElement;
        if (remoteWebElement == null)
            throw new NotSupportedException("This method is only supported on RemoteWebElement instances. Got: {0}".FormatWith(elem.GetType().Name));

        var scriptTxt = script.Implode(separator: " ");
        var scriptExecutor = remoteWebElement.WrappedDriver as IJavaScriptExecutor;
        if (scriptExecutor == null)
            throw new NotSupportedException("This method is only supported on drivers implementing IJavaScriptExecutor interface. Got: {0}".FormatWith(elem.GetType().Name));

        var resultObj = scriptExecutor.ExecuteScript(scriptTxt, elem) as ReadOnlyCollection<IWebElement>;
        if (resultObj == null)
            return new ReadOnlyCollection<IWebElement>(new List<IWebElement>());
        return resultObj;
    }

    public static IWebElement Closest(this IWebElement elem, string selector)
    {
        var script = new[]
        {
            "return",
            "  (function(elem) {",
            "     var p = elem;",
            "     while (p && p != document) {",
            "       if (p.matches && p.matches('" + selector + "'))",
            "          return p;",
            "       p = p.parentNode;",
            "     }",
            "     return null;",
            "   })(arguments[0]);"
        };
        var remoteWebElement = elem as RemoteWebElement;
        if (remoteWebElement == null)
            throw new NotSupportedException("This method is only supported on RemoteWebElement instances. Got: {0}".FormatWith(elem.GetType().Name));

        var scriptTxt = script.Implode(separator: " ");
        var scriptExecutor = remoteWebElement.WrappedDriver as IJavaScriptExecutor;
        if (scriptExecutor == null)
            throw new NotSupportedException("This method is only supported on drivers implementing IJavaScriptExecutor interface. Got: {0}".FormatWith(elem.GetType().Name));

        return scriptExecutor.ExecuteScript(scriptTxt, elem) as IWebElement;
    }