diff --git a/src/common/UITestAutomation/Element/By.cs b/src/common/UITestAutomation/Element/By.cs index 2e4814accde5..b2d8ecf6c449 100644 --- a/src/common/UITestAutomation/Element/By.cs +++ b/src/common/UITestAutomation/Element/By.cs @@ -11,17 +11,40 @@ namespace Microsoft.PowerToys.UITest /// public class By { - private readonly OpenQA.Selenium.By by; + private readonly OpenQA.Selenium.By? by; + private readonly bool isAccessibilityId; + private readonly string? accessibilityId; private By(OpenQA.Selenium.By by) { + isAccessibilityId = false; this.by = by; } + private By(string accessibilityId) + { + isAccessibilityId = true; + this.accessibilityId = accessibilityId; + } + public override string ToString() { // override ToString to return detailed debugging content provided by OpenQA.Selenium.By - return this.by.ToString(); + return this.GetAccessibilityId(); + } + + public bool GetIsAccessibilityId() => this.isAccessibilityId; + + public string GetAccessibilityId() + { + if (this.isAccessibilityId) + { + return this.accessibilityId!; + } + else + { + return this.by!.ToString(); + } } /// @@ -45,6 +68,13 @@ public override string ToString() /// A By object. public static By Id(string id) => new By(OpenQA.Selenium.By.Id(id)); + /// + /// Creates a By object using the ID attribute. + /// + /// The ID attribute to search for. + /// A By object. + public static By AccessibilityId(string accessibilityId) => new By(accessibilityId); + /// /// Creates a By object using the XPath expression. /// @@ -77,6 +107,6 @@ public override string ToString() /// Converts the By object to an OpenQA.Selenium.By object. /// /// An OpenQA.Selenium.By object. - internal OpenQA.Selenium.By ToSeleniumBy() => by; + internal OpenQA.Selenium.By ToSeleniumBy() => by!; } } diff --git a/src/common/UITestAutomation/Element/Element.cs b/src/common/UITestAutomation/Element/Element.cs index 70bdc690c08e..7ca0cf53a5b5 100644 --- a/src/common/UITestAutomation/Element/Element.cs +++ b/src/common/UITestAutomation/Element/Element.cs @@ -7,6 +7,7 @@ using System.Runtime.CompilerServices; using System.Xml.Linq; using ABI.Windows.Foundation; +using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenQA.Selenium; using OpenQA.Selenium.Appium; @@ -192,7 +193,10 @@ public void Drag(Element element) /// The Key to Send. public void SendKeys(string key) { - windowsElement?.SendKeys(key); + PerformAction((actions, windowElement) => + { + windowElement.SendKeys(key); + }); } /// @@ -241,26 +245,6 @@ public T Find(string name, int timeoutMS = 3000) return this.Find(By.Name(name), timeoutMS); } - /// - /// Shortcut for this.FindAllByAccessibilityId(accessibilityId, timeoutMS) - /// - /// The class of the element, should be Element or its derived class. - /// The accessibilityId of the element. - /// The timeout in milliseconds (default is 3000). - /// The found element. - public T FindByAccessibilityId(string accessibilityId, int timeoutMS = 3000) - where T : Element, new() - { - Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method Find<{typeof(T).Name}> with parameters: accessibilityId = {accessibilityId}, timeoutMS = {timeoutMS}"); - - // leverage findAll to filter out mismatched elements - var collection = this.FindAllByAccessibilityId(accessibilityId, timeoutMS); - - Assert.IsTrue(collection.Count > 0, $"Element not found using selector: {accessibilityId}"); - - return collection[0]; - } - /// /// Finds an element by the selector. /// Shortcut for this.Find(by, timeoutMS) @@ -299,31 +283,16 @@ public ReadOnlyCollection FindAll(By by, int timeoutMS = 3000) var foundElements = FindHelper.FindAll( () => { - var elements = this.windowsElement.FindElements(by.ToSeleniumBy()); - return elements; - }, - this.driver, - timeoutMS); - - return foundElements ?? new ReadOnlyCollection([]); - } - - /// - /// Finds all elements by accessibilityId. - /// - /// The class of the elements, should be Element or its derived class. - /// The accessibilityId to find the elements. - /// The timeout in milliseconds (default is 3000). - /// A read-only collection of the found elements. - public ReadOnlyCollection FindAllByAccessibilityId(string accessibilityId, int timeoutMS = 3000) - where T : Element, new() - { - Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: accessibilityId = {accessibilityId}, timeoutMS = {timeoutMS}"); - var foundElements = FindHelper.FindAll( - () => - { - var elements = this.windowsElement.FindElementsByAccessibilityId(accessibilityId); - return elements; + if (by.GetIsAccessibilityId()) + { + var elements = this.windowsElement.FindElementsByAccessibilityId(by.GetAccessibilityId()); + return elements; + } + else + { + var elements = this.windowsElement.FindElements(by.ToSeleniumBy()); + return elements; + } }, this.driver, timeoutMS); diff --git a/src/common/UITestAutomation/Session.cs b/src/common/UITestAutomation/Session.cs index f4b5535ae10d..9e5101fc759e 100644 --- a/src/common/UITestAutomation/Session.cs +++ b/src/common/UITestAutomation/Session.cs @@ -8,6 +8,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenQA.Selenium.Appium; using OpenQA.Selenium.Appium.Windows; +using OpenQA.Selenium.Interactions; namespace Microsoft.PowerToys.UITest { @@ -62,26 +63,6 @@ public T Find(string name, int timeoutMS = 3000) return this.Find(By.Name(name), timeoutMS); } - /// - /// Shortcut for this.FindAllByAccessibilityId(accessibilityId, timeoutMS) - /// - /// The class of the element, should be Element or its derived class. - /// The accessibilityId of the element. - /// The timeout in milliseconds (default is 3000). - /// The found element. - public T FindByAccessibilityId(string accessibilityId, int timeoutMS = 3000) - where T : Element, new() - { - Assert.IsNotNull(this.WindowsDriver, $"WindowsElement is null in method Find<{typeof(T).Name}> with parameters: accessibilityId = {accessibilityId}, timeoutMS = {timeoutMS}"); - - // leverage findAll to filter out mismatched elements - var collection = this.FindAllByAccessibilityId(accessibilityId, timeoutMS); - - Assert.IsTrue(collection.Count > 0, $"Element not found using selector: {accessibilityId}"); - - return collection[0]; - } - /// /// Shortcut for this.Find(by, timeoutMS) /// @@ -118,31 +99,16 @@ public ReadOnlyCollection FindAll(By by, int timeoutMS = 3000) var foundElements = FindHelper.FindAll( () => { - var elements = this.WindowsDriver.FindElements(by.ToSeleniumBy()); - return elements; - }, - this.WindowsDriver, - timeoutMS); - - return foundElements ?? new ReadOnlyCollection([]); - } - - /// - /// Finds all elements by accessibilityId. - /// - /// The class of the elements, should be Element or its derived class. - /// The accessibilityId to find the elements. - /// The timeout in milliseconds (default is 3000). - /// A read-only collection of the found elements. - public ReadOnlyCollection FindAllByAccessibilityId(string accessibilityId, int timeoutMS = 3000) - where T : Element, new() - { - Assert.IsNotNull(this.WindowsDriver, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: accessibilityId = {accessibilityId}, timeoutMS = {timeoutMS}"); - var foundElements = FindHelper.FindAll( - () => - { - var elements = this.WindowsDriver.FindElementsByAccessibilityId(accessibilityId); - return elements; + if (by.GetIsAccessibilityId()) + { + var elements = this.WindowsDriver.FindElementsByAccessibilityId(by.GetAccessibilityId()); + return elements; + } + else + { + var elements = this.WindowsDriver.FindElements(by.ToSeleniumBy()); + return elements; + } }, this.WindowsDriver, timeoutMS); @@ -188,6 +154,39 @@ public ReadOnlyCollection FindAll(string name, int timeoutMS = 3000) return this.FindAll(By.Name(name), timeoutMS); } + /// + /// Keyboard Action key. + /// + /// The Keys1 to click. + /// The Keys2 to click. + /// The Keys3 to click. + /// The Keys4 to click. + public void KeyboardAction(string key1, string key2 = "", string key3 = "", string key4 = "") + { + PerformAction((actions, windowElement) => + { + if (string.IsNullOrEmpty(key2)) + { + actions.SendKeys(key1); + } + else if (string.IsNullOrEmpty(key3)) + { + actions.SendKeys(key1).SendKeys(key2); + } + else if (string.IsNullOrEmpty(key4)) + { + actions.SendKeys(key1).SendKeys(key2).SendKeys(key3); + } + else + { + actions.SendKeys(key1).SendKeys(key2).SendKeys(key3).SendKeys(key4); + } + + actions.Release(); + actions.Build().Perform(); + }); + } + /// /// Attaches to an existing PowerToys module. /// @@ -232,5 +231,28 @@ public Session Attach(string windowName) return this; } + + /// + /// Simulates a manual operation on the element. + /// + /// The action to perform on the element. + /// The number of milliseconds to wait before the action. Default value is 500 ms + /// The number of milliseconds to wait after the action. Default value is 500 ms + protected void PerformAction(Action> action, int msPreAction = 500, int msPostAction = 500) + { + if (msPreAction > 0) + { + Task.Delay(msPreAction).Wait(); + } + + var windowsDriver = this.WindowsDriver; + Actions actions = new Actions(this.WindowsDriver); + action(actions, windowsDriver); + + if (msPostAction > 0) + { + Task.Delay(msPostAction).Wait(); + } + } } } diff --git a/src/common/UITestAutomation/UITestBase.cs b/src/common/UITestAutomation/UITestBase.cs index d9887708289e..1fb37468ff2a 100644 --- a/src/common/UITestAutomation/UITestBase.cs +++ b/src/common/UITestAutomation/UITestBase.cs @@ -52,9 +52,10 @@ public void TestInit() /// /// UnInitializes the test. /// + [TestCleanup] public void TestClean() { - this.sessionHelper.Cleanup(); + // this.sessionHelper.Cleanup(); } /// diff --git a/src/modules/fancyzones/UITests-FancyZonesEditor/DefaultLayoutsTest.cs b/src/modules/fancyzones/UITests-FancyZonesEditor/DefaultLayoutsTest.cs index 4f95b4712e7a..06c06d53e289 100644 --- a/src/modules/fancyzones/UITests-FancyZonesEditor/DefaultLayoutsTest.cs +++ b/src/modules/fancyzones/UITests-FancyZonesEditor/DefaultLayoutsTest.cs @@ -64,27 +64,21 @@ public DefaultLayoutsTest() FancyZonesEditorHelper.Files.ParamsIOHelper.WriteData(editorParameters.Serialize(parameters)); } - [TestCleanup] - public void TestCleanup() - { - this.TestClean(); - } - [TestMethod] public void ClickMonitor() { - Assert.IsNotNull(Session.FindByAccessibilityId("Monitors").Find("Monitor 1")); - Assert.IsNotNull(Session.FindByAccessibilityId("Monitors").Find("Monitor 2")); + Assert.IsNotNull(Session.Find("Monitor 1")); + Assert.IsNotNull(Session.Find("Monitor 2")); // verify that the monitor 1 is selected initially - Assert.IsTrue(Session.FindByAccessibilityId("Monitors").Find("Monitor 1").Selected); - Assert.IsFalse(Session.FindByAccessibilityId("Monitors").Find("Monitor 2").Selected); + Assert.IsTrue(Session.Find("Monitor 1").Selected); + Assert.IsFalse(Session.Find("Monitor 2").Selected); - Session.FindByAccessibilityId("Monitors").Find("Monitor 2").Click(); + Session.Find("Monitor 2").Click(); // verify that the monitor 2 is selected after click - Assert.IsFalse(Session.FindByAccessibilityId("Monitors").Find("Monitor 1").Selected); - Assert.IsTrue(Session.FindByAccessibilityId("Monitors").Find("Monitor 2").Selected); + Assert.IsFalse(Session.Find("Monitor 1").Selected); + Assert.IsTrue(Session.Find("Monitor 2").Selected); } } } diff --git a/src/modules/fancyzones/UITests-FancyZonesEditor/DeleteLayoutTests.cs b/src/modules/fancyzones/UITests-FancyZonesEditor/DeleteLayoutTests.cs new file mode 100644 index 000000000000..4d24dc4045c4 --- /dev/null +++ b/src/modules/fancyzones/UITests-FancyZonesEditor/DeleteLayoutTests.cs @@ -0,0 +1,356 @@ +// 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.Collections.Generic; +using FancyZonesEditor.Models; +using FancyZonesEditorCommon.Data; +using Microsoft.FancyZonesEditor.UnitTests.Utils; +using Microsoft.PowerToys.UITest; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using ModernWpf.Controls; +using OpenQA.Selenium; +using static FancyZonesEditorCommon.Data.CustomLayouts; +using static FancyZonesEditorCommon.Data.DefaultLayouts; +using static FancyZonesEditorCommon.Data.EditorParameters; +using static FancyZonesEditorCommon.Data.LayoutHotkeys; +using static Microsoft.FancyZonesEditor.UnitTests.Utils.FancyZonesEditorHelper; + +namespace Microsoft.FancyZonesEditor.UITests +{ + [TestClass] + public class DeleteLayoutTests : UITestBase + { + public DeleteLayoutTests() + : base(PowerToysModule.FancyZone) + { + } + + private static readonly CustomLayoutListWrapper CustomLayouts = new CustomLayoutListWrapper + { + CustomLayouts = new List + { + new CustomLayoutWrapper + { + Uuid = "{0D6D2F58-9184-4804-81E4-4E4CC3476DC1}", + Type = CustomLayout.Grid.TypeToString(), + Name = "Custom layout 1", + Info = new CustomLayouts().ToJsonElement(new CustomLayouts.GridInfoWrapper + { + Rows = 2, + Columns = 3, + RowsPercentage = new List { 2967, 7033 }, + ColumnsPercentage = new List { 2410, 6040, 1550 }, + CellChildMap = new int[][] { [0, 1, 1], [0, 2, 3] }, + SensitivityRadius = 30, + Spacing = 26, + ShowSpacing = false, + }), + }, + new CustomLayoutWrapper + { + Uuid = "{E7807D0D-6223-4883-B15B-1F3883944C09}", + Type = CustomLayout.Canvas.TypeToString(), + Name = "Custom layout 2", + Info = new CustomLayouts().ToJsonElement(new CanvasInfoWrapper + { + RefHeight = 952, + RefWidth = 1500, + SensitivityRadius = 10, + Zones = new List + { + new CanvasInfoWrapper.CanvasZoneWrapper + { + X = 0, + Y = 0, + Width = 900, + Height = 522, + }, + new CanvasInfoWrapper.CanvasZoneWrapper + { + X = 900, + Y = 0, + Width = 600, + Height = 750, + }, + new CanvasInfoWrapper.CanvasZoneWrapper + { + X = 0, + Y = 522, + Width = 1500, + Height = 430, + }, + }, + }), + }, + }, + }; + + private static readonly DefaultLayouts.DefaultLayoutsListWrapper DefaultLayoutsList = new DefaultLayouts.DefaultLayoutsListWrapper + { + DefaultLayouts = new List + { + new DefaultLayoutWrapper + { + MonitorConfiguration = MonitorConfigurationType.Horizontal.TypeToString(), + Layout = new DefaultLayoutWrapper.LayoutWrapper + { + Type = LayoutType.Custom.TypeToString(), + Uuid = CustomLayouts.CustomLayouts[1].Uuid, + }, + }, + }, + }; + + private static readonly LayoutHotkeys.LayoutHotkeysWrapper Hotkeys = new LayoutHotkeys.LayoutHotkeysWrapper + { + LayoutHotkeys = new List + { + new LayoutHotkeyWrapper + { + LayoutId = CustomLayouts.CustomLayouts[1].Uuid, + Key = 0, + }, + }, + }; + + private static readonly ParamsWrapper Parameters = new ParamsWrapper + { + ProcessId = 1, + SpanZonesAcrossMonitors = false, + Monitors = new List + { + new NativeMonitorDataWrapper + { + Monitor = "monitor-1", + MonitorInstanceId = "instance-id-1", + MonitorSerialNumber = "serial-number-1", + MonitorNumber = 1, + VirtualDesktop = "{FF34D993-73F3-4B8C-AA03-73730A01D6A8}", + Dpi = 192, + LeftCoordinate = 0, + TopCoordinate = 0, + WorkAreaHeight = 1040, + WorkAreaWidth = 1920, + MonitorHeight = 1080, + MonitorWidth = 1920, + IsSelected = true, + }, + }, + }; + + [TestInitialize] + public void TestInitialize() + { + EditorParameters editorParameters = new EditorParameters(); + FancyZonesEditorHelper.Files.ParamsIOHelper.WriteData(editorParameters.Serialize(Parameters)); + + CustomLayouts customLayouts = new CustomLayouts(); + FancyZonesEditorHelper.Files.CustomLayoutsIOHelper.WriteData(customLayouts.Serialize(CustomLayouts)); + + LayoutTemplates layoutTemplates = new LayoutTemplates(); + LayoutTemplates.TemplateLayoutsListWrapper templateLayoutsListWrapper = new LayoutTemplates.TemplateLayoutsListWrapper + { + LayoutTemplates = new List + { + new LayoutTemplates.TemplateLayoutWrapper + { + Type = LayoutType.Blank.TypeToString(), + }, + new LayoutTemplates.TemplateLayoutWrapper + { + Type = LayoutType.Focus.TypeToString(), + ZoneCount = 10, + }, + new LayoutTemplates.TemplateLayoutWrapper + { + Type = LayoutType.Rows.TypeToString(), + ZoneCount = 2, + ShowSpacing = true, + Spacing = 10, + SensitivityRadius = 10, + }, + new LayoutTemplates.TemplateLayoutWrapper + { + Type = LayoutType.Columns.TypeToString(), + ZoneCount = 2, + ShowSpacing = true, + Spacing = 20, + SensitivityRadius = 20, + }, + new LayoutTemplates.TemplateLayoutWrapper + { + Type = LayoutType.Grid.TypeToString(), + ZoneCount = 4, + ShowSpacing = false, + Spacing = 10, + SensitivityRadius = 30, + }, + new LayoutTemplates.TemplateLayoutWrapper + { + Type = LayoutType.PriorityGrid.TypeToString(), + ZoneCount = 3, + ShowSpacing = true, + Spacing = 1, + SensitivityRadius = 40, + }, + }, + }; + FancyZonesEditorHelper.Files.LayoutTemplatesIOHelper.WriteData(layoutTemplates.Serialize(templateLayoutsListWrapper)); + + DefaultLayouts defaultLayouts = new DefaultLayouts(); + FancyZonesEditorHelper.Files.DefaultLayoutsIOHelper.WriteData(defaultLayouts.Serialize(DefaultLayoutsList)); + + LayoutHotkeys layoutHotkeys = new LayoutHotkeys(); + FancyZonesEditorHelper.Files.LayoutHotkeysIOHelper.WriteData(layoutHotkeys.Serialize(Hotkeys)); + + AppliedLayouts appliedLayouts = new AppliedLayouts(); + AppliedLayouts.AppliedLayoutsListWrapper appliedLayoutsWrapper = new AppliedLayouts.AppliedLayoutsListWrapper + { + AppliedLayouts = new List { }, + }; + FancyZonesEditorHelper.Files.AppliedLayoutsIOHelper.WriteData(appliedLayouts.Serialize(appliedLayoutsWrapper)); + + this.RestartScopeExe(); + Session.Find(CustomLayouts.CustomLayouts[0].Name).Click(); + } + + [TestCleanup] + public void TestCleanup() + { + FancyZonesEditorHelper.Files.Restore(); + } + + [TestMethod] + public void DeleteNotAppliedLayout() + { + var deletedLayout = CustomLayouts.CustomLayouts[1].Name; + Session.Find(deletedLayout).Find