From b8774b3456e816061f3bd287fbf15b3808537365 Mon Sep 17 00:00:00 2001 From: Saw-jan Date: Thu, 18 Jun 2026 10:48:21 +0545 Subject: [PATCH 1/8] test: wait for sync progress to complete Signed-off-by: Saw-jan --- test/gui/helpers/ConfigHelper.py | 1 + test/gui/helpers/SyncHelper.py | 46 ++++++++++++++++---------- test/gui/pageObjects/SyncConnection.py | 34 ++++++++++++++----- 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/test/gui/helpers/ConfigHelper.py b/test/gui/helpers/ConfigHelper.py index ad5d95a81..910f4c0da 100644 --- a/test/gui/helpers/ConfigHelper.py +++ b/test/gui/helpers/ConfigHelper.py @@ -82,6 +82,7 @@ def get_app_env(): 'max_timeout': 60, 'min_timeout': 5, 'lowest_timeout': 1, + 'min_sync_timeout': 10, 'files_for_upload': os.path.join(CURRENT_DIR.parent, 'files-for-upload'), } diff --git a/test/gui/helpers/SyncHelper.py b/test/gui/helpers/SyncHelper.py index 750aa14ff..69fc8769f 100644 --- a/test/gui/helpers/SyncHelper.py +++ b/test/gui/helpers/SyncHelper.py @@ -252,33 +252,43 @@ def wait_for_resource_to_sync( if patterns is None: patterns = get_synced_pattern(resource) + synced = False if force_sync: - initial_timeout = get_config('min_timeout') + initial_timeout = get_config('min_sync_timeout') # first try with 5 seconds timeout synced = wait_for( lambda: has_sync_pattern(patterns, resource), initial_timeout, ) if not synced: - # trigger force sync if the current status is OK - status = get_current_sync_status(resource, resource_type) - if status.startswith(SYNC_STATUS['OK']): - print('[WARN] Retrying sync pattern check with force sync') - SyncConnection.force_sync() - else: - clear_socket_messages(resource) - return - - synced = wait_for( - lambda: has_sync_pattern(patterns, resource), - timeout - initial_timeout, - ) + # do not trigger force sync if the sync is still in progress + if SyncConnection.is_sync_in_progress(get_config('syncConnectionName')): + print('[INFO] Sync is in progress. Waiting...') + else: + # trigger force sync if the current status is OK + status = get_current_sync_status(resource, resource_type) + if status.startswith(SYNC_STATUS['OK']): + print('[INFO] Retrying sync pattern check with force sync') + SyncConnection.force_sync() + + if not synced: + synced = wait_for( + lambda: has_sync_pattern(patterns, resource), + timeout - initial_timeout, + ) - messages = read_and_update_socket_messages() - messages = filter_messages_for_item(messages, resource) - clear_socket_messages(resource) if synced: - return + loaded = wait_for( + lambda: not SyncConnection.is_sync_in_progress( + get_config('syncConnectionName') + ), + get_config('sync_timeout'), + ) + if loaded: + clear_socket_messages(resource) + return + else: + print('[ERROR] Sync is still in progress after matching the sync pattern.') elif not force_sync: # if the sync pattern doesn't match then check the last sync status # and pass the step if the last sync status is STATUS:OK diff --git a/test/gui/pageObjects/SyncConnection.py b/test/gui/pageObjects/SyncConnection.py index a5990378c..e960d1c00 100644 --- a/test/gui/pageObjects/SyncConnection.py +++ b/test/gui/pageObjects/SyncConnection.py @@ -22,8 +22,7 @@ class SyncConnection: by=By.NAME, selector="Remove Space" ) PERMISSION_ERROR_LABEL = SimpleNamespace( - by=By.XPATH, - selector="//label[contains(@name, 'permission')]" + by=By.XPATH, selector="//label[contains(@name, 'permission')]" ) @staticmethod @@ -100,6 +99,21 @@ def has_sync_connection(sync_folder): except NoSuchElementException: return False + @staticmethod + def is_sync_in_progress(sync_folder): + connection = SyncConnection.get_current_account_connection() + try: + connection.find_element( + By.NAME, + "{sync_folder},Queued,Local folder: {sync_path}{sync_folder}".format( + sync_folder=sync_folder, + sync_path=get_config('currentUserSyncPath'), + ), + ) + return True + except NoSuchElementException: + return False + @staticmethod def remove_folder_sync_connection(): SyncConnection.perform_action("Remove Space") @@ -122,10 +136,15 @@ def wait_for_error_label(to_exist=True): """Wait for permission error label to appear or disappear""" status = wait_for( - lambda: (bool(app().find_elements( - SyncConnection.PERMISSION_ERROR_LABEL.by, - SyncConnection.PERMISSION_ERROR_LABEL.selector - ))) == to_exist, + lambda: ( + bool( + app().find_elements( + SyncConnection.PERMISSION_ERROR_LABEL.by, + SyncConnection.PERMISSION_ERROR_LABEL.selector, + ) + ) + ) + == to_exist, get_config("max_timeout"), ) if not status: @@ -138,7 +157,6 @@ def get_permission_error_message(): SyncConnection.wait_for_error_label(True) # Wait for label to appear element = app().find_element( SyncConnection.PERMISSION_ERROR_LABEL.by, - SyncConnection.PERMISSION_ERROR_LABEL.selector + SyncConnection.PERMISSION_ERROR_LABEL.selector, ) return str(element.text) - From 88d519c5c290a1e82545a38bfc42a786b0282659 Mon Sep 17 00:00:00 2001 From: Saw-jan Date: Thu, 18 Jun 2026 11:57:56 +0545 Subject: [PATCH 2/8] test: check async activity status using resource Signed-off-by: Saw-jan --- test/gui/features/spaces/spaces.feature | 3 ++- test/gui/pageObjects/Activity.py | 15 ++++++++++----- test/gui/steps/sync_context.py | 5 ++++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/test/gui/features/spaces/spaces.feature b/test/gui/features/spaces/spaces.feature index 4f2bb3267..6aaeaf2b6 100644 --- a/test/gui/features/spaces/spaces.feature +++ b/test/gui/features/spaces/spaces.feature @@ -79,7 +79,8 @@ Feature: Project spaces """ When the user opens the activity tab And the user selects "Not Synced" tab in the activity - Then the following activities should be displayed in not synced table + Then the folder "simple-folder" should be blacklisted + And the following activities should be displayed in not synced table | resource | status | account | | simple-folder | Blacklisted | Alice Hansen@%local_server_hostname% | diff --git a/test/gui/pageObjects/Activity.py b/test/gui/pageObjects/Activity.py index 442e49348..a0bcfb819 100644 --- a/test/gui/pageObjects/Activity.py +++ b/test/gui/pageObjects/Activity.py @@ -13,8 +13,7 @@ class Activity: TAB_CONTAINER = SimpleNamespace(by=None, selector=None) SUBTAB_CONTAINER = SimpleNamespace( - by=By.XPATH, - selector="//page_tab[starts-with(@name, '{tab_name}')]" + by=By.XPATH, selector="//page_tab[starts-with(@name, '{tab_name}')]" ) NOT_SYNCED_TABLE = SimpleNamespace(by=None, selector=None) LOCAL_ACTIVITY_FILTER_BUTTON = SimpleNamespace(by=By.NAME, selector="Filter") @@ -32,7 +31,6 @@ class Activity: NOT_SYNCED_ACTIVITY_TABLE_HEADER_SELECTOR = SimpleNamespace(by=None, selector=None) SYNCED_ACTIVITY_STATUS = SimpleNamespace(by=By.NAME, selector=None) - @staticmethod def get_not_synced_file_selector(resource): return { @@ -93,8 +91,15 @@ def is_resource_excluded(filename): @staticmethod def has_sync_status(filename, status): try: - app().find_element(Activity.SYNCED_ACTIVITY_STATUS.by, status) - return True + row = app().find_element(By.NAME, filename) + row_y = row.rect['y'] + status_cells = app().find_elements( + Activity.SYNCED_ACTIVITY_STATUS.by, status + ) + for status_el in status_cells: + if status_el.rect['y'] == row_y: + return True + return False except NoSuchElementException: return False diff --git a/test/gui/steps/sync_context.py b/test/gui/steps/sync_context.py index 421ad481b..4acbd9cc0 100644 --- a/test/gui/steps/sync_context.py +++ b/test/gui/steps/sync_context.py @@ -114,6 +114,7 @@ def step(context, filename): @Then('the file "{filename}" should be blacklisted') +@Then('the folder "{filename}" should be blacklisted') def step(context, filename): with ensure('File is Blacklisted'): Activity.is_resource_blacklisted(filename).should.be.true @@ -341,7 +342,9 @@ def step(context): # wait for error message to disappear SyncConnection.wait_for_error_label(False) - with ensure(f'Expected error message: "{expected_error_message}" but got: "{actual_error_message}"'): + with ensure( + f'Expected error message: "{expected_error_message}" but got: "{actual_error_message}"' + ): expected_error_message.should.equal(actual_error_message) From 8ac7a0bffb83294197dcf71637656f59b5d84094 Mon Sep 17 00:00:00 2001 From: Saw-jan Date: Thu, 18 Jun 2026 12:58:57 +0545 Subject: [PATCH 3/8] test: verify the target item is visible Signed-off-by: Saw-jan --- .../sync-resources/syncResources.feature | 4 +- test/gui/pageObjects/SyncConnectionWizard.py | 60 +++++++++++-------- test/gui/steps/sync_context.py | 3 +- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/test/gui/features/sync-resources/syncResources.feature b/test/gui/features/sync-resources/syncResources.feature index 619b67507..10eb8fd36 100644 --- a/test/gui/features/sync-resources/syncResources.feature +++ b/test/gui/features/sync-resources/syncResources.feature @@ -94,7 +94,7 @@ Feature: Syncing files When the user selects manual sync folder option in advanced section And the user sets the sync path in sync connection wizard And the user selects "Personal" space in sync connection wizard - And the user selects the following folders to sync: + And the user selects only the following folders to sync: | folder | | simple-folder | Then the folder "simple-folder" should exist on the file system @@ -473,7 +473,7 @@ Feature: Syncing files When the user selects manual sync folder option in advanced section And the user sets the temp folder "~`!@#$^&()-_=+{[}];',)PRN%" as local sync path in sync connection wizard And the user selects "Personal" space in sync connection wizard - And the user selects the following folders to sync: + And the user selects only the following folders to sync: | folder | | ~`!@#$^&()-_=+{[}];',) | | simple-folder | diff --git a/test/gui/pageObjects/SyncConnectionWizard.py b/test/gui/pageObjects/SyncConnectionWizard.py index e58917a7b..cff4641d8 100644 --- a/test/gui/pageObjects/SyncConnectionWizard.py +++ b/test/gui/pageObjects/SyncConnectionWizard.py @@ -4,6 +4,7 @@ from helpers.SetupClientHelper import get_current_user_sync_path from helpers.AppHelper import app +from helpers.ConfigHelper import get_config class SyncConnectionWizard: @@ -12,7 +13,7 @@ class SyncConnectionWizard: ) BACK_BUTTON = SimpleNamespace(by=By.NAME, selector="< Back") NEXT_BUTTON = SimpleNamespace(by=By.NAME, selector="Next >") - SELECTIVE_SYNC_ROOT_FOLDER = SimpleNamespace(by=None, selector=None) + SELECTIVE_SYNC_ROOT_FOLDER = SimpleNamespace(by=By.NAME, selector=None) ADD_SYNC_CONNECTION_BUTTON = SimpleNamespace( by=By.XPATH, selector="//dialog[@name='Add Space']//*[@name='Add Space']" ) @@ -71,12 +72,12 @@ def select_remote_destination_folder(folder): @staticmethod def deselect_all_remote_folders(): - element = app().find_element( - SyncConnectionWizard.ADD_SYNC_CONNECTION_BUTTON.by, - SyncConnectionWizard.ADD_SYNC_CONNECTION_BUTTON.selector, + root = app().find_element( + SyncConnectionWizard.SELECTIVE_SYNC_ROOT_FOLDER.by, + get_config('syncConnectionName'), ) - element.send_keys(Keys.ARROW_DOWN) - element.native_send_keys(Keys.SPACE) # uncheck the root folder + root.native_click() + root.native_send_keys(Keys.SPACE) # uncheck the root folder @staticmethod def sort_by(header_text): @@ -197,20 +198,20 @@ def is_add_sync_folder_button_enabled(): ).enabled @staticmethod - def select_or_unselect_folders_to_sync(folders, select=True): + def toggle_folder_selection(folders, select=True): expected_state = "true" if select else "false" - if select: - SyncConnectionWizard.deselect_all_remote_folders() - for folder_path in folders: parents = folder_path.strip("/").split("/") target_folder = parents.pop() parent_element = None parent_position = 0 - for parent in parents: + target_element = None + for idx, parent in enumerate(parents): p_elements = app().find_elements(By.NAME, parent) + next_item = idx + 1 < len(parents) and parents[idx + 1] or target_folder + # select nested folders based on the position of the parent folder for p_element in p_elements: if ( @@ -220,34 +221,41 @@ def select_or_unselect_folders_to_sync(folders, select=True): parent_element = p_element parent_position = p_element.rect["x"] break + parent_element.native_double_click() # expand the folder + + next_targets = app().find_elements(By.NAME, next_item) + for n_target in next_targets: + if n_target.rect["x"] > parent_position: + target_element = n_target + break + # retry once if the folder is not expanded - if parent_element.is_selected(): + if not target_element or not target_element.is_displayed(): print('[WARN] Folder was not expanded, retrying with space key') # expand using space key parent_element.native_click() parent_element.native_send_keys(Keys.SPACE) - if parent_element.is_selected(): + # try to get the next target again + next_targets = app().find_elements(By.NAME, next_item) + for n_target in next_targets: + if n_target.rect["x"] > parent_position: + target_element = n_target + break + if not target_element or not target_element.is_displayed(): raise AssertionError(f'Failed to expand folder: {parent}') - folder_element = None - target_folders = app().find_elements(By.NAME, target_folder) - # select the folder that is inside the current parent position - for folder in target_folders: - if folder.rect["x"] > parent_position: - folder_element = folder - break - is_checked = folder_element.get_attribute("checked") + is_checked = target_element.get_attribute("checked") # return early if the folder is already in the expected state. if is_checked == expected_state: return - folder_element.native_click() - if not folder_element.is_selected(): + target_element.native_click() + if not target_element.is_selected(): raise AssertionError(f"Failed to focus folder: {target_folder}") - folder_element.native_send_keys(Keys.SPACE) # toggle the folder selection + target_element.native_send_keys(Keys.SPACE) # toggle the folder selection - is_checked = folder_element.get_attribute("checked") + is_checked = target_element.get_attribute("checked") if is_checked != expected_state: raise AssertionError( f"Failed to {'select' if select else 'unselect'} folder: {folder_path}" @@ -259,7 +267,7 @@ def confirm_choose_what_to_sync_selection(): @staticmethod def __handle_folder_selection(folders, should_select, new_sync_connection_wizard): - SyncConnectionWizard.select_or_unselect_folders_to_sync(folders, should_select) + SyncConnectionWizard.toggle_folder_selection(folders, should_select) if new_sync_connection_wizard: SyncConnectionWizard.add_sync_connection() diff --git a/test/gui/steps/sync_context.py b/test/gui/steps/sync_context.py index 4acbd9cc0..404866b66 100644 --- a/test/gui/steps/sync_context.py +++ b/test/gui/steps/sync_context.py @@ -145,11 +145,12 @@ def step(context): Toolbar.has_tab(tab_name).should.be.true -@When('the user selects the following folders to sync:') +@When('the user selects only the following folders to sync:') def step(context): folders = [] for row in context.table: folders.append(row[0]) + SyncConnectionWizard.deselect_all_remote_folders() SyncConnectionWizard.select_folders_to_sync( folders, new_sync_connection_wizard=True ) From 9309519eb687f9314a06c4b28051d7e6b2dad5e4 Mon Sep 17 00:00:00 2001 From: Saw-jan Date: Thu, 18 Jun 2026 13:18:08 +0545 Subject: [PATCH 4/8] test: fix ffmpeg warning Signed-off-by: Saw-jan --- test/gui/helpers/ScreenRecorder.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/gui/helpers/ScreenRecorder.py b/test/gui/helpers/ScreenRecorder.py index 88bdbdda4..a703cec20 100644 --- a/test/gui/helpers/ScreenRecorder.py +++ b/test/gui/helpers/ScreenRecorder.py @@ -22,7 +22,8 @@ def _record_loop(video_path): size=(width, height), fps=24, codec="libx264", - output_params=["-crf", "23", "-pix_fmt", "yuv420p"], + pix_fmt_out="yuv420p", + output_params=["-crf", "23"], ) writer.send(None) From 8ec53e393f2c10f8f1f9984f82d7e9e55222e556 Mon Sep 17 00:00:00 2001 From: Saw-jan Date: Thu, 18 Jun 2026 15:06:20 +0545 Subject: [PATCH 5/8] test: fix sync queue check for multiple accounts Signed-off-by: Saw-jan --- test/gui/helpers/SyncHelper.py | 35 +++++++++++--------- test/gui/pageObjects/SyncConnection.py | 5 ++- test/gui/pageObjects/SyncConnectionWizard.py | 28 ++++++++++------ test/gui/steps/account_context.py | 11 +++--- 4 files changed, 46 insertions(+), 33 deletions(-) diff --git a/test/gui/helpers/SyncHelper.py b/test/gui/helpers/SyncHelper.py index 69fc8769f..e82b2091d 100644 --- a/test/gui/helpers/SyncHelper.py +++ b/test/gui/helpers/SyncHelper.py @@ -242,7 +242,7 @@ def get_current_sync_status(resource, resource_type): def wait_for_resource_to_sync( - resource, resource_type='FOLDER', patterns=None, force_sync=False + resource, resource_type='FOLDER', patterns=None, force_sync=False, check_queued=True ): listen_sync_status_for_item(resource, resource_type) @@ -262,7 +262,9 @@ def wait_for_resource_to_sync( ) if not synced: # do not trigger force sync if the sync is still in progress - if SyncConnection.is_sync_in_progress(get_config('syncConnectionName')): + if check_queued and SyncConnection.is_sync_in_progress( + get_config('syncConnectionName') + ): print('[INFO] Sync is in progress. Waiting...') else: # trigger force sync if the current status is OK @@ -276,19 +278,21 @@ def wait_for_resource_to_sync( lambda: has_sync_pattern(patterns, resource), timeout - initial_timeout, ) - + # clear stored socket messages + clear_socket_messages(resource) if synced: - loaded = wait_for( - lambda: not SyncConnection.is_sync_in_progress( - get_config('syncConnectionName') - ), - get_config('sync_timeout'), - ) - if loaded: - clear_socket_messages(resource) - return - else: - print('[ERROR] Sync is still in progress after matching the sync pattern.') + if check_queued: + loaded = wait_for( + lambda: not SyncConnection.is_sync_in_progress( + get_config('syncConnectionName') + ), + get_config('sync_timeout'), + ) + if not loaded: + raise TimeoutError( + '[ERROR] Sync is still in progress after matching the sync pattern.' + ) + return elif not force_sync: # if the sync pattern doesn't match then check the last sync status # and pass the step if the last sync status is STATUS:OK @@ -306,12 +310,13 @@ def wait_for_resource_to_sync( ) -def wait_for_initial_sync_to_complete(path): +def wait_for_initial_sync_to_complete(path, check_queued=True): wait_for_resource_to_sync( path, 'FOLDER', get_initial_sync_patterns(), True, + check_queued, ) diff --git a/test/gui/pageObjects/SyncConnection.py b/test/gui/pageObjects/SyncConnection.py index e960d1c00..6ae2e1575 100644 --- a/test/gui/pageObjects/SyncConnection.py +++ b/test/gui/pageObjects/SyncConnection.py @@ -1,6 +1,6 @@ from types import SimpleNamespace from appium.webdriver.common.appiumby import AppiumBy as By -from selenium.common.exceptions import NoSuchElementException +from selenium.common.exceptions import NoSuchElementException, WebDriverException from helpers.ConfigHelper import get_config from helpers.AppHelper import app @@ -113,6 +113,9 @@ def is_sync_in_progress(sync_folder): return True except NoSuchElementException: return False + except WebDriverException as e: + if "NoneType" in str(e): + return False @staticmethod def remove_folder_sync_connection(): diff --git a/test/gui/pageObjects/SyncConnectionWizard.py b/test/gui/pageObjects/SyncConnectionWizard.py index cff4641d8..70d5b1d52 100644 --- a/test/gui/pageObjects/SyncConnectionWizard.py +++ b/test/gui/pageObjects/SyncConnectionWizard.py @@ -197,6 +197,13 @@ def is_add_sync_folder_button_enabled(): SyncConnectionWizard.ADD_FOLDER_SYNC_BUTTON ).enabled + @staticmethod + def get_relative_folder_element(target_folder, parent_row): + possible_els = app().find_elements(By.NAME, target_folder) + for folder in possible_els: + if folder.rect["x"] > parent_row: + return folder + @staticmethod def toggle_folder_selection(folders, select=True): expected_state = "true" if select else "false" @@ -223,12 +230,9 @@ def toggle_folder_selection(folders, select=True): break parent_element.native_double_click() # expand the folder - - next_targets = app().find_elements(By.NAME, next_item) - for n_target in next_targets: - if n_target.rect["x"] > parent_position: - target_element = n_target - break + target_element = SyncConnectionWizard.get_relative_folder_element( + next_item, parent_position + ) # retry once if the folder is not expanded if not target_element or not target_element.is_displayed(): @@ -237,14 +241,16 @@ def toggle_folder_selection(folders, select=True): parent_element.native_click() parent_element.native_send_keys(Keys.SPACE) # try to get the next target again - next_targets = app().find_elements(By.NAME, next_item) - for n_target in next_targets: - if n_target.rect["x"] > parent_position: - target_element = n_target - break + target_element = SyncConnectionWizard.get_relative_folder_element( + next_item, parent_position + ) if not target_element or not target_element.is_displayed(): raise AssertionError(f'Failed to expand folder: {parent}') + if not target_element: + target_element = SyncConnectionWizard.get_relative_folder_element( + target_folder, parent_position + ) is_checked = target_element.get_attribute("checked") # return early if the folder is already in the expected state. if is_checked == expected_state: diff --git a/test/gui/steps/account_context.py b/test/gui/steps/account_context.py index 18ebb662e..f5d2f6f91 100644 --- a/test/gui/steps/account_context.py +++ b/test/gui/steps/account_context.py @@ -74,11 +74,11 @@ def step(context): sync_paths = generate_account_config(users) start_client() # accept certificate for each user - for idx, _ in enumerate(users): + for _ in users: enter_password = EnterPassword() enter_password.accept_certificate() - for idx, _ in enumerate(sync_paths.values()): + for _ in sync_paths.values(): # login from last dialog enter_password = EnterPassword() username = enter_password.get_username() @@ -86,7 +86,7 @@ def step(context): listen_sync_status_for_item(sync_paths[username]) enter_password.login_after_setup(username, password) # wait for files to sync - wait_for_initial_sync_to_complete(sync_paths[username]) + wait_for_initial_sync_to_complete(sync_paths[username], False) Toolbar.wait_toolbar_enabled() @@ -193,11 +193,10 @@ def step(context): @Then('credentials wizard should be visible') def step(context): - with ensure( - 'Credentials wizard is not be visible' - ): + with ensure('Credentials wizard is not be visible'): AccountConnectionWizard.is_credential_window_visible().should.be.true + @When('the user selects download everything option in advanced section') def step(context): AccountConnectionWizard.select_download_everything_option() From 5cd7ba011415fa066f6a167e2d065e83507a0ff0 Mon Sep 17 00:00:00 2001 From: Saw-jan Date: Thu, 18 Jun 2026 16:32:36 +0545 Subject: [PATCH 6/8] test: add sync info in the error log Signed-off-by: Saw-jan --- test/gui/helpers/SyncHelper.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/gui/helpers/SyncHelper.py b/test/gui/helpers/SyncHelper.py index e82b2091d..e2e0508fa 100644 --- a/test/gui/helpers/SyncHelper.py +++ b/test/gui/helpers/SyncHelper.py @@ -252,6 +252,7 @@ def wait_for_resource_to_sync( if patterns is None: patterns = get_synced_pattern(resource) + sync_info = [] synced = False if force_sync: initial_timeout = get_config('min_sync_timeout') @@ -267,6 +268,7 @@ def wait_for_resource_to_sync( ): print('[INFO] Sync is in progress. Waiting...') else: + sync_info.append('Force synced: True') # trigger force sync if the current status is OK status = get_current_sync_status(resource, resource_type) if status.startswith(SYNC_STATUS['OK']): @@ -280,6 +282,7 @@ def wait_for_resource_to_sync( ) # clear stored socket messages clear_socket_messages(resource) + sync_info.append('Sync complete (socket): %s' % synced) if synced: if check_queued: loaded = wait_for( @@ -288,9 +291,11 @@ def wait_for_resource_to_sync( ), get_config('sync_timeout'), ) + sync_info.append('Sync complete (UI): %s' % loaded) if not loaded: raise TimeoutError( '[ERROR] Sync is still in progress after matching the sync pattern.' + + '\n'.join(sync_info) ) return elif not force_sync: @@ -306,7 +311,8 @@ def wait_for_resource_to_sync( ) return raise TimeoutError( - 'Timeout while waiting for sync to complete for ' + str(timeout) + ' seconds' + 'Timeout while waiting for sync to complete for %s seconds.\n' % timeout + + '\n'.join(sync_info) ) From 0789756326e0122b9bf90f0fc1a940a7511699fd Mon Sep 17 00:00:00 2001 From: Saw-jan Date: Thu, 18 Jun 2026 17:37:06 +0545 Subject: [PATCH 7/8] test: add more sync error logs Signed-off-by: Saw-jan --- test/gui/helpers/SyncHelper.py | 9 ++++----- test/gui/pageObjects/Activity.py | 5 ++++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/test/gui/helpers/SyncHelper.py b/test/gui/helpers/SyncHelper.py index e2e0508fa..9b1997935 100644 --- a/test/gui/helpers/SyncHelper.py +++ b/test/gui/helpers/SyncHelper.py @@ -269,11 +269,8 @@ def wait_for_resource_to_sync( print('[INFO] Sync is in progress. Waiting...') else: sync_info.append('Force synced: True') - # trigger force sync if the current status is OK - status = get_current_sync_status(resource, resource_type) - if status.startswith(SYNC_STATUS['OK']): - print('[INFO] Retrying sync pattern check with force sync') - SyncConnection.force_sync() + print('[INFO] Retrying sync pattern check with force sync') + SyncConnection.force_sync() if not synced: synced = wait_for( @@ -310,6 +307,8 @@ def wait_for_resource_to_sync( + '. So passing the step.' ) return + print('[ERROR] Sync patterns: %s' % patterns) + print('[ERROR] Sync messages: %s' % read_socket_messages()) raise TimeoutError( 'Timeout while waiting for sync to complete for %s seconds.\n' % timeout + '\n'.join(sync_info) diff --git a/test/gui/pageObjects/Activity.py b/test/gui/pageObjects/Activity.py index a0bcfb819..caa5f8fe7 100644 --- a/test/gui/pageObjects/Activity.py +++ b/test/gui/pageObjects/Activity.py @@ -2,7 +2,7 @@ from types import SimpleNamespace from appium.webdriver.common.appiumby import AppiumBy as By from selenium.webdriver.common.keys import Keys -from selenium.common.exceptions import NoSuchElementException +from selenium.common.exceptions import NoSuchElementException, WebDriverException from helpers.FilesHelper import build_conflicted_regex from helpers.ConfigHelper import get_config @@ -102,6 +102,9 @@ def has_sync_status(filename, status): return False except NoSuchElementException: return False + except WebDriverException as e: + if "NoneType" in str(e): + return False @staticmethod def select_synced_filter(sync_filter): From f459ae8165bd6b7065e40fc599553540e1b768c3 Mon Sep 17 00:00:00 2001 From: Sawjan Gurung Date: Fri, 19 Jun 2026 12:39:48 +0545 Subject: [PATCH 8/8] test: address reviews Co-authored-by: Prashant Gurung <53248463+prashant-gurung899@users.noreply.github.com> --- test/gui/steps/sync_context.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/gui/steps/sync_context.py b/test/gui/steps/sync_context.py index 404866b66..9c35305ce 100644 --- a/test/gui/steps/sync_context.py +++ b/test/gui/steps/sync_context.py @@ -113,11 +113,10 @@ def step(context, filename): Activity.check_file_exist(filename) -@Then('the file "{filename}" should be blacklisted') -@Then('the folder "{filename}" should be blacklisted') -def step(context, filename): - with ensure('File is Blacklisted'): - Activity.is_resource_blacklisted(filename).should.be.true +@Then('the {resource_type:ResourceType} "{resourceName}" should be blacklisted') +def step(context, resource_type, resourceName): + with ensure(f'{resource_type.capitalize()} is blacklisted'): + Activity.is_resource_blacklisted(resourceName).should.be.true @Then('the file "|any|" should be ignored')