Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AutomationElement Enter functionality and XPath support implemented.. #391

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,7 @@ packages
# our output folder for build artifacts
build

Thumbs.db
Thumbs.db

# sample project
src/WhiteProject/*
5 changes: 5 additions & 0 deletions src/.nuget/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NuGet.Build" version="2.8.1" />
<package id="NuGet.CommandLine" version="3.3.0" />
</packages>
23 changes: 17 additions & 6 deletions src/TestStack.White.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.40629.0
# Visual Studio Express 2013 for Windows Desktop
VisualStudioVersion = 12.0.21005.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1BCBA4C4-8C47-496C-B0D4-FDE9D066ED27}"
ProjectSection(SolutionItems) = preProject
Expand Down Expand Up @@ -57,6 +57,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinformsTodo", "Samples\Win
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Todo.Core", "Samples\Todo.Core\Todo.Core.csproj", "{F5604A48-3BD0-4418-83F1-3ECC03DD2FF0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{81F4B351-6572-4CEA-9E39-B1978CB6DFA2}"
ProjectSection(SolutionItems) = preProject
.nuget\packages.config = .nuget\packages.config
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhiteProject", "WhiteProject\WhiteProject.csproj", "{39964358-1A5B-4F78-9520-1B106F70CE72}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -131,20 +138,24 @@ Global
{F5604A48-3BD0-4418-83F1-3ECC03DD2FF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5604A48-3BD0-4418-83F1-3ECC03DD2FF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5604A48-3BD0-4418-83F1-3ECC03DD2FF0}.Release|Any CPU.Build.0 = Release|Any CPU
{39964358-1A5B-4F78-9520-1B106F70CE72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{39964358-1A5B-4F78-9520-1B106F70CE72}.Debug|Any CPU.Build.0 = Debug|Any CPU
{39964358-1A5B-4F78-9520-1B106F70CE72}.Release|Any CPU.ActiveCfg = Release|Any CPU
{39964358-1A5B-4F78-9520-1B106F70CE72}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{24929CE3-4000-4600-8830-503BE6A2BA42} = {1AF6AEE7-C9D5-4B05-BEFE-AB61D83667D4}
{492E6340-32CC-4D03-A9C4-36FB4C40CF5C} = {1AF6AEE7-C9D5-4B05-BEFE-AB61D83667D4}
{DFCF10D7-82C1-4916-A829-B520E8C65321} = {37EE7583-778A-481E-ADD4-52A357BE95F0}
{5620401C-0857-4F7C-869A-F3F56DF1697F} = {37EE7583-778A-481E-ADD4-52A357BE95F0}
{EC32732F-6CB9-4078-B3A8-E3F038D5C2E1} = {1AF6AEE7-C9D5-4B05-BEFE-AB61D83667D4}
{C186FEFC-D0B7-4FED-822D-688302B3B8A0} = {1AF6AEE7-C9D5-4B05-BEFE-AB61D83667D4}
{DFCF10D7-82C1-4916-A829-B520E8C65321} = {37EE7583-778A-481E-ADD4-52A357BE95F0}
{5620401C-0857-4F7C-869A-F3F56DF1697F} = {37EE7583-778A-481E-ADD4-52A357BE95F0}
{F5604A48-3BD0-4418-83F1-3ECC03DD2FF0} = {37EE7583-778A-481E-ADD4-52A357BE95F0}
{D0ED95E7-584A-45B9-B8E2-1A7ADD78C366} = {DFCF10D7-82C1-4916-A829-B520E8C65321}
{3CC2654B-2108-4A38-AFFF-82718703EBE3} = {5620401C-0857-4F7C-869A-F3F56DF1697F}
{51509F9D-12C4-4043-A68F-16A300F38FDB} = {5620401C-0857-4F7C-869A-F3F56DF1697F}
{D0ED95E7-584A-45B9-B8E2-1A7ADD78C366} = {DFCF10D7-82C1-4916-A829-B520E8C65321}
{F5604A48-3BD0-4418-83F1-3ECC03DD2FF0} = {37EE7583-778A-481E-ADD4-52A357BE95F0}
EndGlobalSection
EndGlobal
15 changes: 15 additions & 0 deletions src/TestStack.White/AutomationElementSearch/DescendantFinder.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
using Castle.Core.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Automation;
using TestStack.White.Configuration;
using TestStack.White.UIItems.Finders;

namespace TestStack.White.AutomationElementSearch
{
public class DescendantFinder : IDescendantFinder
{
private readonly AutomationElement automationElement;
private readonly ILogger logger = CoreAppXmlConfiguration.Instance.LoggerFactory.Create(typeof(DescendantFinder));

public DescendantFinder(AutomationElement automationElement)
{
Expand All @@ -28,6 +32,17 @@ public virtual AutomationElement Descendant(Condition condition)
public virtual List<AutomationElement> Descendants(AutomationSearchCondition automationSearchCondition)
{
var collection = automationElement.FindAll(TreeScope.Descendants, automationSearchCondition.Condition);

//Automation elements identified in current window...
//AutomationElement[] elementsArray = new AutomationElement[collection.Count];

//collection.CopyTo(elementsArray, 0);

//foreach (AutomationElement e in elementsArray)
//{
// logger.InfoFormat("Element Automation Id: ({0})..", e.Current.AutomationId);
//}

var enumerable = collection.Cast<AutomationElement>();
return new List<AutomationElement>(enumerable);
}
Expand Down
3 changes: 2 additions & 1 deletion src/TestStack.White/Interceptors/CoreInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ public virtual void Intercept(IInvocation invocation)
{
CoreAppXmlConfiguration.Instance.Interceptors.Process(invocation, coreInterceptContext);
}
catch (Exception)
catch (Exception e)
{
logger.Error(e.Message);
logger.Error(DynamicProxyInterceptors.ToString(invocation));
throw;
}
Expand Down
4 changes: 4 additions & 0 deletions src/TestStack.White/Mappings/ControlDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ public virtual Type GetTestControlType(string className, string name, ControlTyp
if (isPrimary.Length == 1)
return isPrimary.Single().TestControlType;

//Get the first TestControldType when 'dictionaryItems' contains multiple elements...
var isFirstItem = dictionaryItems.First();
return isFirstItem.TestControlType;

throw new ControlDictionaryException(string.Format(
"Multiple TestControls found for ControlType={0} and FrameworkId:{1} - {2}",
controlType.LocalizedControlType, frameWorkId,
Expand Down
7 changes: 6 additions & 1 deletion src/TestStack.White/TestStack.White.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,13 @@
<NoWarn>1591, 1570</NoWarn>
</PropertyGroup>
<ItemGroup>
<Reference Include="Castle.Core">
<Reference Include="Castle.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\packages\Castle.Core.3.3.0\lib\net40-client\Castle.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Castle.Windsor, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\packages\Castle.Windsor.3.3.0\lib\net40\Castle.Windsor.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.configuration" />
Expand Down
69 changes: 69 additions & 0 deletions src/TestStack.White/UIItems/Finders/SearchConditionFactory.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using System;
using System.Linq;
using System.Windows.Automation;
using System.Xml;
using System.Xml.XPath;
using TestStack.White.Mappings;
using TestStack.White.UIItems.Custom;
using TestStack.White.UIItems.WindowItems;

namespace TestStack.White.UIItems.Finders
{
Expand Down Expand Up @@ -41,6 +44,72 @@ public static SearchCondition CreateForAutomationId(string id)
new AutomationElementProperty(id, "AutomationId", AutomationElement.AutomationIdProperty));
}

/// <summary>
/// Getting the objects (AutomationElement) hierarchy of the given window
/// Create SimpleSearchCondition with AutomationId for given xpath
/// </summary>
/// <param name="xpath"></param>
/// <param name="window"></param>
/// <returns></returns>
public static SearchCondition CreateForXPath(string xpath, Window window)
{
XmlDocument xmlDoc = new XmlDocument();
string hierarchy = window.GetHierarchy(window.AutomationElement);

xmlDoc.LoadXml(hierarchy);
XmlNodeList nodeList = xmlDoc.SelectNodes(xpath);
if (nodeList.Count == 0)
{
throw new Exception("No element found for given XPath: " + xpath);
}
else if (nodeList.Count > 1)
{
throw new Exception("Multiple elements found for given XPath: " + xpath);
}
else
{
foreach (XmlNode node in nodeList)
{
var automationId = node.Attributes["AutomationId"].Value;
var className = node.Attributes["ClassName"].Value;
//var controlType = node.Attributes["ControlType"].Value;
var frameworkId = node.Attributes["FrameworkId"].Value;
var name = node.Attributes["Name"].Value;
var helpText = node.Attributes["HelpText"].Value;

if (!String.IsNullOrEmpty(name))
{
return new SimpleSearchCondition(automationElement => automationElement.Current.Name,
new AutomationElementProperty(name, "Name", AutomationElement.NameProperty));
}
else if (!String.IsNullOrEmpty(automationId))
{
return new SimpleSearchCondition(automationElement => automationElement.Current.AutomationId,
new AutomationElementProperty(automationId, "AutomationId", AutomationElement.AutomationIdProperty));
}
else if (!String.IsNullOrEmpty(helpText))
{
return new SimpleSearchCondition(automationElement => automationElement.Current.HelpText,
new AutomationElementProperty(helpText, "HelpText", AutomationElement.HelpTextProperty));
}
else if (!String.IsNullOrEmpty(className))
{
return new SimpleSearchCondition(automationElement => automationElement.Current.ClassName,
new AutomationElementProperty(className, "ClassName", AutomationElement.ClassNameProperty));
}
else if (!String.IsNullOrEmpty(frameworkId))
{
return new SimpleSearchCondition(automationElement => automationElement.Current.FrameworkId,
new AutomationElementProperty(frameworkId, "FrameworkId", AutomationElement.FrameworkIdProperty));
}


}
}

throw new Exception("Invalid Xpath");
}

public static SearchCondition CreateForName(string name)
{
return new SimpleSearchCondition(automationElement => automationElement.Current.Name, new AutomationElementProperty(name, "Name", AutomationElement.NameProperty));
Expand Down
12 changes: 12 additions & 0 deletions src/TestStack.White/UIItems/Finders/SearchCriteria.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Windows.Automation;
using TestStack.White.AutomationElementSearch;
using TestStack.White.UIItems.Custom;
using TestStack.White.UIItems.WindowItems;
using TestStack.White.UIItems.WindowStripControls;

namespace TestStack.White.UIItems.Finders
Expand Down Expand Up @@ -72,6 +73,17 @@ public static SearchCriteria ByAutomationId(string identification)
return new SearchCriteria(SearchConditionFactory.CreateForAutomationId(identification));
}

/// <summary>
/// Create criteria for specified window with specified xpath
/// </summary>
/// <param name="xpath"></param>
/// <param name="window"></param>
/// <returns></returns>
public static SearchCriteria ByXPath(string xpath, Window window)
{
return new SearchCriteria(SearchConditionFactory.CreateForXPath(xpath, window));
}

public static SearchCriteria ByFramework(string framework)
{
return new SearchCriteria(SearchConditionFactory.CreateForFrameworkId(framework));
Expand Down
71 changes: 59 additions & 12 deletions src/TestStack.White/UIItems/ListBoxItems/ComboBox.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Windows.Automation;
using TestStack.White.AutomationElementSearch;
using TestStack.White.Recording;
Expand Down Expand Up @@ -67,26 +68,72 @@ private AutomationElement EditableElement()

public override void Select(string itemText)
{
if (!Enabled)
//if (!Enabled)
//{
// Logger.WarnFormat("Could not select {0}in {1} since it is disabled", itemText, Name);
// return;
//}
//if (Equals(itemText, SelectedItemText)) return;
//base.Select(itemText);

AutomationElement comboboxList = this.AutomationElement.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.ControlTypeProperty,
ControlType.List));

AutomationElementCollection comboboxItem = comboboxList.FindAll(TreeScope.Children,
new PropertyCondition(AutomationElement.ControlTypeProperty,
ControlType.ListItem));

AutomationElement[] itemArray = new AutomationElement[comboboxItem.Count];
comboboxItem.CopyTo(itemArray, 0);

AutomationElement itemToSelect = null;

for (int i = 0; i < itemArray.Length; i++)
{
if (itemArray[i].Current.Name.Equals(itemText))
{
itemToSelect = itemArray[i];
}
}

if (itemToSelect != null)
{
Logger.WarnFormat("Could not select {0}in {1} since it is disabled", itemText, Name);
return;
Object selectPattern = null;
if (itemToSelect.TryGetCurrentPattern(SelectionItemPattern.Pattern, out selectPattern))
{
((SelectionItemPattern)selectPattern).Select();
}
}
if (Equals(itemText, SelectedItemText)) return;
base.Select(itemText);
}

public override void Select(int index)
{
if (!Enabled)
//if (!Enabled)
//{
// Logger.Warn("Could not select " + index + "in " + Name + " since it is disabled");
// return;
//}
//base.Select(index);
//var p = (ExpandCollapsePattern) this.AutomationElement.GetCurrentPattern(ExpandCollapsePattern.Pattern);
//if (p.Current.ExpandCollapseState.Equals(ExpandCollapseState.Expanded))
// p.Collapse();

AutomationElement comboboxList = this.AutomationElement.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.ControlTypeProperty,
ControlType.List));

AutomationElementCollection comboboxItem = comboboxList.FindAll(TreeScope.Children,
new PropertyCondition(AutomationElement.ControlTypeProperty,
ControlType.ListItem));

AutomationElement itemToSelect = comboboxItem[index];

Object selectPattern = null;
if (itemToSelect.TryGetCurrentPattern(SelectionItemPattern.Pattern, out selectPattern))
{
Logger.Warn("Could not select " + index + "in " + Name + " since it is disabled");
return;
((SelectionItemPattern)selectPattern).Select();
}
base.Select(index);
var p = (ExpandCollapsePattern) this.AutomationElement.GetCurrentPattern(ExpandCollapsePattern.Pattern);
if (p.Current.ExpandCollapseState.Equals(ExpandCollapseState.Expanded))
p.Collapse();
}

public override void HookEvents(IUIItemEventListener eventListener)
Expand Down
6 changes: 4 additions & 2 deletions src/TestStack.White/UIItems/UIItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,10 @@ public virtual void SetValue(object value)
/// </summary>
public virtual void Enter(string value)
{
var pattern = Pattern(ValuePattern.Pattern) as ValuePattern;
if (pattern != null) pattern.SetValue(string.Empty);
//
//var pattern = Pattern(ValuePattern.Pattern) as ValuePattern;
//if (pattern != null) pattern.SetValue(string.Empty);

if (string.IsNullOrEmpty(value)) return;

actionListener.ActionPerformed(Action.WindowMessage);
Expand Down
4 changes: 3 additions & 1 deletion src/TestStack.White/UIItems/UIItemCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ public UIItemCollection(IEnumerable automationElements, IActionListener actionLi
var uiItem = DictionaryMappedItemFactory.Create(automationElement, actionListener, customItemType);
if (uiItem != null) Add(uiItem);
}
catch (ControlDictionaryException)
catch (ControlDictionaryException e)
{
//Printing the Bease exception message...
logger.Warn(e.GetBaseException().Message);
logger.WarnFormat("Couldn't create UIItem for AutomationElement, {0}", automationElement.Display());
}
}
Expand Down
Loading