WPF Support

Oct 17, 2012 at 3:31 AM
Edited Oct 17, 2012 at 3:32 AM

Hi, I know WPF support has not been released yet, but I've just downloaded and built the source code to give it a try. I tried using the ObjectRecorder to launch my application (by typing the path to it in the Address field) and create references to controls, but nothing seems to get recorded when I interact with it. Is this expected to work yet? Am I doing the right thing?

Thanks

Coordinator
Oct 17, 2012 at 4:01 AM

The CUITe ObjectRecorder does not support recording desktop applications.

I recommend using the built-in Visual Studio Coded UI Test Recorder and then convert the generated code for use with CUITe.

Visual UI Automation Verify can also be used to help figure out control types and automation property values.

http://uiautomationverify.codeplex.com

Oct 19, 2012 at 4:58 AM

Thanks for that. Are you able to elaborate on ‘convert the generated code for use with CUITe’? Can you give me a quick example?

Grateful for any time you can spare on this…

Martin

From: icnocop [email removed]
Sent: Wednesday, 17 October 2012 4:01 p.m.
To: Martin White
Subject: Re: WPF Support [cuite:399681]

From: icnocop

The CUITe ObjectRecorder does not support recording desktop applications.

I recommend using the built-in Visual Studio Coded UI Test Recorder and then convert the generated code for use with CUITe.

Visual UI Automation Verify can also be used to help figure out control types and automation property values.

http://uiautomationverify.codeplex.com

Coordinator
Oct 21, 2012 at 3:52 AM

Hi Martin,

You can see some sample code that the Visual Studio Coded UI Test Recorder generates and how it can be converted into less code using CUITe in questions 1 and 2 of the FAQ.

http://cuite.codeplex.com/wikipage?title=Frequently Asked Questions

Oct 24, 2012 at 12:00 AM

Great, thanks for your quick response. I’ve got CUITe working, but I am experiencing strange behaviour with CUITe_WpfTable. My table object (which represents a WPF ListView) is correctly returning the number of rows in the table, but the Rows and RowsAsCUITe properties return empty collections. I’ve debugged my way through the CUITe source code and realise that the Rows property is just mapped to the Rows property of the underlying control, so this appears to be a WPF/Coded UI problem but I’m hoping you might have some idea as to what the problem is because I don’t!

Thanks again for your time

Martin

From: icnocop [email removed]
Sent: Wednesday, 17 October 2012 4:01 p.m.
To: Martin White
Subject: Re: WPF Support [cuite:399681]

From: icnocop

The CUITe ObjectRecorder does not support recording desktop applications.

I recommend using the built-in Visual Studio Coded UI Test Recorder and then convert the generated code for use with CUITe.

Visual UI Automation Verify can also be used to help figure out control types and automation property values.

http://uiautomationverify.codeplex.com

Coordinator
Oct 25, 2012 at 10:17 AM

Hi Martin,

Can you provide some sample html and coded ui test code that can reproduce the problem?

Thank you.

Oct 26, 2012 at 4:15 AM

Hi,

It’s a WPF app, so no HTML but the XAML is below along with the test code.

XAML

<ListView Tag="{Binding}" AutomationProperties.AutomationId="clientSearchResults" TargetUpdated="ListBox_TargetUpdated" TabIndex="1060" ItemsSource="{Binding Path=SearchResults, NotifyOnTargetUpdated=True}" SelectedValue="{Binding Path=SelectedClient}" d:LayoutOverrides="VerticalAlignment" Margin="40,173.362,0,0" Style="{DynamicResource Scrolling list}" Height="150" VerticalAlignment="Top" HorizontalAlignment="Left" Width="636" SelectionMode="Single" GridViewColumnHeader.Click="Listbox_Sort" MouseDoubleClick="ListView_MouseDoubleClick" IsTabStop="False">

<ListView.Resources>

<Style TargetType="ListViewItem">

<Setter Property="AutomationProperties.AutomationId">

<Setter.Value>

<MultiBinding StringFormat="client_{0}_name_{1}">

<Binding Path="Id" />

<Binding Path="Surname" />

</MultiBinding>

</Setter.Value>

</Setter>

</Style>

</ListView.Resources>

<ListView.View>

