diff --git a/test/gui/environment.py b/test/gui/environment.py index 26764d37ac..3f9b9bc64a 100644 --- a/test/gui/environment.py +++ b/test/gui/environment.py @@ -1,4 +1,3 @@ -import psutil import shutil import os @@ -7,7 +6,7 @@ from helpers.SpaceHelper import delete_project_spaces from helpers.ConfigHelper import set_config, get_config from helpers.FilesHelper import prefix_path_namespace, cleanup_created_paths -from helpers.SetupClientHelper import app +from helpers.SetupClientHelper import close_and_kill_app from step_types.types import * # register all step types @@ -37,10 +36,4 @@ def after_scenario(context, scenario): delete_project_spaces() delete_created_users() # quit the application - if app() is not None: - app().quit() - for process in psutil.process_iter(['pid', 'exe']): - if process.info['exe'] == get_config("app_path"): - print("Closing desktop client...") - psutil.Process(process.info['pid']).kill() - break + close_and_kill_app() diff --git a/test/gui/tst_loginLogout/test.feature b/test/gui/features/login-logout/login-logout.feature similarity index 96% rename from test/gui/tst_loginLogout/test.feature rename to test/gui/features/login-logout/login-logout.feature index aedd31706f..1b08bd854f 100644 --- a/test/gui/tst_loginLogout/test.feature +++ b/test/gui/features/login-logout/login-logout.feature @@ -6,13 +6,13 @@ Feature: Logout users Background: Given user "Alice" has been created in the server with default attributes - @smoke @skip + @smoke Scenario: logging out Given user "Alice" has set up a client with default settings When the user "Alice" logs out using the client-UI Then user "Alice" should be signed out - @smoke @skip + @smoke Scenario: login after logging out Given user "Alice" has set up a client with default settings And user "Alice" has logged out from the client-UI diff --git a/test/gui/helpers/SetupClientHelper.py b/test/gui/helpers/SetupClientHelper.py index d8e935969d..45392fb8b8 100644 --- a/test/gui/helpers/SetupClientHelper.py +++ b/test/gui/helpers/SetupClientHelper.py @@ -270,3 +270,25 @@ def run_sys_command(command=None, shell=False): check=False, ) return cmd.stdout, cmd.stderr, cmd.returncode + + +def close_and_kill_app(): + """ + Close Appium session and kill the desktop client process. + Use this for both mid-scenario and end-of-scenario cleanup. + """ + global app_driver + # Quit Appium session + if app_driver is not None: + app_driver.quit() + + # Kill remaining process by exe path + app_path = get_config("app_path") + for process in psutil.process_iter(['pid', 'exe']): + if process.info['exe'] == app_path: + print("Closing desktop client...") + psutil.Process(process.info['pid']).kill() + break + + # Reset driver for reuse + app_driver = None diff --git a/test/gui/helpers/SyncHelper.py b/test/gui/helpers/SyncHelper.py index 3504bdee46..d1c1460561 100644 --- a/test/gui/helpers/SyncHelper.py +++ b/test/gui/helpers/SyncHelper.py @@ -2,6 +2,8 @@ import re import time import urllib.request +from selenium.webdriver.support.ui import WebDriverWait +from selenium.common.exceptions import TimeoutException from helpers.ConfigHelper import get_config, is_linux, is_windows from helpers.FilesHelper import sanitize_path @@ -357,10 +359,10 @@ def make_available_locally(resource_path): def wait_for(condition, timeout, interval=0.5): - start = time.time() * 1000 - while True: - if condition(): - return True - if time.time() * 1000 - start > timeout: - return False - time.sleep(interval) + from helpers.SetupClientHelper import app + wait = WebDriverWait(app(), timeout / 1000, poll_frequency=interval) + try: + wait.until(lambda _: condition()) + return True + except TimeoutException: + return False diff --git a/test/gui/pageObjects/AccountSetting.py b/test/gui/pageObjects/AccountSetting.py index 2418eb406a..3d8acc919a 100644 --- a/test/gui/pageObjects/AccountSetting.py +++ b/test/gui/pageObjects/AccountSetting.py @@ -1,5 +1,7 @@ from types import SimpleNamespace from appium.webdriver.common.appiumby import AppiumBy as By +from helpers.UserHelper import get_displayname_for_user +from helpers.SetupClientHelper import substitute_inline_codes, app from pageObjects.Toolbar import Toolbar from helpers.UserHelper import get_displayname_for_user @@ -16,7 +18,10 @@ class AccountSetting: CONFIRM_REMOVE_CONNECTION_BUTTON = SimpleNamespace( by=By.NAME, selector="Remove connection" ) - ACCOUNT_CONNECTION_LABEL = SimpleNamespace(by=None, selector=None) + ACCOUNT_CONNECTION_LABEL = SimpleNamespace( + by=By.XPATH, + selector="//list[@name='Folder Sync']//label", + ) LOG_BROWSER_WINDOW = SimpleNamespace(by=None, selector=None) ACCOUNT_LOADING = SimpleNamespace(by=None, selector=None) DIALOG_STACK = SimpleNamespace(by=None, selector=None) @@ -61,9 +66,8 @@ def login(): @staticmethod def get_account_connection_label(): - return str( - squish.waitForObjectExists(AccountSetting.ACCOUNT_CONNECTION_LABEL).text - ) + label = app().find_element(AccountSetting.ACCOUNT_CONNECTION_LABEL.by, AccountSetting.ACCOUNT_CONNECTION_LABEL.selector).text + return label @staticmethod def is_connecting(): @@ -93,7 +97,7 @@ def wait_until_connection_is_configured(timeout=5000): @staticmethod def wait_until_account_is_connected(timeout=5000): - result = squish.waitFor( + result = wait_for( AccountSetting.is_user_signed_in, timeout, ) @@ -106,6 +110,7 @@ def wait_until_account_is_connected(timeout=5000): ) return result + @staticmethod def wait_until_sync_folder_is_configured(timeout=5000): result = squish.waitFor( diff --git a/test/gui/pageObjects/Toolbar.py b/test/gui/pageObjects/Toolbar.py index d36410d8bd..6a88acb065 100644 --- a/test/gui/pageObjects/Toolbar.py +++ b/test/gui/pageObjects/Toolbar.py @@ -3,9 +3,8 @@ from appium.webdriver.common.appiumby import AppiumBy as By from selenium.webdriver.common.keys import Keys -from helpers.SetupClientHelper import wait_until_app_killed +from helpers.SetupClientHelper import app, close_and_kill_app from helpers.ConfigHelper import get_config -from helpers.SetupClientHelper import app from helpers.UserHelper import get_displayname_for_user @@ -15,8 +14,14 @@ class Toolbar: ADD_ACCOUNT_BUTTON = SimpleNamespace(by=By.NAME, selector="Add Account") ACTIVITY_BUTTON = SimpleNamespace(by=By.NAME, selector="Activity") SETTINGS_BUTTON = SimpleNamespace(by=None, selector=None) - QUIT_BUTTON = SimpleNamespace(by=None, selector=None) - CONFIRM_QUIT_BUTTON = SimpleNamespace(by=None, selector=None) + QUIT_BUTTON = SimpleNamespace( + by=By.NAME, + selector="Quit" + ) + CONFIRM_QUIT_BUTTON = SimpleNamespace( + by=By.ACCESSIBILITY_ID, + selector="QApplication.QMessageBox.qt_msgbox_buttonbox.QPushButton" + ) TOOLBAR_ITEMS = ["Add Account", "Activity", "Settings", "Quit"] @@ -75,12 +80,16 @@ def open_settings_tab(): @staticmethod def quit_opencloud(): - squish.mouseClick(squish.waitForObject(Toolbar.QUIT_BUTTON)) - squish.clickButton(squish.waitForObject(Toolbar.CONFIRM_QUIT_BUTTON)) - for ctx in squish.applicationContextList(): - pid = ctx.pid - ctx.detach() - wait_until_app_killed(pid) + app().find_element( + Toolbar.QUIT_BUTTON.by, + Toolbar.QUIT_BUTTON.selector + ).click() + app().find_element( + Toolbar.CONFIRM_QUIT_BUTTON.by, + Toolbar.CONFIRM_QUIT_BUTTON.selector + ).click() + close_and_kill_app() + @staticmethod def get_accounts(): diff --git a/test/gui/steps/account_context.py b/test/gui/steps/account_context.py index f3bd9b90b0..bd92e94bdb 100644 --- a/test/gui/steps/account_context.py +++ b/test/gui/steps/account_context.py @@ -1,13 +1,14 @@ import shutil import os from behave import given as Given, when as When, then as Then -from sure import expect +from sure import expect, ensure from pageObjects.AccountConnectionWizard import AccountConnectionWizard from pageObjects.SyncConnectionWizard import SyncConnectionWizard from pageObjects.AccountSetting import AccountSetting from pageObjects.Toolbar import Toolbar from pageObjects.EnterPassword import EnterPassword +from pageObjects.AccountSetting import AccountSetting from helpers.SetupClientHelper import ( start_client, setup_client, @@ -15,6 +16,7 @@ get_client_details, generate_account_config, get_resource_path, + app, ) from helpers.SyncHelper import ( wait_for_initial_sync_to_complete, @@ -113,28 +115,27 @@ def step(context): AccountConnectionWizard.add_account_information(account_details) -@When('the user "|any|" logs out using the client-UI') -def step(context, _): +@When('the user "{username}" logs out using the client-UI') +def step(context, username): AccountSetting.logout() -@Then('user "|any|" should be signed out') +@Then('user "{username}" should be signed out') def step(context, username): - test.compare( - AccountSetting.is_user_signed_out(), - True, - f'User "{username}" is signed out', - ) + user_signed_out = AccountSetting.is_user_signed_out() + + with ensure('User "{0}" should be signed out, but is still signed in', username): + user_signed_out.should.be.true -@Given('user "|any|" has logged out from the client-UI') +@Given('user "{username}" has logged out from the client-UI') def step(context, username): AccountSetting.logout() if not AccountSetting.is_user_signed_out(): raise LookupError(f'Failed to logout user {username}') -@When('user "|any|" logs in using the client-UI') +@When('user "{username}" logs in using the client-UI') def step(context, username): AccountSetting.login() password = get_password_for_user(username) @@ -150,10 +151,9 @@ def step(context, _): AccountSetting.login() -@Then('user "|any|" should be connected to the server') -def step(context, _): +@Then('user "{username}" should be connected to the server') +def step(context, username): AccountSetting.wait_until_account_is_connected() - AccountSetting.wait_until_sync_folder_is_configured() @When('the user removes the connection for user "{username}"') @@ -233,7 +233,7 @@ def step(context): test.compare(True, AccountSetting.is_log_dialog_visible(), 'Log dialog is opened') -@Step('the user cancels the sync connection wizard') +@When('the user cancels the sync connection wizard') def step(context): SyncConnectionWizard.cancel_folder_sync_connection_wizard() diff --git a/test/gui/tst_loginLogout/test.py b/test/gui/tst_loginLogout/test.py deleted file mode 100644 index 83b0a5275a..0000000000 --- a/test/gui/tst_loginLogout/test.py +++ /dev/null @@ -1,8 +0,0 @@ -source(findFile('scripts', 'python/bdd.py')) - -setupHooks('../shared/scripts/bdd_hooks.py') -collectStepDefinitions('./steps', '../shared/steps') - - -def main(): - runFeatureFile('test.feature')