From 4da388b5bcd4e736f6c497855b340969242189fd Mon Sep 17 00:00:00 2001 From: Vladimir Druzhinin Date: Fri, 19 May 2023 21:10:38 +0200 Subject: [PATCH] test/start application with custom data folder #10293 --- test/ui-test/__init__.py | 0 test/ui-test/src/common/Common.py | 12 +++-- test/ui-test/src/configs/__init__.py | 3 +- test/ui-test/src/configs/path.py | 10 ++++ test/ui-test/src/configs/squish.py | 2 +- test/ui-test/src/drivers/SquishDriver.py | 30 ++++++------ .../src/drivers/SquishDriverVerification.py | 3 ++ test/ui-test/src/drivers/aut.py | 28 +++++++++++ test/ui-test/src/drivers/context.py | 12 +++++ test/ui-test/src/utils/system_path.py | 24 ++++++++++ .../global_shared/scripts/bdd_hooks.py | 11 +++-- .../global_shared/steps/commonInitSteps.py | 13 +++--- .../testSuites/global_shared/steps/steps.py | 46 +++++++++++++------ 13 files changed, 147 insertions(+), 47 deletions(-) create mode 100644 test/ui-test/__init__.py create mode 100644 test/ui-test/src/configs/path.py create mode 100644 test/ui-test/src/drivers/aut.py create mode 100644 test/ui-test/src/drivers/context.py create mode 100644 test/ui-test/src/utils/system_path.py diff --git a/test/ui-test/__init__.py b/test/ui-test/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/ui-test/src/common/Common.py b/test/ui-test/src/common/Common.py index 0b32abf7b6..bd37b360b7 100644 --- a/test/ui-test/src/common/Common.py +++ b/test/ui-test/src/common/Common.py @@ -1,23 +1,29 @@ - +import configs import drivers.SquishDriver as driver import drivers.SquishDriverVerification as verification import drivers.SDKeyboardCommands as keyCommands -def start_application(app_name: str): - driver.start_application(app_name) + +def start_application(app_data_dir=configs.path.STATUS_APP_DATA, clear_user_data: bool = True): + driver.start_application(app_data_dir=app_data_dir, clear_user_data=clear_user_data) + def click_on_an_object(objName: str): driver.click_obj_by_name(objName) + def input_text(text: str, objName: str): driver.type_text(objName, text) + def object_not_enabled(objName: str): verification.verify_object_enabled(objName, 500, False) + def str_to_bool(string: str): return string.lower() in ["yes", "true", "1", "y", "enabled"] + def clear_input_text(objName: str): keyCommands.press_select_all(objName) keyCommands.press_backspace(objName) diff --git a/test/ui-test/src/configs/__init__.py b/test/ui-test/src/configs/__init__.py index a47f6f22ad..0b209a3cca 100644 --- a/test/ui-test/src/configs/__init__.py +++ b/test/ui-test/src/configs/__init__.py @@ -1 +1,2 @@ -from . import squish +from . import path +from . import squish \ No newline at end of file diff --git a/test/ui-test/src/configs/path.py b/test/ui-test/src/configs/path.py new file mode 100644 index 0000000000..853288799f --- /dev/null +++ b/test/ui-test/src/configs/path.py @@ -0,0 +1,10 @@ +import os + +from utils.system_path import SystemPath + +ROOT: SystemPath = SystemPath(__file__).resolve().parent.parent.parent +TMP: SystemPath = ROOT / 'tmp' + +AUT: SystemPath = SystemPath(os.getenv('AUT_PATH', ROOT.parent.parent / 'bin' / 'nim_status_client')) +STATUS_APP_DATA = TMP / 'Status' +STATUS_USER_DATA: SystemPath = STATUS_APP_DATA / 'data' diff --git a/test/ui-test/src/configs/squish.py b/test/ui-test/src/configs/squish.py index a6218900da..8b8d75c09d 100644 --- a/test/ui-test/src/configs/squish.py +++ b/test/ui-test/src/configs/squish.py @@ -1,3 +1,3 @@ - UI_LOAD_TIMEOUT_MSEC = 5000 APP_LOAD_TIMEOUT_MSEC = 60000 +PROCESS_LOAD_TIMEOUT_MSEC = 10000 diff --git a/test/ui-test/src/drivers/SquishDriver.py b/test/ui-test/src/drivers/SquishDriver.py index f1ff0e0098..f3eec316c8 100755 --- a/test/ui-test/src/drivers/SquishDriver.py +++ b/test/ui-test/src/drivers/SquishDriver.py @@ -10,18 +10,20 @@ import copy import sys import test -import time import configs import names import object import objectMap +import toplevelwindow +import utils.FileManager as filesMngr # 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 toplevelwindow from objectmaphelper import Wildcard +from utils.system_path import SystemPath +from .aut import * # noqa +from .context import * # noqa from .elements import * # noqa # The default maximum timeout to find ui object @@ -36,18 +38,16 @@ _MAX_WAIT_APP_TIMEOUT = 15000 _SEARCH_IMAGES_PATH = "../shared/searchImages/" -def start_application(app_name: str, attempt=2): - try: - ctx = squish.startApplication(app_name) - assert squish.waitFor(lambda: ctx.isRunning, _MAX_WAIT_APP_TIMEOUT), 'Start application error' - toplevelwindow.ToplevelWindow(squish.waitForObject(names.statusDesktop_mainWindow)).maximize() - except (AssertionError, RuntimeError): - if attempt: - [ctx.detach() for ctx in squish.applicationContextList()] - time.sleep(1) - start_application(app_name, attempt - 1) - else: - raise +def start_application( + fp: SystemPath = configs.path.AUT, + app_data_dir: SystemPath = configs.path.STATUS_APP_DATA, + clear_user_data: bool = True +): + if clear_user_data: + filesMngr.clear_directory(str(app_data_dir / 'data')) + app_data_dir.mkdir(parents=True, exist_ok=True) + ExecutableAut(fp).start(f'--datadir={app_data_dir}') + toplevelwindow.ToplevelWindow(squish.waitForObject(names.statusDesktop_mainWindow)).maximize() # Waits for the given object is loaded, visible and enabled. diff --git a/test/ui-test/src/drivers/SquishDriverVerification.py b/test/ui-test/src/drivers/SquishDriverVerification.py index 99be567b8e..6509f435ac 100644 --- a/test/ui-test/src/drivers/SquishDriverVerification.py +++ b/test/ui-test/src/drivers/SquishDriverVerification.py @@ -17,14 +17,17 @@ _MAX_WAIT_CLOSE_APP_TIMEOUT = 20 import traceback def verify_screen(objName: str, timeout: int=1000): + from drivers.SquishDriver import is_loaded_visible_and_enabled result = is_loaded_visible_and_enabled(objName, timeout) test.verify(result, f'Verifying screen for real-name {objName}') def verify_object_enabled(objName: str, timeout: int=_MIN_WAIT_OBJ_TIMEOUT, condition: bool=True): + from drivers.SquishDriver import is_loaded_visible_and_enabled result = is_loaded_visible_and_enabled(objName, timeout) test.verify(result[0] == condition, "Checking if object enabled") def verify_text_matching(objName: str, text: str): + from drivers.SquishDriver import is_text_matching test.verify(is_text_matching(objName, text), "Checking if text matches") def verify_text_matching_insensitive(obj, text: str): diff --git a/test/ui-test/src/drivers/aut.py b/test/ui-test/src/drivers/aut.py new file mode 100644 index 0000000000..f8e13747f5 --- /dev/null +++ b/test/ui-test/src/drivers/aut.py @@ -0,0 +1,28 @@ +import configs +from utils.system_path import SystemPath + +import squish +from . import context + + +class ExecutableAut: + + def __init__(self, fp: SystemPath): + self.fp = fp + self.ctx = None + + def start(self, *args, attempt: int = 2): + args = ' '.join([self.fp.name] + [str(arg) for arg in args]) + try: + self.ctx = squish.startApplication(args) + assert squish.waitFor(lambda: self.ctx.isRunning, configs.squish.APP_LOAD_TIMEOUT_MSEC) + except (AssertionError, RuntimeError): + if attempt: + self.detach() + self.start(*args, attempt - 1) + else: + raise + + @staticmethod + def detach(): + context.detach() diff --git a/test/ui-test/src/drivers/context.py b/test/ui-test/src/drivers/context.py new file mode 100644 index 0000000000..908f83eac0 --- /dev/null +++ b/test/ui-test/src/drivers/context.py @@ -0,0 +1,12 @@ +import configs +import squish +import time + + +def detach(): + for ctx in squish.applicationContextList(): + ctx.detach() + assert squish.waitFor( + lambda: not ctx.isRunning, configs.squish.PROCESS_LOAD_TIMEOUT_MSEC), 'Detach application failed' + # TODO: close by ctx.pid and then detach + time.sleep(5) diff --git a/test/ui-test/src/utils/system_path.py b/test/ui-test/src/utils/system_path.py new file mode 100644 index 0000000000..2692fbaf40 --- /dev/null +++ b/test/ui-test/src/utils/system_path.py @@ -0,0 +1,24 @@ +import os +import pathlib + + +class SystemPath(pathlib.Path): + _accessor = pathlib._normal_accessor # noqa + _flavour = pathlib._windows_flavour if os.name == 'nt' else pathlib._posix_flavour # noqa + + def rmtree(self, ignore_errors=False): + children = list(self.iterdir()) + for child in children: + if child.is_dir(): + child.rmtree(ignore_errors=ignore_errors) + else: + try: + child.unlink() + except OSError as e: + if not ignore_errors: + raise + try: + self.rmdir() + except OSError: + if not ignore_errors: + raise diff --git a/test/ui-test/testSuites/global_shared/scripts/bdd_hooks.py b/test/ui-test/testSuites/global_shared/scripts/bdd_hooks.py index 8ab32f2b4f..2c09177d3e 100644 --- a/test/ui-test/testSuites/global_shared/scripts/bdd_hooks.py +++ b/test/ui-test/testSuites/global_shared/scripts/bdd_hooks.py @@ -4,20 +4,21 @@ sys.path.append(os.path.join(os.path.dirname(__file__), "../../../testSuites/global_shared/")) sys.path.append(os.path.join(os.path.dirname(__file__), "../../../src/")) +import drivers.SquishDriver as driver from steps.commonInitSteps import context_init - + @OnScenarioStart def hook(context): context_init(context, testSettings) context.userData["scenario_name"] = context._data["title"] + @OnScenarioEnd def hook(context): - ctx = currentApplicationContext() - ctx.detach() - assert waitFor(lambda: not ctx.isRunning, _app_closure_timeout), 'Detach application failed' + driver.detach() + @OnStepEnd def hook(context): - context.userData["step_name"] = context._data["text"] \ No newline at end of file + context.userData["step_name"] = context._data["text"] diff --git a/test/ui-test/testSuites/global_shared/steps/commonInitSteps.py b/test/ui-test/testSuites/global_shared/steps/commonInitSteps.py index 8e8d0a64d3..068a02bdc7 100644 --- a/test/ui-test/testSuites/global_shared/steps/commonInitSteps.py +++ b/test/ui-test/testSuites/global_shared/steps/commonInitSteps.py @@ -4,6 +4,7 @@ import os import utils.FileManager as filesMngr import common.Common as common +import configs from screens.StatusWelcomeScreen import StatusWelcomeScreen from screens.StatusMainScreen import StatusMainScreen @@ -58,13 +59,12 @@ def context_init(context, testSettings, screenshot_on_fail = True): context.userData[_fixtures_root] = os.path.join(joined_path, "fixtures/") def a_first_time_user_lands_on(context): - filesMngr.erase_directory(context.userData[_status_data_folder]) - common.start_application(context.userData[_aut_name]) + common.start_application() def a_user_starts_the_application_with_a_specific_data_folder(context, data_folder_path): - filesMngr.clear_directory(context.userData["status_data_folder_path"]) - filesMngr.copy_directory(data_folder_path, context.userData["status_data_folder_path"]) - common.start_application(context.userData[_aut_name]) + filesMngr.clear_directory(configs.path.STATUS_USER_DATA) + filesMngr.copy_directory(data_folder_path, str(configs.path.STATUS_USER_DATA)) + common.start_application(clear_user_data=False) def a_first_time_user_lands_on_and_generates_new_key(context): a_first_time_user_lands_on(context) @@ -76,8 +76,7 @@ def a_user_lands_on_and_generates_new_key(context): welcome_screen.generate_new_key() def a_first_time_user_lands_on_and_navigates_to_import_seed_phrase(context): - filesMngr.erase_directory(context.userData[_status_data_folder]) - filesMngr.start_application(context.userData[_aut_name]) + common.start_application() welcome_screen = StatusWelcomeScreen() welcome_screen.agree_terms_conditions_and_navigate_to_import_seed_phrase() diff --git a/test/ui-test/testSuites/global_shared/steps/steps.py b/test/ui-test/testSuites/global_shared/steps/steps.py index ec0bb0b10a..91d05960c9 100644 --- a/test/ui-test/testSuites/global_shared/steps/steps.py +++ b/test/ui-test/testSuites/global_shared/steps/steps.py @@ -17,13 +17,14 @@ import time # ***************************************************************************** import common.Common as common import steps.commonInitSteps as init_steps -from drivers.SquishDriver import start_application +import drivers.SquishDriver as driver from screens.StatusChatScreen import StatusChatScreen from screens.StatusMainScreen import StatusMainScreen _statusMain = StatusMainScreen() _statusChat = StatusChatScreen() + ######################### ### PRECONDITIONS region: ######################### @@ -32,30 +33,37 @@ _statusChat = StatusChatScreen() def step(context, data_folder_path): init_steps.a_user_starts_the_application_with_a_specific_data_folder(context, data_folder_path) + @Given("the user restarts the app") def step(context): the_user_restarts_the_app(context) - + + @Given("the user joins chat room \"|any|\"") def step(context, room): the_user_joins_chat_room(room) + @Given("the user clicks on escape key") def step(context): - _statusMain.click_escape() - + _statusMain.click_escape() + + @Given("the user clears input \"|any|\"") def step(context, input_component): - common.clear_input_text(input_component) - + common.clear_input_text(input_component) + + @Given("the user inputs the following \"|any|\" with ui-component \"|any|\"") def step(context, text, obj): - the_user_inputs_the_following_text_with_uicomponent(text, obj) - + the_user_inputs_the_following_text_with_uicomponent(text, obj) + + @Given("the user clicks on the following ui-component \"|any|\"") def step(context: any, obj: str): the_user_clicks_on_the_following_ui_component(obj) + ######################### ### ACTIONS region: ######################### @@ -63,24 +71,29 @@ def step(context: any, obj: str): @When("the user restarts the app") def step(context): the_user_restarts_the_app(context) - + + @When("the user inputs the following \"|any|\" with ui-component \"|any|\"") def step(context, text, obj): the_user_inputs_the_following_text_with_uicomponent(text, obj) + @When("the user clicks on the following ui-component \"|any|\"") def step(context: any, obj: str): init_steps.the_user_clicks_on_the_following_ui_component(obj) + @When("the user joins chat room \"|any|\"") def step(context, room): the_user_joins_chat_room(room) + # TODO remove when we have a reliable local mailserver @When("the user waits |any| seconds") def step(context, amount): time.sleep(2) + ######################### ### VERIFICATIONS region: ######################### @@ -88,21 +101,24 @@ def step(context, amount): @Then("the following ui-component \"|any|\" is not enabled") def step(context, obj): common.object_not_enabled(obj) - + + ########################################################################### ### COMMON methods used in different steps given/when/then region: ########################################################################### def the_user_restarts_the_app(context: any): - [ctx.detach() for ctx in squish.applicationContextList()] - start_application(context.userData["aut_name"]) - + driver.detach() + driver.start_application(clear_user_data=False) + + def the_user_joins_chat_room(room: str): init_steps.the_user_joins_chat_room(room) - + + def the_user_inputs_the_following_text_with_uicomponent(text: str, obj): common.input_text(text, obj) + def the_user_clicks_on_the_following_ui_component(obj): init_steps.the_user_clicks_on_the_following_ui_component(obj) -