<GridView>

<GridViewColumn Header="First name(s)" DisplayMemberBinding="{Binding Path=FirstNames}" Width="150" />

<GridViewColumn Header="Surname" DisplayMemberBinding="{Binding Path=Surname}" Width="150"/>

<GridViewColumn Header="Client ID" DisplayMemberBinding="{Binding Path=Id}" Width="70">

<AutomationProperties.AutomationId>

<MultiBinding StringFormat="Row{0}_Link">

<Binding Path="Id" />

<Binding Path="LinkDescription" />

</MultiBinding>

</AutomationProperties.AutomationId>

</GridViewColumn>

<GridViewColumn Header="Date of birth" DisplayMemberBinding="{Binding Path=DateOfBirth, StringFormat=dd/MM/yyyy}" Width="90"/>

<GridViewColumn Header="Alias match" DisplayMemberBinding="{Binding Path=MatchedOnAlias, Converter={StaticResource boolToString}}" Width="85"/>

<GridViewColumn Header="Linked as" DisplayMemberBinding="{Binding Path=LinkDescription}" Width="70"/>

<GridViewColumn Header="Address" Width="100" DisplayMemberBinding="{Binding Path=Address}"/>

</GridView>

</ListView.View>

</ListView>

Test Code

CUITe_WpfWindow window = new CUITe_WpfWindow("Name=Legal Services Management System 3.3.0;ClassName~HwndWrapper");

var results = window.Get<CUITe_WpfTable>("AutomationId=clientSearchResults");

var row = (CUITe_WpfRow)results.RowsAsCUITe.Find(r => MatchingClient((CUITe_WpfRow)r, clientId));

row.Click();

The results.RowsAsCuite property returns an empty collection, but results.RowCount = 6!

Thanks

Martin

From: icnocop [email removed]
Sent: Thursday, 25 October 2012 10:17 p.m.
To: Martin White
Subject: Re: WPF Support [cuite:399681]

From: icnocop

Hi Martin,

Can you provide some sample html and coded ui test code that can reproduce the problem?

Thank you.

Coordinator
Oct 26, 2012 at 6:58 AM

Hi, Martin.

Thank you for the sample xaml and coded ui test code.

It seems that the Rows property is only for a DataGrid and not a ListView.

However, the functionality can still be performed in a slightly different manner.

I've checked in some sample integration tests that exercise this functionality named WpfListView_GetRow_CanClickOnRow and WpfListView_GetRow_CanClickOnCell.

Here they are for convenience:

        [TestMethod]
        public void WpfListView_GetRow_CanClickOnRow()
        {
            CUITe_WpfTable listView = mainWindow.Get<CUITe_WpfTable>("AutomationId=lv1");
            Assert.AreEqual(8, listView.RowCount);
            CUITe_WpfControl<WpfControl> control = listView.Get<CUITe_WpfControl<WpfControl>>("AutomationId=Item0");
            control.Click();
        }

        [TestMethod]
        public void WpfListView_GetRow_CanClickOnCell()
        {
            CUITe_WpfTable listView = mainWindow.Get<CUITe_WpfTable>("AutomationId=lv1");
            Assert.AreEqual(8, listView.RowCount);
            CUITe_WpfControl<WpfControl> control = listView.Get<CUITe_WpfControl<WpfControl>>("AutomationId=Item0");
            CUITe_WpfCell cell = control.Get<CUITe_WpfCell>("ColumnHeader=Content");
            cell.Click();
        }

Nov 2, 2012 at 3:03 AM

Works a treat, thanks so much!

From: icnocop [email removed]
Sent: Friday, 26 October 2012 6:59 p.m.
To: Martin White
Subject: Re: WPF Support [cuite:399681]

From: icnocop

Hi, Martin.

Thank you for the sample xaml and coded ui test code.

It seems that the Rows property is only for a DataGrid and not a ListView.

However, the functionality can still be performed in a slightly different manner.

I've checked in some sample integration tests that exercise this functionality named WpfListView_GetRow_CanClickOnRow and WpfListView_GetRow_CanClickOnCell.

