diff --git a/source/src/main/java/org/cerberus/core/crud/entity/TestCaseStepAction.java b/source/src/main/java/org/cerberus/core/crud/entity/TestCaseStepAction.java index 66dace630..e38ce3011 100644 --- a/source/src/main/java/org/cerberus/core/crud/entity/TestCaseStepAction.java +++ b/source/src/main/java/org/cerberus/core/crud/entity/TestCaseStepAction.java @@ -135,6 +135,9 @@ public class TestCaseStepAction { public static final String ACTION_SETCONTENT = "setContent"; public static final String ACTION_SETSERVICECALLCONTENT = "setServiceCallContent"; public static final String ACTION_SWITCHTOCONTEXT = "switchToContext"; + public static final String ACTION_LOCKDEVICE = "lockDevice"; + public static final String ACTION_UNLOCKDEVICE = "unlockDevice"; + public static final String ACTION_ROTATEDEVICE = "rotateDevice"; public static final String ACTION_DONOTHING = "doNothing"; // ??? TODO. Clean this unused action. diff --git a/source/src/main/java/org/cerberus/core/engine/gwt/impl/ActionService.java b/source/src/main/java/org/cerberus/core/engine/gwt/impl/ActionService.java index e36046e21..743c4816e 100644 --- a/source/src/main/java/org/cerberus/core/engine/gwt/impl/ActionService.java +++ b/source/src/main/java/org/cerberus/core/engine/gwt/impl/ActionService.java @@ -464,6 +464,15 @@ public TestCaseStepActionExecution doAction(TestCaseStepActionExecution actionEx case TestCaseStepAction.ACTION_GETROBOTFILE: res = this.doActionGetRobotFile(execution, actionExecution, value1, value2, value3); break; + case TestCaseStepAction.ACTION_LOCKDEVICE: + res = this.doActionLockDevice(execution); + break; + case TestCaseStepAction.ACTION_UNLOCKDEVICE: + res = this.doActionUnlockDevice(execution); + break; + case TestCaseStepAction.ACTION_ROTATEDEVICE: + res = this.doActionRotateDevice(execution); + break; case TestCaseStepAction.ACTION_DONOTHING: res = new MessageEvent(MessageEventEnum.ACTION_SUCCESS); break; @@ -2286,6 +2295,70 @@ private MessageEvent doActionHideKeyboard(TestCaseExecution tCExecution) { .resolveDescription("APPLICATIONTYPE", tCExecution.getApplicationObj().getType()); } + private MessageEvent doActionLockDevice(TestCaseExecution tCExecution) { + // Check argument + if (tCExecution == null) { + return new MessageEvent(MessageEventEnum.ACTION_FAILED_TYPE); + } + + // Hide keyboard according to application type + String applicationType = tCExecution.getApplicationObj().getType(); + if (Application.TYPE_APK.equals(applicationType)) { + return androidAppiumService.lockDevice(tCExecution.getSession()); + } + if (Application.TYPE_IPA.equals(applicationType)) { + return iosAppiumService.lockDevice(tCExecution.getSession()); + } + + // Else we are faced with a non supported application + return new MessageEvent(MessageEventEnum.ACTION_NOTEXECUTED_NOTSUPPORTED_FOR_APPLICATION) + .resolveDescription("ACTION", "lockDevice") + .resolveDescription("APPLICATIONTYPE", tCExecution.getApplicationObj().getType()); + } + + private MessageEvent doActionUnlockDevice(TestCaseExecution tCExecution) { + // Check argument + if (tCExecution == null) { + return new MessageEvent(MessageEventEnum.ACTION_FAILED_TYPE); + } + + // Hide keyboard according to application type + String applicationType = tCExecution.getApplicationObj().getType(); + if (Application.TYPE_APK.equals(applicationType)) { + return androidAppiumService.unlockDevice(tCExecution.getSession()); + } + if (Application.TYPE_IPA.equals(applicationType)) { + return iosAppiumService.unlockDevice(tCExecution.getSession()); + } + + // Else we are faced with a non supported application + return new MessageEvent(MessageEventEnum.ACTION_NOTEXECUTED_NOTSUPPORTED_FOR_APPLICATION) + .resolveDescription("ACTION", "unlockDevice") + .resolveDescription("APPLICATIONTYPE", tCExecution.getApplicationObj().getType()); + } + + private MessageEvent doActionRotateDevice(TestCaseExecution tCExecution) { + // Check argument + if (tCExecution == null) { + return new MessageEvent(MessageEventEnum.ACTION_FAILED_TYPE); + } + + // Hide keyboard according to application type + String applicationType = tCExecution.getApplicationObj().getType(); + if (Application.TYPE_APK.equals(applicationType)) { + return androidAppiumService.rotateDevice(tCExecution.getSession()); + } + if (Application.TYPE_IPA.equals(applicationType)) { + return iosAppiumService.rotateDevice(tCExecution.getSession()); + } + + // Else we are faced with a non supported application + return new MessageEvent(MessageEventEnum.ACTION_NOTEXECUTED_NOTSUPPORTED_FOR_APPLICATION) + .resolveDescription("ACTION", "rotateDevice") + .resolveDescription("APPLICATIONTYPE", tCExecution.getApplicationObj().getType()); + } + + private MessageEvent doActionSwipe(TestCaseExecution tCExecution, String object, String property) { // Check arguments if (tCExecution == null || object == null) { diff --git a/source/src/main/java/org/cerberus/core/enums/MessageEventEnum.java b/source/src/main/java/org/cerberus/core/enums/MessageEventEnum.java index 86f920bcd..a0a922570 100644 --- a/source/src/main/java/org/cerberus/core/enums/MessageEventEnum.java +++ b/source/src/main/java/org/cerberus/core/enums/MessageEventEnum.java @@ -179,6 +179,9 @@ public enum MessageEventEnum { ACTION_SUCCESS_OPENAPP(200, "OK", "Application '%APP%' opened.", false, false, false, MessageGeneralEnum.EXECUTION_PE_TESTSTARTED), ACTION_SUCCESS_CLOSEAPP(200, "OK", "Application '%APP%' closed.", false, false, false, MessageGeneralEnum.EXECUTION_PE_TESTSTARTED), ACTION_SUCCESS_CLOSEAPP_GENERIC(200, "OK", "Application closed.", false, false, false, MessageGeneralEnum.EXECUTION_PE_TESTSTARTED), + ACTION_SUCCESS_UNLOCKDEVICE_GENERIC(200, "OK", "Device unlocked.", false, false, false, MessageGeneralEnum.EXECUTION_PE_TESTSTARTED), + ACTION_SUCCESS_LOCKDEVICE_GENERIC(200, "OK", "Device locked.", false, false, false, MessageGeneralEnum.EXECUTION_PE_TESTSTARTED), + ACTION_SUCCESS_ROTATEDEVICE_GENERIC(200, "OK", "Device rotated.", false, false, false, MessageGeneralEnum.EXECUTION_PE_TESTSTARTED), ACTION_SUCCESS_CLICK(200, "OK", "Element '%ELEMENT%' clicked.", false, false, false, MessageGeneralEnum.EXECUTION_PE_TESTSTARTED), ACTION_SUCCESS_CLICKANDWAIT(200, "OK", "Element '%ELEMENT%' clicked and waited %TIME% ms.", false, false, false, MessageGeneralEnum.EXECUTION_PE_TESTSTARTED), ACTION_SUCCESS_CLICKANDNOWAIT(200, "OK", "Element '%ELEMENT%' clicked and waited for page to load", false, false, false, MessageGeneralEnum.EXECUTION_PE_TESTSTARTED), diff --git a/source/src/main/java/org/cerberus/core/service/appium/IAppiumService.java b/source/src/main/java/org/cerberus/core/service/appium/IAppiumService.java index 98b558c2a..2fe8427ad 100644 --- a/source/src/main/java/org/cerberus/core/service/appium/IAppiumService.java +++ b/source/src/main/java/org/cerberus/core/service/appium/IAppiumService.java @@ -166,4 +166,22 @@ public interface IAppiumService { * @return */ MessageEvent clearField(Session session, Identifier identifier); + + /** + * @param session + * @return + */ + MessageEvent lockDevice(Session session); + + /** + * @param session + * @return + */ + MessageEvent unlockDevice(Session session); + + /** + * @param session + * @return + */ + MessageEvent rotateDevice(Session session); } diff --git a/source/src/main/java/org/cerberus/core/service/appium/impl/AndroidAppiumService.java b/source/src/main/java/org/cerberus/core/service/appium/impl/AndroidAppiumService.java index e3524023d..f40f24eef 100644 --- a/source/src/main/java/org/cerberus/core/service/appium/impl/AndroidAppiumService.java +++ b/source/src/main/java/org/cerberus/core/service/appium/impl/AndroidAppiumService.java @@ -37,6 +37,7 @@ import org.cerberus.core.util.JSONUtil; import org.cerberus.core.util.StringUtil; import org.json.JSONException; +import org.openqa.selenium.ScreenOrientation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -252,4 +253,49 @@ public MessageEvent closeApp(Session session) { } } + @Override + public MessageEvent lockDevice(Session session) { + try { + AndroidDriver driver = ((AndroidDriver) session.getAppiumDriver()); + driver.lockDevice(); + + return new MessageEvent(MessageEventEnum.ACTION_SUCCESS_LOCKDEVICE_GENERIC); + + } catch (Exception e) { + LOG.warn("Unable to close app " + e.getMessage(), e); + return new MessageEvent(MessageEventEnum.ACTION_FAILED_GENERIC) + .resolveDescription("DETAIL", "Unable to close app : " + e.getMessage()); + } + } + + @Override + public MessageEvent unlockDevice(Session session) { + try { + AndroidDriver driver = ((AndroidDriver) session.getAppiumDriver()); + driver.unlockDevice(); + + return new MessageEvent(MessageEventEnum.ACTION_SUCCESS_UNLOCKDEVICE_GENERIC); + + } catch (Exception e) { + LOG.warn("Unable to close app " + e.getMessage(), e); + return new MessageEvent(MessageEventEnum.ACTION_FAILED_GENERIC) + .resolveDescription("DETAIL", "Unable to close app : " + e.getMessage()); + } + } + + @Override + public MessageEvent rotateDevice(Session session) { + try { + AndroidDriver driver = ((AndroidDriver) session.getAppiumDriver()); + driver.rotate(ScreenOrientation.LANDSCAPE == driver.getOrientation() ? ScreenOrientation.PORTRAIT : ScreenOrientation.LANDSCAPE); + + return new MessageEvent(MessageEventEnum.ACTION_SUCCESS_UNLOCKDEVICE_GENERIC); + + } catch (Exception e) { + LOG.warn("Unable to close app " + e.getMessage(), e); + return new MessageEvent(MessageEventEnum.ACTION_FAILED_GENERIC) + .resolveDescription("DETAIL", "Unable to close app : " + e.getMessage()); + } + } + } diff --git a/source/src/main/java/org/cerberus/core/service/appium/impl/IOSAppiumService.java b/source/src/main/java/org/cerberus/core/service/appium/impl/IOSAppiumService.java index 1dbfb7a5a..74fa7a553 100644 --- a/source/src/main/java/org/cerberus/core/service/appium/impl/IOSAppiumService.java +++ b/source/src/main/java/org/cerberus/core/service/appium/impl/IOSAppiumService.java @@ -20,6 +20,8 @@ package org.cerberus.core.service.appium.impl; import io.appium.java_client.TouchAction; +import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.ios.IOSDriver; import io.appium.java_client.touch.WaitOptions; import io.appium.java_client.touch.offset.PointOption; import org.apache.logging.log4j.LogManager; @@ -34,6 +36,7 @@ import org.cerberus.core.util.StringUtil; import org.json.JSONException; import org.openqa.selenium.Keys; +import org.openqa.selenium.ScreenOrientation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -264,4 +267,50 @@ public MessageEvent closeApp(Session session) { } } + @Override + public MessageEvent lockDevice(Session session) { + try { + IOSDriver driver = ((IOSDriver) session.getAppiumDriver()); + driver.lockDevice(); + + return new MessageEvent(MessageEventEnum.ACTION_SUCCESS_LOCKDEVICE_GENERIC); + + } catch (Exception e) { + LOG.warn("Unable to close app " + e.getMessage(), e); + return new MessageEvent(MessageEventEnum.ACTION_FAILED_GENERIC) + .resolveDescription("DETAIL", "Unable to close app : " + e.getMessage()); + } + } + + @Override + public MessageEvent unlockDevice(Session session) { + try { + IOSDriver driver = ((IOSDriver) session.getAppiumDriver()); + driver.unlockDevice(); + + return new MessageEvent(MessageEventEnum.ACTION_SUCCESS_UNLOCKDEVICE_GENERIC); + + } catch (Exception e) { + LOG.warn("Unable to close app " + e.getMessage(), e); + return new MessageEvent(MessageEventEnum.ACTION_FAILED_GENERIC) + .resolveDescription("DETAIL", "Unable to close app : " + e.getMessage()); + } + } + + @Override + public MessageEvent rotateDevice(Session session) { + try { + IOSDriver driver = ((IOSDriver) session.getAppiumDriver()); + LOG.warn("orientation : " + driver.getOrientation().value()); + driver.rotate(ScreenOrientation.LANDSCAPE == driver.getOrientation() ? ScreenOrientation.PORTRAIT : ScreenOrientation.LANDSCAPE); + + return new MessageEvent(MessageEventEnum.ACTION_SUCCESS_UNLOCKDEVICE_GENERIC); + + } catch (Exception e) { + LOG.warn("Unable to close app " + e.getMessage(), e); + return new MessageEvent(MessageEventEnum.ACTION_FAILED_GENERIC) + .resolveDescription("DETAIL", "Unable to close app : " + e.getMessage()); + } + } + } diff --git a/source/src/main/resources/documentation/D1/include/en/testmanagement_tcactions_en.adoc b/source/src/main/resources/documentation/D1/include/en/testmanagement_tcactions_en.adoc index 8a4d9a543..19a802acc 100644 --- a/source/src/main/resources/documentation/D1/include/en/testmanagement_tcactions_en.adoc +++ b/source/src/main/resources/documentation/D1/include/en/testmanagement_tcactions_en.adoc @@ -856,6 +856,30 @@ Force current content of the test case to the console logs in a JSON format. Tha |=== Force current content of the test case to the data provided in value1 parameter. It can be any format you need (JSON, XML). All controls attached to that action will point to that data. Once all controls of that action are finished, current content are set back to normal value (could be an html or app page or another service). +==== lockDevice +|=== + +| *[green]#APK#* | *[green]#IPA#* + +|=== +Lock the device during the execution + +==== unlockDevice +|=== + +| *[green]#APK#* | *[green]#IPA#* + +|=== +Unlock the locked device during the execution. For APK, capabilities unlockType et unlockKey can be used to unlock devices protected by a key. + +==== rotateDevice +|=== + +| *[green]#APK#* | *[green]#IPA#* + +|=== +Rotate the screen 90 degrees. + ==== doNothing |=== diff --git a/source/src/main/webapp/js/testcase/action.js b/source/src/main/webapp/js/testcase/action.js index 85cede5e4..214ef63d3 100644 --- a/source/src/main/webapp/js/testcase/action.js +++ b/source/src/main/webapp/js/testcase/action.js @@ -26,6 +26,7 @@ var actionOptGroupList = [ {"name":"command", "label":{"en":"Execute Command","fr":"Execution de Commande"}, "picto":""}, {"name":"wait", "label":{"en":"Wait","fr":"Attendre"}, "picto":""}, {"name":"file", "label":{"en":"File","fr":"Fichier"}, "picto":""}, + {"name":"device", "label":{"en":"Mobile Device","fr":"Appareil Mobile"}, "picto":""}, {"name":"context_control", "label":{"en":"Context Controls","fr":"Contexte des Contrôles"}, "picto":""} ]; @@ -142,5 +143,8 @@ var actionOptList = { "field1":{"label":{"en": "Path/Pattern to retrieved. ex : /home/seluser/Downloads/", "fr": "Nom du fichier à récupérer. ex : /home/seluser/Downloads/"},"picto":"images/action-file.png", "class": "col-lg-12 crb-autocomplete-variable"}, "field2":{"label":{"en":"Nb of files","fr":"Nb de fichiers"},"picto":"images/action-numeric.png", "class": "col-lg-6 crb-autocomplete-variable"}, "field3":{"label":{"en":"Sorting Option. (LASTMODIFIED/IGNORECASEDESC/IGNORECASEASC/DESC/ASC)","fr":"Option de tri. (LASTMODIFIED/IGNORECASEDESC/IGNORECASEASC/DESC/ASC)"},"picto":"images/action-settings.png", "class": "col-lg-6 crb-autocomplete-filesortflag"},"documentation":{"en":"...","fr":"..."}}, + "lockDevice":{"group":"device","value": "lockDevice","label":{"en":"Lock Device","fr":"Verrouiller l'appareil"},"application_types":["APK","IPA"],"documentation":{"en":"...","fr":"..."}}, + "unlockDevice":{"group":"device","value": "unlockDevice","label":{"en":"Unlock Device","fr":"Déverrouiller l'appareil"},"application_types":["APK","IPA"],"documentation":{"en":"...","fr":"..."}}, + "rotateDevice":{"group":"device","value": "rotateDevice","label":{"en":"Rotate Device","fr":"Tourner l'appareil"},"application_types":["APK","IPA"],"documentation":{"en":"...","fr":"..."}}, "doNothing":{"group":"none","value": "doNothing","label":{"en":"No action","fr":"Pas d'action"},"application_types":["GUI","SRV","IPA","APK","FAT"],"documentation":{"en":"...","fr":"..."}} };