From 3d6cc65fc2ee3afdf1196db8531964e3c23199a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alaksiej=20Miale=C5=A1ka?= Date: Fri, 10 May 2024 16:54:53 +0200 Subject: [PATCH] Application management functionality (#42) +semver: feature * Application management functionality +semver: feature * Add missed logging and retry appManagement test * Fix naming issue --- pom.xml | 2 +- .../mobile/application/Application.java | 123 ++++++++++--- .../mobile/application/AqualityServices.java | 8 +- .../application/IApplicationFactory.java | 2 +- .../application/IMobileApplication.java | 168 ++++++++++++++++++ .../application/LocalApplicationFactory.java | 2 +- .../mobile/application/MobileModule.java | 4 +- .../application/RemoteApplicationFactory.java | 2 +- .../mobile/configuration/DriverSettings.java | 20 ++- .../mobile/configuration/IDriverSettings.java | 7 + .../appium/mobile/elements/Element.java | 4 +- src/main/resources/localization/be.json | 36 ++-- src/main/resources/localization/en.json | 10 +- src/main/resources/localization/pl.json | 10 +- src/main/resources/localization/ru.json | 10 +- src/main/resources/localization/uk.json | 36 ++-- .../AndroidBasicInteractionsTest.java | 35 ++++ 17 files changed, 408 insertions(+), 71 deletions(-) create mode 100644 src/main/java/aquality/appium/mobile/application/IMobileApplication.java diff --git a/pom.xml b/pom.xml index 5f71c4f..98c72fa 100644 --- a/pom.xml +++ b/pom.xml @@ -189,7 +189,7 @@ com.github.aquality-automation aquality-selenium-core - 4.0.1 + 4.0.2 diff --git a/src/main/java/aquality/appium/mobile/application/Application.java b/src/main/java/aquality/appium/mobile/application/Application.java index 7d93cb3..6023028 100644 --- a/src/main/java/aquality/appium/mobile/application/Application.java +++ b/src/main/java/aquality/appium/mobile/application/Application.java @@ -1,16 +1,25 @@ package aquality.appium.mobile.application; import aquality.appium.mobile.configuration.IApplicationProfile; -import aquality.selenium.core.applications.IApplication; import aquality.selenium.core.configurations.ITimeoutConfiguration; import aquality.selenium.core.localization.ILocalizedLogger; +import aquality.selenium.core.utilities.IActionRetrier; import io.appium.java_client.AppiumDriver; -import io.appium.java_client.InteractsWithApps; +import io.appium.java_client.CommandExecutionHelper; +import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.appmanagement.ApplicationState; +import io.appium.java_client.appmanagement.BaseActivateApplicationOptions; +import io.appium.java_client.appmanagement.BaseTerminateApplicationOptions; +import org.openqa.selenium.WebDriverException; import org.openqa.selenium.remote.service.DriverService; import java.time.Duration; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; -public class Application implements IApplication { +public class Application implements IMobileApplication { private final ILocalizedLogger localizedLogger; private final IApplicationProfile applicationProfile; @@ -36,10 +45,7 @@ private void setImplicitlyWaitToDriver(Duration duration) { this.timeoutImpl = duration; } - /** - * Provides AppiumDriver instance for current application session. - * @return driver instance. - */ + @Override public AppiumDriver getDriver() { return appiumDriver; } @@ -49,18 +55,12 @@ public boolean isStarted() { return appiumDriver.getSessionId() != null; } - /** - * Provides current AppiumDriver service instance (would be null if driver is not local) - * @return driver service instance - */ + @Override public DriverService getDriverService() { return driverService; } - /** - * Returns name of current platform - * @return name - */ + @Override public final PlatformName getPlatformName() { return applicationProfile.getPlatformName(); } @@ -73,9 +73,7 @@ public void setImplicitWaitTimeout(Duration timeout) { } } - /** - * Executes appium driver quit, then stops the driver service - */ + @Override public void quit() { localizedLogger.info("loc.application.quit"); if (getDriver() != null) { @@ -88,11 +86,90 @@ public void quit() { } /** - * Terminate the particular application if it is running. - * @param bundleId the bundle identifier (or app id) of the app to be terminated. - * @return true if the app was running before and has been successfully stopped. + * Retries application actions. + * @param function result supplier. + * @return result of the function executed. + * @param type of the result. + */ + protected T doWithRetry(Supplier function) { + return AqualityServices.get(IActionRetrier.class) + .doWithRetry(function, Collections.singletonList(WebDriverException.class)); + } + + /** + * Retries application actions. + * @param function runnable function. */ - public boolean terminateApp(String bundleId) { - return ((InteractsWithApps)getDriver()).terminateApp(bundleId); + protected void doWithRetry(Runnable function) { + AqualityServices.get(IActionRetrier.class) + .doWithRetry(function, Collections.singletonList(WebDriverException.class)); + } + + @Override + public String getId() { + final String iosExtName = "mobile: activeAppInfo"; + return doWithRetry(() -> { + if (PlatformName.ANDROID == getPlatformName()) { + return ((AndroidDriver) getDriver()).getCurrentPackage(); + } + Map result = CommandExecutionHelper.executeScript(getDriver().assertExtensionExists(iosExtName), iosExtName); + return String.valueOf(Objects.requireNonNull(result).get("bundleId")); + }); + } + + @Override + public ApplicationState getState(String appId) { + localizedLogger.info("loc.application.get.state", appId); + ApplicationState state = doWithRetry(() -> appManagement().queryAppState(appId)); + localizedLogger.info("loc.application.state", state); + return state; + } + + @Override + public void install(String appPath) { + localizedLogger.info("loc.application.install", appPath); + doWithRetry(() -> appManagement().installApp(appPath)); + } + + @Override + public void background(Duration timeout) { + localizedLogger.info("loc.application.background"); + doWithRetry(() -> appManagement().runAppInBackground(timeout)); + } + + @Override + public boolean remove(String appId) { + localizedLogger.info("loc.application.remove", appId); + return doWithRetry(() -> appManagement().removeApp(appId)); + } + + @Override + public void activate(String appId) { + localizedLogger.info("loc.application.activate", appId); + doWithRetry(() -> appManagement().activateApp(appId)); + } + + @Override + public void activate(String appId, Duration timeout) { + localizedLogger.info("loc.application.activate", appId); + class Options extends BaseActivateApplicationOptions { + @Override + public Map build() { + return Collections.singletonMap("timeout", timeout.toMillis()); + } + } + doWithRetry(() -> appManagement().activateApp(appId, new Options())); + } + + @Override + public boolean terminate(String appId, Duration timeout) { + localizedLogger.info("loc.application.terminate", appId); + class Options extends BaseTerminateApplicationOptions { + @Override + public Map build() { + return Collections.singletonMap("timeout", timeout.toMillis()); + } + } + return doWithRetry(() -> appManagement().terminateApp(appId, new Options())); } } diff --git a/src/main/java/aquality/appium/mobile/application/AqualityServices.java b/src/main/java/aquality/appium/mobile/application/AqualityServices.java index 56a8d10..0ab3cac 100644 --- a/src/main/java/aquality/appium/mobile/application/AqualityServices.java +++ b/src/main/java/aquality/appium/mobile/application/AqualityServices.java @@ -11,7 +11,7 @@ import aquality.selenium.core.waitings.IConditionalWait; import com.google.inject.Injector; -public class AqualityServices extends aquality.selenium.core.applications.AqualityServices { +public class AqualityServices extends aquality.selenium.core.applications.AqualityServices { private static final ThreadLocal INSTANCE_CONTAINER = ThreadLocal.withInitial(AqualityServices::new); @@ -34,7 +34,7 @@ private static AqualityServices getInstance() { * * @return Instance of application. */ - public static Application getApplication() { + public static IMobileApplication getApplication() { return getInstance().getApp(injector -> AqualityServices.startApplication()); } @@ -95,7 +95,7 @@ public static void setApplicationFactory(IApplicationFactory applicationFactory) getInstance().applicationFactory = applicationFactory; } - private static Application startApplication() { + private static IMobileApplication startApplication() { return getApplicationFactory().getApplication(); } @@ -104,7 +104,7 @@ private static Application startApplication() { * * @param application Instance of desired application. */ - public static void setApplication(Application application) { + public static void setApplication(IMobileApplication application) { getInstance().setApp(application); } diff --git a/src/main/java/aquality/appium/mobile/application/IApplicationFactory.java b/src/main/java/aquality/appium/mobile/application/IApplicationFactory.java index 992cbd7..188f97a 100644 --- a/src/main/java/aquality/appium/mobile/application/IApplicationFactory.java +++ b/src/main/java/aquality/appium/mobile/application/IApplicationFactory.java @@ -2,5 +2,5 @@ public interface IApplicationFactory { - Application getApplication(); + IMobileApplication getApplication(); } diff --git a/src/main/java/aquality/appium/mobile/application/IMobileApplication.java b/src/main/java/aquality/appium/mobile/application/IMobileApplication.java new file mode 100644 index 0000000..cfe4518 --- /dev/null +++ b/src/main/java/aquality/appium/mobile/application/IMobileApplication.java @@ -0,0 +1,168 @@ +package aquality.appium.mobile.application; + +import aquality.selenium.core.applications.IApplication; +import aquality.selenium.core.configurations.ITimeoutConfiguration; +import io.appium.java_client.AppiumDriver; +import io.appium.java_client.InteractsWithApps; +import io.appium.java_client.appmanagement.ApplicationState; +import org.openqa.selenium.remote.service.DriverService; + +import java.time.Duration; + +public interface IMobileApplication extends IApplication { + /** + * Provides default timeout for terminate methods. + * @return default timeout for waiting until the application is terminated. + */ + static Duration getDefaultTerminateTimeout() { + return AqualityServices.get(ITimeoutConfiguration.class).getCondition(); + } + + /** + * Provides AppiumDriver instance for current application session. + * + * @return driver instance. + */ + AppiumDriver getDriver(); + + /** + * Provides current AppiumDriver service instance (would be null if driver is not local) + * + * @return driver service instance + */ + DriverService getDriverService(); + + /** + * Returns name of current platform + * + * @return name + */ + PlatformName getPlatformName(); + + /** + * Executes appium driver quit, then stops the driver service + */ + void quit(); + + /** + * Gets the bundle identifier (or appId) of the currently running application. + * + * @return id of the application in the foreground. + */ + String getId(); + + default InteractsWithApps appManagement() { + return (InteractsWithApps) getDriver(); + } + + /** + * Gets the state of the application. + * + * @param appId the bundle identifier (or appId) of the application. + * @return an enumeration of the application state + */ + ApplicationState getState(String appId); + + /** + * Installs an application. + * + * @param appPath file path or url of the application. + */ + void install(String appPath); + + /** + * Installs an application defined in settings. + * Note that path to the application must be defined as 'app' capability. + */ + default void install() { + install(AqualityServices.getApplicationProfile().getDriverSettings().getApplicationPath()); + } + + /** + * Send the currently active application to the background, + * and either return after a certain amount of time. + * + * @param timeout How long to background the application for. + */ + void background(Duration timeout); + + /** + * Send the currently active application to the background, + * and leave the application deactivated (as "Home" button does). + */ + default void background() { + background(Duration.ofSeconds(-1)); + } + + /** + * Removes an application. + * + * @param appId the bundle identifier (or appId) of the application to be removed. + * @return true if the uninstallation was successful. + */ + boolean remove(String appId); + + /** + * Removes currently running application. + * + * @return true if the uninstallation was successful. + */ + default boolean remove() { + return remove(getId()); + } + + /** + * Activates the given application by moving to the foreground if it is running in the background + * or starting it if it is not running yet. + * + * @param appId the bundle identifier (or appId) of the application. + */ + void activate(String appId); + + /** + * Activates the given application by moving to the foreground if it is running in the background + * or starting it if it is not running yet. + * + * @param appId the bundle identifier (or appId) of the application. + * @param timeout command timeout. + */ + void activate(String appId, Duration timeout); + + /** + * Terminate the particular application if it is running. + * + * @param appId the bundle identifier (or appId) of the application to be terminated. + * @param timeout defines for how long to wait until the application is terminated. + * @return true if the application was running before and has been successfully stopped. + */ + boolean terminate(String appId, Duration timeout); + + /** + * Terminate the particular application if it is running. + * + * @param appId the bundle identifier (or appId) of the application to be terminated. + * @return true if the application was running before and has been successfully stopped. + */ + default boolean terminate(String appId) { + return terminate(appId, getDefaultTerminateTimeout()); + } + + /** + * Terminates currently running application. + * + * @param timeout defines for how long to wait until the application is terminated. + * @return true if the application was running before and has been successfully stopped. + */ + default boolean terminate(Duration timeout) { + return terminate(getId(), timeout); + } + + /** + * Terminates currently running application. + * + * @return true if the application was running before and has been successfully stopped. + */ + default boolean terminate() { + return terminate(getDefaultTerminateTimeout()); + } +} diff --git a/src/main/java/aquality/appium/mobile/application/LocalApplicationFactory.java b/src/main/java/aquality/appium/mobile/application/LocalApplicationFactory.java index f4d20a4..cf8122a 100644 --- a/src/main/java/aquality/appium/mobile/application/LocalApplicationFactory.java +++ b/src/main/java/aquality/appium/mobile/application/LocalApplicationFactory.java @@ -11,7 +11,7 @@ public class LocalApplicationFactory extends ApplicationFactory { @Override - public Application getApplication() { + public IMobileApplication getApplication() { ILocalServiceSettings settings = AqualityServices.getLocalServiceSettings(); AppiumServiceBuilder builder = new AppiumServiceBuilder() .withCapabilities(settings.getCapabilities()); diff --git a/src/main/java/aquality/appium/mobile/application/MobileModule.java b/src/main/java/aquality/appium/mobile/application/MobileModule.java index 6932974..749d95b 100644 --- a/src/main/java/aquality/appium/mobile/application/MobileModule.java +++ b/src/main/java/aquality/appium/mobile/application/MobileModule.java @@ -15,9 +15,9 @@ import com.google.inject.Provider; import com.google.inject.Singleton; -public class MobileModule extends AqualityModule implements IConfigurationsModule, IElementsModule, IScreensModule, IActionsModule { +public class MobileModule extends AqualityModule implements IConfigurationsModule, IElementsModule, IScreensModule, IActionsModule { - public MobileModule(Provider applicationProvider) { + public MobileModule(Provider applicationProvider) { super(applicationProvider); } diff --git a/src/main/java/aquality/appium/mobile/application/RemoteApplicationFactory.java b/src/main/java/aquality/appium/mobile/application/RemoteApplicationFactory.java index 59ecee2..502b4c8 100644 --- a/src/main/java/aquality/appium/mobile/application/RemoteApplicationFactory.java +++ b/src/main/java/aquality/appium/mobile/application/RemoteApplicationFactory.java @@ -8,7 +8,7 @@ public class RemoteApplicationFactory extends ApplicationFactory { @Override - public Application getApplication() { + public IMobileApplication getApplication() { URL serverUrl = AqualityServices.getApplicationProfile().getRemoteConnectionUrl(); AppiumDriver driver = getDriver(serverUrl); driver.setFileDetector(new LocalFileDetector()); diff --git a/src/main/java/aquality/appium/mobile/configuration/DriverSettings.java b/src/main/java/aquality/appium/mobile/configuration/DriverSettings.java index b314f33..e5984d8 100644 --- a/src/main/java/aquality/appium/mobile/configuration/DriverSettings.java +++ b/src/main/java/aquality/appium/mobile/configuration/DriverSettings.java @@ -18,6 +18,7 @@ public class DriverSettings implements IDriverSettings { private static final String APPLICATION_PATH_KEY = "applicationPath"; private static final String APP_CAPABILITY_KEY = "app"; private static final String DEVICE_KEY_KEY = "deviceKey"; + public static final String CAPABILITIES = "capabilities"; private final ISettingsFile settingsFile; private final PlatformName platformName; @@ -33,12 +34,12 @@ public Capabilities getCapabilities() { DesiredCapabilities capabilities = new DesiredCapabilities(); capabilitiesFromSettings.forEach((key, value) -> { if (key.toLowerCase().endsWith("options")) { - value = settingsFile.getMap(getDriverSettingsPath("capabilities", key)); + value = settingsFile.getMap(getDriverSettingsPath(CAPABILITIES, key)); } capabilities.setCapability(key, value); }); if (hasApplicationPath()) { - capabilities.setCapability(APP_CAPABILITY_KEY, getAbsolutePath(getApplicationPath())); + capabilities.setCapability(APP_CAPABILITY_KEY, getApplicationPath()); } return capabilities.merge(getDeviceCapabilities()); } @@ -48,7 +49,7 @@ private Map getCapabilitiesFromSettings() { } private String getDriverCapabilitiesJsonPath() { - return getDriverSettingsPath("capabilities"); + return getDriverSettingsPath(CAPABILITIES); } private String getAbsolutePath(String relativePath) { @@ -64,7 +65,7 @@ private String getAbsolutePath(String relativePath) { } private boolean hasApplicationPath() { - return settingsFile.getMap(getDriverSettingsPath()).containsKey(APPLICATION_PATH_KEY); + return settingsFile.getMap(getDriverSettingsPath()).containsKey(APPLICATION_PATH_KEY) || getDeviceCapabilities().is(APP_CAPABILITY_KEY); } private Capabilities getDeviceCapabilities() { @@ -75,7 +76,16 @@ private Capabilities getDeviceCapabilities() { @Override public String getApplicationPath() { - return String.valueOf(settingsFile.getValue(getDriverSettingsPath(APPLICATION_PATH_KEY))); + return getAbsolutePath(String.valueOf(settingsFile.getValueOrDefault(getDriverSettingsPath(APPLICATION_PATH_KEY), + getDeviceCapabilities().getCapability(APP_CAPABILITY_KEY)))); + } + + @Override + public String getBundleId() { + final String BUNDLE_ID_CAPABILITY_KEY = platformName == PlatformName.ANDROID ? "appPackage" : "bundleId"; + String pathToCapability = getDriverSettingsPath(CAPABILITIES, BUNDLE_ID_CAPABILITY_KEY); + Object capabilityForDevice = getDeviceCapabilities().getCapability(BUNDLE_ID_CAPABILITY_KEY); + return (capabilityForDevice == null ? settingsFile.getValue(pathToCapability) : capabilityForDevice).toString(); } private String getDriverSettingsPath(final String... paths) { diff --git a/src/main/java/aquality/appium/mobile/configuration/IDriverSettings.java b/src/main/java/aquality/appium/mobile/configuration/IDriverSettings.java index 3412288..40cf14e 100644 --- a/src/main/java/aquality/appium/mobile/configuration/IDriverSettings.java +++ b/src/main/java/aquality/appium/mobile/configuration/IDriverSettings.java @@ -18,4 +18,11 @@ public interface IDriverSettings { * @return path to application. */ String getApplicationPath(); + + /** + * Provides the bundleId/appPackage of the application. + * + * @return application id. + */ + String getBundleId(); } diff --git a/src/main/java/aquality/appium/mobile/elements/Element.java b/src/main/java/aquality/appium/mobile/elements/Element.java index ccebf02..efe59e2 100644 --- a/src/main/java/aquality/appium/mobile/elements/Element.java +++ b/src/main/java/aquality/appium/mobile/elements/Element.java @@ -1,7 +1,7 @@ package aquality.appium.mobile.elements; -import aquality.appium.mobile.application.Application; import aquality.appium.mobile.application.AqualityServices; +import aquality.appium.mobile.application.IMobileApplication; import aquality.appium.mobile.elements.actions.ElementTouchActions; import aquality.appium.mobile.elements.actions.IElementTouchActions; import aquality.appium.mobile.elements.interfaces.IElement; @@ -32,7 +32,7 @@ protected Element(final By loc, final String nameOf, final ElementState stateOf) } @Override - protected Application getApplication() { + protected IMobileApplication getApplication() { return AqualityServices.getApplication(); } diff --git a/src/main/resources/localization/be.json b/src/main/resources/localization/be.json index b5b645e..992cf9e 100644 --- a/src/main/resources/localization/be.json +++ b/src/main/resources/localization/be.json @@ -1,5 +1,13 @@ { - "loc.application.quit": "Закрываем праграму", + "loc.application.quit": "Закрываем драйвер праграмы", + "loc.application.terminate": "Закрываем праграму '%s'", + "loc.application.install": "Усталёўваем праграму '%s'", + "loc.application.background": "Адпраўляем праграму ў фонавы рэжым", + "loc.application.background.with.timeout": "Адпраўляем праграму ў фонавы рэжым на %s сек.", + "loc.application.remove": "Выдаляем праграму '%s'", + "loc.application.activate": "Актывуем праграму '%s'", + "loc.application.get.state": "Атрымліваем стан праграмы '%s'", + "loc.application.state": "Стан праграмы: [%s]", "loc.application.driver.remote": "Усталёўваем драйвер праграммы з выкарыстаннем сервера па адрасе '%s'", "loc.platform.name.wrong": "Платформа '%s' не падтрымліваецца.", "loc.application.ready": "Праграма на платформе '%1$s' гатовая...", @@ -14,22 +22,22 @@ "loc.combobox.getting.selected.value": "Атрымліваем выбранае значэнне", "loc.combobox.selected.text": "Выбраны тэкст: [%s]", "loc.combobox.selected.value": "Выбранае значэнне: [%s]", - "loc.combobox.select.by.text" : "Выбіраем значэнне з тэкстам '%s'", - "loc.combobox.select.by.value" : "Выбіраем значэнне '%s'", + "loc.combobox.select.by.text": "Выбіраем значэнне з тэкстам '%s'", + "loc.combobox.select.by.value": "Выбіраем значэнне '%s'", "loc.combobox.get.texts": "Атрымліваем спіс тэкстаў опцыяў", - "loc.combobox.get.values" : "Атрымліваем спіс значэнняў", + "loc.combobox.get.values": "Атрымліваем спіс значэнняў", "loc.combobox.texts": "Спіс тэкстаў опцыяў: [%s]", "loc.combobox.values": "Спіс значэнняў: [%s]", - "loc.combobox.impossible.to.select.contain.value.or.text" : "Немагчыма выбраць опцыю, якая змяшчае значэнне/тэкст '%1$s' у камбабоксе '%2$s'", - "loc.file.reading_exception" : "Памылка пры чытанні файла: '%s'", - "loc.label" : "Надпіс", - "loc.link" : "Спасылка", - "loc.radio" : "Радыёкнопка", - "loc.send.text" : "Задаем тэкст - '%s'", - "loc.setting.value" : "Задаем значэнне '%s'", - "loc.text.clearing" : "Ачышчаем", - "loc.text.field" : "Тэкставае поле", - "loc.text.typing" : "Уводзім '%s'", + "loc.combobox.impossible.to.select.contain.value.or.text": "Немагчыма выбраць опцыю, якая змяшчае значэнне/тэкст '%1$s' у камбабоксе '%2$s'", + "loc.file.reading_exception": "Памылка пры чытанні файла: '%s'", + "loc.label": "Надпіс", + "loc.link": "Спасылка", + "loc.radio": "Радыёкнопка", + "loc.send.text": "Задаем тэкст - '%s'", + "loc.setting.value": "Задаем значэнне '%s'", + "loc.text.clearing": "Ачышчаем", + "loc.text.field": "Тэкставае поле", + "loc.text.typing": "Уводзім '%s'", "loc.text.focusing": "Факусуемся на полі", "loc.text.unfocusing": "Адводзім фокус з поля", "loc.text.masked_value": "********", diff --git a/src/main/resources/localization/en.json b/src/main/resources/localization/en.json index d3bcc73..b0f4c0e 100644 --- a/src/main/resources/localization/en.json +++ b/src/main/resources/localization/en.json @@ -1,5 +1,13 @@ { - "loc.application.quit": "Closing application", + "loc.application.quit": "Closing the driver", + "loc.application.terminate": "Closing the application '%s'", + "loc.application.install": "Installing the application '%s'", + "loc.application.background": "Sending the application to the background", + "loc.application.background.with.timeout": "Sending the application to the background for %s seconds", + "loc.application.remove": "Removing the application '%s'", + "loc.application.activate": "Activating the application '%s'", + "loc.application.get.state": "Getting state of the application '%s'", + "loc.application.state": "Application state: [%s]", "loc.application.driver.remote": "Setting application driver using the server on '%s'", "loc.platform.name.wrong": "Platform '%s' is not supported.", "loc.application.ready": "Application on platform '%1$s' is ready...", diff --git a/src/main/resources/localization/pl.json b/src/main/resources/localization/pl.json index 4801b1b..96cb7b9 100644 --- a/src/main/resources/localization/pl.json +++ b/src/main/resources/localization/pl.json @@ -1,5 +1,13 @@ { - "loc.application.quit": "Zamykanie aplikacji", + "loc.application.quit": "Zamykanie sterownika aplikacji", + "loc.application.terminate": "Zamykanie aplikacji '%s'", + "loc.application.install": "Instalowanie aplikacji '%s'", + "loc.application.background": "Wysyłanie aplikacji do tła", + "loc.application.background.with.timeout": "Wysyłanie aplikacji do tła na %s sek.", + "loc.application.remove": "Usuwanie aplikacji '%s'", + "loc.application.activate": "Aktywacja aplikacji '%s'", + "loc.application.get.state": "Uzyskiwanie stanu aplikacji '%s'", + "loc.application.state": "Stan aplikacji: [%s]", "loc.application.driver.remote": "Ustawianie sterownika aplikacji przy użyciu serwera na adresie '%s'", "loc.platform.name.wrong": "Platforma '%s' nie jest obsługiwana.", "loc.application.ready": "Aplikacja na platformie '%1$s' jest gotowa...", diff --git a/src/main/resources/localization/ru.json b/src/main/resources/localization/ru.json index 9874dec..9e39388 100644 --- a/src/main/resources/localization/ru.json +++ b/src/main/resources/localization/ru.json @@ -1,5 +1,13 @@ { - "loc.application.quit": "Закрытие приложения", + "loc.application.quit": "Закрытие драйвера приложения", + "loc.application.terminate": "Закрытие приложения '%s'", + "loc.application.install": "Установка приложения '%s'", + "loc.application.background": "Отправка приложения в фоновый режим", + "loc.application.background.with.timeout": "Отправка приложения в фоновый режим на %s сек.", + "loc.application.remove": "Удаление приложения '%s'", + "loc.application.activate": "Активация приложения '%s'", + "loc.application.get.state": "Получение состояния приложения '%s'", + "loc.application.state": "Состояние приложения: [%s]", "loc.application.driver.remote": "Установка драйвера для приложения с использованием сервера по адресу '%s'", "loc.platform.name.wrong": "Платформа '%s' не поддерживается.", "loc.application.ready": "Приложение на платформе '%1$s' готово...", diff --git a/src/main/resources/localization/uk.json b/src/main/resources/localization/uk.json index 0816225..2a1d3ed 100644 --- a/src/main/resources/localization/uk.json +++ b/src/main/resources/localization/uk.json @@ -1,5 +1,13 @@ { - "loc.application.quit": "Закриття програми", + "loc.application.quit": "Закриття драйвера програми", + "loc.application.terminate": "Закриття програми '%s'", + "loc.application.install": "Встановлення програми '%s'", + "loc.application.background": "Відправка програми у фоновий режим", + "loc.application.background.with.timeout": "Відправка програми у фоновий режим на %s сек.", + "loc.application.remove": "Выдалення програми '%s'", + "loc.application.activate": "Активація програми '%s'", + "loc.application.get.state": "Отримання стану програми '%s'", + "loc.application.state": "Стан програми: [%s]", "loc.application.driver.remote": "Налаштування драйвера програми за допомогою сервера за адресою '%s'", "loc.platform.name.wrong": "Платформа '%s' не підтримується.", "loc.application.ready": "Програма на платформі '%1$s' готова...", @@ -14,22 +22,22 @@ "loc.combobox.getting.selected.value": "Отримання вибраного значення", "loc.combobox.selected.text": "Вибраний текст: [%s]", "loc.combobox.selected.value": "Вибране значення: [%s]", - "loc.combobox.select.by.text" : "Вибір значення за текстом '%s'", - "loc.combobox.select.by.value" : "Вибір значення '%s'", + "loc.combobox.select.by.text": "Вибір значення за текстом '%s'", + "loc.combobox.select.by.value": "Вибір значення '%s'", "loc.combobox.get.texts": "Отримання масиву текстів опцій", - "loc.combobox.get.values" : "Отримання масиву значень", + "loc.combobox.get.values": "Отримання масиву значень", "loc.combobox.texts": "Список текстів опцій: [%s]", "loc.combobox.values": "Список значень: [%s]", - "loc.combobox.impossible.to.select.contain.value.or.text" : "Не вдалося вибрати опцію, що містіть значення/текст '%1$s' у комбобоксі '%2$s'", - "loc.file.reading_exception" : "Помилка підчас читання файлу: '%s'", - "loc.label" : "Напис", - "loc.link" : "Посилання", - "loc.radio" : "Радіокнопка", - "loc.send.text" : "Встановлення тексту - '%s'", - "loc.setting.value" : "Встановлення значення '%s'", - "loc.text.clearing" : "Очищення", - "loc.text.field" : "Текстове поле", - "loc.text.typing" : "Введення '%s'", + "loc.combobox.impossible.to.select.contain.value.or.text": "Не вдалося вибрати опцію, що містіть значення/текст '%1$s' у комбобоксі '%2$s'", + "loc.file.reading_exception": "Помилка підчас читання файлу: '%s'", + "loc.label": "Напис", + "loc.link": "Посилання", + "loc.radio": "Радіокнопка", + "loc.send.text": "Встановлення тексту - '%s'", + "loc.setting.value": "Встановлення значення '%s'", + "loc.text.clearing": "Очищення", + "loc.text.field": "Текстове поле", + "loc.text.typing": "Введення '%s'", "loc.text.focusing": "Фокусування на полі", "loc.text.unfocusing": "Зняття фокусування з поля", "loc.text.masked_value": "********", diff --git a/src/test/java/samples/android/nativeapp/AndroidBasicInteractionsTest.java b/src/test/java/samples/android/nativeapp/AndroidBasicInteractionsTest.java index 5edd379..231d7bd 100644 --- a/src/test/java/samples/android/nativeapp/AndroidBasicInteractionsTest.java +++ b/src/test/java/samples/android/nativeapp/AndroidBasicInteractionsTest.java @@ -1,14 +1,19 @@ package samples.android.nativeapp; import aquality.appium.mobile.application.AqualityServices; +import aquality.appium.mobile.application.IMobileApplication; import aquality.appium.mobile.application.MobileModule; import aquality.appium.mobile.elements.interfaces.ICheckBox; import aquality.appium.mobile.elements.interfaces.IRadioButton; +import aquality.selenium.core.configurations.ITimeoutConfiguration; +import io.appium.java_client.appmanagement.ApplicationState; import org.testng.Assert; +import org.testng.ITestResult; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Listeners; import org.testng.annotations.Test; +import org.testng.util.RetryAnalyzerCount; import samples.android.ITestCheckBox; import samples.android.ITestRadioButton; import samples.android.nativeapp.apidemos.ApplicationActivity; @@ -54,6 +59,36 @@ public void tearDown() { AqualityServices.getApplication().quit(); } + public static class RetryAnalyzer extends RetryAnalyzerCount { + public RetryAnalyzer() {} + @Override + public boolean retryMethod(ITestResult result) { + return !result.isSuccess() && result.getThrowable() instanceof AssertionError; + } + } + + @Test(retryAnalyzer = RetryAnalyzer.class, successPercentage = 50) + public void testApplicationManagement() { + IMobileApplication app = AqualityServices.getApplication(); + Assert.assertThrows(IllegalArgumentException.class, () -> AqualityServices.getApplicationProfile().getDriverSettings().getBundleId()); + ApplicationActivity.SEARCH.open(); + String id = app.getId(); + app.background(); + Assert.assertEquals(app.getState(id), ApplicationState.RUNNING_IN_BACKGROUND); + app.activate(id); + Assert.assertEquals(app.getState(id), ApplicationState.RUNNING_IN_FOREGROUND); + Assert.assertTrue(app.terminate()); + Assert.assertTrue(app.isStarted()); + Assert.assertEquals(app.getState(id), ApplicationState.NOT_RUNNING); + app.activate(id, AqualityServices.get(ITimeoutConfiguration.class).getCondition()); + Assert.assertEquals(app.getState(id), ApplicationState.RUNNING_IN_FOREGROUND); + Assert.assertTrue(app.remove()); + Assert.assertEquals(app.getState(id), ApplicationState.NOT_INSTALLED); + Assert.assertFalse(app.terminate(id)); + app.install(); + Assert.assertEquals(app.getState(id), ApplicationState.NOT_RUNNING); + } + @Test public void testSendKeys() { InvokeSearchScreen searchScreen = ApplicationActivity.SEARCH.open();