Skip to content

Commit

Permalink
tests(Settings): Can backup seed phrase
Browse files Browse the repository at this point in the history
Use icon for identifying settings buttons to make it more reliable
to translation and design changes
Added debug helpers found useful in debugging while implementing squish
tests

Closes: #6902
  • Loading branch information
stefandunca committed Aug 17, 2022
1 parent 29add2f commit 9f3247a
Show file tree
Hide file tree
Showing 13 changed files with 142 additions and 16 deletions.
24 changes: 18 additions & 6 deletions test/ui-test/src/drivers/SquishDriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
# * \date February 2022
# * \brief It contains generic Status view components definitions and Squish driver API.
# *****************************************************************************/
from email import message
from enum import Enum
import sys

Expand Down Expand Up @@ -116,12 +117,15 @@ def click_obj_by_name(objName: str):
obj = squish.waitForObject(getattr(names, objName))
squish.mouseClick(obj, squish.Qt.LeftButton)

def click_obj_by_wildcards_name(objName: str, wildcardString: str):
wildcardRealName = copy.deepcopy(getattr(names, objName))
wildcardRealName["objectName"] = Wildcard(wildcardString)
# Replaces all occurrences of objectNamePlaceholder with newValue in the objectName from the realName
# Then use the new objectName as a wildcard search pattern, waiting for the object with the new Real Name
# and return it if found. Raise an exception if not found.
def wait_by_wildcards(realNameVarName: str, objectNamePlaceholder: str, newValue: str, timeoutMSec: int = _MAX_WAIT_OBJ_TIMEOUT):
wildcardRealName = copy.deepcopy(getattr(names, realNameVarName))
newObjectName = wildcardRealName["objectName"].replace(objectNamePlaceholder, newValue)
wildcardRealName["objectName"] = Wildcard(newObjectName)

obj = squish.waitForObject(wildcardRealName)
squish.mouseClick(obj, squish.Qt.LeftButton)
return squish.waitForObject(wildcardRealName, timeoutMSec)

# It executes the right-click action into object with given object name:
def right_click_obj_by_name(objName: str):
Expand Down Expand Up @@ -228,5 +232,13 @@ def _find_link(objName: str, link: str):
squish.uninstallSignalHandler(obj, "linkHovered(QString)", "_handle_link_hovered")
return [-1, -1]

def expectTrue(assertionValue: bool, message: str):
def expect_true(assertionValue: bool, message: str):
return test.verify(assertionValue, message)

# Fail if the object is found and pass if not found
def verify_not_found(realNameVarName: str, message: str, timeoutMSec: int = 500):
try:
squish.waitForObject(getattr(names, realNameVarName), timeoutMSec)
test.fail(message, f'Unexpected: the object "{realNameVarName}" was found.')
except LookupError as err:
test.passes(message, f'Expected: the object "{realNameVarName}" was not found. Exception: {str(err)}.')
63 changes: 60 additions & 3 deletions test/ui-test/src/screens/SettingsScreen.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@


from enum import Enum
from faulthandler import disable
from drivers.SquishDriver import *
from drivers.SquishDriverVerification import *
from .StatusMainScreen import MainScreenComponents
from .StatusMainScreen import StatusMainScreen

import debug_helpers as debug
import names

class SidebarComponents(Enum):
ADVANCED_OPTION: str = "advanced_StatusBaseText"
WALLET_ITEM: str = "wallet_AppMenu_StatusNavigationListItem"
Expand All @@ -38,7 +42,8 @@ class WalletSettingsScreen(Enum):
EDIT_ACCOUNT_COLOR_REPEATER: str = "settings_Wallet_AccountView_EditAccountColorRepeater"
EDIT_ACCOUNT_SAVE_BUTTON: str = "settings_Wallet_AccountView_EditAccountSaveButton"
ACCOUNT_VIEW_ACCOUNT_NAME: str = "settings_Wallet_AccountView_AccountName"
ACCOUNT_VIEW_ICON_SETTINGS: str = "settings_Wallet_AccountView_IconSettings"
ACCOUNT_VIEW_ICON_SETTINGS: str = "settings_Wallet_AccountView_IconSettings"
BACKUP_SEED_PHRASE_BUTTON: str = "settings_Wallet_MainView_BackupSeedPhrase"


class ConfirmationDialog(Enum):
Expand All @@ -48,8 +53,19 @@ class CommunitiesSettingsScreen(Enum):
LEAVE_COMMUNITY_BUTTONS: str = "settings_Communities_MainView_LeaveCommunityButtons"
LEAVE_COMMUNITY_POPUP_LEAVE_BUTTON: str = "settings_Communities_MainView_LeavePopup_LeaveCommunityButton"



