UI Automation in Silverlight - Part II (The Easy Way)
So, I wrote a blog post a bit earlier on exposing custom controls for accessibility and automation in Silverlight. If you're only interested in the automation perspective and the Silverlight unit testing framework doesn't quite suit your needs, then there's an easier way than going out and implementing AutomationPeer types.
As of Silverlight Beta 2, you can utilize the AutomationProperties.AutomationId attribute in your XAML to make types you use unique and identifiable through automation:
<TextBox AutomationProperties.AutomationId="SearchTextBox" x:Name="SearchText" KeyDown="CheckKey" />
AutomationProperties.AutomationId doesn't come up in Intellisense in Beta 2, and you'll get a false positive on the "The attachable property 'AutomationId' was not found in type 'AutomationProperties'." error message. Ignore it for now and spin up your Silverlight app :) Once this has been done, your element should look something like this when identified in UISpy (more):
Identification
ClassName: ""
ControlType: "ControlType.Custom"
Culture: "(null)"
AutomationId: "SearchTextBox"
LocalizedControlType: "custom"
Name: ""
ProcessId: "2808 (iexplore)"
RuntimeId: "42 197822 3"
IsPassword: "False"
IsControlElement: "True"
IsContentElement: "True"
Visibility
BoundingRectangle: "(240, 327, 300, 23)"
ClickablePoint: "390,338"
IsOffscreen: "False"
So, if we want to simulate a user at this point, we have three objectives:
1. Move the mouse cursor over to the ClickablePoint.
2. Click the mouse button.
3. Start typing.
To move the cursor to the clickable point, we need to set the Cursor.Position property. To do this, we must do a conversion between System.Windows.Point and System.Drawing.Point. I chose to handle this conversion through an extension method that looks something like:
public static class ExtensionMethods
{
public static System.Drawing.Point ToDrawingPoint(this System.Windows.Point windowsPoint)
{
return new System.Drawing.Point
{
X = Convert.ToInt32(windowsPoint.X),
Y = Convert.ToInt32(windowsPoint.Y)
};
}
}
Once we've moved the mouse, we also need to simulate a click. We're a DllImport away from making that happen. We can create a nice wrapper class to simplify our calling code:
public static class Mouse
{
private const UInt32 MouseEventLeftDown = 0x0002;
private const UInt32 MouseEventLeftUp = 0x0004;
[DllImport("user32.dll")]
private static extern void mouse_event(UInt32 dwFlags, UInt32 dx, UInt32 dy, UInt32 dwData, IntPtr dwExtraInfo);
public static void Click()
{
mouse_event(MouseEventLeftDown, 0, 0, 0, IntPtr.Zero);
mouse_event(MouseEventLeftUp, 0, 0, 0, IntPtr.Zero);
}
}
So, we set automation IDs on our elements in XAML, we can identify elements with unique automation IDs, we can move the mouse with an element's ClickablePoint property, and we can simulate a mouse click. How does this all come together?
[TestMethod]
public void TestMethod1()
{
// Assumes an existing Internet Explorer process is running and pointed at your Silverlight app
Process process = System.Diagnostics.Process.GetProcessesByName("iexplore").First();
AutomationElement browserInstance = System.Windows.Automation.AutomationElement.FromHandle(process.MainWindowHandle);
Thread.Sleep(1000);
TreeWalker tw = new TreeWalker(new PropertyCondition(AutomationElement.AutomationIdProperty, "SearchTextBox"));
AutomationElement searchBox = tw.GetFirstChild(browserInstance);
Thread.Sleep(1000);
System.Windows.Point uiaPoint;
if (searchBox.TryGetClickablePoint(out uiaPoint))
{
Cursor.Position = uiaPoint.ToDrawingPoint();
Mouse.Click();
SendKeys.SendWait("Hello, world!");
}
else
{
Assert.Fail();
}
}
But what if you're dynamically building out your controls in code? You can still set the AutomationId property of generated controls using the static methods in AutomationProperties, which would look something like this:
ListBoxItem myDynamicListBoxItem = new ListBoxItem { Content = "Hello, world!" };
AutomationProperties.SetAutomationId(myDynamicListBoxItem, "myDynamicListBoxAutomationId");
SendKeys has some new behaviors that were introduced in the .NET Framework 3.0. If that sort of thing interests you, you can read more about it here. If you find that those results give you some inconsistent behaviors, you can make the following addition to your app.config in your Test project:
<appSettings>
<add key="SendKeys" value="SendInput"/>
</appSettings>
Hope this helps :)
Comments
Anonymous
July 17, 2008
PingBack from http://blog.a-foton.ru/2008/07/ui-automation-in-silverlight-part-ii-the-easy-way/Anonymous
July 30, 2008
Was this WPF...as Silverlight does not have an AuotmationElement for starters...??Anonymous
September 12, 2008
The comment has been removedAnonymous
October 08, 2008
kesavareddy - You may want to check out gisenberg's first post on this topic for the answer you're looking for. You can find it <a href="http://blogs.msdn.com/gisenberg/archive/2008/07/12/ui-automation-in-silverlight-simulating-user-interactions.aspx">here</a>.Anonymous
May 05, 2009
UI Automation/Accessibility in Silverlight 2, tools and resources summaryAnonymous
October 26, 2009
Seems to me like a difficult way to go about testing apps. Isn't there any easier way?Anonymous
October 27, 2010
Is it possible to automate non-silverlight apps(zune player, camera) UI Testing using Silverlight Framework? Please respond.