From 6da3dfe3c1d2b40591dd24907a0a4ea992a11736 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Thu, 12 Mar 2020 19:27:31 +0100 Subject: [PATCH 01/13] feat: Add wrappers for the screen recorder --- .../java_client/windows/WindowsDriver.java | 3 +- .../WindowsStartScreenRecordingOptions.java | 110 ++++++++++++++++++ .../WindowsStopScreenRecordingOptions.java | 28 +++++ 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java create mode 100644 src/main/java/io/appium/java_client/windows/WindowsStopScreenRecordingOptions.java 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 af559f12a..3c8126ac1 100644 --- a/src/main/java/io/appium/java_client/windows/WindowsDriver.java +++ b/src/main/java/io/appium/java_client/windows/WindowsDriver.java @@ -21,6 +21,7 @@ import io.appium.java_client.AppiumDriver; import io.appium.java_client.FindsByWindowsAutomation; import io.appium.java_client.HidesKeyboardWithKeyName; +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; @@ -32,7 +33,7 @@ public class WindowsDriver extends AppiumDriver implements PressesKeyCode, HidesKeyboardWithKeyName, - FindsByWindowsAutomation { + FindsByWindowsAutomation, CanRecordScreen { public WindowsDriver(HttpCommandExecutor executor, Capabilities capabilities) { super(executor, updateDefaultPlatformName(capabilities, WINDOWS)); diff --git a/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java b/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java new file mode 100644 index 000000000..3c0572e38 --- /dev/null +++ b/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java @@ -0,0 +1,110 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.appium.java_client.windows; + +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 WindowsStartScreenRecordingOptions + extends BaseStartScreenRecordingOptions { + private Integer fps; + private String videoFilter; + private String preset; + + public static WindowsStartScreenRecordingOptions startScreenRecordingOptions() { + return new WindowsStartScreenRecordingOptions(); + } + + /** + * 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 WindowsStartScreenRecordingOptions withFps(int fps) { + this.fps = fps; + 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 WindowsStartScreenRecordingOptions 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 (the default) + * * - faster + * * - fast + * * - medium + * * - slow + * * - slower + * * - veryslow + * @return self instance for chaining. + */ + public WindowsStartScreenRecordingOptions 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 WindowsStartScreenRecordingOptions 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)); + return builder.build(); + } +} diff --git a/src/main/java/io/appium/java_client/windows/WindowsStopScreenRecordingOptions.java b/src/main/java/io/appium/java_client/windows/WindowsStopScreenRecordingOptions.java new file mode 100644 index 000000000..206e8c644 --- /dev/null +++ b/src/main/java/io/appium/java_client/windows/WindowsStopScreenRecordingOptions.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.windows; + +import io.appium.java_client.screenrecording.BaseStopScreenRecordingOptions; + +public class WindowsStopScreenRecordingOptions extends + BaseStopScreenRecordingOptions { + + public static WindowsStopScreenRecordingOptions stopScreenRecordingOptions() { + return new WindowsStopScreenRecordingOptions(); + } + +} From e5790444537124bd776c1430dad3de9e8682211a Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Thu, 12 Mar 2020 19:53:39 +0100 Subject: [PATCH 02/13] Fix docs --- .../WindowsStartScreenRecordingOptions.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 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 3c0572e38..52985f5bf 100644 --- a/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java @@ -70,15 +70,15 @@ public WindowsStartScreenRecordingOptions withVideoFilter(String videoFilter) { * for more details. * * @param preset One of the supported encoding presets. Possible values are: - * * - ultrafast - * * - superfast - * * - veryfast (the default) - * * - faster - * * - fast - * * - medium - * * - slow - * * - slower - * * - veryslow + * - ultrafast + * - superfast + * - veryfast (default) + * - faster + * - fast + * - medium + * - slow + * - slower + * - veryslow * @return self instance for chaining. */ public WindowsStartScreenRecordingOptions withPreset(String preset) { From b74a6f4a56c419f2a935fe7adf9f51932facde84 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Thu, 12 Mar 2020 20:01:39 +0100 Subject: [PATCH 03/13] Fix indent --- .../WindowsStartScreenRecordingOptions.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 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 52985f5bf..80d0cb7d0 100644 --- a/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java @@ -70,15 +70,15 @@ public WindowsStartScreenRecordingOptions withVideoFilter(String videoFilter) { * for more details. * * @param preset One of the supported encoding presets. Possible values are: - * - ultrafast - * - superfast - * - veryfast (default) - * - faster - * - fast - * - medium - * - slow - * - slower - * - veryslow + * - ultrafast + * - superfast + * - veryfast (default) + * - faster + * - fast + * - medium + * - slow + * - slower + * - veryslow * @return self instance for chaining. */ public WindowsStartScreenRecordingOptions withPreset(String preset) { From b54baec7a944875dfa39fd49ede788d7926e28de Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Fri, 13 Mar 2020 08:27:44 +0100 Subject: [PATCH 04/13] Add more options --- .../WindowsStartScreenRecordingOptions.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) 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 80d0cb7d0..ff90a08f2 100644 --- a/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java @@ -29,6 +29,9 @@ public class WindowsStartScreenRecordingOptions private Integer fps; private String videoFilter; private String preset; + private Boolean captureCursor; + private Boolean captureClicks; + private String audioInput; public static WindowsStartScreenRecordingOptions startScreenRecordingOptions() { return new WindowsStartScreenRecordingOptions(); @@ -47,6 +50,41 @@ public WindowsStartScreenRecordingOptions 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 WindowsStartScreenRecordingOptions 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 WindowsStartScreenRecordingOptions 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 WindowsStartScreenRecordingOptions withAudioInput(String audioInput) { + this.audioInput = audioInput; + return this; + } /** * The video filter spec to apply for ffmpeg. @@ -105,6 +143,9 @@ public Map 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(audioInput).map(x -> builder.put("audioInput", x)); return builder.build(); } } From 6d8cc047ce617c332de03ef82aeb29a4b01ba01f Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sat, 14 Mar 2020 23:21:43 +0100 Subject: [PATCH 05/13] Add retry for tests --- .../java_client/ios/BaseIOSWebViewTest.java | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/appium/java_client/ios/BaseIOSWebViewTest.java b/src/test/java/io/appium/java_client/ios/BaseIOSWebViewTest.java index ce0b115a5..a44d88226 100644 --- a/src/test/java/io/appium/java_client/ios/BaseIOSWebViewTest.java +++ b/src/test/java/io/appium/java_client/ios/BaseIOSWebViewTest.java @@ -20,12 +20,16 @@ import io.appium.java_client.remote.MobileCapabilityType; import io.appium.java_client.service.local.AppiumServerHasNotBeenStartedLocallyException; import org.junit.BeforeClass; +import org.openqa.selenium.SessionNotCreatedException; import org.openqa.selenium.remote.DesiredCapabilities; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; import java.net.URL; import java.time.Duration; +import java.util.function.Supplier; public class BaseIOSWebViewTest extends BaseIOSTest { private static final Duration WEB_VIEW_DETECT_INTERVAL = Duration.ofSeconds(1); @@ -41,13 +45,30 @@ public static void beforeClass() throws IOException { File appDir = new File("src/test/java/io/appium/java_client"); File app = new File(appDir, "vodqa.zip"); - DesiredCapabilities capabilities = new DesiredCapabilities(); + final DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, PLATFORM_VERSION); //sometimes environment has performance problems capabilities.setCapability(IOSMobileCapabilityType.LAUNCH_TIMEOUT, 500000); capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, DEVICE_NAME); capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); - driver = new IOSDriver<>(new URL("http://" + ip + ":" + PORT + "/wd/hub"), capabilities); + Supplier> createDriver = () -> { + try { + return new IOSDriver<>(new URL("http://" + ip + ":" + PORT + "/wd/hub"), capabilities); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + }; + try { + driver = createDriver.get(); + } catch (SessionNotCreatedException e) { + if (!(e.getCause() instanceof InvocationTargetException)) { + throw e; + } + // Sometimes WDA session creation freezes unexpectedly on CI: + // https://dev.azure.com/srinivasansekar/java-client/_build/results?buildId=356&view=ms.vss-test-web.build-test-results-tab + capabilities.setCapability("useNewWDA", true); + driver = createDriver.get(); + } } protected void findAndSwitchToWebView() throws InterruptedException { From 91b8765405aaeb1148e0431b6dc9d15cb07e6593 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sun, 15 Mar 2020 09:12:45 +0100 Subject: [PATCH 06/13] Set timeouts --- .../java/io/appium/java_client/ios/BaseIOSWebViewTest.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/test/java/io/appium/java_client/ios/BaseIOSWebViewTest.java b/src/test/java/io/appium/java_client/ios/BaseIOSWebViewTest.java index a44d88226..647718603 100644 --- a/src/test/java/io/appium/java_client/ios/BaseIOSWebViewTest.java +++ b/src/test/java/io/appium/java_client/ios/BaseIOSWebViewTest.java @@ -25,7 +25,6 @@ import java.io.File; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.time.Duration; @@ -50,6 +49,7 @@ public static void beforeClass() throws IOException { //sometimes environment has performance problems capabilities.setCapability(IOSMobileCapabilityType.LAUNCH_TIMEOUT, 500000); capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, DEVICE_NAME); + capabilities.setCapability("commandTimeouts", "120000"); capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); Supplier> createDriver = () -> { try { @@ -61,9 +61,6 @@ public static void beforeClass() throws IOException { try { driver = createDriver.get(); } catch (SessionNotCreatedException e) { - if (!(e.getCause() instanceof InvocationTargetException)) { - throw e; - } // Sometimes WDA session creation freezes unexpectedly on CI: // https://dev.azure.com/srinivasansekar/java-client/_build/results?buildId=356&view=ms.vss-test-web.build-test-results-tab capabilities.setCapability("useNewWDA", true); From d3a0c4a2d0f77aca8c64b1823b13b4af0b3b31a6 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sun, 15 Mar 2020 11:11:53 +0100 Subject: [PATCH 07/13] Tune tests --- .../AndroidStartScreenRecordingOptions.java | 9 +++++ .../ios/IOSStartScreenRecordingOptions.java | 9 +++++ .../BaseScreenRecordingOptions.java | 2 +- .../screenrecording/CanRecordScreen.java | 4 --- .../generation/BaseElementGenerationTest.java | 12 ++----- .../android/AndroidElementGeneratingTest.java | 22 +++++++++--- .../ios/IOSElementGenerationTest.java | 34 +++++++++++++++---- 7 files changed, 67 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/appium/java_client/android/AndroidStartScreenRecordingOptions.java b/src/main/java/io/appium/java_client/android/AndroidStartScreenRecordingOptions.java index f3c79a508..e83f3b31f 100644 --- a/src/main/java/io/appium/java_client/android/AndroidStartScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/android/AndroidStartScreenRecordingOptions.java @@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableMap; import io.appium.java_client.screenrecording.BaseStartScreenRecordingOptions; +import io.appium.java_client.screenrecording.ScreenRecordingUploadOptions; import java.time.Duration; import java.util.Map; @@ -49,6 +50,14 @@ public AndroidStartScreenRecordingOptions withBitRate(int bitRate) { this.bitRate = bitRate; return this; } + + /** + * {@inheritDoc} + */ + @Override + public AndroidStartScreenRecordingOptions withUploadOptions(ScreenRecordingUploadOptions uploadOptions) { + return super.withUploadOptions(uploadOptions); + } /** * The video size of the generated media file. The format is WIDTHxHEIGHT. diff --git a/src/main/java/io/appium/java_client/ios/IOSStartScreenRecordingOptions.java b/src/main/java/io/appium/java_client/ios/IOSStartScreenRecordingOptions.java index ff45f7e08..47d5fe302 100644 --- a/src/main/java/io/appium/java_client/ios/IOSStartScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/ios/IOSStartScreenRecordingOptions.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableMap; import io.appium.java_client.screenrecording.BaseStartScreenRecordingOptions; +import io.appium.java_client.screenrecording.ScreenRecordingUploadOptions; import java.time.Duration; import java.util.Map; @@ -38,6 +39,14 @@ public static IOSStartScreenRecordingOptions startScreenRecordingOptions() { return new IOSStartScreenRecordingOptions(); } + /** + * {@inheritDoc} + */ + @Override + public IOSStartScreenRecordingOptions withUploadOptions(ScreenRecordingUploadOptions uploadOptions) { + return super.withUploadOptions(uploadOptions); + } + /** * The video codec type used for encoding of the recorded screen capture. * Execute `ffmpeg -codecs` in the terminal to see the list of supported video codecs. diff --git a/src/main/java/io/appium/java_client/screenrecording/BaseScreenRecordingOptions.java b/src/main/java/io/appium/java_client/screenrecording/BaseScreenRecordingOptions.java index d194e6ab6..127cc29a9 100644 --- a/src/main/java/io/appium/java_client/screenrecording/BaseScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/screenrecording/BaseScreenRecordingOptions.java @@ -33,7 +33,7 @@ public abstract class BaseScreenRecordingOptions String startRecordingScreen(T options) { return CommandExecutionHelper.execute(this, startRecordingScreenCommand(options)); @@ -40,8 +38,6 @@ default String startRecordingScreen( /** * Start asynchronous screen recording process with default options. - * - * @return Base-64 encoded content of the recorded media file. */ default String startRecordingScreen() { return this.execute(START_RECORDING_SCREEN).getValue().toString(); diff --git a/src/test/java/io/appium/java_client/appium/element/generation/BaseElementGenerationTest.java b/src/test/java/io/appium/java_client/appium/element/generation/BaseElementGenerationTest.java index f6d8db2bd..83271f229 100644 --- a/src/test/java/io/appium/java_client/appium/element/generation/BaseElementGenerationTest.java +++ b/src/test/java/io/appium/java_client/appium/element/generation/BaseElementGenerationTest.java @@ -7,14 +7,16 @@ import org.junit.After; import org.openqa.selenium.By; import org.openqa.selenium.Capabilities; +import org.openqa.selenium.SessionNotCreatedException; import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.DesiredCapabilities; import java.util.function.BiPredicate; import java.util.function.Supplier; public class BaseElementGenerationTest { protected AppiumDriver driver; - private AppiumDriverLocalService service; + protected AppiumDriverLocalService service; protected final BiPredicate> commonPredicate = (by, aClass) -> { WebElement element = driver.findElement(by); @@ -37,12 +39,4 @@ public void tearDown() { } } - protected boolean check(Supplier capabilitiesSupplier, - BiPredicate> filter, - By by, Class clazz) { - service = AppiumDriverLocalService.buildDefaultService(); - driver = new AppiumDriver<>(service, capabilitiesSupplier.get()); - return filter.test(by, clazz); - } - } diff --git a/src/test/java/io/appium/java_client/appium/element/generation/android/AndroidElementGeneratingTest.java b/src/test/java/io/appium/java_client/appium/element/generation/android/AndroidElementGeneratingTest.java index 992cb43cf..9e139e2cd 100644 --- a/src/test/java/io/appium/java_client/appium/element/generation/android/AndroidElementGeneratingTest.java +++ b/src/test/java/io/appium/java_client/appium/element/generation/android/AndroidElementGeneratingTest.java @@ -5,16 +5,22 @@ import static org.openqa.selenium.By.name; import static org.openqa.selenium.By.tagName; +import io.appium.java_client.AppiumDriver; import io.appium.java_client.android.AndroidElement; import io.appium.java_client.appium.element.generation.BaseElementGenerationTest; import io.appium.java_client.remote.AndroidMobileCapabilityType; import io.appium.java_client.remote.MobileBrowserType; import io.appium.java_client.remote.MobileCapabilityType; import io.appium.java_client.remote.MobilePlatform; +import io.appium.java_client.service.local.AppiumDriverLocalService; import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.DesiredCapabilities; import java.io.File; +import java.util.function.BiPredicate; import java.util.function.Supplier; public class AndroidElementGeneratingTest extends BaseElementGenerationTest { @@ -36,8 +42,8 @@ public void whenAndroidNativeAppIsLaunched() { clientCapabilities.setCapability(MobileCapabilityType.FULL_RESET, true); clientCapabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 60); return clientCapabilities; - }, commonPredicate, AndroidUIAutomator("new UiSelector().clickable(true)"), - AndroidElement.class)); + }, commonPredicate, AndroidUIAutomator("new UiSelector().clickable(true)") + )); } @Test @@ -50,7 +56,7 @@ public void whenAndroidHybridAppIsLaunched() { }, (by, aClass) -> { driver.context("WEBVIEW_io.appium.android.apis"); return commonPredicate.test(by, aClass); - }, tagName("a"), AndroidElement.class)); + }, tagName("a"))); } @Test @@ -64,8 +70,14 @@ public void whenAndroidBrowserIsLaunched() { }, (by, aClass) -> { driver.get("https://www.google.com"); return commonPredicate.test(by, aClass); - }, name("q"), AndroidElement.class)); + }, name("q"))); } - + private boolean check(Supplier capabilitiesSupplier, + BiPredicate> filter, + By by) { + service = AppiumDriverLocalService.buildDefaultService(); + driver = new AppiumDriver<>(service, capabilitiesSupplier.get()); + return filter.test(by, AndroidElement.class); + } } diff --git a/src/test/java/io/appium/java_client/appium/element/generation/ios/IOSElementGenerationTest.java b/src/test/java/io/appium/java_client/appium/element/generation/ios/IOSElementGenerationTest.java index 0b7572be3..5b08da9a5 100644 --- a/src/test/java/io/appium/java_client/appium/element/generation/ios/IOSElementGenerationTest.java +++ b/src/test/java/io/appium/java_client/appium/element/generation/ios/IOSElementGenerationTest.java @@ -6,6 +6,7 @@ import static org.openqa.selenium.By.name; import static org.openqa.selenium.By.partialLinkText; +import io.appium.java_client.AppiumDriver; import io.appium.java_client.appium.element.generation.BaseElementGenerationTest; import io.appium.java_client.ios.BaseIOSTest; import io.appium.java_client.ios.IOSElement; @@ -13,14 +14,19 @@ import io.appium.java_client.remote.MobileBrowserType; import io.appium.java_client.remote.MobileCapabilityType; import io.appium.java_client.remote.MobilePlatform; +import io.appium.java_client.service.local.AppiumDriverLocalService; import org.junit.Ignore; import org.junit.Test; +import org.openqa.selenium.By; import org.openqa.selenium.Capabilities; +import org.openqa.selenium.SessionNotCreatedException; +import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import java.io.File; +import java.util.function.BiPredicate; import java.util.function.Function; import java.util.function.Supplier; @@ -73,8 +79,8 @@ public void whenIOSNativeAppIsLaunched() { Capabilities caps = commonAppCapabilitiesSupplier.get(); return caps.merge(appFileSupplierFunction.apply(testApp).get()); }, commonPredicate, - AccessibilityId("Answer"), - IOSElement.class)); + AccessibilityId("Answer") + )); } @Ignore @@ -102,7 +108,7 @@ public void whenIOSHybridAppIsLaunched() { } }); return commonPredicate.test(by, aClass); - }, partialLinkText("login"), IOSElement.class)); + }, partialLinkText("login"))); } @Test @@ -113,7 +119,7 @@ public void whenIOSBrowserIsLaunched() { }, (by, aClass) -> { driver.get("https://www.google.com"); return commonPredicate.test(by, aClass); - }, name("q"), IOSElement.class)); + }, name("q"))); } @Test @@ -123,7 +129,7 @@ public void whenIOSNativeAppIsLaunched2() { serverCapabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, BaseIOSTest.PLATFORM_VERSION); serverCapabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, MobilePlatform.IOS); return serverCapabilities.merge(appFileSupplierFunction.apply(testApp).get()); - }, commonPredicate, id("IntegerA"), IOSElement.class)); + }, commonPredicate, id("IntegerA"))); } @Test @@ -136,6 +142,22 @@ public void whenIOSBrowserIsLaunched2() { }, (by, aClass) -> { driver.get("https://www.google.com"); return commonPredicate.test(by, aClass); - }, name("q"), IOSElement.class)); + }, name("q"))); + } + + private boolean check(Supplier capabilitiesSupplier, + BiPredicate> filter, + By by) { + service = AppiumDriverLocalService.buildDefaultService(); + Capabilities caps = capabilitiesSupplier.get(); + DesiredCapabilities fixedCaps = new DesiredCapabilities(caps); + fixedCaps.setCapability("commandTimeouts", "120000"); + try { + driver = new AppiumDriver<>(service, fixedCaps); + } catch (SessionNotCreatedException e) { + fixedCaps.setCapability("useNewWda", true); + driver = new AppiumDriver<>(service, fixedCaps); + } + return filter.test(by, IOSElement.class); } } From 4f95853adec894864b26a271d550a5ae266d70fd Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sun, 15 Mar 2020 11:29:15 +0100 Subject: [PATCH 08/13] Tune force start behavior --- .../BaseStartScreenRecordingOptions.java | 5 ++--- .../windows/WindowsStartScreenRecordingOptions.java | 12 ++++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/appium/java_client/screenrecording/BaseStartScreenRecordingOptions.java b/src/main/java/io/appium/java_client/screenrecording/BaseStartScreenRecordingOptions.java index dbe22cf93..8d8c2ae8b 100644 --- a/src/main/java/io/appium/java_client/screenrecording/BaseStartScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/screenrecording/BaseStartScreenRecordingOptions.java @@ -26,7 +26,7 @@ public abstract class BaseStartScreenRecordingOptions> extends BaseScreenRecordingOptions> { - private Boolean forceRestart; + protected Boolean forceRestart; private Duration timeLimit; /** @@ -64,8 +64,7 @@ public T withTimeLimit(Duration timeLimit) { /** * Whether to ignore the result of previous capture and start a new recording - * immediately. By default the endpoint will try to catch and return the result of - * the previous capture if it's still available. + * immediately. * * @return self instance for chaining. */ 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 ff90a08f2..4fe2f37c5 100644 --- a/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java @@ -136,6 +136,18 @@ public WindowsStartScreenRecordingOptions withTimeLimit(Duration timeLimit) { return super.withTimeLimit(timeLimit); } + /** + * Whether to return silently if there is an active video recording. + * The default behavior is to forcefully stop the active recording + * and start a new one. + * + * @return self instance for chaining. + */ + public WindowsStartScreenRecordingOptions disableForcedRestart() { + this.forceRestart = false; + return this; + } + @Override public Map build() { final ImmutableMap.Builder builder = ImmutableMap.builder(); From 39c8424dd930e1294c4a94cde2ce8f53049e7d45 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sun, 15 Mar 2020 14:01:24 +0100 Subject: [PATCH 09/13] Increase the timeout --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e374442f0..c678f6afa 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -46,6 +46,7 @@ steps: java -version - task: Gradle@2 + timeoutInMinutes: 120 inputs: gradleWrapperFile: 'gradlew' gradleOptions: '-Xmx3072m' From 01e3c618cd1a5b9e8153c3ae36b6d7725827ca97 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sun, 15 Mar 2020 15:02:11 +0100 Subject: [PATCH 10/13] remove redundant method --- .../AndroidStartScreenRecordingOptions.java | 2 +- .../ios/IOSStartScreenRecordingOptions.java | 2 +- .../BaseStartScreenRecordingOptions.java | 21 ------------------- 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/main/java/io/appium/java_client/android/AndroidStartScreenRecordingOptions.java b/src/main/java/io/appium/java_client/android/AndroidStartScreenRecordingOptions.java index e83f3b31f..c8378a86b 100644 --- a/src/main/java/io/appium/java_client/android/AndroidStartScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/android/AndroidStartScreenRecordingOptions.java @@ -56,7 +56,7 @@ public AndroidStartScreenRecordingOptions withBitRate(int bitRate) { */ @Override public AndroidStartScreenRecordingOptions withUploadOptions(ScreenRecordingUploadOptions uploadOptions) { - return super.withUploadOptions(uploadOptions); + return (AndroidStartScreenRecordingOptions) super.withUploadOptions(uploadOptions); } /** diff --git a/src/main/java/io/appium/java_client/ios/IOSStartScreenRecordingOptions.java b/src/main/java/io/appium/java_client/ios/IOSStartScreenRecordingOptions.java index 47d5fe302..d9b918977 100644 --- a/src/main/java/io/appium/java_client/ios/IOSStartScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/ios/IOSStartScreenRecordingOptions.java @@ -44,7 +44,7 @@ public static IOSStartScreenRecordingOptions startScreenRecordingOptions() { */ @Override public IOSStartScreenRecordingOptions withUploadOptions(ScreenRecordingUploadOptions uploadOptions) { - return super.withUploadOptions(uploadOptions); + return (IOSStartScreenRecordingOptions) super.withUploadOptions(uploadOptions); } /** diff --git a/src/main/java/io/appium/java_client/screenrecording/BaseStartScreenRecordingOptions.java b/src/main/java/io/appium/java_client/screenrecording/BaseStartScreenRecordingOptions.java index 8d8c2ae8b..b9d278cf5 100644 --- a/src/main/java/io/appium/java_client/screenrecording/BaseStartScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/screenrecording/BaseStartScreenRecordingOptions.java @@ -29,27 +29,6 @@ public abstract class BaseStartScreenRecordingOptions Date: Sun, 15 Mar 2020 18:54:42 +0100 Subject: [PATCH 11/13] Set timeout per job --- azure-pipelines.yml | 82 ++++++++++--------- .../BaseStartScreenRecordingOptions.java | 13 ++- .../WindowsStartScreenRecordingOptions.java | 12 --- 3 files changed, 54 insertions(+), 53 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c678f6afa..238427bd9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -15,44 +15,46 @@ variables: IOS_PLATFORM_VERSION: 12.2 IOS_DEVICE_NAME: iPhone X -steps: -- task: NodeTool@0 - inputs: - versionSpec: '11.x' - -- script: | - echo "Configuring Environment" - echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install 'system-images;$(ANDROID_EMU_TARGET);$(ANDROID_EMU_TAG);$(ANDROID_EMU_ABI)' - echo "no" | $ANDROID_HOME/tools/bin/avdmanager create avd -n "$(ANDROID_EMU_NAME)" -k 'system-images;$(ANDROID_EMU_TARGET);$(ANDROID_EMU_TAG);$(ANDROID_EMU_ABI)' --force - echo $ANDROID_HOME/emulator/emulator -list-avds - - echo "Starting emulator" - nohup $ANDROID_HOME/emulator/emulator -avd "$(ANDROID_EMU_NAME)" -no-snapshot > /dev/null 2>&1 & - $ANDROID_HOME/platform-tools/adb wait-for-device - while [[ $? -ne 0 ]]; do sleep 1; $ANDROID_HOME/platform-tools/adb shell pm list packages; done; - $ANDROID_HOME/platform-tools/adb devices - echo "Emulator started" - - sudo xcode-select -s /Applications/Xcode_$(XCODE_VERSION).app/Contents/Developer - xcrun simctl list - - npm config delete prefix - npm config set prefix $NVM_DIR/versions/node/`node --version` - node --version - - npm install -g appium@beta - appium --version - - java -version - -- task: Gradle@2 +jobs: +- job: E2E Tests timeoutInMinutes: 120 - inputs: - gradleWrapperFile: 'gradlew' - gradleOptions: '-Xmx3072m' - javaHomeOption: 'JDKVersion' - jdkVersionOption: '1.8' - jdkArchitectureOption: 'x64' - publishJUnitResults: true - tasks: 'build' - options: 'xcuiTest uiAutomationTest -x checkstyleTest -x test -x signMavenJavaPublication' + steps: + - task: NodeTool@0 + inputs: + versionSpec: '11.x' + + - script: | + echo "Configuring Environment" + echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install 'system-images;$(ANDROID_EMU_TARGET);$(ANDROID_EMU_TAG);$(ANDROID_EMU_ABI)' + echo "no" | $ANDROID_HOME/tools/bin/avdmanager create avd -n "$(ANDROID_EMU_NAME)" -k 'system-images;$(ANDROID_EMU_TARGET);$(ANDROID_EMU_TAG);$(ANDROID_EMU_ABI)' --force + echo $ANDROID_HOME/emulator/emulator -list-avds + + echo "Starting emulator" + nohup $ANDROID_HOME/emulator/emulator -avd "$(ANDROID_EMU_NAME)" -no-snapshot > /dev/null 2>&1 & + $ANDROID_HOME/platform-tools/adb wait-for-device + while [[ $? -ne 0 ]]; do sleep 1; $ANDROID_HOME/platform-tools/adb shell pm list packages; done; + $ANDROID_HOME/platform-tools/adb devices + echo "Emulator started" + + sudo xcode-select -s /Applications/Xcode_$(XCODE_VERSION).app/Contents/Developer + xcrun simctl list + + npm config delete prefix + npm config set prefix $NVM_DIR/versions/node/`node --version` + node --version + + npm install -g appium@beta + appium --version + + java -version + + - task: Gradle@2 + inputs: + gradleWrapperFile: 'gradlew' + gradleOptions: '-Xmx3072m' + javaHomeOption: 'JDKVersion' + jdkVersionOption: '1.8' + jdkArchitectureOption: 'x64' + publishJUnitResults: true + tasks: 'build' + options: 'xcuiTest uiAutomationTest -x checkstyleTest -x test -x signMavenJavaPublication' diff --git a/src/main/java/io/appium/java_client/screenrecording/BaseStartScreenRecordingOptions.java b/src/main/java/io/appium/java_client/screenrecording/BaseStartScreenRecordingOptions.java index b9d278cf5..206cc1a6c 100644 --- a/src/main/java/io/appium/java_client/screenrecording/BaseStartScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/screenrecording/BaseStartScreenRecordingOptions.java @@ -26,7 +26,7 @@ public abstract class BaseStartScreenRecordingOptions> extends BaseScreenRecordingOptions> { - protected Boolean forceRestart; + private Boolean forceRestart; private Duration timeLimit; /** @@ -53,6 +53,17 @@ public T enableForcedRestart() { return (T) this; } + /** + * Whether to return silently if there is an active video recording. + * + * @return self instance for chaining. + */ + public T disableForcedRestart() { + this.forceRestart = false; + //noinspection unchecked + return (T) this; + } + @Override public Map build() { final ImmutableMap.Builder builder = ImmutableMap.builder(); 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 4fe2f37c5..ff90a08f2 100644 --- a/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java +++ b/src/main/java/io/appium/java_client/windows/WindowsStartScreenRecordingOptions.java @@ -136,18 +136,6 @@ public WindowsStartScreenRecordingOptions withTimeLimit(Duration timeLimit) { return super.withTimeLimit(timeLimit); } - /** - * Whether to return silently if there is an active video recording. - * The default behavior is to forcefully stop the active recording - * and start a new one. - * - * @return self instance for chaining. - */ - public WindowsStartScreenRecordingOptions disableForcedRestart() { - this.forceRestart = false; - return this; - } - @Override public Map build() { final ImmutableMap.Builder builder = ImmutableMap.builder(); From 5494e5c20e0073416419d4854265cf16d41b6124 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sun, 15 Mar 2020 19:00:44 +0100 Subject: [PATCH 12/13] Update job name --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 238427bd9..ea502ec76 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -16,7 +16,7 @@ variables: IOS_DEVICE_NAME: iPhone X jobs: -- job: E2E Tests +- job: E2E_Tests timeoutInMinutes: 120 steps: - task: NodeTool@0 From 190e9504371032d4693c5e9ee456268f0f26ce42 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sun, 15 Mar 2020 19:49:45 +0100 Subject: [PATCH 13/13] add ios workaround --- .../element/generation/ios/IOSElementGenerationTest.java | 2 +- src/test/java/io/appium/java_client/ios/AppIOSTest.java | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/appium/java_client/appium/element/generation/ios/IOSElementGenerationTest.java b/src/test/java/io/appium/java_client/appium/element/generation/ios/IOSElementGenerationTest.java index 5b08da9a5..19f1fd32b 100644 --- a/src/test/java/io/appium/java_client/appium/element/generation/ios/IOSElementGenerationTest.java +++ b/src/test/java/io/appium/java_client/appium/element/generation/ios/IOSElementGenerationTest.java @@ -155,7 +155,7 @@ private boolean check(Supplier capabilitiesSupplier, try { driver = new AppiumDriver<>(service, fixedCaps); } catch (SessionNotCreatedException e) { - fixedCaps.setCapability("useNewWda", true); + fixedCaps.setCapability("useNewWDA", true); driver = new AppiumDriver<>(service, fixedCaps); } return filter.test(by, IOSElement.class); diff --git a/src/test/java/io/appium/java_client/ios/AppIOSTest.java b/src/test/java/io/appium/java_client/ios/AppIOSTest.java index b4b47247c..e41e325a6 100644 --- a/src/test/java/io/appium/java_client/ios/AppIOSTest.java +++ b/src/test/java/io/appium/java_client/ios/AppIOSTest.java @@ -5,6 +5,7 @@ import io.appium.java_client.remote.MobileCapabilityType; import io.appium.java_client.service.local.AppiumServerHasNotBeenStartedLocallyException; import org.junit.BeforeClass; +import org.openqa.selenium.SessionNotCreatedException; import org.openqa.selenium.remote.DesiredCapabilities; import java.io.File; @@ -30,7 +31,13 @@ public static void beforeClass() throws Exception { capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.IOS_XCUI_TEST); //sometimes environment has performance problems capabilities.setCapability(IOSMobileCapabilityType.LAUNCH_TIMEOUT, 500000); + capabilities.setCapability("commandTimeouts", "120000"); capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); - driver = new IOSDriver<>(new URL("http://" + ip + ":" + PORT + "/wd/hub"), capabilities); + try { + driver = new IOSDriver<>(new URL("http://" + ip + ":" + PORT + "/wd/hub"), capabilities); + } catch (SessionNotCreatedException e) { + capabilities.setCapability("useNewWDA", true); + driver = new IOSDriver<>(new URL("http://" + ip + ":" + PORT + "/wd/hub"), capabilities); + } } }