如何使用编码UI测试按自定义HTML属性搜索元素

本文关键字:HTML 属性 搜索 元素 自定义 何使用 编码 UI 测试 | 更新日期: 2023-09-27 18:05:49

我在GridView中有一堆input[type=submit]按钮。这些按钮的id s和name s虽然可以预测,因为它们使用索引号,但不太适合使用SpecFlow和Coded UI测试进行自动化。我发现很难搜索这些按钮元素。

传递给浏览器的HTML片段:

<input type="submit" id="abc_xyz_0" data-task-id="123" value="Apply">
<input type="submit" id="qrs_tuv_0" data-task-id="345" value="Renew">

按钮文本在每行("Apply"answers"Renew")中是通用的,但是data-task-id属性是唯一的。我想使用此属性和值来标识要单击的按钮。我试图使用SearchPropertiesFilterProperties,但我不断得到异常:

系统。NotSupportedException:此控件不支持属性DataTaskId。

我如何尝试找到控件:

HtmlInputButton button = new HtmlInputButton(document);
button.SearchProperties["data-task-id"] = "123";
// or button.SearchProperties["DataTaskId"] = "123";

一些附加细节:

    Windows 7
  • Internet Explorer 11
  • localhost提供的网站,在Internet选项安全设置
  • 中标记为"可信"

更新:感谢marcel de vries和AfroMogli的回答。Marcel使用JavaScript来查找元素,而AfroMogli使用纯c#和CodedUI API。两种答案都同样有效,我没有注意到两者之间的性能差异。两个同样好的解决方案

如何使用编码UI测试按自定义HTML属性搜索元素

您可以通过使用ControlDefinition作为属性名进行搜索来完成此操作。下面这行代码为我解决了这个问题:

HtmlInputButton button = new HtmlInputButton(document);
button.SearchProperties.Add(new PropertyExpression(HtmlControl.PropertyNames.ControlDefinition, "data-task-id='"123'"", PropertyExpressionOperator.Contains));

允许传入任何属性名称和值的示例方法:

public HtmlInputButton InputButton(string attributeName, string attributeValue, UITestControl container = null)
{
    string controlDefinition = string.Format("{0}='"{1}'"", attributeName, attributeValue);
    HtmlInputButton button = new HtmlInputButton(container ?? document);
    button.SearchProperties.Add(new PropertyExpression(HtmlControl.PropertyNames.ControlDefinition, controlDefinition, PropertyExpressionOperator.Contains));
    return button;
}

编辑:

或者如果属性名没有改变,执行以下操作?

public PropertyExpression GetByTaskId(string value) 
{
  return new PropertyExpression(
    HtmlControl.PropertyNames.ControlDefinition, 
    $"data-task-id='"{value}'"", 
    PropertyExpressionOperator.Contains
  );
}
HtmlInputButton button = new HtmlInputButton(document);
button.SearchProperties.Add(GetByTaskId("123"));

最简单的方法是使用一个简单的javascript执行来根据您提供的任何属性名和值获得控件。像这样:

const string javascript = "return document.querySelector('{0}');";
var bw = BrowserWindow.Launch("your page");
string selector = "[data-task-id]='123']";
var control = bw.ExecuteScript(string.Format(javascript,selector));`

变量控件现在包含您正在查找的控件,并且具有正确的类型。因此,如果它是HtmlHyperLink,你可以这样使用它。

关于如何在Angular站点中使用它,我有一个更长的故事:http://fluentbytes.com/testing-angular-sites-with-codedui/因为Angular甚至使用自定义属性,如ng-*

我编写了这些实用程序方法来测试JavaScript和其他答案中解释的直接解决方案。

我发现,虽然ControlDefinition属性最初没有使用Visual Studio Marketplace (v1.7)上可用的开箱即装的Selenium跨浏览器组件跨浏览器工作,但在升级chromedriver.exe(到v2.41)后,一切都开始按预期工作。

请注意,升级的chromedriver.exe必须"解锁"才能工作。我使用PowerShell的Unblock-File命令来做到这一点,注意必须使用Run as Administrator启动才能成功。

实用方法:

public PropertyExpression GetByCustomAttribute(string attributeName, string value)
{
    return new PropertyExpression(HtmlControl.PropertyNames.ControlDefinition,
                                  $"{attributeName}='"{value}'"",
                                  PropertyExpressionOperator.Contains);
}
public static T GetControlByCustomAttribute<T>(this BrowserWindow browserWindow,
                                               string attributeName,
                                               string value,
                                               HtmlControl context = null)
     where T : HtmlControl
{
    string queryContext = (context != null ? "arguments[0]" : "document");
    string script = $"return {queryContext}.querySelector('"[{attributeName}=''{value}'']'");";
    return browserWindow.ExecuteScript(script, context) as T;
}

用法:

BrowserWindow.CurrentBrowser = "chrome";
var browser = BrowserWindow.Launch(new Uri("index.html"));
// first find parent of item with custom data attribute
var div = new HtmlControl(browser);
div.SearchProperties.Add(HtmlControl.PropertyNames.Id, "myDiv");
// Either using ControlDefinition solution:
HtmlEdit edit1 = new HtmlEdit(div);
edit1.SearchProperties.Add(GetByItemId("4711"));
// Or using JavaScript solution:
HtmlEdit edit2 = browser.GetControlByItemId<HtmlEdit>("4711", div);
// use control...
edit1.Text = "text";
edit2.Text = "text";