This is a migrated thread and some comments may be shown as answers.

Cannot Locate elements in GoXam canvas

13 Answers 148 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
David
Top achievements
Rank 1
David asked on 27 Sep 2016, 10:17 AM

Hi everyone,

I am trying to use Telerik Testing Framework to automate tests fpr a WPF application that has a drag and drop style "pallette" created using GoXam. I originally started using TestStudio, but got mixed results (sometimes the elements would be dragged and droppe, but not always), I believe that this is because it finds the elements based on location data.

I want to find the elements in the canvas via element data (automationId, type, text etc.), but whenever I try and search for an element by these attributes I get an element not found error. I tried with various different ways of finding the element (Find.ByTextContent, Find.ByAutomationID, Find.ByXamlExpression), but I always seem to get an element not found error.

I have posted my test and the section of the xaml that holds the element that I want to drag and drop below.

Any help would be hugely appreciated.

David

 

Xaml Section:

<DataTemplate x:Key="GroupTemplate">
                <Border BorderThickness="2" Background="{Binding Path=Data.Color}"
              BorderBrush="{Binding Path=Data.Color}"
              go:Part.SelectionAdorned="True"
              go:Part.Resizable= "{Binding Path=Data.Resizable, Mode=TwoWay}"
              go:Node.LocationElementName="myGroupPanel"
              go:Group.IsSubGraphExpanded="{Binding Path=Data.IsSubGraphExpanded, Mode=TwoWay}"
              go:Part.DropOntoBehavior="AddsToGroup">
                    <StackPanel Background="{Binding Path=Node.IsDropOntoAccepted, Converter={StaticResource theBrushChooser}}">
                        <StackPanel AutomationProperties.AutomationId="Canvas_Element" Orientation="Horizontal" HorizontalAlignment="Left">
                            <Button x:Name="myCollapseExpandButton"                                
                            Content="{Binding Path=Group.IsExpandedSubGraph, Converter={StaticResource theButtonConverter}}"
                            Width="20" Margin="0 0 5 0" Visibility="{Binding Path=Data.Visibility}">
                                <i:Interaction.Triggers>
                                    <i:EventTrigger EventName="Click">
                                        <i:InvokeCommandAction Command="{Binding Path=DataContext.CollapseExpandCommand, 
                                        RelativeSource={RelativeSource AncestorType={x:Type local:HierarchicalView}}, Mode=OneWay}" 
                                        CommandParameter="{Binding Path=Group}"/>
                                    </i:EventTrigger>
                                </i:Interaction.Triggers>
                            </Button>
                            <TextBox Text="{Binding Path=Data.Name}" FontWeight="Bold" go:Part.TextEditable="{Binding Path=Data.Editable}" FontSize="20" x:Name="TextBox" 
                                 IsEnabled="{Binding Path=Data.Editable}" Background="{Binding Path=Data.Color}">
                                <i:Interaction.Triggers>
                                    <i:EventTrigger EventName="LostFocus">
                                        <i:InvokeCommandAction Command="{Binding Path=DataContext.LostFocusCommand, 
                                        RelativeSource={RelativeSource AncestorType={x:Type local:HierarchicalView}}, Mode=OneWay}" 
                                        CommandParameter="{Binding Path=Node}"/>
                                    </i:EventTrigger>
                                    <i:EventTrigger EventName="GotFocus">
                                        <i:InvokeCommandAction Command="{Binding Path=DataContext.GotFocusCommand, 
                                        RelativeSource={RelativeSource AncestorType={x:Type local:HierarchicalView}}, Mode=OneWay}" 
                                        CommandParameter="{Binding Path=Node}"/>
                                    </i:EventTrigger>
                                </i:Interaction.Triggers>
                            </TextBox>
                            <ComboBox Height="20" Width="75" x:Name="combo" Margin="20 0 0 0" ItemsSource="{Binding Path=Data.Elements}" Visibility="{Binding Path=Data.Visibility}">
                                <i:Interaction.Triggers>
                                    <i:EventTrigger EventName="SelectionChanged">
                                        <i:InvokeCommandAction Command="{Binding Path=DataContext.SelectionChangedComboCommand, 
                                        RelativeSource={RelativeSource AncestorType={x:Type local:HierarchicalView}}, Mode=OneWay}" 
                                        CommandParameter="{Binding Path=Node}"/>
                                    </i:EventTrigger>
                                </i:Interaction.Triggers>
                            </ComboBox>
                        </StackPanel>
                        <go:GroupPanel x:Name="myGroupPanel" Padding="5" SurroundsMembersAfterDrop="True" />

                        <i:Interaction.Triggers>
                            <i:EventTrigger EventName="Drop">
                                <i:InvokeCommandAction Command="{Binding Path=DataContext.DropNodeCommand, 
                                            RelativeSource={RelativeSource AncestorType={x:Type local:HierarchicalView}}, Mode=OneWay}" 
                                            CommandParameter="{Binding Path=Node}"/>
                            </i:EventTrigger>
                        </i:Interaction.Triggers>

                    </StackPanel>
                    <go:Group.Layout>
                        <go:GridLayout WrappingColumn="1" CellSize="1 1" Conditions="Standard GroupSizeChanged"/>
                    </go:Group.Layout>
                </Border>
            </DataTemplate>
        </go:DataTemplateDictionary>

 

 

