TestStack.White Gotcha/Tips

RadioButton Click might not actually change anything

The click method does not actually click on the radio button itself. It’s noticeable where a radio button fills some extra space, in some cases the click will not be over the radio button or the text and thus doesn’t seem to work.

Instead use

var radioButton = window.Get<RadioButton>(SearchCriteria.ByText("One"));
radioButton.SetValue(true);

Assert.IsTrue(radioButton.IsSelected);

What type is a UserControl mapped to in TestStack.White?

WPF UserControl’s maps to the TestStack.White frameworks CustomUIItem. Hence

<UserControl 
   x:Class="MyClass"
   x:Name="myClass">
<!-- Other elements -->
</UserControl>

can be accessed using

var myClassUserControl =
   window.Get<CustomUIItem>(
      SearchCriteria.ByAutomationId("myClass"));

Defining a custom control mapping

When using the generic Get method in TestStack.White, you’re have the ability to convert the automation control to a TestStack.White Label, Button etc. to give the feel of interacting with such capabilities that are exposed by these types of controls.

In the case of a WPF UserControl we see this maps to a CustomUIItem. It might be useful if we were to define a TestStack.White compatible UserControl for use with the Get method (for example).

Let’s firstly look at how TestStack.White source code implements a Label (here’s the source for the Label control)

public class Label : UIItem
{
   protected Label() {}
   public Label(AutomationElement automationElement, 
       IActionListener actionListener) : 
          base(automationElement, actionListener) {}

   public virtual string Text
   {
      get { return (string) Property(AutomationElement.NameProperty); }
   }
}

Now in our case we need to create a similar class but derived from the CustomUIItem, so here’s ours

[ControlTypeMapping(CustomUIItemType.Custom, WindowsFramework.Wpf)]
public class UserControl : CustomUIItem
{
   public UserControl(
      AutomationElement automationElement, 
      ActionListener actionListener)
         : base(automationElement, actionListener)
   {            
   }

   protected UserControl()
   {            
   }
}

According to the Custom UI Items documentation, an Empty constructor is mandatory with protected or public access modifier also required.

The ControlTypeMapping attribute is used to allow TestStack.White to map the return from the Get method to the new UserControl type, for example

var userControl = window.Get<UserControl>(
   SearchCriteria.ByAutomationId("myClass"));

Selecting an item in a ComboBox

The code for selecting an item in a ComboBox is fairly simple in TestStack.White, but when I used it I kept getting exceptions saying something about virtualization pattern.

Luckily as TestStack.White is built upon the MS Automation framework and others have been here before me, this from Stackoverflow worked for me, here’s the code slightly altered to use as an extension method

public static void SelectItem(this ComboBox control, string item)
{
   var listControl = control.AutomationElement;

   var automationPatternFromElement = 
      GetSpecifiedPattern(listControl,
         "ExpandCollapsePatternIdentifiers.Pattern");

   var expandCollapsePattern =
      listControl.GetCurrentPattern(automationPatternFromElement) 
         as ExpandCollapsePattern;
   
   if(expandCollapsePattern != null)
   {
      expandCollapsePattern.Expand();
      expandCollapsePattern.Collapse();

      var listItem = listControl.FindFirst(
          TreeScope.Subtree,
          new PropertyCondition(AutomationElement.NameProperty, item));

      automationPatternFromElement = 
         GetSpecifiedPattern(listItem, 
            "SelectionItemPatternIdentifiers.Pattern");

      var selectionItemPattern =
         listItem.GetCurrentPattern(automationPatternFromElement) 
            as SelectionItemPattern;

      if(selectionItemPattern != null)
      {
         selectionItemPattern.Select();
      }
   }
}

private static AutomationPattern GetSpecifiedPattern(
   AutomationElement element, string patternName)
{
   return element.GetSupportedPatterns()
      .FirstOrDefault(pattern => 
         pattern.ProgrammaticName == patternName);
}