Here they are for convenience:

        [TestMethod]
        public void WpfListView_GetRow_CanClickOnRow()
        {
            CUITe_WpfTable listView = mainWindow.Get<CUITe_WpfTable>("AutomationId=lv1");
            Assert.AreEqual(8, listView.RowCount);
            CUITe_WpfControl<WpfControl> control = listView.Get<CUITe_WpfControl<WpfControl>>("AutomationId=Item0");
            control.Click();
        }
 
        [TestMethod]
        public void WpfListView_GetRow_CanClickOnCell()
        {
            CUITe_WpfTable listView = mainWindow.Get<CUITe_WpfTable>("AutomationId=lv1");
            Assert.AreEqual(8, listView.RowCount);
            CUITe_WpfControl<WpfControl> control = listView.Get<CUITe_WpfControl<WpfControl>>("AutomationId=Item0");
            CUITe_WpfCell cell = control.Get<CUITe_WpfCell>("ColumnHeader=Content");
            cell.Click();
        }
Nov 12, 2012 at 3:14 AM

Hi again,

I’m now trying to do a similar thing with a ListBox (rather than a ListView) and while I can get to the row I want, I can’t get to the specific controls within the row. Both the row (ListBoxItem) and the control (TextBox) that I want to get at have unique AutomationIds, I can get the row and click on it but when I try to get the textbox I get the “Search may have failed at <ControlX> as it may have virtualized children. If the control being searched is descendant of <ControlX> then including it as the parent container may solve the problem.” error.

XAML for ListBox:

<ListBox x:Name="AlertDetailsList" AutomationProperties.AutomationId="alertDetailsList" Width="636" KeyboardNavigation.TabNavigation="Local" ItemsSource="{Binding Path=Alerts, NotifyOnTargetUpdated=True}" ScrollViewer.HorizontalScrollBarVisibility="Hidden" Tag="{Binding}" Height="Auto" BorderBrush="{x:Null}" Foreground="{x:Null}" IsTabStop="True" TabIndex="1010" HorizontalContentAlignment="Right">

<ListBox.Resources>

<Style TargetType="ListBoxItem">

<Setter Property="AutomationProperties.AutomationId">

<Setter.Value>

<MultiBinding StringFormat="alertId_{0}_sequenceId_{1}">

<Binding Path="AlertId" />

<Binding Path="NewAlertSequenceId" />

</MultiBinding>

</Setter.Value>

</Setter>

</Style>

</ListBox.Resources>

<ListBox.ItemTemplate>

<DataTemplate>

<templates:AlertRow/>

</DataTemplate>

</ListBox.ItemTemplate>

</ListBox>

XAML for AlertRow:

<common:CommonRowTemplate

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:common="clr-namespace:LSA.LSMS.Client.LSMSApplication.Common"

xmlns:LSA_LSMS_Client_Common_Controls="clr-namespace:LSA.LSMS.Client.Common.Controls;assembly=LSA.LSMS.Client.Common"

x:Class="LSA.LSMS.Client.LSMSApplication.Create.PersonalDetails.Templates.AlertRow"

xmlns:d="http://schemas.microsoft.com/expression/blend/2006"

Height="Auto" Width="Auto" Foreground="{x:Null}">

<Grid Background="#FFFFFFFF" HorizontalAlignment="Left" Width="626" VerticalAlignment="Top" Height="30">

<Grid.ColumnDefinitions>

<ColumnDefinition Width="592*" />

<ColumnDefinition Width="34*" />

</Grid.ColumnDefinitions>

<TextBox HorizontalAlignment="Stretch" VerticalAlignment="Center" MaxLength="60" x:Name="Alert_text_input_feild" Style="{DynamicResource Input feild}" Text="{Binding Path=AlertText}" TextWrapping="NoWrap" TabIndex="1000" Margin="1,0,131,0">

<AutomationProperties.AutomationId>

<MultiBinding StringFormat="alertText_alertId_{0}_sequenceId_{1}">

<Binding Path="AlertId" />

<Binding Path="NewAlertSequenceId" />

</MultiBinding >

</AutomationProperties.AutomationId>

</TextBox>

<ComboBox KeyDown="ComboBox_KeyDown" x:Name="Priority_combo" Style="{DynamicResource Dropdown}" ItemsSource="{Binding Path=PriorityIds}" DisplayMemberPath="DisplayName" SelectedValuePath="Id" SelectedValue="{Binding Path=PriorityId}" Width="125" TabIndex="1030" Margin="467,5,0,0">