Test:

using ArtOfTest.WebAii.Controls.Xaml.Wpf;
using ArtOfTest.WebAii.Core;
using ArtOfTest.WebAii.Silverlight;
using ArtOfTest.WebAii.TestTemplates;
using ArtOfTest.WebAii.Wpf;
using ArtOfTest.WebAii.Xaml;
using ArtOfTest.WebAii.TestAttributes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ArtOfTest.WebAii.Controls.HtmlControls;
using ArtOfTest.WebAii.Controls.HtmlControls.HtmlAsserts;
using ArtOfTest.WebAii.Controls.Xaml.Wpf;


namespace UnitTestProject2
{

    [TestClass]
    public class TelerikNUnitTest1 : BaseWpfTest
    {

        private TestContext testContextInstance;

        public WpfApplication foo_app { get; private set; }


        [TestMethod]
        public void Open_Application()
        {
            
            //TestContext.WriteLine("Start Unit Test");
            /// Create my own Settings object and then modify the defaults
            Settings mySettings = new Settings();
            mySettings.ClientReadyTimeout = 60000;

            /// Use my Settings object to construct my Manager object
            Manager myManager = new Manager(mySettings);
            myManager.Start();

            // Launch the application instance from its location in file system
            WpfApplication foo_app = myManager.LaunchNewApplication(@"C:\PathTo\Startup.exe");
            //TestContext.WriteLine("Opened application");

            /// Validate the title of the homepage
            ArtOfTest.Common.UnitTesting.Assert.IsTrue(foo_app.MainWindow.Window.Caption.Equals("MainWindow"));
            //TestContext.WriteLine("Validated window");

            ///Click the new project button
            FrameworkElement newProjButton = foo_app.MainWindow.Find.ByTextContent("New project...");
            //TestContext.WriteLine("Found element");
            newProjButton.User.Click();

            ///Send keys to the project name textbox
            FrameworkElement projNameTextBox = foo_app.MainWindow.Find.ByAutomationId("New_Proj_Text_Box");

            projNameTextBox.Wait.ForNoMotion(5000);
            projNameTextBox.User.Click();
            projNameTextBox.User.TypeText("PokeMoon", 100);
                 
            ///Select the View family
            FrameworkElement viewElement = foo_app.MainWindow.Find.ByTextContent("View");
            viewElement.User.Click();

            ///Click the "Create" Button 
            FrameworkElement createButton = foo_app.MainWindow.Find.ByTextContent("Create");
            createButton.User.Click();

            /// Wait for the canvas to load 
            FrameworkElement hierarchicalViewPage = foo_app.MainWindow.Find.ByAutomationId("Hierarchical_View");
            hierarchicalViewPage.Wait.ForExists();

            ///Click on the PROCESS element and then drag it into the canvas
///This is where I cannot find the element.
            FrameworkElement processElement = foo_app.MainWindow.Find.ByExpression(new XamlFindExpression("Key='GroupTemplate'", "|", "textcontent='PROCESS', "|", 'AutomationProperties.AutomationId='Canvas_Element'" ")).As<FrameworkElement>(); ;

            processElement.Wait.ForNoMotion(5000);
            processElement.User.Click();
            processElement.User.DragTo(100, 100);
        }

    }

}

13 Answers, 1 is accepted

Sort by
0
Georgi
Telerik team
answered on 30 Sep 2016, 06:39 AM
Hello David,

It seems like the XamlFindExpression fails to find the element as it is inside DataTemplate and you are using the x:key of the DataTemplate in order to located it, but this would not work as the DataTemplate is not a visual element located in the visual tree as you can check with tools like Snoop.

So you can change your search to look for all stackpanels that have that AutomationId and get the one with text PROCESS in it:

foo_app.MainWindow.Find.AllByAutomationId<StackPanel>("Canvas_Element").FirstOrDefault(c => c.TextBlockContent.Contains("PROCESS"));

Also please note that TextBlockContent would give you the sum of all of the Text inside of given FrameworkElement (if there are two TextBlocks for example) and in some cases you might want to use the TextLiteralContent property instead.

Furthermore you could drag and drop one framework element to another with specific offsets using one of the DragTo method overrides.

Hope this would help.

Regards,
Georgi
Telerik by Progress
Do you need help with upgrading your AJAX, WPF or WinForms project? Check the Telerik API Analyzer and share your thoughts.
0
David
Top achievements
Rank 1
answered on 05 Oct 2016, 10:40 AM

Hi Georgi,

Firstly: many thanks for your help, it´s really appreciated.

When I try and use the above method to find the object I keep on getting a null reference exception (when I try and drag and drop the object or use Wait.ForExists();). I think this is due to the fact that the element is not loaded when the page is presented (it gets presented after the page is loaded).

Is there any way that I can manage this? I have tried: element.wait.forExist, element.wait.forVisible, element.waitForNoMotion, app..WaitForWindow("MainWindow", 10000).

Updated test code is detailed below (please note that the textbox has been removed from the element so I am now searching by "text"):

using ArtOfTest.WebAii.Controls.Xaml.Wpf;
using ArtOfTest.WebAii.Core;
using ArtOfTest.WebAii.Silverlight;
using ArtOfTest.WebAii.TestTemplates;
using ArtOfTest.WebAii.Wpf;
using ArtOfTest.WebAii.Xaml;
using ArtOfTest.WebAii.TestAttributes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ArtOfTest.WebAii.Controls.HtmlControls;
using ArtOfTest.WebAii.Controls.HtmlControls.HtmlAsserts;
using ArtOfTest.WebAii.Controls.Xaml.Wpf;


namespace UnitTestProject2
{