class BackupSeedPhrasePopup(Enum):
HAVE_PEN_CHECKBOX: str = "backup_seed_phrase_popup_Acknowledgements_havePen_checkbox"
WRITE_DOWN_CHECKBOX: str = "backup_seed_phrase_popup_Acknowledgements_writeDown_checkbox"
STORE_IT_CHECKBOX: str = "backup_seed_phrase_popup_Acknowledgements_storeIt_checkbox"
NEXT_BUTTON: str = "backup_seed_phrase_popup_nextButton"
REVEAL_SEED_PHRASE_BUTTON: str = "backup_seed_phrase_popup_ConfirmSeedPhrasePanel_RevealSeedPhraseButton"
SEED_PHRASE_WORD_PLACEHOLDER: str = "backup_seed_phrase_popup_ConfirmSeedPhrasePanel_StatusSeedPhraseInput_placeholder"
CONFIRM_FIRST_WORD_PAGE: str = "backup_seed_phrase_popup_BackupSeedStepBase_confirmFirstWord"
CONFIRM_FIRST_WORD_INPUT: str = "backup_seed_phrase_popup_BackupSeedStepBase_confirmFirstWord_inputText"
CONFIRM_SECOND_WORD_PAGE: str = "backup_seed_phrase_popup_BackupSeedStepBase_confirmSecondWord"
CONFIRM_SECOND_WORD_INPUT: str = "backup_seed_phrase_popup_BackupSeedStepBase_confirmSecondWord_inputText"
CONFIRM_YOU_STORED_CHECKBOX: str = "backup_seed_phrase_popup_ConfirmStoringSeedPhrasePanel_storeCheck"
CONFIRM_YOU_STORED_BUTTON: str = "backup_seed_phrase_popup_BackupSeedModal_completeAndDeleteSeedPhraseButton"

class SettingsScreen:
__pid = 0
Expand Down Expand Up @@ -148,3 +164,44 @@ def leave_community(self):
# In our case we have only one visible community and only one button
click_obj_by_name(CommunitiesSettingsScreen.LEAVE_COMMUNITY_BUTTONS.value)
click_obj_by_name(CommunitiesSettingsScreen.LEAVE_COMMUNITY_POPUP_LEAVE_BUTTON.value)

def check_backup_seed_phrase_workflow(self):
self.open_wallet_settings()
click_obj_by_name(WalletSettingsScreen.BACKUP_SEED_PHRASE_BUTTON.value)

# Check all checkboxes and click next button
obj = wait_and_get_obj(BackupSeedPhrasePopup.HAVE_PEN_CHECKBOX.value)
obj.checked = True
obj = wait_and_get_obj(BackupSeedPhrasePopup.WRITE_DOWN_CHECKBOX.value)
obj.checked = True
obj = wait_and_get_obj(BackupSeedPhrasePopup.STORE_IT_CHECKBOX.value)
obj.checked = True
click_obj_by_name(BackupSeedPhrasePopup.NEXT_BUTTON.value)

# Show seed phrase
click_obj_by_name(BackupSeedPhrasePopup.REVEAL_SEED_PHRASE_BUTTON.value)

# Collect word phrases for the next random confirmation steps
seed_phrase = [wait_by_wildcards(BackupSeedPhrasePopup.SEED_PHRASE_WORD_PLACEHOLDER.value, "%WORD_NO%", str(i + 1)).textEdit.input.edit.text for i in range(12)]
click_obj_by_name(BackupSeedPhrasePopup.NEXT_BUTTON.value)

# Confirm first random word of the seed phrase
firstSeedBaseObj = wait_and_get_obj(BackupSeedPhrasePopup.CONFIRM_FIRST_WORD_PAGE.value)
firstSeedWord = str(seed_phrase[firstSeedBaseObj.wordRandomNumber])
wait_for_object_and_type(BackupSeedPhrasePopup.CONFIRM_FIRST_WORD_INPUT.value, firstSeedWord)
click_obj_by_name(BackupSeedPhrasePopup.NEXT_BUTTON.value)

# Confirm second random word of the seed phrase
secondSeedBaseObj = wait_and_get_obj(BackupSeedPhrasePopup.CONFIRM_SECOND_WORD_PAGE.value)
secondSeedWord = str(seed_phrase[secondSeedBaseObj.wordRandomNumber])
wait_for_object_and_type(BackupSeedPhrasePopup.CONFIRM_SECOND_WORD_INPUT.value, secondSeedWord)

