diff --git a/src/main/java/io/appium/java_client/android/options/BaseMapOptionData.java b/src/main/java/io/appium/java_client/android/options/BaseMapOptionData.java new file mode 100644 index 000000000..ce473a607 --- /dev/null +++ b/src/main/java/io/appium/java_client/android/options/BaseMapOptionData.java @@ -0,0 +1,77 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.android.options; + +import com.google.gson.GsonBuilder; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public abstract class BaseMapOptionData> { + protected Map options; + + public BaseMapOptionData() { + } + + public BaseMapOptionData(Map options) { + this.options = options; + } + + /** + * Sets the given value on the data object. + * + * @param key Key name. + * @param value The actual value to set. + * @return self instance for chaining. + */ + public T assignOptionValue(String key, Object value) { + if (options == null) { + options = new HashMap<>(); + } + options.put(key, value); + //noinspection unchecked + return (T) this; + } + + /** + * Retrieves a value with the given name from a data object. + * This method does not perform any type transformation, but rather + * just tries to cast the received value to the given type, so + * be careful while providing a very specific result type value to + * not get a type cast error. + * + * @param name Key name. + * @param The expected value type. + * @return The actual value. + */ + public Optional getOptionValue(String name) { + //noinspection unchecked + return Optional.ofNullable(options) + .map(opts -> (R) opts.getOrDefault(name, null)); + } + + public Map toMap() { + return Optional.ofNullable(options).orElseGet(Collections::emptyMap); + } + + @Override + public String toString() { + return new GsonBuilder().create().toJson(toMap()); + } +} diff --git a/src/main/java/io/appium/java_client/android/options/EspressoOptions.java b/src/main/java/io/appium/java_client/android/options/EspressoOptions.java new file mode 100644 index 000000000..6d3df918d --- /dev/null +++ b/src/main/java/io/appium/java_client/android/options/EspressoOptions.java @@ -0,0 +1,211 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.android.options; + +import io.appium.java_client.android.options.adb.SupportsAdbExecTimeoutOption; +import io.appium.java_client.android.options.adb.SupportsAdbPortOption; +import io.appium.java_client.android.options.adb.SupportsAllowDelayAdbOption; +import io.appium.java_client.android.options.adb.SupportsBuildToolsVersionOption; +import io.appium.java_client.android.options.adb.SupportsClearDeviceLogsOnStartOption; +import io.appium.java_client.android.options.adb.SupportsIgnoreHiddenApiPolicyErrorOption; +import io.appium.java_client.android.options.adb.SupportsLogcatFilterSpecsOption; +import io.appium.java_client.android.options.adb.SupportsLogcatFormatOption; +import io.appium.java_client.android.options.adb.SupportsMockLocationAppOption; +import io.appium.java_client.android.options.adb.SupportsRemoteAdbHostOption; +import io.appium.java_client.android.options.adb.SupportsSkipLogcatCaptureOption; +import io.appium.java_client.android.options.adb.SupportsSuppressKillServerOption; +import io.appium.java_client.android.options.app.SupportsActivityOptionsOption; +import io.appium.java_client.android.options.app.SupportsAllowTestPackagesOption; +import io.appium.java_client.android.options.app.SupportsAndroidInstallTimeoutOption; +import io.appium.java_client.android.options.app.SupportsAppActivityOption; +import io.appium.java_client.android.options.app.SupportsIntentOptionsOption; +import io.appium.java_client.android.options.app.SupportsAppWaitDurationOption; +import io.appium.java_client.android.options.app.SupportsAppPackageOption; +import io.appium.java_client.android.options.app.SupportsAppWaitActivityOption; +import io.appium.java_client.android.options.app.SupportsAppWaitPackageOption; +import io.appium.java_client.android.options.app.SupportsAutoGrantPermissionsOption; +import io.appium.java_client.android.options.app.SupportsEnforceAppInstallOption; +import io.appium.java_client.android.options.app.SupportsRemoteAppsCacheLimitOption; +import io.appium.java_client.android.options.app.SupportsUninstallOtherPackagesOption; +import io.appium.java_client.android.options.avd.SupportsAvdArgsOption; +import io.appium.java_client.android.options.avd.SupportsAvdEnvOption; +import io.appium.java_client.android.options.avd.SupportsAvdLaunchTimeoutOption; +import io.appium.java_client.android.options.avd.SupportsAvdOption; +import io.appium.java_client.android.options.avd.SupportsAvdReadyTimeoutOption; +import io.appium.java_client.android.options.avd.SupportsGpsEnabledOption; +import io.appium.java_client.android.options.avd.SupportsNetworkSpeedOption; +import io.appium.java_client.android.options.context.SupportsAutoWebviewTimeoutOption; +import io.appium.java_client.android.options.context.SupportsChromeLoggingPrefsOption; +import io.appium.java_client.android.options.context.SupportsChromeOptionsOption; +import io.appium.java_client.android.options.context.SupportsChromedriverArgsOption; +import io.appium.java_client.android.options.context.SupportsChromedriverChromeMappingFileOption; +import io.appium.java_client.android.options.context.SupportsChromedriverDisableBuildCheckOption; +import io.appium.java_client.android.options.context.SupportsChromedriverExecutableDirOption; +import io.appium.java_client.android.options.context.SupportsChromedriverExecutableOption; +import io.appium.java_client.android.options.context.SupportsChromedriverPortOption; +import io.appium.java_client.android.options.context.SupportsChromedriverPortsOption; +import io.appium.java_client.android.options.context.SupportsChromedriverUseSystemExecutableOption; +import io.appium.java_client.android.options.context.SupportsEnsureWebviewsHavePagesOption; +import io.appium.java_client.android.options.context.SupportsExtractChromeAndroidPackageFromContextNameOption; +import io.appium.java_client.android.options.context.SupportsNativeWebScreenshotOption; +import io.appium.java_client.android.options.context.SupportsRecreateChromeDriverSessionsOption; +import io.appium.java_client.android.options.context.SupportsShowChromedriverLogOption; +import io.appium.java_client.android.options.context.SupportsWebviewDevtoolsPortOption; +import io.appium.java_client.android.options.localization.SupportsAppLocaleOption; +import io.appium.java_client.android.options.localization.SupportsLocaleScriptOption; +import io.appium.java_client.android.options.locking.SupportsSkipUnlockOption; +import io.appium.java_client.android.options.locking.SupportsUnlockKeyOption; +import io.appium.java_client.android.options.locking.SupportsUnlockStrategyOption; +import io.appium.java_client.android.options.locking.SupportsUnlockSuccessTimeoutOption; +import io.appium.java_client.android.options.locking.SupportsUnlockTypeOption; +import io.appium.java_client.android.options.mjpeg.SupportsMjpegScreenshotUrlOption; +import io.appium.java_client.android.options.mjpeg.SupportsMjpegServerPortOption; +import io.appium.java_client.android.options.other.SupportsDisableSuppressAccessibilityServiceOption; +import io.appium.java_client.android.options.server.SupportsEspressoServerLaunchTimeoutOption; +import io.appium.java_client.android.options.server.SupportsForceEspressoRebuildOption; +import io.appium.java_client.android.options.server.SupportsShowGradleLogOption; +import io.appium.java_client.android.options.server.SupportsSkipServerInstallationOption; +import io.appium.java_client.android.options.server.SupportsSystemPortOption; +import io.appium.java_client.android.options.signing.SupportsKeystoreOptions; +import io.appium.java_client.android.options.signing.SupportsNoSignOption; +import io.appium.java_client.remote.AutomationName; +import io.appium.java_client.remote.MobilePlatform; +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.SupportsAppOption; +import io.appium.java_client.remote.options.SupportsAutoWebViewOption; +import io.appium.java_client.remote.options.SupportsDeviceNameOption; +import io.appium.java_client.remote.options.SupportsIsHeadlessOption; +import io.appium.java_client.remote.options.SupportsLanguageOption; +import io.appium.java_client.remote.options.SupportsLocaleOption; +import io.appium.java_client.remote.options.SupportsOrientationOption; +import io.appium.java_client.remote.options.SupportsOtherAppsOption; +import io.appium.java_client.remote.options.SupportsSkipLogCaptureOption; +import io.appium.java_client.remote.options.SupportsUdidOption; +import org.openqa.selenium.Capabilities; + +import java.util.Map; + +/** + * https://github.com/appium/appium-espresso-driver#capabilities + */ +public class EspressoOptions extends BaseOptions implements + // General options: https://github.com/appium/appium-uiautomator2-driver#general + SupportsDeviceNameOption, + SupportsUdidOption, + // Driver/Server options: https://github.com/appium/appium-uiautomator2-driver#driverserver + SupportsSystemPortOption, + SupportsSkipServerInstallationOption, + SupportsEspressoServerLaunchTimeoutOption, + SupportsForceEspressoRebuildOption, + SupportsShowGradleLogOption, + SupportsOrientationOption, + // App options: https://github.com/appium/appium-uiautomator2-driver#app + SupportsAppOption, + SupportsAppPackageOption, + SupportsAppActivityOption, + SupportsAppWaitActivityOption, + SupportsAppWaitPackageOption, + SupportsAppWaitDurationOption, + SupportsAndroidInstallTimeoutOption, + SupportsIntentOptionsOption, + SupportsActivityOptionsOption, + SupportsAutoGrantPermissionsOption, + SupportsOtherAppsOption, + SupportsUninstallOtherPackagesOption, + SupportsAllowTestPackagesOption, + SupportsRemoteAppsCacheLimitOption, + SupportsEnforceAppInstallOption, + // App localization options: https://github.com/appium/appium-uiautomator2-driver#app-localization + SupportsLocaleScriptOption, + SupportsLanguageOption, + SupportsLocaleOption, + SupportsAppLocaleOption, + // ADB options: https://github.com/appium/appium-uiautomator2-driver#adb + SupportsAdbPortOption, + SupportsRemoteAdbHostOption, + SupportsAdbExecTimeoutOption, + SupportsClearDeviceLogsOnStartOption, + SupportsBuildToolsVersionOption, + SupportsSkipLogcatCaptureOption, + SupportsSuppressKillServerOption, + SupportsIgnoreHiddenApiPolicyErrorOption, + SupportsMockLocationAppOption, + SupportsLogcatFormatOption, + SupportsLogcatFilterSpecsOption, + SupportsAllowDelayAdbOption, + // AVD options: https://github.com/appium/appium-uiautomator2-driver#emulator-android-virtual-device + SupportsAvdOption, + SupportsAvdLaunchTimeoutOption, + SupportsAvdReadyTimeoutOption, + SupportsAvdArgsOption, + SupportsAvdEnvOption, + SupportsNetworkSpeedOption, + SupportsGpsEnabledOption, + SupportsIsHeadlessOption, + // App signing options: https://github.com/appium/appium-uiautomator2-driver#app-signing + SupportsKeystoreOptions, + SupportsNoSignOption, + // Device locking options: https://github.com/appium/appium-uiautomator2-driver#device-locking + SupportsSkipUnlockOption, + SupportsUnlockTypeOption, + SupportsUnlockKeyOption, + SupportsUnlockStrategyOption, + SupportsUnlockSuccessTimeoutOption, + // MJPEG options: https://github.com/appium/appium-uiautomator2-driver#mjpeg + SupportsMjpegServerPortOption, + SupportsMjpegScreenshotUrlOption, + // Web Context options: https://github.com/appium/appium-uiautomator2-driver#web-context + SupportsAutoWebViewOption, + SupportsWebviewDevtoolsPortOption, + SupportsEnsureWebviewsHavePagesOption, + SupportsChromedriverPortOption, + SupportsChromedriverPortsOption, + SupportsChromedriverArgsOption, + SupportsChromedriverExecutableOption, + SupportsChromedriverExecutableDirOption, + SupportsChromedriverChromeMappingFileOption, + SupportsChromedriverUseSystemExecutableOption, + SupportsChromedriverDisableBuildCheckOption, + SupportsAutoWebviewTimeoutOption, + SupportsRecreateChromeDriverSessionsOption, + SupportsNativeWebScreenshotOption, + SupportsExtractChromeAndroidPackageFromContextNameOption, + SupportsShowChromedriverLogOption, + SupportsChromeOptionsOption, + SupportsChromeLoggingPrefsOption, + // Other options: https://github.com/appium/appium-uiautomator2-driver#other + SupportsDisableSuppressAccessibilityServiceOption, + SupportsSkipLogCaptureOption { + public EspressoOptions() { + setCommonOptions(); + } + + public EspressoOptions(Capabilities source) { + super(source); + setCommonOptions(); + } + + public EspressoOptions(Map source) { + super(source); + setCommonOptions(); + } + + private void setCommonOptions() { + setPlatformName(MobilePlatform.ANDROID); + setAutomationName(AutomationName.ESPRESSO); + } +} diff --git a/src/main/java/io/appium/java_client/android/options/UiAutomator2Options.java b/src/main/java/io/appium/java_client/android/options/UiAutomator2Options.java index dc4b7a74c..14eddf9eb 100644 --- a/src/main/java/io/appium/java_client/android/options/UiAutomator2Options.java +++ b/src/main/java/io/appium/java_client/android/options/UiAutomator2Options.java @@ -31,7 +31,7 @@ import io.appium.java_client.android.options.app.SupportsAllowTestPackagesOption; import io.appium.java_client.android.options.app.SupportsAndroidInstallTimeoutOption; import io.appium.java_client.android.options.app.SupportsAppActivityOption; -import io.appium.java_client.android.options.app.SupportsAppDurationOption; +import io.appium.java_client.android.options.app.SupportsAppWaitDurationOption; import io.appium.java_client.android.options.app.SupportsAppPackageOption; import io.appium.java_client.android.options.app.SupportsAppWaitActivityOption; import io.appium.java_client.android.options.app.SupportsAppWaitForLaunchOption; @@ -130,7 +130,7 @@ public class UiAutomator2Options extends BaseOptions implem SupportsAppActivityOption, SupportsAppWaitActivityOption, SupportsAppWaitPackageOption, - SupportsAppDurationOption, + SupportsAppWaitDurationOption, SupportsAndroidInstallTimeoutOption, SupportsAppWaitForLaunchOption, SupportsIntentCategoryOption, diff --git a/src/main/java/io/appium/java_client/android/options/app/ActivityOptions.java b/src/main/java/io/appium/java_client/android/options/app/ActivityOptions.java new file mode 100644 index 000000000..e0cc553a0 --- /dev/null +++ b/src/main/java/io/appium/java_client/android/options/app/ActivityOptions.java @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.android.options.app; + +import io.appium.java_client.android.options.BaseMapOptionData; + +import java.util.Map; +import java.util.Optional; + +public class ActivityOptions extends BaseMapOptionData { + public ActivityOptions() { + super(); + } + + public ActivityOptions(Map options) { + super(options); + } + + /** + * Display id which you want to assign to launch the main app activity on. + * This might be useful if the device under test supports multiple displays. + * + * @param id Display identifier. + * @return self instance for chaining. + */ + public ActivityOptions withLaunchDisplayId(int id) { + return assignOptionValue("launchDisplayId", id); + } + + /** + * Get display id which you want to assign to launch the main app activity on. + * + * @return Display identifier. + */ + public Optional getLaunchDisplayId() { + Optional result = getOptionValue("launchDisplayId"); + return result.map((v) -> Integer.parseInt(String.valueOf(v))); + } +} diff --git a/src/main/java/io/appium/java_client/android/options/app/IntentOptions.java b/src/main/java/io/appium/java_client/android/options/app/IntentOptions.java new file mode 100644 index 000000000..2ba619aac --- /dev/null +++ b/src/main/java/io/appium/java_client/android/options/app/IntentOptions.java @@ -0,0 +1,421 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.android.options.app; + +import io.appium.java_client.android.options.BaseMapOptionData; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class IntentOptions extends BaseMapOptionData { + public IntentOptions() { + super(); + } + + public IntentOptions(Map options) { + super(options); + } + + /** + * An intent action name. Application-specific actions should be prefixed with + * the vendor's package name. + * + * @param action E.g. ACTION_MAIN. + * @return self instance for chaining. + */ + public IntentOptions withAction(String action) { + return assignOptionValue("action", action); + } + + /** + * Get the action name. + * + * @return Action name. + */ + public Optional getAction() { + return getOptionValue("action"); + } + + /** + * Set an intent data URI. + * + * @param data E.g. content://contacts/people/1. + * @return self instance for chaining. + */ + public IntentOptions withData(String data) { + return assignOptionValue("data", data); + } + + /** + * Get intent data URI. + * + * @return Intent data URI. + */ + public Optional getData() { + return getOptionValue("data"); + } + + /** + * Intent MIME type. + * + * @param type E.g. image/png. + * @return self instance for chaining. + */ + public IntentOptions withType(String type) { + return assignOptionValue("type", type); + } + + /** + * Get an intent type. + * + * @return Intent type. + */ + public Optional getType() { + return getOptionValue("type"); + } + + /** + * Set intent categories. + * + * @param categories One or more comma-separated Intent categories. + * @return self instance for chaining. + */ + public IntentOptions withCategories(String categories) { + return assignOptionValue("categories", categories); + } + + /** + * Get intent categories. + * + * @return Intent categories. + */ + public Optional getCategories() { + return getOptionValue("categories"); + } + + /** + * Set intent component name with package name prefix + * to create an explicit intent. + * + * @param component E.g. com.example.app/.ExampleActivity. + * @return self instance for chaining. + */ + public IntentOptions withComponent(String component) { + return assignOptionValue("component", component); + } + + /** + * Get intent component name. + * + * @return Intent component name. + */ + public Optional getComponent() { + return getOptionValue("component"); + } + + /** + * Single-string value, which represents intent flags set encoded into + * an integer. Could also be provided in hexadecimal format. Check + * https://developer.android.com/reference/android/content/Intent.html#setFlags(int) + * for more details. + * + * @param intFlags E.g. 0x0F. + * @return self instance for chaining. + */ + public IntentOptions withIntFlags(String intFlags) { + return assignOptionValue("intFlags", intFlags); + } + + /** + * Get intent flags. + * + * @return Intent flags encoded into a hexadecimal value. + */ + public Optional getIntFlags() { + return getOptionValue("intFlags"); + } + + /** + * Comma-separated string of intent flag names. + * + * @param flags E.g. 'ACTIVITY_CLEAR_TASK' (the 'FLAG_' prefix is optional). + * @return self instance for chaining. + */ + public IntentOptions withFlags(String flags) { + return assignOptionValue("flags", flags); + } + + /** + * Get intent flag names. + * + * @return Comma-separated string of intent flag names. + */ + public Optional getFlags() { + return getOptionValue("flags"); + } + + /** + * The name of a class inside of the application package that + * will be used as the component for this Intent. + * + * @param className E.g. com.example.app.MainActivity. + * @return self instance for chaining. + */ + public IntentOptions withClassName(String className) { + return assignOptionValue("className", className); + } + + /** + * Get class name. + * + * @return Class name. + */ + public Optional getClassName() { + return getOptionValue("className"); + } + + /** + * Intent string parameters. + * + * @param es Map, where the key is arg parameter name and value is its string value. + * @return self instance for chaining. + */ + public IntentOptions withEs(Map es) { + return assignOptionValue("es", es); + } + + /** + * Get intent string parameters. + * + * @return Intent string parameters mapping. + */ + public Optional> getEs() { + return getOptionValue("es"); + } + + /** + * Intent null parameters. + * + * @param esn List, where keys are parameter names. + * @return self instance for chaining. + */ + public IntentOptions withEsn(List esn) { + return assignOptionValue("esn", esn); + } + + /** + * Get intent null parameters. + * + * @return Intent null parameters. + */ + public Optional> getEsn() { + return getOptionValue("esn"); + } + + /** + * Intent boolean parameters. + * + * @param ez Map, where keys are parameter names and values are booleans. + * @return self instance for chaining. + */ + public IntentOptions withEz(Map ez) { + return assignOptionValue("ez", ez); + } + + /** + * Get intent boolean parameters. + * + * @return Intent boolean parameters. + */ + public Optional> getEz() { + return getOptionValue("ez"); + } + + /** + * Intent integer parameters. + * + * @param ei Map, where keys are parameter names and values are integers. + * @return self instance for chaining. + */ + public IntentOptions withEi(Map ei) { + return assignOptionValue("ei", ei); + } + + private Map convertMapValues(Map map, Function converter) { + return map.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, (entry) -> converter.apply(String.valueOf(entry.getValue()))) + ); + } + + /** + * Get intent integer parameters. + * + * @return Intent integer parameters. + */ + public Optional> getEi() { + Optional> value = getOptionValue("ei"); + return value.map((v) -> convertMapValues(v, Integer::parseInt)); + } + + /** + * Intent long parameters. + * + * @param el Map, where keys are parameter names and values are long numbers. + * @return self instance for chaining. + */ + public IntentOptions withEl(Map el) { + return assignOptionValue("el", el); + } + + /** + * Get intent long parameters. + * + * @return Intent long parameters. + */ + public Optional> getEl() { + Optional> value = getOptionValue("el"); + return value.map((v) -> convertMapValues(v, Long::parseLong)); + } + + /** + * Intent float parameters. + * + * @param ef Map, where keys are parameter names and values are float numbers. + * @return self instance for chaining. + */ + public IntentOptions withEf(Map ef) { + return assignOptionValue("ef", ef); + } + + /** + * Get intent float parameters. + * + * @return Intent float parameters. + */ + public Optional> getEf() { + Optional> value = getOptionValue("ef"); + return value.map((v) -> convertMapValues(v, Float::parseFloat)); + } + + /** + * Intent URI-data parameters. + * + * @param eu Map, where keys are parameter names and values are valid URIs. + * @return self instance for chaining. + */ + public IntentOptions withEu(Map eu) { + return assignOptionValue("eu", eu); + } + + /** + * Get intent URI parameters. + * + * @return Intent URI parameters. + */ + public Optional> getEu() { + return getOptionValue("eu"); + } + + /** + * Intent component name parameters. + * + * @param ecn Map, where keys are parameter names and values are valid component names. + * @return self instance for chaining. + */ + public IntentOptions withEcn(Map ecn) { + return assignOptionValue("ecn", ecn); + } + + /** + * Get intent component name parameters. + * + * @return Intent component name parameters. + */ + public Optional> getEcn() { + return getOptionValue("ecn"); + } + + private static Map mergeValues(Map map) { + return map.entrySet().stream() + .collect( + Collectors.toMap(Map.Entry::getKey, (entry) -> ((List) entry.getValue()).stream() + .map(String::valueOf) + .collect(Collectors.joining(","))) + ); + } + + /** + * Intent integer array parameters. + * + * @param eia Map, where keys are parameter names and values are lists of integers. + * @return self instance for chaining. + */ + public IntentOptions withEia(Map> eia) { + return assignOptionValue("eia", mergeValues(eia)); + } + + /** + * Get intent integer array parameters. + * + * @return Intent integer array parameters. + */ + public Optional> getEia() { + return getOptionValue("eia"); + } + + /** + * Intent long array parameters. + * + * @param ela Map, where keys are parameter names and values are lists of long numbers. + * @return self instance for chaining. + */ + public IntentOptions withEla(Map> ela) { + return assignOptionValue("ela", mergeValues(ela)); + } + + /** + * Get intent long array parameters. + * + * @return Intent long array parameters. + */ + public Optional> getEla() { + return getOptionValue("ela"); + } + + /** + * Intent float array parameters. + * + * @param efa Map, where keys are parameter names and values are lists of float numbers. + * @return self instance for chaining. + */ + public IntentOptions withEfa(Map> efa) { + return assignOptionValue("efa", mergeValues(efa)); + } + + /** + * Get intent float array parameters. + * + * @return Intent float array parameters. + */ + public Optional> getEfa() { + return getOptionValue("efa"); + } +} diff --git a/src/main/java/io/appium/java_client/android/options/app/SupportsActivityOptionsOption.java b/src/main/java/io/appium/java_client/android/options/app/SupportsActivityOptionsOption.java new file mode 100644 index 000000000..393ee51c5 --- /dev/null +++ b/src/main/java/io/appium/java_client/android/options/app/SupportsActivityOptionsOption.java @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.android.options.app; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Map; +import java.util.Optional; + +public interface SupportsActivityOptionsOption> extends + Capabilities, CanSetCapability { + String ACTIVITY_OPTIONS_OPTION = "activityOptions"; + + /** + * The mapping of custom options for the main app activity that is going to + * be started. Check + * https://github.com/appium/appium-espresso-driver#activity-options + * for more details. + * + * @param options Activity options. + * @return self instance for chaining. + */ + default T setActivityOptions(ActivityOptions options) { + return amend(ACTIVITY_OPTIONS_OPTION, options.toMap()); + } + + /** + * Get activity options. + * + * @return Activity options. + */ + default Optional getActivityOptions() { + //noinspection unchecked + return Optional.ofNullable(getCapability(ACTIVITY_OPTIONS_OPTION)) + .map((v) -> new ActivityOptions((Map) v)); + } +} diff --git a/src/main/java/io/appium/java_client/android/options/app/SupportsAppDurationOption.java b/src/main/java/io/appium/java_client/android/options/app/SupportsAppWaitDurationOption.java similarity index 95% rename from src/main/java/io/appium/java_client/android/options/app/SupportsAppDurationOption.java rename to src/main/java/io/appium/java_client/android/options/app/SupportsAppWaitDurationOption.java index 3a3bce7b7..77039f70d 100644 --- a/src/main/java/io/appium/java_client/android/options/app/SupportsAppDurationOption.java +++ b/src/main/java/io/appium/java_client/android/options/app/SupportsAppWaitDurationOption.java @@ -25,7 +25,7 @@ import static io.appium.java_client.internal.CapabilityHelpers.toDuration; -public interface SupportsAppDurationOption> extends +public interface SupportsAppWaitDurationOption> extends Capabilities, CanSetCapability { String APP_WAIT_DURATION_OPTION = "appWaitDuration"; diff --git a/src/main/java/io/appium/java_client/android/options/app/SupportsIntentOptionsOption.java b/src/main/java/io/appium/java_client/android/options/app/SupportsIntentOptionsOption.java new file mode 100644 index 000000000..91c2b49a2 --- /dev/null +++ b/src/main/java/io/appium/java_client/android/options/app/SupportsIntentOptionsOption.java @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.android.options.app; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Map; +import java.util.Optional; + +public interface SupportsIntentOptionsOption> extends + Capabilities, CanSetCapability { + String INTENT_OPTIONS_OPTION = "intentOptions"; + + /** + * The mapping of custom options for the intent that is going to be passed + * to the main app activity. Check + * https://github.com/appium/appium-espresso-driver#intent-options + * for more details. + * + * @param options Intent options. + * @return self instance for chaining. + */ + default T setIntentOptions(IntentOptions options) { + return amend(INTENT_OPTIONS_OPTION, options.toMap()); + } + + /** + * Get intent options. + * + * @return Intent options. + */ + default Optional getIntentOptions() { + //noinspection unchecked + return Optional.ofNullable(getCapability(INTENT_OPTIONS_OPTION)) + .map((v) -> new IntentOptions((Map) v)); + } +} diff --git a/src/main/java/io/appium/java_client/android/options/localization/AppLocale.java b/src/main/java/io/appium/java_client/android/options/localization/AppLocale.java new file mode 100644 index 000000000..d7d0209d0 --- /dev/null +++ b/src/main/java/io/appium/java_client/android/options/localization/AppLocale.java @@ -0,0 +1,93 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.android.options.localization; + +import io.appium.java_client.android.options.BaseMapOptionData; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class AppLocale extends BaseMapOptionData { + public AppLocale() { + super(); + } + + public AppLocale(Map options) { + super(options); + } + + /** + * Language identifier. See + * https://github.com/libyal/libfwnt/wiki/Language-Code-identifiers#language-identifiers + * for the list of available values. + * + * @param lang Language identifier, for example "zh". + * @return self instance for chaining. + */ + public AppLocale withLanguage(String lang) { + return assignOptionValue("language", lang); + } + + /** + * Get the language identifier. + * + * @return Language identifier. + */ + public Optional getLanguage() { + return getOptionValue("language"); + } + + /** + * Allows to set a country identifier. + * + * @param country Country identifier, for example "CN". + * @return self instance for chaining. + */ + public AppLocale withCountry(String country) { + return assignOptionValue("country", country); + } + + /** + * Get the country identifier. + * + * @return Country identifier. + */ + public Optional getCountry() { + return getOptionValue("country"); + } + + /** + * Allows to set an optional language variant value. + * + * @param variant Language variant, for example "Hans". + * @return self instance for chaining. + */ + public AppLocale withVariant(String variant) { + return assignOptionValue("variant", variant); + } + + /** + * Get the language variant. + * + * @return Language variant. + */ + public Optional getVariant() { + return getOptionValue("variant"); + } +} diff --git a/src/main/java/io/appium/java_client/android/options/localization/SupportsAppLocaleOption.java b/src/main/java/io/appium/java_client/android/options/localization/SupportsAppLocaleOption.java new file mode 100644 index 000000000..40447d749 --- /dev/null +++ b/src/main/java/io/appium/java_client/android/options/localization/SupportsAppLocaleOption.java @@ -0,0 +1,56 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.android.options.localization; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Map; +import java.util.Optional; + +public interface SupportsAppLocaleOption> extends + Capabilities, CanSetCapability { + String APP_LOCALE_OPTION = "appLocale"; + + /** + * Sets the locale for the app under test. The main difference between this option + * and the above ones is that this option only changes the locale for the application + * under test and does not affect other parts of the system. Also, it only uses + * public APIs for its purpose. See + * https://github.com/libyal/libfwnt/wiki/Language-Code-identifiers to get the + * list of available language abbreviations. + * Example: {"language": "zh", "country": "CN", "variant": "Hans"}. + * + * @param locale App locale data. + * @return this MobileOptions, for chaining. + */ + default T setAppLocale(AppLocale locale) { + return amend(APP_LOCALE_OPTION, locale.toMap()); + } + + /** + * Get the locale for the app under test. + * + * @return App locale data. + */ + default Optional getAppLocale() { + //noinspection unchecked + return Optional.ofNullable(getCapability(APP_LOCALE_OPTION)) + .map((v) -> new AppLocale((Map) v)); + } +} diff --git a/src/main/java/io/appium/java_client/android/options/server/EspressoBuildConfig.java b/src/main/java/io/appium/java_client/android/options/server/EspressoBuildConfig.java new file mode 100644 index 000000000..28763a79a --- /dev/null +++ b/src/main/java/io/appium/java_client/android/options/server/EspressoBuildConfig.java @@ -0,0 +1,293 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.android.options.server; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class EspressoBuildConfig { + public static final String TOOLS_VERSION = "toolsVersions"; + public static final String ADDITIONAL_APP_DEPENDENCIES = "additionalAppDependencies"; + public static final String ADDITIONAL_ANDROID_TEST_DEPENDENCIES + = "additionalAndroidTestDependencies"; + + private JsonObject json; + + public EspressoBuildConfig() { + } + + public EspressoBuildConfig(JsonObject json) { + this.json = json; + } + + public EspressoBuildConfig(String json) { + this(JsonParser.parseString(json).getAsJsonObject()); + } + + private EspressoBuildConfig assignToolsVersionsField(String name, Object value) { + if (json == null) { + json = new JsonObject(); + } + boolean hasTools = json.has(TOOLS_VERSION); + JsonObject toolsVersions = hasTools + ? json.getAsJsonObject(TOOLS_VERSION) + : new JsonObject(); + if (value instanceof Number) { + toolsVersions.addProperty(name, (Number) value); + } else { + toolsVersions.addProperty(name, String.valueOf(value)); + } + if (!hasTools) { + json.add(TOOLS_VERSION, toolsVersions); + } + return this; + } + + private Optional getToolsVersionsFieldValue(String name) { + //noinspection unchecked + return json == null || !json.has(TOOLS_VERSION) + ? Optional.empty() + : Optional.ofNullable((R) json.getAsJsonObject(TOOLS_VERSION).get(name)); + } + + /** + * Set Gradle version. + * By default, the version from the build.gradle is used. + * + * @param version E.g. "6.3". + * @return self instance for chaining. + */ + public EspressoBuildConfig withGradleVersion(String version) { + return assignToolsVersionsField("gradle", version); + } + + /** + * Get Gradle version. + * + * @return Gradle version. + */ + public Optional getGradleVersion() { + return getToolsVersionsFieldValue("gradle"); + } + + /** + * Set Gradle plugin version. It must correspond to the Gradle version + * (if provided). By default, the version from the build.gradle is used. + * + * @param version E.g. "4.1.1" + * @return self instance for chaining. + */ + public EspressoBuildConfig withAndroidGradlePluginVersion(String version) { + return assignToolsVersionsField("androidGradlePlugin", version); + } + + /** + * Get Gradle plugin version. + * + * @return Gradle plugin version. + */ + public Optional getAndroidGradlePluginVersion() { + return getToolsVersionsFieldValue("androidGradlePlugin"); + } + + /** + * Set Android build tools version to compile the server with. + * By default, the version from the build.gradle is used. + * + * @param version E.g. "28.0.3". + * @return self instance for chaining. + */ + public EspressoBuildConfig withBuildToolsVersion(String version) { + return assignToolsVersionsField("buildTools", version); + } + + /** + * Get Android build tools version. + * + * @return Android build tools version. + */ + public Optional getBuildToolsVersion() { + return getToolsVersionsFieldValue("buildTools"); + } + + /** + * Set Android SDK version to compile the server for. + * By default, the version from the app build.gradle is used. + * + * @param version E.g. "28" + * @return self instance for chaining. + */ + public EspressoBuildConfig withCompileSdkVersion(String version) { + return assignToolsVersionsField("compileSdk", version); + } + + /** + * Get the target Android SDK version. + * + * @return Android SDK version. + */ + public Optional getCompileSdkVersion() { + return getToolsVersionsFieldValue("compileSdk"); + } + + /** + * Set the minimum Android SDK version to compile the server for. + * By default, the version from the app build.gradle is used. + * + * @param apiLevel E.g. 18. + * @return self instance for chaining. + */ + public EspressoBuildConfig withMinSdk(int apiLevel) { + return assignToolsVersionsField("minSdk", apiLevel); + } + + /** + * Get the minimum Android SDK version. + * + * @return Minimum Android SDK version. + */ + public Optional getMinSdkVersion() { + Optional result = getToolsVersionsFieldValue("minSdk"); + return result.map((v) -> Integer.parseInt(String.valueOf(v))); + } + + /** + * Set the target Android SDK version to compile the server for. + * By default, the version from the app build.gradle is used. + * + * @param apiLevel E.g. 28. + * @return self instance for chaining. + */ + public EspressoBuildConfig withTargetSdk(int apiLevel) { + return assignToolsVersionsField("targetSdk", apiLevel); + } + + /** + * Get the target Android SDK version. + * + * @return Target Android SDK version. + */ + public Optional getTargetSdkVersion() { + Optional result = getToolsVersionsFieldValue("targetSdk"); + return result.map((v) -> Integer.parseInt(String.valueOf(v))); + } + + /** + * Kotlin version to compile the server for. + * By default, the version from the build.gradle is used. + * + * @param version E.g. "1.5.10". + * @return self instance for chaining. + */ + public EspressoBuildConfig withKotlinVersion(String version) { + return assignToolsVersionsField("kotlin", version); + } + + /** + * Get the target Kotlin version. + * + * @return Kotlin version. + */ + public Optional getKotlinVersion() { + return getToolsVersionsFieldValue("kotlin"); + } + + private EspressoBuildConfig assignDependenciesField(String name, List value) { + if (json == null) { + json = new JsonObject(); + } + boolean hasField = json.has(name); + JsonArray dependencies = hasField + ? json.getAsJsonArray(name) + : new JsonArray(); + while (dependencies.size() > 0) { + dependencies.remove(0); + } + value.forEach(dependencies::add); + if (!hasField) { + json.add(name, dependencies); + } + return this; + } + + private Optional> getDependenciesValue(String name) { + return json == null + ? Optional.empty() + : Optional.ofNullable(json.getAsJsonArray(name)) + .map((v) -> { + List result = new ArrayList<>(); + v.forEach((x) -> result.add(String.valueOf(x))); + return result; + }); + } + + /** + * Set a non-empty array of dependent module names with their versions. + * The scripts add all these items as "implementation" lines of dependencies + * category in the app build.gradle script. + * + * @param dependencies E.g. ["xerces.xercesImpl:2.8.0", "xerces.xmlParserAPIs:2.6.2"]. + * @return self instance for chaining. + */ + public EspressoBuildConfig withAdditionalAppDependencies(List dependencies) { + return assignDependenciesField(ADDITIONAL_APP_DEPENDENCIES, dependencies); + } + + /** + * Get the array of dependent application module names with their versions. + * + * @return Dependent module names with their versions. + */ + public Optional> getAdditionalAppDependencies() { + return getDependenciesValue(ADDITIONAL_APP_DEPENDENCIES); + } + + /** + * Set a non-empty array of dependent module names with their versions. + * The scripts add all these items as "androidTestImplementation" lines of + * dependencies category in the app build.gradle script. + * + * @param dependencies E.g. ["xerces.xercesImpl:2.8.0", "xerces.xmlParserAPIs:2.6.2"]. + * @return self instance for chaining. + */ + public EspressoBuildConfig withAdditionalAndroidTestDependencies(List dependencies) { + return assignDependenciesField(ADDITIONAL_ANDROID_TEST_DEPENDENCIES, dependencies); + } + + /** + * Get the array of dependent Android test module names with their versions. + * + * @return Dependent module names with their versions. + */ + public Optional> getAdditionalAndroidTestDependencies() { + return getDependenciesValue(ADDITIONAL_ANDROID_TEST_DEPENDENCIES); + } + + public JsonObject toJson() { + return Optional.ofNullable(json).orElseGet(JsonObject::new); + } + + @Override + public String toString() { + return toJson().toString(); + } +} diff --git a/src/main/java/io/appium/java_client/android/options/server/SupportsEspressoBuildConfigOption.java b/src/main/java/io/appium/java_client/android/options/server/SupportsEspressoBuildConfigOption.java new file mode 100644 index 000000000..9444bc9ef --- /dev/null +++ b/src/main/java/io/appium/java_client/android/options/server/SupportsEspressoBuildConfigOption.java @@ -0,0 +1,69 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.android.options.server; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.internal.Either; + +import java.util.Optional; + +public interface SupportsEspressoBuildConfigOption> extends + Capabilities, CanSetCapability { + String ESPRESSO_BUILD_CONFIG_OPTION = "espressoBuildConfig"; + + /** + * This config allows to customize several important properties of + * Espresso server. Refer to + * https://github.com/appium/appium-espresso-driver#espresso-build-config + * for more information on how to properly construct such config. + * + * @param configPath The path to the config file on the server file system. + * @return self instance for chaining. + */ + default T setEspressoBuildConfig(String configPath) { + return amend(ESPRESSO_BUILD_CONFIG_OPTION, configPath); + } + + /** + * This config allows to customize several important properties of + * Espresso server. Refer to + * https://github.com/appium/appium-espresso-driver#espresso-build-config + * for more information on how to properly construct such config. + * + * @param config Config instance. + * @return self instance for chaining. + */ + default T setEspressoBuildConfig(EspressoBuildConfig config) { + return amend(ESPRESSO_BUILD_CONFIG_OPTION, config.toJson().toString()); + } + + /** + * Get the Espresso build config. + * + * @return Either the config itself or a path to a JSON file on the server FS. + */ + default Optional> getEspressoBuildConfig() { + return Optional.ofNullable(getCapability(ESPRESSO_BUILD_CONFIG_OPTION)) + .map(String::valueOf) + .map((v) -> v.trim().startsWith("{") + ? Either.left(new EspressoBuildConfig(v)) + : Either.right(v) + ); + } +} diff --git a/src/main/java/io/appium/java_client/android/options/server/SupportsEspressoServerLaunchTimeoutOption.java b/src/main/java/io/appium/java_client/android/options/server/SupportsEspressoServerLaunchTimeoutOption.java new file mode 100644 index 000000000..41bc66ee5 --- /dev/null +++ b/src/main/java/io/appium/java_client/android/options/server/SupportsEspressoServerLaunchTimeoutOption.java @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.android.options.server; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.time.Duration; +import java.util.Optional; + +import static io.appium.java_client.internal.CapabilityHelpers.toDuration; + +public interface SupportsEspressoServerLaunchTimeoutOption> extends + Capabilities, CanSetCapability { + String ESPRESSO_SERVER_LAUNCH_TIMEOUT_OPTION = "espressoServerLaunchTimeout"; + + /** + * Set the maximum timeout to wait util Espresso is listening on the device. + * 45000 ms by default + * + * @param timeout Timeout value. + * @return self instance for chaining. + */ + default T setUiautomator2ServerInstallTimeout(Duration timeout) { + return amend(ESPRESSO_SERVER_LAUNCH_TIMEOUT_OPTION, timeout.toMillis()); + } + + /** + * Get the maximum timeout to wait until Espresso server is listening on the device. + * + * @return The timeout value. + */ + default Optional getUiautomator2ServerInstallTimeout() { + return Optional.ofNullable( + toDuration(getCapability(ESPRESSO_SERVER_LAUNCH_TIMEOUT_OPTION)) + ); + } +} diff --git a/src/main/java/io/appium/java_client/android/options/server/SupportsForceEspressoRebuildOption.java b/src/main/java/io/appium/java_client/android/options/server/SupportsForceEspressoRebuildOption.java new file mode 100644 index 000000000..843d25ddc --- /dev/null +++ b/src/main/java/io/appium/java_client/android/options/server/SupportsForceEspressoRebuildOption.java @@ -0,0 +1,63 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.android.options.server; + +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 SupportsForceEspressoRebuildOption> extends + Capabilities, CanSetCapability { + String FORCE_ESPRESSO_REBUILD_OPTION = "forceEspressoRebuild"; + + /** + * Enforces Espresso server rebuild on a new session startup. + * + * @return self instance for chaining. + */ + default T forceEspressoRebuild() { + return amend(FORCE_ESPRESSO_REBUILD_OPTION, true); + } + + /** + * Whether to always enforce Espresso server rebuild (true). + * By default, Espresso caches the already built server apk and only rebuilds + * it when it is necessary, because rebuilding process needs extra time. + * false by default. + * + * @param value True to force Espresso server rebuild on a new session startup. + * @return self instance for chaining. + */ + default T setForceEspressoRebuild(boolean value) { + return amend(FORCE_ESPRESSO_REBUILD_OPTION, value); + } + + /** + * Get to force Espresso server rebuild on a new session startup. + * + * @return True or false. + */ + default Optional doesForceEspressoRebuild() { + return Optional.ofNullable( + toSafeBoolean(getCapability(FORCE_ESPRESSO_REBUILD_OPTION)) + ); + } +} diff --git a/src/main/java/io/appium/java_client/android/options/server/SupportsShowGradleLogOption.java b/src/main/java/io/appium/java_client/android/options/server/SupportsShowGradleLogOption.java new file mode 100644 index 000000000..dabd0f0ec --- /dev/null +++ b/src/main/java/io/appium/java_client/android/options/server/SupportsShowGradleLogOption.java @@ -0,0 +1,61 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.android.options.server; + +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 SupportsShowGradleLogOption> extends + Capabilities, CanSetCapability { + String SHOW_GRADLE_LOG_OPTION = "showGradleLog"; + + /** + * Enforces inclusion of the Gradle log to the regular server logs. + * + * @return self instance for chaining. + */ + default T showGradleLog() { + return amend(SHOW_GRADLE_LOG_OPTION, true); + } + + /** + * Whether to include Gradle log to the regular server logs while + * building Espresso server. false by default. + * + * @param value Whether to include Gradle log to the regular server logs. + * @return self instance for chaining. + */ + default T setShowGradleLog(boolean value) { + return amend(SHOW_GRADLE_LOG_OPTION, value); + } + + /** + * Get whether to include Gradle log to the regular server log. + * + * @return True or false. + */ + default Optional doesShowGradleLog() { + return Optional.ofNullable( + toSafeBoolean(getCapability(SHOW_GRADLE_LOG_OPTION)) + ); + } +} diff --git a/src/main/java/io/appium/java_client/android/options/server/SupportsSkipServerInstallationOption.java b/src/main/java/io/appium/java_client/android/options/server/SupportsSkipServerInstallationOption.java index 088f040fc..ad572b3f6 100644 --- a/src/main/java/io/appium/java_client/android/options/server/SupportsSkipServerInstallationOption.java +++ b/src/main/java/io/appium/java_client/android/options/server/SupportsSkipServerInstallationOption.java @@ -29,7 +29,7 @@ public interface SupportsSkipServerInstallationOption> String SKIP_SERVER_INSTALLATION_OPTION = "skipServerInstallation"; /** - * Enables skipping of the UiAutomator2 Server component installation + * Enables skipping of the server components installation * on the device under test and all the related checks. * This could help to speed up the session startup if you know for sure the * correct server version is installed on the device. @@ -43,7 +43,7 @@ default T skipServerInstallation() { } /** - * Set whether to skip the UiAutomator2 Server component installation + * Set whether to skip the server components installation * on the device under test and all the related checks. * This could help to speed up the session startup if you know for sure the * correct server version is installed on the device. @@ -58,7 +58,7 @@ default T setSkipServerInstallation(boolean value) { } /** - * Get whether to skip the UiAutomator2 Server component installation + * Get whether to skip the server components installation * on the device under test and all the related checks. * * @return True or false. diff --git a/src/main/java/io/appium/java_client/android/options/server/SupportsSystemPortOption.java b/src/main/java/io/appium/java_client/android/options/server/SupportsSystemPortOption.java index cf638ff9f..925b8ea3d 100644 --- a/src/main/java/io/appium/java_client/android/options/server/SupportsSystemPortOption.java +++ b/src/main/java/io/appium/java_client/android/options/server/SupportsSystemPortOption.java @@ -29,8 +29,9 @@ public interface SupportsSystemPortOption> extends String SYSTEM_PORT_OPTION = "systemPort"; /** - * The number of the port the UiAutomator2 server is listening on. - * By default, the first free port from 8200..8299 range is selected. + * The number of the port the UiAutomator2 or Espresso server is listening on. + * By default, the first free port from 8200..8299 range is selected for UIA2 + * and 8300..8399 range is selected for Espresso. * It is recommended to set this value if you are running parallel * tests on the same machine. * diff --git a/src/main/java/io/appium/java_client/remote/options/SupportsEventTimingsOption.java b/src/main/java/io/appium/java_client/remote/options/SupportsEventTimingsOption.java index 3d6bd39e6..c961841f8 100644 --- a/src/main/java/io/appium/java_client/remote/options/SupportsEventTimingsOption.java +++ b/src/main/java/io/appium/java_client/remote/options/SupportsEventTimingsOption.java @@ -31,7 +31,7 @@ public interface SupportsEventTimingsOption> extends * * @return self instance for chaining. */ - default T setEventTimings() { + default T eventTimings() { return setEventTimings(true); } diff --git a/src/test/java/io/appium/java_client/android/AndroidViewMatcherTest.java b/src/test/java/io/appium/java_client/android/AndroidViewMatcherTest.java index 2e3b73448..191055553 100644 --- a/src/test/java/io/appium/java_client/android/AndroidViewMatcherTest.java +++ b/src/test/java/io/appium/java_client/android/AndroidViewMatcherTest.java @@ -24,6 +24,8 @@ import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; +import java.time.Duration; + import static org.junit.Assert.assertNotNull; public class AndroidViewMatcherTest extends BaseEspressoTest { @@ -35,7 +37,7 @@ public void testFindByViewMatcher() { "args", ImmutableList.of("Animation"), "class", "androidx.test.espresso.matcher.ViewMatchers" )); - final WebDriverWait wait = new WebDriverWait(driver, 10); + final WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); assertNotNull(wait.until(ExpectedConditions .presenceOfElementLocated(AppiumBy.androidViewMatcher(selector)))); } diff --git a/src/test/java/io/appium/java_client/android/BaseAndroidTest.java b/src/test/java/io/appium/java_client/android/BaseAndroidTest.java index 065794566..997b83a23 100644 --- a/src/test/java/io/appium/java_client/android/BaseAndroidTest.java +++ b/src/test/java/io/appium/java_client/android/BaseAndroidTest.java @@ -44,7 +44,7 @@ public class BaseAndroidTest { UiAutomator2Options options = new UiAutomator2Options() .setDeviceName("Android Emulator") .setApp(apiDemosApk().toAbsolutePath().toString()) - .setEventTimings(); + .eventTimings(); driver = new AndroidDriver(service.getUrl(), options); } diff --git a/src/test/java/io/appium/java_client/android/BaseEspressoTest.java b/src/test/java/io/appium/java_client/android/BaseEspressoTest.java index 6d9cd7671..6375ff58e 100644 --- a/src/test/java/io/appium/java_client/android/BaseEspressoTest.java +++ b/src/test/java/io/appium/java_client/android/BaseEspressoTest.java @@ -16,13 +16,11 @@ package io.appium.java_client.android; -import io.appium.java_client.remote.AutomationName; -import io.appium.java_client.remote.MobileCapabilityType; +import io.appium.java_client.android.options.EspressoOptions; import io.appium.java_client.service.local.AppiumDriverLocalService; import io.appium.java_client.service.local.AppiumServerHasNotBeenStartedLocallyException; import org.junit.AfterClass; import org.junit.BeforeClass; -import org.openqa.selenium.remote.DesiredCapabilities; import static io.appium.java_client.TestResources.apiDemosApk; @@ -43,12 +41,11 @@ public class BaseEspressoTest { "An appium server node is not started!"); } - DesiredCapabilities capabilities = new DesiredCapabilities(); - capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.ESPRESSO); - capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); - capabilities.setCapability(MobileCapabilityType.APP, apiDemosApk().toAbsolutePath().toString()); - capabilities.setCapability("eventTimings", true); - driver = new AndroidDriver(service.getUrl(), capabilities); + EspressoOptions options = new EspressoOptions() + .setDeviceName("Android Emulator") + .setApp(apiDemosApk().toAbsolutePath().toString()) + .eventTimings(); + driver = new AndroidDriver(service.getUrl(), options); } /**