diff --git a/README.md b/README.md index 5782a7ec9..675bc01e1 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,8 @@ Appium java client has dedicated classes to support the following Appium drivers - [UiAutomator2](https://github.com/appium/appium-uiautomator2-driver) and [Espresso](https://github.com/appium/appium-espresso-driver): [AndroidDriver](src/main/java/io/appium/java_client/android/AndroidDriver.java) - [XCUITest](https://github.com/appium/appium-xcuitest-driver): [IOSDriver](src/main/java/io/appium/java_client/ios/IOSDriver.java) -- [Windows](https://github.com/appium/appium-windows-driver): [WindowsDriver](src/main/java/io/appium/java_client/windows/WindowsDriver.java) +- [Windows (deprecated, not supported since Appium 4)](https://github.com/appium/appium-windows-driver): [WindowsDriver](src/main/java/io/appium/java_client/windows/WindowsDriver.java) +- [Wincore](https://github.com/appium/appium-wincore-driver): [WincoreDriver](src/main/java/io/appium/java_client/wincore/WincoreDriver.java) - [Safari](https://github.com/appium/appium-safari-driver): [SafariDriver](src/main/java/io/appium/java_client/safari/SafariDriver.java) - [Gecko](https://github.com/appium/appium-geckodriver): [GeckoDriver](src/main/java/io/appium/java_client/gecko/GeckoDriver.java) - [Mac2](https://github.com/appium/appium-mac2-driver): [Mac2Driver](src/main/java/io/appium/java_client/mac/Mac2Driver.java) diff --git a/src/main/java/io/appium/java_client/remote/AutomationName.java b/src/main/java/io/appium/java_client/remote/AutomationName.java index ed5e9c699..35a0ddbcf 100644 --- a/src/main/java/io/appium/java_client/remote/AutomationName.java +++ b/src/main/java/io/appium/java_client/remote/AutomationName.java @@ -29,8 +29,16 @@ public interface AutomationName { String ESPRESSO = "Espresso"; /** The automation name of the Mac2 driver for macOS. */ String MAC2 = "Mac2"; - /** The automation name of the Windows driver. */ + /** + * The automation name of the Windows driver. + * + * @deprecated The Windows driver is deprecated and not supported since Appium 4. + * Use {@link #WINCORE} instead. + */ + @Deprecated String WINDOWS = "Windows"; + /** The automation name of the Wincore driver, the successor of the Windows driver. */ + String WINCORE = "Wincore"; /** The automation name of the Safari driver. */ String SAFARI = "Safari"; /** The automation name of the Gecko driver for Firefox. */ diff --git a/src/main/java/io/appium/java_client/wincore/WincoreDriver.java b/src/main/java/io/appium/java_client/wincore/WincoreDriver.java new file mode 100644 index 000000000..56f414cd8 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/WincoreDriver.java @@ -0,0 +1,263 @@ +/* + * 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.wincore; + +import io.appium.java_client.AppiumClientConfig; +import io.appium.java_client.AppiumDriver; +import io.appium.java_client.CommandExecutionHelper; +import io.appium.java_client.PullsFiles; +import io.appium.java_client.PushesFiles; +import io.appium.java_client.http.HttpClient; +import io.appium.java_client.remote.AppiumCommandExecutor; +import io.appium.java_client.remote.AutomationName; +import io.appium.java_client.remote.SupportsContextSwitching; +import io.appium.java_client.screenrecording.CanRecordScreen; +import io.appium.java_client.service.local.AppiumDriverLocalService; +import io.appium.java_client.service.local.AppiumServiceBuilder; +import org.jspecify.annotations.Nullable; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.Platform; + +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; + +import static io.appium.java_client.MobileCommand.PULL_FILE; +import static io.appium.java_client.MobileCommand.PUSH_FILE; +import static java.util.Objects.requireNonNull; + +/** + * WincoreDriver is an officially supported Appium driver created to automate Windows apps. + * It is the successor of the Windows driver, which is deprecated and not supported since Appium 4, + * see {@link io.appium.java_client.windows.WindowsDriver}. + * Read https://github.com/appium/appium-wincore-driver for more details. + */ +public class WincoreDriver extends AppiumDriver implements + PullsFiles, + PushesFiles, + SupportsContextSwitching, + CanRecordScreen { + private static final String PLATFORM_NAME = Platform.WINDOWS.name(); + private static final String AUTOMATION_NAME = AutomationName.WINCORE; + + /** + * Creates a new instance based on command {@code executor} and {@code capabilities}. + * + * @param executor is an instance of {@link AppiumCommandExecutor} + * or class that extends it. Default commands or another vendor-specific + * commands may be specified there. + * @param capabilities take a look at {@link Capabilities} + */ + public WincoreDriver(AppiumCommandExecutor executor, Capabilities capabilities) { + super(executor, ensurePlatformAndAutomationNames(capabilities, PLATFORM_NAME, AUTOMATION_NAME)); + } + + /** + * Creates a new instance based on Appium server URL and {@code capabilities}. + * + * @param remoteAddress is the address of remotely/locally started Appium server + * @param capabilities take a look at {@link Capabilities} + */ + public WincoreDriver(URL remoteAddress, Capabilities capabilities) { + super(remoteAddress, ensurePlatformAndAutomationNames( + capabilities, PLATFORM_NAME, AUTOMATION_NAME)); + } + + /** + * Creates a new instance based on Appium server URL, HTTP client factory and {@code capabilities}. + * + * @param remoteAddress is the address of remotely/locally started Appium server + * @param httpClientFactory take a look at {@link HttpClient.Factory} + * @param capabilities take a look at {@link Capabilities} + */ + public WincoreDriver(URL remoteAddress, HttpClient.Factory httpClientFactory, Capabilities capabilities) { + super(remoteAddress, httpClientFactory, ensurePlatformAndAutomationNames( + capabilities, PLATFORM_NAME, AUTOMATION_NAME)); + } + + /** + * Creates a new instance based on Appium driver local service and {@code capabilities}. + * + * @param service take a look at {@link AppiumDriverLocalService} + * @param capabilities take a look at {@link Capabilities} + */ + public WincoreDriver(AppiumDriverLocalService service, Capabilities capabilities) { + super(service, ensurePlatformAndAutomationNames(capabilities, PLATFORM_NAME, AUTOMATION_NAME)); + } + + /** + * Creates a new instance based on Appium driver local service, HTTP client factory and {@code capabilities}. + * + * @param service take a look at {@link AppiumDriverLocalService} + * @param httpClientFactory take a look at {@link HttpClient.Factory} + * @param capabilities take a look at {@link Capabilities} + */ + public WincoreDriver(AppiumDriverLocalService service, HttpClient.Factory httpClientFactory, + Capabilities capabilities) { + super(service, httpClientFactory, ensurePlatformAndAutomationNames( + capabilities, PLATFORM_NAME, AUTOMATION_NAME)); + } + + /** + * Creates a new instance based on Appium service builder and {@code capabilities}. + * + * @param builder take a look at {@link AppiumServiceBuilder} + * @param capabilities take a look at {@link Capabilities} + */ + public WincoreDriver(AppiumServiceBuilder builder, Capabilities capabilities) { + super(builder, ensurePlatformAndAutomationNames(capabilities, PLATFORM_NAME, AUTOMATION_NAME)); + } + + /** + * Creates a new instance based on Appium service builder, HTTP client factory and {@code capabilities}. + * + * @param builder take a look at {@link AppiumServiceBuilder} + * @param httpClientFactory take a look at {@link HttpClient.Factory} + * @param capabilities take a look at {@link Capabilities} + */ + public WincoreDriver(AppiumServiceBuilder builder, HttpClient.Factory httpClientFactory, + Capabilities capabilities) { + super(builder, httpClientFactory, ensurePlatformAndAutomationNames( + capabilities, PLATFORM_NAME, AUTOMATION_NAME)); + } + + /** + * Creates a new instance based on HTTP client factory and {@code capabilities}. + * + * @param httpClientFactory take a look at {@link HttpClient.Factory} + * @param capabilities take a look at {@link Capabilities} + */ + public WincoreDriver(HttpClient.Factory httpClientFactory, Capabilities capabilities) { + super(httpClientFactory, ensurePlatformAndAutomationNames( + capabilities, PLATFORM_NAME, AUTOMATION_NAME)); + } + + /** + * Creates a new instance connected to an already running driver session. + * Intended for debugging purposes only; the caller maintains the session state. + * + * @param remoteSessionAddress The address of the **running** session including the session identifier. + */ + public WincoreDriver(URL remoteSessionAddress) { + super(remoteSessionAddress, PLATFORM_NAME, AUTOMATION_NAME); + } + + /** + * Creates a new instance based on the given AppiumClientConfig and {@code capabilities}. + * The HTTP client is default client generated by {@link HttpClient.Factory#createDefault()}. + * For example: + * + *
+     *
+     * AppiumClientConfig appiumClientConfig = AppiumClientConfig.defaultConfig()
+     *     .directConnect(true)
+     *     .baseUri(URI.create("WebDriver URL"))
+     *     .readTimeout(Duration.ofMinutes(5));
+     * WincoreOptions options = new WincoreOptions();
+     * WincoreDriver driver = new WincoreDriver(appiumClientConfig, options);
+     *
+     * 
+ * + * @param appiumClientConfig take a look at {@link AppiumClientConfig} + * @param capabilities take a look at {@link Capabilities} + * + */ + public WincoreDriver(AppiumClientConfig appiumClientConfig, Capabilities capabilities) { + super(appiumClientConfig, ensurePlatformAndAutomationNames( + capabilities, PLATFORM_NAME, AUTOMATION_NAME)); + } + + /** + * Creates a new instance based on {@code capabilities}. + * + * @param capabilities take a look at {@link Capabilities} + */ + public WincoreDriver(Capabilities capabilities) { + super(ensurePlatformAndAutomationNames(capabilities, PLATFORM_NAME, AUTOMATION_NAME)); + } + + /** + * Pulls a file from the remote system. The Wincore driver has no `mobile:` extension for it. + * The server must enable the {@code wincore:modify_fs} insecure feature, + * for example {@code --allow-insecure=wincore:modify_fs}. + * + * @param remotePath Path to the file to read data from the remote device. + * @return A byte array of Base64 encoded data. + */ + @Override + public byte[] pullFile(String remotePath) { + return decodeBase64(CommandExecutionHelper.execute(this, Map.entry(PULL_FILE, Map.of("path", remotePath)))); + } + + /** + * Not supported by the Wincore driver. + * + * @param remotePath Path to the folder to read data from the remote device. + * @return Never returns normally. + * @throws UnsupportedOperationException always. + */ + @Override + public byte[] pullFolder(String remotePath) { + throw new UnsupportedOperationException("The Wincore driver cannot pull folders"); + } + + /** + * Saves base64-encoded data as a file on the remote system. The Wincore driver has no `mobile:` extension for it. + * The server must enable the {@code wincore:modify_fs} insecure feature, + * for example {@code --allow-insecure=wincore:modify_fs}. + * + * @param remotePath Path to the file to write data to on the remote device. + * @param base64Data Base64 encoded byte array of the data to write to the remote device. + */ + @Override + public void pushFile(String remotePath, byte[] base64Data) { + CommandExecutionHelper.execute(this, Map.entry(PUSH_FILE, Map.of( + "path", remotePath, + "data", new String(base64Data, StandardCharsets.UTF_8) + ))); + } + + /** + * Starts the screen recording with the default options. The driver returns no value, + * so the options overload is used to stay null-safe. + * + * @return `not used`. + */ + @Override + public String startRecordingScreen() { + return startRecordingScreen(new WincoreStartScreenRecordingOptions()); + } + + private static byte[] decodeBase64(@Nullable String base64String) { + return Base64.getDecoder().decode(requireNonNull(base64String).getBytes(StandardCharsets.UTF_8)); + } + + /** + * Launch the application app under test after it was closed. + */ + public void launchApp() { + CommandExecutionHelper.executeScript(this, "windows: launchApp"); + } + + /** + * Close the app under test. + */ + public void closeApp() { + CommandExecutionHelper.executeScript(this, "windows: closeApp"); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/WincoreStartScreenRecordingOptions.java b/src/main/java/io/appium/java_client/wincore/WincoreStartScreenRecordingOptions.java new file mode 100644 index 000000000..b86932342 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/WincoreStartScreenRecordingOptions.java @@ -0,0 +1,163 @@ +/* + * 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.wincore; + +import io.appium.java_client.screenrecording.BaseStartScreenRecordingOptions; + +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static java.util.Optional.ofNullable; + +/** Wincore-specific options for starting a screen recording. */ +public class WincoreStartScreenRecordingOptions + extends BaseStartScreenRecordingOptions { + /** + * Creates a new instance. + */ + public WincoreStartScreenRecordingOptions() { + } + + private Integer fps; + private String videoFilter; + private String preset; + private Boolean captureCursor; + private Boolean captureClicks; + private String audioInput; + + /** + * Creates a new options instance. + * + * @return a new {@link WincoreStartScreenRecordingOptions} instance. + */ + public static WincoreStartScreenRecordingOptions startScreenRecordingOptions() { + return new WincoreStartScreenRecordingOptions(); + } + + /** + * The count of frames per second in the resulting video. + * Increasing fps value also increases the size of the resulting + * video file and the CPU usage. + * + * @param fps The actual frames per second value. + * The default value is 15. + * @return self instance for chaining. + */ + public WincoreStartScreenRecordingOptions withFps(int fps) { + this.fps = fps; + return this; + } + + /** + * Whether to capture the mouse cursor while recording + * the screen. Disabled by default. + * + * @return self instance for chaining. + */ + public WincoreStartScreenRecordingOptions enableCursorCapture() { + this.captureCursor = true; + return this; + } + + /** + * Whether to capture the click gestures while recording + * the screen. Disabled by default. + * + * @return self instance for chaining. + */ + public WincoreStartScreenRecordingOptions enableClicksCapture() { + this.captureClicks = true; + return this; + } + + /** + * If provided then the given audio input will be used to record the computer audio + * along with the desktop video. The list of available devices could be retrieved using + * `ffmpeg -list_devices true -f dshow -i dummy` command. + * + * @param audioInput One of valid audio input names listed by ffmpeg + * @return self instance for chaining. + */ + public WincoreStartScreenRecordingOptions withAudioInput(String audioInput) { + this.audioInput = audioInput; + return this; + } + + /** + * The video filter spec to apply for ffmpeg. + * See https://trac.ffmpeg.org/wiki/FilteringGuide for more details on the possible values. + * Example: Set it to `scale=ifnot(gte(iw\,1024)\,iw\,1024):-2` in order to limit the video width + * to 1024px. The height will be adjusted automatically to match the actual screen aspect ratio. + * + * @param videoFilter Valid ffmpeg video filter spec string. + * @return self instance for chaining. + */ + public WincoreStartScreenRecordingOptions withVideoFilter(String videoFilter) { + this.videoFilter = videoFilter; + return this; + } + + /** + * A preset is a collection of options that will provide a certain encoding speed to compression ratio. + * A slower preset will provide better compression (compression is quality per filesize). + * This means that, for example, if you target a certain file size or constant bit rate, you will + * achieve better quality with a slower preset. Read https://trac.ffmpeg.org/wiki/Encode/H.264 + * for more details. + * + * @param preset One of the supported encoding presets. Possible values are: + * - ultrafast + * - superfast + * - veryfast (default) + * - faster + * - fast + * - medium + * - slow + * - slower + * - veryslow + * @return self instance for chaining. + */ + public WincoreStartScreenRecordingOptions withPreset(String preset) { + this.preset = preset; + return this; + } + + /** + * The maximum recording time. The default value is 600 seconds (10 minutes). + * The minimum time resolution unit is one second. + * + * @param timeLimit The actual time limit of the recorded video. + * @return self instance for chaining. + */ + @Override + public WincoreStartScreenRecordingOptions withTimeLimit(Duration timeLimit) { + return super.withTimeLimit(timeLimit); + } + + @Override + public Map build() { + var map = new HashMap<>(super.build()); + ofNullable(fps).map(x -> map.put("videoFps", x)); + ofNullable(preset).map(x -> map.put("preset", x)); + ofNullable(videoFilter).map(x -> map.put("videoFilter", x)); + ofNullable(captureClicks).map(x -> map.put("captureClicks", x)); + ofNullable(captureCursor).map(x -> map.put("captureCursor", x)); + ofNullable(audioInput).map(x -> map.put("audioInput", x)); + return Collections.unmodifiableMap(map); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/WincoreStopScreenRecordingOptions.java b/src/main/java/io/appium/java_client/wincore/WincoreStopScreenRecordingOptions.java new file mode 100644 index 000000000..0bda75260 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/WincoreStopScreenRecordingOptions.java @@ -0,0 +1,39 @@ +/* + * 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.wincore; + +import io.appium.java_client.screenrecording.BaseStopScreenRecordingOptions; + +/** Wincore-specific options for stopping a screen recording. */ +public class WincoreStopScreenRecordingOptions extends + BaseStopScreenRecordingOptions { + /** + * Creates a new instance. + */ + public WincoreStopScreenRecordingOptions() { + } + + /** + * Creates a new options instance. + * + * @return a new {@link WincoreStopScreenRecordingOptions} instance. + */ + public static WincoreStopScreenRecordingOptions stopScreenRecordingOptions() { + return new WincoreStopScreenRecordingOptions(); + } + +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsChromedriverCdnUrlOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsChromedriverCdnUrlOption.java new file mode 100644 index 000000000..2989f8c6d --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsChromedriverCdnUrlOption.java @@ -0,0 +1,55 @@ +/* + * 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.wincore.options; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +/** + * Provides getters and setters for the {@code chromedriverCdnUrl} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsChromedriverCdnUrlOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code chromedriverCdnUrl} capability. + */ + String CHROMEDRIVER_CDN_URL_OPTION = "chromedriverCdnUrl"; + + /** + * The custom CDN URL to download chromedriver binaries from. + * + * @param value The capability value. + * @return self instance for chaining. + */ + default T setChromedriverCdnUrl(String value) { + return amend(CHROMEDRIVER_CDN_URL_OPTION, value); + } + + /** + * Get the custom CDN URL to download chromedriver binaries from. + * + * @return The capability value. + */ + default Optional getChromedriverCdnUrl() { + return Optional.ofNullable((String) getCapability(CHROMEDRIVER_CDN_URL_OPTION)); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsChromedriverExecutablePathOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsChromedriverExecutablePathOption.java new file mode 100644 index 000000000..640bc9557 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsChromedriverExecutablePathOption.java @@ -0,0 +1,55 @@ +/* + * 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.wincore.options; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +/** + * Provides getters and setters for the {@code chromedriverExecutablePath} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsChromedriverExecutablePathOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code chromedriverExecutablePath} capability. + */ + String CHROMEDRIVER_EXECUTABLE_PATH_OPTION = "chromedriverExecutablePath"; + + /** + * Full path to the local chromedriver binary on the server file system. + * + * @param value The capability value. + * @return self instance for chaining. + */ + default T setChromedriverExecutablePath(String value) { + return amend(CHROMEDRIVER_EXECUTABLE_PATH_OPTION, value); + } + + /** + * Get the path to the local chromedriver binary on the server file system. + * + * @return The capability value. + */ + default Optional getChromedriverExecutablePath() { + return Optional.ofNullable((String) getCapability(CHROMEDRIVER_EXECUTABLE_PATH_OPTION)); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsDelayAfterClickOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsDelayAfterClickOption.java new file mode 100644 index 000000000..e7d74355c --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsDelayAfterClickOption.java @@ -0,0 +1,58 @@ +/* + * 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.wincore.options; + +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; + +/** + * Provides getters and setters for the {@code delayAfterClick} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsDelayAfterClickOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code delayAfterClick} capability. + */ + String DELAY_AFTER_CLICK_OPTION = "delayAfterClick"; + + /** + * The delay after each click. The minimum time resolution unit is one millisecond. + * + * @param value The delay value. + * @return self instance for chaining. + */ + default T setDelayAfterClick(Duration value) { + return amend(DELAY_AFTER_CLICK_OPTION, value.toMillis()); + } + + /** + * Get the delay after each click. + * + * @return The capability value. + */ + default Optional getDelayAfterClick() { + return Optional.ofNullable(toDuration(getCapability(DELAY_AFTER_CLICK_OPTION))); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsDelayBeforeClickOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsDelayBeforeClickOption.java new file mode 100644 index 000000000..7bdbce853 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsDelayBeforeClickOption.java @@ -0,0 +1,58 @@ +/* + * 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.wincore.options; + +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; + +/** + * Provides getters and setters for the {@code delayBeforeClick} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsDelayBeforeClickOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code delayBeforeClick} capability. + */ + String DELAY_BEFORE_CLICK_OPTION = "delayBeforeClick"; + + /** + * The delay before each click. The minimum time resolution unit is one millisecond. + * + * @param value The delay value. + * @return self instance for chaining. + */ + default T setDelayBeforeClick(Duration value) { + return amend(DELAY_BEFORE_CLICK_OPTION, value.toMillis()); + } + + /** + * Get the delay before each click. + * + * @return The capability value. + */ + default Optional getDelayBeforeClick() { + return Optional.ofNullable(toDuration(getCapability(DELAY_BEFORE_CLICK_OPTION))); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsEdgedriverCdnUrlOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsEdgedriverCdnUrlOption.java new file mode 100644 index 000000000..bb6f08c33 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsEdgedriverCdnUrlOption.java @@ -0,0 +1,55 @@ +/* + * 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.wincore.options; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +/** + * Provides getters and setters for the {@code edgedriverCdnUrl} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsEdgedriverCdnUrlOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code edgedriverCdnUrl} capability. + */ + String EDGEDRIVER_CDN_URL_OPTION = "edgedriverCdnUrl"; + + /** + * The custom CDN URL to download msedgedriver binaries from. + * + * @param value The capability value. + * @return self instance for chaining. + */ + default T setEdgedriverCdnUrl(String value) { + return amend(EDGEDRIVER_CDN_URL_OPTION, value); + } + + /** + * Get the custom CDN URL to download msedgedriver binaries from. + * + * @return The capability value. + */ + default Optional getEdgedriverCdnUrl() { + return Optional.ofNullable((String) getCapability(EDGEDRIVER_CDN_URL_OPTION)); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsEdgedriverExecutablePathOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsEdgedriverExecutablePathOption.java new file mode 100644 index 000000000..98663b97a --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsEdgedriverExecutablePathOption.java @@ -0,0 +1,55 @@ +/* + * 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.wincore.options; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +/** + * Provides getters and setters for the {@code edgedriverExecutablePath} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsEdgedriverExecutablePathOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code edgedriverExecutablePath} capability. + */ + String EDGEDRIVER_EXECUTABLE_PATH_OPTION = "edgedriverExecutablePath"; + + /** + * Full path to the local msedgedriver binary on the server file system. + * + * @param value The capability value. + * @return self instance for chaining. + */ + default T setEdgedriverExecutablePath(String value) { + return amend(EDGEDRIVER_EXECUTABLE_PATH_OPTION, value); + } + + /** + * Get the path to the local msedgedriver binary on the server file system. + * + * @return The capability value. + */ + default Optional getEdgedriverExecutablePath() { + return Optional.ofNullable((String) getCapability(EDGEDRIVER_EXECUTABLE_PATH_OPTION)); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsFfmpegExecutablePathOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsFfmpegExecutablePathOption.java new file mode 100644 index 000000000..d075f18f2 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsFfmpegExecutablePathOption.java @@ -0,0 +1,55 @@ +/* + * 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.wincore.options; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +/** + * Provides getters and setters for the {@code ffmpegExecutablePath} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsFfmpegExecutablePathOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code ffmpegExecutablePath} capability. + */ + String FFMPEG_EXECUTABLE_PATH_OPTION = "ffmpegExecutablePath"; + + /** + * Full path to the local ffmpeg binary on the server file system, used for screen recording. + * + * @param value The capability value. + * @return self instance for chaining. + */ + default T setFfmpegExecutablePath(String value) { + return amend(FFMPEG_EXECUTABLE_PATH_OPTION, value); + } + + /** + * Get the path to the local ffmpeg binary on the server file system. + * + * @return The capability value. + */ + default Optional getFfmpegExecutablePath() { + return Optional.ofNullable((String) getCapability(FFMPEG_EXECUTABLE_PATH_OPTION)); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsIsolatedScriptExecutionOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsIsolatedScriptExecutionOption.java new file mode 100644 index 000000000..c51809012 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsIsolatedScriptExecutionOption.java @@ -0,0 +1,58 @@ +/* + * 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.wincore.options; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +import static io.appium.java_client.internal.CapabilityHelpers.toSafeBoolean; + +/** + * Provides getters and setters for the {@code isolatedScriptExecution} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsIsolatedScriptExecutionOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code isolatedScriptExecution} capability. + */ + String ISOLATED_SCRIPT_EXECUTION_OPTION = "isolatedScriptExecution"; + + /** + * Whether the {@code prerun} and {@code postrun} PowerShell scripts are executed in an isolated + * PowerShell instance. Disabled by default. + * + * @param value True to enable, false to disable. + * @return self instance for chaining. + */ + default T setIsolatedScriptExecution(boolean value) { + return amend(ISOLATED_SCRIPT_EXECUTION_OPTION, value); + } + + /** + * Get whether the PowerShell scripts are executed in an isolated PowerShell instance. + * + * @return The capability value. + */ + default Optional isIsolatedScriptExecution() { + return Optional.ofNullable(toSafeBoolean(getCapability(ISOLATED_SCRIPT_EXECUTION_OPTION))); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsLogFileOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsLogFileOption.java new file mode 100644 index 000000000..c80182a6f --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsLogFileOption.java @@ -0,0 +1,55 @@ +/* + * 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.wincore.options; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +/** + * Provides getters and setters for the {@code logFile} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsLogFileOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code logFile} capability. + */ + String LOG_FILE_OPTION = "logFile"; + + /** + * Path to the file on the server file system, where the driver log is mirrored. + * + * @param value The capability value. + * @return self instance for chaining. + */ + default T setLogFile(String value) { + return amend(LOG_FILE_OPTION, value); + } + + /** + * Get the path to the file where the driver log is mirrored. + * + * @return The capability value. + */ + default Optional getLogFile() { + return Optional.ofNullable((String) getCapability(LOG_FILE_OPTION)); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsMsForceQuitOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsMsForceQuitOption.java new file mode 100644 index 000000000..af5db2ff9 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsMsForceQuitOption.java @@ -0,0 +1,58 @@ +/* + * 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.wincore.options; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +import static io.appium.java_client.internal.CapabilityHelpers.toSafeBoolean; + +/** + * Provides getters and setters for the {@code ms:forcequit} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsMsForceQuitOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code ms:forcequit} capability. + */ + String MS_FORCE_QUIT_OPTION = "ms:forcequit"; + + /** + * Whether the application process is force-killed at the end of the session. Only applies if + * {@code shouldCloseApp} is enabled. + * + * @param value True to enable, false to disable. + * @return self instance for chaining. + */ + default T setForceQuit(boolean value) { + return amend(MS_FORCE_QUIT_OPTION, value); + } + + /** + * Get whether the application process is force-killed at the end of the session. + * + * @return The capability value. + */ + default Optional isForceQuit() { + return Optional.ofNullable(toSafeBoolean(getCapability(MS_FORCE_QUIT_OPTION))); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsPerfMetricsOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsPerfMetricsOption.java new file mode 100644 index 000000000..ed7f6f7c9 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsPerfMetricsOption.java @@ -0,0 +1,59 @@ +/* + * 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.wincore.options; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +import static io.appium.java_client.internal.CapabilityHelpers.toSafeBoolean; + +/** + * Provides getters and setters for the {@code perfMetrics} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsPerfMetricsOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code perfMetrics} capability. + */ + String PERF_METRICS_OPTION = "perfMetrics"; + + /** + * Whether the per-session performance counters are collected. They are read and reset via the + * {@code windows: getPerfMetrics} and {@code windows: resetPerfMetrics} extensions. Disabled by + * default. + * + * @param value True to enable, false to disable. + * @return self instance for chaining. + */ + default T setPerfMetrics(boolean value) { + return amend(PERF_METRICS_OPTION, value); + } + + /** + * Get whether the per-session performance counters are collected. + * + * @return The capability value. + */ + default Optional isPerfMetrics() { + return Optional.ofNullable(toSafeBoolean(getCapability(PERF_METRICS_OPTION))); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsShouldCloseAppOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsShouldCloseAppOption.java new file mode 100644 index 000000000..73828d2e1 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsShouldCloseAppOption.java @@ -0,0 +1,58 @@ +/* + * 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.wincore.options; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +import static io.appium.java_client.internal.CapabilityHelpers.toSafeBoolean; + +/** + * Provides getters and setters for the {@code shouldCloseApp} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsShouldCloseAppOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code shouldCloseApp} capability. + */ + String SHOULD_CLOSE_APP_OPTION = "shouldCloseApp"; + + /** + * Whether to close the application window at the end of the session. Ignored for the {@code + * Root} app. Enabled by default. + * + * @param value True to enable, false to disable. + * @return self instance for chaining. + */ + default T setShouldCloseApp(boolean value) { + return amend(SHOULD_CLOSE_APP_OPTION, value); + } + + /** + * Get whether the application window is closed at the end of the session. + * + * @return The capability value. + */ + default Optional isShouldCloseApp() { + return Optional.ofNullable(toSafeBoolean(getCapability(SHOULD_CLOSE_APP_OPTION))); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsSmoothPointerMoveOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsSmoothPointerMoveOption.java new file mode 100644 index 000000000..0aaabedc2 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsSmoothPointerMoveOption.java @@ -0,0 +1,55 @@ +/* + * 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.wincore.options; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +/** + * Provides getters and setters for the {@code smoothPointerMove} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsSmoothPointerMoveOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code smoothPointerMove} capability. + */ + String SMOOTH_POINTER_MOVE_OPTION = "smoothPointerMove"; + + /** + * The CSS easing function used to interpolate the mouse pointer movement, for example {@code ease-in}. + * + * @param value The capability value. + * @return self instance for chaining. + */ + default T setSmoothPointerMove(String value) { + return amend(SMOOTH_POINTER_MOVE_OPTION, value); + } + + /** + * Get the CSS easing function used to interpolate the mouse pointer movement. + * + * @return The capability value. + */ + default Optional getSmoothPointerMove() { + return Optional.ofNullable((String) getCapability(SMOOTH_POINTER_MOVE_OPTION)); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsWaitForAppLaunchOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsWaitForAppLaunchOption.java new file mode 100644 index 000000000..a2cbc1b35 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsWaitForAppLaunchOption.java @@ -0,0 +1,66 @@ +/* + * 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.wincore.options; + +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; + +/** + * Provides getters and setters for the {@code ms:waitForAppLaunch} capability. + * The driver treats values up to 120 as seconds and greater values as milliseconds. + * + * @param options type, used for chaining. + */ +public interface SupportsWaitForAppLaunchOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code ms:waitForAppLaunch} capability. + */ + String MS_WAIT_FOR_APP_LAUNCH_OPTION = "ms:waitForAppLaunch"; + + /** + * The time to wait for the application window after the app launch is initiated. + * Whole seconds up to 120 are sent as seconds, anything else is sent as milliseconds. + * Durations shorter than 121 ms cannot be expressed and are rounded up to one second. + * + * @param timeout The timeout value. + * @return self instance for chaining. + */ + default T setWaitForAppLaunch(Duration timeout) { + long ms = timeout.toMillis(); + if (ms <= 120_000 && ms % 1000 == 0) { + return amend(MS_WAIT_FOR_APP_LAUNCH_OPTION, ms / 1000); + } + return amend(MS_WAIT_FOR_APP_LAUNCH_OPTION, ms > 120 ? ms : 1); + } + + /** + * Get the time to wait for the application window after the app launch is initiated. + * + * @return The timeout value. + */ + default Optional doesWaitForAppLaunch() { + return Optional.ofNullable(toDuration(getCapability(MS_WAIT_FOR_APP_LAUNCH_OPTION), raw -> + raw > 120 ? Duration.ofMillis(raw) : Duration.ofSeconds(raw))); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/SupportsWebviewEnabledOption.java b/src/main/java/io/appium/java_client/wincore/options/SupportsWebviewEnabledOption.java new file mode 100644 index 000000000..39bb25b53 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/SupportsWebviewEnabledOption.java @@ -0,0 +1,57 @@ +/* + * 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.wincore.options; + +import io.appium.java_client.remote.options.BaseOptions; +import io.appium.java_client.remote.options.CanSetCapability; +import org.openqa.selenium.Capabilities; + +import java.util.Optional; + +import static io.appium.java_client.internal.CapabilityHelpers.toSafeBoolean; + +/** + * Provides getters and setters for the {@code webviewEnabled} capability. + * + * @param options type, used for chaining. + */ +public interface SupportsWebviewEnabledOption> extends + Capabilities, CanSetCapability { + /** + * Name of the {@code webviewEnabled} capability. + */ + String WEBVIEW_ENABLED_OPTION = "webviewEnabled"; + + /** + * Whether the automation of WebView2, Chrome and Edge web contents over the Chrome DevTools Protocol is enabled. + * + * @param value True to enable, false to disable. + * @return self instance for chaining. + */ + default T setWebviewEnabled(boolean value) { + return amend(WEBVIEW_ENABLED_OPTION, value); + } + + /** + * Get whether the automation of web contents is enabled. + * + * @return The capability value. + */ + default Optional isWebviewEnabled() { + return Optional.ofNullable(toSafeBoolean(getCapability(WEBVIEW_ENABLED_OPTION))); + } +} diff --git a/src/main/java/io/appium/java_client/wincore/options/WincoreOptions.java b/src/main/java/io/appium/java_client/wincore/options/WincoreOptions.java new file mode 100644 index 000000000..d2e782593 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/WincoreOptions.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.wincore.options; + +import io.appium.java_client.android.options.context.SupportsWebviewDevtoolsPortOption; +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.SupportsDeviceNameOption; +import io.appium.java_client.remote.options.SupportsPostrunOption; +import io.appium.java_client.remote.options.SupportsPrerunOption; +import io.appium.java_client.windows.options.PowerShellData; +import io.appium.java_client.windows.options.SupportsAppArgumentsOption; +import io.appium.java_client.windows.options.SupportsAppTopLevelWindowOption; +import io.appium.java_client.windows.options.SupportsAppWorkingDirOption; +import org.openqa.selenium.Capabilities; + +import java.util.Map; +import java.util.Optional; + +/** + * Capabilities of the Wincore driver. + * See https://github.com/appium/appium-wincore-driver#capabilities + * for more details. + * + *

The driver replaces appium-windows-driver, which is deprecated and not supported since Appium 4, see + * {@link io.appium.java_client.windows.options.WindowsOptions}.

+ */ +public class WincoreOptions extends BaseOptions implements + SupportsAppOption, + SupportsDeviceNameOption, + SupportsAppArgumentsOption, + SupportsAppTopLevelWindowOption, + SupportsAppWorkingDirOption, + SupportsWaitForAppLaunchOption, + SupportsMsForceQuitOption, + SupportsShouldCloseAppOption, + SupportsIsolatedScriptExecutionOption, + SupportsSmoothPointerMoveOption, + SupportsDelayBeforeClickOption, + SupportsDelayAfterClickOption, + SupportsWebviewEnabledOption, + SupportsWebviewDevtoolsPortOption, + SupportsChromedriverExecutablePathOption, + SupportsEdgedriverExecutablePathOption, + SupportsChromedriverCdnUrlOption, + SupportsEdgedriverCdnUrlOption, + SupportsFfmpegExecutablePathOption, + SupportsLogFileOption, + SupportsPerfMetricsOption, + SupportsPrerunOption, + SupportsPostrunOption { + /** Creates options with the default capabilities set. */ + public WincoreOptions() { + setCommonOptions(); + } + + /** + * Creates options from the given capabilities. + * + * @param source The capabilities to copy. + */ + public WincoreOptions(Capabilities source) { + super(source); + setCommonOptions(); + } + + /** + * Creates options from the given capabilities map. + * + * @param source The capabilities to copy. + */ + public WincoreOptions(Map source) { + super(source); + setCommonOptions(); + } + + private void setCommonOptions() { + setPlatformName(MobilePlatform.WINDOWS); + setAutomationName(AutomationName.WINCORE); + } + + /** + * 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 session startup. + * + * @param script E.g. {script: 'Get-Process outlook -ErrorAction SilentlyContinue'}. + * @return self instance for chaining. + */ + public WincoreOptions 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 the session is finished. + * + * @param script E.g. {script: 'Get-Process outlook -ErrorAction SilentlyContinue'}. + * @return self instance for chaining. + */ + public WincoreOptions 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/main/java/io/appium/java_client/windows/WindowsDriver.java b/src/main/java/io/appium/java_client/windows/WindowsDriver.java index 5db88bf58..d4684f989 100644 --- a/src/main/java/io/appium/java_client/windows/WindowsDriver.java +++ b/src/main/java/io/appium/java_client/windows/WindowsDriver.java @@ -42,9 +42,13 @@ import static java.util.Objects.requireNonNull; /** - * WindowsDriver is an officially supported Appium driver created to automate Windows apps. + * WindowsDriver is an Appium driver created to automate Windows apps. * Read https://github.com/appium/appium-windows-driver for more details. + * + * @deprecated The Windows driver is deprecated and not supported since Appium 4. + * Use {@link io.appium.java_client.wincore.WincoreDriver} instead. */ +@Deprecated public class WindowsDriver extends AppiumDriver implements PullsFiles, PushesFiles, diff --git a/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java b/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java index ef4a4b4bf..de2c21a0a 100644 --- a/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java @@ -25,7 +25,13 @@ import static java.util.Optional.ofNullable; -/** Windows-specific options for starting a screen recording. */ +/** + * Windows-specific options for starting a screen recording. + * + * @deprecated The Windows driver is deprecated and not supported since Appium 4. + * Use {@link io.appium.java_client.wincore.WincoreStartScreenRecordingOptions} instead. + */ +@Deprecated public class WindowsStartScreenRecordingOptions extends BaseStartScreenRecordingOptions { /** diff --git a/src/main/java/io/appium/java_client/windows/WindowsStopScreenRecordingOptions.java b/src/main/java/io/appium/java_client/windows/WindowsStopScreenRecordingOptions.java index 90c3a9fc6..7de721548 100644 --- a/src/main/java/io/appium/java_client/windows/WindowsStopScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/windows/WindowsStopScreenRecordingOptions.java @@ -18,7 +18,13 @@ import io.appium.java_client.screenrecording.BaseStopScreenRecordingOptions; -/** Windows-specific options for stopping a screen recording. */ +/** + * Windows-specific options for stopping a screen recording. + * + * @deprecated The Windows driver is deprecated and not supported since Appium 4. + * Use {@link io.appium.java_client.wincore.WincoreStopScreenRecordingOptions} instead. + */ +@Deprecated public class WindowsStopScreenRecordingOptions extends BaseStopScreenRecordingOptions { /** diff --git a/src/main/java/io/appium/java_client/windows/options/SupportsCreateSessionTimeoutOption.java b/src/main/java/io/appium/java_client/windows/options/SupportsCreateSessionTimeoutOption.java index b36174f28..19a9b891c 100644 --- a/src/main/java/io/appium/java_client/windows/options/SupportsCreateSessionTimeoutOption.java +++ b/src/main/java/io/appium/java_client/windows/options/SupportsCreateSessionTimeoutOption.java @@ -27,9 +27,13 @@ /** * Support for the {@code createSessionTimeout} capability: the timeout for creating a session. - * - * @param the concrete options type, returned for chaining + * + * @param the concrete options type, returned for chaining + * + * @deprecated The Windows driver is deprecated and not supported since Appium 4. + * Its successor, the Wincore driver, does not support this capability. */ +@Deprecated public interface SupportsCreateSessionTimeoutOption> extends Capabilities, CanSetCapability { /** diff --git a/src/main/java/io/appium/java_client/windows/options/SupportsMsExperimentalWebDriverOption.java b/src/main/java/io/appium/java_client/windows/options/SupportsMsExperimentalWebDriverOption.java index ade5084df..ce5f7be49 100644 --- a/src/main/java/io/appium/java_client/windows/options/SupportsMsExperimentalWebDriverOption.java +++ b/src/main/java/io/appium/java_client/windows/options/SupportsMsExperimentalWebDriverOption.java @@ -26,9 +26,13 @@ /** * Support for the {@code ms:experimental-webdriver} capability: whether experimental driver features are enabled. - * - * @param the concrete options type, returned for chaining + * + * @param the concrete options type, returned for chaining + * + * @deprecated The Windows driver is deprecated and not supported since Appium 4. + * Its successor, the Wincore driver, does not support this capability. */ +@Deprecated public interface SupportsMsExperimentalWebDriverOption> extends Capabilities, CanSetCapability { /** diff --git a/src/main/java/io/appium/java_client/windows/options/SupportsMsWaitForAppLaunchOption.java b/src/main/java/io/appium/java_client/windows/options/SupportsMsWaitForAppLaunchOption.java index 8e766cc7d..d53ac49c3 100644 --- a/src/main/java/io/appium/java_client/windows/options/SupportsMsWaitForAppLaunchOption.java +++ b/src/main/java/io/appium/java_client/windows/options/SupportsMsWaitForAppLaunchOption.java @@ -27,9 +27,13 @@ /** * Support for the {@code ms:waitForAppLaunch} capability: the time to wait for the application to launch. - * - * @param the concrete options type, returned for chaining + * + * @param the concrete options type, returned for chaining + * + * @deprecated The Windows driver is deprecated and not supported since Appium 4. + * Use {@link io.appium.java_client.wincore.options.SupportsWaitForAppLaunchOption} instead. */ +@Deprecated public interface SupportsMsWaitForAppLaunchOption> extends Capabilities, CanSetCapability { /** diff --git a/src/main/java/io/appium/java_client/windows/options/SupportsSystemPortOption.java b/src/main/java/io/appium/java_client/windows/options/SupportsSystemPortOption.java index 8e12a01f8..e900fa687 100644 --- a/src/main/java/io/appium/java_client/windows/options/SupportsSystemPortOption.java +++ b/src/main/java/io/appium/java_client/windows/options/SupportsSystemPortOption.java @@ -26,9 +26,13 @@ /** * Support for the {@code systemPort} capability: the port the driver server listens on. - * - * @param the concrete options type, returned for chaining + * + * @param the concrete options type, returned for chaining + * + * @deprecated The Windows driver is deprecated and not supported since Appium 4. + * Its successor, the Wincore driver, does not support this capability. */ +@Deprecated public interface SupportsSystemPortOption> extends Capabilities, CanSetCapability { /** 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 9d7d54e9d..80fff5d0e 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 @@ -30,9 +30,10 @@ /** * https://github.com/appium/appium-windows-driver#usage. * - *

Note: appium-windows-driver is deprecated in favor of appium-wincore-driver, - * which has its own automation name and capability set.

+ * @deprecated The Windows driver is deprecated and not supported since Appium 4. + * Use {@link io.appium.java_client.wincore.options.WincoreOptions} instead. */ +@Deprecated public class WindowsOptions extends BaseOptions implements SupportsAppOption, SupportsAppArgumentsOption, 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 index 4ab700ca3..f5a9716a8 100644 --- a/src/test/java/io/appium/java_client/drivers/options/OptionsBuildingTest.java +++ b/src/test/java/io/appium/java_client/drivers/options/OptionsBuildingTest.java @@ -32,6 +32,7 @@ import io.appium.java_client.remote.AutomationName; import io.appium.java_client.safari.options.SafariOptions; import io.appium.java_client.safari.options.WebrtcData; +import io.appium.java_client.wincore.options.WincoreOptions; import io.appium.java_client.windows.options.PowerShellData; import io.appium.java_client.windows.options.WindowsOptions; import org.junit.jupiter.api.Test; @@ -130,6 +131,45 @@ public void canBuildWindowsOptions() { assertEquals("yolo command", options.getPostrun().orElse(null).getCommand().orElse(null)); } + @Test + public void canBuildWincoreOptions() { + WincoreOptions options = new WincoreOptions(); + assertEquals(Platform.WINDOWS, options.getPlatformName()); + assertEquals(AutomationName.WINCORE, options.getAutomationName().orElse(null)); + options.setShouldCloseApp(false) + .setDelayBeforeClick(Duration.ofMillis(150)) + .setSmoothPointerMove("ease-in") + .setPerfMetrics(true) + .setForceQuit(true) + .setPrerun(new PowerShellData().withScript("yolo prescript")); + assertEquals(false, options.isShouldCloseApp().orElse(null)); + assertEquals(Duration.ofMillis(150), options.getDelayBeforeClick().orElse(null)); + assertEquals("ease-in", options.getSmoothPointerMove().orElse(null)); + assertTrue(options.isPerfMetrics().orElse(false)); + assertTrue(options.isForceQuit().orElse(false)); + assertEquals("yolo prescript", options.getPrerun().orElse(null).getScript().orElse(null)); + assertEquals(150L, options.asMap().get("appium:delayBeforeClick")); + assertEquals(true, options.asMap().get("ms:forcequit")); + } + + @Test + public void wincoreWaitForAppLaunchRespectsTheDriverUnitDetection() { + var options = new WincoreOptions(); + var cases = new Object[][] { + {Duration.ofSeconds(90), 90L, Duration.ofSeconds(90)}, + {Duration.ofSeconds(120), 120L, Duration.ofSeconds(120)}, + {Duration.ofSeconds(121), 121_000L, Duration.ofSeconds(121)}, + {Duration.ofMinutes(3), 180_000L, Duration.ofMinutes(3)}, + {Duration.ofMillis(1500), 1500L, Duration.ofMillis(1500)}, + {Duration.ofMillis(50), 1L, Duration.ofSeconds(1)}, + }; + for (var c : cases) { + options.setWaitForAppLaunch((Duration) c[0]); + assertEquals(c[1], options.asMap().get("ms:waitForAppLaunch")); + assertEquals(c[2], options.doesWaitForAppLaunch().orElse(null)); + } + } + @Test public void canBuildMac2Options() { Mac2Options options = new Mac2Options(); diff --git a/src/test/java/io/appium/java_client/remote/ExtensionCommandsOverHttpTest.java b/src/test/java/io/appium/java_client/remote/ExtensionCommandsOverHttpTest.java index c88845982..4190850f1 100644 --- a/src/test/java/io/appium/java_client/remote/ExtensionCommandsOverHttpTest.java +++ b/src/test/java/io/appium/java_client/remote/ExtensionCommandsOverHttpTest.java @@ -22,6 +22,9 @@ import io.appium.java_client.android.AndroidDriver; import io.appium.java_client.android.PowerACState; import io.appium.java_client.android.options.UiAutomator2Options; +import io.appium.java_client.wincore.WincoreDriver; +import io.appium.java_client.wincore.WincoreStartScreenRecordingOptions; +import io.appium.java_client.wincore.options.WincoreOptions; import io.appium.java_client.windows.WindowsDriver; import io.appium.java_client.windows.options.WindowsOptions; import org.junit.jupiter.api.AfterEach; @@ -37,9 +40,14 @@ import java.util.Base64; import java.util.List; import java.util.Map; +import java.util.Set; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Checks the commands that the drivers send for the features that have `mobile:` or `windows:` extensions @@ -79,6 +87,12 @@ private synchronized void handle(HttpExchange exchange) throws IOException { json = "{\"value\":\"" + Base64.getEncoder().encodeToString("data".getBytes(StandardCharsets.UTF_8)) + "\"}"; break; + case "GET /session/s1/contexts": + json = "{\"value\":[\"NATIVE_APP\",\"WEBVIEW_1\"]}"; + break; + case "GET /session/s1/context": + json = "{\"value\":\"WEBVIEW_1\"}"; + break; default: json = "{\"value\":null}"; break; @@ -142,4 +156,67 @@ void windowsAppIsLaunchedAndClosedWithTheDriverExtensions() throws IOException { driver.launchApp(); assertEquals("windows: launchApp", lastBody().get("script")); } + + @Test + void wincoreFilesAreTransferredWithTheDriverCommands() throws IOException { + var driver = new WincoreDriver(url(), new WincoreOptions()); + + assertArrayEquals("data".getBytes(StandardCharsets.UTF_8), driver.pullFile("C:\\a.txt")); + assertEquals("POST /session/s1/appium/device/pull_file", lastRequest()); + assertEquals(Map.of("path", "C:\\a.txt"), lastBody()); + + driver.pushFile("C:\\b.txt", "ZGF0YQ==".getBytes(StandardCharsets.UTF_8)); + assertEquals("POST /session/s1/appium/device/push_file", lastRequest()); + assertEquals(Map.of("path", "C:\\b.txt", "data", "ZGF0YQ=="), lastBody()); + + assertThrows(UnsupportedOperationException.class, () -> driver.pullFolder("C:\\dir")); + } + + @Test + void wincoreAppIsLaunchedAndClosedWithTheDriverExtensions() throws IOException { + var driver = new WincoreDriver(url(), new WincoreOptions()); + + driver.closeApp(); + assertEquals("windows: closeApp", lastBody().get("script")); + + driver.launchApp(); + assertEquals("windows: launchApp", lastBody().get("script")); + } + + @Test + void wincoreScreenRecordingUsesTheVideoFpsParameter() throws IOException { + var driver = new WincoreDriver(url(), new WincoreOptions()); + + driver.startRecordingScreen(new WincoreStartScreenRecordingOptions().withFps(30)); + + assertEquals("POST /session/s1/appium/start_recording_screen", lastRequest()); + var options = (Map) lastBody().get("options"); + assertEquals(30, ((Number) options.get("videoFps")).intValue()); + assertFalse(options.containsKey("fps")); + } + + @Test + void wincoreDefaultScreenRecordingToleratesTheEmptyResponse() throws IOException { + var driver = new WincoreDriver(url(), new WincoreOptions()); + + assertNull(driver.startRecordingScreen()); + assertEquals("POST /session/s1/appium/start_recording_screen", lastRequest()); + } + + @Test + void wincoreSwitchesContexts() throws IOException { + var driver = new WincoreDriver(url(), new WincoreOptions()); + + assertEquals(Set.of("NATIVE_APP", "WEBVIEW_1"), driver.getContextHandles()); + assertEquals("GET /session/s1/contexts", lastRequest()); + + assertEquals("WEBVIEW_1", driver.getContext()); + assertEquals("GET /session/s1/context", lastRequest()); + + driver.context("WEBVIEW_1"); + assertEquals("POST /session/s1/context", lastRequest()); + assertEquals(Map.of("name", "WEBVIEW_1"), lastBody()); + + assertTrue(driver.isBrowser()); + } }