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 @@ + - + + + + + + - + + + - - + + + -