Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions src/main/java/io/appium/java_client/flutter/FlutterDriver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
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;
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);
}

/**
* 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.
*
* @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();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
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;
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 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);
}

@Override
public RemoteWebDriver getDriver() {
return this;
}
}
Original file line number Diff line number Diff line change
@@ -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 {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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<String, Object> toJson() {
return Map.of("source", source, "target", target);
}
}
Original file line number Diff line number Diff line change
@@ -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<String, Object> parseFlutterLocator(AppiumBy.FlutterBy by) {
By.Remotable.Parameters parameters = by.getRemoteParameters();
return Map.of(
"using", parameters.using(),
"value", parameters.value()
);
}

public abstract Map<String, Object> toJson();
}
Original file line number Diff line number Diff line change
@@ -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)
@Setter
@Getter
public class FlutterGestureParameter extends FlutterCommandParameter {
WebElement element;
Point point;

@Override
public Map<String, Object> toJson() {
Map<String, Object> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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 LongPressParameter extends FlutterGestureParameter {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
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;
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 ScrollParameter extends FlutterCommandParameter {
AppiumBy.FlutterBy scrollTo;
WebElement scrollView;
ScrollDirection scrollDirection;
Integer delta;
Integer maxScrolls;
Integer settleBetweenScrollsTimeout;
Duration dragDuration;

private ScrollParameter() {
}

/**
* 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 ScrollParameter(AppiumBy.FlutterBy scrollTo, ScrollDirection scrollDirection) {
Preconditions.checkArgument(scrollTo != null, "Must supply a valid locator for scrollTo");
this.scrollTo = scrollTo;
this.scrollDirection = scrollDirection;
}

@Override
public Map<String, Object> 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()
);
}

@Getter
public static enum ScrollDirection {
UP("up"),
RIGHT("right"),
DOWN("down"),
LEFT("left");

private final String direction;

ScrollDirection(String direction) {
this.direction = direction;
}
}
}
Original file line number Diff line number Diff line change
@@ -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 WaitParameter extends FlutterCommandParameter {
private WebElement element;
private AppiumBy.FlutterBy locator;
private Duration timeout;

@Override
public Map<String, Object> toJson() {
Map<String, Object> 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);
}
}
Loading