click_obj_by_name(BackupSeedPhrasePopup.NEXT_BUTTON.value)

# Acknowledge and confirm that you won't have access to the seed phrase anymore
obj = wait_and_get_obj(BackupSeedPhrasePopup.CONFIRM_YOU_STORED_CHECKBOX.value)
obj.checked = True
click_obj_by_name(BackupSeedPhrasePopup.CONFIRM_YOU_STORED_BUTTON.value)

def verify_seed_phrase_indicator_not_visible(self):
verify_not_found(WalletSettingsScreen.BACKUP_SEED_PHRASE_BUTTON.value, "Check that backup seed phrase settings button is visible")
6 changes: 3 additions & 3 deletions test/ui-test/src/screens/StatusCommunityScreen.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def edit_community(self, new_community_name: str, new_community_description: str
verify_text_matching(CommunitySettingsComponents.COMMUNITY_NAME_TEXT.value, new_community_name)
verify_text_matching(CommunitySettingsComponents.COMMUNITY_DESCRIPTION_TEXT.value, new_community_description)
obj = get_obj(CommunitySettingsComponents.COMMUNITY_LETTER_IDENTICON.value)
expectTrue(obj.color.name == new_community_color, "Community color was not changed correctly")
expect_true(obj.color.name == new_community_color, "Community color was not changed correctly")

def go_back_to_community(self):
click_obj_by_name(CommunitySettingsComponents.BACK_TO_COMMUNITY_BUTTON.value)
Expand All @@ -205,10 +205,10 @@ def search_and_change_community_channel_emoji(self, emoji_description: str):
# Search emoji
wait_for_object_and_type(CreateOrEditCommunityChannelPopup.EMOJI_SEARCH_TEXT_INPUT.value, emoji_description)
# Click on the first found emoji button
click_obj_by_wildcards_name(CreateOrEditCommunityChannelPopup.EMOJI_POPUP_EMOJI_PLACEHOLDER.value, "statusEmoji_*")
click_obj(wait_by_wildcards(CreateOrEditCommunityChannelPopup.EMOJI_POPUP_EMOJI_PLACEHOLDER.value, "%NAME%", "*"))
# save changes
click_obj_by_name(CreateOrEditCommunityChannelPopup.COMMUNITY_CHANNEL_SAVE_OR_CREATE_BUTTON.value)

def check_community_channel_emoji(self, emojiStr: str):
obj = wait_and_get_obj(CommunityScreenComponents.CHAT_IDENTIFIER_CHANNEL_ICON.value)
expectTrue(str(obj.icon.emoji).find(emojiStr) >= 0, "Same emoji check")
expect_true(str(obj.icon.emoji).find(emojiStr) >= 0, "Same emoji check")
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# encoding: UTF-8

import squish
import object
import names
import test

def debugWaitForObject(objRealName: dict, timeoutMSec: int = 1000):
return squish.waitForObject(objRealName, timeoutMSec)

def type_text(obj, text: str):
squish.type(obj, text)

def find_object(objRealName: dict):
obj = squish.findObject(objRealName)
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
settings_Wallet_AccountView_EditAccountColorRepeater = {"container": statusDesktop_mainWindow, "type": "Repeater", "objectName": "statusColorRepeater", "visible": True}
settings_Wallet_AccountView_AccountName = {"container": statusDesktop_mainWindow, "type": "StatusBaseText", "objectName": "walletAccountViewAccountName"}
settings_Wallet_AccountView_IconSettings = {"container": statusDesktop_mainWindow, "type": "StatusSmartIdenticon", "objectName": "walletAccountViewAccountImage" , "visible": True}
settings_Wallet_MainView_BackupSeedPhrase = {"container": mainWindow_ScrollView, "objectName": "seed-phrase-MainMenu", "type": "StatusNavigationListItem", "visible": True}
settings_Wallet_MainView_BackupSeedPhraseNotVisible = {"container": mainWindow_ScrollView, "objectName": "seed-phrase-MainMenu", "type": "StatusNavigationListItem", "visible": False}

generatedAccounts_ListView = {"container": statusDesktop_mainWindow, "objectName": "generatedAccounts", "type": "ListView"}

Expand All @@ -34,5 +36,19 @@
i_understand_StatusBaseText = {"container": statusDesktop_mainWindow_overlay, "text": "I understand", "type": "StatusBaseText", "unnamed": 1, "visible": True}

# Extra Settings:
sign_out_Quit_ExtraMenu_StatusNavigationListItem = {"container": mainWindow_ScrollView, "objectName": "Sign out & Quit-ExtraMenu", "type": "StatusNavigationListItem", "visible": True}
signOutConfirmation_StatusButton = {"container": statusDesktop_mainWindow_overlay, "objectName": "signOutConfirmation", "type": "StatusButton", "visible": True}
sign_out_Quit_ExtraMenu_StatusNavigationListItem = {"container": mainWindow_ScrollView, "objectName": "logout-ExtraMenu", "type": "StatusNavigationListItem", "visible": True}
signOutConfirmation_StatusButton = {"container": statusDesktop_mainWindow_overlay, "objectName": "signOutConfirmation", "type": "StatusButton", "visible": True}

# Backup seed phrase:
backup_seed_phrase_popup_Acknowledgements_havePen_checkbox = {"container": statusDesktop_mainWindow_overlay, "objectName": "Acknowledgements_havePen", "type": "StatusCheckBox", "checkable": True, "visible": True}
backup_seed_phrase_popup_Acknowledgements_writeDown_checkbox = {"container": statusDesktop_mainWindow_overlay, "objectName": "Acknowledgements_writeDown", "type": "StatusCheckBox", "checkable": True, "visible": True}
backup_seed_phrase_popup_Acknowledgements_storeIt_checkbox = {"container": statusDesktop_mainWindow_overlay, "objectName": "Acknowledgements_storeIt", "type": "StatusCheckBox", "checkable": True, "visible": True}
backup_seed_phrase_popup_nextButton = {"container": statusDesktop_mainWindow_overlay, "objectName": "BackupSeedModal_nextButton", "type": "StatusButton", "visible": True, "enabled": True}
backup_seed_phrase_popup_ConfirmSeedPhrasePanel_RevealSeedPhraseButton = {"container": statusDesktop_mainWindow_overlay, "objectName": "ConfirmSeedPhrasePanel_RevealSeedPhraseButton", "type": "StatusButton", "visible": True}
backup_seed_phrase_popup_ConfirmSeedPhrasePanel_StatusSeedPhraseInput_placeholder = {"container": statusDesktop_mainWindow_overlay, "objectName": "ConfirmSeedPhrasePanel_StatusSeedPhraseInput_%WORD_NO%", "type": "StatusSeedPhraseInput", "visible": True}
backup_seed_phrase_popup_BackupSeedStepBase_confirmFirstWord = {"container": statusDesktop_mainWindow_overlay, "objectName": "BackupSeedModal_BackupSeedStepBase_confirmFirstWord", "type": "BackupSeedStepBase", "visible": True}
backup_seed_phrase_popup_BackupSeedStepBase_confirmFirstWord_inputText = {"container": backup_seed_phrase_popup_BackupSeedStepBase_confirmFirstWord, "objectName": "BackupSeedStepBase_inputText", "type": "TextEdit", "visible": True}
backup_seed_phrase_popup_BackupSeedStepBase_confirmSecondWord = {"container": statusDesktop_mainWindow_overlay, "objectName": "BackupSeedModal_BackupSeedStepBase_confirmSecondWord", "type": "BackupSeedStepBase", "visible": True}
backup_seed_phrase_popup_BackupSeedStepBase_confirmSecondWord_inputText = {"container": backup_seed_phrase_popup_BackupSeedStepBase_confirmSecondWord, "objectName": "BackupSeedStepBase_inputText", "type": "TextEdit", "visible": True}
backup_seed_phrase_popup_ConfirmStoringSeedPhrasePanel_storeCheck = {"container": statusDesktop_mainWindow_overlay, "objectName": "ConfirmStoringSeedPhrasePanel_storeCheck", "type": "StatusCheckBox", "checkable": True, "visible": True}
backup_seed_phrase_popup_BackupSeedModal_completeAndDeleteSeedPhraseButton = {"container": statusDesktop_mainWindow_overlay, "objectName": "BackupSeedModal_completeAndDeleteSeedPhraseButton", "type": "StatusButton", "visible": True}
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,11 @@ def step(context: any):
@When("the user leaves the community")
def step(context: any):
_settingsScreen.leave_community()

@When("the user backs up the wallet seed phrase")
def step(context):
_settingsScreen.check_backup_seed_phrase_workflow()

@Then("the backup seed phrase indicator is not displayed")
def step(context):
_settingsScreen.verify_seed_phrase_indicator_not_visible()
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ Feature: Status Desktop Settings Menu

Scenario: The user quits the app
When the user clicks on Sign out and Quit
Then the app is closed
Then the app is closed

Scenario: User can backup seed phrase
When the user activates wallet and opens the wallet settings
And the user backs up the wallet seed phrase
Then the backup seed phrase indicator is not displayed
4 changes: 3 additions & 1 deletion ui/app/AppLayouts/Profile/panels/MenuPanel.qml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Column {
Repeater {
id: mainMenuItems
delegate: StatusNavigationListItem {
objectName: model.icon + "-MainMenu"
itemId: model.subsection
title: model.text
icon.name: model.icon
Expand Down Expand Up @@ -100,7 +101,8 @@ Column {
Repeater {
id: extraMenuItems
delegate: StatusNavigationListItem {
objectName: model.text + "-ExtraMenu"
objectName: model.icon + "-ExtraMenu"
onVisibleChanged: console.debug("@dd obj name", objectName)
itemId: model.subsection
title: model.text
icon.name: model.icon
Expand Down
4 changes: 4 additions & 0 deletions ui/app/AppLayouts/Profile/popups/BackupSeedModal.qml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ StatusStackModal {
rightButtons: [ d.skipButton, nextButton, finishButton ]

nextButton: StatusButton {
objectName: "BackupSeedModal_nextButton"
enabled: {
switch (root.currentIndex) {
case 0:
Expand Down Expand Up @@ -87,6 +88,7 @@ StatusStackModal {

finishButton: StatusButton {
text: qsTr("Complete & Delete My Seed Phrase")
objectName: "BackupSeedModal_completeAndDeleteSeedPhraseButton"
enabled: d.seedStored
onClicked: {
root.privacyStore.removeMnemonic();
Expand All @@ -112,12 +114,14 @@ StatusStackModal {
},
BackupSeedStepBase {
id: confirmFirstWord
objectName: "BackupSeedModal_BackupSeedStepBase_confirmFirstWord"
titleText: qsTr("Confirm word #%1 of your seed phrase").arg(d.firstRandomNo + 1)
wordRandomNumber: d.firstRandomNo
wordAtRandomNumber: root.privacyStore.getMnemonicWordAtIndex(d.firstRandomNo)
},
BackupSeedStepBase {
id: confirmSecondWord
objectName: "BackupSeedModal_BackupSeedStepBase_confirmSecondWord"
titleText: qsTr("Confirm word #%1 of your seed phrase").arg(d.secondRandomNo + 1)
wordRandomNumber: d.secondRandomNo
wordAtRandomNumber: root.privacyStore.getMnemonicWordAtIndex(d.secondRandomNo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ ColumnLayout {

StatusCheckBox {
id: havePen
objectName: "Acknowledgements_havePen"
spacing: Style.current.padding
text: qsTr("I have a pen and paper")
font.pixelSize: Style.current.primaryTextFontSize
Expand All @@ -88,6 +89,7 @@ ColumnLayout {

StatusCheckBox {
id: writeDown
objectName: "Acknowledgements_writeDown"
spacing: Style.current.padding
text: qsTr("I am ready to write down my seed phrase")
font.pixelSize: Style.current.primaryTextFontSize
Expand All @@ -96,6 +98,7 @@ ColumnLayout {

StatusCheckBox {
id: storeIt
objectName: "Acknowledgements_storeIt"
spacing: Style.current.padding
text: qsTr("I know where I’ll store it")
font.pixelSize: Style.current.primaryTextFontSize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ StatusScrollView {

StatusInput {
id: inputText
input.edit.objectName: "BackupSeedStepBase_inputText"
visible: (wordRandomNumber > -1)
implicitWidth: 448
validationMode: StatusInput.ValidationMode.Always
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ BackupSeedStepBase {
readonly property int spacing: 4
delegate: StatusSeedPhraseInput {
id: seedWordInput
objectName: "ConfirmSeedPhrasePanel_StatusSeedPhraseInput_" + grid.wordIndex[index]
width: (grid.cellWidth - grid.spacing)
height: (grid.cellHeight - grid.spacing)
textEdit.input.edit.enabled: false
Expand All @@ -61,6 +62,7 @@ BackupSeedStepBase {
}

StatusButton {
objectName: "ConfirmSeedPhrasePanel_RevealSeedPhraseButton"
anchors.centerIn: parent
visible: hideSeed
icon.name: "view"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ BackupSeedStepBase {

StatusCheckBox {
id: storeCheck
objectName: "ConfirmStoringSeedPhrasePanel_storeCheck"
spacing: Style.current.padding
font.pixelSize: Style.current.primaryTextFontSize
text: qsTr("I acknowledge that Status will not be able to show me my seed phrase again.")
Expand Down

0 comments on commit 9f3247a

Please sign in to comment.