diff --git a/Tests/AbsoluteXPath/AbsoluteXPath.csproj b/Tests/AbsoluteXPath/AbsoluteXPath.csproj
new file mode 100644
index 00000000..e982634b
--- /dev/null
+++ b/Tests/AbsoluteXPath/AbsoluteXPath.csproj
@@ -0,0 +1,81 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {971BE3D2-1EFF-4660-A8A8-C1A52966572C}
+ Library
+ Properties
+ AbsoluteXPath
+ AbsoluteXPath
+ v4.6.1
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 15.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ packages\Appium.WebDriver.4.0.0.1-beta\lib\net45\appium-dotnet-driver.dll
+
+
+ packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
+
+
+ packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
+
+
+
+
+ packages\Selenium.WebDriver.3.11.2\lib\net45\WebDriver.dll
+
+
+
+
+ CommonTestSettings.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Tests/AbsoluteXPath/AbsoluteXPath.sln b/Tests/AbsoluteXPath/AbsoluteXPath.sln
new file mode 100644
index 00000000..ecbac48c
--- /dev/null
+++ b/Tests/AbsoluteXPath/AbsoluteXPath.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.329
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AbsoluteXPath", "AbsoluteXPath.csproj", "{971BE3D2-1EFF-4660-A8A8-C1A52966572C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {971BE3D2-1EFF-4660-A8A8-C1A52966572C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {971BE3D2-1EFF-4660-A8A8-C1A52966572C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {971BE3D2-1EFF-4660-A8A8-C1A52966572C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {971BE3D2-1EFF-4660-A8A8-C1A52966572C}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {4E2EBE62-05BC-42C6-8002-CB40A0D39472}
+ EndGlobalSection
+EndGlobal
diff --git a/Tests/AbsoluteXPath/Alarms.cs b/Tests/AbsoluteXPath/Alarms.cs
new file mode 100644
index 00000000..b7df4b76
--- /dev/null
+++ b/Tests/AbsoluteXPath/Alarms.cs
@@ -0,0 +1,218 @@
+//******************************************************************************
+//
+// Copyright (c) 2019 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//******************************************************************************
+
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OpenQA.Selenium;
+
+namespace AbsoluteXPath
+{
+ [TestClass]
+ public class Alarms
+ {
+ [TestMethod]
+ public void TestMethod1()
+ {
+ DesktopSession desktopSession = new DesktopSession();
+ TimeSpan.FromSeconds(2);
+
+ bool bSuccess = false;
+
+ try
+ {
+ // LeftClick on Button "Type here to search" at (59,39)
+ Console.WriteLine("LeftClick on Button \"Type here to search\" at (59,39)");
+ string xpath_LeftClickButtonTypehereto_59_39 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[@ClassName=\"Shell_TrayWnd\"][@Name=\"Taskbar\"]/Pane[@ClassName=\"TrayDummySearchControl\"]/Button[@ClassName=\"Button\"][@Name=\"Type here to search\"]";
+ var winElem_LeftClickButtonTypehereto_59_39 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonTypehereto_59_39);
+ if (winElem_LeftClickButtonTypehereto_59_39 != null)
+ {
+ winElem_LeftClickButtonTypehereto_59_39.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonTypehereto_59_39}");
+ return;
+ }
+
+
+ // KeyboardInput VirtualKeys=""alarms"Keys.Space + Keys.SpaceKeys.Shift + "7" + Keys.ShiftKeys.Space + Keys.Space"clock"" CapsLock=False NumLock=False ScrollLock=False
+ Console.WriteLine("KeyboardInput VirtualKeys=\"\"alarms\"Keys.Space + Keys.SpaceKeys.Shift + \"7\" + Keys.ShiftKeys.Space + Keys.Space\"clock\"\" CapsLock=False NumLock=False ScrollLock=False");
+ System.Threading.Thread.Sleep(1000);
+ winElem_LeftClickButtonTypehereto_59_39.SendKeys("alarms");
+ winElem_LeftClickButtonTypehereto_59_39.SendKeys(Keys.Space + Keys.Space);
+ winElem_LeftClickButtonTypehereto_59_39.SendKeys(Keys.Shift + "7" + Keys.Shift);
+ winElem_LeftClickButtonTypehereto_59_39.SendKeys(Keys.Space + Keys.Space);
+ winElem_LeftClickButtonTypehereto_59_39.SendKeys("clock");
+
+
+ // LeftClick on ListItem "Alarms & Clock, Trusted Microsoft Store app, Press right to swit" at (126,13)
+ Console.WriteLine("LeftClick on ListItem \"Alarms & Clock, Trusted Microsoft Store app, Press right to swit\" at (126,13)");
+ string xpath_LeftClickListItemAlarmsCloc_126_13 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Cortana\"]/Pane[@ClassName=\"WebView\"][@Name=\"Bing\"]/Pane[@Name=\"Bing\"]/List[@Name=\"Results\"][@AutomationId=\"suggestionsList\"]/ListItem[starts-with(@Name,\"Alarms & Clock, Trusted Microsoft Store app, Press right to swit\")][starts-with(@AutomationId,\"id_\")]";
+ var winElem_LeftClickListItemAlarmsCloc_126_13 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickListItemAlarmsCloc_126_13);
+ if (winElem_LeftClickListItemAlarmsCloc_126_13 != null)
+ {
+ winElem_LeftClickListItemAlarmsCloc_126_13.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickListItemAlarmsCloc_126_13}");
+ return;
+ }
+
+
+ // LeftClick on ListItem "Alarm" at (56,17)
+ Console.WriteLine("LeftClick on ListItem \"Alarm\" at (56,17)");
+ string xpath_LeftClickListItemAlarm_56_17 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Alarms & Clock\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Alarms & Clock\"]/List[@AutomationId=\"TopNavMenuItemsHost\"]/ListItem[@Name=\"Alarm\"][@AutomationId=\"AlarmButton\"]";
+ var winElem_LeftClickListItemAlarm_56_17 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickListItemAlarm_56_17);
+ if (winElem_LeftClickListItemAlarm_56_17 != null)
+ {
+ winElem_LeftClickListItemAlarm_56_17.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickListItemAlarm_56_17}");
+ return;
+ }
+
+
+ // LeftClick on ListItem "Clock" at (54,10)
+ Console.WriteLine("LeftClick on ListItem \"Clock\" at (54,10)");
+ string xpath_LeftClickListItemClock_54_10 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Alarms & Clock\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Alarms & Clock\"]/List[@AutomationId=\"TopNavMenuItemsHost\"]/ListItem[@Name=\"Clock\"][@AutomationId=\"ClockButton\"]";
+ var winElem_LeftClickListItemClock_54_10 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickListItemClock_54_10);
+ if (winElem_LeftClickListItemClock_54_10 != null)
+ {
+ winElem_LeftClickListItemClock_54_10.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickListItemClock_54_10}");
+ return;
+ }
+
+
+ // LeftClick on ListItem "Timer" at (56,21)
+ Console.WriteLine("LeftClick on ListItem \"Timer\" at (56,21)");
+ string xpath_LeftClickListItemTimer_56_21 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Alarms & Clock\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Alarms & Clock\"]/List[@AutomationId=\"TopNavMenuItemsHost\"]/ListItem[@Name=\"Timer\"][@AutomationId=\"TimerButton\"]";
+ var winElem_LeftClickListItemTimer_56_21 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickListItemTimer_56_21);
+ if (winElem_LeftClickListItemTimer_56_21 != null)
+ {
+ winElem_LeftClickListItemTimer_56_21.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickListItemTimer_56_21}");
+ return;
+ }
+
+
+ // LeftClick on ListItem "Stopwatch" at (71,24)
+ Console.WriteLine("LeftClick on ListItem \"Stopwatch\" at (71,24)");
+ string xpath_LeftClickListItemStopwatch_71_24 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Alarms & Clock\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Alarms & Clock\"]/List[@AutomationId=\"TopNavMenuItemsHost\"]/ListItem[@Name=\"Stopwatch\"][@AutomationId=\"StopwatchButton\"]";
+ var winElem_LeftClickListItemStopwatch_71_24 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickListItemStopwatch_71_24);
+ if (winElem_LeftClickListItemStopwatch_71_24 != null)
+ {
+ winElem_LeftClickListItemStopwatch_71_24.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickListItemStopwatch_71_24}");
+ return;
+ }
+
+
+ // LeftClick on Button "Reset" at (16,23)
+ Console.WriteLine("LeftClick on Button \"Reset\" at (16,23)");
+ string xpath_LeftClickButtonReset_16_23 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Alarms & Clock\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Alarms & Clock\"]/Button[@Name=\"Reset\"][@AutomationId=\"StopWatchResetButton\"]";
+ var winElem_LeftClickButtonReset_16_23 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonReset_16_23);
+ if (winElem_LeftClickButtonReset_16_23 != null)
+ {
+ winElem_LeftClickButtonReset_16_23.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonReset_16_23}");
+ return;
+ }
+
+
+ // LeftClick on Button "Start" at (40,25)
+ Console.WriteLine("LeftClick on Button \"Start\" at (40,25)");
+ string xpath_LeftClickButtonStart_40_25 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Alarms & Clock\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Alarms & Clock\"]/Button[@Name=\"Start\"][@AutomationId=\"StopwatchPlayPauseButton\"]";
+ var winElem_LeftClickButtonStart_40_25 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonStart_40_25);
+ if (winElem_LeftClickButtonStart_40_25 != null)
+ {
+ winElem_LeftClickButtonStart_40_25.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonStart_40_25}");
+ return;
+ }
+
+
+ // LeftClick on Button "Pause" at (34,28)
+ Console.WriteLine("LeftClick on Button \"Pause\" at (34,28)");
+ string xpath_LeftClickButtonPause_34_28 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Alarms & Clock\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Alarms & Clock\"]/Button[@Name=\"Pause\"][@AutomationId=\"StopwatchPlayPauseButton\"]";
+ var winElem_LeftClickButtonPause_34_28 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonPause_34_28);
+ if (winElem_LeftClickButtonPause_34_28 != null)
+ {
+ winElem_LeftClickButtonPause_34_28.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonPause_34_28}");
+ return;
+ }
+
+
+ // LeftClick on Button "Reset" at (25,25)
+ Console.WriteLine("LeftClick on Button \"Reset\" at (25,25)");
+ string xpath_LeftClickButtonReset_25_25 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Alarms & Clock\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Alarms & Clock\"]/Button[@Name=\"Reset\"][@AutomationId=\"StopWatchResetButton\"]";
+ var winElem_LeftClickButtonReset_25_25 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonReset_25_25);
+ if (winElem_LeftClickButtonReset_25_25 != null)
+ {
+ winElem_LeftClickButtonReset_25_25.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonReset_25_25}");
+ return;
+ }
+
+
+ // LeftClick on Button "Close Alarms & Clock" at (19,22)
+ Console.WriteLine("LeftClick on Button \"Close Alarms & Clock\" at (19,22)");
+ string xpath_LeftClickButtonCloseAlarm_19_22 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Alarms & Clock\"]/Window[@Name=\"Alarms & Clock\"][@AutomationId=\"TitleBar\"]/Button[@Name=\"Close Alarms & Clock\"][@AutomationId=\"Close\"]";
+ var winElem_LeftClickButtonCloseAlarm_19_22 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonCloseAlarm_19_22);
+ if (winElem_LeftClickButtonCloseAlarm_19_22 != null)
+ {
+ winElem_LeftClickButtonCloseAlarm_19_22.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonCloseAlarm_19_22}");
+ return;
+ }
+
+ bSuccess = true;
+ }
+ finally
+ {
+ Assert.AreEqual(bSuccess, true);
+ }
+ }
+ }
+}
diff --git a/Tests/AbsoluteXPath/Calculator.cs b/Tests/AbsoluteXPath/Calculator.cs
new file mode 100644
index 00000000..830c410c
--- /dev/null
+++ b/Tests/AbsoluteXPath/Calculator.cs
@@ -0,0 +1,169 @@
+//******************************************************************************
+//
+// Copyright (c) 2019 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//******************************************************************************
+
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace AbsoluteXPath
+{
+ [TestClass]
+ public class Calculator
+ {
+ [TestMethod]
+ public void TestMethod1()
+ {
+ DesktopSession desktopSession = new DesktopSession();
+ TimeSpan.FromSeconds(2);
+
+ bool bSuccess = false;
+
+ try
+ {
+ // LeftClick on Button "Type here to search" at (95,20)
+ Console.WriteLine("LeftClick on Button \"Type here to search\" at (95,20)");
+ string xpath_LeftClickButtonTypehereto_95_20 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[@ClassName=\"Shell_TrayWnd\"][@Name=\"Taskbar\"]/Pane[@ClassName=\"TrayDummySearchControl\"]/Button[@ClassName=\"Button\"][@Name=\"Type here to search\"]";
+ var winElem_LeftClickButtonTypehereto_95_20 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonTypehereto_95_20);
+ if (winElem_LeftClickButtonTypehereto_95_20 != null)
+ {
+ winElem_LeftClickButtonTypehereto_95_20.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonTypehereto_95_20}");
+ return;
+ }
+
+
+ // KeyboardInput VirtualKeys=""calculator"" CapsLock=False NumLock=False ScrollLock=False
+ Console.WriteLine("KeyboardInput VirtualKeys=\"\"calculator\"\" CapsLock=False NumLock=False ScrollLock=False");
+ System.Threading.Thread.Sleep(1000);
+ winElem_LeftClickButtonTypehereto_95_20.SendKeys("calculator");
+
+ // LeftClick on ListItem "Calculator, Trusted Microsoft Store app, Press right to switch p" at (113,27)
+ Console.WriteLine("LeftClick on ListItem \"Calculator, Trusted Microsoft Store app, Press right to switch p\" at (113,27)");
+ string xpath_LeftClickListItemCalculator_113_27 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Cortana\"]/Pane[@ClassName=\"WebView\"][@Name=\"Bing\"]/Pane[@Name=\"Bing\"]/List[@Name=\"Results\"][@AutomationId=\"suggestionsList\"]/ListItem[starts-with(@Name,\"Calculator, Trusted Microsoft Store app, Press right to switch p\")][starts-with(@AutomationId,\"id_\")]";
+ var winElem_LeftClickListItemCalculator_113_27 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickListItemCalculator_113_27);
+ if (winElem_LeftClickListItemCalculator_113_27 != null)
+ {
+ winElem_LeftClickListItemCalculator_113_27.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickListItemCalculator_113_27}");
+ return;
+ }
+
+
+ // LeftClick on Button "One" at (41,16)
+ Console.WriteLine("LeftClick on Button \"One\" at (41,16)");
+ string xpath_LeftClickButtonOne_41_16 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Calculator\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Calculator\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@Name=\"Number pad\"][@AutomationId=\"NumberPad\"]/Button[@Name=\"One\"][@AutomationId=\"num1Button\"]";
+ var winElem_LeftClickButtonOne_41_16 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonOne_41_16);
+ if (winElem_LeftClickButtonOne_41_16 != null)
+ {
+ winElem_LeftClickButtonOne_41_16.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonOne_41_16}");
+ return;
+ }
+
+
+ // LeftClick on Button "Plus" at (52,24)
+ Console.WriteLine("LeftClick on Button \"Plus\" at (52,24)");
+ string xpath_LeftClickButtonPlus_52_24 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Calculator\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Calculator\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@ClassName=\"NamedContainerAutomationPeer\"][@Name=\"Standard operators\"]/Button[@Name=\"Plus\"][@AutomationId=\"plusButton\"]";
+ var winElem_LeftClickButtonPlus_52_24 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonPlus_52_24);
+ if (winElem_LeftClickButtonPlus_52_24 != null)
+ {
+ winElem_LeftClickButtonPlus_52_24.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonPlus_52_24}");
+ return;
+ }
+
+
+ // LeftClick on Button "Two" at (58,28)
+ Console.WriteLine("LeftClick on Button \"Two\" at (58,28)");
+ string xpath_LeftClickButtonTwo_58_28 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Calculator\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Calculator\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@Name=\"Number pad\"][@AutomationId=\"NumberPad\"]/Button[@Name=\"Two\"][@AutomationId=\"num2Button\"]";
+ var winElem_LeftClickButtonTwo_58_28 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonTwo_58_28);
+ if (winElem_LeftClickButtonTwo_58_28 != null)
+ {
+ winElem_LeftClickButtonTwo_58_28.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonTwo_58_28}");
+ return;
+ }
+
+
+ // LeftClick on Button "Equals" at (56,27)
+ Console.WriteLine("LeftClick on Button \"Equals\" at (56,27)");
+ string xpath_LeftClickButtonEquals_56_27 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Calculator\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Calculator\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@ClassName=\"NamedContainerAutomationPeer\"][@Name=\"Standard operators\"]/Button[@Name=\"Equals\"][@AutomationId=\"equalButton\"]";
+ var winElem_LeftClickButtonEquals_56_27 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonEquals_56_27);
+ if (winElem_LeftClickButtonEquals_56_27 != null)
+ {
+ winElem_LeftClickButtonEquals_56_27.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonEquals_56_27}");
+ return;
+ }
+
+
+ // LeftClick on Text "Display is 3" at (472,2)
+ Console.WriteLine("LeftClick on Text \"Display is 3\" at (472,2)");
+ string xpath_LeftClickTextDisplayis3_472_2 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Calculator\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Calculator\"]/Group[@ClassName=\"LandmarkTarget\"]/Text[@Name=\"Display is 3\"][@AutomationId=\"CalculatorResults\"]";
+ var winElem_LeftClickTextDisplayis3_472_2 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickTextDisplayis3_472_2);
+ if (winElem_LeftClickTextDisplayis3_472_2 != null)
+ {
+ winElem_LeftClickTextDisplayis3_472_2.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickTextDisplayis3_472_2}");
+ return;
+ }
+
+
+ // LeftClick on Button "Close Calculator" at (24,15)
+ Console.WriteLine("LeftClick on Button \"Close Calculator\" at (24,15)");
+ string xpath_LeftClickButtonCloseCalcu_24_15 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"ApplicationFrameWindow\"][@Name=\"Calculator\"]/Window[@Name=\"Calculator\"][@AutomationId=\"TitleBar\"]/Button[@Name=\"Close Calculator\"][@AutomationId=\"Close\"]";
+ var winElem_LeftClickButtonCloseCalcu_24_15 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonCloseCalcu_24_15);
+ if (winElem_LeftClickButtonCloseCalcu_24_15 != null)
+ {
+ winElem_LeftClickButtonCloseCalcu_24_15.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonCloseCalcu_24_15}");
+ return;
+ }
+
+
+ // test complete
+ bSuccess = true;
+ }
+ finally
+ {
+ Assert.AreEqual(bSuccess, true);
+ }
+ }
+ }
+}
diff --git a/Tests/AbsoluteXPath/DesktopSession.cs b/Tests/AbsoluteXPath/DesktopSession.cs
new file mode 100644
index 00000000..32b785c9
--- /dev/null
+++ b/Tests/AbsoluteXPath/DesktopSession.cs
@@ -0,0 +1,73 @@
+//******************************************************************************
+//
+// Copyright (c) 2019 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//******************************************************************************
+
+using System;
+using OpenQA.Selenium.Appium.Windows;
+using OpenQA.Selenium.Remote;
+
+namespace AbsoluteXPath
+{
+ public class DesktopSession
+ {
+ private const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723/";
+ WindowsDriver desktopSession;
+
+ public DesktopSession()
+ {
+ DesiredCapabilities appCapabilities = new DesiredCapabilities();
+ appCapabilities.SetCapability("app", "Root");
+ appCapabilities.SetCapability("deviceName", "WindowsPC");
+ desktopSession = new WindowsDriver(new Uri(WindowsApplicationDriverUrl), appCapabilities);
+ }
+
+ ~DesktopSession()
+ {
+ desktopSession.Quit();
+ }
+
+ public WindowsDriver DesktopSessionElement
+ {
+ get { return desktopSession; }
+ }
+
+ public WindowsElement FindElementByAbsoluteXPath(string xPath, int nTryCount = 15)
+ {
+ WindowsElement uiTarget = null;
+
+ while (nTryCount-- > 0)
+ {
+ try
+ {
+ uiTarget = desktopSession.FindElementByXPath(xPath);
+ }
+ catch
+ {
+ }
+
+ if (uiTarget != null)
+ {
+ break;
+ }
+ else
+ {
+ System.Threading.Thread.Sleep(2000);
+ }
+ }
+
+ return uiTarget;
+ }
+ }
+}
diff --git a/Tests/AbsoluteXPath/Properties/AssemblyInfo.cs b/Tests/AbsoluteXPath/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..eb81e3b6
--- /dev/null
+++ b/Tests/AbsoluteXPath/Properties/AssemblyInfo.cs
@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("AbsoluteXPath")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AbsoluteXPath")]
+[assembly: AssemblyCopyright("Copyright © 2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("971be3d2-1eff-4660-a8a8-c1a52966572c")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Tests/AbsoluteXPath/README.md b/Tests/AbsoluteXPath/README.md
new file mode 100644
index 00000000..ed4d3ef2
--- /dev/null
+++ b/Tests/AbsoluteXPath/README.md
@@ -0,0 +1,28 @@
+# AbsoluteXPath
+
+AbsoluteXPath is a sample test project that demonstrates how UIRecorder generated xpaths are used to find target elements. You can use this as a template for writing your test project.
+
+This test project highlights some common interactions below that can be put together to perform and verify various UI scenario on a modern app.
+- Sending click action to an element
+- Sending keyboard input to a text box
+
+## Requirements
+
+- Windows 10 PC with the latest Windows 10 version (Version 1607 or later)
+- Microsoft Visual Studio 2015 or later
+
+## Getting Started
+
+1. Open `AlarmClockTest.sln` in Visual Studio
+2. Select **Test** > **Windows** > **Test Explorer**
+3. Select **Run All** on the test pane or through menu **Test** > **Run** > **All Tests**
+
+> Once the project is successfully built, you can use the **TestExplorer** to pick and choose the test scenario(s) to run
+
+
+## Adding/Updating Test Scenario
+
+Please follow the guidelines below to maintain test reliability and conciseness:
+1. Group test methods based on the UI view page such as Alarm, Stopwatch, Timer, etc.
+2. Maintain original state after test runs and keep clean state in between tests when possible
+3. Only add tests that provide additional value to the sample
diff --git a/Tests/AbsoluteXPath/SimpleAttributeExpression.cs b/Tests/AbsoluteXPath/SimpleAttributeExpression.cs
new file mode 100644
index 00000000..d1232da0
--- /dev/null
+++ b/Tests/AbsoluteXPath/SimpleAttributeExpression.cs
@@ -0,0 +1,111 @@
+//******************************************************************************
+//
+// Copyright (c) 2019 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//******************************************************************************
+
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace AbsoluteXPath
+{
+ [TestClass]
+ public class SimpleAttributeExpression
+ {
+ DesktopSession desktopSession = new DesktopSession();
+
+ [TestMethod]
+ public void InvalidAttributeValue()
+ {
+ try
+ {
+ // [@Name=\"St^*~art\"]
+ var xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[@ClassName=\"Shell_TrayWnd\"][@Name=\"Taskbar\"]/Button[@ClassName=\"Start\"][@Name=\"St^*~art\"]";
+ desktopSession.DesktopSessionElement.FindElementByXPath(xpath);
+ Assert.Fail("Exception should have been thrown");
+ }
+ catch (InvalidOperationException exception)
+ {
+ Assert.AreEqual(WebDriverAPI.ErrorStrings.NoSuchElement, exception.Message);
+ }
+ }
+
+ [TestMethod]
+ public void NoAttributeValue()
+ {
+ // [@Name]
+ var xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[@ClassName=\"Shell_TrayWnd\"][@Name=\"Taskbar\"]/Button[@ClassName=\"Start\"][@Name]";
+ Assert.IsNotNull(desktopSession.DesktopSessionElement.FindElementByXPath(xpath));
+ }
+
+ [TestMethod]
+ public void InvalidAttributeName()
+ {
+ try
+ {
+ // [@ClassNameInvalid=\"Shell_TrayWnd\"]
+ var xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[@ClassNameInvalid=\"Shell_TrayWnd\"][@Name=\"Taskbar\"]/Button[@ClassName=\"Start\"][@Name=\"Start\"]";
+ desktopSession.DesktopSessionElement.FindElementByXPath(xpath);
+ Assert.Fail("Exception should have been thrown");
+ }
+ catch (InvalidOperationException exception)
+ {
+ Assert.AreEqual(WebDriverAPI.ErrorStrings.NoSuchElement, exception.Message);
+ }
+ }
+
+ [TestMethod]
+ public void InvalidTagName()
+ {
+ try
+ {
+ // InvalidPane
+ var xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/InvalidPane[@ClassName=\"Shell_TrayWnd\"][@Name=\"Taskbar\"]/Button[@ClassName=\"Start\"][@Name=\"Start\"]";
+ desktopSession.DesktopSessionElement.FindElementByXPath(xpath);
+ Assert.Fail("Exception should have been thrown");
+ }
+ catch (InvalidOperationException exception)
+ {
+ Assert.AreEqual(WebDriverAPI.ErrorStrings.NoSuchElement, exception.Message);
+ }
+ }
+
+ [TestMethod]
+ public void NameContainsStart()
+ {
+ var xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[@ClassName=\"Shell_TrayWnd\"][@Name=\"Taskbar\"]/Button[contains(@Name,\"Start\")]";
+ Assert.IsNotNull(desktopSession.DesktopSessionElement.FindElementByXPath(xpath));
+ }
+
+ [TestMethod]
+ public void ClassNameStartsWithStart()
+ {
+ var xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[starts-with(@ClassName,\"Shell_TrayWnd\")][@Name=\"Taskbar\"]/Button[contains(@Name,\"Start\")]";
+ Assert.IsNotNull(desktopSession.DesktopSessionElement.FindElementByXPath(xpath));
+ }
+
+ [TestMethod]
+ public void AttributesInSingleQuote()
+ {
+ var xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[starts-with(@ClassName,'Shell_TrayWnd')][@Name='Taskbar']/Button[contains(@Name,'Start')]";
+ Assert.IsNotNull(desktopSession.DesktopSessionElement.FindElementByXPath(xpath));
+ }
+
+ [TestMethod]
+ public void ButtonByPosition()
+ {
+ string xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[@ClassName=\"Shell_TrayWnd\"][@Name=\"Taskbar\"]/Button[position()=1]";
+ Assert.IsNotNull(desktopSession.DesktopSessionElement.FindElementByXPath(xpath));
+ }
+ }
+}
diff --git a/Tests/AbsoluteXPath/ValidXPath.cs b/Tests/AbsoluteXPath/ValidXPath.cs
new file mode 100644
index 00000000..12048fe6
--- /dev/null
+++ b/Tests/AbsoluteXPath/ValidXPath.cs
@@ -0,0 +1,91 @@
+//******************************************************************************
+//
+// Copyright (c) 2019 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//******************************************************************************
+
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace AbsoluteXPath
+{
+ [TestClass]
+ public class ValidXPath
+ {
+ static DesktopSession desktopSession = new DesktopSession();
+
+ [TestMethod]
+ public void StarAsTagName()
+ {
+ string xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[@Name=\"Taskbar\"][@ClassName=\"Shell_TrayWnd\"]/*[@Name=\"Start\"]";
+ Assert.IsNotNull(desktopSession.DesktopSessionElement.FindElementByXPath(xpath));
+ }
+
+ [TestMethod]
+ public void TwoAncestors()
+ {
+ string xpath = "/*/*/*[@Name=\"Start\"]";
+ Assert.IsNotNull(desktopSession.DesktopSessionElement.FindElementByXPath(xpath));
+ }
+
+ [TestMethod]
+ public void SelectAllElements()
+ {
+ string xpath = "//*[@Name=\"Start\"]";
+ Assert.IsNotNull(desktopSession.DesktopSessionElement.FindElementByXPath(xpath));
+ }
+
+ [TestMethod]
+ public void DoubleSlash()
+ {
+ var xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]//Button[@Name=\"Start\"]";
+ Assert.IsNotNull(desktopSession.DesktopSessionElement.FindElementByXPath(xpath));
+ }
+
+ [TestMethod]
+ public void IdAttributeName()
+ {
+ try
+ {
+ // Id attribute is not expected
+ var xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[@Name=\"Taskbar\"][@ClassName=\"Shell_TrayWnd\"]/Button[@Name=\"Start\"][@Id=\"Start\"]";
+ Assert.IsNull(desktopSession.DesktopSessionElement.FindElementByXPath(xpath));
+ }
+ catch (InvalidOperationException exception)
+ {
+ Assert.AreEqual(WebDriverAPI.ErrorStrings.NoSuchElement, exception.Message);
+ }
+ }
+
+ [TestMethod]
+ public void andInAttributes()
+ {
+ var xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[@ClassName=\"Shell_TrayWnd\"][@Name=\"Taskbar\"]/Button[@ClassName=\"Start\" and @Name=\"Start\"]";
+ Assert.IsNotNull(desktopSession.DesktopSessionElement.FindElementByXPath(xpath));
+ }
+
+ [TestMethod]
+ public void orInAttributes()
+ {
+ string xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[@ClassName=\"Shell_TrayWnd\"][@Name=\"Taskbar\"]/Button[@ClassName=\"Start\" or @Name=\"Start\"]";
+ Assert.IsNotNull(desktopSession.DesktopSessionElement.FindElementByXPath(xpath));
+ }
+
+ [TestMethod]
+ public void ButtonByIndex()
+ {
+ string xpath = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[@ClassName=\"Shell_TrayWnd\"][@Name=\"Taskbar\"]/Button[1]";
+ Assert.IsNotNull(desktopSession.DesktopSessionElement.FindElementByXPath(xpath));
+ }
+ }
+}
diff --git a/Tests/AbsoluteXPath/notepad.cs b/Tests/AbsoluteXPath/notepad.cs
new file mode 100644
index 00000000..ba4df202
--- /dev/null
+++ b/Tests/AbsoluteXPath/notepad.cs
@@ -0,0 +1,239 @@
+//******************************************************************************
+//
+// Copyright (c) 2019 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//******************************************************************************
+
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OpenQA.Selenium;
+
+namespace AbsoluteXPath
+{
+ [TestClass]
+ public class Notepad
+ {
+ [TestMethod]
+ public void TestMethod1()
+ {
+ DesktopSession desktopSession = new DesktopSession();
+ TimeSpan.FromSeconds(2);
+
+ bool bTestSuccess = false;
+
+ try
+ {
+ // LeftClick on Button "Type here to search" at (85,17)
+ Console.WriteLine("LeftClick on Button \"Type here to search\" at (85,17)");
+ string xpath_LeftClickButtonTypehereto_85_17 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Pane[@ClassName=\"Shell_TrayWnd\"][@Name=\"Taskbar\"]/Pane[@ClassName=\"TrayDummySearchControl\"]/Button[@ClassName=\"Button\"][@Name=\"Type here to search\"]";
+ var winElem_LeftClickButtonTypehereto_85_17 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonTypehereto_85_17);
+ if (winElem_LeftClickButtonTypehereto_85_17 != null)
+ {
+ winElem_LeftClickButtonTypehereto_85_17.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonTypehereto_85_17}");
+ return;
+ }
+
+
+ // KeyboardInput VirtualKeys=""notepad"" CapsLock=False NumLock=False ScrollLock=False
+ Console.WriteLine("KeyboardInput VirtualKeys=\"\"notepad\"\" CapsLock=False NumLock=False ScrollLock=False");
+ System.Threading.Thread.Sleep(1000);
+ winElem_LeftClickButtonTypehereto_85_17.SendKeys("notepad");
+
+
+ // LeftClick on ListItem "Notepad, Desktop app, Press right to switch preview" at (46,32)
+ Console.WriteLine("LeftClick on ListItem \"Notepad, Desktop app, Press right to switch preview\" at (46,32)");
+ string xpath_LeftClickListItemNotepadDes_46_32 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"Windows.UI.Core.CoreWindow\"][@Name=\"Cortana\"]/Pane[@ClassName=\"WebView\"][@Name=\"Bing\"]/Pane[@Name=\"Bing\"]/List[@Name=\"Results\"][@AutomationId=\"suggestionsList\"]/ListItem[@Name=\"Notepad, Desktop app, Press right to switch preview\"][starts-with(@AutomationId,\"id_\")]";
+ var winElem_LeftClickListItemNotepadDes_46_32 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickListItemNotepadDes_46_32);
+ if (winElem_LeftClickListItemNotepadDes_46_32 != null)
+ {
+ winElem_LeftClickListItemNotepadDes_46_32.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickListItemNotepadDes_46_32}");
+ return;
+ }
+
+
+ // LeftClick on Document "Text Editor" at (143,95)
+ Console.WriteLine("LeftClick on Document \"Text Editor\" at (143,95)");
+ string xpath_LeftClickDocumentTextEditor_143_95 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"Notepad\"][contains(@Name,\"Untitled\")]/Document[@ClassName=\"Edit\"][@Name=\"Text Editor\"]";
+ var winElem_LeftClickDocumentTextEditor_143_95 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickDocumentTextEditor_143_95);
+ if (winElem_LeftClickDocumentTextEditor_143_95 != null)
+ {
+ winElem_LeftClickDocumentTextEditor_143_95.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickDocumentTextEditor_143_95}");
+ return;
+ }
+
+
+ // KeyboardInput VirtualKeys=""test"" CapsLock=False NumLock=False ScrollLock=False
+ Console.WriteLine("KeyboardInput VirtualKeys=\"\"test\"\" CapsLock=False NumLock=False ScrollLock=False");
+ System.Threading.Thread.Sleep(100);
+ winElem_LeftClickDocumentTextEditor_143_95.SendKeys("test");
+
+ Console.WriteLine("KeyboardInput VirtualKeys=\"Alt + F\" CapsLock=False NumLock=False ScrollLock=False");
+ System.Threading.Thread.Sleep(1000);
+ winElem_LeftClickDocumentTextEditor_143_95.SendKeys(Keys.Alt + "F" + Keys.Alt);
+
+ // LeftClick on MenuItem "Save As..." at (62,17)
+ Console.WriteLine("LeftClick on MenuItem \"Save As...\" at (62,17)");
+ string xpath_LeftClickMenuItemSaveAs_62_17 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"Notepad\"][contains(@Name,\"Untitled\")]/Menu[@ClassName=\"#32768\"][@Name=\"File\"]/MenuItem[@Name=\"Save As...\"]";
+ var winElem_LeftClickMenuItemSaveAs_62_17 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickMenuItemSaveAs_62_17);
+ if (winElem_LeftClickMenuItemSaveAs_62_17 != null)
+ {
+ winElem_LeftClickMenuItemSaveAs_62_17.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickMenuItemSaveAs_62_17}");
+ return;
+ }
+
+
+ // LeftClick on Edit "File name:" at (147,11)
+ Console.WriteLine("LeftClick on Edit \"File name:\" at (147,11)");
+ string xpath_LeftClickEditFilename_147_11 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"Notepad\"][contains(@Name,\"Untitled\")]/Window[@ClassName=\"#32770\"][@Name=\"Save As\"]/Pane[@ClassName=\"DUIViewWndClassName\"]/ComboBox[@Name=\"File name:\"][@AutomationId=\"FileNameControlHost\"]/Edit[@ClassName=\"Edit\"][@Name=\"File name:\"]";
+ var winElem_LeftClickEditFilename_147_11 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickEditFilename_147_11);
+ if (winElem_LeftClickEditFilename_147_11 != null)
+ {
+ winElem_LeftClickEditFilename_147_11.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickEditFilename_147_11}");
+ return;
+ }
+
+
+ // KeyboardInput VirtualKeys="Keys.Backspace + Keys.BackspaceKeys.Backspace + Keys.BackspaceKeys.Backspace + Keys.BackspaceKeys.Backspace + Keys.BackspaceKeys.Backspace + Keys.Backspace"winappdrivertest.txt"" CapsLock=False NumLock=False ScrollLock=False
+ Console.WriteLine("KeyboardInput VirtualKeys=\"Keys.Backspace + Keys.BackspaceKeys.Backspace + Keys.BackspaceKeys.Backspace + Keys.BackspaceKeys.Backspace + Keys.BackspaceKeys.Backspace + Keys.Backspace\"winappdrivertest.txt\"\" CapsLock=False NumLock=False ScrollLock=False");
+ System.Threading.Thread.Sleep(100);
+ winElem_LeftClickEditFilename_147_11.SendKeys(Keys.Backspace + Keys.Backspace);
+ winElem_LeftClickEditFilename_147_11.SendKeys(Keys.Backspace + Keys.Backspace);
+ winElem_LeftClickEditFilename_147_11.SendKeys(Keys.Backspace + Keys.Backspace);
+ winElem_LeftClickEditFilename_147_11.SendKeys(Keys.Backspace + Keys.Backspace);
+ winElem_LeftClickEditFilename_147_11.SendKeys(Keys.Backspace + Keys.Backspace);
+ winElem_LeftClickEditFilename_147_11.SendKeys("1winappdrivertest.txt");
+
+
+ // LeftClick on Button "Save" at (22,14)
+ Console.WriteLine("LeftClick on Button \"Save\" at (22,14)");
+ string xpath_LeftClickButtonSave_22_14 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"Notepad\"][contains(@Name,\"Untitled\")]/Window[@ClassName=\"#32770\"][@Name=\"Save As\"]/Button[@ClassName=\"Button\"][@Name=\"Save\"]";
+ var winElem_LeftClickButtonSave_22_14 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonSave_22_14);
+ if (winElem_LeftClickButtonSave_22_14 != null)
+ {
+ winElem_LeftClickButtonSave_22_14.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonSave_22_14}");
+ return;
+ }
+
+ Console.WriteLine("KeyboardInput VirtualKeys=\"Alt + F\" CapsLock=False NumLock=False ScrollLock=False");
+ System.Threading.Thread.Sleep(1000);
+ winElem_LeftClickDocumentTextEditor_143_95.SendKeys(Keys.Alt + "F" + Keys.Alt);
+
+ // LeftClick on MenuItem "Open..." at (44,11)
+ Console.WriteLine("LeftClick on MenuItem \"Open...\" at (44,11)");
+ string xpath_LeftClickMenuItemOpen_44_11 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"Notepad\"][contains(@Name,\"1winappdrivertest\")]/Menu[@ClassName=\"#32768\"][@Name=\"File\"]/MenuItem[@Name=\"Open...\"]";
+ var winElem_LeftClickMenuItemOpen_44_11 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickMenuItemOpen_44_11);
+ if (winElem_LeftClickMenuItemOpen_44_11 != null)
+ {
+ winElem_LeftClickMenuItemOpen_44_11.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickMenuItemOpen_44_11}");
+ return;
+ }
+
+
+ // RightClick on Edit "Name" at (9,8)
+ Console.WriteLine("RightClick on Edit \"Name\" at (9,8)");
+ string xpath_RightClickEditName_9_8 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"Notepad\"][contains(@Name,\"1winappdrivertest\")]/Window[@ClassName=\"#32770\"][@Name=\"Open\"]/Pane[@ClassName=\"DUIViewWndClassName\"]/Pane[@Name=\"Shell Folder View\"][@AutomationId=\"listview\"]/List[@ClassName=\"UIItemsView\"][@Name=\"Items View\"]/ListItem[@ClassName=\"UIItem\"][@Name=\"1winappdrivertest.txt\"]/Edit[@Name=\"Name\"][@AutomationId=\"System.ItemNameDisplay\"]";
+ var winElem_RightClickEditName_9_8 = desktopSession.FindElementByAbsoluteXPath(xpath_RightClickEditName_9_8);
+ if (winElem_RightClickEditName_9_8 != null)
+ {
+ winElem_RightClickEditName_9_8.Click();
+ desktopSession.DesktopSessionElement.Mouse.MouseMove(winElem_RightClickEditName_9_8.Coordinates);
+ desktopSession.DesktopSessionElement.Mouse.ContextClick(null);
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_RightClickEditName_9_8}");
+ return;
+ }
+
+
+ // LeftClick on MenuItem "Delete" at (71,5)
+ Console.WriteLine("LeftClick on MenuItem \"Delete\" at (71,5)");
+ string xpath_LeftClickMenuItemDelete_71_5 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Menu[@ClassName=\"#32768\"][@Name=\"Context\"]/MenuItem[@Name=\"Delete\"]";
+ var winElem_LeftClickMenuItemDelete_71_5 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickMenuItemDelete_71_5);
+ if (winElem_LeftClickMenuItemDelete_71_5 != null)
+ {
+ winElem_LeftClickMenuItemDelete_71_5.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickMenuItemDelete_71_5}");
+ return;
+ }
+
+
+ // LeftClick on Button "Cancel" at (24,13)
+ Console.WriteLine("LeftClick on Button \"Cancel\" at (24,13)");
+ string xpath_LeftClickButtonCancel_24_13 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"Notepad\"][contains(@Name,\"1winappdrivertest\")]/Window[@ClassName=\"#32770\"][@Name=\"Open\"]/Button[@ClassName=\"Button\"][@Name=\"Cancel\"]";
+ var winElem_LeftClickButtonCancel_24_13 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonCancel_24_13);
+ if (winElem_LeftClickButtonCancel_24_13 != null)
+ {
+ winElem_LeftClickButtonCancel_24_13.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonCancel_24_13}");
+ return;
+ }
+
+
+ // LeftClick on Button "Close" at (28,15)
+ Console.WriteLine("LeftClick on Button \"Close\" at (28,15)");
+ string xpath_LeftClickButtonClose_28_15 = "/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"Notepad\"][contains(@Name,\"1winappdrivertest\")]/TitleBar[@AutomationId=\"TitleBar\"]/Button[@Name=\"Close\"]";
+ var winElem_LeftClickButtonClose_28_15 = desktopSession.FindElementByAbsoluteXPath(xpath_LeftClickButtonClose_28_15);
+ if (winElem_LeftClickButtonClose_28_15 != null)
+ {
+ winElem_LeftClickButtonClose_28_15.Click();
+ }
+ else
+ {
+ Console.WriteLine($"Failed to find element using xpath: {xpath_LeftClickButtonClose_28_15}");
+ return;
+ }
+
+ bTestSuccess = true;
+ }
+ finally
+ {
+ Assert.AreEqual(bTestSuccess, true);
+ }
+ }
+ }
+}
diff --git a/Tests/AbsoluteXPath/packages.config b/Tests/AbsoluteXPath/packages.config
new file mode 100644
index 00000000..f60471f1
--- /dev/null
+++ b/Tests/AbsoluteXPath/packages.config
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Tools/UIRecorder/UIRecorder/App.xaml.cs b/Tools/UIRecorder/UIRecorder/App.xaml.cs
index c0b7d643..c66e2b88 100644
--- a/Tools/UIRecorder/UIRecorder/App.xaml.cs
+++ b/Tools/UIRecorder/UIRecorder/App.xaml.cs
@@ -16,15 +16,67 @@
using System;
using System.Collections.Generic;
-using System.Configuration;
-using System.Data;
-using System.Linq;
-using System.Threading.Tasks;
using System.Windows;
-using System.Runtime.InteropServices;
+using Microsoft.ApplicationInsights;
namespace WinAppDriverUIRecorder
{
+ public class AppInsights
+ {
+ static TelemetryClient tc = new TelemetryClient();
+
+ public static void InitAppSights()
+ {
+ Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine;
+ Microsoft.Win32.RegistryKey skey = key.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion");
+ string bldStr = skey == null ? Environment.OSVersion.ToString() : $"{skey.GetValue("BuildLab")}";
+ //Please follow the instruction on https://docs.microsoft.com/en-us/azure/azure-monitor/app/windows-desktop
+ //to get your own Instrumentation Key
+ Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.Active.InstrumentationKey = "Your Instrumentation Key";
+
+ tc.Context.User.Id = Environment.UserName;
+ tc.Context.Session.Id = Guid.NewGuid().ToString();
+ tc.Context.Device.OperatingSystem = bldStr;
+ }
+
+ public static void Flush()
+ {
+ if (tc != null)
+ tc.Flush();
+ }
+
+ public static void LogEvent(string eventName, string eventData)
+ {
+ if (eventData != null)
+ {
+ var dict = new Dictionary();
+ dict.Add("EventData", eventData);
+ tc.TrackEvent(eventName, dict);
+ }
+ else
+ {
+ tc.TrackEvent(eventName);
+ }
+ }
+
+ public static void LogEvent(string eventName)
+ {
+ tc.TrackEvent(eventName);
+ }
+
+ public static void LogException(string metricName, string metricData)
+ {
+ var et = new Microsoft.ApplicationInsights.DataContracts.ExceptionTelemetry();
+ et.Message = metricData;
+ tc.TrackException(et);
+ }
+
+ public static void LogMetric(string metricName, int metricData)
+ {
+ tc.TrackMetric(new Microsoft.ApplicationInsights.DataContracts.MetricTelemetry(metricName, metricData));
+ }
+ }
+
///
/// Interaction logic for App.xaml
///
@@ -34,14 +86,14 @@ protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
NativeMethods.SetProcessDPIAware();
-
+ AppInsights.InitAppSights();
NativeMethods.InitUiTreeWalk();
}
protected override void OnExit(ExitEventArgs e)
{
NativeMethods.UnInitUiTreeWalk();
-
+ AppInsights.Flush();
base.OnExit(e);
Environment.Exit(0);
}
diff --git a/Tools/UIRecorder/UIRecorder/ApplicationInsights.config b/Tools/UIRecorder/UIRecorder/ApplicationInsights.config
new file mode 100644
index 00000000..9575f661
--- /dev/null
+++ b/Tools/UIRecorder/UIRecorder/ApplicationInsights.config
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+ search|spider|crawl|Bot|Monitor|AlwaysOn
+
+
+
+
+
+
+
+
+
+
+
+
+
+ core.windows.net
+ core.chinacloudapi.cn
+ core.cloudapi.de
+ core.usgovcloudapi.net
+ localhost
+ 127.0.0.1
+
+
+ Microsoft.Azure.EventHubs
+ Microsoft.Azure.ServiceBus
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Microsoft.VisualStudio.Web.PageInspector.Runtime.Tracing.RequestDataHttpHandler
+ System.Web.StaticFileHandler
+ System.Web.Handlers.AssemblyResourceLoader
+ System.Web.Optimization.BundleHandler
+ System.Web.Script.Services.ScriptHandlerFactory
+ System.Web.Handlers.TraceHandler
+ System.Web.Services.Discovery.DiscoveryRequestHandler
+ System.Web.HttpDebugHandler
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
+ Event
+
+
+ 5
+ Event
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Tools/UIRecorder/UIRecorder/GenerateXPath.cs b/Tools/UIRecorder/UIRecorder/GenerateXPath.cs
index edafe532..1565e424 100644
--- a/Tools/UIRecorder/UIRecorder/GenerateXPath.cs
+++ b/Tools/UIRecorder/UIRecorder/GenerateXPath.cs
@@ -16,14 +16,10 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Xml;
namespace WinAppDriverUIRecorder
{
- public enum UiTaskName : UInt32
+ public enum EnumUiTaskName : UInt32
{
UnknownUiTask = 3000,
KeyboardInput,
@@ -51,24 +47,12 @@ public CharPair(char c1, char c2)
class ConstVariables
{
- public const int DragDeltaOffset = 12345678;
-
- public const string DELTAX = "DELTAX";
- public const string DELTAY = "DELTAY";
- public const string WHEEL = "WHEEL";
-
- public const string ClassName = "ClassName";
- public const string Name = "Name";
- public const string AutomationId = "AutomationId";
- public const string UiTask = "UiTask";
- public const string Pos = "Pos";
-
public static Dictionary s_Vk2String = new Dictionary();
public static Dictionary s_Vk2CharPair = new Dictionary();
- public static UiTaskName FromStringTaskName(string strTask)
+ public static EnumUiTaskName FromStringTaskName(string strTask)
{
- foreach (UiTaskName val in Enum.GetValues(typeof(UiTaskName)))
+ foreach (EnumUiTaskName val in Enum.GetValues(typeof(EnumUiTaskName)))
{
if (strTask == val.ToString())
{
@@ -76,7 +60,7 @@ public static UiTaskName FromStringTaskName(string strTask)
}
}
- return UiTaskName.UnknownUiTask;
+ return EnumUiTaskName.UnknownUiTask;
}
public static void InitVk2StringMap()
@@ -110,7 +94,6 @@ public static void InitVk2StringMap()
s_Vk2String.Add(VirtualKeys.VK_F11.ToString(), "F11");
s_Vk2String.Add(VirtualKeys.VK_F12.ToString(), "F12");
s_Vk2String.Add(VirtualKeys.VK_DECIMAL.ToString(), "Decimal");
- //s_Vk2String.Add(VirtualKeys., "Meta");
s_Vk2String.Add(VirtualKeys.VK_OEM_1.ToString(), "Semicolon");
s_Vk2String.Add(VirtualKeys.VK_INSERT.ToString(), "Insert");
s_Vk2String.Add(VirtualKeys.VK_CANCEL.ToString(), "Cancel");
@@ -119,14 +102,12 @@ public static void InitVk2StringMap()
s_Vk2String.Add(VirtualKeys.VK_TAB.ToString(), "Tab");
s_Vk2String.Add(VirtualKeys.VK_CLEAR.ToString(), "Clear");
s_Vk2String.Add(VirtualKeys.VK_RETURN.ToString(), "Return");
- //s_Vk2String.Add(VirtualKeys.VK_RETURN.ToString(), "Enter");
- s_Vk2String.Add(VirtualKeys.VK_LSHIFT.ToString(), "Shift");
- s_Vk2String.Add(VirtualKeys.VK_RSHIFT.ToString(), "RightShift");
- s_Vk2String.Add(VirtualKeys.VK_LCONTROL.ToString(), "Control");
- s_Vk2String.Add(VirtualKeys.VK_RCONTROL.ToString(), "RightControl");
- s_Vk2String.Add(VirtualKeys.VK_LMENU.ToString(), "Alt");
- s_Vk2String.Add(VirtualKeys.VK_RMENU.ToString(), "RightAlt");
-
+ s_Vk2String.Add(VirtualKeys.VK_LSHIFT.ToString(), "LeftShift");
+ s_Vk2String.Add(VirtualKeys.VK_RSHIFT.ToString(), "Shift");
+ s_Vk2String.Add(VirtualKeys.VK_LCONTROL.ToString(), "LeftControl");
+ s_Vk2String.Add(VirtualKeys.VK_RCONTROL.ToString(), "Control");
+ s_Vk2String.Add(VirtualKeys.VK_LMENU.ToString(), "LeftAlt");
+ s_Vk2String.Add(VirtualKeys.VK_RMENU.ToString(), "Alt");
s_Vk2String.Add(VirtualKeys.VK_DELETE.ToString(), "Delete");
s_Vk2String.Add(VirtualKeys.VK_PAUSE.ToString(), "Pause");
s_Vk2String.Add(VirtualKeys.VK_SPACE.ToString(), "Space");
@@ -134,14 +115,10 @@ public static void InitVk2StringMap()
s_Vk2String.Add(VirtualKeys.VK_NEXT.ToString(), "PageDown");
s_Vk2String.Add(VirtualKeys.VK_END.ToString(), "End");
s_Vk2String.Add(VirtualKeys.VK_HOME.ToString(), "Home");
- s_Vk2String.Add(VirtualKeys.VK_LEFT.ToString(), "Left");
- //s_Vk2String.Add(VirtualKeys.VK_LEFT.ToString(), "ArrowLeft");
- s_Vk2String.Add(VirtualKeys.VK_UP.ToString(), "Up");
- //s_Vk2String.Add(VirtualKeys.VK_UP.ToString(), "ArrowUp");
- s_Vk2String.Add(VirtualKeys.VK_RIGHT.ToString(), "Right");
- //s_Vk2String.Add(VirtualKeys.VK_RIGHT.ToString(), "ArrowRight");
- s_Vk2String.Add(VirtualKeys.VK_DOWN.ToString(), "Down");
- //s_Vk2String.Add(VirtualKeys.VK_DOWN.ToString(), "ArrowDown");
+ s_Vk2String.Add(VirtualKeys.VK_LEFT.ToString(), "ArrowLeft");
+ s_Vk2String.Add(VirtualKeys.VK_UP.ToString(), "ArrowUp");
+ s_Vk2String.Add(VirtualKeys.VK_RIGHT.ToString(), "ArrowRight");
+ s_Vk2String.Add(VirtualKeys.VK_DOWN.ToString(), "ArrowDown");
s_Vk2String.Add(VirtualKeys.VK_ESCAPE.ToString(), "Escape");
s_Vk2String.Add(VirtualKeys.VK_LWIN.ToString(), "Command");
s_Vk2String.Add(VirtualKeys.VK_RWIN.ToString(), "Command");
@@ -224,46 +201,37 @@ class GenerateXPath
const string sNameValue = "[@{0}=\\\"{1}\\\"]";
const string sNameStartsWithValue = "[starts-with(@{0},\\\"{1}\\\")]";
- public static string GenerateXPathToUiElement(string strXmlNodes)
+ public static string XmlEncode(string strData)
{
- XmlDocument xmlDocument = new XmlDocument();
- try
- {
- xmlDocument.LoadXml(strXmlNodes);
- }
- catch (Exception ex)
- {
- return ex.ToString();
- }
-
- string strRet = "";
- foreach (XmlElement uiTask in xmlDocument.GetElementsByTagName(ConstVariables.UiTask))
- {
- strRet += GetXPathFromUiTaskNode(uiTask);
- }
-
- if (strRet.Length > 2)
- {
- return strRet.Substring(0, strRet.Length - 2) + "\n"; // remove last ,
- }
- else
+ if (strData != null)
{
- return strRet;
+ strData = strData.Replace("&", "&");
+ strData = strData.Replace("\"", """);
+ strData = strData.Replace("\'", "'");
+ strData = strData.Replace("<", "<");
+ strData = strData.Replace(">", ">");
}
+ return strData;
}
- public static string XmlEncode(string strData)
+ public static string XmlDecode(string strData)
{
- strData = strData.Replace("&", "&");
- strData = strData.Replace("\"", """);
- strData = strData.Replace("\'", "'");
- strData = strData.Replace("<", "<");
- strData = strData.Replace(">", ">");
+ if (strData != null)
+ {
+ strData = strData.Replace("&", "&");
+ strData = strData.Replace(""", "\"");
+ strData = strData.Replace("'", "\'");
+ strData = strData.Replace("<", "<");
+ strData = strData.Replace(">", ">");
+ }
return strData;
}
static string CheckAndFixNoneStaticValue(string strValue)
{
+ if (string.IsNullOrEmpty(strValue))
+ return strValue;
+
const string strGUIDPtn = @"[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}";
if (strValue.StartsWith("HwndWrapper[") || strValue.StartsWith("starts-with:HwndWrapper["))
@@ -286,37 +254,140 @@ static string CheckAndFixNoneStaticValue(string strValue)
return strValue;
}
- static string GetXPathFromUiTaskNode(XmlElement uiTaskNode)
+ static Dictionary GetTagAttributes(string xpath)
{
- if (uiTaskNode == null || uiTaskNode.ChildNodes.Count < 1)
+ Dictionary foundAttrs = new Dictionary();
+ List listAttrs = new List();
+
+ // Get tag and groups of []
+ var sb = new System.Text.StringBuilder();
+ string Tag = null;
+ int ncount = 0;
+ int nBracketCount = 0;
+ foreach (char c in xpath)
{
- return "";
+ if (c == '[')
+ {
+ nBracketCount++;
+
+ if (sb.Length > 0 && Tag == null && nBracketCount == 1)
+ {
+ Tag = sb.ToString();
+ sb.Clear();
+ }
+
+ sb.Append(c);
+ }
+ else if (c == ']')
+ {
+ nBracketCount--;
+
+ if (nBracketCount == 0)
+ {
+ sb.Append(c);
+ listAttrs.Add(sb.ToString());
+ sb.Clear();
+ }
+ else
+ {
+ sb.Append(c);
+ }
+ }
+ else if (c == '/' && nBracketCount == 0)
+ {
+ if (Tag == null && sb.Length > 0)
+ {
+ Tag = sb.ToString();
+ break;
+ }
+
+ if (Tag != null)
+ {
+ break;
+ }
+ }
+ else
+ {
+ sb.Append(c);
+ }
+
+ ncount++;
}
+ if (Tag != null)
+ {
+ foundAttrs.Add("Tag", Tag);
+ }
+ else if (Tag == null && sb.Length > 0)
+ {
+ foundAttrs.Add("Tag", sb.ToString());
+ }
+ else
+ {
+ //unexpected
+ return null;
+ }
+
+ foreach (string attrNameValue in listAttrs)
+ {
+ const string patternNameValue = @"\[([^=]+)=([^\]]+)\]";
+ var regNameValue = new System.Text.RegularExpressions.Regex(patternNameValue, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
+ System.Text.RegularExpressions.Match matchNameValue = regNameValue.Match(attrNameValue);
+ if (matchNameValue.Success)
+ {
+ if (matchNameValue.Groups.Count == 3)
+ {
+ var valStr = matchNameValue.Groups[2].Value;
+ if (valStr.StartsWith("\"") && valStr.EndsWith("\""))
+ valStr = valStr.Substring(1, valStr.Length - 2);
+
+ foundAttrs.Add(matchNameValue.Groups[1].Value, valStr);
+ }
+ }
+ }
+
+ return foundAttrs;
+ }
+
+ public static string GenerateXPathToUiElement(RecordedUiTask recordedUiTask, List pathNodes, ref UiTreeNode rootRet)
+ {
+ rootRet = null;
+
string tag, ClassName, Name, AutomationId, Pos;
string xPath = "";
- for (int i = 0; i < uiTaskNode.ChildNodes.Count; i++)
+ UiTreeNode parent = null;
+
+ for (int i = 0; i < pathNodes.Count; i++)
{
- XmlNode childNode = uiTaskNode.ChildNodes[i];
+ var nodePath = pathNodes[i];
- tag = childNode.Name != "Unknown" ? childNode.Name : "*";
- AutomationId = XmlEncode(childNode.Attributes[ConstVariables.AutomationId].Value);
- AutomationId = CheckAndFixNoneStaticValue(AutomationId);
+ bool bStartsWithName = false;
+ bool bStartsWithClass = false;
+ bool bStartsWithAutoId = false;
+
+ var tagAttrs = GetTagAttributes(nodePath);
+
+ tag = tagAttrs.ContainsKey("Tag") ? tagAttrs["Tag"] : "Unknown";
- Name = XmlEncode(childNode.Attributes[ConstVariables.Name].Value);
+ AutomationId = tagAttrs.ContainsKey("AutomationId") ? tagAttrs["AutomationId"] : null;
- ClassName = childNode.Attributes[ConstVariables.ClassName].Value;
+ Name = tagAttrs.ContainsKey("Name") ? tagAttrs["Name"] : null;
+
+ ClassName = tagAttrs.ContainsKey("ClassName") ? tagAttrs["ClassName"] : null;
ClassName = CheckAndFixNoneStaticValue(ClassName);
- Pos = childNode.Attributes[ConstVariables.Pos].Value;
- xPath += $"/{tag}";
- int nPos = xPath.Length;
+ Pos = tagAttrs.ContainsKey("position()") ? tagAttrs["position()"] : null;
+
+ string xPathNode = $"/{tag}";
+
+ // Set AutomationId to null if it is a GUID which is very likely generated at runtime
+ AutomationId = CheckAndFixNoneStaticValue(AutomationId);
// AutomationId (like UIs on Cortana search result list) created at runtime may end with digits
if (!string.IsNullOrEmpty(AutomationId) && !AutomationId.StartsWith("starts-with:"))
{
- string patAutoIdEndsWithDigits = @"^([^\d]*)[\d]+$";
+ string patAutoIdEndsWithDigits = @"^([^\d]*)[_\.\-\d]+$";
System.Text.RegularExpressions.Regex regAutoId = new System.Text.RegularExpressions.Regex(patAutoIdEndsWithDigits, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
if (regAutoId != null)
{
@@ -326,6 +397,7 @@ static string GetXPathFromUiTaskNode(XmlElement uiTaskNode)
if (matchAutoId.Groups[1].Length > 0)
{
AutomationId = "starts-with:" + matchAutoId.Groups[1].ToString();
+ bStartsWithAutoId = true;
}
else
{
@@ -335,16 +407,17 @@ static string GetXPathFromUiTaskNode(XmlElement uiTaskNode)
}
}
- if (!string.IsNullOrEmpty(AutomationId))
+ if (!string.IsNullOrEmpty(ClassName) && string.IsNullOrEmpty(AutomationId))
{
- if (AutomationId.StartsWith("starts-with:"))
+ if (ClassName.StartsWith("starts-with:"))
{
- AutomationId = AutomationId.Remove(0, "starts-with:".Length);
- xPath += string.Format(sNameStartsWithValue, ConstVariables.AutomationId, AutomationId);
+ ClassName = ClassName.Remove(0, "starts-with:".Length);
+ xPathNode += string.Format(sNameStartsWithValue, "ClassName", ClassName);
+ bStartsWithClass = true;
}
else
{
- xPath += string.Format(sNameValue, ConstVariables.AutomationId, AutomationId);
+ xPathNode += string.Format(sNameValue, "ClassName", ClassName);
}
}
@@ -353,35 +426,87 @@ static string GetXPathFromUiTaskNode(XmlElement uiTaskNode)
if (Name.StartsWith("starts-with:"))
{
Name = Name.Remove(0, "starts-with:".Length);
- xPath += string.Format(sNameStartsWithValue, ConstVariables.Name, Name);
+ xPathNode += string.Format(sNameStartsWithValue, "Name", Name);
+ bStartsWithName = true;
}
else
{
- xPath += string.Format(sNameValue, ConstVariables.Name, Name);
+ xPathNode += string.Format(sNameValue, "Name", Name);
}
}
- if (!string.IsNullOrEmpty(ClassName) && string.IsNullOrEmpty(AutomationId))
+ if (!string.IsNullOrEmpty(AutomationId))
{
- if (ClassName.StartsWith("starts-with:"))
+ if (AutomationId.StartsWith("starts-with:"))
{
- ClassName = ClassName.Remove(0, "starts-with:".Length);
- xPath += string.Format(sNameStartsWithValue, ConstVariables.ClassName, ClassName);
+ AutomationId = AutomationId.Remove(0, "starts-with:".Length);
+ xPathNode += string.Format(sNameStartsWithValue, "AutomationId", AutomationId);
+ bStartsWithAutoId = true;
}
else
{
- xPath += string.Format(sNameValue, ConstVariables.ClassName, ClassName);
+ xPathNode += string.Format(sNameValue, "AutomationId", AutomationId);
}
}
if (!string.IsNullOrEmpty(Pos) && string.IsNullOrEmpty(AutomationId) && string.IsNullOrEmpty(Name) && string.IsNullOrEmpty(ClassName))
{
- //TODO: verify
- xPath = xPath.Insert(nPos, $"[position()={Pos}]");
+ xPathNode += $"[position()={Pos}]";
+ }
+
+ // UiTreeNode
+ var left = tagAttrs.ContainsKey("x") ? tagAttrs["x"] : null;
+ var top = tagAttrs.ContainsKey("y") ? tagAttrs["y"] : null;
+ var leftLocal = tagAttrs.ContainsKey("lx") ? tagAttrs["lx"] : null;
+ var topLocal = tagAttrs.ContainsKey("ly") ? tagAttrs["ly"] : null;
+ var width = tagAttrs.ContainsKey("width") ? tagAttrs["width"] : null;
+ var height = tagAttrs.ContainsKey("height") ? tagAttrs["height"] : null;
+ var runtimeId = tagAttrs.ContainsKey("RuntimeId") ? tagAttrs["RuntimeId"] : null;
+
+ xPath += xPathNode;
+
+ var uiTreeNode = new UiTreeNode(parent) { Title = $"{tag}, \"{Name}\", {ClassName}" };
+
+ uiTreeNode.NodePath = xPathNode;
+ uiTreeNode.Tag = tag;
+ uiTreeNode.ClassName = ClassName;
+ uiTreeNode.Name = Name;
+ uiTreeNode.AutomationId = AutomationId;
+ uiTreeNode.Left = left;
+ uiTreeNode.Top = left;
+ uiTreeNode.Width = width;
+ uiTreeNode.Height = height;
+ uiTreeNode.RuntimeId = runtimeId;
+ uiTreeNode.Position = Pos;
+ uiTreeNode.NameCompareMethod = bStartsWithName ? UiTreeNode.CompareMethod.StartsWith : UiTreeNode.CompareMethod.Equal;
+ uiTreeNode.ClassNameCompareMethod = bStartsWithClass ? UiTreeNode.CompareMethod.StartsWith : UiTreeNode.CompareMethod.Equal;
+ uiTreeNode.AutomationIdCompareMethod = bStartsWithAutoId ? UiTreeNode.CompareMethod.StartsWith : UiTreeNode.CompareMethod.Equal;
+
+ if (i == pathNodes.Count - 1)
+ {
+ uiTreeNode.UiTask = recordedUiTask;
+ recordedUiTask.Left = left;
+ recordedUiTask.Top = top;
+ recordedUiTask.LeftLocal = leftLocal;
+ recordedUiTask.TopLocal = topLocal;
+ recordedUiTask.Name = Name;
+ recordedUiTask.Tag = tag;
}
+
+ if (rootRet == null)
+ {
+ rootRet = uiTreeNode;
+ }
+
+ if (parent != null)
+ {
+ parent.Items.Add(uiTreeNode);
+ }
+
+ parent = uiTreeNode;
}
- return "\"" + xPath + "\",\n";
+ return "\"" + xPath + "\"";
}
}
}
diff --git a/Tools/UIRecorder/UIRecorder/MainWindow.xaml b/Tools/UIRecorder/UIRecorder/MainWindow.xaml
index cdbe6f3a..1bf013f6 100644
--- a/Tools/UIRecorder/UIRecorder/MainWindow.xaml
+++ b/Tools/UIRecorder/UIRecorder/MainWindow.xaml
@@ -18,6 +18,8 @@
mc:Ignorable="d"
Loaded="Window_Loaded"
Closed="Window_Closed"
+ MouseEnter="Window_MouseEnter"
+ MouseLeave="Window_MouseLeave"
Title="WAD UIRecorder" Height="500" Width="330">
@@ -34,10 +36,17 @@
-
+
+
+
+
+
+
-
+
+
+
@@ -47,15 +56,49 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
+
+
+
+
+
+
@@ -63,44 +106,79 @@
+
-
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
-
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
-
+
+
diff --git a/Tools/UIRecorder/UIRecorder/MainWindow.xaml.cs b/Tools/UIRecorder/UIRecorder/MainWindow.xaml.cs
index 4c2752da..c1f91727 100644
--- a/Tools/UIRecorder/UIRecorder/MainWindow.xaml.cs
+++ b/Tools/UIRecorder/UIRecorder/MainWindow.xaml.cs
@@ -15,19 +15,12 @@
//******************************************************************************
using System;
-using System.Collections.Generic;
using System.Linq;
using System.Text;
-using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
-using System.Windows.Data;
using System.Windows.Documents;
-using System.Windows.Input;
using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Navigation;
-using System.Windows.Shapes;
namespace WinAppDriverUIRecorder
{
@@ -36,169 +29,310 @@ namespace WinAppDriverUIRecorder
///
public partial class MainWindow : Window
{
- const double tabCodeHeight = 250;
- public static IntPtr windowHandle;
- static MainWindow s_mainWin;
- public static List s_listRecordedUi = new List();
+ public static MainWindow s_mainWin = null;
+ public static IntPtr s_windowHandle;
+ RecordedUiTask selectedTreeViewItem;
+ static RecordedUiTask s_inspectUi;
+ bool bPauseMouseKeyboardCurrent = false;
System.Timers.Timer timer = null;
- const string strStopRecording = "Stop Recording";
+
+ public enum UiThreadTask { UnknownUiThreadTask = 3000, RemoveInspectNode, UpdateTreeView, Active, XPathReady, ActionAdded, PauseRecording, Init };
+ public enum UiSelectChangedSource { ComboBoxSelected, TreeViewItemSelected, SelectChangeFinished };
public MainWindow()
{
InitializeComponent();
}
- public double TabCodeHeight
- {
- get { return tabCodeHeight; }
- }
-
private void Window_Loaded(object sender, RoutedEventArgs e)
{
- gridXPath.RowDefinitions[2].Height = new GridLength(TabCodeHeight);
- windowHandle = new System.Windows.Interop.WindowInteropHelper(Application.Current.MainWindow).Handle;
- System.Windows.Interop.HwndSource.FromHwnd(windowHandle).AddHook(XmlNodePathRecorder.ProcessMessage);
+ s_windowHandle = new System.Windows.Interop.WindowInteropHelper(Application.Current.MainWindow).Handle;
+ System.Windows.Interop.HwndSource.FromHwnd(s_windowHandle).AddHook(RunOnUiThread);
s_mainWin = this;
- this.comboBoxRecordedUi.ItemsSource = s_listRecordedUi;
+ this.comboBoxRecordedUi.ItemsSource = RecordedUiTask.s_listRecordedUi;
+ this.treeUiPath.ItemsSource = UiTreeNode.s_uiTreeNodes;
ConstVariables.InitVk2StringMap();
- timer = new System.Timers.Timer(100) { Enabled = true, AutoReset = false };
- timer.Elapsed += InitMouseKeyboard;
- timer.Start();
+ AppInsights.LogEvent("Window_Loaded");
}
- private void InitMouseKeyboard(Object source, System.Timers.ElapsedEventArgs e)
+ public static IntPtr RunOnUiThread(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
- timer.Stop();
- timer.Elapsed -= InitMouseKeyboard;
- timer = null;
+ handled = false;
- this.Dispatcher.Invoke(new Action(() =>
+ if (s_mainWin.IsRecording == true)
{
- MouseKeyboardEventHandler.Init();
- }), System.Windows.Threading.DispatcherPriority.Normal);
+ if (msg == (int)UiThreadTask.Active)
+ {
+ s_mainWin.gridTextRecording.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0x40, 0x40));
+ s_mainWin.textBoxRecording.Text = "Active";
+ s_mainWin.gridTextRecording.UpdateLayout();
+ }
+ else if (msg == (int)UiThreadTask.XPathReady)
+ {
+ s_mainWin.gridTextRecording.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x40, 0x40, 0xff));
+ s_mainWin.textBoxRecording.Text = "XPath Ready";
+ s_mainWin.gridTextRecording.UpdateLayout();
+ }
+ else if (msg == (int)UiThreadTask.ActionAdded)
+ {
+ s_mainWin.gridTextRecording.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x40, 0xff, 0x40));
+ s_mainWin.textBoxRecording.Text = "UI Action Added";
+ s_mainWin.gridTextRecording.UpdateLayout();
+ }
+ else if (msg == (int)UiThreadTask.PauseRecording)
+ {
+ s_mainWin.gridTextRecording.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x80, 0x80, 0x80));
+ s_mainWin.gridTextRecording.UpdateLayout();
+ }
+ }
+
+ if (msg == (int)UiThreadTask.RemoveInspectNode)
+ {
+ s_mainWin.DeleteRecordedUiTask(s_inspectUi);
+ }
+ else if (msg == (int)UiThreadTask.UpdateTreeView)
+ {
+ if (s_mainWin.SelectChangedSource == UiSelectChangedSource.SelectChangeFinished)
+ {
+ lock (RecordedUiTask.s_lockRecordedUi)
+ {
+ if (RecordedUiTask.s_listRecordedUi.Count > 0)
+ {
+ // reset ItemsSource to null and s_listRecordedUi will force combobox value to update
+ s_mainWin.comboBoxRecordedUi.ItemsSource = null;
+ s_mainWin.comboBoxRecordedUi.ItemsSource = RecordedUiTask.s_listRecordedUi;
+ s_mainWin.comboBoxRecordedUi.SelectedIndex = RecordedUiTask.s_listRecordedUi.Count - 1;
+ s_mainWin.comboBoxRecordedUi.Items.Refresh();
+
+ s_mainWin.treeUiPath.Items.Refresh();
+ }
+ }
+ }
+ }
+
+ return IntPtr.Zero;
}
private void Window_Closed(object sender, EventArgs e)
{
MouseKeyboardEventHandler.UnInit();
MouseKeyboardHook.StopHook();
-
- System.Windows.Interop.HwndSource.FromHwnd(windowHandle).RemoveHook(XmlNodePathRecorder.ProcessMessage);
s_mainWin = null;
+
+ System.Windows.Interop.HwndSource.FromHwnd(s_windowHandle).RemoveHook(RunOnUiThread);
+
+ AppInsights.LogEvent("Window_Closed");
}
+ public string RootSessionPath { get; set; }
+
+ public bool IsRecording { get; set; }
+
+ public UiSelectChangedSource SelectChangedSource { get; set; }
+
private void btnRecord_Click(object sender, RoutedEventArgs e)
{
- // Stop recording
- if (btnRecord.Content.ToString() == strStopRecording)
+ SelectChangedSource = UiSelectChangedSource.SelectChangeFinished;
+
+ if (IsRecording == false)
{
- btnRecord.Content = "Record";
- this.btnClear.IsEnabled = true;
+ MouseKeyboardEventHandler.Init();
+
+ btnRecord.Content = "Pause";
+ textBoxRecording.Text = "Recording";
+ gridTextRecording.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0, 0));
+
+ IsRecording = true;
+ AppInsights.LogEvent("Record");
}
- else // Start recording
+ else
{
- btnRecord.Content = strStopRecording;
- this.btnClear.IsEnabled = false;
- }
+ IsRecording = false;
+
+ MouseKeyboardEventHandler.UnInit();
- btnRecord.Focusable = false;
+ btnRecord.Content = "Record";
+ textBoxRecording.Text = "";
+ gridTextRecording.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
+
+ AddInspectUi(null); // remove last Inspect node
+ AppInsights.LogEvent("Pause");
+ }
}
- public static bool IsRecording()
+ public static void UpdateLastUi(RecordedUiTask uiTask)
{
- return s_mainWin.btnRecord.Content.ToString() == strStopRecording;
+ NativeMethods.PostMessage(MainWindow.s_windowHandle, (int)UiThreadTask.UpdateTreeView, 0, 0);
}
- public static void UpdateLastUi(RecordedUiTask uiTask)
+ public static void AddInspectUi(RecordedUiTask uiTask)
{
- s_mainWin.Dispatcher.Invoke(new Action(() =>
+ s_inspectUi = null;
+ lock (RecordedUiTask.s_lockRecordedUi)
{
- TextRange tr = new TextRange(s_mainWin.rtbXPath.Document.ContentStart, s_mainWin.rtbXPath.Document.ContentEnd);
- tr.Text = uiTask.GetXPath();
+ foreach (var recordedUi in RecordedUiTask.s_listRecordedUi)
+ {
+ if (recordedUi.UiTaskName == EnumUiTaskName.Inspect)
+ {
+ s_inspectUi = recordedUi;
+ break;
+ }
+ }
- s_mainWin.textBoxXml.Text = uiTask.GetXml();
+ if (uiTask != null)
+ {
+ RecordedUiTask.s_listRecordedUi.Add(uiTask);
- s_mainWin.textBoxCode.Text = "";
- }), System.Windows.Threading.DispatcherPriority.ContextIdle);
+ if (uiTask.UiTaskName != EnumUiTaskName.KeyboardInput)
+ {
+ UiTreeNode.AddToUiTree(uiTask);
+ }
+ }
+ }
+
+ NativeMethods.PostMessage(MainWindow.s_windowHandle, (int)UiThreadTask.RemoveInspectNode, 0, 0);
+ NativeMethods.PostMessage(MainWindow.s_windowHandle, (int)UiThreadTask.UpdateTreeView, 0, 0);
+ if (uiTask != null)
+ {
+ NativeMethods.PostMessage(MainWindow.s_windowHandle, (int)UiThreadTask.XPathReady, 0, 0);
+ }
}
public static void AddRecordedUi(RecordedUiTask uiTask)
{
- if (uiTask.GetTask() != UiTaskName.Inspect)
+ lock (RecordedUiTask.s_lockRecordedUi)
{
- MainWindow.s_listRecordedUi.Add(uiTask);
- s_mainWin.btnClear.IsEnabled = s_listRecordedUi.Count > 0;
- s_mainWin.btnWinAppDriverCode.IsEnabled = s_listRecordedUi.Count > 0;
- }
+ RecordedUiTask.s_listRecordedUi.Add(uiTask);
- s_mainWin.Dispatcher.Invoke(new Action(() =>
- {
- if (uiTask.GetTask() != UiTaskName.Inspect)
+ if (uiTask.UiTaskName != EnumUiTaskName.KeyboardInput)
{
- s_mainWin.comboBoxRecordedUi.ItemsSource = null;
- s_mainWin.comboBoxRecordedUi.ItemsSource = MainWindow.s_listRecordedUi;
- s_mainWin.comboBoxRecordedUi.SelectedIndex = MainWindow.s_listRecordedUi.Count - 1;
- }
- else
- {
- UpdateLastUi(uiTask);
+ UiTreeNode.AddToUiTree(uiTask);
}
+ }
- }), System.Windows.Threading.DispatcherPriority.ContextIdle);
+ NativeMethods.PostMessage(MainWindow.s_windowHandle, (int)UiThreadTask.UpdateTreeView, 0, 0);
}
private void comboBoxRecordedUi_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
- var selUiTask = (comboBoxRecordedUi.SelectedItem as RecordedUiTask);
- if (selUiTask != null)
+ if (SelectChangedSource == UiSelectChangedSource.ComboBoxSelected)
+ {
+ return;
+ }
+
+ var selectedItem = (comboBoxRecordedUi.SelectedItem as RecordedUiTask);
+ if (selectedItem == null)
+ {
+ return;
+ }
+
+ if (SelectChangedSource != UiSelectChangedSource.TreeViewItemSelected)
+ SelectChangedSource = UiSelectChangedSource.ComboBoxSelected;
+
+ // C# code
+ var focusedElementName = "PreviousLeftClickElement";
+ if (selectedItem.UiTaskName == EnumUiTaskName.KeyboardInput)
{
+ lock (RecordedUiTask.s_lockRecordedUi)
+ {
+ foreach (var uiTask in RecordedUiTask.s_listRecordedUi)
+ {
+ if (uiTask.UiTaskName == EnumUiTaskName.LeftClick)
+ {
+ focusedElementName = uiTask.VariableName;
+ }
+
+ if (uiTask == selectedItem)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ textBoxCode.Text = selectedItem.GetCSCode(focusedElementName);
+
+ if (selectedItem.UiTaskName != EnumUiTaskName.KeyboardInput)
+ {
+ // xpath
TextRange tr = new TextRange(rtbXPath.Document.ContentStart, rtbXPath.Document.ContentEnd);
- tr.Text = selUiTask.GetXPath();
+ tr.Text = selectedItem.GetXPath(false);
- textBoxXml.Text = selUiTask.GetXml();
+ // TreeView
+ HighlightPath(0, true/*highlight session path*/);
- textBoxCode.Text = selUiTask.GetCSCode(comboBoxRecordedUi.SelectedIndex, comboBoxRecordedUi.SelectedIndex);
+ if (treeUiPath.Items != null && treeUiPath.Items.Count > 0)
+ {
+ var tvi = (TreeViewItem)treeUiPath.ItemContainerGenerator.ContainerFromItem(treeUiPath.Items[0]);
+ if (tvi != null)
+ {
+ tvi = ExpandSelectedTreeNode(UiTreeNode.s_uiTreeNodes.First(), tvi, selectedItem);
+ if (tvi != null)
+ {
+ // select to highlight leaf tree view node
+ tvi.IsSelected = true;
+ tvi.BringIntoView();
+ }
+ }
+ }
}
+
+ if (SelectChangedSource != UiSelectChangedSource.TreeViewItemSelected)
+ SelectChangedSource = UiSelectChangedSource.SelectChangeFinished;
}
private void btnClear_Click(object sender, RoutedEventArgs e)
{
- s_listRecordedUi.Clear();
+ lock (RecordedUiTask.s_lockRecordedUi)
+ {
+ RecordedUiTask.s_listRecordedUi.Clear();
+ UiTreeNode.s_uiTreeNodes.Clear();
+ }
this.comboBoxRecordedUi.Items.Refresh();
+ treeUiPath.Items.Refresh();
TextRange tr = new TextRange(rtbXPath.Document.ContentStart, rtbXPath.Document.ContentEnd);
tr.Text = "";
- textBoxXml.Text = "";
textBoxCode.Text = "";
+ RootSessionPath = null;
- btnClear.IsEnabled = s_listRecordedUi.Count > 0;
- btnWinAppDriverCode.IsEnabled = s_listRecordedUi.Count > 0;
+ AppInsights.LogEvent("btnClear_Click");
}
private void btnWinAppDriverCode_Click(object sender, RoutedEventArgs e)
{
- // Stop recording
- if (btnRecord.Content.ToString() == strStopRecording)
- {
- btnRecord_Click(null, null);
- }
-
StringBuilder sb = new StringBuilder();
- int nOrder = 0;
- int nOrderFocused = 0;
- foreach (var uiTask in s_listRecordedUi)
+
+ lock (RecordedUiTask.s_lockRecordedUi)
{
- nOrder++;
- if (uiTask.GetTask() == UiTaskName.LeftClick)
+ if (RecordedUiTask.s_listRecordedUi.Count == 0)
{
- nOrderFocused = nOrder;
+ return;
}
- sb.AppendLine(uiTask.GetCSCode(nOrder, nOrderFocused));
+ // Stop recording
+ if (btnRecord.IsChecked == true)
+ {
+ btnRecord_Click(null, null);
+ }
+
+ string focusedLeftElementName = "";
+ foreach (var uiTask in RecordedUiTask.s_listRecordedUi)
+ {
+ if (uiTask.UiTaskName != EnumUiTaskName.Inspect)
+ {
+ if (uiTask.UiTaskName == EnumUiTaskName.LeftClick)
+ {
+ focusedLeftElementName = uiTask.VariableName;
+ }
+
+ sb.AppendLine(uiTask.GetCSCode(focusedLeftElementName));
+ }
+ }
}
if (sb.Length > 0)
@@ -215,6 +349,8 @@ private void btnWinAppDriverCode_Click(object sender, RoutedEventArgs e)
timer.Start();
}), System.Windows.Threading.DispatcherPriority.ContextIdle);
}
+
+ AppInsights.LogEvent("Recorded UI Count", $"{RecordedUiTask.s_listRecordedUi.Count}");
}
private void ResetToolTip(Object source, System.Timers.ElapsedEventArgs e)
@@ -230,59 +366,498 @@ private void ResetToolTip(Object source, System.Timers.ElapsedEventArgs e)
}), System.Windows.Threading.DispatcherPriority.ContextIdle);
}
- private void HideTabXmlCode(bool bHide)
+ private void MenuCopyFullPath(object sender, RoutedEventArgs e)
+ {
+ var tr = new TextRange(rtbXPath.Document.ContentStart, rtbXPath.Document.ContentEnd);
+ if (tr != null)
+ {
+ Clipboard.SetText(tr.Text);
+ }
+
+ AppInsights.LogEvent("Copy full xpath");
+ }
+
+ private void MenuCopyPathWithoutSessionPath(object sender, RoutedEventArgs e)
+ {
+ var tr = new TextRange(rtbXPath.Document.ContentStart, rtbXPath.Document.ContentEnd);
+ if (tr != null && RootSessionPath != null)
+ {
+ int nPos = tr.Text.IndexOf(RootSessionPath);
+ if (nPos > -1)
+ {
+ string xpath = "\"" + tr.Text.Substring(nPos + RootSessionPath.Length);
+ Clipboard.SetText(xpath);
+ }
+ else
+ {
+ Clipboard.SetText(tr.Text);
+ }
+ }
+
+ AppInsights.LogEvent("Copy relative xpath");
+ }
+
+ private void MenuClearSessionPath(object sender, RoutedEventArgs e)
+ {
+ RootSessionPath = null;
+ HighlightPath(0, true);
+ }
+
+ private void DeleteRecordedUiTask(RecordedUiTask uiTask)
{
- if (bHide == true)
+ if (uiTask == null)
+ return;
+
+ lock (RecordedUiTask.s_lockRecordedUi)
{
- if (gridXPath.RowDefinitions[2].ActualHeight < TabCodeHeight + 1)
+ if (uiTask.UiTaskName != EnumUiTaskName.KeyboardInput)
+ {
+ UiTreeNode.RemoveUiTreeNode(uiTask);
+ }
+
+ RecordedUiTask.s_listRecordedUi.Remove(uiTask);
+
+ if (RecordedUiTask.s_listRecordedUi.Count > 0)
{
- bHide = false;
+ comboBoxRecordedUi.SelectedIndex = 0;
+ comboBoxRecordedUi.SelectedValue = RecordedUiTask.s_listRecordedUi[comboBoxRecordedUi.SelectedIndex];
}
+ else
+ {
+ TextRange tr = new TextRange(rtbXPath.Document.ContentStart, rtbXPath.Document.ContentEnd);
+ tr.Text = "";
+ textBoxCode.Text = "";
+ }
+
+ comboBoxRecordedUi.Items.Refresh();
+ treeUiPath.Items.Refresh();
+ }
+ }
+
+ private void btnDelete_Click(object sender, RoutedEventArgs e)
+ {
+ if (comboBoxRecordedUi.Items == null
+ || comboBoxRecordedUi.Items.Count == 0
+ || comboBoxRecordedUi.SelectedItem == null)
+ {
+ return;
}
- if (bHide == false)
+ var uiTask = (RecordedUiTask)comboBoxRecordedUi.SelectedItem;
+ if (uiTask == null)
{
- gridXPath.RowDefinitions[2].Height = new GridLength(gridXPath.ActualHeight * 0.667);
+ return;
+ }
+
+ DeleteRecordedUiTask(uiTask);
+
+ AppInsights.LogEvent("btnDelete_Click");
+ }
+
+ private string RemoveFirstNode(string xpath)
+ {
+ bool bTagStart = false;
+ int ncount = 0;
+ int nBracketCount = 0;
+
+ foreach (char c in xpath)
+ {
+ if (c == '[')
+ {
+ nBracketCount++;
+ }
+ else if (c == ']')
+ {
+ nBracketCount--;
+ }
+ else if (c == '/' && nBracketCount == 0)
+ {
+ if (bTagStart == false)
+ {
+ bTagStart = true;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ ncount++;
+ }
+
+ return xpath.Substring(ncount);
+ }
+
+ private string HighlightPath(int nNodeCount, bool bHighlight)
+ {
+ var tr = new TextRange(rtbXPath.Document.ContentStart, rtbXPath.Document.ContentEnd);
+
+ string pathText = tr.Text;
+ if (string.IsNullOrEmpty(pathText))
+ {
+ return null;
+ }
+
+ string xpath = pathText;
+ int nIndex1 = 0;
+ int nIndex2 = 0;
+
+ for (int node = 0; node < nNodeCount; node++)
+ {
+ int Len1 = xpath.Length;
+ xpath = RemoveFirstNode(xpath);
+
+ nIndex1 = nIndex2 + 1;
+ nIndex2 += (Len1 - xpath.Length);
+ }
+
+ if (nIndex2 < nIndex1)
+ {
+ return null;
+ }
+
+ if (bHighlight == true)
+ {
+ tr.ClearAllProperties();
+
+ if (RootSessionPath != null && pathText.Contains(RootSessionPath) == true)
+ {
+ var posRoot1 = rtbXPath.Document.ContentStart.GetPositionAtOffset(1, LogicalDirection.Forward);
+ var posRoot2 = rtbXPath.Document.ContentStart.GetPositionAtOffset(RootSessionPath.Length + 2, LogicalDirection.Forward);
+ var trRoot = new TextRange(posRoot1, posRoot2);
+ trRoot.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.LightGray);
+ }
+
+ if (nNodeCount > 0)
+ {
+ var pos1 = rtbXPath.Document.ContentStart.GetPositionAtOffset(nIndex1 + 1, LogicalDirection.Forward);
+ var pos2 = rtbXPath.Document.ContentStart.GetPositionAtOffset(nIndex2 + 2, LogicalDirection.Forward);
+ tr = new TextRange(pos1, pos2);
+ tr.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.LightBlue);
+ }
+ }
+
+ return pathText.Substring(nIndex1, nIndex2 - nIndex1); //excluding leading quote
+ }
+
+ private RecordedUiTask GetExpandedLeafNode(TreeViewItem tvi, UiTreeNode node, RecordedUiTask recordedUiTask)
+ {
+ if (tvi == null || node == null)
+ {
+ return null;
+ }
+
+ if (recordedUiTask != null)
+ {
+ if (node.UiTask == recordedUiTask)
+ {
+ return node.UiTask;
+ }
+ }
+ else if (node.Items.Count == 0)
+ {
+ return node.UiTask;
+ }
+
+ foreach (var c in node.Items)
+ {
+ var subContainer = (TreeViewItem)tvi.ItemContainerGenerator.ContainerFromItem(c);
+ if (subContainer != null)
+ {
+ if (subContainer.IsExpanded == true)
+ {
+ var retTemp = GetExpandedLeafNode(subContainer, c, recordedUiTask);
+ if (retTemp != null)
+ {
+ return retTemp;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private TreeViewItem UiTreeNode2TreeViewItem(TreeViewItem tvi, UiTreeNode node, UiTreeNode nodeTarget)
+ {
+ if (tvi == null || node == null)
+ {
+ return null;
+ }
+
+ if (node == nodeTarget)
+ {
+ return tvi;
+ }
+
+ foreach (var c in node.Items)
+ {
+ var subContainer = (TreeViewItem)tvi.ItemContainerGenerator.ContainerFromItem(c);
+ var ret = UiTreeNode2TreeViewItem(subContainer, c, nodeTarget);
+ if (ret != null)
+ {
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
+ private void treeUiPath_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs