From 9ce32cf2ace9d37fcfa6937730e2e0005e321add Mon Sep 17 00:00:00 2001 From: Valentina1133 <141633821+Valentina1133@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:45:49 +0700 Subject: [PATCH] Syncing - Sync device during onboarding (#120) * Syncing - Sync device during onboarding --- constants/syncing.py | 6 ++ .../community/authenticate_popup.py | 2 +- .../settings/sync_new_device_popup.py | 26 ++++++ gui/objects_map/component_names.py | 5 ++ gui/objects_map/main_names.py | 1 + gui/objects_map/onboarding_names.py | 17 +++- gui/objects_map/settings_names.py | 7 ++ gui/screens/onboarding.py | 79 ++++++++++++++++++- gui/screens/settings.py | 38 +++++++++ tests/test_syncing.py | 75 ++++++++++++++++++ 10 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 constants/syncing.py create mode 100644 gui/components/settings/sync_new_device_popup.py create mode 100644 tests/test_syncing.py diff --git a/constants/syncing.py b/constants/syncing.py new file mode 100644 index 0000000..d118e13 --- /dev/null +++ b/constants/syncing.py @@ -0,0 +1,6 @@ +from enum import Enum + + +class SyncingSettings(Enum): + SYNC_A_NEW_DEVICE_INSTRUCTIONS_HEADER = 'Sync a New Device' + SYNC_A_NEW_DEVICE_INSTRUCTIONS_SUBTITLE = 'You own your data. Sync it among your devices.' diff --git a/gui/components/community/authenticate_popup.py b/gui/components/community/authenticate_popup.py index 8203d8f..72c4ab1 100644 --- a/gui/components/community/authenticate_popup.py +++ b/gui/components/community/authenticate_popup.py @@ -24,4 +24,4 @@ class AuthenticatePopup(BasePopup): def authenticate(self, password: str): self._passwort_text_edit.type_text(password) self._authenticate_button.click() - self.wait_until_hidden() + self._authenticate_button.wait_until_hidden() diff --git a/gui/components/settings/sync_new_device_popup.py b/gui/components/settings/sync_new_device_popup.py new file mode 100644 index 0000000..79a6bb4 --- /dev/null +++ b/gui/components/settings/sync_new_device_popup.py @@ -0,0 +1,26 @@ +import allure +import pyperclip + +from gui.components.base_popup import BasePopup +from gui.elements.qt.button import Button +from gui.elements.qt.text_edit import TextEdit + + +class SyncNewDevicePopup(BasePopup): + + def __init__(self): + super().__init__() + self._copy_button = Button('copy_SyncCodeStatusButton') + self._done_button = Button('done_SyncCodeStatusButton') + self._sync_code_field = TextEdit('syncCodeInput_StatusPasswordInput') + + @property + @allure.step('Get syncing code') + def syncing_code(self): + self._copy_button.click() + return pyperclip.paste() + + @allure.step('Click done') + def done(self): + self._done_button.click() + self.wait_until_hidden() diff --git a/gui/objects_map/component_names.py b/gui/objects_map/component_names.py index 1fba237..53a4d00 100644 --- a/gui/objects_map/component_names.py +++ b/gui/objects_map/component_names.py @@ -287,3 +287,8 @@ settingsSave_StatusButton = {"container": statusDesktop_mainWindow, "objectName" # Back up seed phrase banner mainWindow_secureYourSeedPhraseBanner_ModuleWarning = {"container": statusDesktop_mainWindow, "objectName": "secureYourSeedPhraseBanner", "type": "ModuleWarning", "visible": True} + +# Sync new device popup +copy_SyncCodeStatusButton = {"container": statusDesktop_mainWindow_overlay, "objectName": "syncCodeCopyButton", "type": "StatusButton", "visible": True} +done_SyncCodeStatusButton = {"container": statusDesktop_mainWindow_overlay, "objectName": "syncAnewDeviceNextButton", "type": "StatusButton", "visible": True} +syncCodeInput_StatusPasswordInput = {"container": statusDesktop_mainWindow_overlay, "id": "syncCodeInput", "type": "StatusPasswordInput", "unnamed": 1, "visible": True} diff --git a/gui/objects_map/main_names.py b/gui/objects_map/main_names.py index b2b1044..658c1ab 100644 --- a/gui/objects_map/main_names.py +++ b/gui/objects_map/main_names.py @@ -1,4 +1,5 @@ statusDesktop_mainWindow = {"name": "mainWindow", "type": "StatusWindow"} +mainWindow_StatusWindow = {"name": "mainWindow", "type": "StatusWindow", "visible": True} statusDesktop_mainWindow_overlay = {"container": statusDesktop_mainWindow, "type": "Overlay", "unnamed": 1, "visible": True} statusDesktop_mainWindow_overlay_popup2 = {"container": statusDesktop_mainWindow_overlay, "occurrence": 2, "type": "PopupItem", "unnamed": 1, "visible": True} scrollView_StatusScrollView = {"container": statusDesktop_mainWindow_overlay, "id": "scrollView", "type": "StatusScrollView", "unnamed": 1, "visible": True} diff --git a/gui/objects_map/onboarding_names.py b/gui/objects_map/onboarding_names.py index 2138e85..c5d9ec0 100755 --- a/gui/objects_map/onboarding_names.py +++ b/gui/objects_map/onboarding_names.py @@ -9,7 +9,7 @@ mainWindow_allowNotificationsOnboardingOkButton = {"container": mainWindow_Allow # Welcome View mainWindow_WelcomeView = {"container": statusDesktop_mainWindow, "type": "WelcomeView", "unnamed": 1, "visible": True} mainWindow_I_am_new_to_Status_StatusBaseText = {"container": mainWindow_WelcomeView, "objectName": "welcomeViewIAmNewToStatusButton", "type": "StatusButton"} -mainWindow_I_already_use_Status_StatusBaseText = {"container": mainWindow_WelcomeView, "objectName": "welcomeViewIAlreadyUseStatusButton", "type": "StatusFlatButton", "visible": True} +mainWindow_I_already_use_Status_StatusFlatButton = {"checkable": False, "container": statusDesktop_mainWindow, "id": "btnExistingUser", "type": "StatusFlatButton", "visible": True} # Get Keys View mainWindow_KeysMainView = {"container": statusDesktop_mainWindow, "type": "KeysMainView", "unnamed": 1, "visible": True} @@ -39,6 +39,21 @@ mainWindow_Import_StatusButton = {"checkable": False, "container": mainWindow_Se "objectName": "seedPhraseViewSubmitButton", "text": "Import", "type": "StatusButton", "visible": True} +# SyncCode View +mainWindow_SyncCodeView = {"container": statusDesktop_mainWindow, "type": "SyncCodeView", "unnamed": 1, "visible": True} +mainWindow_switchTabBar_StatusSwitchTabBar_2 = {"container": statusDesktop_mainWindow, "id": "switchTabBar", "type": "StatusSwitchTabBar", "unnamed": 1, "visible": True} +switchTabBar_Enter_sync_code_StatusSwitchTabButton = {"checkable": True, "container": mainWindow_switchTabBar_StatusSwitchTabBar_2, "text": "Enter sync code", "type": "StatusSwitchTabButton", "unnamed": 1, "visible": True} +mainWindow_statusBaseInput_StatusBaseInput = {"container": statusDesktop_mainWindow, "id": "statusBaseInput", "type": "StatusBaseInput", "unnamed": 1, "visible": True} +mainWindow_Paste_StatusButton = {"checkable": False, "container": statusDesktop_mainWindow, "text": "Paste", "type": "StatusButton", "unnamed": 1, "visible": True} + +# SyncDevice View +mainWindow_SyncingDeviceView_found = {"container": statusDesktop_mainWindow, "type": "SyncingDeviceView", "unnamed": 1, "visible": True} +mainWindow_SyncingDeviceView_synced = {"container": mainWindow_StatusWindow, "type": "SyncingDeviceView", "unnamed": 1, "visible": True} +mainWindow_SyncDeviceResult = {"container": mainWindow_StatusWindow, "type": "SyncDeviceResult", "unnamed": 1, "visible": True} +synced_StatusBaseText = {"container": mainWindow_StatusWindow, "type": "StatusBaseText", "unnamed": 1, "visible": True} +mainWindow_Sign_in_StatusButton = {"checkable": False, "container": mainWindow_StatusWindow, "text": "Sign in", "type": "StatusButton", "unnamed": 1, "visible": True} +sync_text_item = {"container": statusDesktop_mainWindow, "type": "StatusBaseText", "unnamed": 1, "visible": True} + # Keycard Init View mainWindow_KeycardInitView = {"container": statusDesktop_mainWindow, "type": "KeycardInitView", "unnamed": 1, "visible": True} diff --git a/gui/objects_map/settings_names.py b/gui/objects_map/settings_names.py index d4abca8..d889534 100644 --- a/gui/objects_map/settings_names.py +++ b/gui/objects_map/settings_names.py @@ -66,3 +66,10 @@ change_password_button = {"container": statusDesktop_mainWindow, "type": "Status bio_StatusInput = {"container": statusDesktop_mainWindow, "objectName": "bioInput", "type": "StatusInput", "visible": True} bio_TextEdit = {"container": bio_StatusInput, "type": "TextEdit", "unnamed": 1, "visible": True} addMoreSocialLinks = {"container": statusDesktop_mainWindow, "objectName": "addMoreSocialLinks", "type": "StatusLinkText", "visible": True} + +# Syncing Settings View +mainWindow_SyncingView = {"container": statusDesktop_mainWindow, "type": "SyncingView", "unnamed": 1, "visible": True} +settings_Setup_Syncing_StatusButton = {"container": statusDesktop_mainWindow, "objectName": "setupSyncingStatusButton", "type": "StatusButton", "visible": True} +settings_Backup_Data_StatusButton = {"container": settingsContentBase_ScrollView, "objectName": "setupSyncBackupDataButton", "type": "StatusButton", "visible": True} +settings_Sync_New_Device_Header = {"container": settingsContentBase_ScrollView, "objectName": "syncNewDeviceTextLabel", "type": "StatusBaseText", "visible": True} +settings_Sync_New_Device_SubTitle = {"container": settingsContentBase_ScrollView, "objectName": "syncNewDeviceSubTitleTextLabel", "type": "StatusBaseText", "visible": True} diff --git a/gui/screens/onboarding.py b/gui/screens/onboarding.py index 3eef6ab..d1e4c2c 100755 --- a/gui/screens/onboarding.py +++ b/gui/screens/onboarding.py @@ -37,7 +37,7 @@ class WelcomeToStatusView(QObject): def __init__(self): super(WelcomeToStatusView, self).__init__('mainWindow_WelcomeView') self._i_am_new_to_status_button = Button('mainWindow_I_am_new_to_Status_StatusBaseText') - self._i_already_use_status_button = Button('mainWindow_I_already_use_Status_StatusBaseText') + self._i_already_use_status_button = Button('mainWindow_I_already_use_Status_StatusFlatButton') @allure.step('Open Keys view') def get_keys(self) -> 'KeysView': @@ -45,6 +45,12 @@ class WelcomeToStatusView(QObject): time.sleep(1) return KeysView().wait_until_appears() + @allure.step('Open Sign by syncing form') + def sync_existing_user(self) -> 'SignBySyncingView': + self._i_already_use_status_button.click() + time.sleep(1) + return SignBySyncingView().wait_until_appears() + class OnboardingView(QObject): @@ -103,6 +109,77 @@ class ImportSeedPhraseView(OnboardingView): return KeysView().wait_until_appears() +class SignBySyncingView(OnboardingView): + + def __init__(self): + super(SignBySyncingView, self).__init__('mainWindow_KeysMainView') + self._scan_or_enter_sync_code_button = Button('keysMainView_PrimaryAction_Button') + + @allure.step('Open sync code view') + def open_sync_code_view(self): + self._scan_or_enter_sync_code_button.click() + return SyncCodeView().wait_until_appears() + + +class SyncCodeView(OnboardingView): + + def __init__(self): + super(SyncCodeView, self).__init__('mainWindow_SyncCodeView') + self._enter_sync_code_button = Button('switchTabBar_Enter_sync_code_StatusSwitchTabButton') + self._paste_sync_code_button = Button('mainWindow_Paste_StatusButton') + + @allure.step('Open enter sync code form') + def open_enter_sync_code_form(self): + self._enter_sync_code_button.click() + return self + + @allure.step('Paste sync code') + def paste_sync_code(self): + self._paste_sync_code_button.click() + return SyncDeviceFoundView().wait_until_appears() + + +class SyncDeviceFoundView(OnboardingView): + + def __init__(self): + super(SyncDeviceFoundView, self).__init__('mainWindow_SyncingDeviceView_found') + self._sync_text_item = QObject('sync_text_item') + + @property + @allure.step('Get device_found_notifications') + def device_found_notifications(self) -> typing.List: + device_found_notifications = [] + for obj in driver.findAllObjects(self._sync_text_item.real_name): + device_found_notifications.append(str(obj.text)) + return device_found_notifications + + +class SyncResultView(OnboardingView): + + def __init__(self): + super(SyncResultView, self).__init__('mainWindow_SyncDeviceResult') + self._sync_result = QObject('mainWindow_SyncDeviceResult') + self._sign_in_button = Button('mainWindow_Sign_in_StatusButton') + self._synced_text_item = QObject('synced_StatusBaseText') + + @property + @allure.step('Get device synced notifications') + def device_synced_notifications(self) -> typing.List: + device_synced_notifications = [] + for obj in driver.findAllObjects(self._synced_text_item.real_name): + device_synced_notifications.append(str(obj.text)) + return device_synced_notifications + + @allure.step('Wait until appears {0}') + def wait_until_appears(self, timeout_msec: int = configs.timeouts.UI_LOAD_TIMEOUT_MSEC): + self._sign_in_button.wait_until_appears(timeout_msec) + return self + + @allure.step('Sign in') + def sign_in(self): + self._sign_in_button.click() + + class SeedPhraseInputView(OnboardingView): def __init__(self): diff --git a/gui/screens/settings.py b/gui/screens/settings.py index 24c0964..b4924c4 100644 --- a/gui/screens/settings.py +++ b/gui/screens/settings.py @@ -7,11 +7,14 @@ import allure import configs.timeouts import driver from constants import UserCommunityInfo, wallet_account_list_item +from constants.syncing import SyncingSettings from driver import objects_access from driver.objects_access import walk_children from gui.components.back_up_your_seed_phrase_popup import BackUpYourSeedPhrasePopUp from gui.components.change_password_popup import ChangePasswordPopup +from gui.components.community.authenticate_popup import AuthenticatePopup from gui.components.settings.send_contact_request_popup import SendContactRequest +from gui.components.settings.sync_new_device_popup import SyncNewDevicePopup from gui.components.social_links_popup import SocialLinksPopup from gui.components.wallet.testnet_mode_popup import TestnetModePopup from gui.components.wallet.wallet_account_popups import AccountPopup @@ -61,6 +64,11 @@ class LeftPanel(QObject): self._open_settings(15) return BackUpYourSeedPhrasePopUp() + @allure.step('Open syncing settings') + def open_syncing_settings(self): + self._open_settings(8) + return SyncingSettingsView() + class SettingsScreen(QObject): @@ -456,3 +464,33 @@ class EditAccountOrderSettings(WalletSettingsView): @allure.step('Verify that back button is present') def is_back_button_present(self) -> bool: return self._back_button.is_visible + + +class SyncingSettingsView(QObject): + + def __init__(self): + super().__init__('mainWindow_SyncingView') + self._setup_syncing_button = Button('settings_Setup_Syncing_StatusButton') + self._backup_data_button = Button('settings_Backup_Data_StatusButton') + self._sync_new_device_instructions_header = TextLabel('settings_Sync_New_Device_Header') + self._sync_new_device_instructions_subtitle = TextLabel('settings_Sync_New_Device_SubTitle') + + @allure.step('Checking instructions elements: back up button presence') + def is_backup_button_present(self): + assert self._backup_data_button.is_visible, f"Backup button is not visible" + + @allure.step('Checking instructions elements: header presence') + def is_instructions_header_present(self): + assert (self._sync_new_device_instructions_header.text + == SyncingSettings.SYNC_A_NEW_DEVICE_INSTRUCTIONS_HEADER.value), f"Sync a new device title is incorrect" + + @allure.step('Checking instructions elements: subtitle presence') + def is_instructions_subtitle_present(self): + assert (self._sync_new_device_instructions_subtitle.text + == SyncingSettings.SYNC_A_NEW_DEVICE_INSTRUCTIONS_SUBTITLE.value), f"Sync a new device subtitle is incorrect" + + @allure.step('Setup syncing') + def set_up_syncing(self, password: str): + self._setup_syncing_button.click() + AuthenticatePopup().wait_until_appears().authenticate(password) + return SyncNewDevicePopup().wait_until_appears() diff --git a/tests/test_syncing.py b/tests/test_syncing.py new file mode 100644 index 0000000..fb39406 --- /dev/null +++ b/tests/test_syncing.py @@ -0,0 +1,75 @@ +import allure +import pytest +from allure_commons._allure import step + +import configs.testpath +import constants +import driver +from constants import UserAccount +from gui.components.onboarding.before_started_popup import BeforeStartedPopUp +from gui.components.onboarding.beta_consent_popup import BetaConsentPopup +from gui.components.splash_screen import SplashScreen +from gui.main_window import MainWindow +from gui.screens.onboarding import AllowNotificationsView, WelcomeToStatusView, SyncResultView + +pytestmark = allure.suite("Syncing") + + +@allure.testcase('https://ethstatus.testrail.net/index.php?/cases/view/703592', 'Sync device during onboarding') +@pytest.mark.case(703592) +@pytest.mark.parametrize('user_data', [configs.testpath.TEST_USER_DATA / 'user_account_one']) +def test_sync_device_during_onboarding(multiple_instance, user_data): + user: UserAccount = constants.user_account_one + main_window = MainWindow() + + with (multiple_instance() as aut_one, multiple_instance() as aut_two): + with step('Get syncing code in first instance'): + aut_one.attach() + main_window.prepare() + main_window.authorize_user(user) + sync_settings_view = main_window.left_panel.open_settings().left_panel.open_syncing_settings() + sync_settings_view.is_instructions_header_present() + sync_settings_view.is_instructions_subtitle_present() + if configs.DEV_BUILD: + sync_settings_view.is_backup_button_present() + setup_syncing = main_window.left_panel.open_settings().left_panel.open_syncing_settings().set_up_syncing( + user.password) + sync_code = setup_syncing.syncing_code + setup_syncing.done() + main_window.hide() + + with step('Verify syncing code is correct'): + sync_code_fields = sync_code.split(':') + assert sync_code_fields[0] == 'cs3' + assert len(sync_code_fields) == 5 + + with step('Open sync code form in second instance'): + aut_two.attach() + main_window.prepare() + if configs.system.IS_MAC: + AllowNotificationsView().wait_until_appears().allow() + BeforeStartedPopUp().get_started() + wellcome_screen = WelcomeToStatusView().wait_until_appears() + sync_view = wellcome_screen.sync_existing_user().open_sync_code_view() + + with step('Paste sync code on second instance and wait until device is synced'): + sync_start = sync_view.open_enter_sync_code_form().paste_sync_code() + assert driver.waitFor(lambda: 'Device found!' in sync_start.device_found_notifications) + sync_result = SyncResultView().wait_until_appears() + assert driver.waitFor(lambda: 'Device synced!' in sync_result.device_synced_notifications) + assert user.name in sync_start.device_found_notifications + + with step('Sign in to synced account'): + sync_result.sign_in() + SplashScreen().wait_until_appears().wait_until_hidden() + if not configs.DEV_BUILD: + BetaConsentPopup().confirm() + + with step('Verify user details are the same with user in first instance'): + user_canvas = main_window.left_panel.open_user_canvas() + user_canvas_name = user_canvas.user_name + assert user_canvas_name == user.name + assert driver.waitFor( + lambda: user_canvas.is_user_image_contains(user.name[:2]), + configs.timeouts.UI_LOAD_TIMEOUT_MSEC + )