mercredi 25 mars 2015

How to extend CodedUI HtmlControls and preserve typing

I've been digging into CodedUI heavily recently and have been having fun extending the library to include the rest of the HTML elements that (for whatever reason) were left out of the Microsoft.VisualStudio.TestTools.UITesting.HtmlControls namespace.


I've noticed that I can do things like:



var divs = new HtmlDiv(window).FindMatchingControls().OfType<HtmlDiv>();


and this will return what you expect: an IEnumerable with the same elements as the FindMatchingControls() call (just typed as HtmlDiv instead of UITestControl).


Also, I can do this:



public class HtmlHeader : HtmlCustom
{
public static readonly string HeaderTag = "header";

public HtmlHeader() : base() {
this.SearchProperties.Add(HtmlControl.PropertyNames.TagName, HeaderTag, PropertyExpressionOperator.EqualTo);
}
public HtmlHeader(UITestControl parent) : base(parent) {
this.SearchProperties.Add(HtmlControl.PropertyNames.TagName, HeaderTag, PropertyExpressionOperator.EqualTo);
}
}


and even this works:



var headerTag = new HeaderTag(window);


it will find the header tags on the screen and get the first one for you.



var headerTags = new HeaderTag(window).FindMatchingControls();


works fine, but



var headerTags = new HeaderTag(window).FindMatchingControls().OfType<HeaderTag>();


is empty.


Why do the HtmlControls in the above namespace properly resolve types?


Would I be able to utilize an implicit conversion operator / some other trick to get OfType() to return a collection of HtmlTags?


I started writing a conversion like:



protected HeaderTag ConvertControl(HtmlControl toConvert)
{
if (StringComparer.OrdinalIgnoreCase(toConvert.TagName, "header"))
{
var ret = new HeaderTag();
ret.SearchProperties.AddRange(toConvert.SearchProperties);
ret.SearchConfigurations = toConvert.SearchConfigurations;
ret.FilterProperties.AddRange(toConvert.FilterProperties);
// screen element
// technology is already set - web
// cached queryid
// cached parent
// boundary screen element
return ret;
}
throw new InvalidOperationException("Control cannot be converted.");
}


However, after decompiling the HtmlControl type itself, I saw there is a CopyFromControl method which is not accessible; further, the fields it is setting are inaccessible (commented above).


I'm not sure if it really matters whether I copy those fields and if they are overly important to copy or not, but I'm hoping to find a better solution. If nothing better comes up, I'll probably resort to reflection, but I'd rather not go that route if it can be avoided.






Aucun commentaire:

Enregistrer un commentaire