Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion test/gui/features/spaces/spaces.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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% |

Expand Down
4 changes: 2 additions & 2 deletions test/gui/features/sync-resources/syncResources.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 |
Expand Down
1 change: 1 addition & 0 deletions test/gui/helpers/ConfigHelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
}

Expand Down
3 changes: 2 additions & 1 deletion test/gui/helpers/ScreenRecorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
56 changes: 38 additions & 18 deletions test/gui/helpers/SyncHelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -252,32 +252,48 @@ 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_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')
# do not trigger force sync if the sync is still in progress
if check_queued and SyncConnection.is_sync_in_progress(
get_config('syncConnectionName')
):
print('[INFO] Sync is in progress. Waiting...')
else:
sync_info.append('Force synced: True')
print('[INFO] 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,
)

messages = read_and_update_socket_messages()
messages = filter_messages_for_item(messages, resource)
if not synced:
synced = wait_for(
lambda: has_sync_pattern(patterns, resource),
timeout - initial_timeout,
)
# clear stored socket messages
clear_socket_messages(resource)
sync_info.append('Sync complete (socket): %s' % synced)
if synced:
if check_queued:
loaded = wait_for(
lambda: not SyncConnection.is_sync_in_progress(
get_config('syncConnectionName')
),
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:
# if the sync pattern doesn't match then check the last sync status
Expand All @@ -291,17 +307,21 @@ 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 ' + str(timeout) + ' seconds'
'Timeout while waiting for sync to complete for %s seconds.\n' % timeout
+ '\n'.join(sync_info)
)


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,
)


Expand Down
20 changes: 14 additions & 6 deletions test/gui/pageObjects/Activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
Expand All @@ -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 {
Expand Down Expand Up @@ -93,10 +91,20 @@ 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
except WebDriverException as e:
if "NoneType" in str(e):
return False

@staticmethod
def select_synced_filter(sync_filter):
Expand Down
39 changes: 30 additions & 9 deletions test/gui/pageObjects/SyncConnection.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -100,6 +99,24 @@ 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
except WebDriverException as e:
if "NoneType" in str(e):
return False

@staticmethod
def remove_folder_sync_connection():
SyncConnection.perform_action("Remove Space")
Expand All @@ -122,10 +139,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:
Expand All @@ -138,7 +160,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)

66 changes: 40 additions & 26 deletions test/gui/pageObjects/SyncConnectionWizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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']"
)
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -197,20 +198,27 @@ def is_add_sync_folder_button_enabled():
).enabled

@staticmethod
def select_or_unselect_folders_to_sync(folders, select=True):
expected_state = "true" if select else "false"
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

if select:
SyncConnectionWizard.deselect_all_remote_folders()
@staticmethod
def toggle_folder_selection(folders, select=True):
expected_state = "true" if select else "false"

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 (
Expand All @@ -220,34 +228,40 @@ 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
target_element = SyncConnectionWizard.get_relative_folder_element(
next_item, parent_position
)

# 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
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}')

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")
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:
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}"
Expand All @@ -259,7 +273,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()
Expand Down
Loading