chore(ui-test): First-approach

Add basic layered architecture and basic test suite (status login).
This commit is contained in:
Noelia 2022-03-29 10:30:38 +02:00 committed by Iuri Matias
parent dd9653bdec
commit 57b731198f
30 changed files with 590 additions and 0 deletions

18
test/ui-test/README.md Normal file
View File

@ -0,0 +1,18 @@
# status-desktop-ui-test
UI test application for **Status Desktop**
* Test automation project that uses [Squish](https://www.froglogic.com/squish/) as a testing tool with [BDD](https://www.froglogic.com/squish/features/bdd-behavior-driven-development-testing/).
* Information about its architecture can be found in [wiki](https://hackmd.io/@status-desktop/B1MlJV5nd/%2Fm9D4p_y7ShOm3ooD7GAT0A).
![Screenshot 2022-02-25 at 10 22 45](https://user-images.githubusercontent.com/97019400/155689587-e933bbfa-519c-4f73-90a7-c019c0bb163f.png)
## Preparing the environment to develop and run tests
1) Install [Squish](https://doc.froglogic.com/squish/latest/) and run its IDE.
2) `File / Open Test Suite` and browse to `testSuites` directory.
3) Once the suite is open, click `Test Suite Settings` button to configure the **AUT** (Application Under test).
* Select `AUT tab` and browse until the corresponding `status-desktop\bin\nim_status_client` binary is set.
* Uncheck `Automatically start the AUT` option (if it is already checked).
* Save changes.
Now you should be able to create new suites, test cases and run the existing ones just only by clicking `Run` buttons!!

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 260 56"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#5dc754;}</style></defs><g id="Ebene_2" data-name="Ebene 2"><g id="Ebene_1-2" data-name="Ebene 1"><path d="M70.9,15a12.41,12.41,0,0,0-2.7-.3c-2.3,0-3.3.8-3.8,2.9L64,19.7h6.3l-1.2,6H62.8l-3.7,19H52l3.7-19h-4l1.2-6h4l.5-2.7c1.1-5.7,4.4-8.3,10.8-8.3a17.59,17.59,0,0,1,4.6.6Z"/><path d="M91.4,19.4l-1.8,6a5.5,5.5,0,0,0-2.2-.2,8,8,0,0,0-7,4.2L77.5,44.7H70.3l4.8-25h6.4l-.1,3.1A9.47,9.47,0,0,1,89,19C89.7,19.1,90.5,19.2,91.4,19.4Z"/><path d="M90.9,32.2c1.6-8,7.7-13.1,15-13.1s11.4,5.2,9.8,13.1-7.6,13.1-15,13.1S89.3,40.1,90.9,32.2Zm17.5,0c.9-4.5-.7-7-3.8-7s-5.6,2.5-6.5,7,.7,7,3.8,7S107.5,36.7,108.4,32.2Z"/><path d="M134.8,42.7a9.71,9.71,0,0,1-6.7,2.6c-6.1,0-10.1-4.8-8.5-13.1s7.6-13.1,13.7-13.1c2.8,0,5,1.2,5.9,2.9l.8-2.3h6.5l-4.8,24.5c-1.5,7.5-7.2,11.8-15,11.8-4.4,0-8.2-1.8-9.9-5l6.2-3.4a5.25,5.25,0,0,0,4.9,2.8,6.28,6.28,0,0,0,6.4-5.3Zm-4.1-3.5a7,7,0,0,0,5.2-2.5l1.8-9.1a4.55,4.55,0,0,0-4.3-2.5c-3.2,0-5.7,2.4-6.6,7S127.5,39.2,130.7,39.2Z"/><path d="M148.2,44.7l7-35.9h7.2l-7,35.9Z"/><path d="M163.1,32.2c1.6-8,7.6-13.1,15-13.1s11.4,5.2,9.9,13.1-7.6,13.1-15,13.1S161.6,40.1,163.1,32.2Zm17.6,0c.9-4.5-.7-7-3.8-7s-5.6,2.5-6.5,7,.7,7,3.8,7S179.8,36.7,180.7,32.2Z"/><path d="M207,42.7a9.71,9.71,0,0,1-6.7,2.6c-6.1,0-10.1-4.8-8.5-13.1s7.6-13.1,13.7-13.1c2.7,0,5,1.2,5.9,2.9l.8-2.3h6.5L214,44.2c-1.5,7.5-7.1,11.8-14.9,11.8-4.4,0-8.2-1.8-9.9-5l6.2-3.4a5.25,5.25,0,0,0,4.9,2.8,6.05,6.05,0,0,0,6.4-5.3Zm-4-3.5a7.38,7.38,0,0,0,5.2-2.5l1.8-9.1a4.55,4.55,0,0,0-4.3-2.5c-3.2,0-5.8,2.4-6.7,7S199.8,39.2,203,39.2Z"/><path d="M220.6,44.7l4.9-25h7.2l-4.9,25ZM231.2,8.4a3.36,3.36,0,0,1,3.5,3.2,3.4,3.4,0,0,1-.1,1,5.32,5.32,0,0,1-5,4.2,3.46,3.46,0,0,1-3.6-3.3,2.77,2.77,0,0,1,.1-.9A5.35,5.35,0,0,1,231.2,8.4Z"/><path d="M250.3,19.1c5.5,0,9.1,3,9.7,7.8l-7.2,1.6a3.35,3.35,0,0,0-3.4-3.3h-.3c-3.1,0-5.6,2.6-6.4,7s.6,7,3.7,7a5.63,5.63,0,0,0,5-3.3l6.6,1.6a14.23,14.23,0,0,1-12.7,7.9c-7.3,0-11.3-5.2-9.8-13.1S243,19.1,250.3,19.1Z"/><rect class="cls-1" x="25.5" y="7.6" width="5.7" height="13.1" transform="translate(-0.01 28.28) rotate(-53)"/><path class="cls-2" d="M37.7,28.9l6.9-9.1-3.3-2.5-4.9,6.5-1.3-1L40,16.3,23,3.5,18.2,10,16.9,9l4.9-6.5L18.5,0,11.6,9.1l4.6,3.4L9.8,21,5.9,18.1,0,26l3.9,2.9,3.9-5.2,17,12.8-3.9,5.2,3.9,3,5.9-7.8L26.8,34l6.4-8.5ZM25.8,14.8l-3.3-2.5L25,9l3.3,2.5Zm5.9,4.4-3.3-2.5,2.5-3.3,3.3,2.5Z"/><rect x="24.7" y="10.09" width="2.4" height="2.4" transform="translate(1.29 25.18) rotate(-53)"/><rect x="30.61" y="14.49" width="2.4" height="2.4" transform="translate(0.13 31.65) rotate(-52.99)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="252" height="252"><path fill="#BABCBE" d="M223.99 65a.01.01 0 0 1 .01.01v144.98a.01.01 0 0 1-.01.01H28.01a.01.01 0 0 1-.01-.01V65.01a.01.01 0 0 1 .01-.01h195.98z"/><path fill="#F2F2F2" d="M40 141h82v57H40zM129 141h81v57h-81zM40 74h82v60H40zM129 74h81v60h-81z"/><path fill="#333" d="M28 42h196v22H28z"/><path stroke="#333" stroke-width="3" stroke-miterlimit="10" d="M157 85.5h-12M176 96.5h-11M171 107.5h-26M155 118.5h-10"/><path stroke="#4CA34B" stroke-width="5.566" stroke-miterlimit="10" d="M53.254 120.958l22.116-15.686"/><circle fill="none" stroke="#4CA34B" stroke-width="5.566" stroke-miterlimit="10" cx="89.423" cy="98.538" r="15.087"/><circle fill="#4CA34B" cx="155.333" cy="169.933" r="8.271"/><path fill="#4CA34B" d="M183 161.713l15.388 8.883L183 179.479zM168.298 192h-2.964l8.895-44h2.966z"/><path stroke="#848689" stroke-width="3" stroke-miterlimit="10" d="M111 162.5H72M111 180.5H72"/><path fill="none" stroke="#4CA34B" stroke-width="4.287" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M51.996 159.113l6.848 7.303 4.506-13.098"/><path fill="#4CA34B" stroke="#333" stroke-width="4.271" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M52.043 185.148l10.458-10.22M61.908 185.148l-9.272-10.22"/><circle fill="#FFF" cx="41.214" cy="52.164" r="5.639"/><circle fill="#FFF" cx="58.132" cy="52.164" r="5.639"/><circle fill="#FFF" cx="75.049" cy="52.164" r="5.639"/><path fill="none" stroke="#848689" stroke-width="2" stroke-miterlimit="10" d="M40 135h82M129 135h81M40 198h82M129 198h81M28 65h196"/><path fill="#BABCBE" stroke="#848689" stroke-width="3" stroke-miterlimit="10" d="M171 85.5h-10"/><path stroke="#848689" stroke-width="3" stroke-miterlimit="10" d="M189 96.5h-10"/><path stroke="#333" stroke-width="3" stroke-miterlimit="10" d="M161 96.5h-16M194 107.5h-20"/><path stroke="#848689" stroke-width="3" stroke-miterlimit="10" d="M168 118.5h-10"/><path stroke="#333" stroke-width="3" stroke-miterlimit="10" d="M182 118.5h-10"/></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

0
test/ui-test/src/__init__.py Executable file
View File

View File

@ -0,0 +1,24 @@
#******************************************************************************
# Status.im
#*****************************************************************************/
#/**
# * \file StatusAccount.py
# *
# * \date February 2022
# * \brief It defines a basic status account object.
# *****************************************************************************/
#It defines a basic status account object.
class StatusAccount():
__name = None
__password = None
def __init__(self, name, password = None):
self.__name = name
self.__password = password
def get_name(self):
return self.__name
def get_password(self):
return self.__password

View File

View File

@ -0,0 +1,76 @@
#******************************************************************************
# Status.im
#*****************************************************************************/
#/**
# * \file SquishDriver.py
# *
# * \date February 2022
# * \brief It contains generic Status view components definitions and Squish driver API.
# *****************************************************************************/
from enum import Enum
# IMPORTANT: It is necessary to import manually the Squish drivers module by module.
# More info in: https://kb.froglogic.com/display/KB/Article+-+Using+Squish+functions+in+your+own+Python+modules+or+packages
import squish
import object
import names
# The default maximum timeout to find ui object
_MAX_WAIT_OBJ_TIMEOUT = 5000 #[milliseconds]
# Waits for the given object is loaded, visible and enabled.
# It returns a tuple: True in case it is found. Otherwise, false. And the object itself.
def is_loaded_visible_and_enabled(objName, timeout = _MAX_WAIT_OBJ_TIMEOUT):
obj = None
try:
obj = squish.waitForObject(getattr(names, objName), timeout)
return True, obj
except LookupError:
return False, obj
# Waits for the given object is loaded and might be not visible and/or not enabled:
# It returns a tuple: True in case it is found. Otherwise, false. And the object itself.
def is_loaded(objName):
obj = None
try:
obj = squish.findObject(getattr(names, objName))
return True, obj
except LookupError:
return False, obj
# It checks if the given object is visible and enabled.
def is_visible_and_enabled(obj):
return obj.visible and obj.enabled
# Given a specific object, get a specific child.
def get_child(obj, child_index = None):
if None == child_index:
return object.children(obj)
else:
return object.children(obj)[child_index]
# It executes the click action into the given object:
def click_obj(obj):
try:
squish.mouseClick(obj, squish.Qt.LeftButton)
return True
except LookupError:
return False
# It executes the click action into object with given object name:
def click_obj_by_name(objName):
try:
obj = squish.waitForObject(getattr(names, objName))
squish.mouseClick(obj, squish.Qt.LeftButton)
return True
except LookupError:
return False
# It types the specified text into the given object (as if the user had used the keyboard):
def type(objName, text):
try:
obj = squish.findObject(getattr(names, objName))
squish.type(obj, text)
return True
except LookupError:
return False

View File

View File

@ -0,0 +1,127 @@
#******************************************************************************
# Status.im
#*****************************************************************************/
#/**
# * \file StatusLoginProcess.py
# *
# * \date February 2022
# * \brief It defines the status login process.
# *****************************************************************************/
from processes.StatusProcess import StatusProcess
from screens.StatusLoginScreen import StatusLoginScreen
from screens.StatusLoginScreen import PswPlaceholderTextType
# It defines the status login process.
class StatusLoginProcess(StatusProcess):
__login_screen = None
__account = None
__byKeycard = False
__isBiometrics = False
__step1_result = False
__step2_result = False
__step3_result = False
def __init__(self, account, isBiometrics=False):
self.__account = account
self.__byKeycard = (account.get_password() == None)
self.__isBiometrics = isBiometrics
# It is used to check if the process can be run
def can_execute_process(self):
# Create current screen and verify if it is correctly loaded
self.__login_screen = StatusLoginScreen()
return self.__login_screen.is_loaded()
# It is used to execute the status login process steps.
def execute_process(self, verify_success = True):
self.__verify_success = verify_success
if(self.__account.get_password()):
self.__execute_password_steps()
else:
raise NotImplementedError("TODO: __execute_keycard_steps")
# It is used to obtain the status login process output result.
def get_process_result(self):
result = False
result_description = None
# Login by password:
if(self.__account.get_password()):
if not self.__step1_result:
result_description = "Not possible to select the given account."
elif not self.__step2_result:
result_description = "Not possible to introduce given password and submit."
elif not self.__step3_result and self.__verify_success:
result_description = "Expected connection to Status Desktop failed."
elif not self.__step3_result and not self.__verify_success:
result_description = "Expected error message not shown."
else:
# All the steps have been correctly executed.
result = True
result_description = "Process steps succeeded!"
# Login by keycard:
else:
raise NotImplementedError("TODO: __execute_keycard_steps")
return result, result_description
# Step 1 - The user selects an account
# Step 2 - The user enters a password
# Step 3 - Verify login success / failure
def __execute_password_steps(self):
# Step 1:
self.__step1_result = self.__step_user_selects_account(self.__account.get_name())
# Step 2:
self.__step2_result = self.__step_user_enters_password(self.__account.get_password())
# Step 3:
if self.__verify_success:
self.__step3_result = self.__verify_login_success()
else:
self.__step3_result = self.__verify_login_failure()
# It navigates through login screen to select the given account:
def __step_user_selects_account(self, accountName):
result = False
if self.__login_screen and self.__login_screen.is_loaded():
if self.__login_screen.open_accounts_selector_popup():
accounts_popup = self.__login_screen.get_accounts_selector_popup()
if accounts_popup.is_loaded():
result = accounts_popup.select_account(accountName)
return result
# It navigates through password input and submits the introduced one:
def __step_user_enters_password(self, password):
res1 = False
res2 = False
if self.__login_screen and self.__login_screen.is_loaded():
res1 = self.__login_screen.introduce_password(password)
res2 = self.__login_screen.submit_password()
return res1 & res2
# It inspects login screen and decides if login has been succeed:
def __verify_login_success(self):
res1 = False
res2 = False
if self.__login_screen and self.__login_screen.is_loaded():
res1 = self.__login_screen.get_password_placeholder_text() == PswPlaceholderTextType.CONNECTING.value
res2 = self.__login_screen.get_error_message_text() == ""
return res1 & res2
# It inspects login screen and decides if it displays the expected failure information:
def __verify_login_failure(self):
result = False
if self.__login_screen and self.__login_screen.is_loaded():
result = self.__login_screen.get_error_message_text() == self.__login_screen.get_expected_error_message_text()
return result

View File

@ -0,0 +1,47 @@
#******************************************************************************
# Status.im
#*****************************************************************************/
#/**
# * \file StatusProcess.py
# *
# * \date February 2022
# * \brief Base template class to define testing status processes.
# *****************************************************************************/
class StatusProcess:
__context = None
# Variable used to determine if it is needed to verify the process success or the process failure behavior
__verify_success = True
def __init__(self, context):
self.__context = context
# It is used to check if the process can be run
#@abstractmethod
def can_execute_process(self):
print("TODO: Invoke needed screen/s constructors")
# ***
# Below, the code to create the necessary status screen to execute the process.
# ***
# It is used to execute the specific status process steps.
#@abstractmethod
def execute_process(self, verify_success = True):
self.__verify_success = verify_success
print("TODO: Invoke navigations")
# ***
# Below, the code to invoke the necessary status screen's navigations.
# ***
# It is used to obtain the status process output result.
#@abstractmethod
def get_process_result(self):
result = False
result_description = None
print("TODO: Validate process steps")
# ***
# Below, the code to validate status process steps.
# ***
return result, result_description

View File

View File

@ -0,0 +1,47 @@
#******************************************************************************
# Status.im
#*****************************************************************************/
#/**
# * \file AccountsPopup.py
# *
# * \date February 2022
# * \brief It defines the status accounts popup behavior and properties.
# *****************************************************************************/
from drivers.SquishDriver import *
# It defines the identifier for each Account View component:
class SAccountsComponents(Enum):
ACCOUNTS_POPUP = "accountsView_accountListPanel"
#It defines the status accounts popup behavior and properties.
class StatusAccountsScreen():
__is_loaded = False
__accountsList = None
def __init__(self):
[self.__is_loaded, self.__accountsList] = is_loaded_visible_and_enabled(SAccountsComponents.ACCOUNTS_POPUP.value)
def is_loaded(self):
return self.__is_loaded
def find_account(self, account):
[found, account_obj] = self.__find_account(account)
return found
def select_account(self, account):
[found, account_obj] = self.__find_account(account)
if found:
return click_obj(account_obj)
return found
def __find_account(self, account):
found = False
account_obj = None
for index in range(self.__accountsList.count):
a = self.__accountsList.itemAtIndex(index)
if(a.username == account):
account_obj = a
found = True
break
return found, account_obj

View File

@ -0,0 +1,86 @@
#******************************************************************************
# Status.im
#*****************************************************************************/
#/**
# * \file StatusLoginScreen.py
# *
# * \date February 2022
# * \brief It defines the status login screen behavior and properties.
# *****************************************************************************/
from enum import Enum
from screens.StatusAccountsScreen import StatusAccountsScreen
from drivers.SquishDriver import *
# It defines the identifier for each Login View component:
class SLoginComponents(Enum):
MAIN_VIEW = "loginView_main"
PASSWORD_INPUT = "loginView_passwordInput"
SUBMIT_BTN = "loginView_submitBtn"
CHANGE_ACCOUNT_BTN = "loginView_changeAccountBtn"
ERR_MSG_LABEL = "loginView_errMsgLabel"
# It defines expected password placeholder text.
class PswPlaceholderTextType(Enum):
NONE = None
CONNECTING = "Connecting..."
PASSWORD = "Enter password"
# It defines the status login screen behavior and properties.
class StatusLoginScreen():
__is_loaded = False
__login_view_obj = None
def __init__(self):
[self.__is_loaded, self.__login_view_obj] = is_loaded_visible_and_enabled(SLoginComponents.MAIN_VIEW.value)
def is_loaded(self):
return self.__is_loaded
def introduce_password(self, password):
result = False
if click_obj_by_name(SLoginComponents.PASSWORD_INPUT.value) and type(SLoginComponents.PASSWORD_INPUT.value, password):
result = True
return result
def submit_password(self):
return click_obj_by_name(SLoginComponents.SUBMIT_BTN.value)
def open_accounts_selector_popup(self):
return click_obj_by_name(SLoginComponents.CHANGE_ACCOUNT_BTN.value)
def get_accounts_selector_popup(self):
return StatusAccountsScreen()
def get_password_placeholder_text(self):
result = ""
[loaded, obj] = is_loaded(SLoginComponents.PASSWORD_INPUT.value)
if loaded:
result = obj.placeholderText
return result
def get_error_message_text(self):
result = ""
[loaded, obj] = is_loaded_visible_and_enabled(SLoginComponents.ERR_MSG_LABEL.value)
if loaded:
result = obj.text
return result
def get_expected_error_message_text(self):#, language):
# NOTE: It could introduce language checkers.
return "Login failed. Please re-enter your password and try again."
# NOT IMPLEMENTED STUFF:
def get_expected_placeholder_text(self, pswPlaceholderTextType):#, language):
# NOTE: It could introduce language checkers.
raise NotImplementedError("TODO: get_expected_placeholder_text method")
def open_generate_new_keys_popup(self):
raise NotImplementedError("TODO: open_generate_new_keys_popup method")
def get_current_account_name(self):
raise NotImplementedError("TODO: get_current_account_name method")
def get_current_identicon(self):
raise NotImplementedError("TODO: get_current_identicon method")

View File

View File

View File

@ -0,0 +1,8 @@
<testconfig version="1.0">
<information>
<summary/>
<description/>
</information>
<testsettings/>
<passwords/>
</testconfig>

View File

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# This file contains hook functions to run as the .feature file is executed.
sys.path.append(os.path.join(os.path.dirname(__file__), "../../../src/"))
_statusDektopAppName = "nim_status_client"
_appClosureTimeout = 2 #[seconds]
@OnScenarioStart
def hook(context):
startApplication(_statusDektopAppName)
context.userData = {}
@OnScenarioEnd
def hook(context):
currentApplicationContext().detach()
snooze(_appClosureTimeout)

View File

@ -0,0 +1,13 @@
# encoding: UTF-8
from objectmaphelper import *
statusDesktop_mainWindow = {"name": "mainWindow", "type": "StatusWindow", "visible": True}
mainWindow_dropRectangle_Rectangle = {"container": statusDesktop_mainWindow, "id": "dropRectangle", "type": "Rectangle", "unnamed": 1, "visible": True}
loginView_passwordInput = {"container": statusDesktop_mainWindow, "echoMode": 2, "id": "inputValue", "passwordCharacter": "", "type": "StyledTextField", "unnamed": 1, "visible": True}
loginView_changeAccountBtn = {"container": statusDesktop_mainWindow, "id": "changeAccountBtn", "type": "Rectangle", "unnamed": 1, "visible": True}
loginView_submitBtn = {"container": statusDesktop_mainWindow, "type": "StatusRoundButton", "visible": True}
loginView_main = {"container": statusDesktop_mainWindow, "type": "LoginView", "visible": True}
loginView_errMsgLabel = {"container": statusDesktop_mainWindow, "id": "errMsg", "type": "StyledText", "visible": True}
statusDesktop_mainWindow_overlay = {"container": statusDesktop_mainWindow, "type": "Overlay", "unnamed": 1, "visible": True}
accountsView_accountListPanel = {"container": statusDesktop_mainWindow_overlay, "type": "AccountListPanel", "visible": True}

View File

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
#******************************************************************************
# Status.im
#*****************************************************************************/
#/**
# * \file steps.py
# *
# * \test Status Desktop - Login
# * \date February 2022
# * \brief This file contains snippets of script code to be executed as the .feature
# * file is processed.
# * The decorators Given/When/Then/Step can be used to associate a script snippet
# * with a pattern which is matched against the steps being executed.
# *****************************************************************************
from data.StatusAccount import StatusAccount
from processes.StatusLoginProcess import StatusLoginProcess
@Given("A Status Desktop |any| and |word| with |word| as a preference language")
def step(context,account,password,languageType):
# Create new data domain:
accountObj = StatusAccount(account, password)
# Create new process:
process = StatusLoginProcess(accountObj)
# Set needed context properties:
context.userData['process'] = process
context.userData['account'] = accountObj
context.userData['languageType'] = languageType
# Verify process can be executed:
test.verify(process.can_execute_process(), "Not possible to start login process. Check if expected Login Screen is available.")
@When("the user tries to login with valid credentials")
def step(context):
loginProcess = context.userData['process']
# Check valid process behavior:
loginProcess.execute_process(True)
@When("the user tries to login with invalid credentials")
def step(context):
loginProcess = context.userData['process']
# Check invalid process behavior:
loginProcess.execute_process(False)
@Then("the user is able to login to Status Desktop application")
def step(context):
get_process_result(context)
@Then("the user is NOT able to login to Status Desktop application")
def step(context):
get_process_result(context)
# Common:
def get_process_result(context):
loginProcess = context.userData['process']
result, description = loginProcess.get_process_result()
test.verify(result, description)

View File

@ -0,0 +1,9 @@
AUT=nim_status_client
ENVVARS=envvars
HOOK_SUB_PROCESSES=false
IMPLICITAUTSTART=0
LANGUAGE=Python
OBJECTMAPSTYLE=script
TEST_CASES=tst_statusLoginPassword
VERSION=3
WRAPPERS=Qt

View File

@ -0,0 +1,39 @@
#******************************************************************************
# Status.im
#*****************************************************************************/
#/**
# * \file test.feature
# *
# * \test Status Desktop - Login
# * \date February 2022
# **
# *****************************************************************************/
Feature: Status Desktop login
As a user I want to login into the Status Desktop application.
The following scenarios cover login by using a password.
Scenario Outline: User tries to login with a valid password
Given A Status Desktop <account> and <password> with <languageType> as a preference language
When the user tries to login with valid credentials
Then the user is able to login to Status Desktop application
Examples:
| account | password | languageType |
| Athletic Prime Springtail | Test_1234 | english |
| Nervous Pesky Serpent | Test_1234 | english |
| Granular Diligent Gorilla | Test_1234 | english |
Scenario Outline: User tries to login with an invalid password
Given A Status Desktop <account> and <password> with <languageType> as a preference language
When the user tries to login with invalid credentials
Then the user is NOT able to login to Status Desktop application
Examples:
| account | password | languageType |
| Athletic Prime Springtail | Invalid34 | english |
| Granular Diligent Gorilla | Testpwd | english |
| Nervous Pesky Serpent | WrongPSW | english |

View File

@ -0,0 +1,8 @@
source(findFile('scripts', 'python/bdd.py'))
setupHooks('../shared/scripts/bdd_hooks.py')
collectStepDefinitions('./steps', '../shared/steps')
def main():
testSettings.throwOnFailure = True
runFeatureFile('test.feature')