From 2f2d77bfb71a5156e7a8525db43b3b57eabb35e5 Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Tue, 16 Jul 2024 19:34:13 +0530 Subject: [PATCH 01/16] feat: Add locator types supported by flutter integration driver --- src/main/java/io/appium/java_client/AppiumBy.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/io/appium/java_client/AppiumBy.java b/src/main/java/io/appium/java_client/AppiumBy.java index 164c7d3e7..d8d8f1b9e 100644 --- a/src/main/java/io/appium/java_client/AppiumBy.java +++ b/src/main/java/io/appium/java_client/AppiumBy.java @@ -26,6 +26,9 @@ import java.io.Serializable; import java.util.List; +import java.util.Map; +import java.util.Collections; +import java.util.HashMap; import static com.google.common.base.Strings.isNullOrEmpty; @@ -57,6 +60,13 @@ public String toString() { return String.format("%s.%s: %s", AppiumBy.class.getSimpleName(), locatorName, remoteParameters.value()); } + public Map toJson() { + Map params = new HashMap(); + params.put("using", this.remoteParameters.using()); + params.put("value", this.remoteParameters.value()); + return Collections.unmodifiableMap(params); + } + /** * About Android accessibility * https://developer.android.com/intl/ru/training/accessibility/accessible-app.html From 980cca035c5b6fa11cd0819590824819561cf764 Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Sat, 20 Jul 2024 16:42:54 +0530 Subject: [PATCH 02/16] Implement custom flutter drivers for android and ios --- .../java/io/appium/java_client/AppiumBy.java | 12 +- .../flutter/FlutterAndroidDriver.java | 134 ++++++++++++++++++ .../java_client/flutter/FlutterDriver.java | 22 +++ .../java_client/flutter/FlutterIOSDriver.java | 133 +++++++++++++++++ .../commands/CommonFlutterCommands.java | 94 ++++++++++++ .../flutter/commands/DoubleClickOptions.java | 5 + .../flutter/commands/DragAndDropOptions.java | 28 ++++ .../commands/FlutterCommandOptions.java | 25 ++++ .../commands/FlutterGestureOptions.java | 29 ++++ .../flutter/commands/LongPressOptions.java | 13 ++ .../flutter/commands/ScrollOptions.java | 59 ++++++++ .../flutter/commands/WaitOptions.java | 34 +++++ 12 files changed, 579 insertions(+), 9 deletions(-) create mode 100644 src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java create mode 100644 src/main/java/io/appium/java_client/flutter/FlutterDriver.java create mode 100644 src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java create mode 100644 src/main/java/io/appium/java_client/flutter/commands/CommonFlutterCommands.java create mode 100644 src/main/java/io/appium/java_client/flutter/commands/DoubleClickOptions.java create mode 100644 src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java create mode 100644 src/main/java/io/appium/java_client/flutter/commands/FlutterCommandOptions.java create mode 100644 src/main/java/io/appium/java_client/flutter/commands/FlutterGestureOptions.java create mode 100644 src/main/java/io/appium/java_client/flutter/commands/LongPressOptions.java create mode 100644 src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java create mode 100644 src/main/java/io/appium/java_client/flutter/commands/WaitOptions.java diff --git a/src/main/java/io/appium/java_client/AppiumBy.java b/src/main/java/io/appium/java_client/AppiumBy.java index d8d8f1b9e..1fc9ca6ad 100644 --- a/src/main/java/io/appium/java_client/AppiumBy.java +++ b/src/main/java/io/appium/java_client/AppiumBy.java @@ -25,10 +25,11 @@ import org.openqa.selenium.WebElement; import java.io.Serializable; -import java.util.List; -import java.util.Map; import java.util.Collections; import java.util.HashMap; +import java.util.List; +import java.util.Map; + import static com.google.common.base.Strings.isNullOrEmpty; @@ -60,13 +61,6 @@ public String toString() { return String.format("%s.%s: %s", AppiumBy.class.getSimpleName(), locatorName, remoteParameters.value()); } - public Map toJson() { - Map params = new HashMap(); - params.put("using", this.remoteParameters.using()); - params.put("value", this.remoteParameters.value()); - return Collections.unmodifiableMap(params); - } - /** * About Android accessibility * https://developer.android.com/intl/ru/training/accessibility/accessible-app.html diff --git a/src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java b/src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java new file mode 100644 index 000000000..3e6900205 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java @@ -0,0 +1,134 @@ +package io.appium.java_client.flutter; + +import io.appium.java_client.AppiumClientConfig; +import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.flutter.commands.CommonFlutterCommands; +import io.appium.java_client.flutter.commands.DoubleClickOptions; +import io.appium.java_client.flutter.commands.DragAndDropOptions; +import io.appium.java_client.flutter.commands.LongPressOptions; +import io.appium.java_client.flutter.commands.ScrollOptions; +import io.appium.java_client.flutter.commands.WaitOptions; +import io.appium.java_client.service.local.AppiumDriverLocalService; +import io.appium.java_client.service.local.AppiumServiceBuilder; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.remote.HttpCommandExecutor; +import org.openqa.selenium.remote.http.ClientConfig; +import org.openqa.selenium.remote.http.HttpClient; + +import java.net.URL; + +/** + * Custom AndroidDriver implementation with additional Flutter-specific capabilities. + */ +public class FlutterAndroidDriver extends AndroidDriver implements FlutterDriver { + + public FlutterAndroidDriver(HttpCommandExecutor executor, Capabilities capabilities) { + super(executor, capabilities); + } + + public FlutterAndroidDriver(URL remoteAddress, Capabilities capabilities) { + super(remoteAddress, capabilities); + } + + public FlutterAndroidDriver(URL remoteAddress, HttpClient.Factory httpClientFactory, Capabilities capabilities) { + super(remoteAddress, httpClientFactory, capabilities); + } + + public FlutterAndroidDriver(AppiumDriverLocalService service, Capabilities capabilities) { + super(service, capabilities); + } + + public FlutterAndroidDriver( + AppiumDriverLocalService service, HttpClient.Factory httpClientFactory, Capabilities capabilities) { + super(service, httpClientFactory, capabilities); + } + + public FlutterAndroidDriver(AppiumServiceBuilder builder, Capabilities capabilities) { + super(builder, capabilities); + } + + public FlutterAndroidDriver( + AppiumServiceBuilder builder, HttpClient.Factory httpClientFactory, Capabilities capabilities) { + super(builder, httpClientFactory, capabilities); + } + + public FlutterAndroidDriver(HttpClient.Factory httpClientFactory, Capabilities capabilities) { + super(httpClientFactory, capabilities); + } + + public FlutterAndroidDriver(ClientConfig clientConfig, Capabilities capabilities) { + super(clientConfig, capabilities); + } + + public FlutterAndroidDriver(AppiumClientConfig appiumClientConfig, Capabilities capabilities) { + super(appiumClientConfig, capabilities); + } + + public FlutterAndroidDriver(Capabilities capabilities) { + super(capabilities); + } + + public FlutterAndroidDriver(URL remoteSessionAddress, String automationName) { + super(remoteSessionAddress, automationName); + } + + /** + * Waits for an element to become visible on the Flutter application. + * + * @param option WaitOptions specifying the element and optional timeout. + */ + @Override + public void waitForVisible(WaitOptions option) { + CommonFlutterCommands.waitForVisible(this, option); + } + + /** + * Waits for an element to become invisible on the Flutter application. + * + * @param option WaitOptions specifying the element and optional timeout. + */ + @Override + public void waitForInVisible(WaitOptions option) { + CommonFlutterCommands.waitForInVisible(this, option); + } + + /** + * Scrolls to make an element visible on the Flutter application. + * + * @param option ScrollOptions specifying the finder and scroll direction. + */ + @Override + public void scrollTillVisible(ScrollOptions option) { + CommonFlutterCommands.scrollTillVisible(this, option); + } + + /** + * Performs a double click on an element in the Flutter application. + * + * @param option DoubleClickOptions specifying the element to double-click. + */ + @Override + public void performDoubleClick(DoubleClickOptions option) { + CommonFlutterCommands.performDoubleClick(this, option); + } + + /** + * Performs a long press on an element in the Flutter application. + * + * @param option LongPressOptions specifying the element to long-press. + */ + @Override + public void performLongPress(LongPressOptions option) { + CommonFlutterCommands.performLongPress(this, option); + } + + /** + * Performs a drag-and-drop operation between two elements in the Flutter application. + * + * @param option DragAndDropOptions specifying the source and target elements for the drag-and-drop operation. + */ + @Override + public void performDragAndDrop(DragAndDropOptions option) { + CommonFlutterCommands.performDragAndDrop(this, option); + } +} \ No newline at end of file diff --git a/src/main/java/io/appium/java_client/flutter/FlutterDriver.java b/src/main/java/io/appium/java_client/flutter/FlutterDriver.java new file mode 100644 index 000000000..999839014 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/FlutterDriver.java @@ -0,0 +1,22 @@ +package io.appium.java_client.flutter; + + +import io.appium.java_client.flutter.commands.DoubleClickOptions; +import io.appium.java_client.flutter.commands.DragAndDropOptions; +import io.appium.java_client.flutter.commands.LongPressOptions; +import io.appium.java_client.flutter.commands.ScrollOptions; +import io.appium.java_client.flutter.commands.WaitOptions; + +public interface FlutterDriver { + void waitForVisible(WaitOptions option); + + void waitForInVisible(WaitOptions option); + + void scrollTillVisible(ScrollOptions option); + + void performDoubleClick(DoubleClickOptions option); + + void performLongPress(LongPressOptions option); + + void performDragAndDrop(DragAndDropOptions option); +} diff --git a/src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java b/src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java new file mode 100644 index 000000000..250415713 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java @@ -0,0 +1,133 @@ +package io.appium.java_client.flutter; + +import io.appium.java_client.AppiumClientConfig; +import io.appium.java_client.flutter.commands.CommonFlutterCommands; +import io.appium.java_client.flutter.commands.DoubleClickOptions; +import io.appium.java_client.flutter.commands.DragAndDropOptions; +import io.appium.java_client.flutter.commands.LongPressOptions; +import io.appium.java_client.flutter.commands.ScrollOptions; +import io.appium.java_client.flutter.commands.WaitOptions; +import io.appium.java_client.ios.IOSDriver; +import io.appium.java_client.service.local.AppiumDriverLocalService; +import io.appium.java_client.service.local.AppiumServiceBuilder; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.remote.HttpCommandExecutor; +import org.openqa.selenium.remote.http.ClientConfig; +import org.openqa.selenium.remote.http.HttpClient; + +import java.net.URL; + +/** + * Custom AndroidDriver implementation with additional Flutter-specific capabilities. + */ +public class FlutterIOSDriver extends IOSDriver implements FlutterDriver { + + public FlutterIOSDriver(HttpCommandExecutor executor, Capabilities capabilities) { + super(executor, capabilities); + } + + public FlutterIOSDriver(URL remoteAddress, Capabilities capabilities) { + super(remoteAddress, capabilities); + } + + public FlutterIOSDriver(URL remoteAddress, HttpClient.Factory httpClientFactory, Capabilities capabilities) { + super(remoteAddress, httpClientFactory, capabilities); + } + + public FlutterIOSDriver(AppiumDriverLocalService service, Capabilities capabilities) { + super(service, capabilities); + } + + public FlutterIOSDriver( + AppiumDriverLocalService service, HttpClient.Factory httpClientFactory, Capabilities capabilities) { + super(service, httpClientFactory, capabilities); + } + + public FlutterIOSDriver(AppiumServiceBuilder builder, Capabilities capabilities) { + super(builder, capabilities); + } + + public FlutterIOSDriver( + AppiumServiceBuilder builder, HttpClient.Factory httpClientFactory, Capabilities capabilities) { + super(builder, httpClientFactory, capabilities); + } + + public FlutterIOSDriver(HttpClient.Factory httpClientFactory, Capabilities capabilities) { + super(httpClientFactory, capabilities); + } + + public FlutterIOSDriver(ClientConfig clientConfig, Capabilities capabilities) { + super(clientConfig, capabilities); + } + + public FlutterIOSDriver(AppiumClientConfig appiumClientConfig, Capabilities capabilities) { + super(appiumClientConfig, capabilities); + } + + public FlutterIOSDriver(URL remoteSessionAddress) { + super(remoteSessionAddress); + } + + public FlutterIOSDriver(Capabilities capabilities) { + super(capabilities); + } + + /** + * Waits for an element to become visible on the Flutter application. + * + * @param option WaitOptions specifying the element and optional timeout. + */ + public void waitForVisible(WaitOptions option) { + CommonFlutterCommands.waitForVisible(this, option); + } + + /** + * Waits for an element to become invisible on the Flutter application. + * + * @param option WaitOptions specifying the element and optional timeout. + */ + @Override + public void waitForInVisible(WaitOptions option) { + CommonFlutterCommands.waitForInVisible(this, option); + } + + /** + * Scrolls to make an element visible on the Flutter application. + * + * @param option ScrollOptions specifying the finder and scroll direction. + */ + @Override + public void scrollTillVisible(ScrollOptions option) { + CommonFlutterCommands.scrollTillVisible(this, option); + } + + /** + * Performs a double click on an element in the Flutter application. + * + * @param option DoubleClickOptions specifying the element to double-click. + */ + @Override + public void performDoubleClick(DoubleClickOptions option) { + CommonFlutterCommands.performDoubleClick(this, option); + } + + /** + * Performs a long press on an element in the Flutter application. + * + * @param option LongPressOptions specifying the element to long-press. + */ + @Override + public void performLongPress(LongPressOptions option) { + CommonFlutterCommands.performLongPress(this, option); + } + + /** + * Performs a drag-and-drop operation between two elements in the Flutter application. + * + * @param option DragAndDropOptions specifying the source and target elements for the drag-and-drop operation. + */ + @Override + public void performDragAndDrop(DragAndDropOptions option) { + CommonFlutterCommands.performDragAndDrop(this, option); + } +} \ No newline at end of file diff --git a/src/main/java/io/appium/java_client/flutter/commands/CommonFlutterCommands.java b/src/main/java/io/appium/java_client/flutter/commands/CommonFlutterCommands.java new file mode 100644 index 000000000..a37a551cd --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/commands/CommonFlutterCommands.java @@ -0,0 +1,94 @@ +package io.appium.java_client.flutter.commands; + +import io.appium.java_client.flutter.FlutterDriver; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public class CommonFlutterCommands { + + private static final String FLUTTER_COMMAND_PREFIX = "flutter"; + private static final String WAIT_FOR_VISIBLE_COMMAND = "waitForVisible"; + private static final String WAIT_FOR_INVISIBLE_COMMAND = "waitForAbsent"; + private static final String SCROLL_TILL_VISIBLE = "scrollTillVisible"; + private static final String DOUBLE_CLICK = "doubleClick"; + private static final String LONG_PRESS = "longPress"; + private static final String DRAG_AND_DROP = "dragAndDrop"; + + private CommonFlutterCommands() { + } + + /** + * Waits for an element to become visible on the Flutter application. + * + * @param driver WebDriver instance to execute the command on. + * @param option WaitOptions specifying the element and optional timeout. + */ + public static void waitForVisible(FlutterDriver driver, WaitOptions option) { + executeScript(driver, WAIT_FOR_VISIBLE_COMMAND, option); + } + + /** + * Waits for an element to become invisible on the Flutter application. + * + * @param driver WebDriver instance to execute the command on. + * @param option WaitOptions specifying the element and optional timeout. + */ + public static void waitForInVisible(FlutterDriver driver, WaitOptions option) { + executeScript(driver, WAIT_FOR_INVISIBLE_COMMAND, option); + } + + /** + * Scrolls to make an element visible on the Flutter application. + * + * @param driver WebDriver instance to execute the command on. + * @param option ScrollOptions specifying the finder and scroll direction. + * @return WebElement that was scrolled to and made visible. + */ + public static WebElement scrollTillVisible(FlutterDriver driver, ScrollOptions option) { + return (WebElement) executeScript(driver, SCROLL_TILL_VISIBLE, option); + } + + /** + * Performs a double click on an element in the Flutter application. + * + * @param driver WebDriver instance to execute the command on. + * @param option DoubleClickOptions specifying the element to double-click. + */ + public static void performDoubleClick(FlutterDriver driver, DoubleClickOptions option) { + executeScript(driver, DOUBLE_CLICK, option); + } + + /** + * Performs a long press on an element in the Flutter application. + * + * @param driver WebDriver instance to execute the command on. + * @param option LongPressOptions specifying the element to long-press. + */ + public static void performLongPress(FlutterDriver driver, LongPressOptions option) { + executeScript(driver, LONG_PRESS, option); + } + + /** + * Performs a drag-and-drop operation between two elements in the Flutter application. + * + * @param driver WebDriver instance to execute the command on. + * @param option DragAndDropOptions specifying the source and target elements for the drag-and-drop operation. + */ + public static void performDragAndDrop(FlutterDriver driver, DragAndDropOptions option) { + executeScript(driver, DRAG_AND_DROP, option); + } + + /** + * Executes a Flutter command using JavaScriptExecutor. + * + * @param driver WebDriver instance to execute the command on. + * @param scriptName Name of the Flutter command script. + * @param args FlutterCommandOptions containing the command arguments. + * @return Object returned by the JavaScript execution. + */ + public static Object executeScript(FlutterDriver driver, String scriptName, FlutterCommandOptions args) { + String commandName = String.format("%s: %s", FLUTTER_COMMAND_PREFIX, scriptName); + return ((JavascriptExecutor) driver).executeScript(commandName, args.toJson()); + } +} diff --git a/src/main/java/io/appium/java_client/flutter/commands/DoubleClickOptions.java b/src/main/java/io/appium/java_client/flutter/commands/DoubleClickOptions.java new file mode 100644 index 000000000..311b6b05d --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/commands/DoubleClickOptions.java @@ -0,0 +1,5 @@ +package io.appium.java_client.flutter.commands; + +public class DoubleClickOptions extends FlutterGestureOptions { + +} \ No newline at end of file diff --git a/src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java b/src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java new file mode 100644 index 000000000..d6df695e3 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java @@ -0,0 +1,28 @@ +package io.appium.java_client.flutter.commands; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import org.openqa.selenium.WebElement; + +import java.util.Map; + +@Accessors(chain = true) +@Getter +@AllArgsConstructor +public class DragAndDropOptions implements FlutterCommandOptions { + @NonNull + WebElement source; + @NonNull + WebElement target; + + private DragAndDropOptions() { + } + + @Override + public Map toJson() { + return Map.of("source", source, "target", target); + } +} \ No newline at end of file diff --git a/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandOptions.java b/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandOptions.java new file mode 100644 index 000000000..7223e5972 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandOptions.java @@ -0,0 +1,25 @@ +package io.appium.java_client.flutter.commands; + +import io.appium.java_client.AppiumBy; +import org.openqa.selenium.By; + +import java.util.Map; + +public interface FlutterCommandOptions { + /** + * Parses an {@link AppiumBy.FlutterBy} object into a Map of parameters + * to be executed by flutter integration driver. + * + * @param by The {@link AppiumBy.FlutterBy} object to parse. + * @return A {@link Map} containing the parsed parameters + */ + default Map parseBy(AppiumBy.FlutterBy by) { + By.Remotable.Parameters parameters = by.getRemoteParameters(); + return Map.of( + "using", parameters.using(), + "value", parameters.value() + ); + } + + Map toJson(); +} diff --git a/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureOptions.java b/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureOptions.java new file mode 100644 index 000000000..52be2a390 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureOptions.java @@ -0,0 +1,29 @@ +package io.appium.java_client.flutter.commands; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.openqa.selenium.Point; +import org.openqa.selenium.WebElement; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +@Accessors(chain = true) +@Getter +@Setter +public class FlutterGestureOptions implements FlutterCommandOptions { + WebElement element; + Point point; + + @Override + public Map toJson() { + Map args = new HashMap<>(); + args.put("origin", element); + if (point != null) { + args.put("offset", Map.of("x", point.x, "y", point.y)); + } + return Collections.unmodifiableMap(args); + } +} diff --git a/src/main/java/io/appium/java_client/flutter/commands/LongPressOptions.java b/src/main/java/io/appium/java_client/flutter/commands/LongPressOptions.java new file mode 100644 index 000000000..ab7cf08da --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/commands/LongPressOptions.java @@ -0,0 +1,13 @@ +package io.appium.java_client.flutter.commands; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + + +@Accessors(chain = true) +@Setter +@Getter +public class LongPressOptions extends FlutterGestureOptions { + +} \ No newline at end of file diff --git a/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java b/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java new file mode 100644 index 000000000..701e11aa2 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java @@ -0,0 +1,59 @@ +package io.appium.java_client.flutter.commands; + +import io.appium.java_client.AppiumBy; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.openqa.selenium.WebElement; + +import java.time.Duration; +import java.util.Map; +import java.util.Optional; + +@Accessors(chain = true) +@Getter +@Setter +public class ScrollOptions implements FlutterCommandOptions { + AppiumBy.FlutterBy scrollTo; + WebElement scrollView; + ScrollDirection scrollDirection; + Integer delta; + Integer maxScrolls; + Integer settleBetweenScrollsTimeout; + Duration dragDuration; + + private ScrollOptions() { + } + + public ScrollOptions(AppiumBy.FlutterBy scrollTo, ScrollDirection scrollDirection) { + this.scrollTo = scrollTo; + this.scrollDirection = scrollDirection; + } + + @Override + public Map toJson() { + return Map.of( + "finder", scrollTo != null ? parseBy(scrollTo) : null, + "scrollView", scrollView, + "delta", delta, + "maxScrolls", maxScrolls, + "settleBetweenScrollsTimeout", settleBetweenScrollsTimeout, + "scrollDirection", Optional.ofNullable(scrollDirection).orElse(ScrollDirection.UP).getDirection(), + "dragDuration", Optional.ofNullable(dragDuration).orElse(Duration.ZERO).getSeconds() + ); + } + + @Getter + public static enum ScrollDirection { + UP("up"), + RIGHT("right"), + DOWN("down"), + LEFT("left"); + + private final String direction; + + ScrollDirection(String direction) { + this.direction = direction; + } + } +} \ No newline at end of file diff --git a/src/main/java/io/appium/java_client/flutter/commands/WaitOptions.java b/src/main/java/io/appium/java_client/flutter/commands/WaitOptions.java new file mode 100644 index 000000000..327b529d4 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/commands/WaitOptions.java @@ -0,0 +1,34 @@ +package io.appium.java_client.flutter.commands; + +import io.appium.java_client.AppiumBy; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.openqa.selenium.WebElement; + +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +@Accessors(chain = true) +@Getter +@Setter +public class WaitOptions implements FlutterCommandOptions { + private WebElement element; + private AppiumBy.FlutterBy locator; + private Duration timeout; + + @Override + public Map toJson() { + Map args = new HashMap<>(); + args.put("element", element); + if (locator != null) { + args.put("locator", locator.toJson()); + } + if (timeout != null) { + args.put("timeout", timeout.getSeconds()); + } + return Collections.unmodifiableMap(args); + } +} \ No newline at end of file From d68dc13c826a275ff3fc709407d91a6f39a3f1fb Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Sat, 20 Jul 2024 16:54:06 +0530 Subject: [PATCH 03/16] checkStyle fixes --- .../java/io/appium/java_client/AppiumBy.java | 2 -- .../commands/CommonFlutterCommands.java | 1 - .../flutter/commands/DragAndDropOptions.java | 1 - .../commands/FlutterCommandOptions.java | 18 ------------------ .../flutter/commands/ScrollOptions.java | 2 +- 5 files changed, 1 insertion(+), 23 deletions(-) diff --git a/src/main/java/io/appium/java_client/AppiumBy.java b/src/main/java/io/appium/java_client/AppiumBy.java index 1fc9ca6ad..0ad042dcc 100644 --- a/src/main/java/io/appium/java_client/AppiumBy.java +++ b/src/main/java/io/appium/java_client/AppiumBy.java @@ -25,8 +25,6 @@ import org.openqa.selenium.WebElement; import java.io.Serializable; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/src/main/java/io/appium/java_client/flutter/commands/CommonFlutterCommands.java b/src/main/java/io/appium/java_client/flutter/commands/CommonFlutterCommands.java index a37a551cd..95c51c827 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/CommonFlutterCommands.java +++ b/src/main/java/io/appium/java_client/flutter/commands/CommonFlutterCommands.java @@ -2,7 +2,6 @@ import io.appium.java_client.flutter.FlutterDriver; import org.openqa.selenium.JavascriptExecutor; -import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; public class CommonFlutterCommands { diff --git a/src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java b/src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java index d6df695e3..eaeb63281 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java @@ -3,7 +3,6 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NonNull; -import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; import org.openqa.selenium.WebElement; diff --git a/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandOptions.java b/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandOptions.java index 7223e5972..c4c77885b 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandOptions.java @@ -1,25 +1,7 @@ package io.appium.java_client.flutter.commands; -import io.appium.java_client.AppiumBy; -import org.openqa.selenium.By; - import java.util.Map; public interface FlutterCommandOptions { - /** - * Parses an {@link AppiumBy.FlutterBy} object into a Map of parameters - * to be executed by flutter integration driver. - * - * @param by The {@link AppiumBy.FlutterBy} object to parse. - * @return A {@link Map} containing the parsed parameters - */ - default Map parseBy(AppiumBy.FlutterBy by) { - By.Remotable.Parameters parameters = by.getRemoteParameters(); - return Map.of( - "using", parameters.using(), - "value", parameters.value() - ); - } - Map toJson(); } diff --git a/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java b/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java index 701e11aa2..4292c1397 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java @@ -33,7 +33,7 @@ public ScrollOptions(AppiumBy.FlutterBy scrollTo, ScrollDirection scrollDirectio @Override public Map toJson() { return Map.of( - "finder", scrollTo != null ? parseBy(scrollTo) : null, + "finder", scrollTo != null ? scrollTo.toJson() : null, "scrollView", scrollView, "delta", delta, "maxScrolls", maxScrolls, From ccb7110f743fe6f144fc9e0cffba3fa3efb90761 Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Sat, 20 Jul 2024 17:02:06 +0530 Subject: [PATCH 04/16] Minor tweaks --- .../java_client/flutter/FlutterAndroidDriver.java | 2 +- .../java_client/flutter/FlutterIOSDriver.java | 2 +- .../flutter/commands/DoubleClickOptions.java | 2 +- .../flutter/commands/DragAndDropOptions.java | 2 +- .../flutter/commands/LongPressOptions.java | 2 +- .../flutter/commands/ScrollOptions.java | 15 +++++++++++---- .../java_client/flutter/commands/WaitOptions.java | 2 +- 7 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java b/src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java index 3e6900205..d1b3f04d1 100644 --- a/src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java +++ b/src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java @@ -131,4 +131,4 @@ public void performLongPress(LongPressOptions option) { public void performDragAndDrop(DragAndDropOptions option) { CommonFlutterCommands.performDragAndDrop(this, option); } -} \ No newline at end of file +} diff --git a/src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java b/src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java index 250415713..d76d81bbc 100644 --- a/src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java +++ b/src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java @@ -130,4 +130,4 @@ public void performLongPress(LongPressOptions option) { public void performDragAndDrop(DragAndDropOptions option) { CommonFlutterCommands.performDragAndDrop(this, option); } -} \ No newline at end of file +} diff --git a/src/main/java/io/appium/java_client/flutter/commands/DoubleClickOptions.java b/src/main/java/io/appium/java_client/flutter/commands/DoubleClickOptions.java index 311b6b05d..809e831f1 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/DoubleClickOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/DoubleClickOptions.java @@ -2,4 +2,4 @@ public class DoubleClickOptions extends FlutterGestureOptions { -} \ No newline at end of file +} diff --git a/src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java b/src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java index eaeb63281..aecd55238 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java @@ -24,4 +24,4 @@ private DragAndDropOptions() { public Map toJson() { return Map.of("source", source, "target", target); } -} \ No newline at end of file +} diff --git a/src/main/java/io/appium/java_client/flutter/commands/LongPressOptions.java b/src/main/java/io/appium/java_client/flutter/commands/LongPressOptions.java index ab7cf08da..798054fa9 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/LongPressOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/LongPressOptions.java @@ -10,4 +10,4 @@ @Getter public class LongPressOptions extends FlutterGestureOptions { -} \ No newline at end of file +} diff --git a/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java b/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java index 4292c1397..1667d721a 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java @@ -1,7 +1,9 @@ package io.appium.java_client.flutter.commands; +import com.google.common.base.Preconditions; import io.appium.java_client.AppiumBy; import lombok.Getter; +import lombok.NonNull; import lombok.Setter; import lombok.experimental.Accessors; import org.openqa.selenium.WebElement; @@ -10,6 +12,8 @@ import java.util.Map; import java.util.Optional; +import static com.google.common.base.Strings.isNullOrEmpty; + @Accessors(chain = true) @Getter @Setter @@ -26,6 +30,7 @@ private ScrollOptions() { } public ScrollOptions(AppiumBy.FlutterBy scrollTo, ScrollDirection scrollDirection) { + Preconditions.checkArgument(scrollTo != null, "Must supply a valid locator for scrollTo"); this.scrollTo = scrollTo; this.scrollDirection = scrollDirection; } @@ -33,13 +38,15 @@ public ScrollOptions(AppiumBy.FlutterBy scrollTo, ScrollDirection scrollDirectio @Override public Map toJson() { return Map.of( - "finder", scrollTo != null ? scrollTo.toJson() : null, + "finder", scrollTo.toJson(), "scrollView", scrollView, "delta", delta, "maxScrolls", maxScrolls, "settleBetweenScrollsTimeout", settleBetweenScrollsTimeout, - "scrollDirection", Optional.ofNullable(scrollDirection).orElse(ScrollDirection.UP).getDirection(), - "dragDuration", Optional.ofNullable(dragDuration).orElse(Duration.ZERO).getSeconds() + "scrollDirection", Optional.ofNullable(scrollDirection) + .orElse(ScrollDirection.UP).getDirection(), + "dragDuration", Optional.ofNullable(dragDuration) + .orElse(Duration.ZERO).getSeconds() ); } @@ -56,4 +63,4 @@ public static enum ScrollDirection { this.direction = direction; } } -} \ No newline at end of file +} diff --git a/src/main/java/io/appium/java_client/flutter/commands/WaitOptions.java b/src/main/java/io/appium/java_client/flutter/commands/WaitOptions.java index 327b529d4..a3aaa3cd5 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/WaitOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/WaitOptions.java @@ -31,4 +31,4 @@ public Map toJson() { } return Collections.unmodifiableMap(args); } -} \ No newline at end of file +} From 61cb9e601cf9f921a3c9601582d09b774aab245f Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Sat, 20 Jul 2024 17:04:07 +0530 Subject: [PATCH 05/16] improvements --- .../java_client/flutter/commands/FlutterGestureOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureOptions.java b/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureOptions.java index 52be2a390..b4ea5205b 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureOptions.java @@ -22,7 +22,7 @@ public Map toJson() { Map args = new HashMap<>(); args.put("origin", element); if (point != null) { - args.put("offset", Map.of("x", point.x, "y", point.y)); + args.put("offset", Map.of("x", point.getX(), "y", point.getY())); } return Collections.unmodifiableMap(args); } From 434689664d97846c5527b90b9b8c4bf14b835db8 Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Sat, 20 Jul 2024 22:05:25 +0530 Subject: [PATCH 06/16] Minor improvements --- .../flutter/FlutterAndroidDriver.java | 66 +------------------ .../java_client/flutter/FlutterDriver.java | 59 +++++++++++++++-- .../java_client/flutter/FlutterIOSDriver.java | 65 +----------------- ...tterCommands.java => FlutterCommands.java} | 20 +++--- .../flutter/commands/ScrollOptions.java | 10 ++- 5 files changed, 76 insertions(+), 144 deletions(-) rename src/main/java/io/appium/java_client/flutter/commands/{CommonFlutterCommands.java => FlutterCommands.java} (80%) diff --git a/src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java b/src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java index d1b3f04d1..fc6a07b03 100644 --- a/src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java +++ b/src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java @@ -2,16 +2,11 @@ import io.appium.java_client.AppiumClientConfig; import io.appium.java_client.android.AndroidDriver; -import io.appium.java_client.flutter.commands.CommonFlutterCommands; -import io.appium.java_client.flutter.commands.DoubleClickOptions; -import io.appium.java_client.flutter.commands.DragAndDropOptions; -import io.appium.java_client.flutter.commands.LongPressOptions; -import io.appium.java_client.flutter.commands.ScrollOptions; -import io.appium.java_client.flutter.commands.WaitOptions; import io.appium.java_client.service.local.AppiumDriverLocalService; import io.appium.java_client.service.local.AppiumServiceBuilder; import org.openqa.selenium.Capabilities; import org.openqa.selenium.remote.HttpCommandExecutor; +import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.remote.http.ClientConfig; import org.openqa.selenium.remote.http.HttpClient; @@ -72,63 +67,8 @@ public FlutterAndroidDriver(URL remoteSessionAddress, String automationName) { super(remoteSessionAddress, automationName); } - /** - * Waits for an element to become visible on the Flutter application. - * - * @param option WaitOptions specifying the element and optional timeout. - */ @Override - public void waitForVisible(WaitOptions option) { - CommonFlutterCommands.waitForVisible(this, option); - } - - /** - * Waits for an element to become invisible on the Flutter application. - * - * @param option WaitOptions specifying the element and optional timeout. - */ - @Override - public void waitForInVisible(WaitOptions option) { - CommonFlutterCommands.waitForInVisible(this, option); - } - - /** - * Scrolls to make an element visible on the Flutter application. - * - * @param option ScrollOptions specifying the finder and scroll direction. - */ - @Override - public void scrollTillVisible(ScrollOptions option) { - CommonFlutterCommands.scrollTillVisible(this, option); - } - - /** - * Performs a double click on an element in the Flutter application. - * - * @param option DoubleClickOptions specifying the element to double-click. - */ - @Override - public void performDoubleClick(DoubleClickOptions option) { - CommonFlutterCommands.performDoubleClick(this, option); - } - - /** - * Performs a long press on an element in the Flutter application. - * - * @param option LongPressOptions specifying the element to long-press. - */ - @Override - public void performLongPress(LongPressOptions option) { - CommonFlutterCommands.performLongPress(this, option); - } - - /** - * Performs a drag-and-drop operation between two elements in the Flutter application. - * - * @param option DragAndDropOptions specifying the source and target elements for the drag-and-drop operation. - */ - @Override - public void performDragAndDrop(DragAndDropOptions option) { - CommonFlutterCommands.performDragAndDrop(this, option); + public RemoteWebDriver getDriver() { + return this; } } diff --git a/src/main/java/io/appium/java_client/flutter/FlutterDriver.java b/src/main/java/io/appium/java_client/flutter/FlutterDriver.java index 999839014..c3e1751b4 100644 --- a/src/main/java/io/appium/java_client/flutter/FlutterDriver.java +++ b/src/main/java/io/appium/java_client/flutter/FlutterDriver.java @@ -3,20 +3,67 @@ import io.appium.java_client.flutter.commands.DoubleClickOptions; import io.appium.java_client.flutter.commands.DragAndDropOptions; +import io.appium.java_client.flutter.commands.FlutterCommands; import io.appium.java_client.flutter.commands.LongPressOptions; import io.appium.java_client.flutter.commands.ScrollOptions; import io.appium.java_client.flutter.commands.WaitOptions; +import org.openqa.selenium.remote.RemoteWebDriver; public interface FlutterDriver { - void waitForVisible(WaitOptions option); - void waitForInVisible(WaitOptions option); + /** + * Waits for an element to become visible on the Flutter application. + * + * @param option WaitOptions specifying the element and optional timeout. + */ + default void waitForVisible(WaitOptions option) { + FlutterCommands.waitForVisible(this.getDriver(), option); + } - void scrollTillVisible(ScrollOptions option); + /** + * Waits for an element to become invisible on the Flutter application. + * + * @param option WaitOptions specifying the element and optional timeout. + */ + default void waitForInVisible(WaitOptions option) { + FlutterCommands.waitForInVisible(this.getDriver(), option); + } - void performDoubleClick(DoubleClickOptions option); + /** + * Scrolls to make an element visible on the Flutter application. + * + * @param option ScrollOptions specifying the finder and scroll direction. + */ + default void scrollTillVisible(ScrollOptions option) { + FlutterCommands.scrollTillVisible(this.getDriver(), option); + } - void performLongPress(LongPressOptions option); + /** + * Performs a double click on an element in the Flutter application. + * + * @param option DoubleClickOptions specifying the element to double-click. + */ + default void performDoubleClick(DoubleClickOptions option) { + FlutterCommands.performDoubleClick(this.getDriver(), option); + } - void performDragAndDrop(DragAndDropOptions option); + /** + * Performs a long press on an element in the Flutter application. + * + * @param option LongPressOptions specifying the element to long-press. + */ + default void performLongPress(LongPressOptions option) { + FlutterCommands.performLongPress(this.getDriver(), option); + } + + /** + * Performs a drag-and-drop operation between two elements in the Flutter application. + * + * @param option DragAndDropOptions specifying the source and target elements for the drag-and-drop operation. + */ + default void performDragAndDrop(DragAndDropOptions option) { + FlutterCommands.performDragAndDrop(this.getDriver(), option); + } + + RemoteWebDriver getDriver(); } diff --git a/src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java b/src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java index d76d81bbc..021900dd5 100644 --- a/src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java +++ b/src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java @@ -1,17 +1,12 @@ package io.appium.java_client.flutter; import io.appium.java_client.AppiumClientConfig; -import io.appium.java_client.flutter.commands.CommonFlutterCommands; -import io.appium.java_client.flutter.commands.DoubleClickOptions; -import io.appium.java_client.flutter.commands.DragAndDropOptions; -import io.appium.java_client.flutter.commands.LongPressOptions; -import io.appium.java_client.flutter.commands.ScrollOptions; -import io.appium.java_client.flutter.commands.WaitOptions; import io.appium.java_client.ios.IOSDriver; import io.appium.java_client.service.local.AppiumDriverLocalService; import io.appium.java_client.service.local.AppiumServiceBuilder; import org.openqa.selenium.Capabilities; import org.openqa.selenium.remote.HttpCommandExecutor; +import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.remote.http.ClientConfig; import org.openqa.selenium.remote.http.HttpClient; @@ -72,62 +67,8 @@ public FlutterIOSDriver(Capabilities capabilities) { super(capabilities); } - /** - * Waits for an element to become visible on the Flutter application. - * - * @param option WaitOptions specifying the element and optional timeout. - */ - public void waitForVisible(WaitOptions option) { - CommonFlutterCommands.waitForVisible(this, option); - } - - /** - * Waits for an element to become invisible on the Flutter application. - * - * @param option WaitOptions specifying the element and optional timeout. - */ - @Override - public void waitForInVisible(WaitOptions option) { - CommonFlutterCommands.waitForInVisible(this, option); - } - - /** - * Scrolls to make an element visible on the Flutter application. - * - * @param option ScrollOptions specifying the finder and scroll direction. - */ - @Override - public void scrollTillVisible(ScrollOptions option) { - CommonFlutterCommands.scrollTillVisible(this, option); - } - - /** - * Performs a double click on an element in the Flutter application. - * - * @param option DoubleClickOptions specifying the element to double-click. - */ - @Override - public void performDoubleClick(DoubleClickOptions option) { - CommonFlutterCommands.performDoubleClick(this, option); - } - - /** - * Performs a long press on an element in the Flutter application. - * - * @param option LongPressOptions specifying the element to long-press. - */ - @Override - public void performLongPress(LongPressOptions option) { - CommonFlutterCommands.performLongPress(this, option); - } - - /** - * Performs a drag-and-drop operation between two elements in the Flutter application. - * - * @param option DragAndDropOptions specifying the source and target elements for the drag-and-drop operation. - */ @Override - public void performDragAndDrop(DragAndDropOptions option) { - CommonFlutterCommands.performDragAndDrop(this, option); + public RemoteWebDriver getDriver() { + return this; } } diff --git a/src/main/java/io/appium/java_client/flutter/commands/CommonFlutterCommands.java b/src/main/java/io/appium/java_client/flutter/commands/FlutterCommands.java similarity index 80% rename from src/main/java/io/appium/java_client/flutter/commands/CommonFlutterCommands.java rename to src/main/java/io/appium/java_client/flutter/commands/FlutterCommands.java index 95c51c827..15707a8e7 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/CommonFlutterCommands.java +++ b/src/main/java/io/appium/java_client/flutter/commands/FlutterCommands.java @@ -1,10 +1,10 @@ package io.appium.java_client.flutter.commands; -import io.appium.java_client.flutter.FlutterDriver; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.RemoteWebDriver; -public class CommonFlutterCommands { +public class FlutterCommands { private static final String FLUTTER_COMMAND_PREFIX = "flutter"; private static final String WAIT_FOR_VISIBLE_COMMAND = "waitForVisible"; @@ -14,7 +14,7 @@ public class CommonFlutterCommands { private static final String LONG_PRESS = "longPress"; private static final String DRAG_AND_DROP = "dragAndDrop"; - private CommonFlutterCommands() { + private FlutterCommands() { } /** @@ -23,7 +23,7 @@ private CommonFlutterCommands() { * @param driver WebDriver instance to execute the command on. * @param option WaitOptions specifying the element and optional timeout. */ - public static void waitForVisible(FlutterDriver driver, WaitOptions option) { + public static void waitForVisible(RemoteWebDriver driver, WaitOptions option) { executeScript(driver, WAIT_FOR_VISIBLE_COMMAND, option); } @@ -33,7 +33,7 @@ public static void waitForVisible(FlutterDriver driver, WaitOptions option) { * @param driver WebDriver instance to execute the command on. * @param option WaitOptions specifying the element and optional timeout. */ - public static void waitForInVisible(FlutterDriver driver, WaitOptions option) { + public static void waitForInVisible(RemoteWebDriver driver, WaitOptions option) { executeScript(driver, WAIT_FOR_INVISIBLE_COMMAND, option); } @@ -44,7 +44,7 @@ public static void waitForInVisible(FlutterDriver driver, WaitOptions option) { * @param option ScrollOptions specifying the finder and scroll direction. * @return WebElement that was scrolled to and made visible. */ - public static WebElement scrollTillVisible(FlutterDriver driver, ScrollOptions option) { + public static WebElement scrollTillVisible(RemoteWebDriver driver, ScrollOptions option) { return (WebElement) executeScript(driver, SCROLL_TILL_VISIBLE, option); } @@ -54,7 +54,7 @@ public static WebElement scrollTillVisible(FlutterDriver driver, ScrollOptions o * @param driver WebDriver instance to execute the command on. * @param option DoubleClickOptions specifying the element to double-click. */ - public static void performDoubleClick(FlutterDriver driver, DoubleClickOptions option) { + public static void performDoubleClick(RemoteWebDriver driver, DoubleClickOptions option) { executeScript(driver, DOUBLE_CLICK, option); } @@ -64,7 +64,7 @@ public static void performDoubleClick(FlutterDriver driver, DoubleClickOptions o * @param driver WebDriver instance to execute the command on. * @param option LongPressOptions specifying the element to long-press. */ - public static void performLongPress(FlutterDriver driver, LongPressOptions option) { + public static void performLongPress(RemoteWebDriver driver, LongPressOptions option) { executeScript(driver, LONG_PRESS, option); } @@ -74,7 +74,7 @@ public static void performLongPress(FlutterDriver driver, LongPressOptions optio * @param driver WebDriver instance to execute the command on. * @param option DragAndDropOptions specifying the source and target elements for the drag-and-drop operation. */ - public static void performDragAndDrop(FlutterDriver driver, DragAndDropOptions option) { + public static void performDragAndDrop(RemoteWebDriver driver, DragAndDropOptions option) { executeScript(driver, DRAG_AND_DROP, option); } @@ -86,7 +86,7 @@ public static void performDragAndDrop(FlutterDriver driver, DragAndDropOptions o * @param args FlutterCommandOptions containing the command arguments. * @return Object returned by the JavaScript execution. */ - public static Object executeScript(FlutterDriver driver, String scriptName, FlutterCommandOptions args) { + public static Object executeScript(RemoteWebDriver driver, String scriptName, FlutterCommandOptions args) { String commandName = String.format("%s: %s", FLUTTER_COMMAND_PREFIX, scriptName); return ((JavascriptExecutor) driver).executeScript(commandName, args.toJson()); } diff --git a/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java b/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java index 1667d721a..1dadc9c06 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java @@ -3,7 +3,6 @@ import com.google.common.base.Preconditions; import io.appium.java_client.AppiumBy; import lombok.Getter; -import lombok.NonNull; import lombok.Setter; import lombok.experimental.Accessors; import org.openqa.selenium.WebElement; @@ -12,8 +11,6 @@ import java.util.Map; import java.util.Optional; -import static com.google.common.base.Strings.isNullOrEmpty; - @Accessors(chain = true) @Getter @Setter @@ -29,6 +26,13 @@ public class ScrollOptions implements FlutterCommandOptions { private ScrollOptions() { } + /** + * Constructs a new ScrollOptions object with the given parameters. + * + * @param scrollTo the locator used for scrolling to a specific element + * @param scrollDirection the direction in which to scroll (e.g., ScrollDirection.DOWN) + * @throws IllegalArgumentException if scrollTo is null + */ public ScrollOptions(AppiumBy.FlutterBy scrollTo, ScrollDirection scrollDirection) { Preconditions.checkArgument(scrollTo != null, "Must supply a valid locator for scrollTo"); this.scrollTo = scrollTo; From cffadc9442b0a0f829f45460cfc5502c09e33efe Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Sat, 20 Jul 2024 22:15:19 +0530 Subject: [PATCH 07/16] Add flutteroptions --- .../{ => android}/FlutterAndroidDriver.java | 3 +- .../flutter/{ => ios}/FlutterIOSDriver.java | 3 +- .../flutter/ios/FlutterIOSOptions.java | 29 +++++++++++++++++++ .../java_client/remote/AutomationName.java | 2 ++ 4 files changed, 35 insertions(+), 2 deletions(-) rename src/main/java/io/appium/java_client/flutter/{ => android}/FlutterAndroidDriver.java (96%) rename src/main/java/io/appium/java_client/flutter/{ => ios}/FlutterIOSDriver.java (96%) create mode 100644 src/main/java/io/appium/java_client/flutter/ios/FlutterIOSOptions.java diff --git a/src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java b/src/main/java/io/appium/java_client/flutter/android/FlutterAndroidDriver.java similarity index 96% rename from src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java rename to src/main/java/io/appium/java_client/flutter/android/FlutterAndroidDriver.java index fc6a07b03..16cfea62d 100644 --- a/src/main/java/io/appium/java_client/flutter/FlutterAndroidDriver.java +++ b/src/main/java/io/appium/java_client/flutter/android/FlutterAndroidDriver.java @@ -1,7 +1,8 @@ -package io.appium.java_client.flutter; +package io.appium.java_client.flutter.android; import io.appium.java_client.AppiumClientConfig; import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.flutter.FlutterDriver; import io.appium.java_client.service.local.AppiumDriverLocalService; import io.appium.java_client.service.local.AppiumServiceBuilder; import org.openqa.selenium.Capabilities; diff --git a/src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java b/src/main/java/io/appium/java_client/flutter/ios/FlutterIOSDriver.java similarity index 96% rename from src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java rename to src/main/java/io/appium/java_client/flutter/ios/FlutterIOSDriver.java index 021900dd5..f79fc2970 100644 --- a/src/main/java/io/appium/java_client/flutter/FlutterIOSDriver.java +++ b/src/main/java/io/appium/java_client/flutter/ios/FlutterIOSDriver.java @@ -1,6 +1,7 @@ -package io.appium.java_client.flutter; +package io.appium.java_client.flutter.ios; import io.appium.java_client.AppiumClientConfig; +import io.appium.java_client.flutter.FlutterDriver; import io.appium.java_client.ios.IOSDriver; import io.appium.java_client.service.local.AppiumDriverLocalService; import io.appium.java_client.service.local.AppiumServiceBuilder; diff --git a/src/main/java/io/appium/java_client/flutter/ios/FlutterIOSOptions.java b/src/main/java/io/appium/java_client/flutter/ios/FlutterIOSOptions.java new file mode 100644 index 000000000..9d1ec9817 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/ios/FlutterIOSOptions.java @@ -0,0 +1,29 @@ +package io.appium.java_client.flutter.ios; + +import io.appium.java_client.ios.options.XCUITestOptions; +import io.appium.java_client.remote.AutomationName; +import org.openqa.selenium.Capabilities; + +import java.util.Map; + +public class FlutterIOSOptions extends XCUITestOptions { + + public FlutterIOSOptions() { + super(); + addDefaultOptions(); + } + + public FlutterIOSOptions(Capabilities source) { + super(source); + addDefaultOptions(); + } + + public FlutterIOSOptions(Map source) { + super(source); + addDefaultOptions(); + } + + private void addDefaultOptions() { + setAutomationName(AutomationName.FLUTTER_INTEGRATION); + } +} diff --git a/src/main/java/io/appium/java_client/remote/AutomationName.java b/src/main/java/io/appium/java_client/remote/AutomationName.java index 7df904c4d..e941d516b 100644 --- a/src/main/java/io/appium/java_client/remote/AutomationName.java +++ b/src/main/java/io/appium/java_client/remote/AutomationName.java @@ -38,4 +38,6 @@ public interface AutomationName { // Third-party drivers // https://github.com/YOU-i-Labs/appium-youiengine-driver String YOUI_ENGINE = "youiengine"; + //https://github.com/AppiumTestDistribution/appium-flutter-integration-driver + String FLUTTER_INTEGRATION = "FlutterIntegration"; } From 5d03cbca013ba3fdff0e63ff89f9ed406887582c Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Sun, 21 Jul 2024 12:20:32 +0530 Subject: [PATCH 08/16] Refactor parameters --- .../java_client/flutter/FlutterDriver.java | 87 ++++++++++------- .../flutter/commands/DoubleClickOptions.java | 5 - .../commands/DoubleClickParameter.java | 12 +++ ...Options.java => DragAndDropParameter.java} | 4 +- .../commands/FlutterCommandOptions.java | 7 -- .../commands/FlutterCommandParameter.java | 25 +++++ .../flutter/commands/FlutterCommands.java | 93 ------------------- ...ions.java => FlutterGestureParameter.java} | 4 +- ...ssOptions.java => LongPressParameter.java} | 2 +- ...crollOptions.java => ScrollParameter.java} | 8 +- .../{WaitOptions.java => WaitParameter.java} | 4 +- 11 files changed, 103 insertions(+), 148 deletions(-) delete mode 100644 src/main/java/io/appium/java_client/flutter/commands/DoubleClickOptions.java create mode 100644 src/main/java/io/appium/java_client/flutter/commands/DoubleClickParameter.java rename src/main/java/io/appium/java_client/flutter/commands/{DragAndDropOptions.java => DragAndDropParameter.java} (82%) delete mode 100644 src/main/java/io/appium/java_client/flutter/commands/FlutterCommandOptions.java create mode 100644 src/main/java/io/appium/java_client/flutter/commands/FlutterCommandParameter.java delete mode 100644 src/main/java/io/appium/java_client/flutter/commands/FlutterCommands.java rename src/main/java/io/appium/java_client/flutter/commands/{FlutterGestureOptions.java => FlutterGestureParameter.java} (90%) rename src/main/java/io/appium/java_client/flutter/commands/{LongPressOptions.java => LongPressParameter.java} (72%) rename src/main/java/io/appium/java_client/flutter/commands/{ScrollOptions.java => ScrollParameter.java} (88%) rename src/main/java/io/appium/java_client/flutter/commands/{WaitOptions.java => WaitParameter.java} (86%) diff --git a/src/main/java/io/appium/java_client/flutter/FlutterDriver.java b/src/main/java/io/appium/java_client/flutter/FlutterDriver.java index c3e1751b4..99266ba38 100644 --- a/src/main/java/io/appium/java_client/flutter/FlutterDriver.java +++ b/src/main/java/io/appium/java_client/flutter/FlutterDriver.java @@ -1,69 +1,92 @@ package io.appium.java_client.flutter; - -import io.appium.java_client.flutter.commands.DoubleClickOptions; -import io.appium.java_client.flutter.commands.DragAndDropOptions; -import io.appium.java_client.flutter.commands.FlutterCommands; -import io.appium.java_client.flutter.commands.LongPressOptions; -import io.appium.java_client.flutter.commands.ScrollOptions; -import io.appium.java_client.flutter.commands.WaitOptions; +import io.appium.java_client.flutter.commands.DoubleClickParameter; +import io.appium.java_client.flutter.commands.DragAndDropParameter; +import io.appium.java_client.flutter.commands.FlutterCommandParameter; +import io.appium.java_client.flutter.commands.LongPressParameter; +import io.appium.java_client.flutter.commands.ScrollParameter; +import io.appium.java_client.flutter.commands.WaitParameter; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.RemoteWebDriver; +/** + * Interface representing a Flutter driver that extends {@link RemoteWebDriver}. + * Provides convenience methods for executing Flutter-specific commands using JavaScriptExecutor. + */ public interface FlutterDriver { /** - * Waits for an element to become visible on the Flutter application. + * Waits for an element to become visible on the screen. * - * @param option WaitOptions specifying the element and optional timeout. + * @param parameter The parameters for waiting, specifying timeout and element details. */ - default void waitForVisible(WaitOptions option) { - FlutterCommands.waitForVisible(this.getDriver(), option); + default void waitForVisible(WaitParameter parameter) { + executeScript("waitForVisible", parameter); } /** - * Waits for an element to become invisible on the Flutter application. + * Waits for an element to become invisible on the screen. * - * @param option WaitOptions specifying the element and optional timeout. + * @param parameter The parameters for waiting, specifying timeout and element details. */ - default void waitForInVisible(WaitOptions option) { - FlutterCommands.waitForInVisible(this.getDriver(), option); + default void waitForInVisible(WaitParameter parameter) { + executeScript("waitForAbsent", parameter); } /** - * Scrolls to make an element visible on the Flutter application. + * Scrolls to make an element visible on the screen. * - * @param option ScrollOptions specifying the finder and scroll direction. + * @param parameter The parameters for scrolling, specifying element details. + * @return The WebElement that was scrolled to. */ - default void scrollTillVisible(ScrollOptions option) { - FlutterCommands.scrollTillVisible(this.getDriver(), option); + default WebElement scrollTillVisible(ScrollParameter parameter) { + return (WebElement) executeScript("scrollTillVisible", parameter); } /** - * Performs a double click on an element in the Flutter application. + * Performs a double-click action on an element. * - * @param option DoubleClickOptions specifying the element to double-click. + * @param parameter The parameters for double-clicking, specifying element details. */ - default void performDoubleClick(DoubleClickOptions option) { - FlutterCommands.performDoubleClick(this.getDriver(), option); + default void performDoubleClick(DoubleClickParameter parameter) { + executeScript("doubleClick", parameter); } /** - * Performs a long press on an element in the Flutter application. + * Performs a long press action on an element. * - * @param option LongPressOptions specifying the element to long-press. + * @param parameter The parameters for long pressing, specifying element details. */ - default void performLongPress(LongPressOptions option) { - FlutterCommands.performLongPress(this.getDriver(), option); + default void performLongPress(LongPressParameter parameter) { + executeScript("longPress", parameter); } /** - * Performs a drag-and-drop operation between two elements in the Flutter application. + * Performs a drag-and-drop action between two elements. * - * @param option DragAndDropOptions specifying the source and target elements for the drag-and-drop operation. + * @param parameter The parameters for drag-and-drop, specifying source and target elements. */ - default void performDragAndDrop(DragAndDropOptions option) { - FlutterCommands.performDragAndDrop(this.getDriver(), option); + default void performDragAndDrop(DragAndDropParameter parameter) { + executeScript("dragAndDrop", parameter); } + /** + * Executes a Flutter-specific script using JavascriptExecutor. + * + * @param scriptName The name of the Flutter script to execute. + * @param parameter The parameters for the Flutter command. + * @return The result of executing the script. + */ + private Object executeScript(String scriptName, FlutterCommandParameter parameter) { + String commandName = String.format("flutter: %s", scriptName); + return ((JavascriptExecutor) this.getDriver()).executeScript(commandName, parameter.toJson()); + } + + /** + * Retrieves the underlying RemoteWebDriver instance associated with this FlutterDriver. + * + * @return The RemoteWebDriver instance used by this FlutterDriver. + */ RemoteWebDriver getDriver(); -} +} \ No newline at end of file diff --git a/src/main/java/io/appium/java_client/flutter/commands/DoubleClickOptions.java b/src/main/java/io/appium/java_client/flutter/commands/DoubleClickOptions.java deleted file mode 100644 index 809e831f1..000000000 --- a/src/main/java/io/appium/java_client/flutter/commands/DoubleClickOptions.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.appium.java_client.flutter.commands; - -public class DoubleClickOptions extends FlutterGestureOptions { - -} diff --git a/src/main/java/io/appium/java_client/flutter/commands/DoubleClickParameter.java b/src/main/java/io/appium/java_client/flutter/commands/DoubleClickParameter.java new file mode 100644 index 000000000..3eee83876 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/commands/DoubleClickParameter.java @@ -0,0 +1,12 @@ +package io.appium.java_client.flutter.commands; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +@Accessors(chain = true) +@Setter +@Getter +public class DoubleClickParameter extends FlutterGestureParameter { + +} diff --git a/src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java b/src/main/java/io/appium/java_client/flutter/commands/DragAndDropParameter.java similarity index 82% rename from src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java rename to src/main/java/io/appium/java_client/flutter/commands/DragAndDropParameter.java index aecd55238..78689b25e 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/DragAndDropOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/DragAndDropParameter.java @@ -11,13 +11,13 @@ @Accessors(chain = true) @Getter @AllArgsConstructor -public class DragAndDropOptions implements FlutterCommandOptions { +public class DragAndDropParameter extends FlutterCommandParameter { @NonNull WebElement source; @NonNull WebElement target; - private DragAndDropOptions() { + private DragAndDropParameter() { } @Override diff --git a/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandOptions.java b/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandOptions.java deleted file mode 100644 index c4c77885b..000000000 --- a/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandOptions.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.appium.java_client.flutter.commands; - -import java.util.Map; - -public interface FlutterCommandOptions { - Map toJson(); -} diff --git a/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandParameter.java b/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandParameter.java new file mode 100644 index 000000000..befe2b310 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandParameter.java @@ -0,0 +1,25 @@ +package io.appium.java_client.flutter.commands; + +import io.appium.java_client.AppiumBy; +import org.openqa.selenium.By; + +import java.util.Map; + +public abstract class FlutterCommandParameter { + + /** + * Parses an Appium Flutter locator into a Map representation suitable for Flutter Integration Driver. + * + * @param by The FlutterBy instance representing the locator to parse. + * @return A Map containing the parsed locator information with keys using and value. + */ + Map parseFlutterLocator(AppiumBy.FlutterBy by) { + By.Remotable.Parameters parameters = by.getRemoteParameters(); + return Map.of( + "using", parameters.using(), + "value", parameters.value() + ); + } + + public abstract Map toJson(); +} diff --git a/src/main/java/io/appium/java_client/flutter/commands/FlutterCommands.java b/src/main/java/io/appium/java_client/flutter/commands/FlutterCommands.java deleted file mode 100644 index 15707a8e7..000000000 --- a/src/main/java/io/appium/java_client/flutter/commands/FlutterCommands.java +++ /dev/null @@ -1,93 +0,0 @@ -package io.appium.java_client.flutter.commands; - -import org.openqa.selenium.JavascriptExecutor; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; - -public class FlutterCommands { - - private static final String FLUTTER_COMMAND_PREFIX = "flutter"; - private static final String WAIT_FOR_VISIBLE_COMMAND = "waitForVisible"; - private static final String WAIT_FOR_INVISIBLE_COMMAND = "waitForAbsent"; - private static final String SCROLL_TILL_VISIBLE = "scrollTillVisible"; - private static final String DOUBLE_CLICK = "doubleClick"; - private static final String LONG_PRESS = "longPress"; - private static final String DRAG_AND_DROP = "dragAndDrop"; - - private FlutterCommands() { - } - - /** - * Waits for an element to become visible on the Flutter application. - * - * @param driver WebDriver instance to execute the command on. - * @param option WaitOptions specifying the element and optional timeout. - */ - public static void waitForVisible(RemoteWebDriver driver, WaitOptions option) { - executeScript(driver, WAIT_FOR_VISIBLE_COMMAND, option); - } - - /** - * Waits for an element to become invisible on the Flutter application. - * - * @param driver WebDriver instance to execute the command on. - * @param option WaitOptions specifying the element and optional timeout. - */ - public static void waitForInVisible(RemoteWebDriver driver, WaitOptions option) { - executeScript(driver, WAIT_FOR_INVISIBLE_COMMAND, option); - } - - /** - * Scrolls to make an element visible on the Flutter application. - * - * @param driver WebDriver instance to execute the command on. - * @param option ScrollOptions specifying the finder and scroll direction. - * @return WebElement that was scrolled to and made visible. - */ - public static WebElement scrollTillVisible(RemoteWebDriver driver, ScrollOptions option) { - return (WebElement) executeScript(driver, SCROLL_TILL_VISIBLE, option); - } - - /** - * Performs a double click on an element in the Flutter application. - * - * @param driver WebDriver instance to execute the command on. - * @param option DoubleClickOptions specifying the element to double-click. - */ - public static void performDoubleClick(RemoteWebDriver driver, DoubleClickOptions option) { - executeScript(driver, DOUBLE_CLICK, option); - } - - /** - * Performs a long press on an element in the Flutter application. - * - * @param driver WebDriver instance to execute the command on. - * @param option LongPressOptions specifying the element to long-press. - */ - public static void performLongPress(RemoteWebDriver driver, LongPressOptions option) { - executeScript(driver, LONG_PRESS, option); - } - - /** - * Performs a drag-and-drop operation between two elements in the Flutter application. - * - * @param driver WebDriver instance to execute the command on. - * @param option DragAndDropOptions specifying the source and target elements for the drag-and-drop operation. - */ - public static void performDragAndDrop(RemoteWebDriver driver, DragAndDropOptions option) { - executeScript(driver, DRAG_AND_DROP, option); - } - - /** - * Executes a Flutter command using JavaScriptExecutor. - * - * @param driver WebDriver instance to execute the command on. - * @param scriptName Name of the Flutter command script. - * @param args FlutterCommandOptions containing the command arguments. - * @return Object returned by the JavaScript execution. - */ - public static Object executeScript(RemoteWebDriver driver, String scriptName, FlutterCommandOptions args) { - String commandName = String.format("%s: %s", FLUTTER_COMMAND_PREFIX, scriptName); - return ((JavascriptExecutor) driver).executeScript(commandName, args.toJson()); - } -} diff --git a/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureOptions.java b/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureParameter.java similarity index 90% rename from src/main/java/io/appium/java_client/flutter/commands/FlutterGestureOptions.java rename to src/main/java/io/appium/java_client/flutter/commands/FlutterGestureParameter.java index b4ea5205b..4120e96e0 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureParameter.java @@ -11,9 +11,9 @@ import java.util.Map; @Accessors(chain = true) -@Getter @Setter -public class FlutterGestureOptions implements FlutterCommandOptions { +@Getter +public class FlutterGestureParameter extends FlutterCommandParameter { WebElement element; Point point; diff --git a/src/main/java/io/appium/java_client/flutter/commands/LongPressOptions.java b/src/main/java/io/appium/java_client/flutter/commands/LongPressParameter.java similarity index 72% rename from src/main/java/io/appium/java_client/flutter/commands/LongPressOptions.java rename to src/main/java/io/appium/java_client/flutter/commands/LongPressParameter.java index 798054fa9..668b4c1ba 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/LongPressOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/LongPressParameter.java @@ -8,6 +8,6 @@ @Accessors(chain = true) @Setter @Getter -public class LongPressOptions extends FlutterGestureOptions { +public class LongPressParameter extends FlutterGestureParameter { } diff --git a/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java b/src/main/java/io/appium/java_client/flutter/commands/ScrollParameter.java similarity index 88% rename from src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java rename to src/main/java/io/appium/java_client/flutter/commands/ScrollParameter.java index 1dadc9c06..26fb8a360 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/ScrollOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/ScrollParameter.java @@ -14,7 +14,7 @@ @Accessors(chain = true) @Getter @Setter -public class ScrollOptions implements FlutterCommandOptions { +public class ScrollParameter extends FlutterCommandParameter { AppiumBy.FlutterBy scrollTo; WebElement scrollView; ScrollDirection scrollDirection; @@ -23,7 +23,7 @@ public class ScrollOptions implements FlutterCommandOptions { Integer settleBetweenScrollsTimeout; Duration dragDuration; - private ScrollOptions() { + private ScrollParameter() { } /** @@ -33,7 +33,7 @@ private ScrollOptions() { * @param scrollDirection the direction in which to scroll (e.g., ScrollDirection.DOWN) * @throws IllegalArgumentException if scrollTo is null */ - public ScrollOptions(AppiumBy.FlutterBy scrollTo, ScrollDirection scrollDirection) { + public ScrollParameter(AppiumBy.FlutterBy scrollTo, ScrollDirection scrollDirection) { Preconditions.checkArgument(scrollTo != null, "Must supply a valid locator for scrollTo"); this.scrollTo = scrollTo; this.scrollDirection = scrollDirection; @@ -42,7 +42,7 @@ public ScrollOptions(AppiumBy.FlutterBy scrollTo, ScrollDirection scrollDirectio @Override public Map toJson() { return Map.of( - "finder", scrollTo.toJson(), + "finder", parseFlutterLocator(scrollTo), "scrollView", scrollView, "delta", delta, "maxScrolls", maxScrolls, diff --git a/src/main/java/io/appium/java_client/flutter/commands/WaitOptions.java b/src/main/java/io/appium/java_client/flutter/commands/WaitParameter.java similarity index 86% rename from src/main/java/io/appium/java_client/flutter/commands/WaitOptions.java rename to src/main/java/io/appium/java_client/flutter/commands/WaitParameter.java index a3aaa3cd5..2e0941780 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/WaitOptions.java +++ b/src/main/java/io/appium/java_client/flutter/commands/WaitParameter.java @@ -14,7 +14,7 @@ @Accessors(chain = true) @Getter @Setter -public class WaitOptions implements FlutterCommandOptions { +public class WaitParameter extends FlutterCommandParameter { private WebElement element; private AppiumBy.FlutterBy locator; private Duration timeout; @@ -24,7 +24,7 @@ public Map toJson() { Map args = new HashMap<>(); args.put("element", element); if (locator != null) { - args.put("locator", locator.toJson()); + args.put("locator", parseFlutterLocator(locator)); } if (timeout != null) { args.put("timeout", timeout.getSeconds()); From 9c3b659610b4f69d6c7d8c2947f4d05b34b3e0e7 Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Sun, 21 Jul 2024 12:21:00 +0530 Subject: [PATCH 09/16] Add e2e tests for flutter driver --- .github/workflows/gradle.yml | 28 ++++--- build.gradle | 26 +++++- .../java_client/android/BaseFlutterTest.java | 82 +++++++++++++++++++ .../java_client/android/CommandTest.java | 62 ++++++++++++++ .../java_client/android/FinderTests.java | 55 +++++++++++++ .../java/io/appium/java_client/AppiumBy.java | 12 ++- .../java_client/flutter/FlutterDriver.java | 32 +------- .../commands/DoubleClickParameter.java | 12 --- .../commands/DragAndDropParameter.java | 27 ------ .../commands/FlutterCommandParameter.java | 2 +- .../commands/FlutterGestureParameter.java | 29 ------- .../flutter/commands/LongPressParameter.java | 13 --- .../flutter/commands/ScrollParameter.java | 36 +++++--- .../flutter/ios/FlutterIOSDriver.java | 75 ----------------- .../flutter/ios/FlutterIOSOptions.java | 29 ------- 15 files changed, 271 insertions(+), 249 deletions(-) create mode 100644 src/e2eFlutterTest/java/io/appium/java_client/android/BaseFlutterTest.java create mode 100644 src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java create mode 100644 src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java delete mode 100644 src/main/java/io/appium/java_client/flutter/commands/DoubleClickParameter.java delete mode 100644 src/main/java/io/appium/java_client/flutter/commands/DragAndDropParameter.java delete mode 100644 src/main/java/io/appium/java_client/flutter/commands/FlutterGestureParameter.java delete mode 100644 src/main/java/io/appium/java_client/flutter/commands/LongPressParameter.java delete mode 100644 src/main/java/io/appium/java_client/flutter/ios/FlutterIOSDriver.java delete mode 100644 src/main/java/io/appium/java_client/flutter/ios/FlutterIOSOptions.java diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index e8006e8df..94ee31463 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -26,6 +26,7 @@ env: XCODE_VERSION: "15.4" IOS_DEVICE_NAME: iPhone 15 IOS_PLATFORM_VERSION: "17.5" + FLUTTER_ANDROID_APP: "https://github.com/AppiumTestDistribution/appium-flutter-server/releases/latest/download/app-debug.apk" jobs: build: @@ -33,15 +34,15 @@ jobs: strategy: matrix: include: - - java: 11 - # Need to use specific (not `-latest`) version of macOS to be sure the required version of Xcode/simulator is available - platform: macos-14 - e2e-tests: ios - - java: 17 - platform: ubuntu-latest - e2e-tests: android - - java: 21 - platform: ubuntu-latest + - java: 11 + # Need to use specific (not `-latest`) version of macOS to be sure the required version of Xcode/simulator is available + platform: macos-14 + e2e-tests: ios + - java: 17 + platform: ubuntu-latest + e2e-tests: android + - java: 21 + platform: ubuntu-latest fail-fast: false runs-on: ${{ matrix.platform }} @@ -78,9 +79,11 @@ jobs: with: node-version: 'lts/*' - - name: Install Appium + - name: Install Appium & Flutter driver if: matrix.e2e-tests == 'android' || matrix.e2e-tests == 'ios' - run: npm install --location=global appium + run: | + npm install --location=global appium + appium driver install appium-flutter-integration-driver --source npm - name: Install UIA2 driver if: matrix.e2e-tests == 'android' @@ -89,7 +92,8 @@ jobs: if: matrix.e2e-tests == 'android' uses: reactivecircus/android-emulator-runner@v2 with: - script: ./gradlew e2eAndroidTest -PisCI -Pselenium.version=$latest_snapshot + script: ./gradlew e2eAndroidTest -PisCI -Pselenium.version=$latest_snapshot && + ./gradlew e2eFlutterTest -Pplatform="android" -Pselenium.version=$latest_snapshot -PisCI -PflutterApp=${{ env.FLUTTER_ANDROID_APP }} api-level: ${{ env.ANDROID_SDK_VERSION }} avd-name: ${{ env.ANDROID_EMU_NAME }} disable-spellchecker: true diff --git a/build.gradle b/build.gradle index 8de5f95be..ab03351f7 100644 --- a/build.gradle +++ b/build.gradle @@ -70,7 +70,7 @@ dependencies { } dependencyCheck { - failBuildOnCVSS=22 + failBuildOnCVSS = 22 } jacoco { @@ -185,7 +185,7 @@ wrapper { processResources { filter ReplaceTokens, tokens: [ - 'selenium.version': seleniumVersion, + 'selenium.version' : seleniumVersion, 'appiumClient.version': appiumClientVersion ] } @@ -290,5 +290,27 @@ testing { } } } + + e2eFlutterTest(JvmTestSuite) { + sources { + java { + srcDirs = ['src/e2eFlutterTest/java'] + } + } + dependencies { + implementation project() + implementation(sourceSets.test.output) + implementation('io.github.bonigarcia:webdrivermanager:5.9.1') { + exclude group: 'org.seleniumhq.selenium' + } + } + + targets.configureEach { + testTask.configure { + shouldRunAfter(test) + systemProperties project.properties.subMap(["platform", "flutterApp"]) + } + } + } } } diff --git a/src/e2eFlutterTest/java/io/appium/java_client/android/BaseFlutterTest.java b/src/e2eFlutterTest/java/io/appium/java_client/android/BaseFlutterTest.java new file mode 100644 index 000000000..9b58c9b27 --- /dev/null +++ b/src/e2eFlutterTest/java/io/appium/java_client/android/BaseFlutterTest.java @@ -0,0 +1,82 @@ +package io.appium.java_client.android; + +import io.appium.java_client.AppiumBy; +import io.appium.java_client.android.options.UiAutomator2Options; +import io.appium.java_client.flutter.android.FlutterAndroidDriver; +import io.appium.java_client.flutter.commands.ScrollParameter; +import io.appium.java_client.remote.AutomationName; +import io.appium.java_client.service.local.AppiumDriverLocalService; +import io.appium.java_client.service.local.AppiumServiceBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.openqa.selenium.By; +import org.openqa.selenium.InvalidArgumentException; +import org.openqa.selenium.WebElement; + +import java.net.MalformedURLException; +import java.util.Optional; + +public class BaseFlutterTest { + + protected static final boolean IS_ANDROID = Optional + .ofNullable(System.getProperty("platform")) + .orElse("android") + .equalsIgnoreCase("android"); + public static final String APP_ID = IS_ANDROID + ? "com.example.appium_testing_app" : "com.example.appiumTestingApp"; + protected static final int PORT = 4723; + + private static AppiumDriverLocalService service; + protected static FlutterAndroidDriver driver; + protected By loginButton = AppiumBy.flutterText("Login"); + + /** + * initialization. + */ + @BeforeAll + public static void beforeClass() { + service = new AppiumServiceBuilder() + .withIPAddress("127.0.0.1") + .usingPort(PORT) + .build(); + service.start(); + } + + @BeforeEach + public void startSession() throws MalformedURLException { + if (IS_ANDROID) { + // TODO: update it with FlutterDriverOptions once implemented + UiAutomator2Options options = new UiAutomator2Options() + .setAutomationName(AutomationName.FLUTTER_INTEGRATION) + .setApp(System.getProperty("flutterApp")) + .eventTimings(); + driver = new FlutterAndroidDriver(service.getUrl(), options); + } else { + throw new InvalidArgumentException( + "Currently flutter driver implementation only supports android platform"); + } + } + + @AfterEach + public void stopSession() { + if (driver != null) { + driver.quit(); + } + } + + @AfterAll + public static void afterClass() { + if (service.isRunning()) { + service.stop(); + } + } + + public void openScreen(String screenTitle) { + ScrollParameter scrollOptions = new ScrollParameter( + AppiumBy.flutterText(screenTitle), ScrollParameter.ScrollDirection.DOWN); + WebElement element = driver.scrollTillVisible(scrollOptions); + element.click(); + } +} diff --git a/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java b/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java new file mode 100644 index 000000000..6a4c95992 --- /dev/null +++ b/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java @@ -0,0 +1,62 @@ +package io.appium.java_client.android; + +import io.appium.java_client.AppiumBy; +import io.appium.java_client.flutter.commands.ScrollParameter; +import io.appium.java_client.flutter.commands.WaitParameter; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.WebElement; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class CommandTest extends BaseFlutterTest { + + AppiumBy.FlutterBy messageFieldLocator = AppiumBy.flutterKey("message_field"); + AppiumBy.FlutterBy toggleButtonLocator = AppiumBy.flutterKey("toggle_button"); + + @Test + public void testWaitCommand() { + WebElement loginButton = driver.findElement(this.loginButton); + loginButton.click(); + openScreen("Lazy Loading"); + + WebElement messageField = driver.findElement(messageFieldLocator); + WebElement toggleButton = driver.findElement(toggleButtonLocator); + + assertEquals(messageField.getText(), "Hello world"); + toggleButton.click(); + assertEquals(messageField.getText(), "Hello world"); + + WaitParameter waitParameter = new WaitParameter().setLocator(messageFieldLocator); + + driver.waitForInVisible(waitParameter); + assertEquals(0, driver.findElements(messageFieldLocator).size()); + toggleButton.click(); + driver.waitForVisible(waitParameter); + assertEquals(1, driver.findElements(messageFieldLocator).size()); + assertEquals(messageField.getText(), "Hello world"); + } + + @Test + public void testScrollTillVisibleCommand() { + WebElement loginButton = driver.findElement(this.loginButton); + loginButton.click(); + openScreen("Vertical Swiping"); + + WebElement firstElement = driver.scrollTillVisible(new ScrollParameter(AppiumBy.flutterText("Java"))); + assertTrue(Boolean.parseBoolean(firstElement.getAttribute("displayed"))); + + WebElement lastElement = driver.scrollTillVisible(new ScrollParameter(AppiumBy.flutterText("Protractor"))); + assertTrue(Boolean.parseBoolean(lastElement.getAttribute("displayed"))); + assertFalse(Boolean.parseBoolean(firstElement.getAttribute("displayed"))); + + firstElement = driver.scrollTillVisible( + new ScrollParameter(AppiumBy.flutterText("Java"), + ScrollParameter.ScrollDirection.UP) + ); + assertTrue(Boolean.parseBoolean(firstElement.getAttribute("displayed"))); + assertFalse(Boolean.parseBoolean(lastElement.getAttribute("displayed"))); + } + +} diff --git a/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java b/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java new file mode 100644 index 000000000..9eb1780c3 --- /dev/null +++ b/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java @@ -0,0 +1,55 @@ +package io.appium.java_client.android; + +import io.appium.java_client.AppiumBy; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.WebElement; + +import static org.junit.jupiter.api.Assertions.assertEquals; + + +public class FinderTests extends BaseFlutterTest { + + + @Test + public void testFlutterByKey() { + WebElement userNameField = driver.findElement(AppiumBy.flutterKey("username_text_field")); + assertEquals("admin", userNameField.getText()); + userNameField.clear(); + driver.findElement(AppiumBy.flutterKey("username_text_field")).sendKeys("admin123"); + assertEquals("admin123", userNameField.getText()); + } + + @Test + public void testFlutterByType() { + WebElement loginButton = driver.findElement(AppiumBy.flutterType("ElevatedButton")); + assertEquals(loginButton.findElement(AppiumBy.flutterType("Text")).getText(), "Login"); + } + + @Test + public void testFlutterText() { + WebElement loginButton = driver.findElement(AppiumBy.flutterText("Login")); + assertEquals(loginButton.getText(), "Login"); + loginButton.click(); + + assertEquals(1, driver.findElements(AppiumBy.flutterText("Slider")).size()); + } + + @Test + public void testFlutterTextContaining() { + WebElement loginButton = driver.findElement(this.loginButton); + loginButton.click(); + assertEquals(driver.findElement(AppiumBy.flutterTextContaining("Vertical")).getText(), + "Vertical Swiping"); + } + + @Test + public void testFlutterSemanticsLabel() { + WebElement loginButton = driver.findElement(this.loginButton); + loginButton.click(); + openScreen("Lazy Loading"); + + WebElement messageField = driver.findElement(AppiumBy.flutterSemanticsLabel("message_field")); + assertEquals(messageField.getText(), + "Hello world"); + } +} diff --git a/src/main/java/io/appium/java_client/AppiumBy.java b/src/main/java/io/appium/java_client/AppiumBy.java index 0ad042dcc..aca01fea7 100644 --- a/src/main/java/io/appium/java_client/AppiumBy.java +++ b/src/main/java/io/appium/java_client/AppiumBy.java @@ -26,8 +26,6 @@ import java.io.Serializable; import java.util.List; -import java.util.Map; - import static com.google.common.base.Strings.isNullOrEmpty; @@ -208,7 +206,7 @@ public static By iOSNsPredicateString(final String iOSNsPredicateString) { * @param selector is the value defined to the key attribute of the flutter element * @return an instance of {@link AppiumBy.ByFlutterKey} */ - public static By flutterKey(final String selector) { + public static FlutterBy flutterKey(final String selector) { return new ByFlutterKey(selector); } @@ -218,7 +216,7 @@ public static By flutterKey(final String selector) { * @param selector is the Type of widget mounted in the app tree * @return an instance of {@link AppiumBy.ByFlutterType} */ - public static By flutterType(final String selector) { + public static FlutterBy flutterType(final String selector) { return new ByFlutterType(selector); } @@ -228,7 +226,7 @@ public static By flutterType(final String selector) { * @param selector is the text that is present on the widget * @return an instance of {@link AppiumBy.ByFlutterText} */ - public static By flutterText(final String selector) { + public static FlutterBy flutterText(final String selector) { return new ByFlutterText(selector); } @@ -238,7 +236,7 @@ public static By flutterText(final String selector) { * @param selector is the text that is partially present on the widget * @return an instance of {@link AppiumBy.ByFlutterTextContaining} */ - public static By flutterTextContaining(final String selector) { + public static FlutterBy flutterTextContaining(final String selector) { return new ByFlutterTextContaining(selector); } @@ -248,7 +246,7 @@ public static By flutterTextContaining(final String selector) { * @param semanticsLabel represents the value assigned to the label attribute of semantics element * @return an instance of {@link AppiumBy.ByFlutterSemanticsLabel} */ - public static By flutterSemanticsLabel(final String semanticsLabel) { + public static FlutterBy flutterSemanticsLabel(final String semanticsLabel) { return new ByFlutterSemanticsLabel(semanticsLabel); } diff --git a/src/main/java/io/appium/java_client/flutter/FlutterDriver.java b/src/main/java/io/appium/java_client/flutter/FlutterDriver.java index 99266ba38..be239437c 100644 --- a/src/main/java/io/appium/java_client/flutter/FlutterDriver.java +++ b/src/main/java/io/appium/java_client/flutter/FlutterDriver.java @@ -1,9 +1,6 @@ package io.appium.java_client.flutter; -import io.appium.java_client.flutter.commands.DoubleClickParameter; -import io.appium.java_client.flutter.commands.DragAndDropParameter; import io.appium.java_client.flutter.commands.FlutterCommandParameter; -import io.appium.java_client.flutter.commands.LongPressParameter; import io.appium.java_client.flutter.commands.ScrollParameter; import io.appium.java_client.flutter.commands.WaitParameter; import org.openqa.selenium.JavascriptExecutor; @@ -43,34 +40,7 @@ default void waitForInVisible(WaitParameter parameter) { default WebElement scrollTillVisible(ScrollParameter parameter) { return (WebElement) executeScript("scrollTillVisible", parameter); } - - /** - * Performs a double-click action on an element. - * - * @param parameter The parameters for double-clicking, specifying element details. - */ - default void performDoubleClick(DoubleClickParameter parameter) { - executeScript("doubleClick", parameter); - } - - /** - * Performs a long press action on an element. - * - * @param parameter The parameters for long pressing, specifying element details. - */ - default void performLongPress(LongPressParameter parameter) { - executeScript("longPress", parameter); - } - - /** - * Performs a drag-and-drop action between two elements. - * - * @param parameter The parameters for drag-and-drop, specifying source and target elements. - */ - default void performDragAndDrop(DragAndDropParameter parameter) { - executeScript("dragAndDrop", parameter); - } - + /** * Executes a Flutter-specific script using JavascriptExecutor. * diff --git a/src/main/java/io/appium/java_client/flutter/commands/DoubleClickParameter.java b/src/main/java/io/appium/java_client/flutter/commands/DoubleClickParameter.java deleted file mode 100644 index 3eee83876..000000000 --- a/src/main/java/io/appium/java_client/flutter/commands/DoubleClickParameter.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.appium.java_client.flutter.commands; - -import lombok.Getter; -import lombok.Setter; -import lombok.experimental.Accessors; - -@Accessors(chain = true) -@Setter -@Getter -public class DoubleClickParameter extends FlutterGestureParameter { - -} diff --git a/src/main/java/io/appium/java_client/flutter/commands/DragAndDropParameter.java b/src/main/java/io/appium/java_client/flutter/commands/DragAndDropParameter.java deleted file mode 100644 index 78689b25e..000000000 --- a/src/main/java/io/appium/java_client/flutter/commands/DragAndDropParameter.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.appium.java_client.flutter.commands; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NonNull; -import lombok.experimental.Accessors; -import org.openqa.selenium.WebElement; - -import java.util.Map; - -@Accessors(chain = true) -@Getter -@AllArgsConstructor -public class DragAndDropParameter extends FlutterCommandParameter { - @NonNull - WebElement source; - @NonNull - WebElement target; - - private DragAndDropParameter() { - } - - @Override - public Map toJson() { - return Map.of("source", source, "target", target); - } -} diff --git a/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandParameter.java b/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandParameter.java index befe2b310..ddd2d74f6 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandParameter.java +++ b/src/main/java/io/appium/java_client/flutter/commands/FlutterCommandParameter.java @@ -13,7 +13,7 @@ public abstract class FlutterCommandParameter { * @param by The FlutterBy instance representing the locator to parse. * @return A Map containing the parsed locator information with keys using and value. */ - Map parseFlutterLocator(AppiumBy.FlutterBy by) { + protected static Map parseFlutterLocator(AppiumBy.FlutterBy by) { By.Remotable.Parameters parameters = by.getRemoteParameters(); return Map.of( "using", parameters.using(), diff --git a/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureParameter.java b/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureParameter.java deleted file mode 100644 index 4120e96e0..000000000 --- a/src/main/java/io/appium/java_client/flutter/commands/FlutterGestureParameter.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.appium.java_client.flutter.commands; - -import lombok.Getter; -import lombok.Setter; -import lombok.experimental.Accessors; -import org.openqa.selenium.Point; -import org.openqa.selenium.WebElement; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -@Accessors(chain = true) -@Setter -@Getter -public class FlutterGestureParameter extends FlutterCommandParameter { - WebElement element; - Point point; - - @Override - public Map toJson() { - Map args = new HashMap<>(); - args.put("origin", element); - if (point != null) { - args.put("offset", Map.of("x", point.getX(), "y", point.getY())); - } - return Collections.unmodifiableMap(args); - } -} diff --git a/src/main/java/io/appium/java_client/flutter/commands/LongPressParameter.java b/src/main/java/io/appium/java_client/flutter/commands/LongPressParameter.java deleted file mode 100644 index 668b4c1ba..000000000 --- a/src/main/java/io/appium/java_client/flutter/commands/LongPressParameter.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.appium.java_client.flutter.commands; - -import lombok.Getter; -import lombok.Setter; -import lombok.experimental.Accessors; - - -@Accessors(chain = true) -@Setter -@Getter -public class LongPressParameter extends FlutterGestureParameter { - -} diff --git a/src/main/java/io/appium/java_client/flutter/commands/ScrollParameter.java b/src/main/java/io/appium/java_client/flutter/commands/ScrollParameter.java index 26fb8a360..e3309dca8 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/ScrollParameter.java +++ b/src/main/java/io/appium/java_client/flutter/commands/ScrollParameter.java @@ -8,6 +8,8 @@ import org.openqa.selenium.WebElement; import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -26,6 +28,15 @@ public class ScrollParameter extends FlutterCommandParameter { private ScrollParameter() { } + /** + * Constructs a new ScrollOptions object with the given parameters. + * + * @param scrollTo the locator used for scrolling to a specific element + */ + public ScrollParameter(AppiumBy.FlutterBy scrollTo) { + this(scrollTo, ScrollDirection.DOWN); + } + /** * Constructs a new ScrollOptions object with the given parameters. * @@ -41,17 +52,20 @@ public ScrollParameter(AppiumBy.FlutterBy scrollTo, ScrollDirection scrollDirect @Override public Map toJson() { - return Map.of( - "finder", parseFlutterLocator(scrollTo), - "scrollView", scrollView, - "delta", delta, - "maxScrolls", maxScrolls, - "settleBetweenScrollsTimeout", settleBetweenScrollsTimeout, - "scrollDirection", Optional.ofNullable(scrollDirection) - .orElse(ScrollDirection.UP).getDirection(), - "dragDuration", Optional.ofNullable(dragDuration) - .orElse(Duration.ZERO).getSeconds() - ); + Map params = new HashMap<>(); + + params.put("finder", parseFlutterLocator(scrollTo)); + params.put("scrollView", scrollView); + params.put("delta", delta); + params.put("maxScrolls", maxScrolls); + params.put("settleBetweenScrollsTimeout", settleBetweenScrollsTimeout); + Optional.ofNullable(scrollDirection) + .ifPresent(direction -> params.put("scrollDirection", direction.getDirection())); + Optional.ofNullable(dragDuration) + .ifPresent(direction -> params.put("dragDuration", dragDuration.getSeconds())); + + params.entrySet().removeIf(entry -> entry.getValue() == null); + return Collections.unmodifiableMap(params); } @Getter diff --git a/src/main/java/io/appium/java_client/flutter/ios/FlutterIOSDriver.java b/src/main/java/io/appium/java_client/flutter/ios/FlutterIOSDriver.java deleted file mode 100644 index f79fc2970..000000000 --- a/src/main/java/io/appium/java_client/flutter/ios/FlutterIOSDriver.java +++ /dev/null @@ -1,75 +0,0 @@ -package io.appium.java_client.flutter.ios; - -import io.appium.java_client.AppiumClientConfig; -import io.appium.java_client.flutter.FlutterDriver; -import io.appium.java_client.ios.IOSDriver; -import io.appium.java_client.service.local.AppiumDriverLocalService; -import io.appium.java_client.service.local.AppiumServiceBuilder; -import org.openqa.selenium.Capabilities; -import org.openqa.selenium.remote.HttpCommandExecutor; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.openqa.selenium.remote.http.ClientConfig; -import org.openqa.selenium.remote.http.HttpClient; - -import java.net.URL; - -/** - * Custom AndroidDriver implementation with additional Flutter-specific capabilities. - */ -public class FlutterIOSDriver extends IOSDriver implements FlutterDriver { - - public FlutterIOSDriver(HttpCommandExecutor executor, Capabilities capabilities) { - super(executor, capabilities); - } - - public FlutterIOSDriver(URL remoteAddress, Capabilities capabilities) { - super(remoteAddress, capabilities); - } - - public FlutterIOSDriver(URL remoteAddress, HttpClient.Factory httpClientFactory, Capabilities capabilities) { - super(remoteAddress, httpClientFactory, capabilities); - } - - public FlutterIOSDriver(AppiumDriverLocalService service, Capabilities capabilities) { - super(service, capabilities); - } - - public FlutterIOSDriver( - AppiumDriverLocalService service, HttpClient.Factory httpClientFactory, Capabilities capabilities) { - super(service, httpClientFactory, capabilities); - } - - public FlutterIOSDriver(AppiumServiceBuilder builder, Capabilities capabilities) { - super(builder, capabilities); - } - - public FlutterIOSDriver( - AppiumServiceBuilder builder, HttpClient.Factory httpClientFactory, Capabilities capabilities) { - super(builder, httpClientFactory, capabilities); - } - - public FlutterIOSDriver(HttpClient.Factory httpClientFactory, Capabilities capabilities) { - super(httpClientFactory, capabilities); - } - - public FlutterIOSDriver(ClientConfig clientConfig, Capabilities capabilities) { - super(clientConfig, capabilities); - } - - public FlutterIOSDriver(AppiumClientConfig appiumClientConfig, Capabilities capabilities) { - super(appiumClientConfig, capabilities); - } - - public FlutterIOSDriver(URL remoteSessionAddress) { - super(remoteSessionAddress); - } - - public FlutterIOSDriver(Capabilities capabilities) { - super(capabilities); - } - - @Override - public RemoteWebDriver getDriver() { - return this; - } -} diff --git a/src/main/java/io/appium/java_client/flutter/ios/FlutterIOSOptions.java b/src/main/java/io/appium/java_client/flutter/ios/FlutterIOSOptions.java deleted file mode 100644 index 9d1ec9817..000000000 --- a/src/main/java/io/appium/java_client/flutter/ios/FlutterIOSOptions.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.appium.java_client.flutter.ios; - -import io.appium.java_client.ios.options.XCUITestOptions; -import io.appium.java_client.remote.AutomationName; -import org.openqa.selenium.Capabilities; - -import java.util.Map; - -public class FlutterIOSOptions extends XCUITestOptions { - - public FlutterIOSOptions() { - super(); - addDefaultOptions(); - } - - public FlutterIOSOptions(Capabilities source) { - super(source); - addDefaultOptions(); - } - - public FlutterIOSOptions(Map source) { - super(source); - addDefaultOptions(); - } - - private void addDefaultOptions() { - setAutomationName(AutomationName.FLUTTER_INTEGRATION); - } -} From fb6ce55b21792ed9726e2b7df08e3645bfb5b463 Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Mon, 22 Jul 2024 08:45:54 +0530 Subject: [PATCH 10/16] EOL fix --- .../java/io/appium/java_client/flutter/FlutterDriver.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/appium/java_client/flutter/FlutterDriver.java b/src/main/java/io/appium/java_client/flutter/FlutterDriver.java index be239437c..e4533bb9f 100644 --- a/src/main/java/io/appium/java_client/flutter/FlutterDriver.java +++ b/src/main/java/io/appium/java_client/flutter/FlutterDriver.java @@ -40,7 +40,7 @@ default void waitForInVisible(WaitParameter parameter) { default WebElement scrollTillVisible(ScrollParameter parameter) { return (WebElement) executeScript("scrollTillVisible", parameter); } - + /** * Executes a Flutter-specific script using JavascriptExecutor. * @@ -59,4 +59,4 @@ private Object executeScript(String scriptName, FlutterCommandParameter paramete * @return The RemoteWebDriver instance used by this FlutterDriver. */ RemoteWebDriver getDriver(); -} \ No newline at end of file +} From bc309658e8269a4740a59168c91876e54181348c Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Mon, 22 Jul 2024 19:09:20 +0530 Subject: [PATCH 11/16] Code review fixes --- build.gradle | 3 - .../java_client/android/BaseFlutterTest.java | 8 +-- .../java_client/android/CommandTest.java | 6 +- .../java_client/android/FinderTests.java | 5 +- .../flutter/CanExecuteFlutterScripts.java | 20 ++++++ .../java_client/flutter/FlutterDriver.java | 62 ------------------- .../SupportsScrollingOfFlutterElements.java | 17 +++++ .../SupportsWaitingForFlutterElements.java | 25 ++++++++ .../flutter/android/FlutterAndroidDriver.java | 13 ++-- .../flutter/commands/ScrollParameter.java | 27 ++++---- .../flutter/commands/WaitParameter.java | 22 ++++--- 11 files changed, 104 insertions(+), 104 deletions(-) create mode 100644 src/main/java/io/appium/java_client/flutter/CanExecuteFlutterScripts.java delete mode 100644 src/main/java/io/appium/java_client/flutter/FlutterDriver.java create mode 100644 src/main/java/io/appium/java_client/flutter/SupportsScrollingOfFlutterElements.java create mode 100644 src/main/java/io/appium/java_client/flutter/SupportsWaitingForFlutterElements.java diff --git a/build.gradle b/build.gradle index ab03351f7..3488703d7 100644 --- a/build.gradle +++ b/build.gradle @@ -300,9 +300,6 @@ testing { dependencies { implementation project() implementation(sourceSets.test.output) - implementation('io.github.bonigarcia:webdrivermanager:5.9.1') { - exclude group: 'org.seleniumhq.selenium' - } } targets.configureEach { diff --git a/src/e2eFlutterTest/java/io/appium/java_client/android/BaseFlutterTest.java b/src/e2eFlutterTest/java/io/appium/java_client/android/BaseFlutterTest.java index 9b58c9b27..940a82640 100644 --- a/src/e2eFlutterTest/java/io/appium/java_client/android/BaseFlutterTest.java +++ b/src/e2eFlutterTest/java/io/appium/java_client/android/BaseFlutterTest.java @@ -18,19 +18,19 @@ import java.net.MalformedURLException; import java.util.Optional; -public class BaseFlutterTest { +class BaseFlutterTest { - protected static final boolean IS_ANDROID = Optional + private static final boolean IS_ANDROID = Optional .ofNullable(System.getProperty("platform")) .orElse("android") .equalsIgnoreCase("android"); - public static final String APP_ID = IS_ANDROID + private static final String APP_ID = IS_ANDROID ? "com.example.appium_testing_app" : "com.example.appiumTestingApp"; protected static final int PORT = 4723; private static AppiumDriverLocalService service; protected static FlutterAndroidDriver driver; - protected By loginButton = AppiumBy.flutterText("Login"); + protected static final By loginButton = AppiumBy.flutterText("Login"); /** * initialization. diff --git a/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java b/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java index 6a4c95992..18413b0c6 100644 --- a/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java +++ b/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java @@ -10,10 +10,10 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -public class CommandTest extends BaseFlutterTest { +class CommandTest extends BaseFlutterTest { - AppiumBy.FlutterBy messageFieldLocator = AppiumBy.flutterKey("message_field"); - AppiumBy.FlutterBy toggleButtonLocator = AppiumBy.flutterKey("toggle_button"); + private static final AppiumBy.FlutterBy messageFieldLocator = AppiumBy.flutterKey("message_field"); + private static final AppiumBy.FlutterBy toggleButtonLocator = AppiumBy.flutterKey("toggle_button"); @Test public void testWaitCommand() { diff --git a/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java b/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java index 9eb1780c3..4bc62be09 100644 --- a/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java +++ b/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java @@ -7,9 +7,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -public class FinderTests extends BaseFlutterTest { - - +class FinderTests extends BaseFlutterTest { + @Test public void testFlutterByKey() { WebElement userNameField = driver.findElement(AppiumBy.flutterKey("username_text_field")); diff --git a/src/main/java/io/appium/java_client/flutter/CanExecuteFlutterScripts.java b/src/main/java/io/appium/java_client/flutter/CanExecuteFlutterScripts.java new file mode 100644 index 000000000..f8eaf3af4 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/CanExecuteFlutterScripts.java @@ -0,0 +1,20 @@ +package io.appium.java_client.flutter; + +import io.appium.java_client.flutter.commands.FlutterCommandParameter; +import org.openqa.selenium.JavascriptExecutor; + +public interface CanExecuteFlutterScripts extends JavascriptExecutor { + + /** + * Executes a Flutter-specific script using JavascriptExecutor. + * + * @param scriptName The name of the Flutter script to execute. + * @param parameter The parameters for the Flutter command. + * @return The result of executing the script. + */ + default Object executeFlutterCommand(String scriptName, FlutterCommandParameter parameter) { + String commandName = String.format("flutter: %s", scriptName); + return executeScript(commandName, parameter.toJson()); + } + +} diff --git a/src/main/java/io/appium/java_client/flutter/FlutterDriver.java b/src/main/java/io/appium/java_client/flutter/FlutterDriver.java deleted file mode 100644 index e4533bb9f..000000000 --- a/src/main/java/io/appium/java_client/flutter/FlutterDriver.java +++ /dev/null @@ -1,62 +0,0 @@ -package io.appium.java_client.flutter; - -import io.appium.java_client.flutter.commands.FlutterCommandParameter; -import io.appium.java_client.flutter.commands.ScrollParameter; -import io.appium.java_client.flutter.commands.WaitParameter; -import org.openqa.selenium.JavascriptExecutor; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; - -/** - * Interface representing a Flutter driver that extends {@link RemoteWebDriver}. - * Provides convenience methods for executing Flutter-specific commands using JavaScriptExecutor. - */ -public interface FlutterDriver { - - /** - * Waits for an element to become visible on the screen. - * - * @param parameter The parameters for waiting, specifying timeout and element details. - */ - default void waitForVisible(WaitParameter parameter) { - executeScript("waitForVisible", parameter); - } - - /** - * Waits for an element to become invisible on the screen. - * - * @param parameter The parameters for waiting, specifying timeout and element details. - */ - default void waitForInVisible(WaitParameter parameter) { - executeScript("waitForAbsent", parameter); - } - - /** - * Scrolls to make an element visible on the screen. - * - * @param parameter The parameters for scrolling, specifying element details. - * @return The WebElement that was scrolled to. - */ - default WebElement scrollTillVisible(ScrollParameter parameter) { - return (WebElement) executeScript("scrollTillVisible", parameter); - } - - /** - * Executes a Flutter-specific script using JavascriptExecutor. - * - * @param scriptName The name of the Flutter script to execute. - * @param parameter The parameters for the Flutter command. - * @return The result of executing the script. - */ - private Object executeScript(String scriptName, FlutterCommandParameter parameter) { - String commandName = String.format("flutter: %s", scriptName); - return ((JavascriptExecutor) this.getDriver()).executeScript(commandName, parameter.toJson()); - } - - /** - * Retrieves the underlying RemoteWebDriver instance associated with this FlutterDriver. - * - * @return The RemoteWebDriver instance used by this FlutterDriver. - */ - RemoteWebDriver getDriver(); -} diff --git a/src/main/java/io/appium/java_client/flutter/SupportsScrollingOfFlutterElements.java b/src/main/java/io/appium/java_client/flutter/SupportsScrollingOfFlutterElements.java new file mode 100644 index 000000000..25a734cf7 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/SupportsScrollingOfFlutterElements.java @@ -0,0 +1,17 @@ +package io.appium.java_client.flutter; + +import io.appium.java_client.flutter.commands.ScrollParameter; +import org.openqa.selenium.WebElement; + +public interface SupportsScrollingOfFlutterElements extends CanExecuteFlutterScripts { + + /** + * Scrolls to make an element visible on the screen. + * + * @param parameter The parameters for scrolling, specifying element details. + * @return The WebElement that was scrolled to. + */ + default WebElement scrollTillVisible(ScrollParameter parameter) { + return (WebElement) executeFlutterCommand("scrollTillVisible", parameter); + } +} diff --git a/src/main/java/io/appium/java_client/flutter/SupportsWaitingForFlutterElements.java b/src/main/java/io/appium/java_client/flutter/SupportsWaitingForFlutterElements.java new file mode 100644 index 000000000..521f75cc8 --- /dev/null +++ b/src/main/java/io/appium/java_client/flutter/SupportsWaitingForFlutterElements.java @@ -0,0 +1,25 @@ +package io.appium.java_client.flutter; + +import io.appium.java_client.flutter.commands.WaitParameter; + +public interface SupportsWaitingForFlutterElements extends CanExecuteFlutterScripts { + + /** + * Waits for an element to become visible on the screen. + * + * @param parameter The parameters for waiting, specifying timeout and element details. + */ + default void waitForVisible(WaitParameter parameter) { + executeFlutterCommand("waitForVisible", parameter); + } + + /** + * Waits for an element to become absent on the screen. + * + * @param parameter The parameters for waiting, specifying timeout and element details. + */ + default void waitForInVisible(WaitParameter parameter) { + executeFlutterCommand("waitForAbsent", parameter); + } + +} diff --git a/src/main/java/io/appium/java_client/flutter/android/FlutterAndroidDriver.java b/src/main/java/io/appium/java_client/flutter/android/FlutterAndroidDriver.java index 16cfea62d..f7be154ff 100644 --- a/src/main/java/io/appium/java_client/flutter/android/FlutterAndroidDriver.java +++ b/src/main/java/io/appium/java_client/flutter/android/FlutterAndroidDriver.java @@ -2,12 +2,12 @@ import io.appium.java_client.AppiumClientConfig; import io.appium.java_client.android.AndroidDriver; -import io.appium.java_client.flutter.FlutterDriver; +import io.appium.java_client.flutter.SupportsScrollingOfFlutterElements; +import io.appium.java_client.flutter.SupportsWaitingForFlutterElements; import io.appium.java_client.service.local.AppiumDriverLocalService; import io.appium.java_client.service.local.AppiumServiceBuilder; import org.openqa.selenium.Capabilities; import org.openqa.selenium.remote.HttpCommandExecutor; -import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.remote.http.ClientConfig; import org.openqa.selenium.remote.http.HttpClient; @@ -16,7 +16,9 @@ /** * Custom AndroidDriver implementation with additional Flutter-specific capabilities. */ -public class FlutterAndroidDriver extends AndroidDriver implements FlutterDriver { +public class FlutterAndroidDriver extends AndroidDriver implements + SupportsWaitingForFlutterElements, + SupportsScrollingOfFlutterElements { public FlutterAndroidDriver(HttpCommandExecutor executor, Capabilities capabilities) { super(executor, capabilities); @@ -67,9 +69,4 @@ public FlutterAndroidDriver(Capabilities capabilities) { public FlutterAndroidDriver(URL remoteSessionAddress, String automationName) { super(remoteSessionAddress, automationName); } - - @Override - public RemoteWebDriver getDriver() { - return this; - } } diff --git a/src/main/java/io/appium/java_client/flutter/commands/ScrollParameter.java b/src/main/java/io/appium/java_client/flutter/commands/ScrollParameter.java index e3309dca8..695cb060d 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/ScrollParameter.java +++ b/src/main/java/io/appium/java_client/flutter/commands/ScrollParameter.java @@ -17,13 +17,13 @@ @Getter @Setter public class ScrollParameter extends FlutterCommandParameter { - AppiumBy.FlutterBy scrollTo; - WebElement scrollView; - ScrollDirection scrollDirection; - Integer delta; - Integer maxScrolls; - Integer settleBetweenScrollsTimeout; - Duration dragDuration; + private AppiumBy.FlutterBy scrollTo; + private WebElement scrollView; + private ScrollDirection scrollDirection; + private Integer delta; + private Integer maxScrolls; + private Integer settleBetweenScrollsTimeout; + private Duration dragDuration; private ScrollParameter() { } @@ -55,16 +55,19 @@ public Map toJson() { Map params = new HashMap<>(); params.put("finder", parseFlutterLocator(scrollTo)); - params.put("scrollView", scrollView); - params.put("delta", delta); - params.put("maxScrolls", maxScrolls); - params.put("settleBetweenScrollsTimeout", settleBetweenScrollsTimeout); + Optional.ofNullable(scrollView) + .ifPresent(scrollView -> params.put("scrollView", scrollView)); + Optional.ofNullable(delta) + .ifPresent(delta -> params.put("delta", delta)); + Optional.ofNullable(maxScrolls) + .ifPresent(maxScrolls -> params.put("delta", maxScrolls)); + Optional.ofNullable(settleBetweenScrollsTimeout) + .ifPresent(timeout -> params.put("delta", settleBetweenScrollsTimeout)); Optional.ofNullable(scrollDirection) .ifPresent(direction -> params.put("scrollDirection", direction.getDirection())); Optional.ofNullable(dragDuration) .ifPresent(direction -> params.put("dragDuration", dragDuration.getSeconds())); - params.entrySet().removeIf(entry -> entry.getValue() == null); return Collections.unmodifiableMap(params); } diff --git a/src/main/java/io/appium/java_client/flutter/commands/WaitParameter.java b/src/main/java/io/appium/java_client/flutter/commands/WaitParameter.java index 2e0941780..89e0a19cf 100644 --- a/src/main/java/io/appium/java_client/flutter/commands/WaitParameter.java +++ b/src/main/java/io/appium/java_client/flutter/commands/WaitParameter.java @@ -1,5 +1,6 @@ package io.appium.java_client.flutter.commands; +import com.google.common.base.Preconditions; import io.appium.java_client.AppiumBy; import lombok.Getter; import lombok.Setter; @@ -10,6 +11,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Optional; @Accessors(chain = true) @Getter @@ -21,14 +23,16 @@ public class WaitParameter extends FlutterCommandParameter { @Override public Map toJson() { - Map args = new HashMap<>(); - args.put("element", element); - if (locator != null) { - args.put("locator", parseFlutterLocator(locator)); - } - if (timeout != null) { - args.put("timeout", timeout.getSeconds()); - } - return Collections.unmodifiableMap(args); + Preconditions.checkArgument(element != null || locator != null, + "Must supply a valid element or locator to wait for"); + Map params = new HashMap<>(); + Optional.ofNullable(element) + .ifPresent(element -> params.put("element", element)); + Optional.ofNullable(locator) + .ifPresent(locator -> params.put("locator", parseFlutterLocator(locator))); + Optional.ofNullable(timeout) + .ifPresent(timeout -> params.put("timeout", timeout)); + + return Collections.unmodifiableMap(params); } } From 0ab722c9ca3e341c90ec01b75467f66249b8eece Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Mon, 22 Jul 2024 19:41:59 +0530 Subject: [PATCH 12/16] CI fixes --- .github/workflows/gradle.yml | 32 ++++++++++++++----- .../java_client/android/CommandTest.java | 4 +-- .../java_client/android/FinderTests.java | 6 ++-- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 94ee31463..1f82f75ef 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -41,6 +41,9 @@ jobs: - java: 17 platform: ubuntu-latest e2e-tests: android + - java: 17 + platform: ubuntu-latest + e2e-tests: flutter-android - java: 21 platform: ubuntu-latest fail-fast: false @@ -79,21 +82,34 @@ jobs: with: node-version: 'lts/*' - - name: Install Appium & Flutter driver - if: matrix.e2e-tests == 'android' || matrix.e2e-tests == 'ios' - run: | - npm install --location=global appium - appium driver install appium-flutter-integration-driver --source npm + - name: Install Appium + if: matrix.e2e-tests == 'android' || matrix.e2e-tests == 'ios' || matrix.e2e-tests == 'flutter-android' + run: npm install --location=global appium - name: Install UIA2 driver - if: matrix.e2e-tests == 'android' + if: matrix.e2e-tests == 'android' || matrix.e2e-tests == 'flutter-android' run: appium driver install uiautomator2 + + - name: Install Flutter Integration driver + if: matrix.e2e-tests == 'flutter-android' + run: appium driver install appium-flutter-integration-driver --source npm + - name: Run Android E2E tests if: matrix.e2e-tests == 'android' uses: reactivecircus/android-emulator-runner@v2 with: - script: ./gradlew e2eAndroidTest -PisCI -Pselenium.version=$latest_snapshot && - ./gradlew e2eFlutterTest -Pplatform="android" -Pselenium.version=$latest_snapshot -PisCI -PflutterApp=${{ env.FLUTTER_ANDROID_APP }} + script: ./gradlew e2eAndroidTest -PisCI -Pselenium.version=$latest_snapshot + api-level: ${{ env.ANDROID_SDK_VERSION }} + avd-name: ${{ env.ANDROID_EMU_NAME }} + disable-spellchecker: true + disable-animations: true + target: ${{ env.ANDROID_EMU_TARGET }} + + - name: Run Flutter Android E2E tests + if: matrix.e2e-tests == 'flutter-android' + uses: reactivecircus/android-emulator-runner@v2 + with: + script: ./gradlew e2eFlutterTest -Pplatform="android" -Pselenium.version=$latest_snapshot -PisCI -PflutterApp=${{ env.FLUTTER_ANDROID_APP }} api-level: ${{ env.ANDROID_SDK_VERSION }} avd-name: ${{ env.ANDROID_EMU_NAME }} disable-spellchecker: true diff --git a/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java b/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java index 18413b0c6..4e7e39c19 100644 --- a/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java +++ b/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java @@ -17,7 +17,7 @@ class CommandTest extends BaseFlutterTest { @Test public void testWaitCommand() { - WebElement loginButton = driver.findElement(this.loginButton); + WebElement loginButton = driver.findElement(BaseFlutterTest.loginButton); loginButton.click(); openScreen("Lazy Loading"); @@ -40,7 +40,7 @@ public void testWaitCommand() { @Test public void testScrollTillVisibleCommand() { - WebElement loginButton = driver.findElement(this.loginButton); + WebElement loginButton = driver.findElement(BaseFlutterTest.loginButton); loginButton.click(); openScreen("Vertical Swiping"); diff --git a/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java b/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java index 4bc62be09..16c181e8a 100644 --- a/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java +++ b/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java @@ -8,7 +8,7 @@ class FinderTests extends BaseFlutterTest { - + @Test public void testFlutterByKey() { WebElement userNameField = driver.findElement(AppiumBy.flutterKey("username_text_field")); @@ -35,7 +35,7 @@ public void testFlutterText() { @Test public void testFlutterTextContaining() { - WebElement loginButton = driver.findElement(this.loginButton); + WebElement loginButton = driver.findElement(BaseFlutterTest.loginButton); loginButton.click(); assertEquals(driver.findElement(AppiumBy.flutterTextContaining("Vertical")).getText(), "Vertical Swiping"); @@ -43,7 +43,7 @@ public void testFlutterTextContaining() { @Test public void testFlutterSemanticsLabel() { - WebElement loginButton = driver.findElement(this.loginButton); + WebElement loginButton = driver.findElement(BaseFlutterTest.loginButton); loginButton.click(); openScreen("Lazy Loading"); From 9ec42c743cdf9f1c4a01ca200e2e90434235759a Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Mon, 22 Jul 2024 19:50:55 +0530 Subject: [PATCH 13/16] Checkstyle fixes --- .../java_client/android/BaseFlutterTest.java | 2 +- .../java_client/android/CommandTest.java | 18 +++++++++--------- .../java_client/android/FinderTests.java | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/e2eFlutterTest/java/io/appium/java_client/android/BaseFlutterTest.java b/src/e2eFlutterTest/java/io/appium/java_client/android/BaseFlutterTest.java index 940a82640..e18112d4a 100644 --- a/src/e2eFlutterTest/java/io/appium/java_client/android/BaseFlutterTest.java +++ b/src/e2eFlutterTest/java/io/appium/java_client/android/BaseFlutterTest.java @@ -30,7 +30,7 @@ class BaseFlutterTest { private static AppiumDriverLocalService service; protected static FlutterAndroidDriver driver; - protected static final By loginButton = AppiumBy.flutterText("Login"); + protected static final By LOGIN_BUTTON = AppiumBy.flutterText("Login"); /** * initialization. diff --git a/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java b/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java index 4e7e39c19..80f32002d 100644 --- a/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java +++ b/src/e2eFlutterTest/java/io/appium/java_client/android/CommandTest.java @@ -12,35 +12,35 @@ class CommandTest extends BaseFlutterTest { - private static final AppiumBy.FlutterBy messageFieldLocator = AppiumBy.flutterKey("message_field"); - private static final AppiumBy.FlutterBy toggleButtonLocator = AppiumBy.flutterKey("toggle_button"); + private static final AppiumBy.FlutterBy MESSAGE_FIELD = AppiumBy.flutterKey("message_field"); + private static final AppiumBy.FlutterBy TOGGLE_BUTTON = AppiumBy.flutterKey("toggle_button"); @Test public void testWaitCommand() { - WebElement loginButton = driver.findElement(BaseFlutterTest.loginButton); + WebElement loginButton = driver.findElement(BaseFlutterTest.LOGIN_BUTTON); loginButton.click(); openScreen("Lazy Loading"); - WebElement messageField = driver.findElement(messageFieldLocator); - WebElement toggleButton = driver.findElement(toggleButtonLocator); + WebElement messageField = driver.findElement(MESSAGE_FIELD); + WebElement toggleButton = driver.findElement(TOGGLE_BUTTON); assertEquals(messageField.getText(), "Hello world"); toggleButton.click(); assertEquals(messageField.getText(), "Hello world"); - WaitParameter waitParameter = new WaitParameter().setLocator(messageFieldLocator); + WaitParameter waitParameter = new WaitParameter().setLocator(MESSAGE_FIELD); driver.waitForInVisible(waitParameter); - assertEquals(0, driver.findElements(messageFieldLocator).size()); + assertEquals(0, driver.findElements(MESSAGE_FIELD).size()); toggleButton.click(); driver.waitForVisible(waitParameter); - assertEquals(1, driver.findElements(messageFieldLocator).size()); + assertEquals(1, driver.findElements(MESSAGE_FIELD).size()); assertEquals(messageField.getText(), "Hello world"); } @Test public void testScrollTillVisibleCommand() { - WebElement loginButton = driver.findElement(BaseFlutterTest.loginButton); + WebElement loginButton = driver.findElement(BaseFlutterTest.LOGIN_BUTTON); loginButton.click(); openScreen("Vertical Swiping"); diff --git a/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java b/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java index 16c181e8a..dc2361869 100644 --- a/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java +++ b/src/e2eFlutterTest/java/io/appium/java_client/android/FinderTests.java @@ -35,7 +35,7 @@ public void testFlutterText() { @Test public void testFlutterTextContaining() { - WebElement loginButton = driver.findElement(BaseFlutterTest.loginButton); + WebElement loginButton = driver.findElement(BaseFlutterTest.LOGIN_BUTTON); loginButton.click(); assertEquals(driver.findElement(AppiumBy.flutterTextContaining("Vertical")).getText(), "Vertical Swiping"); @@ -43,7 +43,7 @@ public void testFlutterTextContaining() { @Test public void testFlutterSemanticsLabel() { - WebElement loginButton = driver.findElement(BaseFlutterTest.loginButton); + WebElement loginButton = driver.findElement(BaseFlutterTest.LOGIN_BUTTON); loginButton.click(); openScreen("Lazy Loading"); From dbe13a4749e1262fe2ac4b02173a694dc8b6f771 Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Mon, 22 Jul 2024 20:08:13 +0530 Subject: [PATCH 14/16] pipeline improvement --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 1f82f75ef..ba9db7e4b 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -50,7 +50,7 @@ jobs: runs-on: ${{ matrix.platform }} - name: JDK ${{ matrix.java }} - ${{ matrix.platform }} + name: JDK ${{ matrix.java }} - ${{ matrix.platform }} ${{ matrix.e2e-tests }} steps: - uses: actions/checkout@v4 From aaf96efc35a34e93d05a75b6493211a08eb2c902 Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Mon, 22 Jul 2024 20:31:08 +0530 Subject: [PATCH 15/16] Fix nodejs version --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ba9db7e4b..9b12b0663 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -77,7 +77,7 @@ jobs: ./gradlew clean build -PisCI -Pselenium.version=$latest_snapshot - name: Install Node.js - if: matrix.e2e-tests == 'android' || matrix.e2e-tests == 'ios' + if: matrix.e2e-tests == 'android' || matrix.e2e-tests == 'ios' || matrix.e2e-tests == 'flutter-android' uses: actions/setup-node@v4 with: node-version: 'lts/*' From 7d7aa6f03dfefab69d0103d92dc3b5438dbba62f Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Mon, 22 Jul 2024 20:44:29 +0530 Subject: [PATCH 16/16] enable kvm rules --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 9b12b0663..8cdab0ef7 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -55,7 +55,7 @@ jobs: - uses: actions/checkout@v4 - name: Enable KVM group perms - if: matrix.e2e-tests == 'android' + if: matrix.e2e-tests == 'android' || matrix.e2e-tests == 'flutter-android' run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules sudo udevadm control --reload-rules