<AutomationProperties.AutomationId>

<MultiBinding StringFormat="alertPriority_{0}">

<Binding Path="Alert.Id" />

</MultiBinding >

</AutomationProperties.AutomationId>

</ComboBox>

<CheckBox Grid.Column="1" IsChecked="{Binding Path=IsSelected}" HorizontalAlignment="Right" Margin="0,0,10,0" Style="{DynamicResource Check box}" VerticalAlignment="Center" Content="" x:Name="Delete" TabIndex="1050"/>

</Grid>

</common:CommonRowTemplate>

TestCode

CUITe_WpfTable listView = window.Get<CUITe_WpfTable>("AutomationId=alertDetailsList");

var row = listView.Get<CUITe_WpfControl<WpfControl>>(string.Format("AutomationId=alertId_{0}_sequenceId_{1}", 0, 1));

var alertTextBox = row.Get<CUITe_WpfEdit>(string.Format("AutomationId=alertText_alertId_{0}_sequenceId_{1}", 0, 1));

alertTextBox.Text = alertText;

Grateful for any suggestions…!

Thanks

Martin

From: icnocop [email removed]
Sent: Friday, 26 October 2012 6:59 p.m.
To: Martin White
Subject: Re: WPF Support [cuite:399681]

From: icnocop

Hi, Martin.

Thank you for the sample xaml and coded ui test code.

It seems that the Rows property is only for a DataGrid and not a ListView.

However, the functionality can still be performed in a slightly different manner.

I've checked in some sample integration tests that exercise this functionality named WpfListView_GetRow_CanClickOnRow and WpfListView_GetRow_CanClickOnCell.

Here they are for convenience:

        [TestMethod]
        public void WpfListView_GetRow_CanClickOnRow()
        {
            CUITe_WpfTable listView = mainWindow.Get<CUITe_WpfTable>("AutomationId=lv1");
            Assert.AreEqual(8, listView.RowCount);
            CUITe_WpfControl<WpfControl> control = listView.Get<CUITe_WpfControl<WpfControl>>("AutomationId=Item0");
            control.Click();
        }
 
        [TestMethod]
        public void WpfListView_GetRow_CanClickOnCell()
        {
            CUITe_WpfTable listView = mainWindow.Get<CUITe_WpfTable>("AutomationId=lv1");
            Assert.AreEqual(8, listView.RowCount);
            CUITe_WpfControl<WpfControl> control = listView.Get<CUITe_WpfControl<WpfControl>>("AutomationId=Item0");
            CUITe_WpfCell cell = control.Get<CUITe_WpfCell>("ColumnHeader=Content");
            cell.Click();
        }
Coordinator
Jan 20, 2013 at 2:23 AM

Hi Martin,

Can the Coded UI test recorder built-in to Visual Studio 2010 record and playback those actions without issues?

Thank you.

Jan 27, 2013 at 8:42 PM

Hi,

Sort of, but a different way. Originally, the test was coded using the built-in recorder rather than CUITe. The controls within the rows had a non-unique AutomationId (i.e. the same id in each row) and the developers used the UITestControl.PropertyNames.Instance search property to get the correct one. This worked fine on his machine, but not on the build server and I couldn't figure out why.

I then tried to use CUITe to go direct to the control with the desired row by giving it a totally unique AutomationId as described above. This just didn't seem to work, on any machine. I have worked around it by including the value of the control that I want in the AutomationId for the whole row, which does the job fine though is a little messy. I'm happy with this solution but if you happen to have something more elegant I'd be interested....!

Cheers

Martin

Coordinator
Jan 28, 2013 at 9:50 AM

Hi Martin,

Can you share a runnable version of the wpf application and complete test code?

 

Any code generated from using the Coded UI test recorder built into Visual Studio should be able to be "converted" to CUITe.

I have not personally used the UITestControl.PropertyNames.Instance search property, but it should be able to be included in a search filter using CUITe as well.

Visual UI Automation Verify can also be used to help figure out each controls' properties and their values.

http://uiautomationverify.codeplex.com

Thank you.