diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3f0b0dd1c..67d36f46e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,7 +19,6 @@ variables: jobs: - job: Android_E2E_Tests -# timeoutInMinutes: '90' steps: - template: .azure-templates/bootstrap_steps.yml - script: | @@ -61,3 +60,15 @@ jobs: publishJUnitResults: true tasks: 'build' options: 'xcuiTest -x checkstyleTest -x test' +- job: Misc_Tests + steps: + - task: Gradle@2 + inputs: + gradleWrapperFile: 'gradlew' + gradleOptions: '-Xmx3072m' + javaHomeOption: 'JDKVersion' + jdkVersionOption: "$(JDK_VERSION)" + jdkArchitectureOption: 'x64' + publishJUnitResults: true + tasks: 'build' + options: 'miscTest -x checkstyleTest -x test -x signMavenJavaPublication' diff --git a/build.gradle b/build.gradle index b26a68384..4cd6ab8de 100644 --- a/build.gradle +++ b/build.gradle @@ -189,15 +189,11 @@ task xcuiTest( type: Test ) { testLogging.showStandardStreams = true testLogging.exceptionFormat = 'full' filter { - includeTestsMatching '*.appium.element.generation.ios.*' - includeTestsMatching '*.appium.AppiumFluentWaitTest' includeTestsMatching 'io.appium.java_client.ios.*' includeTestsMatching '*.pagefactory_tests.XCUITModeTest' includeTestsMatching '*.pagefactory_tests.widget.tests.combined.*' includeTestsMatching '*.pagefactory_tests.widget.tests.ios.*' - includeTestsMatching '*.StartingAppLocallyTest.startingIOSAppWithCapabilitiesAndServiceTest' - includeTestsMatching '*.StartingAppLocallyTest.startingIOSAppWithCapabilitiesAndFlagsOnServerSideTest' - exclude '**/UIAutomationTest.class' + includeTestsMatching 'io.appium.java_client.service.local.StartingAppLocallyIosTest' exclude '**/IOSScreenRecordTest.class' exclude '**/ImagesComparisonTest.class' } @@ -211,5 +207,22 @@ task uiAutomationTest( type: Test ) { includeTestsMatching 'io.appium.java_client.android.SettingTest' includeTestsMatching 'io.appium.java_client.android.ClipboardTest' includeTestsMatching '*.AndroidAppStringsTest' + includeTestsMatching '*.pagefactory_tests.widget.tests.android.*' + includeTestsMatching '*.pagefactory_tests.widget.tests.AndroidPageObjectTest' + includeTestsMatching 'io.appium.java_client.service.local.StartingAppLocallyAndroidTest' + includeTestsMatching 'io.appium.java_client.service.local.ServerBuilderTest' + includeTestsMatching 'io.appium.java_client.service.local.ThreadSafetyTest' + } +} + +task miscTest( type: Test ) { + useJUnit() + testLogging.showStandardStreams = true + testLogging.exceptionFormat = 'full' + filter { + includeTestsMatching 'io.appium.java_client.touch.*' + includeTestsMatching 'io.appium.java_client.events.*' + includeTestsMatching 'io.appium.java_client.remote.*' + includeTestsMatching 'io.appium.java_client.drivers.options.*' } } 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 index 6d3df918d..0baebf02f 100644 --- a/src/main/java/io/appium/java_client/android/options/EspressoOptions.java +++ b/src/main/java/io/appium/java_client/android/options/EspressoOptions.java @@ -75,6 +75,7 @@ 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.SupportsEspressoBuildConfigOption; 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; @@ -113,6 +114,7 @@ public class EspressoOptions extends BaseOptions implements SupportsForceEspressoRebuildOption, SupportsShowGradleLogOption, SupportsOrientationOption, + SupportsEspressoBuildConfigOption, // App options: https://github.com/appium/appium-uiautomator2-driver#app SupportsAppOption, SupportsAppPackageOption, 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 index de328149f..6a3db25d7 100644 --- 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 @@ -16,6 +16,7 @@ package io.appium.java_client.android.options.app; +import io.appium.java_client.internal.CapabilityHelpers; import io.appium.java_client.remote.options.BaseMapOptionData; import java.util.Map; @@ -48,6 +49,6 @@ public ActivityOptions withLaunchDisplayId(int id) { */ public Optional getLaunchDisplayId() { Optional result = getOptionValue("launchDisplayId"); - return result.map((v) -> Integer.parseInt(String.valueOf(v))); + return result.map(CapabilityHelpers::toInteger); } } 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 index 28763a79a..209cdcac2 100644 --- 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 @@ -16,57 +16,42 @@ package io.appium.java_client.android.options.server; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; +import io.appium.java_client.internal.CapabilityHelpers; +import io.appium.java_client.remote.options.BaseMapOptionData; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; -public class EspressoBuildConfig { +public class EspressoBuildConfig extends BaseMapOptionData { 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; + super(); } public EspressoBuildConfig(String json) { - this(JsonParser.parseString(json).getAsJsonObject()); + super(json); } 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); + Optional> toolsVersionsOptional = getOptionValue(TOOLS_VERSION); + Map toolsVersions = toolsVersionsOptional.orElseGet(HashMap::new); + toolsVersions.put(name, value); + if (!toolsVersionsOptional.isPresent()) { + assignOptionValue(TOOLS_VERSION, toolsVersions); } return this; } private Optional getToolsVersionsFieldValue(String name) { + Optional> toolsVersionsOptional = getOptionValue(TOOLS_VERSION); //noinspection unchecked - return json == null || !json.has(TOOLS_VERSION) - ? Optional.empty() - : Optional.ofNullable((R) json.getAsJsonObject(TOOLS_VERSION).get(name)); + return toolsVersionsOptional.map((v) -> (R) v.getOrDefault(name, null)); } /** @@ -167,7 +152,7 @@ public EspressoBuildConfig withMinSdk(int apiLevel) { */ public Optional getMinSdkVersion() { Optional result = getToolsVersionsFieldValue("minSdk"); - return result.map((v) -> Integer.parseInt(String.valueOf(v))); + return result.map(CapabilityHelpers::toInteger); } /** @@ -188,7 +173,7 @@ public EspressoBuildConfig withTargetSdk(int apiLevel) { */ public Optional getTargetSdkVersion() { Optional result = getToolsVersionsFieldValue("targetSdk"); - return result.map((v) -> Integer.parseInt(String.valueOf(v))); + return result.map(CapabilityHelpers::toInteger); } /** @@ -211,35 +196,6 @@ 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 @@ -249,7 +205,7 @@ private Optional> getDependenciesValue(String name) { * @return self instance for chaining. */ public EspressoBuildConfig withAdditionalAppDependencies(List dependencies) { - return assignDependenciesField(ADDITIONAL_APP_DEPENDENCIES, dependencies); + return assignOptionValue(ADDITIONAL_APP_DEPENDENCIES, dependencies); } /** @@ -258,7 +214,7 @@ public EspressoBuildConfig withAdditionalAppDependencies(List dependenci * @return Dependent module names with their versions. */ public Optional> getAdditionalAppDependencies() { - return getDependenciesValue(ADDITIONAL_APP_DEPENDENCIES); + return getOptionValue(ADDITIONAL_APP_DEPENDENCIES); } /** @@ -270,7 +226,7 @@ public Optional> getAdditionalAppDependencies() { * @return self instance for chaining. */ public EspressoBuildConfig withAdditionalAndroidTestDependencies(List dependencies) { - return assignDependenciesField(ADDITIONAL_ANDROID_TEST_DEPENDENCIES, dependencies); + return assignOptionValue(ADDITIONAL_ANDROID_TEST_DEPENDENCIES, dependencies); } /** @@ -279,15 +235,6 @@ public EspressoBuildConfig withAdditionalAndroidTestDependencies(List de * @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(); + return getOptionValue(ADDITIONAL_ANDROID_TEST_DEPENDENCIES); } } 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 index 9444bc9ef..93eb19831 100644 --- 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 @@ -50,7 +50,7 @@ default T setEspressoBuildConfig(String configPath) { * @return self instance for chaining. */ default T setEspressoBuildConfig(EspressoBuildConfig config) { - return amend(ESPRESSO_BUILD_CONFIG_OPTION, config.toJson().toString()); + return amend(ESPRESSO_BUILD_CONFIG_OPTION, config.toString()); } /** diff --git a/src/main/java/io/appium/java_client/android/options/signing/SupportsKeystoreOptions.java b/src/main/java/io/appium/java_client/android/options/signing/SupportsKeystoreOptions.java index 4a79dbc67..5fbfcc2ce 100644 --- a/src/main/java/io/appium/java_client/android/options/signing/SupportsKeystoreOptions.java +++ b/src/main/java/io/appium/java_client/android/options/signing/SupportsKeystoreOptions.java @@ -38,7 +38,7 @@ public interface SupportsKeystoreOptions> extends * @param keystoreConfig The keystore config to use. * @return self instance for chaining. */ - default T useKeystore(KeystoreConfig keystoreConfig) { + default T setKeystoreConfig(KeystoreConfig keystoreConfig) { return amend(USE_KEYSTORE_OPTION, true) .amend(KEYSTORE_PATH_OPTION, keystoreConfig.getPath()) .amend(KEYSTORE_PASSWORD_OPTION, keystoreConfig.getPassword()) diff --git a/src/main/java/io/appium/java_client/ios/options/other/CommandTimeouts.java b/src/main/java/io/appium/java_client/ios/options/other/CommandTimeouts.java new file mode 100644 index 000000000..36b435a90 --- /dev/null +++ b/src/main/java/io/appium/java_client/ios/options/other/CommandTimeouts.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.ios.options.other; + +import io.appium.java_client.internal.CapabilityHelpers; +import io.appium.java_client.remote.options.BaseMapOptionData; + +import java.time.Duration; +import java.util.Map; +import java.util.Optional; + +public class CommandTimeouts extends BaseMapOptionData { + public static final String DEFAULT_COMMAND = "default"; + + public CommandTimeouts() { + } + + public CommandTimeouts(Map timeouts) { + super(timeouts); + } + + public CommandTimeouts(String json) { + super(json); + } + + /** + * Sets the timeout for the particular Appium command that + * is proxied to WDA. + * Command names you can find in logs, look for + * "Executing command 'command_name'" records. + * Timeout value is expected to contain max milliseconds to wait for + * the given WDA command to be executed before terminating the session forcefully. + * + * @param commandName The command name. + * @param timeout Command timeout. + * @return self instance for chaining. + */ + public CommandTimeouts withCommandTimeout(String commandName, Duration timeout) { + return assignOptionValue(commandName, timeout.toMillis()); + } + + /** + * Sets the default timeout for all Appium commands that + * are proxied to WDA. + * + * @param timeout Commands timeout. + * @return self instance for chaining. + */ + public CommandTimeouts withDefaultCommandTimeout(Duration timeout) { + return withCommandTimeout(DEFAULT_COMMAND, timeout); + } + + /** + * Get the command timeout. + * + * @param commandName The command name + * @return Timeout value. + */ + public Optional getCommandTimeout(String commandName) { + Optional result = getOptionValue(commandName); + return result.map(CapabilityHelpers::toDuration); + } +} diff --git a/src/main/java/io/appium/java_client/ios/options/other/SupportsCommandTimeoutsOption.java b/src/main/java/io/appium/java_client/ios/options/other/SupportsCommandTimeoutsOption.java index 42935504b..0afd1a9a8 100644 --- a/src/main/java/io/appium/java_client/ios/options/other/SupportsCommandTimeoutsOption.java +++ b/src/main/java/io/appium/java_client/ios/options/other/SupportsCommandTimeoutsOption.java @@ -16,14 +16,16 @@ package io.appium.java_client.ios.options.other; -import com.google.gson.JsonObject; 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.time.Duration; import java.util.Optional; +import static io.appium.java_client.internal.CapabilityHelpers.toDuration; + public interface SupportsCommandTimeoutsOption> extends Capabilities, CanSetCapability { String COMMAND_TIMEOUTS_OPTION = "commandTimeouts"; @@ -31,33 +33,19 @@ public interface SupportsCommandTimeoutsOption> extends /** * Custom timeout(s) in milliseconds for WDA backend commands execution. * This might be useful if WDA backend freezes unexpectedly or requires too - * much time to fail and blocks automated test execution. The value is expected - * to be of type string and can either contain max milliseconds to wait for - * each WDA command to be executed before terminating the session forcefully - * or a valid JSON string, where keys are internal Appium command names (you - * can find these in logs, look for "Executing command 'command_name'" records) - * and values are timeouts in milliseconds. You can also set the 'default' key - * to assign the timeout for all other commands not explicitly enumerated as - * JSON keys. + * much time to fail and blocks automated test execution. * - * @param timeouts E.g. '{"findElement": 40000, "findElements": 40000}'. + * @param timeouts Command timeouts. * @return self instance for chaining. */ - default T setCommandTimeouts(JsonObject timeouts) { + default T setCommandTimeouts(CommandTimeouts timeouts) { return amend(COMMAND_TIMEOUTS_OPTION, timeouts.toString()); } /** - * Custom timeout(s) in milliseconds for WDA backend commands execution. + * Custom timeout for all WDA backend commands execution. * This might be useful if WDA backend freezes unexpectedly or requires too - * much time to fail and blocks automated test execution. The value is expected - * to be of type string and can either contain max milliseconds to wait for - * each WDA command to be executed before terminating the session forcefully - * or a valid JSON string, where keys are internal Appium command names (you - * can find these in logs, look for "Executing command 'command_name'" records) - * and values are timeouts in milliseconds. You can also set the 'default' key - * to assign the timeout for all other commands not explicitly enumerated as - * JSON keys. + * much time to fail and blocks automated test execution. * * @param timeout The timeout value for all commands. * @return self instance for chaining. @@ -69,11 +57,14 @@ default T setCommandTimeouts(Duration timeout) { /** * Get custom timeout(s) in milliseconds for WDA backend commands execution. * - * @return Command timeouts. + * @return Either a global timeout duration or detailed command timeouts. */ - default Optional getCommandTimeouts() { - return Optional.ofNullable( - (String) getCapability(COMMAND_TIMEOUTS_OPTION) - ); + default Optional> getCommandTimeouts() { + return Optional.ofNullable(getCapability(COMMAND_TIMEOUTS_OPTION)) + .map(String::valueOf) + .map((v) -> v.trim().startsWith("{") + ? Either.left(new CommandTimeouts(v)) + : Either.right(toDuration(v)) + ); } } diff --git a/src/main/java/io/appium/java_client/ios/options/simulator/PasteboardSyncState.java b/src/main/java/io/appium/java_client/ios/options/simulator/PasteboardSyncState.java index 2252a37bf..885229dd8 100644 --- a/src/main/java/io/appium/java_client/ios/options/simulator/PasteboardSyncState.java +++ b/src/main/java/io/appium/java_client/ios/options/simulator/PasteboardSyncState.java @@ -1,3 +1,19 @@ +/* + * 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.ios.options.simulator; public enum PasteboardSyncState { diff --git a/src/main/java/io/appium/java_client/ios/options/simulator/Permissions.java b/src/main/java/io/appium/java_client/ios/options/simulator/Permissions.java new file mode 100644 index 000000000..53094e5ab --- /dev/null +++ b/src/main/java/io/appium/java_client/ios/options/simulator/Permissions.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.ios.options.simulator; + +import io.appium.java_client.remote.options.BaseMapOptionData; + +import java.util.Map; +import java.util.Optional; + +public class Permissions extends BaseMapOptionData { + public Permissions() { + } + + public Permissions(Map permissions) { + super(permissions); + } + + public Permissions(String json) { + super(json); + } + + /** + * Since Xcode SDK 11.4 Apple provides native APIs to interact with + * application settings. Check the output of `xcrun simctl privacy booted` + * command to get the list of available permission names. Use yes, no + * and unset as values in order to grant, revoke or reset the corresponding + * permission. Below Xcode SDK 11.4 it is required that applesimutils package + * is installed and available in PATH. The list of available service names + * and statuses can be found at https://github.com/wix/AppleSimulatorUtils. + * For example: {"com.apple.mobilecal": {"calendar": "YES"}} + * + * @param bundleId The app identifier to change permissions for. + * @param mapping Permissions mapping, where keys are perm names and vales are YES/NO. + * @return self instance for chaining. + */ + public Permissions withAppPermissions(String bundleId, Map mapping) { + return assignOptionValue(bundleId, mapping); + } + + /** + * Get permissions mapping for the given app bundle identifier. + * + * @param bundleId App bundle identifier. + * @return Permissions mapping. + */ + public Optional> getAppPermissions(String bundleId) { + return getOptionValue(bundleId); + } +} diff --git a/src/main/java/io/appium/java_client/ios/options/simulator/SupportsPermissionsOption.java b/src/main/java/io/appium/java_client/ios/options/simulator/SupportsPermissionsOption.java index 2aca0a82a..e3a4b36c2 100644 --- a/src/main/java/io/appium/java_client/ios/options/simulator/SupportsPermissionsOption.java +++ b/src/main/java/io/appium/java_client/ios/options/simulator/SupportsPermissionsOption.java @@ -16,7 +16,6 @@ package io.appium.java_client.ios.options.simulator; -import com.google.gson.JsonObject; import io.appium.java_client.remote.options.BaseOptions; import io.appium.java_client.remote.options.CanSetCapability; import org.openqa.selenium.Capabilities; @@ -28,49 +27,23 @@ public interface SupportsPermissionsOption> extends String PERMISSIONS_OPTION = "permissions"; /** - * Allows to set permissions for the specified application bundle on - * Simulator only. The capability value is expected to be a valid JSON - * with {"bundleId1": {"serviceName1": "serviceStatus1", ...}, ...} - * format. Since Xcode SDK 11.4 Apple provides native APIs to interact with - * application settings. Check the output of xcrun simctl privacy booted - * command to get the list of available permission names. Use yes, no - * and unset as values in order to grant, revoke or reset the corresponding - * permission. Below Xcode SDK 11.4 it is required that applesimutils package - * is installed and available in PATH. The list of available service names - * and statuses can be found at https://github.com/wix/AppleSimulatorUtils. + * Allows setting of permissions for the specified application bundle on + * Simulator only. * - * @param json For example {"com.apple.mobilecal": {"calendar": "YES"}} + * @param permissions Permissions mapping. * @return self instance for chaining. */ - default T setPermissions(JsonObject json) { - return amend(PERMISSIONS_OPTION, json.toString()); + default T setPermissions(Permissions permissions) { + return amend(PERMISSIONS_OPTION, permissions.toString()); } /** - * Allows to set permissions for the specified application bundle on - * Simulator only. The capability value is expected to be a valid JSON - * string with {"bundleId1": {"serviceName1": "serviceStatus1", ...}, ...} - * format. Since Xcode SDK 11.4 Apple provides native APIs to interact with - * application settings. Check the output of xcrun simctl privacy booted - * command to get the list of available permission names. Use yes, no - * and unset as values in order to grant, revoke or reset the corresponding - * permission. Below Xcode SDK 11.4 it is required that applesimutils package - * is installed and available in PATH. The list of available service names - * and statuses can be found at https://github.com/wix/AppleSimulatorUtils. + * Get Simulator permissions. * - * @param json For example {"com.apple.mobilecal": {"calendar": "YES"}} - * @return self instance for chaining. - */ - default T setPermissions(String json) { - return amend(PERMISSIONS_OPTION, json); - } - - /** - * Get Simulator permissions.. - * - * @return Permissions json. + * @return Permissions object. */ - default Optional getPermissions() { - return Optional.ofNullable((String) getCapability(PERMISSIONS_OPTION)); + default Optional getPermissions() { + return Optional.ofNullable(getCapability(PERMISSIONS_OPTION)) + .map((v) -> new Permissions(String.valueOf(v))); } } diff --git a/src/main/java/io/appium/java_client/mac/options/RunScript.java b/src/main/java/io/appium/java_client/mac/options/AppleScriptData.java similarity index 72% rename from src/main/java/io/appium/java_client/mac/options/RunScript.java rename to src/main/java/io/appium/java_client/mac/options/AppleScriptData.java index 4960d7e86..91b74aa98 100644 --- a/src/main/java/io/appium/java_client/mac/options/RunScript.java +++ b/src/main/java/io/appium/java_client/mac/options/AppleScriptData.java @@ -16,16 +16,16 @@ package io.appium.java_client.mac.options; -import io.appium.java_client.remote.options.BaseMapOptionData; +import io.appium.java_client.remote.options.SystemScript; import java.util.Map; import java.util.Optional; -public class RunScript extends BaseMapOptionData { - public RunScript() { +public class AppleScriptData extends SystemScript { + public AppleScriptData() { } - public RunScript(Map options) { + public AppleScriptData(Map options) { super(options); } @@ -35,8 +35,9 @@ public RunScript(Map options) { * @param script A valid AppleScript. * @return self instance for chaining. */ - public RunScript withScript(String script) { - return assignOptionValue("script", script); + @Override + public AppleScriptData withScript(String script) { + return super.withScript(script); } /** @@ -44,8 +45,9 @@ public RunScript withScript(String script) { * * @return AppleScript snippet. */ + @Override public Optional getScript() { - return getOptionValue("script"); + return super.getScript(); } /** @@ -54,8 +56,9 @@ public Optional getScript() { * @param command A valid AppleScript. * @return self instance for chaining. */ - public RunScript withCommand(String command) { - return assignOptionValue("command", command); + @Override + public AppleScriptData withCommand(String command) { + return super.withCommand(command); } /** @@ -63,7 +66,8 @@ public RunScript withCommand(String command) { * * @return AppleScript snippet. */ + @Override public Optional getCommand() { - return getOptionValue("command"); + return super.getCommand(); } } diff --git a/src/main/java/io/appium/java_client/mac/options/Mac2Options.java b/src/main/java/io/appium/java_client/mac/options/Mac2Options.java index 47214f677..37e31575a 100644 --- a/src/main/java/io/appium/java_client/mac/options/Mac2Options.java +++ b/src/main/java/io/appium/java_client/mac/options/Mac2Options.java @@ -19,9 +19,12 @@ 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.SupportsPostrunOption; +import io.appium.java_client.remote.options.SupportsPrerunOption; import org.openqa.selenium.Capabilities; import java.util.Map; +import java.util.Optional; /** * https://github.com/appium/appium-mac2-driver#capabilities @@ -37,8 +40,8 @@ public class Mac2Options extends BaseOptions implements SupportsServerStartupTimeoutOption, SupportsSkipAppKillOption, SupportsShowServerLogsOption, - SupportsPrerunOption, - SupportsPostrunOption { + SupportsPrerunOption, + SupportsPostrunOption { public Mac2Options() { setCommonOptions(); } @@ -57,4 +60,54 @@ private void setCommonOptions() { setPlatformName(MobilePlatform.MAC); setAutomationName(AutomationName.MAC2); } + + /** + * An object containing either script or command key. The value of + * each key must be a valid AppleScript script or command to be + * executed after before Mac2Driver session is started. See + * https://github.com/appium/appium-mac2-driver#applescript-commands-execution + * for more details. + * + * @param script A valid AppleScript snippet. + * @return self instance for chaining. + */ + public Mac2Options setPrerun(AppleScriptData script) { + return amend(PRERUN_OPTION, script.toMap()); + } + + /** + * Get the prerun script. + * + * @return Prerun script. + */ + public Optional getPrerun() { + //noinspection unchecked + return Optional.ofNullable(getCapability(PRERUN_OPTION)) + .map((v) -> new AppleScriptData((Map) v)); + } + + /** + * An object containing either script or command key. The value of + * each key must be a valid AppleScript script or command to be + * executed after Mac2Driver session is stopped. See + * https://github.com/appium/appium-mac2-driver#applescript-commands-execution + * for more details. + * + * @param script A valid AppleScript snippet. + * @return self instance for chaining. + */ + public Mac2Options setPostrun(AppleScriptData script) { + return amend(POSTRUN_OPTION, script.toMap()); + } + + /** + * Get the postrun script. + * + * @return Postrun script. + */ + public Optional getPostrun() { + //noinspection unchecked + return Optional.ofNullable(getCapability(POSTRUN_OPTION)) + .map((v) -> new AppleScriptData((Map) v)); + } } diff --git a/src/main/java/io/appium/java_client/mac/options/SupportsPostrunOption.java b/src/main/java/io/appium/java_client/mac/options/SupportsPostrunOption.java deleted file mode 100644 index 69430019b..000000000 --- a/src/main/java/io/appium/java_client/mac/options/SupportsPostrunOption.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.mac.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.Map; -import java.util.Optional; - -public interface SupportsPostrunOption> extends - Capabilities, CanSetCapability { - String POSTRUN_OPTION = "postrun"; - - /** - * An object containing either script or command key. The value of - * each key must be a valid AppleScript script or command to be - * executed after Mac2Driver session is stopped. See - * https://github.com/appium/appium-mac2-driver#applescript-commands-execution - * for more details. - * - * @param script A valid AppleScript snippet. - * @return self instance for chaining. - */ - default T setPostrun(RunScript script) { - return amend(POSTRUN_OPTION, script.toMap()); - } - - /** - * Get the postrun script. - * - * @return Postrun script. - */ - default Optional getPostrun() { - //noinspection unchecked - return Optional.ofNullable(getCapability(POSTRUN_OPTION)) - .map((v) -> new RunScript((Map) v)); - } -} diff --git a/src/main/java/io/appium/java_client/mac/options/SupportsPrerunOption.java b/src/main/java/io/appium/java_client/mac/options/SupportsPrerunOption.java deleted file mode 100644 index 61b8d0897..000000000 --- a/src/main/java/io/appium/java_client/mac/options/SupportsPrerunOption.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.mac.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.Map; -import java.util.Optional; - -public interface SupportsPrerunOption> extends - Capabilities, CanSetCapability { - String PRERUN_OPTION = "prerun"; - - /** - * An object containing either script or command key. The value of - * each key must be a valid AppleScript script or command to be - * executed after before Mac2Driver session is started. See - * https://github.com/appium/appium-mac2-driver#applescript-commands-execution - * for more details. - * - * @param script A valid AppleScript snippet. - * @return self instance for chaining. - */ - default T setPrerun(RunScript script) { - return amend(PRERUN_OPTION, script.toMap()); - } - - /** - * Get the prerun script. - * - * @return Prerun script. - */ - default Optional getPrerun() { - //noinspection unchecked - return Optional.ofNullable(getCapability(PRERUN_OPTION)) - .map((v) -> new RunScript((Map) v)); - } -} diff --git a/src/main/java/io/appium/java_client/remote/options/BaseMapOptionData.java b/src/main/java/io/appium/java_client/remote/options/BaseMapOptionData.java index 004d1bdf5..a7b75e3d7 100644 --- a/src/main/java/io/appium/java_client/remote/options/BaseMapOptionData.java +++ b/src/main/java/io/appium/java_client/remote/options/BaseMapOptionData.java @@ -16,7 +16,8 @@ package io.appium.java_client.remote.options; -import com.google.gson.GsonBuilder; +import com.google.gson.Gson; +import com.google.gson.JsonObject; import java.util.Collections; import java.util.HashMap; @@ -25,6 +26,7 @@ public abstract class BaseMapOptionData> { private Map options; + private static final Gson gson = new Gson(); public BaseMapOptionData() { } @@ -33,6 +35,11 @@ public BaseMapOptionData(Map options) { this.options = options; } + public BaseMapOptionData(String json) { + //noinspection unchecked + this((Map) gson.fromJson(json, Map.class)); + } + /** * Sets the given value on the data object. * @@ -70,8 +77,12 @@ public Map toMap() { return Optional.ofNullable(options).orElseGet(Collections::emptyMap); } + public JsonObject toJson() { + return gson.toJsonTree(toMap()).getAsJsonObject(); + } + @Override public String toString() { - return new GsonBuilder().create().toJson(toMap()); + return gson.toJson(toMap()); } } diff --git a/src/main/java/io/appium/java_client/remote/options/SupportsPostrunOption.java b/src/main/java/io/appium/java_client/remote/options/SupportsPostrunOption.java new file mode 100644 index 000000000..e055cb69f --- /dev/null +++ b/src/main/java/io/appium/java_client/remote/options/SupportsPostrunOption.java @@ -0,0 +1,30 @@ +/* + * 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.remote.options; + +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +public interface SupportsPostrunOption, S extends SystemScript> + extends Capabilities, CanSetCapability { + String POSTRUN_OPTION = "postrun"; + + T setPostrun(S script); + + Optional getPostrun(); +} diff --git a/src/main/java/io/appium/java_client/remote/options/SupportsPrerunOption.java b/src/main/java/io/appium/java_client/remote/options/SupportsPrerunOption.java new file mode 100644 index 000000000..a44d2c53f --- /dev/null +++ b/src/main/java/io/appium/java_client/remote/options/SupportsPrerunOption.java @@ -0,0 +1,30 @@ +/* + * 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.remote.options; + +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +public interface SupportsPrerunOption, S extends SystemScript> + extends Capabilities, CanSetCapability { + String PRERUN_OPTION = "prerun"; + + T setPrerun(S script); + + Optional getPrerun(); +} diff --git a/src/main/java/io/appium/java_client/remote/options/SystemScript.java b/src/main/java/io/appium/java_client/remote/options/SystemScript.java new file mode 100644 index 000000000..901d8e220 --- /dev/null +++ b/src/main/java/io/appium/java_client/remote/options/SystemScript.java @@ -0,0 +1,45 @@ +/* + * 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.remote.options; + +import java.util.Map; +import java.util.Optional; + +public abstract class SystemScript> extends BaseMapOptionData { + public SystemScript() { + } + + public SystemScript(Map options) { + super(options); + } + + public T withScript(String script) { + return assignOptionValue("script", script); + } + + public Optional getScript() { + return getOptionValue("script"); + } + + public T withCommand(String command) { + return assignOptionValue("command", command); + } + + public Optional getCommand() { + return getOptionValue("command"); + } +} diff --git a/src/main/java/io/appium/java_client/windows/options/RunScript.java b/src/main/java/io/appium/java_client/windows/options/PowerShellData.java similarity index 73% rename from src/main/java/io/appium/java_client/windows/options/RunScript.java rename to src/main/java/io/appium/java_client/windows/options/PowerShellData.java index e31fa38fc..6dc97f495 100644 --- a/src/main/java/io/appium/java_client/windows/options/RunScript.java +++ b/src/main/java/io/appium/java_client/windows/options/PowerShellData.java @@ -16,16 +16,16 @@ package io.appium.java_client.windows.options; -import io.appium.java_client.remote.options.BaseMapOptionData; +import io.appium.java_client.remote.options.SystemScript; import java.util.Map; import java.util.Optional; -public class RunScript extends BaseMapOptionData { - public RunScript() { +public class PowerShellData extends SystemScript { + public PowerShellData() { } - public RunScript(Map options) { + public PowerShellData(Map options) { super(options); } @@ -35,8 +35,9 @@ public RunScript(Map options) { * @param script A valid PowerShell script. * @return self instance for chaining. */ - public RunScript withScript(String script) { - return assignOptionValue("script", script); + @Override + public PowerShellData withScript(String script) { + return super.withScript(script); } /** @@ -44,8 +45,9 @@ public RunScript withScript(String script) { * * @return PowerShell script. */ + @Override public Optional getScript() { - return getOptionValue("script"); + return super.getScript(); } /** @@ -54,8 +56,9 @@ public Optional getScript() { * @param command A valid PowerShell script. * @return self instance for chaining. */ - public RunScript withCommand(String command) { - return assignOptionValue("command", command); + @Override + public PowerShellData withCommand(String command) { + return super.withCommand(command); } /** @@ -63,7 +66,8 @@ public RunScript withCommand(String command) { * * @return PowerShell script. */ + @Override public Optional getCommand() { - return getOptionValue("command"); + return super.getCommand(); } } diff --git a/src/main/java/io/appium/java_client/windows/options/SupportsPostrunOption.java b/src/main/java/io/appium/java_client/windows/options/SupportsPostrunOption.java deleted file mode 100644 index b766820c3..000000000 --- a/src/main/java/io/appium/java_client/windows/options/SupportsPostrunOption.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.windows.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.Map; -import java.util.Optional; - -public interface SupportsPostrunOption> extends - Capabilities, CanSetCapability { - String POSTRUN_OPTION = "postrun"; - - /** - * An object containing either script or command key. The value of - * each key must be a valid PowerShell script or command to be - * executed after an WinAppDriver session is finished. - * See - * https://github.com/appium/appium-windows-driver#power-shell-commands-execution - * for more details. - * - * @param script E.g. {script: 'Get-Process outlook -ErrorAction SilentlyContinue'}. - * @return self instance for chaining. - */ - default T setPostrun(RunScript script) { - return amend(POSTRUN_OPTION, script.toMap()); - } - - /** - * Get the postrun script. - * - * @return Postrun script. - */ - default Optional getPostrun() { - //noinspection unchecked - return Optional.ofNullable(getCapability(POSTRUN_OPTION)) - .map((v) -> new RunScript((Map) v)); - } -} diff --git a/src/main/java/io/appium/java_client/windows/options/SupportsPrerunOption.java b/src/main/java/io/appium/java_client/windows/options/SupportsPrerunOption.java deleted file mode 100644 index d3722da48..000000000 --- a/src/main/java/io/appium/java_client/windows/options/SupportsPrerunOption.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.windows.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.Map; -import java.util.Optional; - -public interface SupportsPrerunOption> extends - Capabilities, CanSetCapability { - String PRERUN_OPTION = "prerun"; - - /** - * An object containing either script or command key. The value of - * each key must be a valid PowerShell script or command to be - * executed prior to the WinAppDriver session startup. - * See - * https://github.com/appium/appium-windows-driver#power-shell-commands-execution - * for more details. - * - * @param script E.g. {script: 'Get-Process outlook -ErrorAction SilentlyContinue'}. - * @return self instance for chaining. - */ - default T setPrerun(RunScript script) { - return amend(PRERUN_OPTION, script.toMap()); - } - - /** - * Get the prerun script. - * - * @return Prerun script. - */ - default Optional getPrerun() { - //noinspection unchecked - return Optional.ofNullable(getCapability(PRERUN_OPTION)) - .map((v) -> new RunScript((Map) v)); - } -} diff --git a/src/main/java/io/appium/java_client/windows/options/WindowsOptions.java b/src/main/java/io/appium/java_client/windows/options/WindowsOptions.java index 0d25a1ece..8a1f0eb8f 100644 --- a/src/main/java/io/appium/java_client/windows/options/WindowsOptions.java +++ b/src/main/java/io/appium/java_client/windows/options/WindowsOptions.java @@ -20,9 +20,12 @@ 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.SupportsPostrunOption; +import io.appium.java_client.remote.options.SupportsPrerunOption; import org.openqa.selenium.Capabilities; import java.util.Map; +import java.util.Optional; /** * https://github.com/appium/appium-windows-driver#usage @@ -36,8 +39,8 @@ public class WindowsOptions extends BaseOptions implements SupportsMsWaitForAppLaunchOption, SupportsMsExperimentalWebDriverOption, SupportsSystemPortOption, - SupportsPrerunOption, - SupportsPostrunOption { + SupportsPrerunOption, + SupportsPostrunOption { public WindowsOptions() { setCommonOptions(); } @@ -56,4 +59,56 @@ private void setCommonOptions() { setPlatformName(MobilePlatform.WINDOWS); setAutomationName(AutomationName.WINDOWS); } + + /** + * An object containing either script or command key. The value of + * each key must be a valid PowerShell script or command to be + * executed prior to the WinAppDriver session startup. + * See + * https://github.com/appium/appium-windows-driver#power-shell-commands-execution + * for more details. + * + * @param script E.g. {script: 'Get-Process outlook -ErrorAction SilentlyContinue'}. + * @return self instance for chaining. + */ + public WindowsOptions setPrerun(PowerShellData script) { + return amend(PRERUN_OPTION, script.toMap()); + } + + /** + * Get the prerun script. + * + * @return Prerun script. + */ + public Optional getPrerun() { + //noinspection unchecked + return Optional.ofNullable(getCapability(PRERUN_OPTION)) + .map((v) -> new PowerShellData((Map) v)); + } + + /** + * An object containing either script or command key. The value of + * each key must be a valid PowerShell script or command to be + * executed after an WinAppDriver session is finished. + * See + * https://github.com/appium/appium-windows-driver#power-shell-commands-execution + * for more details. + * + * @param script E.g. {script: 'Get-Process outlook -ErrorAction SilentlyContinue'}. + * @return self instance for chaining. + */ + public WindowsOptions setPostrun(PowerShellData script) { + return amend(POSTRUN_OPTION, script.toMap()); + } + + /** + * Get the postrun script. + * + * @return Postrun script. + */ + public Optional getPostrun() { + //noinspection unchecked + return Optional.ofNullable(getCapability(POSTRUN_OPTION)) + .map((v) -> new PowerShellData((Map) v)); + } } diff --git a/src/test/java/io/appium/java_client/drivers/options/OptionsBuildingTest.java b/src/test/java/io/appium/java_client/drivers/options/OptionsBuildingTest.java new file mode 100644 index 000000000..9b12ceda9 --- /dev/null +++ b/src/test/java/io/appium/java_client/drivers/options/OptionsBuildingTest.java @@ -0,0 +1,143 @@ +/* + * 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.drivers.options; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import io.appium.java_client.android.options.EspressoOptions; +import io.appium.java_client.android.options.UiAutomator2Options; +import io.appium.java_client.android.options.localization.AppLocale; +import io.appium.java_client.android.options.server.EspressoBuildConfig; +import io.appium.java_client.android.options.signing.KeystoreConfig; +import io.appium.java_client.ios.options.XCUITestOptions; +import io.appium.java_client.ios.options.other.CommandTimeouts; +import io.appium.java_client.ios.options.simulator.Permissions; +import io.appium.java_client.mac.options.AppleScriptData; +import io.appium.java_client.mac.options.Mac2Options; +import io.appium.java_client.remote.AutomationName; +import io.appium.java_client.windows.options.PowerShellData; +import io.appium.java_client.windows.options.WindowsOptions; +import org.junit.Test; +import org.openqa.selenium.Platform; + +import java.net.MalformedURLException; +import java.net.URL; +import java.time.Duration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@SuppressWarnings("ConstantConditions") +public class OptionsBuildingTest { + @Test + public void canBuildXcuiTestOptions() throws MalformedURLException { + XCUITestOptions options = new XCUITestOptions(); + assertEquals(Platform.IOS, options.getPlatformName()); + assertEquals(AutomationName.IOS_XCUI_TEST, options.getAutomationName().orElse(null)); + options.setNewCommandTimeout(Duration.ofSeconds(10)) + .noReset() + .setWdaBaseUrl("http://localhost:8000") + .setPermissions(new Permissions() + .withAppPermissions("com.apple.MobileSafari", + ImmutableMap.of("calendar", "YES"))) + .setSafariSocketChunkSize(10) + .setCommandTimeouts(new CommandTimeouts() + .withCommandTimeout("yolo", Duration.ofSeconds(1))); + assertEquals(Duration.ofSeconds(10), options.getNewCommandTimeout().orElse(null)); + assertEquals(new URL("http://localhost:8000"), options.getWdaBaseUrl().orElse(null)); + assertNotNull(options.getPermissions() + .map((v) -> v.getAppPermissions("com.apple.MobileSafari")) + .orElse(null)); + assertEquals(10L, (long) options.getSafariSocketChunkSize().orElse(0)); + assertEquals(1L, options.getCommandTimeouts().orElse(null).left() + .getCommandTimeout("yolo").orElse(null).getSeconds()); + } + + @Test + public void canBuildUiAutomator2Options() throws MalformedURLException { + UiAutomator2Options options = new UiAutomator2Options(); + assertEquals(Platform.ANDROID, options.getPlatformName()); + assertEquals(AutomationName.ANDROID_UIAUTOMATOR2, options.getAutomationName().orElse(null)); + options.setNewCommandTimeout(Duration.ofSeconds(10)) + .noReset() + .setAdbExecTimeout(Duration.ofSeconds(3)) + .suppressKillServer() + .setMjpegScreenshotUrl(new URL("http://yolo.com")) + .setKeystoreConfig(new KeystoreConfig("path", "password", "keyAlias", "keyPassword")); + assertEquals(Duration.ofSeconds(10), options.getNewCommandTimeout().orElse(null)); + assertEquals(Duration.ofSeconds(3), options.getAdbExecTimeout().orElse(null)); + assertEquals(new URL("http://yolo.com"), options.getMjpegScreenshotUrl().orElse(null)); + assertEquals("path", options.getKeystoreConfig().orElse(null).getPath()); + assertEquals("keyAlias", options.getKeystoreConfig().orElse(null).getKeyAlias()); + assertTrue(options.doesSuppressKillServer().orElse(false)); + } + + @Test + public void canBuildEspressoOptions() { + EspressoOptions options = new EspressoOptions(); + assertEquals(Platform.ANDROID, options.getPlatformName()); + assertEquals(AutomationName.ESPRESSO, options.getAutomationName().orElse(null)); + options.setNewCommandTimeout(Duration.ofSeconds(10)) + .forceEspressoRebuild() + .setAppLocale(new AppLocale() + .withCountry("CN") + .withLanguage("zh") + .withVariant("hans")) + .setEspressoBuildConfig(new EspressoBuildConfig() + .withAdditionalAppDependencies(ImmutableList.of( + "com.dep1:1.2.3", + "com.dep2:1.2.3" + )) + ); + assertEquals(Duration.ofSeconds(10), options.getNewCommandTimeout().orElse(null)); + assertEquals("CN", options.getAppLocale().orElse(null).getCountry().orElse(null)); + assertEquals(2, options.getEspressoBuildConfig().orElse(null) + .left().getAdditionalAppDependencies().orElse(null).size()); + assertTrue(options.doesForceEspressoRebuild().orElse(false)); + } + + @Test + public void canBuildWindowsOptions() { + WindowsOptions options = new WindowsOptions(); + assertEquals(Platform.WINDOWS, options.getPlatformName()); + assertEquals(AutomationName.WINDOWS, options.getAutomationName().orElse(null)); + options.setNewCommandTimeout(Duration.ofSeconds(10)) + .setPrerun(new PowerShellData().withScript("yolo prescript")) + .setPostrun(new PowerShellData().withCommand("yolo command")); + assertEquals(Duration.ofSeconds(10), options.getNewCommandTimeout().orElse(null)); + assertEquals("yolo prescript", options.getPrerun().orElse(null).getScript().orElse(null)); + assertEquals("yolo command", options.getPostrun().orElse(null).getCommand().orElse(null)); + } + + @Test + public void canBuildMac2Options() { + Mac2Options options = new Mac2Options(); + assertEquals(Platform.MAC, options.getPlatformName()); + assertEquals(AutomationName.MAC2, options.getAutomationName().orElse(null)); + options.setNewCommandTimeout(Duration.ofSeconds(10)) + .skipAppKill() + .setPrerun(new AppleScriptData().withScript("yolo prescript")) + .setPostrun(new AppleScriptData().withCommand("yolo command")); + assertEquals(Duration.ofSeconds(10), options.getNewCommandTimeout().orElse(null)); + assertEquals("yolo prescript", options.getPrerun().orElse(null).getScript().orElse(null)); + assertEquals("yolo command", options.getPostrun().orElse(null).getCommand().orElse(null)); + assertTrue(options.doesSkipAppKill().orElse(false)); + assertFalse(options.doesEventTimings().isPresent()); + } +} diff --git a/src/test/java/io/appium/java_client/service/local/AppiumDriverLocalServiceTest.java b/src/test/java/io/appium/java_client/service/local/AppiumDriverLocalServiceTest.java deleted file mode 100644 index e6d51247b..000000000 --- a/src/test/java/io/appium/java_client/service/local/AppiumDriverLocalServiceTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.appium.java_client.service.local; - -import static io.appium.java_client.service.local.AppiumDriverLocalService.parseSlf4jContextFromLogMessage; -import static org.junit.Assert.assertEquals; -import static org.slf4j.LoggerFactory.getLogger; -import static org.slf4j.event.Level.DEBUG; -import static org.slf4j.event.Level.INFO; - -import org.junit.Test; -import org.slf4j.event.Level; - -public class AppiumDriverLocalServiceTest { - - @Test - public void canParseSlf4jLoggerContext() { - assertLoggerContext(INFO, "appium.service.androidbootstrap", - "[AndroidBootstrap] [BOOTSTRAP LOG] [debug] json loading complete."); - assertLoggerContext(INFO, "appium.service.adb", - "[ADB] Cannot read version codes of "); - assertLoggerContext(INFO, "appium.service.xcuitest", - "[XCUITest] Determining device to run tests on: udid: '1234567890', real device: true"); - assertLoggerContext(INFO, "appium.service", - "no-prefix log message."); - assertLoggerContext(INFO, "appium.service", - "no-prefix log [not-a-logger-name] message."); - assertLoggerContext(DEBUG, "appium.service.mjsonwp", - "[debug] [MJSONWP] Calling AppiumDriver.getStatus() with args: []"); - assertLoggerContext(DEBUG, "appium.service.xcuitest", - "[debug] [XCUITest] Xcode version set to 'x.y.z' "); - assertLoggerContext(DEBUG, "appium.service.jsonwpproxy", - "[debug] [JSONWP Proxy] Proxying [GET /status] to [GET http://localhost:18218/status] with no body"); - } - - private void assertLoggerContext(Level expectedLevel, String expectedLoggerName, String logMessage) { - Slf4jLogMessageContext ctx = parseSlf4jContextFromLogMessage(logMessage); - assertEquals(expectedLoggerName, ctx.getName()); - assertEquals(expectedLevel, ctx.getLevel()); - assertEquals(getLogger(expectedLoggerName), ctx.getLogger()); - } -} diff --git a/src/test/java/io/appium/java_client/service/local/StartingAppLocallyTest.java b/src/test/java/io/appium/java_client/service/local/StartingAppLocallyAndroidTest.java similarity index 57% rename from src/test/java/io/appium/java_client/service/local/StartingAppLocallyTest.java rename to src/test/java/io/appium/java_client/service/local/StartingAppLocallyAndroidTest.java index 467d6c21f..ee7eb82e2 100644 --- a/src/test/java/io/appium/java_client/service/local/StartingAppLocallyTest.java +++ b/src/test/java/io/appium/java_client/service/local/StartingAppLocallyAndroidTest.java @@ -16,19 +16,8 @@ package io.appium.java_client.service.local; -import static io.appium.java_client.TestResources.apiDemosApk; -import static io.appium.java_client.TestResources.uiCatalogAppZip; -import static io.github.bonigarcia.wdm.WebDriverManager.chromedriver; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - import io.appium.java_client.android.AndroidDriver; import io.appium.java_client.android.options.UiAutomator2Options; -import io.appium.java_client.ios.BaseIOSTest; -import io.appium.java_client.ios.IOSDriver; -import io.appium.java_client.ios.options.XCUITestOptions; import io.appium.java_client.remote.AutomationName; import io.appium.java_client.remote.MobileCapabilityType; import io.appium.java_client.remote.MobilePlatform; @@ -36,11 +25,16 @@ import io.github.bonigarcia.wdm.WebDriverManager; import org.junit.Test; import org.openqa.selenium.Capabilities; -import org.openqa.selenium.Platform; import java.time.Duration; -public class StartingAppLocallyTest { +import static io.appium.java_client.TestResources.apiDemosApk; +import static io.github.bonigarcia.wdm.WebDriverManager.chromedriver; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class StartingAppLocallyAndroidTest { @Test public void startingAndroidAppWithCapabilitiesOnlyTest() { @@ -118,75 +112,4 @@ public void startingAndroidAppWithCapabilitiesAndFlagsOnServerSideTest() { driver.quit(); } } - - @Test - public void startingIOSAppWithCapabilitiesOnlyTest() { - XCUITestOptions options = new XCUITestOptions() - .setPlatformVersion(BaseIOSTest.PLATFORM_VERSION) - .setDeviceName(BaseIOSTest.DEVICE_NAME) - .setApp(uiCatalogAppZip().toAbsolutePath().toString()) - .setWdaLaunchTimeout(BaseIOSTest.WDA_LAUNCH_TIMEOUT); - IOSDriver driver = new IOSDriver(options); - try { - XCUITestOptions caps = new XCUITestOptions(driver.getCapabilities()); - - assertEquals(AutomationName.IOS_XCUI_TEST, caps.getAutomationName().orElse(null)); - assertEquals(Platform.IOS, caps.getPlatformName()); - assertNotNull(caps.getDeviceName().orElse(null)); - assertEquals(BaseIOSTest.PLATFORM_VERSION, caps.getPlatformVersion().orElse(null)); - assertEquals(uiCatalogAppZip().toAbsolutePath().toString(), caps.getApp().orElse(null)); - } finally { - driver.quit(); - } - } - - - @Test - public void startingIOSAppWithCapabilitiesAndServiceTest() { - XCUITestOptions options = new XCUITestOptions() - .setPlatformVersion(BaseIOSTest.PLATFORM_VERSION) - .setDeviceName(BaseIOSTest.DEVICE_NAME) - .setApp(uiCatalogAppZip().toAbsolutePath().toString()) - .setWdaLaunchTimeout(BaseIOSTest.WDA_LAUNCH_TIMEOUT); - - AppiumServiceBuilder builder = new AppiumServiceBuilder() - .withArgument(GeneralServerFlag.SESSION_OVERRIDE) - .withArgument(GeneralServerFlag.STRICT_CAPS); - - IOSDriver driver = new IOSDriver(builder, options); - try { - Capabilities caps = driver.getCapabilities(); - assertTrue(caps.getCapability(MobileCapabilityType.PLATFORM_NAME) - .toString().equalsIgnoreCase(MobilePlatform.IOS)); - assertNotNull(caps.getCapability(MobileCapabilityType.DEVICE_NAME)); - } finally { - driver.quit(); - } - } - - @Test - public void startingIOSAppWithCapabilitiesAndFlagsOnServerSideTest() { - XCUITestOptions serverOptions = new XCUITestOptions() - .setPlatformVersion(BaseIOSTest.PLATFORM_VERSION) - .setDeviceName(BaseIOSTest.DEVICE_NAME) - .setWdaLaunchTimeout(BaseIOSTest.WDA_LAUNCH_TIMEOUT); - - XCUITestOptions clientOptions = new XCUITestOptions() - .setApp(uiCatalogAppZip().toAbsolutePath().toString()); - - AppiumServiceBuilder builder = new AppiumServiceBuilder() - .withArgument(GeneralServerFlag.SESSION_OVERRIDE) - .withArgument(GeneralServerFlag.STRICT_CAPS) - .withCapabilities(serverOptions); - - IOSDriver driver = new IOSDriver(builder, clientOptions); - try { - XCUITestOptions caps = new XCUITestOptions(driver.getCapabilities()); - assertEquals(Platform.IOS, caps.getPlatformName()); - assertNotNull(caps.getDeviceName().orElse(null)); - assertFalse(driver.isBrowser()); - } finally { - driver.quit(); - } - } } diff --git a/src/test/java/io/appium/java_client/service/local/StartingAppLocallyIosTest.java b/src/test/java/io/appium/java_client/service/local/StartingAppLocallyIosTest.java new file mode 100644 index 000000000..b7bc4786c --- /dev/null +++ b/src/test/java/io/appium/java_client/service/local/StartingAppLocallyIosTest.java @@ -0,0 +1,107 @@ +/* + * 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.service.local; + +import static io.appium.java_client.TestResources.uiCatalogAppZip; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import io.appium.java_client.ios.BaseIOSTest; +import io.appium.java_client.ios.IOSDriver; +import io.appium.java_client.ios.options.XCUITestOptions; +import io.appium.java_client.remote.AutomationName; +import io.appium.java_client.remote.MobileCapabilityType; +import io.appium.java_client.remote.MobilePlatform; +import io.appium.java_client.service.local.flags.GeneralServerFlag; +import org.junit.Test; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.Platform; + +public class StartingAppLocallyIosTest { + @Test + public void startingIOSAppWithCapabilitiesOnlyTest() { + XCUITestOptions options = new XCUITestOptions() + .setPlatformVersion(BaseIOSTest.PLATFORM_VERSION) + .setDeviceName(BaseIOSTest.DEVICE_NAME) + .setApp(uiCatalogAppZip().toAbsolutePath().toString()) + .setWdaLaunchTimeout(BaseIOSTest.WDA_LAUNCH_TIMEOUT); + IOSDriver driver = new IOSDriver(options); + try { + XCUITestOptions caps = new XCUITestOptions(driver.getCapabilities()); + + assertEquals(AutomationName.IOS_XCUI_TEST, caps.getAutomationName().orElse(null)); + assertEquals(Platform.IOS, caps.getPlatformName()); + assertNotNull(caps.getDeviceName().orElse(null)); + assertEquals(BaseIOSTest.PLATFORM_VERSION, caps.getPlatformVersion().orElse(null)); + assertEquals(uiCatalogAppZip().toAbsolutePath().toString(), caps.getApp().orElse(null)); + } finally { + driver.quit(); + } + } + + + @Test + public void startingIOSAppWithCapabilitiesAndServiceTest() { + XCUITestOptions options = new XCUITestOptions() + .setPlatformVersion(BaseIOSTest.PLATFORM_VERSION) + .setDeviceName(BaseIOSTest.DEVICE_NAME) + .setApp(uiCatalogAppZip().toAbsolutePath().toString()) + .setWdaLaunchTimeout(BaseIOSTest.WDA_LAUNCH_TIMEOUT); + + AppiumServiceBuilder builder = new AppiumServiceBuilder() + .withArgument(GeneralServerFlag.SESSION_OVERRIDE) + .withArgument(GeneralServerFlag.STRICT_CAPS); + + IOSDriver driver = new IOSDriver(builder, options); + try { + Capabilities caps = driver.getCapabilities(); + assertTrue(caps.getCapability(MobileCapabilityType.PLATFORM_NAME) + .toString().equalsIgnoreCase(MobilePlatform.IOS)); + assertNotNull(caps.getCapability(MobileCapabilityType.DEVICE_NAME)); + } finally { + driver.quit(); + } + } + + @Test + public void startingIOSAppWithCapabilitiesAndFlagsOnServerSideTest() { + XCUITestOptions serverOptions = new XCUITestOptions() + .setPlatformVersion(BaseIOSTest.PLATFORM_VERSION) + .setDeviceName(BaseIOSTest.DEVICE_NAME) + .setWdaLaunchTimeout(BaseIOSTest.WDA_LAUNCH_TIMEOUT); + + XCUITestOptions clientOptions = new XCUITestOptions() + .setApp(uiCatalogAppZip().toAbsolutePath().toString()); + + AppiumServiceBuilder builder = new AppiumServiceBuilder() + .withArgument(GeneralServerFlag.SESSION_OVERRIDE) + .withArgument(GeneralServerFlag.STRICT_CAPS) + .withCapabilities(serverOptions); + + IOSDriver driver = new IOSDriver(builder, clientOptions); + try { + XCUITestOptions caps = new XCUITestOptions(driver.getCapabilities()); + assertEquals(Platform.IOS, caps.getPlatformName()); + assertNotNull(caps.getDeviceName().orElse(null)); + assertFalse(driver.isBrowser()); + } finally { + driver.quit(); + } + } +} diff --git a/src/test/java/io/appium/java_client/touch/FailsWithMatcher.java b/src/test/java/io/appium/java_client/touch/FailsWithMatcher.java index 93c1dd804..f4ac4bec8 100644 --- a/src/test/java/io/appium/java_client/touch/FailsWithMatcher.java +++ b/src/test/java/io/appium/java_client/touch/FailsWithMatcher.java @@ -22,7 +22,7 @@ public static Matcher failsWith( public static Matcher failsWith( final Class throwableType, final Matcher throwableMatcher) { - return new FailsWithMatcher<>(allOf(instanceOf(throwableType), throwableMatcher)); + return new FailsWithMatcher(allOf(instanceOf(throwableType), throwableMatcher)); } @Override diff --git a/src/test/java/io/appium/java_client/touch/TouchOptionsTests.java b/src/test/java/io/appium/java_client/touch/TouchOptionsTests.java index 4476ed33e..1500e2f81 100644 --- a/src/test/java/io/appium/java_client/touch/TouchOptionsTests.java +++ b/src/test/java/io/appium/java_client/touch/TouchOptionsTests.java @@ -18,7 +18,6 @@ import io.appium.java_client.touch.offset.PointOption; import org.junit.Test; import org.openqa.selenium.Point; -import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.RemoteWebElement; import java.util.ArrayList; @@ -31,7 +30,7 @@ public class TouchOptionsTests { @Test(expected = IllegalArgumentException.class) public void invalidEmptyPointOptionsShouldFailOnBuild() { - new PointOption().build(); + new PointOption<>().build(); fail("The exception throwing was expected"); }