    [TestClass]
    public class TelerikNUnitTest1 : BaseWpfTest
    {

        private TestContext testContextInstance;

        public WpfApplication foo_app { get; private set; }


        [TestMethod]
        public void Open_Application()
        {
            
            //TestContext.WriteLine("Start Unit Test");
            /// Create my own Settings object and then modify the defaults
            Settings mySettings = new Settings();
            mySettings.ClientReadyTimeout = 60000;

            /// Use my Settings object to construct my Manager object
            Manager myManager = new Manager(mySettings);
            myManager.Start();

            // Launch the application instance from its location in file system
            WpfApplication foo_app = myManager.LaunchNewApplication(@"C:\PathTo\Startup.exe");
            //TestContext.WriteLine("Opened application");

            /// Validate the title of the homepage
            ArtOfTest.Common.UnitTesting.Assert.IsTrue(foo_app.MainWindow.Window.Caption.Equals("MainWindow"));
            //TestContext.WriteLine("Validated window");

            ///Click the new project button
            FrameworkElement newProjButton = foo_app.MainWindow.Find.ByTextContent("New project...");
            //TestContext.WriteLine("Found element");
            newProjButton.User.Click();

            ///Send keys to the project name textbox
            FrameworkElement projNameTextBox = foo_app.MainWindow.Find.ByAutomationId("New_Proj_Text_Box");

            projNameTextBox.Wait.ForNoMotion(5000);
            projNameTextBox.User.Click();
            projNameTextBox.User.TypeText("PokeMoon", 100);
                 
            ///Select the View family
            FrameworkElement viewElement = foo_app.MainWindow.Find.ByTextContent("View");
            viewElement.User.Click();

            ///Click the "Create" Button 
            FrameworkElement createButton = foo_app.MainWindow.Find.ByTextContent("Create");
            createButton.User.Click();

            ///Click on the PROCESS element and then drag it into the canvas
///This is where I cannot find the element.
            FrameworkElement processElement = foo_app.pokemoon_app.MainWindow.Find.AllByAutomationId<StackPanel>("Canvas_Element").FirstOrDefault(c => c.Text.Equals("PROCESS1"));
FrameworkElement activityElement = foo_app.pokemoon_app.MainWindow.Find.AllByAutomationId<StackPanel>("Canvas_Element").FirstOrDefault(c => c.Text.Equals("ACTIVITY1"));
            
processElement.Wait.ForVisible(10000);
            activityElement.Wait.ForVisible(10000)
            activityElement.User.DragTo(processElement);
        }

    }

}

0
Georgi
Telerik team
answered on 10 Oct 2016, 07:57 AM
Hello David,

It seems like the exception is raised because the element (PROGRESS1) is not found.
Indeed it could be happening because the element is not initially visible and in this cases usually refreshing the visual tree helps:

foo_app.RefreshVisualTrees();
foo_app.MainWindow.Wait.ForNoMotion(200);

Also you could create your own wait method and also you can check if switching to Contains instead of Equals would help:

StackPanel panel;
           for (int i = 0; i < 50; i++)
           {
               Thread.Sleep(350);
               foo_app.pokemoon_app.RefreshVisualTrees();
               panel = foo_app.pokemoon_app.MainWindow.Find.AllByAutomationId<StackPanel>("Canvas_Element").FirstOrDefault(c => c.Text.Contains("PROCESS1"));
               if (panel != null)
               {
                   break;
               }
           }

Looking forward to your reply.

Regards,
Georgi
Telerik by Progress
Do you need help with upgrading your AJAX, WPF or WinForms project? Check the Telerik API Analyzer and share your thoughts.
0
David
Top achievements
Rank 1
answered on 10 Oct 2016, 10:42 AM

Hi Georgi,

Many thanks for your quick reply. 

When I try and use 'foo_app.RefreshVisualTrees()' Visual Studio tells me that a WPF does not contain a reference to RefreshVisualTrees() and it says that I might be missing an assembly reference. Is there a different library/package that I need to use to be able to use RefreshVisualTrees?

 

Best regards,

David

0
David
Top achievements
Rank 1
answered on 11 Oct 2016, 06:19 AM

I also think that the way of finding the element is not working. As when I use the below wait method the Debug.WriteLine (just above the break statement is never triggered).

 

StackPanel panel;
            for (int i = 0; i < 50; i++)
            {
                Thread.Sleep(350);

                panel = foo_app.MainWindow.Find.AllByAutomationId<StackPanel>("Canvas_Element").FirstOrDefault(c => c.Text.Contains("PROCESS1"));
                if (panel != null)
                {
                    Debug.WriteLine("StackPanelFound");
                    break;

                }
            };

 

Many thanks for your help with this.

Best regards,

David

0
Georgi
Telerik team
answered on 11 Oct 2016, 07:56 AM
Hello David,

I am sorry for the mistake - you should refresh the MainWindow instead of the application:

foo_app.MainWindow.RefreshVisualTrees();
foo_app.MainWindow.Wait.ForNoMotion(200);

Also is that element in a separate visual tree (popup or another window)?
Nevertheless if the refresh don't help it would be great if you could show us(in video for example) what are the actions you take and where is that element located.

Hope this works.

Regards,
Georgi
Telerik by Progress
Do you need help with upgrading your AJAX, WPF or WinForms project? Check the Telerik API Analyzer and share your thoughts.
0
David
Top achievements
Rank 1
answered on 17 Oct 2016, 03:21 PM

Hi Georgi,

Thanks for your reply. I can now use the .RefreshVisualTrees() method. Unfortunately I can still still not access the element though. I think that may be bacuse the text that is shown in these elements is produced dynamicly by data context.

I am trying to use the script below to get at the data context in the node. Unfortunately though the line  p.node.Data does not work as the Telerik Stackpanel does not seem to to support data data context (in the way that we are using it). I tried to cast the Telerik Stackpanel into a normal stackpanel using ´System.Windows.Controls.StackPanel sp = (System.Windows.Controls.StackPanel)p;´ (where p is the telerik stackpanel), but I got the err message "Error CS0030 Cannot convert type 'ArtOfTest.WebAii.Controls.Xaml.Wpf.StackPanel' to 'System.Windows.Controls.StackPanel'".

Am i approaching this the right way (is there a better way to get at data context using testing framework)? Is there a way to cast/convert the type 'ArtOfTest.WebAii.Controls.Xaml.Wpf.StackPanel' to 'System.Windows.Controls.StackPanel'?

Many thanks for al your help.

Best regards,

David

0
David
Top achievements
Rank 1
answered on 17 Oct 2016, 03:22 PM

Sorry! The section of code is shown below:

IList <StackPanel> canvasElements = foo_app.MainWindow.Find.AllByAutomationId<ArtOfTest.WebAii.Controls.Xaml.Wpf.StackPanel>("Canvas_Element");

