Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to search for an element by custom HTML attribute using Coded UI Tests

I have a bunch of input[type=submit] buttons inside a GridView. The ids and names of these buttons, while predictable because they use an index number, are not well suited to automation using SpecFlow and Coded UI Tests. I'm finding it difficult to search for those button elements.

A snippet of the HTML delivered to the browser:

<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">

The button text is generic in each row ("Apply" and "Renew"), but the data-task-id attribute is unique. I would like to use this attribute and value to identify a button to click on. I'm trying to use the SearchProperties and FilterProperties but I keep getting exceptions:

System.NotSupportedException: The property DataTaskId is not supported for this control.

How I'm attempting to find the control:

HtmlInputButton button = new HtmlInputButton(document);

button.SearchProperties["data-task-id"] = "123";
// or button.SearchProperties["DataTaskId"] = "123";

Some additional details:

  • Windows 7
  • Internet Explorer 11
  • Web site delivered from localhost and is marked "trusted" in the Internet Options Security Settings

Update: Thanks to both marcel de vries and AfroMogli for their answers. Marcel's uses JavaScript to find the element, and AfroMogli's uses pure C# and the CodedUI API. Both answers worked equally well, and I didn't notice a performance difference between either one. Two equally good solutions.

like image 896
Greg Burghardt Avatar asked Jan 07 '23 21:01

Greg Burghardt


2 Answers

You can accomplish this by searching with the ControlDefinition as propertyname. The following line of code did the trick for me:

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

Example method allowing you to pass in any attribute name and value:

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;
}

EDIT:

Or if the attribute name doesn't change, do the following?

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"));
like image 123
AfroMogli Avatar answered Jan 21 '23 14:01

AfroMogli


The simplest way of doing this is by using a simple javascript execute to get the control based on any attribute name and value you provide. something like this:

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));`

The variable control now contains the control you are looking for and is of the correct type. So if it is e.g. and HtmlHyperLink you can use it as such right a way.

I have a longer story about how to use this in Angular sites here: http://fluentbytes.com/testing-angular-sites-with-codedui/ since angular uses even custom attributes like ng-*

like image 45
marcel de vries Avatar answered Jan 21 '23 12:01

marcel de vries