From 16fc08c62d22d1b978e86096f829c5e1f7e190fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Fri, 1 Dec 2023 12:04:21 +0100 Subject: [PATCH 1/4] Added more scroll methods to UITests --- .../tests/UITests/Tests/ScrollViewUITests.cs | 11 + .../Actions/AppiumScrollActions.cs | 215 ++++++++++++++++++ src/TestUtils/src/UITest.Appium/AppiumApp.cs | 3 +- .../src/UITest.Appium/HelperExtensions.cs | 89 +++++++- 4 files changed, 316 insertions(+), 2 deletions(-) create mode 100644 src/TestUtils/src/UITest.Appium/Actions/AppiumScrollActions.cs diff --git a/src/Controls/tests/UITests/Tests/ScrollViewUITests.cs b/src/Controls/tests/UITests/Tests/ScrollViewUITests.cs index 2ced664ed7f3..868dd6b820f7 100644 --- a/src/Controls/tests/UITests/Tests/ScrollViewUITests.cs +++ b/src/Controls/tests/UITests/Tests/ScrollViewUITests.cs @@ -74,5 +74,16 @@ public void ScrollToElement3End() Assert.Ignore("This test is failing, likely due to product issue"); } } + + [Test] + [Description("Scroll down the ScrollView using a gesture")] + public void ScrollUpAndDownWithGestures() + { + App.ScrollDown("thescroller", ScrollStrategy.Gesture, 0.75); + App.Screenshot("Element scrolled down"); + + App.ScrollUp("thescroller", ScrollStrategy.Gesture, 0.75); + App.Screenshot("Element scrolled up"); + } } } \ No newline at end of file diff --git a/src/TestUtils/src/UITest.Appium/Actions/AppiumScrollActions.cs b/src/TestUtils/src/UITest.Appium/Actions/AppiumScrollActions.cs new file mode 100644 index 000000000000..5db6e13953c3 --- /dev/null +++ b/src/TestUtils/src/UITest.Appium/Actions/AppiumScrollActions.cs @@ -0,0 +1,215 @@ +using OpenQA.Selenium.Appium; +using OpenQA.Selenium.Appium.MultiTouch; +using UITest.Core; + +namespace UITest.Appium +{ + public enum ScrollStrategy + { + Auto, + Gesture, + Programmatically + } + + public class AppiumScrollActions : ICommandExecutionGroup + { + const string ScrollLeftCommand = "scrollLeft"; + const string ScrollDownCommand = "scrollDown"; + const string ScrollRightCommand = "scrollRight"; + const string ScrollUpCommand = "scrollUp"; + + readonly AppiumApp _appiumApp; + + readonly List _commands = new() + { + ScrollLeftCommand, + ScrollDownCommand, + ScrollRightCommand, + ScrollUpCommand, + }; + + public AppiumScrollActions(AppiumApp appiumApp) + { + _appiumApp = appiumApp; + } + + public bool IsCommandSupported(string commandName) + { + return _commands.Contains(commandName, StringComparer.OrdinalIgnoreCase); + } + + public CommandResponse Execute(string commandName, IDictionary parameters) + { + return commandName switch + { + ScrollLeftCommand => ScrollLeft(parameters), + ScrollDownCommand => ScrollDown(parameters), + ScrollRightCommand => ScrollRight(parameters), + ScrollUpCommand => ScrollUp(parameters), + _ => CommandResponse.FailedEmptyResponse, + }; + } + + CommandResponse ScrollLeft(IDictionary parameters) + { + parameters.TryGetValue("element", out var value); + var element = GetAppiumElement(value); + + if (element is null) + return CommandResponse.FailedEmptyResponse; + + ScrollStrategy strategy = (ScrollStrategy)parameters["strategy"]; + double swipePercentage = (double)parameters["swipePercentage"]; + int swipeSpeed = (int)parameters["swipeSpeed"]; + bool withInertia = (bool)parameters["withInertia"]; + + ScrollToLeft(_appiumApp.Driver, element, strategy, swipePercentage, swipeSpeed, withInertia); + + return CommandResponse.SuccessEmptyResponse; + } + + CommandResponse ScrollDown(IDictionary parameters) + { + parameters.TryGetValue("element", out var value); + var element = GetAppiumElement(value); + + if (element is null) + return CommandResponse.FailedEmptyResponse; + + ScrollStrategy strategy = (ScrollStrategy)parameters["strategy"]; + double swipePercentage = (double)parameters["swipePercentage"]; + int swipeSpeed = (int)parameters["swipeSpeed"]; + bool withInertia = (bool)parameters["withInertia"]; + + ScrollToDown(_appiumApp.Driver, element, strategy, swipePercentage, swipeSpeed, withInertia); + + return CommandResponse.SuccessEmptyResponse; + } + + CommandResponse ScrollRight(IDictionary parameters) + { + parameters.TryGetValue("element", out var value); + var element = GetAppiumElement(value); + + if (element is null) + return CommandResponse.FailedEmptyResponse; + + ScrollStrategy strategy = (ScrollStrategy)parameters["strategy"]; + double swipePercentage = (double)parameters["swipePercentage"]; + int swipeSpeed = (int)parameters["swipeSpeed"]; + bool withInertia = (bool)parameters["withInertia"]; + + ScrollToRight(_appiumApp.Driver, element, strategy, swipePercentage, swipeSpeed, withInertia); + + return CommandResponse.SuccessEmptyResponse; + } + + CommandResponse ScrollUp(IDictionary parameters) + { + parameters.TryGetValue("element", out var value); + var element = GetAppiumElement(value); + + if (element is null) + return CommandResponse.FailedEmptyResponse; + + ScrollStrategy strategy = (ScrollStrategy)parameters["strategy"]; + double swipePercentage = (double)parameters["swipePercentage"]; + int swipeSpeed = (int)parameters["swipeSpeed"]; + bool withInertia = (bool)parameters["withInertia"]; + + ScrollToUp(_appiumApp.Driver, element, strategy, swipePercentage, swipeSpeed, withInertia); + + return CommandResponse.SuccessEmptyResponse; + } + + static AppiumElement? GetAppiumElement(object? element) + { + if (element is AppiumElement appiumElement) + { + return appiumElement; + } + else if (element is AppiumDriverElement driverElement) + { + return driverElement.AppiumElement; + } + + return null; + } + + static void ScrollToLeft(AppiumDriver driver, AppiumElement element, ScrollStrategy strategy, double swipePercentage, int swipeSpeed, bool withInertia = true) + { + var position = element.Location; + var size = element.Size; + + int startX = (int)(position.X + (size.Width * 0.05)); + int startY = position.Y + size.Height / 2; + + int endX = (int)(position.X + (size.Width * swipePercentage)); + int endY = startY; + + new TouchAction(driver) + .Press(startX, startY) + .Wait(swipeSpeed) + .MoveTo(endX, endY) + .Release() + .Perform(); + } + + static void ScrollToDown(AppiumDriver driver, AppiumElement element, ScrollStrategy strategy, double swipePercentage, int swipeSpeed, bool withInertia = true) + { + var position = element.Location; + var size = element.Size; + + int startX = position.X + size.Width / 2; + int startY = (int)(position.Y + (size.Height * swipePercentage)); + + int endX = startX; + int endY = (int)(position.Y + (size.Height * 0.05)); + + new TouchAction(driver) + .Press(startX, startY) + .Wait(swipeSpeed) + .MoveTo(endX, endY) + .Release() + .Perform(); + } + + static void ScrollToRight(AppiumDriver driver, AppiumElement element, ScrollStrategy strategy, double swipePercentage, int swipeSpeed, bool withInertia = true) + { + var position = element.Location; + var size = element.Size; + + int startX = (int)(position.X + (size.Width * swipePercentage)); + int startY = position.Y + size.Height / 2; + + int endX = (int)(position.X + (size.Width * 0.05)); + int endY = startY; + + new TouchAction(driver) + .Press(startX, startY) + .Wait(swipeSpeed) + .MoveTo(endX, endY) + .Release() + .Perform(); + } + + static void ScrollToUp(AppiumDriver driver, AppiumElement element, ScrollStrategy strategy, double swipePercentage, int swipeSpeed, bool withInertia = true) + { + var position = element.Location; + var size = element.Size; + + int startX = position.X + size.Width / 2; + int startY = (int)(position.Y + (size.Height * 0.05)); + + int endX = startX; + int endY = (int)(position.Y + (size.Height * swipePercentage)); + + new TouchAction(driver) + .Press(startX, startY) + .Wait(swipeSpeed) + .MoveTo(endX, endY) + .Release() + .Perform(); + } + } +} diff --git a/src/TestUtils/src/UITest.Appium/AppiumApp.cs b/src/TestUtils/src/UITest.Appium/AppiumApp.cs index fa8ccd2868c9..d53256de3610 100644 --- a/src/TestUtils/src/UITest.Appium/AppiumApp.cs +++ b/src/TestUtils/src/UITest.Appium/AppiumApp.cs @@ -1,4 +1,4 @@ -using OpenQA.Selenium; +using OpenQA.Selenium; using OpenQA.Selenium.Appium; using OpenQA.Selenium.Appium.Enums; using UITest.Core; @@ -23,6 +23,7 @@ public AppiumApp(AppiumDriver driver, IConfig config) _commandExecutor.AddCommandGroup(new AppiumVirtualKeyboardActions(this)); _commandExecutor.AddCommandGroup(new AppiumSliderActions(this)); _commandExecutor.AddCommandGroup(new AppiumSwipeActions(this)); + _commandExecutor.AddCommandGroup(new AppiumScrollActions(this)); _commandExecutor.AddCommandGroup(new AppiumOrientationActions(this)); } diff --git a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs index 90969d3b68bb..c780d8004ee9 100644 --- a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs +++ b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs @@ -1,4 +1,4 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Diagnostics; using System.Drawing; @@ -225,6 +225,26 @@ public static void SwipeLeftToRight(this IApp app, string marked, double swipePe app.CommandExecutor.Execute("swipeLeftToRight", new Dictionary { { "element", elementToSwipe}, + }); + } + + /// + /// Scrolls left on the first element matching query. + /// + /// Represents the main gateway to interact with an app. + /// Marked selector to match. + /// Strategy for scrolling element. + /// How far across the element to swipe (from 0.0 to 1.0). + /// The speed of the gesture. + /// Whether swipes should cause inertia. + public static void ScrollLeft(this IApp app, string marked, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true) + { + var elementToSwipe = app.FindElement(marked); + + app.CommandExecutor.Execute("scrollLeft", new Dictionary + { + { "element", elementToSwipe}, + { "strategy", strategy }, { "swipePercentage", swipePercentage }, { "swipeSpeed", swipeSpeed }, { "withInertia", withInertia } @@ -264,12 +284,79 @@ public static void SwipeRightToLeft(this IApp app, string marked, double swipePe app.CommandExecutor.Execute("swipeRightToLeft", new Dictionary { { "element", elementToSwipe}, + }); + } + + /// + /// Scrolls down on the first element matching query. + /// + /// Represents the main gateway to interact with an app. + /// Marked selector to match. + /// Strategy for scrolling element. + /// How far across the element to swipe (from 0.0 to 1.0). + /// The speed of the gesture. + /// Whether swipes should cause inertia. + public static void ScrollDown(this IApp app, string marked, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true) + { + var elementToSwipe = app.FindElement(marked); + + app.CommandExecutor.Execute("scrollDown", new Dictionary + { + { "element", elementToSwipe}, + { "strategy", strategy }, { "swipePercentage", swipePercentage }, { "swipeSpeed", swipeSpeed }, { "withInertia", withInertia } }); } + /// + /// Scrolls right on the first element matching query. + /// + /// Represents the main gateway to interact with an app. + /// Marked selector to match. + /// Strategy for scrolling element. + /// How far across the element to swipe (from 0.0 to 1.0). + /// The speed of the gesture. + /// Whether swipes should cause inertia. + public static void ScrollRight(this IApp app, string marked, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true) + { + var elementToSwipe = app.FindElement(marked); + + app.CommandExecutor.Execute("scrollRight", new Dictionary + { + { "element", elementToSwipe}, + { "strategy", strategy }, + { "swipePercentage", swipePercentage }, + { "swipeSpeed", swipeSpeed }, + { "withInertia", withInertia } + }); + } + + /// + /// Scrolls up on the first element matching query. + /// + /// Represents the main gateway to interact with an app. + /// Marked selector to match. + /// Strategy for scrolling element. + /// How far across the element to swipe (from 0.0 to 1.0). + /// The speed of the gesture. + /// Whether swipes should cause inertia. + public static void ScrollUp(this IApp app, string marked, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true) + { + var elementToSwipe = app.FindElement(marked); + + app.CommandExecutor.Execute("scrollUp", new Dictionary + { + { "element", elementToSwipe}, + { "strategy", strategy }, + { "swipePercentage", swipePercentage }, + { "swipeSpeed", swipeSpeed }, + { "withInertia", withInertia } + }); + } + + /// /// Changes the device orientation to landscape mode. /// /// Represents the main gateway to interact with an app. From ed26e69193eb7bc606ff5568272f944497a57052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Fri, 1 Dec 2023 13:25:42 +0100 Subject: [PATCH 2/4] Using ScrollStrategy --- .../src/UITest.Appium/Actions/AppiumScrollActions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/TestUtils/src/UITest.Appium/Actions/AppiumScrollActions.cs b/src/TestUtils/src/UITest.Appium/Actions/AppiumScrollActions.cs index 5db6e13953c3..a58d3650cda6 100644 --- a/src/TestUtils/src/UITest.Appium/Actions/AppiumScrollActions.cs +++ b/src/TestUtils/src/UITest.Appium/Actions/AppiumScrollActions.cs @@ -149,7 +149,7 @@ static void ScrollToLeft(AppiumDriver driver, AppiumElement element, ScrollStrat new TouchAction(driver) .Press(startX, startY) - .Wait(swipeSpeed) + .Wait(strategy != ScrollStrategy.Programmatically ? swipeSpeed : 0) .MoveTo(endX, endY) .Release() .Perform(); @@ -168,7 +168,7 @@ static void ScrollToDown(AppiumDriver driver, AppiumElement element, ScrollStrat new TouchAction(driver) .Press(startX, startY) - .Wait(swipeSpeed) + .Wait(strategy != ScrollStrategy.Programmatically ? swipeSpeed : 0) .MoveTo(endX, endY) .Release() .Perform(); @@ -187,7 +187,7 @@ static void ScrollToRight(AppiumDriver driver, AppiumElement element, ScrollStra new TouchAction(driver) .Press(startX, startY) - .Wait(swipeSpeed) + .Wait(strategy != ScrollStrategy.Programmatically ? swipeSpeed : 0) .MoveTo(endX, endY) .Release() .Perform(); @@ -206,7 +206,7 @@ static void ScrollToUp(AppiumDriver driver, AppiumElement element, ScrollStrateg new TouchAction(driver) .Press(startX, startY) - .Wait(swipeSpeed) + .Wait(strategy != ScrollStrategy.Programmatically ? swipeSpeed : 0) .MoveTo(endX, endY) .Release() .Perform(); From 3d10bff09a86020d24b802294bb73a6dbf2ff9e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Mon, 4 Dec 2023 13:36:40 +0100 Subject: [PATCH 3/4] Fix merge issues --- src/Controls/tests/UITests/Tests/ScrollViewUITests.cs | 2 ++ src/TestUtils/src/UITest.Appium/HelperExtensions.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Controls/tests/UITests/Tests/ScrollViewUITests.cs b/src/Controls/tests/UITests/Tests/ScrollViewUITests.cs index 868dd6b820f7..737403b6743a 100644 --- a/src/Controls/tests/UITests/Tests/ScrollViewUITests.cs +++ b/src/Controls/tests/UITests/Tests/ScrollViewUITests.cs @@ -79,6 +79,8 @@ public void ScrollToElement3End() [Description("Scroll down the ScrollView using a gesture")] public void ScrollUpAndDownWithGestures() { + this.IgnoreIfPlatforms(new TestDevice[] { TestDevice.Mac, TestDevice.Windows }); + App.ScrollDown("thescroller", ScrollStrategy.Gesture, 0.75); App.Screenshot("Element scrolled down"); diff --git a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs index c780d8004ee9..c0c9ed8551d8 100644 --- a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs +++ b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs @@ -479,4 +479,4 @@ public static void SetSliderValue(this IApp app, string marked, double value, do Wait(query, i => i == null, timeoutMessage, timeout, retryFrequency); } } -} +} \ No newline at end of file From 4c1dc1c7c70bc6957521900a6688061be21fa8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Mon, 11 Dec 2023 08:47:44 +0100 Subject: [PATCH 4/4] Fix merge issue --- .../src/UITest.Appium/HelperExtensions.cs | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs index c0c9ed8551d8..6afff7564aff 100644 --- a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs +++ b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs @@ -224,27 +224,24 @@ public static void SwipeLeftToRight(this IApp app, string marked, double swipePe app.CommandExecutor.Execute("swipeLeftToRight", new Dictionary { - { "element", elementToSwipe}, + { "element", elementToSwipe}, + { "swipePercentage", swipePercentage }, + { "swipeSpeed", swipeSpeed }, + { "withInertia", withInertia } }); } /// - /// Scrolls left on the first element matching query. + /// Performs a right to left swipe gesture on the screen. /// /// Represents the main gateway to interact with an app. - /// Marked selector to match. - /// Strategy for scrolling element. /// How far across the element to swipe (from 0.0 to 1.0). /// The speed of the gesture. /// Whether swipes should cause inertia. - public static void ScrollLeft(this IApp app, string marked, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true) + public static void SwipeRightToLeft(this IApp app, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true) { - var elementToSwipe = app.FindElement(marked); - - app.CommandExecutor.Execute("scrollLeft", new Dictionary + app.CommandExecutor.Execute("swipeRightToLeft", new Dictionary { - { "element", elementToSwipe}, - { "strategy", strategy }, { "swipePercentage", swipePercentage }, { "swipeSpeed", swipeSpeed }, { "withInertia", withInertia } @@ -252,16 +249,21 @@ public static void ScrollLeft(this IApp app, string marked, ScrollStrategy strat } /// - /// Performs a right to left swipe gesture on the screen. + /// Performs a right to left swipe gesture on the matching element. + /// If multiple elements are matched, the first one will be used. /// /// Represents the main gateway to interact with an app. + /// Marked selector to match. /// How far across the element to swipe (from 0.0 to 1.0). /// The speed of the gesture. /// Whether swipes should cause inertia. - public static void SwipeRightToLeft(this IApp app, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true) + public static void SwipeRightToLeft(this IApp app, string marked, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true) { + var elementToSwipe = app.FindElement(marked); + app.CommandExecutor.Execute("swipeRightToLeft", new Dictionary { + { "element", elementToSwipe}, { "swipePercentage", swipePercentage }, { "swipeSpeed", swipeSpeed }, { "withInertia", withInertia } @@ -269,21 +271,25 @@ public static void SwipeRightToLeft(this IApp app, double swipePercentage = 0.67 } /// - /// Performs a right to left swipe gesture on the matching element. - /// If multiple elements are matched, the first one will be used. + /// Scrolls left on the first element matching query. /// /// Represents the main gateway to interact with an app. /// Marked selector to match. + /// Strategy for scrolling element. /// How far across the element to swipe (from 0.0 to 1.0). /// The speed of the gesture. /// Whether swipes should cause inertia. - public static void SwipeRightToLeft(this IApp app, string marked, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true) + public static void ScrollLeft(this IApp app, string marked, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true) { var elementToSwipe = app.FindElement(marked); - app.CommandExecutor.Execute("swipeRightToLeft", new Dictionary + app.CommandExecutor.Execute("scrollLeft", new Dictionary { { "element", elementToSwipe}, + { "strategy", strategy }, + { "swipePercentage", swipePercentage }, + { "swipeSpeed", swipeSpeed }, + { "withInertia", withInertia } }); }