            StackPanel processElement;
            StackPanel activityElement;


            foreach (var p in canvasElements)
            {
                System.Windows.Controls.StackPanel sp = (System.Windows.Controls.StackPanel)p;
                Node node = Part.FindAncestor<Node>(p);
                ElementData data = p.node.Data;

                if (data.Text == "PROCESS1")
                {
                    Debug.WriteLine("The Process Canvas Element Has Been Found");
                    processElement = p;
                }

                if (data.Text == "ACTIVITY1")
                {
                    Debug.WriteLine("The Activity Canvas Element Has Been Found");
                    activityElement = p;
                }
            };

            Debug.WriteLine("Canvas Elements Found");
            Debug.WriteLine(processElement.AutomationId);
            activityElement.User.DragTo(processElement); 

0
Georgi
Telerik team
answered on 20 Oct 2016, 01:42 PM
Hello David,

You would not be able to cast objects of the Telerik Testing Framework types to their equivalent in WPF Framework as they are two very different objects. The Telerik classes are created with the purpose of wrapping the WPF controls in the manner that we are looking in to the WPF Visual Three and based on the information there (only visual elements) the Telerik wrappers are created.

That been said the approach for verifying should be based on the visual elements so you should not try to use the DataContext of the StackPanel but rather use the actual TextBlock holding the Text you are looking for. So being in DataTemplate should not matter that much as it is still located in the visual tree and it is usually a matter of looking in the correct tree and making sure it is up to date.

Unfortunately as the refresh of the visual tree does not help seems like this could be issue of looking in the wrong visual tree. So what could help in this case is if you could share more information on the application - image of the element you are trying to locate, image of the element inside of the visual tree using snoop for example. Isolating this scenario in a sample wpf application and sending it to us would also be of help.

Nevertheless you can check if removing the specification for StackPanel would help. Also you can check if the search for CanvasElement finds any canvases (AllByAutomationId would return an empty collection if that is the case).

​
var allPanels = foo_app.MainWindow.Find.AllByAutomationId("Canvas_Element");
var panel = allPanels.FirstOrDefault(c => c.Text.Contains("PROCESS1"));

Looking forward to your reply.

Regards,
Georgi
Telerik by Progress
Do you need help with upgrading your WPF project? Try the Telerik API Analyzer and share your thoughts!
0
David
Top achievements
Rank 1
answered on 03 Nov 2016, 07:53 AM

Hi Georgi,

I finally figured out what the issue was when selecting elemenrts by name/text. It seems that inside the stackpanel there was a textbox and that is what I needed to be looking for. I therefore had to define explicity that i was searching for a TextBox. 

TextBox canvasElement1 = foo_app.MainWindow.Find.AllByAutomationId<TextBox>("Canvas_Element").FirstOrDefault(c => c.Text.Contains("PROCESS"));

The only problem now is that I can select the elements, but when I try and use canvasElement1.User.DragTo(canvasElement2); it throws another null ref exception. I am presuming this is because as canvasElement2 is not a FrameWork element.

I guess that I need to be able to select the stackpanel FrameworkElement based on the textbox that it holds inside, but I am not sure of how to do that.

Many thanks for all your help on this.

Best regards,

David

0
Georgi
Telerik team
answered on 03 Nov 2016, 02:25 PM
Hi David,

The null reference exception is thrown when you are using something that is null as for example in your case canvasElement1 or canvasElement2. You can debug the test and check which one is null and therefore check why it is null (usually it is not located). So the type of the element should not have any connection to this exception.

Nevertheless if none of them is null and the stack trace leads to somewhere in the TelerikTestingFramework it would be helpful if you could send it to us so we could investigate it further.

From the code you have provided I can see that finding the StackPanel before failed as the AutomationId in the WPF application is set to the Textbox not the StackPanel. So if that is the case you can set AutomationId to the StackPanel itself and use similar expression to locate it (assuming that the TextBox is child of the StackPanel):

var stackPanel= foo_app.MainWindow.Find.AllByAutomationId("StackPanelAutomationID").FirstOrDefault(p => p.Text.Contains("PROCESS"));


Hope this would help.

Regards,
Georgi
Telerik by Progress
Do you need help with upgrading your WPF project? Try the Telerik API Analyzer and share your thoughts!
0
David
Top achievements
Rank 1
answered on 10 Nov 2016, 07:10 AM

Hi Georgi,

I managed to figure out what it how to select elements. I needed to select a framework element that had a textbox inside and then use the wait method that you described.

FrameworkElement el1;
          for (int i = 0; i < 50; i++)
          {
              Thread.Sleep(350);
              foo_app.MainWindow.RefreshVisualTrees();
              el1 = foo_app.MainWindow.Find.AllByAutomationId<ArtOfTest.WebAii.Controls.Xaml.Wpf.TextBox>("Canvas_Element_TextBox").FirstOrDefault(c => c.Text.Contains(element1Name));
              if (el1 != null)
              {
                  break;
              }
          }
 
          el1 = foo_app.MainWindow.Find.AllByAutomationId<ArtOfTest.WebAii.Controls.Xaml.Wpf.TextBox>("Canvas_Element_TextBox").FirstOrDefault(c => c.Text.Contains(element1Name));

 

In the end we have removed the drag and drop functionality in the canvas (for the time being) so I can´t try this funcionality right now unfortunately.

Many thanks for all your help with this,

David

0
Georgi
Telerik team
answered on 10 Nov 2016, 07:32 AM
Hi David,

I am glad that you were able to figure it out.

Also if you have any other questions or you get to try the drag and drop, please don't hesitate to write us.

Regards,
Georgi
Telerik by Progress
Do you need help with upgrading your WPF project? Try the Telerik API Analyzer and share your thoughts!
Tags
General Discussions
Asked by
David
Top achievements
Rank 1
Answers by
Georgi
Telerik team
David
Top achievements
Rank 1
Share this question
or