// Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.ObjectModel; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Xml.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenQA.Selenium; using OpenQA.Selenium.Appium; using OpenQA.Selenium.Appium.Windows; using OpenQA.Selenium.Interactions; namespace Microsoft.PowerToys.UITest { /// /// Provides interfaces for interacting with UI elements. /// public class Session { private WindowsDriver Root { get; set; } private WindowsDriver WindowsDriver { get; set; } [DllImport("user32.dll")] private static extern bool SetForegroundWindow(nint hWnd); public Session(WindowsDriver root, WindowsDriver windowsDriver) { this.Root = root; this.WindowsDriver = windowsDriver; } /// /// Finds an element by selector. /// /// The class of the element, should be Element or its derived class. /// The selector to find the element. /// The timeout in milliseconds (default is 3000). /// The found element. public T Find(By by, int timeoutMS = 3000) where T : Element, new() { Assert.IsNotNull(this.WindowsDriver, $"WindowsElement is null in method Find<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}"); var foundElement = FindElementHelper.Find( () => { var element = this.WindowsDriver.FindElement(by.ToSeleniumBy()); Assert.IsNotNull(element, $"Element not found using selector: {by}"); return element; }, this.WindowsDriver, timeoutMS); return foundElement; } /// /// Finds all elements by selector. /// /// The class of the elements, should be Element or its derived class. /// The selector to find the elements. /// The timeout in milliseconds (default is 3000). /// A read-only collection of the found elements. public ReadOnlyCollection? FindAll(By by, int timeoutMS = 3000) where T : Element, new() { Assert.IsNotNull(this.WindowsDriver, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}"); var foundElements = FindElementHelper.FindAll( () => { var elements = this.WindowsDriver.FindElements(by.ToSeleniumBy()); Assert.IsTrue(elements.Count > 0, $"Elements not found using selector: {by}"); return elements; }, this.WindowsDriver, timeoutMS); return foundElements; } /// /// Attaches to an existing PowerToys module. /// /// The PowerToys module to attach to. /// The attached session. public Session Attach(PowerToysModule module) { string windowName = ModuleConfigData.Instance.GetModuleWindowName(module); return this.Attach(windowName); } /// /// Attaches to an existing exe by string window name. /// The session should be attached when a new app is started. /// /// The window name to attach to. /// The attached session. public Session Attach(string windowName) { if (this.Root != null) { var window = this.Root.FindElementByName(windowName); Assert.IsNotNull(window, $"Failed to attach. Window '{windowName}' not found"); var windowHandle = new nint(int.Parse(window.GetAttribute("NativeWindowHandle"))); SetForegroundWindow(windowHandle); var hexWindowHandle = windowHandle.ToString("x"); var appCapabilities = new AppiumOptions(); appCapabilities.AddAdditionalCapability("appTopLevelWindow", hexWindowHandle); appCapabilities.AddAdditionalCapability("deviceName", "WindowsPC"); this.WindowsDriver = new WindowsDriver(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), appCapabilities); Assert.IsNotNull(this.WindowsDriver, "Attach WindowsDriver is null"); // Set implicit timeout to make element search retry every 500 ms this.WindowsDriver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(3); } else { Assert.IsNotNull(this.Root, $"Failed to attach to the window '{windowName}'. Root driver is null"); } return this; } } }