diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9955fa6..df0ab23 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -2,7 +2,7 @@ trigger: - master pool: - vmImage: 'macOS-latest' + vmImage: 'macOS-14' jobs: - job: sonar @@ -44,6 +44,7 @@ jobs: pollingTimeoutSec: '300' - job: tests + timeoutInMinutes: 60 displayName: Run tests strategy: maxParallel: 2 @@ -55,11 +56,12 @@ jobs: variables: ANDROID_EMU_NAME: test - ANDROID_SDK_ID: system-images;android-30;google_apis_playstore;x86_64 + ANDROID_SDK_ID: system-images;android-29;google_apis_playstore;x86_64 steps: - task: CmdLine@2 displayName: 'Configure Appium and Android SDK' + timeoutInMinutes: 15 inputs: script: | echo "Configuring Environment" diff --git a/pom.xml b/pom.xml index b873f89..f676780 100644 --- a/pom.xml +++ b/pom.xml @@ -184,7 +184,7 @@ com.github.aquality-automation aquality-selenium-core - 4.8.0 + 4.9.0 diff --git a/src/main/java/aquality/appium/mobile/application/IMobileApplication.java b/src/main/java/aquality/appium/mobile/application/IMobileApplication.java index b5f3eef..c3a3500 100644 --- a/src/main/java/aquality/appium/mobile/application/IMobileApplication.java +++ b/src/main/java/aquality/appium/mobile/application/IMobileApplication.java @@ -14,6 +14,7 @@ public interface IMobileApplication extends IApplication { /** * Provides default timeout for terminate methods. + * * @return default timeout for waiting until the application is terminated. */ static Duration getDefaultTerminateTimeout() { @@ -67,15 +68,19 @@ default InteractsWithApps appManagement() { /** * Execute application script + * * @param script script * @param params parameters + * @param type of the result * @return result of the script execution. */ T executeScript(String script, Map params); /** * Execute application script + * * @param script script + * @param type of the result * @return result of the script execution. */ default T executeScript(String script) { diff --git a/src/main/java/aquality/appium/mobile/screens/IScreen.java b/src/main/java/aquality/appium/mobile/screens/IScreen.java index 08da550..ffdad63 100644 --- a/src/main/java/aquality/appium/mobile/screens/IScreen.java +++ b/src/main/java/aquality/appium/mobile/screens/IScreen.java @@ -13,16 +13,22 @@ public interface IScreen extends IForm { /** * Locator for specified screen + * + * @return locator */ By getLocator(); /** * Name of specified screen + * + * @return name */ String getName(); /** * Size of the element described by screen locator. + * + * @return size */ Dimension getSize(); diff --git a/src/main/java/aquality/appium/mobile/screens/Screen.java b/src/main/java/aquality/appium/mobile/screens/Screen.java index 66466e1..3f6a1c6 100644 --- a/src/main/java/aquality/appium/mobile/screens/Screen.java +++ b/src/main/java/aquality/appium/mobile/screens/Screen.java @@ -30,6 +30,9 @@ public abstract class Screen extends Form implements IScreen { /** * Constructor with parameters + * + * @param locator Locator of the screen + * @param name Name of the screen */ protected Screen(By locator, String name) { super(IElement.class); @@ -50,7 +53,7 @@ public String getName() { @Override public Dimension getSize() { - return screenElement.visual().getSize(); + return screenElement.visual().getSize(); } @Override @@ -68,7 +71,7 @@ protected IElement getScreenElement() { return screenElement; } - protected IElementFactory getElementFactory(){ + protected IElementFactory getElementFactory() { return AqualityServices.getElementFactory(); } diff --git a/src/main/java/aquality/appium/mobile/screens/screenfactory/IScreenFactory.java b/src/main/java/aquality/appium/mobile/screens/screenfactory/IScreenFactory.java index 63c69cc..165f9cf 100644 --- a/src/main/java/aquality/appium/mobile/screens/screenfactory/IScreenFactory.java +++ b/src/main/java/aquality/appium/mobile/screens/screenfactory/IScreenFactory.java @@ -9,7 +9,10 @@ public interface IScreenFactory { /** * Returns an implementation of a particular app screen. - * @param Type of desired application screen. + * + * @param Type of desired application screen. + * @param clazz Class of desired application screen. + * @return Instance of desired application screen. */ T getScreen(Class clazz); } diff --git a/src/test/java/samples/android/nativeapp/apidemos/ApplicationActivity.java b/src/test/java/samples/android/nativeapp/apidemos/ApplicationActivity.java index 9b2f289..907dd48 100644 --- a/src/test/java/samples/android/nativeapp/apidemos/ApplicationActivity.java +++ b/src/test/java/samples/android/nativeapp/apidemos/ApplicationActivity.java @@ -1,16 +1,16 @@ package samples.android.nativeapp.apidemos; import aquality.appium.mobile.application.AqualityServices; +import aquality.appium.mobile.elements.interfaces.IButton; import aquality.appium.mobile.screens.Screen; +import aquality.selenium.core.configurations.ITimeoutConfiguration; import io.appium.java_client.android.Activity; import org.openqa.selenium.By; -import samples.android.nativeapp.apidemos.screens.AlertsMenuScreen; -import samples.android.nativeapp.apidemos.screens.AndroidScreen; -import samples.android.nativeapp.apidemos.screens.InvokeSearchScreen; -import samples.android.nativeapp.apidemos.screens.ViewControlsScreen; -import samples.android.nativeapp.apidemos.screens.ViewTabsScrollableScreen; +import org.openqa.selenium.WebDriverException; +import samples.android.nativeapp.apidemos.screens.*; import java.lang.reflect.InvocationTargetException; +import java.util.Collections; public enum ApplicationActivity { @@ -38,7 +38,8 @@ public T open() { public T getScreen() { try { return (T) screen.getDeclaredConstructor().newInstance(); - } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | + InvocationTargetException e) { AqualityServices.getLogger().debug(e.getMessage()); throw new IllegalArgumentException("Something went wrong during screen getting"); } @@ -46,6 +47,8 @@ public T getScreen() { private static class ActivityScreen extends AndroidScreen { private final Activity activity; + private final IButton btnWait = getElementFactory().getButton(By.id("android:id/aerr_wait"), "Wait"); + private final IButton btnCloseApp = getElementFactory().getButton(By.id("android:id/aerr_close"), "Close app"); ActivityScreen(Activity activity) { super(By.name(activity.getAppActivity()), activity.getAppActivity()); @@ -54,6 +57,21 @@ private static class ActivityScreen extends AndroidScreen { void open() { startActivity(activity); + // workaround to handle System UI isn't responding dialog + ITimeoutConfiguration timeoutConfiguration = AqualityServices.getConfiguration().getTimeoutConfiguration(); + boolean result = AqualityServices.getConditionalWait().waitFor(() -> + { + if (!btnWait.state().waitForDisplayed()) { + return true; + } + btnWait.click(); + return btnWait.state().waitForNotDisplayed(); + }, timeoutConfiguration.getCommand(), + timeoutConfiguration.getCondition(), + Collections.singletonList(WebDriverException.class)); + if (!result) { + btnCloseApp.click(); + } } } } diff --git a/src/test/java/samples/android/web/WebTextBoxTest.java b/src/test/java/samples/android/web/WebTextBoxTest.java index 7a0c215..c031f5b 100644 --- a/src/test/java/samples/android/web/WebTextBoxTest.java +++ b/src/test/java/samples/android/web/WebTextBoxTest.java @@ -1,7 +1,9 @@ package samples.android.web; import aquality.appium.mobile.application.AqualityServices; +import aquality.appium.mobile.elements.interfaces.IButton; import aquality.appium.mobile.elements.interfaces.ITextBox; +import aquality.selenium.core.configurations.ITimeoutConfiguration; import io.appium.java_client.android.AndroidDriver; import org.openqa.selenium.By; import org.openqa.selenium.Keys; @@ -11,18 +13,24 @@ public class WebTextBoxTest extends AndroidWebTest { private static final String VALUE_TO_SUBMIT = "quality assurance"; + private static final ITextBox txbSearch = AqualityServices.getElementFactory().getTextBox(By.id("searchInput"), "Search"); + private static final IButton btnOverlayToggle = AqualityServices.getElementFactory().getButton(By.className("button-collapse"), "Toggle Overlay"); + private static final IButton btnCloseBanner = AqualityServices.getElementFactory().getButton(By.className("overlay-banner-close"), "Close banner"); @Test public void testTextBoxInteraction() { AqualityServices.getApplication().getDriver().get("https://wikipedia.org"); - ITextBox txbSearch = AqualityServices.getElementFactory().getTextBox(By.id("searchInput"), "Search"); txbSearch.state().waitForClickable(); + if (btnOverlayToggle.state().isDisplayed()) { + btnOverlayToggle.click(); + btnCloseBanner.click(); + } txbSearch.type(VALUE_TO_SUBMIT); Assert.assertEquals(VALUE_TO_SUBMIT, txbSearch.getValue(), "Submitted value should match to expected"); txbSearch.clear(); Assert.assertEquals("", txbSearch.getValue(), "Value should be cleared"); txbSearch.click(); - checkUnfocus(txbSearch); + checkUnfocus(); txbSearch.focus(); Assert.assertTrue(isKeyboardShown(true), "Keyboard should be shown when focus successful"); txbSearch.typeSecret(VALUE_TO_SUBMIT); @@ -35,17 +43,17 @@ public void testTextBoxInteraction() { Assert.assertTrue(txbSearch.state().waitForNotDisplayed(), "text field should disappear after the submit"); } - private void checkUnfocus(ITextBox txbSearch) { + private void checkUnfocus() { txbSearch.unfocus(); Assert.assertFalse(isKeyboardShown(false), "Keyboard should not be shown when unfocus successful"); } - @SuppressWarnings("unchecked") private boolean isKeyboardShown(boolean expectedStateToWait) { + ITimeoutConfiguration timeoutConfiguration = AqualityServices.getConfiguration().getTimeoutConfiguration(); boolean waitResult = AqualityServices.getConditionalWait() - .waitFor(driver -> ((AndroidDriver)driver).isKeyboardShown() == expectedStateToWait, - AqualityServices.getConfiguration().getTimeoutConfiguration().getCommand(), - AqualityServices.getConfiguration().getTimeoutConfiguration().getPollingInterval().multipliedBy(10), + .waitFor(driver -> ((AndroidDriver) driver).isKeyboardShown() == expectedStateToWait, + timeoutConfiguration.getCommand(), + timeoutConfiguration.getPollingInterval().multipliedBy(10), String.format("is keyboard shown condition should be %s", expectedStateToWait)); return expectedStateToWait == waitResult; } diff --git a/src/test/resources/settings.json b/src/test/resources/settings.json index bc76c3b..3c4de1a 100644 --- a/src/test/resources/settings.json +++ b/src/test/resources/settings.json @@ -11,7 +11,8 @@ "platformName": "Android", "automationName": "UIAutomator2", "eventTimings": true, - "uiautomator2ServerInstallTimeout": 30000 + "uiautomator2ServerInstallTimeout": 30000, + "uiautomator2ServerLaunchTimeout": 60000 } }, "ios": {