From d55f4aa1c8b0df27f34dc1c6ee9aeef77a681c8f Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Mon, 12 Oct 2026 08:03:10 +0200 Subject: [PATCH 1/7] feat: add the Wincore driver and options The Windows driver is deprecated and not supported since Appium 4. Add WincoreDriver, WincoreOptions and Wincore screen recording options for its successor, appium-wincore-driver, and document the Windows driver deprecation. The Wincore recording options send videoFps, whereas the Windows ones send fps. Co-Authored-By: Claude Sonnet 5.5 --- README.md | 3 +- .../java_client/remote/AutomationName.java | 4 +- .../java_client/wincore/WincoreDriver.java | 246 ++++++++++++++++++ .../WincoreStartScreenRecordingOptions.java | 163 ++++++++++++ .../WincoreStopScreenRecordingOptions.java | 39 +++ .../SupportsChromedriverCdnUrlOption.java | 55 ++++ ...portsChromedriverExecutablePathOption.java | 55 ++++ .../SupportsDelayAfterClickOption.java | 58 +++++ .../SupportsDelayBeforeClickOption.java | 58 +++++ .../SupportsEdgedriverCdnUrlOption.java | 55 ++++ ...upportsEdgedriverExecutablePathOption.java | 55 ++++ .../SupportsFfmpegExecutablePathOption.java | 55 ++++ ...SupportsIsolatedScriptExecutionOption.java | 58 +++++ .../options/SupportsLogFileOption.java | 55 ++++ .../options/SupportsMsForceQuitOption.java | 58 +++++ .../options/SupportsPerfMetricsOption.java | 59 +++++ .../options/SupportsShouldCloseAppOption.java | 58 +++++ .../SupportsSmoothPointerMoveOption.java | 55 ++++ .../options/SupportsWebviewEnabledOption.java | 57 ++++ .../wincore/options/WincoreOptions.java | 148 +++++++++++ .../java_client/windows/WindowsDriver.java | 5 +- .../windows/options/WindowsOptions.java | 4 +- .../drivers/options/OptionsBuildingTest.java | 22 ++ .../remote/ExtensionCommandsOverHttpTest.java | 43 +++ 24 files changed, 1463 insertions(+), 5 deletions(-) create mode 100644 src/main/java/io/appium/java_client/wincore/WincoreDriver.java create mode 100644 src/main/java/io/appium/java_client/wincore/WincoreStartScreenRecordingOptions.java create mode 100644 src/main/java/io/appium/java_client/wincore/WincoreStopScreenRecordingOptions.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsChromedriverCdnUrlOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsChromedriverExecutablePathOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsDelayAfterClickOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsDelayBeforeClickOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsEdgedriverCdnUrlOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsEdgedriverExecutablePathOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsFfmpegExecutablePathOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsIsolatedScriptExecutionOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsLogFileOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsMsForceQuitOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsPerfMetricsOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsShouldCloseAppOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsSmoothPointerMoveOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsWebviewEnabledOption.java create mode 100644 src/main/java/io/appium/java_client/wincore/options/WincoreOptions.java 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..5cd0bc89b 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,10 @@ 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 and not supported since Appium 4. */ 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..a95918cf9 --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/WincoreDriver.java @@ -0,0 +1,246 @@ +/* + * 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.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, + 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. + * + * @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. + * + * @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) + ))); + } + + 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/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..d2e7eb39c --- /dev/null +++ b/src/main/java/io/appium/java_client/wincore/options/WincoreOptions.java @@ -0,0 +1,148 @@ +/* + * 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 io.appium.java_client.windows.options.SupportsMsExperimentalWebDriverOption; +import io.appium.java_client.windows.options.SupportsMsWaitForAppLaunchOption; +import io.appium.java_client.windows.options.SupportsSystemPortOption; +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, + SupportsMsWaitForAppLaunchOption, + SupportsMsExperimentalWebDriverOption, + SupportsMsForceQuitOption, + SupportsSystemPortOption, + 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..eba2337f6 100644 --- a/src/main/java/io/appium/java_client/windows/WindowsDriver.java +++ b/src/main/java/io/appium/java_client/windows/WindowsDriver.java @@ -42,8 +42,11 @@ 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. + * + *

Note: the Windows driver is deprecated and not supported since Appium 4, + * use {@link io.appium.java_client.wincore.WincoreDriver} instead.

*/ public class WindowsDriver extends AppiumDriver implements PullsFiles, 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..b6ae8479d 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,8 +30,8 @@ /** * 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.

+ *

Note: appium-windows-driver is deprecated and not supported since Appium 4, use appium-wincore-driver instead, + * see {@link io.appium.java_client.wincore.options.WincoreOptions}.

*/ public class WindowsOptions extends BaseOptions implements SupportsAppOption, 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..bc06e8d1e 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,27 @@ 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 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..6718d6b3d 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; @@ -40,6 +43,8 @@ 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.assertThrows; /** * Checks the commands that the drivers send for the features that have `mobile:` or `windows:` extensions @@ -142,4 +147,42 @@ 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")); + } } From 8e4cf0aec40748492522ede426cd393c62d40f93 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Mon, 12 Oct 2026 20:47:18 +0200 Subject: [PATCH 2/7] fix: address Wincore review comments - Override the no-arg startRecordingScreen to avoid an NPE on the empty response. - Implement SupportsContextSwitching so WebView contexts and browser detection work. - Serialize ms:waitForAppLaunch in the unit the driver expects: values up to 120 are seconds, greater values are milliseconds. Co-Authored-By: Claude Sonnet 5.5 --- .../java_client/wincore/WincoreDriver.java | 13 ++++ .../SupportsWaitForAppLaunchOption.java | 66 +++++++++++++++++++ .../wincore/options/WincoreOptions.java | 3 +- .../drivers/options/OptionsBuildingTest.java | 18 +++++ .../remote/ExtensionCommandsOverHttpTest.java | 34 ++++++++++ 5 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/appium/java_client/wincore/options/SupportsWaitForAppLaunchOption.java diff --git a/src/main/java/io/appium/java_client/wincore/WincoreDriver.java b/src/main/java/io/appium/java_client/wincore/WincoreDriver.java index a95918cf9..7dc816fe5 100644 --- a/src/main/java/io/appium/java_client/wincore/WincoreDriver.java +++ b/src/main/java/io/appium/java_client/wincore/WincoreDriver.java @@ -24,6 +24,7 @@ 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; @@ -49,6 +50,7 @@ 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; @@ -226,6 +228,17 @@ public void pushFile(String remotePath, byte[] base64Data) { ))); } + /** + * 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)); } 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/WincoreOptions.java b/src/main/java/io/appium/java_client/wincore/options/WincoreOptions.java index d2e7eb39c..bef1ebaef 100644 --- a/src/main/java/io/appium/java_client/wincore/options/WincoreOptions.java +++ b/src/main/java/io/appium/java_client/wincore/options/WincoreOptions.java @@ -29,7 +29,6 @@ import io.appium.java_client.windows.options.SupportsAppTopLevelWindowOption; import io.appium.java_client.windows.options.SupportsAppWorkingDirOption; import io.appium.java_client.windows.options.SupportsMsExperimentalWebDriverOption; -import io.appium.java_client.windows.options.SupportsMsWaitForAppLaunchOption; import io.appium.java_client.windows.options.SupportsSystemPortOption; import org.openqa.selenium.Capabilities; @@ -50,7 +49,7 @@ public class WincoreOptions extends BaseOptions implements SupportsAppArgumentsOption, SupportsAppTopLevelWindowOption, SupportsAppWorkingDirOption, - SupportsMsWaitForAppLaunchOption, + SupportsWaitForAppLaunchOption, SupportsMsExperimentalWebDriverOption, SupportsMsForceQuitOption, SupportsSystemPortOption, 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 bc06e8d1e..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 @@ -152,6 +152,24 @@ public void canBuildWincoreOptions() { 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 6718d6b3d..4190850f1 100644 --- a/src/test/java/io/appium/java_client/remote/ExtensionCommandsOverHttpTest.java +++ b/src/test/java/io/appium/java_client/remote/ExtensionCommandsOverHttpTest.java @@ -40,11 +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 @@ -84,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; @@ -185,4 +194,29 @@ void wincoreScreenRecordingUsesTheVideoFpsParameter() throws IOException { 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()); + } } From 0e5f64cf0428207e0aecf4f06ca30fb0658c8cc0 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Mon, 12 Oct 2026 23:51:22 +0200 Subject: [PATCH 3/7] docs: document Wincore systemPort and modify_fs requirements Co-Authored-By: Claude Sonnet 5.5 --- .../java_client/wincore/WincoreDriver.java | 4 ++++ .../wincore/options/WincoreOptions.java | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/main/java/io/appium/java_client/wincore/WincoreDriver.java b/src/main/java/io/appium/java_client/wincore/WincoreDriver.java index 7dc816fe5..56f414cd8 100644 --- a/src/main/java/io/appium/java_client/wincore/WincoreDriver.java +++ b/src/main/java/io/appium/java_client/wincore/WincoreDriver.java @@ -193,6 +193,8 @@ public WincoreDriver(Capabilities capabilities) { /** * 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. @@ -216,6 +218,8 @@ public byte[] pullFolder(String remotePath) { /** * 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. 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 index bef1ebaef..03c7c994b 100644 --- a/src/main/java/io/appium/java_client/wincore/options/WincoreOptions.java +++ b/src/main/java/io/appium/java_client/wincore/options/WincoreOptions.java @@ -94,6 +94,30 @@ public WincoreOptions(Map source) { setCommonOptions(); } + /** + * Sets the {@code systemPort} capability. It has no effect for the Wincore driver, + * which ignores it because it communicates with its server over stdin/stdout. + * The capability is only kept for compatibility with the Windows driver configuration. + * + * @param port port number in range 0..65535 + * @return self instance for chaining. + */ + @Override + public WincoreOptions setSystemPort(int port) { + return SupportsSystemPortOption.super.setSystemPort(port); + } + + /** + * Get the {@code systemPort} capability. It has no effect for the Wincore driver, + * see {@link #setSystemPort(int)}. + * + * @return System port value. + */ + @Override + public Optional getSystemPort() { + return SupportsSystemPortOption.super.getSystemPort(); + } + private void setCommonOptions() { setPlatformName(MobilePlatform.WINDOWS); setAutomationName(AutomationName.WINCORE); From 1c898349f2ceda63949b41e1083f9e9b5a90043d Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Mon, 12 Oct 2026 23:53:56 +0200 Subject: [PATCH 4/7] refactor: drop the ignored systemPort option from WincoreOptions Co-Authored-By: Claude Sonnet 5.5 --- .../wincore/options/WincoreOptions.java | 26 ------------------- 1 file changed, 26 deletions(-) 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 index 03c7c994b..68d3bc0c3 100644 --- a/src/main/java/io/appium/java_client/wincore/options/WincoreOptions.java +++ b/src/main/java/io/appium/java_client/wincore/options/WincoreOptions.java @@ -29,7 +29,6 @@ import io.appium.java_client.windows.options.SupportsAppTopLevelWindowOption; import io.appium.java_client.windows.options.SupportsAppWorkingDirOption; import io.appium.java_client.windows.options.SupportsMsExperimentalWebDriverOption; -import io.appium.java_client.windows.options.SupportsSystemPortOption; import org.openqa.selenium.Capabilities; import java.util.Map; @@ -52,7 +51,6 @@ public class WincoreOptions extends BaseOptions implements SupportsWaitForAppLaunchOption, SupportsMsExperimentalWebDriverOption, SupportsMsForceQuitOption, - SupportsSystemPortOption, SupportsShouldCloseAppOption, SupportsIsolatedScriptExecutionOption, SupportsSmoothPointerMoveOption, @@ -94,30 +92,6 @@ public WincoreOptions(Map source) { setCommonOptions(); } - /** - * Sets the {@code systemPort} capability. It has no effect for the Wincore driver, - * which ignores it because it communicates with its server over stdin/stdout. - * The capability is only kept for compatibility with the Windows driver configuration. - * - * @param port port number in range 0..65535 - * @return self instance for chaining. - */ - @Override - public WincoreOptions setSystemPort(int port) { - return SupportsSystemPortOption.super.setSystemPort(port); - } - - /** - * Get the {@code systemPort} capability. It has no effect for the Wincore driver, - * see {@link #setSystemPort(int)}. - * - * @return System port value. - */ - @Override - public Optional getSystemPort() { - return SupportsSystemPortOption.super.getSystemPort(); - } - private void setCommonOptions() { setPlatformName(MobilePlatform.WINDOWS); setAutomationName(AutomationName.WINCORE); From 2342a8d6b26b166846e345921b8ca8a1602a0725 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Tue, 13 Oct 2026 07:36:07 +0200 Subject: [PATCH 5/7] refactor: drop the ignored ms:experimental-webdriver option from WincoreOptions Co-Authored-By: Claude Sonnet 5.5 --- .../io/appium/java_client/wincore/options/WincoreOptions.java | 2 -- 1 file changed, 2 deletions(-) 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 index 68d3bc0c3..d2e782593 100644 --- a/src/main/java/io/appium/java_client/wincore/options/WincoreOptions.java +++ b/src/main/java/io/appium/java_client/wincore/options/WincoreOptions.java @@ -28,7 +28,6 @@ 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 io.appium.java_client.windows.options.SupportsMsExperimentalWebDriverOption; import org.openqa.selenium.Capabilities; import java.util.Map; @@ -49,7 +48,6 @@ public class WincoreOptions extends BaseOptions implements SupportsAppTopLevelWindowOption, SupportsAppWorkingDirOption, SupportsWaitForAppLaunchOption, - SupportsMsExperimentalWebDriverOption, SupportsMsForceQuitOption, SupportsShouldCloseAppOption, SupportsIsolatedScriptExecutionOption, From 883967d71dea897537610b1cde1a3c161d41fdff Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Tue, 13 Oct 2026 18:50:51 +0200 Subject: [PATCH 6/7] refactor: deprecate the Windows driver classes Mark WindowsDriver, WindowsOptions and AutomationName.WINDOWS as deprecated in favor of their Wincore counterparts. Co-Authored-By: Claude Sonnet 5.5 --- .../java/io/appium/java_client/remote/AutomationName.java | 8 +++++++- .../java/io/appium/java_client/windows/WindowsDriver.java | 5 +++-- .../java_client/windows/options/WindowsOptions.java | 5 +++-- 3 files changed, 13 insertions(+), 5 deletions(-) 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 5cd0bc89b..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,7 +29,13 @@ 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, deprecated and not supported since Appium 4. */ + /** + * 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"; 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 eba2337f6..d4684f989 100644 --- a/src/main/java/io/appium/java_client/windows/WindowsDriver.java +++ b/src/main/java/io/appium/java_client/windows/WindowsDriver.java @@ -45,9 +45,10 @@ * WindowsDriver is an Appium driver created to automate Windows apps. * Read https://github.com/appium/appium-windows-driver for more details. * - *

Note: the Windows driver is deprecated and not supported since Appium 4, - * use {@link io.appium.java_client.wincore.WincoreDriver} instead.

+ * @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/options/WindowsOptions.java b/src/main/java/io/appium/java_client/windows/options/WindowsOptions.java index b6ae8479d..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 and not supported since Appium 4, use appium-wincore-driver instead, - * see {@link io.appium.java_client.wincore.options.WincoreOptions}.

+ * @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, From b13343a973ad3ed9f1a3f61a9b390bb4bb9f700b Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Tue, 13 Oct 2026 18:52:48 +0200 Subject: [PATCH 7/7] refactor: deprecate the Windows driver recording and capability classes The remaining windows.options classes (PowerShellData and the app arguments, top-level window and working dir options) stay as they are shared with WincoreOptions. Co-Authored-By: Claude Sonnet 5.5 --- .../windows/WindowsStartScreenRecordingOptions.java | 8 +++++++- .../windows/WindowsStopScreenRecordingOptions.java | 8 +++++++- .../options/SupportsCreateSessionTimeoutOption.java | 8 ++++++-- .../options/SupportsMsExperimentalWebDriverOption.java | 8 ++++++-- .../windows/options/SupportsMsWaitForAppLaunchOption.java | 8 ++++++-- .../windows/options/SupportsSystemPortOption.java | 8 ++++++-- 6 files changed, 38 insertions(+), 10 deletions(-) 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 { /**