Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add flutter driver commands to support camera mocking #2207

Merged
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.appium.java_client.ios.options.XCUITestOptions;
import io.appium.java_client.service.local.AppiumDriverLocalService;
import io.appium.java_client.service.local.AppiumServiceBuilder;
import io.appium.java_client.service.local.flags.GeneralServerFlag;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
Expand Down Expand Up @@ -43,6 +44,10 @@ public static void beforeClass() {
service = new AppiumServiceBuilder()
.withIPAddress("127.0.0.1")
.usingPort(PORT)
// Flutter driver mocking command requires adb_shell permission to set certain permissions
// to the AUT. This can be removed once the server logic is updated to use a different approach
// for setting the permission
.withArgument(GeneralServerFlag.ALLOW_INSECURE, "adb_shell")
.build();
service.start();
}
Expand All @@ -52,11 +57,14 @@ void startSession() throws MalformedURLException {
FlutterDriverOptions flutterOptions = new FlutterDriverOptions()
.setFlutterServerLaunchTimeout(Duration.ofMinutes(2))
.setFlutterSystemPort(9999)
.setFlutterElementWaitTimeout(Duration.ofSeconds(10));
.setFlutterElementWaitTimeout(Duration.ofSeconds(10))
.setFlutterEnableMockCamera(true);

if (IS_ANDROID) {
driver = new FlutterAndroidDriver(service.getUrl(), flutterOptions
.setUiAutomator2Options(new UiAutomator2Options()
.setApp(System.getProperty("flutterApp"))
.setAutoGrantPermissions(true)
.eventTimings())
);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.appium.java_client.android;

import io.appium.java_client.AppiumBy;
import io.appium.java_client.TestUtils;
import io.appium.java_client.flutter.commands.DoubleClickParameter;
import io.appium.java_client.flutter.commands.DragAndDropParameter;
import io.appium.java_client.flutter.commands.LongPressParameter;
Expand All @@ -10,6 +11,8 @@
import org.openqa.selenium.Point;
import org.openqa.selenium.WebElement;

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -115,4 +118,25 @@ void testDragAndDropCommand() {
assertEquals(driver.findElement(AppiumBy.flutterText("The box is dropped")).getText(), "The box is dropped");

}

@Test
void testCameraMocking() throws IOException {
driver.findElement(BaseFlutterTest.LOGIN_BUTTON).click();
openScreen("Image Picker");

final String successQr = driver.injectMockImage(
sudharsan-selvaraj marked this conversation as resolved.
Show resolved Hide resolved
TestUtils.resourcePathToAbsolutePath("success_qr.png").toFile());
driver.injectMockImage(
TestUtils.resourcePathToAbsolutePath("second_qr.png").toFile());

driver.findElement(AppiumBy.flutterKey("capture_image")).click();
driver.findElement(AppiumBy.flutterText("PICK")).click();
assertTrue(driver.findElement(AppiumBy.flutterText("SecondInjectedImage")).isDisplayed());

driver.activateInjectedImage(successQr);

driver.findElement(AppiumBy.flutterKey("capture_image")).click();
driver.findElement(AppiumBy.flutterText("PICK")).click();
assertTrue(driver.findElement(AppiumBy.flutterText("Success!")).isDisplayed());
}
}
Binary file added src/e2eFlutterTest/resources/second_qr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/e2eFlutterTest/resources/success_qr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import io.appium.java_client.flutter.commands.FlutterCommandParameter;
import org.openqa.selenium.JavascriptExecutor;

import java.util.Map;

public interface CanExecuteFlutterScripts extends JavascriptExecutor {

/**
Expand All @@ -13,8 +15,19 @@ public interface CanExecuteFlutterScripts extends JavascriptExecutor {
* @return The result of executing the script.
*/
default Object executeFlutterCommand(String scriptName, FlutterCommandParameter parameter) {
return executeFlutterCommand(scriptName, parameter.toJson());
}

/**
* Executes a Flutter-specific script using JavascriptExecutor.
*
* @param scriptName The name of the Flutter script to execute.
* @param args The args for the Flutter command in Map format.
* @return The result of executing the script.
*/
default Object executeFlutterCommand(String scriptName, Map<String, Object> args) {
String commandName = String.format("flutter: %s", scriptName);
return executeScript(commandName, parameter.toJson());
return executeScript(commandName, args);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.appium.java_client.android.options.UiAutomator2Options;
import io.appium.java_client.flutter.options.SupportsFlutterElementWaitTimeoutOption;
import io.appium.java_client.flutter.options.SupportsFlutterEnableMockCamera;
import io.appium.java_client.flutter.options.SupportsFlutterServerLaunchTimeoutOption;
import io.appium.java_client.flutter.options.SupportsFlutterSystemPortOption;
import io.appium.java_client.ios.options.XCUITestOptions;
Expand All @@ -17,7 +18,8 @@
public class FlutterDriverOptions extends BaseOptions<FlutterDriverOptions> implements
SupportsFlutterSystemPortOption<FlutterDriverOptions>,
SupportsFlutterServerLaunchTimeoutOption<FlutterDriverOptions>,
SupportsFlutterElementWaitTimeoutOption<FlutterDriverOptions> {
SupportsFlutterElementWaitTimeoutOption<FlutterDriverOptions>,
SupportsFlutterEnableMockCamera<FlutterDriverOptions> {

public FlutterDriverOptions() {
setDefaultOptions();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ public interface FlutterIntegrationTestDriver extends
WebDriver,
SupportsGestureOnFlutterElements,
SupportsScrollingOfFlutterElements,
SupportsWaitingForFlutterElements {
SupportsWaitingForFlutterElements,
SupportsFlutterCameraMocking {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.appium.java_client.flutter;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Base64;
import java.util.Map;

/**
* This interface extends {@link CanExecuteFlutterScripts} and provides methods
* to support mocking of camera inputs in Flutter applications.
*/
public interface SupportsFlutterCameraMocking extends CanExecuteFlutterScripts {

/**
* Injects a mock image into the Flutter application using the provided file.
*
* @param image the image file to be mocked (must be in PNG format)
* @return an {@code String} representing a unique id of the injected image
* @throws IOException if an I/O error occurs while reading the image file
*/
default String injectMockImage(File image) throws IOException {
String base64EncodedImage = Base64.getEncoder().encodeToString(Files.readAllBytes(image.toPath()));
return injectMockImage(base64EncodedImage);
}

/**
* Injects a mock image into the Flutter application using the provided Base64-encoded image string.
*
* @param base64Image the Base64-encoded string representation of the image (must be in PNG format)
* @return an {@code String} representing the result of the injection operation
*/
default String injectMockImage(String base64Image) {
return (String) executeFlutterCommand("injectImage", Map.of(
"base64Image", base64Image
));
}

/**
* Activates the injected image identified by the specified image ID in the Flutter application.
*
* @param imageId the ID of the injected image to activate
*/
default void activateInjectedImage(String imageId) {
executeFlutterCommand("activateInjectedImage", Map.of("imageId", imageId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.appium.java_client.flutter.options;

import io.appium.java_client.remote.options.BaseOptions;
import io.appium.java_client.remote.options.CanSetCapability;
import org.openqa.selenium.Capabilities;

import java.util.Optional;

import static io.appium.java_client.internal.CapabilityHelpers.toSafeBoolean;

public interface SupportsFlutterEnableMockCamera<T extends BaseOptions<T>> extends
Capabilities, CanSetCapability<T> {
String FLUTTER_ENABLE_MOCK_CAMERA_OPTION = "flutterEnableMockCamera";

/**
* Sets the 'flutterEnableMockCamera' capability to the specified value.
*
* @param value the value to set for the 'flutterEnableMockCamera' capability
* @return an instance of type {@code T} with the updated capability set
*/
default T setFlutterEnableMockCamera(boolean value) {
return amend(FLUTTER_ENABLE_MOCK_CAMERA_OPTION, value);
}

/**
* Retrieves the current value of the 'flutterEnableMockCamera' capability, if available.
*
* @return an {@code Optional<Boolean>} containing the current value of the capability,
*/
default Optional<Boolean> doesFlutterEnableMockCamera() {
return Optional.ofNullable(toSafeBoolean(getCapability(FLUTTER_ENABLE_MOCK_CAMERA_OPTION)));
}
}
Loading