From 0b95efeff7a270d269cb4dcf5c1e6db226412663 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Thu, 4 Feb 2021 15:22:11 +0100 Subject: [PATCH 1/2] feat: Add Mac2Driver --- .../java_client/mac/FindsByClassChain.java | 51 ++++++ .../java_client/mac/FindsByNsPredicate.java | 51 ++++++ .../io/appium/java_client/mac/Mac2Driver.java | 101 ++++++++++++ .../appium/java_client/mac/Mac2Element.java | 23 +++ .../mac/Mac2StartScreenRecordingOptions.java | 152 ++++++++++++++++++ .../mac/Mac2StopScreenRecordingOptions.java | 28 ++++ .../java_client/remote/AutomationName.java | 1 + .../java_client/remote/MobilePlatform.java | 1 + 8 files changed, 408 insertions(+) create mode 100644 src/main/java/io/appium/java_client/mac/FindsByClassChain.java create mode 100644 src/main/java/io/appium/java_client/mac/FindsByNsPredicate.java create mode 100644 src/main/java/io/appium/java_client/mac/Mac2Driver.java create mode 100644 src/main/java/io/appium/java_client/mac/Mac2Element.java create mode 100644 src/main/java/io/appium/java_client/mac/Mac2StartScreenRecordingOptions.java create mode 100644 src/main/java/io/appium/java_client/mac/Mac2StopScreenRecordingOptions.java diff --git a/src/main/java/io/appium/java_client/mac/FindsByClassChain.java b/src/main/java/io/appium/java_client/mac/FindsByClassChain.java new file mode 100644 index 000000000..733ca4bed --- /dev/null +++ b/src/main/java/io/appium/java_client/mac/FindsByClassChain.java @@ -0,0 +1,51 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.mac; + +import io.appium.java_client.FindsByFluentSelector; +import io.appium.java_client.MobileSelector; +import org.openqa.selenium.WebElement; + +import java.util.List; + +public interface FindsByClassChain extends FindsByFluentSelector { + + /** + * Perform single element lookup by class chain expression. + * Read https://github.com/appium/appium-mac2-driver#element-location + * for more details on elements location strategies supported by Mac2 driver. + * + * @param using A valid class chain lookup expression. + * @return The found element + */ + default T findElementByClassChain(String using) { + return findElement(MobileSelector.IOS_CLASS_CHAIN.toString(), using); + } + + /** + * Perform multiple elements lookup by class chain search expression. + * Read https://github.com/appium/appium-mac2-driver#element-location + * for more details on elements location strategies supported by Mac2 driver. + * + * @param using A valid class chain lookup expression. + * @return The array of found elements or an empty one if no matches + * have been found. + */ + default List findElementsByClassChain(String using) { + return findElements(MobileSelector.IOS_CLASS_CHAIN.toString(), using); + } +} diff --git a/src/main/java/io/appium/java_client/mac/FindsByNsPredicate.java b/src/main/java/io/appium/java_client/mac/FindsByNsPredicate.java new file mode 100644 index 000000000..804e3e4b8 --- /dev/null +++ b/src/main/java/io/appium/java_client/mac/FindsByNsPredicate.java @@ -0,0 +1,51 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.mac; + +import io.appium.java_client.FindsByFluentSelector; +import io.appium.java_client.MobileSelector; +import org.openqa.selenium.WebElement; + +import java.util.List; + +public interface FindsByNsPredicate extends FindsByFluentSelector { + + /** + * Perform single element lookup by predicate search expression. + * Read https://github.com/appium/appium-mac2-driver#element-location + * for more details on elements location strategies supported by Mac2 driver. + * + * @param using A valid predicate lookup expression. + * @return The found element + */ + default T findElementByNsPredicate(String using) { + return findElement(MobileSelector.IOS_PREDICATE_STRING.toString(), using); + } + + /** + * Perform multiple elements lookup by predicate search expression. + * Read https://github.com/appium/appium-mac2-driver#element-location + * for more details on elements location strategies supported by Mac2 driver. + * + * @param using A valid predicate lookup expression. + * @return The array of found elements or an empty one if no matches + * have been found. + */ + default List findElementsByNsPredicate(String using) { + return findElements(MobileSelector.IOS_PREDICATE_STRING.toString(), using); + } +} diff --git a/src/main/java/io/appium/java_client/mac/Mac2Driver.java b/src/main/java/io/appium/java_client/mac/Mac2Driver.java new file mode 100644 index 000000000..732668f9d --- /dev/null +++ b/src/main/java/io/appium/java_client/mac/Mac2Driver.java @@ -0,0 +1,101 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.mac; + +import io.appium.java_client.AppiumDriver; +import io.appium.java_client.HasSettings; +import io.appium.java_client.internal.CapabilityHelpers; +import io.appium.java_client.remote.AutomationName; +import io.appium.java_client.remote.MobileCapabilityType; +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.openqa.selenium.Capabilities; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.remote.HttpCommandExecutor; +import org.openqa.selenium.remote.http.HttpClient; + +import java.net.URL; + +import static io.appium.java_client.remote.MobilePlatform.MAC; +import static org.openqa.selenium.remote.CapabilityType.PLATFORM_NAME; + +/** + * Mac2Driver is an officially supported Appium driver + * created to automate Mac OS apps. The driver uses W3C + * WebDriver protocol and is built on top of Apple's XCTest + * automation framework. Read https://github.com/appium/appium-mac2-driver + * for more details on how to configure and use it. + * + * @since Appium 1.20.0 + */ +public class Mac2Driver + extends AppiumDriver implements CanRecordScreen, FindsByClassChain, + FindsByNsPredicate, HasSettings { + public Mac2Driver(HttpCommandExecutor executor, Capabilities capabilities) { + super(executor, prepareCaps(capabilities)); + } + + public Mac2Driver(URL remoteAddress, Capabilities desiredCapabilities) { + super(remoteAddress, prepareCaps(desiredCapabilities)); + } + + public Mac2Driver(URL remoteAddress, HttpClient.Factory httpClientFactory, Capabilities desiredCapabilities) { + super(remoteAddress, httpClientFactory, prepareCaps(desiredCapabilities)); + } + + public Mac2Driver(AppiumDriverLocalService service, Capabilities desiredCapabilities) { + super(service, prepareCaps(desiredCapabilities)); + } + + public Mac2Driver(AppiumDriverLocalService service, HttpClient.Factory httpClientFactory, + Capabilities desiredCapabilities) { + super(service, httpClientFactory, prepareCaps(desiredCapabilities)); + } + + public Mac2Driver(AppiumServiceBuilder builder, Capabilities desiredCapabilities) { + super(builder, prepareCaps(desiredCapabilities)); + } + + public Mac2Driver(AppiumServiceBuilder builder, HttpClient.Factory httpClientFactory, + Capabilities desiredCapabilities) { + super(builder, httpClientFactory, prepareCaps(desiredCapabilities)); + } + + public Mac2Driver(HttpClient.Factory httpClientFactory, Capabilities desiredCapabilities) { + super(httpClientFactory, prepareCaps(desiredCapabilities)); + } + + public Mac2Driver(Capabilities desiredCapabilities) { + super(prepareCaps(desiredCapabilities)); + } + + private static Capabilities prepareCaps(Capabilities originalCaps) { + DesiredCapabilities dc = new DesiredCapabilities(originalCaps); + if (originalCaps.getCapability(PLATFORM_NAME) == null) { + dc.setCapability(PLATFORM_NAME, MAC); + } + String automationName = CapabilityHelpers.getCapability(originalCaps, + MobileCapabilityType.AUTOMATION_NAME, String.class); + if (!AutomationName.MAC2.equalsIgnoreCase(automationName)) { + dc.setCapability(CapabilityHelpers.APPIUM_PREFIX + + MobileCapabilityType.AUTOMATION_NAME, AutomationName.MAC2); + } + return dc; + } +} diff --git a/src/main/java/io/appium/java_client/mac/Mac2Element.java b/src/main/java/io/appium/java_client/mac/Mac2Element.java new file mode 100644 index 000000000..905bada6e --- /dev/null +++ b/src/main/java/io/appium/java_client/mac/Mac2Element.java @@ -0,0 +1,23 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.mac; + +import io.appium.java_client.MobileElement; + +public class Mac2Element extends MobileElement implements + FindsByClassChain, FindsByNsPredicate { +} diff --git a/src/main/java/io/appium/java_client/mac/Mac2StartScreenRecordingOptions.java b/src/main/java/io/appium/java_client/mac/Mac2StartScreenRecordingOptions.java new file mode 100644 index 000000000..45c573126 --- /dev/null +++ b/src/main/java/io/appium/java_client/mac/Mac2StartScreenRecordingOptions.java @@ -0,0 +1,152 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.mac; + +import com.google.common.collect.ImmutableMap; +import io.appium.java_client.screenrecording.BaseStartScreenRecordingOptions; + +import java.time.Duration; +import java.util.Map; + +import static java.util.Optional.ofNullable; + +public class Mac2StartScreenRecordingOptions + extends BaseStartScreenRecordingOptions { + private Integer fps; + private String videoFilter; + private String preset; + private Boolean captureCursor; + private Boolean captureClicks; + private Integer deviceId; + + public static Mac2StartScreenRecordingOptions startScreenRecordingOptions() { + return new Mac2StartScreenRecordingOptions(); + } + + /** + * 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 Mac2StartScreenRecordingOptions 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 Mac2StartScreenRecordingOptions 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 Mac2StartScreenRecordingOptions enableClicksCapture() { + this.captureClicks = true; + return this; + } + + /** + * Screen device index to use for the recording. + * The list of available devices could be retrieved using + * `ffmpeg -f avfoundation -list_devices true -i` command. + * This option is mandatory and must be always provided. + * + * @param deviceId The valid screen device identifier. + * @return self instance for chaining. + */ + public Mac2StartScreenRecordingOptions withDeviceId(Integer deviceId) { + this.deviceId = deviceId; + 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 Mac2StartScreenRecordingOptions 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 Mac2StartScreenRecordingOptions 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 Mac2StartScreenRecordingOptions withTimeLimit(Duration timeLimit) { + return super.withTimeLimit(timeLimit); + } + + @Override + public Map build() { + final ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.putAll(super.build()); + ofNullable(fps).map(x -> builder.put("fps", x)); + ofNullable(preset).map(x -> builder.put("preset", x)); + ofNullable(videoFilter).map(x -> builder.put("videoFilter", x)); + ofNullable(captureClicks).map(x -> builder.put("captureClicks", x)); + ofNullable(captureCursor).map(x -> builder.put("captureCursor", x)); + ofNullable(deviceId).map(x -> builder.put("deviceId", x)); + return builder.build(); + } +} diff --git a/src/main/java/io/appium/java_client/mac/Mac2StopScreenRecordingOptions.java b/src/main/java/io/appium/java_client/mac/Mac2StopScreenRecordingOptions.java new file mode 100644 index 000000000..8984460be --- /dev/null +++ b/src/main/java/io/appium/java_client/mac/Mac2StopScreenRecordingOptions.java @@ -0,0 +1,28 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.mac; + +import io.appium.java_client.screenrecording.BaseStopScreenRecordingOptions; + +public class Mac2StopScreenRecordingOptions extends + BaseStopScreenRecordingOptions { + + public static Mac2StopScreenRecordingOptions stopScreenRecordingOptions() { + return new Mac2StopScreenRecordingOptions(); + } + +} 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 ce85512c1..2f9cdf11f 100644 --- a/src/main/java/io/appium/java_client/remote/AutomationName.java +++ b/src/main/java/io/appium/java_client/remote/AutomationName.java @@ -25,4 +25,5 @@ public interface AutomationName { String ANDROID_UIAUTOMATOR2 = "UIAutomator2"; String YOUI_ENGINE = "youiengine"; String ESPRESSO = "Espresso"; + String MAC2 = "Mac2"; } diff --git a/src/main/java/io/appium/java_client/remote/MobilePlatform.java b/src/main/java/io/appium/java_client/remote/MobilePlatform.java index 07ecc8223..97e8deaf3 100644 --- a/src/main/java/io/appium/java_client/remote/MobilePlatform.java +++ b/src/main/java/io/appium/java_client/remote/MobilePlatform.java @@ -23,4 +23,5 @@ public interface MobilePlatform { String FIREFOX_OS = "FirefoxOS"; String WINDOWS = "Windows"; String TVOS = "tvOS"; + String MAC = "Mac"; } From 2b73cd4fa98f2d677103b564f0aab8ebf6bdb243 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Thu, 4 Feb 2021 15:34:11 +0100 Subject: [PATCH 2/2] Tune checkstyle --- .../java/io/appium/java_client/mac/FindsByClassChain.java | 3 +-- .../java/io/appium/java_client/mac/FindsByNsPredicate.java | 3 +-- src/main/java/io/appium/java_client/mac/Mac2Driver.java | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/appium/java_client/mac/FindsByClassChain.java b/src/main/java/io/appium/java_client/mac/FindsByClassChain.java index 733ca4bed..3d4eaffcc 100644 --- a/src/main/java/io/appium/java_client/mac/FindsByClassChain.java +++ b/src/main/java/io/appium/java_client/mac/FindsByClassChain.java @@ -42,8 +42,7 @@ default T findElementByClassChain(String using) { * for more details on elements location strategies supported by Mac2 driver. * * @param using A valid class chain lookup expression. - * @return The array of found elements or an empty one if no matches - * have been found. + * @return The array of found elements or an empty one if no matches have been found. */ default List findElementsByClassChain(String using) { return findElements(MobileSelector.IOS_CLASS_CHAIN.toString(), using); diff --git a/src/main/java/io/appium/java_client/mac/FindsByNsPredicate.java b/src/main/java/io/appium/java_client/mac/FindsByNsPredicate.java index 804e3e4b8..665732eb3 100644 --- a/src/main/java/io/appium/java_client/mac/FindsByNsPredicate.java +++ b/src/main/java/io/appium/java_client/mac/FindsByNsPredicate.java @@ -42,8 +42,7 @@ default T findElementByNsPredicate(String using) { * for more details on elements location strategies supported by Mac2 driver. * * @param using A valid predicate lookup expression. - * @return The array of found elements or an empty one if no matches - * have been found. + * @return The array of found elements or an empty one if no matches have been found. */ default List findElementsByNsPredicate(String using) { return findElements(MobileSelector.IOS_PREDICATE_STRING.toString(), using); diff --git a/src/main/java/io/appium/java_client/mac/Mac2Driver.java b/src/main/java/io/appium/java_client/mac/Mac2Driver.java index 732668f9d..d76eaf5d7 100644 --- a/src/main/java/io/appium/java_client/mac/Mac2Driver.java +++ b/src/main/java/io/appium/java_client/mac/Mac2Driver.java @@ -93,8 +93,8 @@ private static Capabilities prepareCaps(Capabilities originalCaps) { String automationName = CapabilityHelpers.getCapability(originalCaps, MobileCapabilityType.AUTOMATION_NAME, String.class); if (!AutomationName.MAC2.equalsIgnoreCase(automationName)) { - dc.setCapability(CapabilityHelpers.APPIUM_PREFIX + - MobileCapabilityType.AUTOMATION_NAME, AutomationName.MAC2); + dc.setCapability(CapabilityHelpers.APPIUM_PREFIX + + MobileCapabilityType.AUTOMATION_NAME, AutomationName.MAC2); } return dc; }