[#8968] custom seed phrase popover
Signed-off-by: yenda <eric@status.im>
This commit is contained in:
parent
3ee52b4fba
commit
18ce3b0129
|
@ -8,6 +8,7 @@
|
||||||
[status-im.multiaccounts.create.core :as multiaccounts.create]
|
[status-im.multiaccounts.create.core :as multiaccounts.create]
|
||||||
[status-im.multiaccounts.db :as db]
|
[status-im.multiaccounts.db :as db]
|
||||||
[status-im.native-module.core :as status]
|
[status-im.native-module.core :as status]
|
||||||
|
[status-im.popover.core :as popover]
|
||||||
[status-im.ui.screens.navigation :as navigation]
|
[status-im.ui.screens.navigation :as navigation]
|
||||||
[status-im.utils.fx :as fx]
|
[status-im.utils.fx :as fx]
|
||||||
[status-im.utils.security :as security]
|
[status-im.utils.security :as security]
|
||||||
|
@ -150,12 +151,23 @@
|
||||||
(navigation/navigate-to-cofx :recover-multiaccount-enter-phrase nil)))
|
(navigation/navigate-to-cofx :recover-multiaccount-enter-phrase nil)))
|
||||||
|
|
||||||
(fx/defn proceed-to-import-mnemonic
|
(fx/defn proceed-to-import-mnemonic
|
||||||
{:events [::enter-phrase-input-submitted ::enter-phrase-next-pressed]}
|
{:events [::enter-phrase-next-pressed]}
|
||||||
[{:keys [db] :as cofx}]
|
[{:keys [db] :as cofx}]
|
||||||
(let [{:keys [password passphrase]} (:multiaccounts/recover db)]
|
(let [{:keys [password passphrase]} (:multiaccounts/recover db)]
|
||||||
|
(if (check-phrase-warnings passphrase)
|
||||||
|
(popover/show-popover cofx {:view :custom-seed-phrase})
|
||||||
(when (mnemonic/valid-length? passphrase)
|
(when (mnemonic/valid-length? passphrase)
|
||||||
{::import-multiaccount {:passphrase passphrase
|
{::import-multiaccount {:passphrase passphrase
|
||||||
:password password}})))
|
:password password}}))))
|
||||||
|
|
||||||
|
(fx/defn continue-to-import-mnemonic
|
||||||
|
{:events [::continue-pressed]}
|
||||||
|
[{:keys [db] :as cofx}]
|
||||||
|
(let [{:keys [password passphrase]} (:multiaccounts/recover db)]
|
||||||
|
(fx/merge cofx
|
||||||
|
{::import-multiaccount {:passphrase passphrase
|
||||||
|
:password password}}
|
||||||
|
(popover/hide-popover))))
|
||||||
|
|
||||||
(fx/defn cancel-pressed
|
(fx/defn cancel-pressed
|
||||||
{:events [::cancel-pressed]}
|
{:events [::cancel-pressed]}
|
||||||
|
|
|
@ -19,7 +19,44 @@
|
||||||
[status-im.utils.utils :as utils]
|
[status-im.utils.utils :as utils]
|
||||||
[status-im.constants :as constants]
|
[status-im.constants :as constants]
|
||||||
[status-im.ui.components.list-item.views :as list-item]
|
[status-im.ui.components.list-item.views :as list-item]
|
||||||
[status-im.utils.platform :as platform]))
|
[status-im.utils.platform :as platform]
|
||||||
|
[status-im.react-native.resources :as resources]
|
||||||
|
[status-im.ui.components.icons.vector-icons :as icons]
|
||||||
|
[status-im.ui.components.button :as button]))
|
||||||
|
|
||||||
|
(defview custom-seed-phrase []
|
||||||
|
[react/view
|
||||||
|
[react/view {:margin-top 24 :margin-horizontal 24 :align-items :center}
|
||||||
|
[react/view {:width 32 :height 32 :border-radius 16
|
||||||
|
:align-items :center :justify-content :center}
|
||||||
|
[icons/icon :main-icons/help {:color colors/blue}]]
|
||||||
|
[react/text {:style {:typography :title-bold
|
||||||
|
:margin-top 8
|
||||||
|
:margin-bottom 8}}
|
||||||
|
(i18n/label :t/custom-seed-phrase)]
|
||||||
|
[react/view {:flex-wrap :wrap
|
||||||
|
:flex-direction :row
|
||||||
|
:justify-content :center
|
||||||
|
:text-align :center}
|
||||||
|
[react/nested-text
|
||||||
|
{:style {:color colors/gray
|
||||||
|
:text-align :center
|
||||||
|
:line-height 22}}
|
||||||
|
(i18n/label :t/custom-seed-phrase-text-1)
|
||||||
|
[{:style {:color colors/black}}
|
||||||
|
(i18n/label :t/custom-seed-phrase-text-2)]
|
||||||
|
(i18n/label :t/custom-seed-phrase-text-3)
|
||||||
|
[{:style {:color colors/black}}
|
||||||
|
(i18n/label :t/custom-seed-phrase-text-4)]]]
|
||||||
|
[react/view {:margin-vertical 24
|
||||||
|
:align-items :center}
|
||||||
|
[button/button {:on-press #(re-frame/dispatch [::multiaccounts.recover/continue-pressed])
|
||||||
|
:accessibility-label :continue-custom-seed-phrase
|
||||||
|
:label (i18n/label :t/continue)}]
|
||||||
|
[button/button {:on-press #(re-frame/dispatch [:hide-popover])
|
||||||
|
:accessibility-label :cancel-custom-seed-phrase
|
||||||
|
:type :secondary
|
||||||
|
:label (i18n/label :t/cancel)}]]]])
|
||||||
|
|
||||||
(defn bottom-sheet-view []
|
(defn bottom-sheet-view []
|
||||||
[react/view {:flex 1 :flex-direction :row}
|
[react/view {:flex 1 :flex-direction :row}
|
||||||
|
@ -37,7 +74,16 @@
|
||||||
:title :t/recover-with-keycard
|
:title :t/recover-with-keycard
|
||||||
:disabled? (not config/hardwallet-enabled?)
|
:disabled? (not config/hardwallet-enabled?)
|
||||||
:accessibility-label :recover-with-keycard-button
|
:accessibility-label :recover-with-keycard-button
|
||||||
:icon :main-icons/keycard-logo
|
:icon [react/view {:border-width 1
|
||||||
|
:border-radius 20
|
||||||
|
:border-color colors/blue-light
|
||||||
|
:background-color colors/blue-light
|
||||||
|
:justify-content :center
|
||||||
|
:align-items :center
|
||||||
|
:width 40
|
||||||
|
:height 40}
|
||||||
|
[react/image {:source (resources/get-image :keycard-logo-blue)
|
||||||
|
:style {:width 24 :height 24}}]]
|
||||||
:on-press #(re-frame/dispatch [::hardwallet/recover-with-keycard-pressed])}])]])
|
:on-press #(re-frame/dispatch [::hardwallet/recover-with-keycard-pressed])}])]])
|
||||||
|
|
||||||
(def bottom-sheet
|
(def bottom-sheet
|
||||||
|
@ -76,7 +122,6 @@
|
||||||
[text-input/text-input-with-label
|
[text-input/text-input-with-label
|
||||||
{:on-change-text #(re-frame/dispatch [::multiaccounts.recover/enter-phrase-input-changed (security/mask-data %)])
|
{:on-change-text #(re-frame/dispatch [::multiaccounts.recover/enter-phrase-input-changed (security/mask-data %)])
|
||||||
:auto-focus true
|
:auto-focus true
|
||||||
:on-submit-editing #(re-frame/dispatch [::multiaccounts.recover/enter-phrase-input-submitted])
|
|
||||||
:error (when passphrase-error (i18n/label passphrase-error))
|
:error (when passphrase-error (i18n/label passphrase-error))
|
||||||
:placeholder nil
|
:placeholder nil
|
||||||
:height 120
|
:height 120
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[status-im.ui.screens.wallet.signing-phrase.views :as signing-phrase]
|
[status-im.ui.screens.wallet.signing-phrase.views :as signing-phrase]
|
||||||
[status-im.ui.screens.wallet.request.views :as request]
|
[status-im.ui.screens.wallet.request.views :as request]
|
||||||
[status-im.ui.screens.profile.user.views :as profile.user]))
|
[status-im.ui.screens.profile.user.views :as profile.user]
|
||||||
|
[status-im.ui.screens.multiaccounts.recover.views :as multiaccounts.recover]))
|
||||||
|
|
||||||
(defn hide-panel-anim
|
(defn hide-panel-anim
|
||||||
[bottom-anim-value alpha-value window-height]
|
[bottom-anim-value alpha-value window-height]
|
||||||
|
@ -98,6 +99,9 @@
|
||||||
(= :share-chat-key view)
|
(= :share-chat-key view)
|
||||||
[profile.user/share-chat-key]
|
[profile.user/share-chat-key]
|
||||||
|
|
||||||
|
(= :custom-seed-phrase view)
|
||||||
|
[multiaccounts.recover/custom-seed-phrase]
|
||||||
|
|
||||||
:else
|
:else
|
||||||
[view])]]]]])))})))
|
[view])]]]]])))})))
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ from tests import marks, unique_password
|
||||||
from tests.base_test_case import SingleDeviceTestCase
|
from tests.base_test_case import SingleDeviceTestCase
|
||||||
from tests.users import basic_user, transaction_senders, recovery_users
|
from tests.users import basic_user, transaction_senders, recovery_users
|
||||||
from views.sign_in_view import SignInView
|
from views.sign_in_view import SignInView
|
||||||
|
from views.recover_access_view import RecoverAccessView
|
||||||
|
|
||||||
|
|
||||||
@marks.all
|
@marks.all
|
||||||
|
@ -71,79 +72,91 @@ class TestRecoverAccessFromSignInScreen(SingleDeviceTestCase):
|
||||||
@marks.high
|
@marks.high
|
||||||
def test_pass_phrase_validation(self):
|
def test_pass_phrase_validation(self):
|
||||||
signin_view = SignInView(self.driver)
|
signin_view = SignInView(self.driver)
|
||||||
recover_access_view = signin_view.access_key_button.click()
|
signin_view.access_key_button.click()
|
||||||
|
recover_access_view = RecoverAccessView(self.driver)
|
||||||
phrase_outside_the_mnemonic = 'one two three four five six seven eight nine ten eleven twelve'
|
|
||||||
validations = [
|
validations = [
|
||||||
|
# empty value
|
||||||
{
|
{
|
||||||
'phrase': ' ',
|
'phrase': ' ',
|
||||||
'element to check': recover_access_view.warnings.required_field,
|
'element to check': recover_access_view.warnings.invalid_recovery_phrase,
|
||||||
'validation message': 'Required field',
|
'validation message': 'Required field',
|
||||||
|
'words count': 1,
|
||||||
|
'popup': False
|
||||||
},
|
},
|
||||||
|
# invalid seed phrase
|
||||||
{
|
{
|
||||||
'phrase': 'a',
|
'phrase': 'a',
|
||||||
'element to check': recover_access_view.warnings.invalid_recovery_phrase,
|
'element to check': recover_access_view.warnings.invalid_recovery_phrase,
|
||||||
'validation message': 'Recovery phrase is invalid'
|
'validation message': 'Seed phrase is invalid',
|
||||||
},
|
'words count': 1,
|
||||||
{
|
'popup' : False
|
||||||
'phrase': 'one two three four five six seven eight nine ten eleven twelve thirteen',
|
|
||||||
'element to check': recover_access_view.warnings.invalid_recovery_phrase,
|
|
||||||
'validation message': 'Recovery phrase is invalid'
|
|
||||||
},
|
},
|
||||||
|
# outside mnemonic
|
||||||
{
|
{
|
||||||
'phrase': '; two three four five six seven eight nine ten eleven twelve',
|
'phrase': '; two three four five six seven eight nine ten eleven twelve',
|
||||||
'element to check': recover_access_view.warnings.invalid_recovery_phrase,
|
'element to check': recover_access_view.warnings.invalid_recovery_phrase,
|
||||||
'validation message': 'Recovery phrase is invalid'
|
'validation message': '',
|
||||||
},
|
'words count': 12,
|
||||||
{
|
'popup': True
|
||||||
'phrase': phrase_outside_the_mnemonic,
|
|
||||||
'element to check': recover_access_view.warnings.misspelled_words,
|
|
||||||
'validation message': 'Some words might be misspelled'
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# check that seed phrase is required (can't be empty)
|
||||||
|
recover_access_view.enter_seed_phrase_button.click()
|
||||||
|
recover_access_view.next_button.click()
|
||||||
|
if recover_access_view.reencrypt_your_key_button.is_element_displayed():
|
||||||
|
self.errors.append("Possible to create account with empty seed phrase")
|
||||||
|
|
||||||
# we're performing the same steps changing only phrase per attempt
|
# we're performing the same steps changing only phrase per attempt
|
||||||
for validation in validations:
|
for validation in validations:
|
||||||
phrase, elm, msg = validation.get('phrase'), validation.get('element to check'), validation.get(
|
phrase, elm, msg, words_count, popup = validation.get('phrase'), \
|
||||||
'validation message')
|
validation.get('element to check'), \
|
||||||
|
validation.get('validation message'), \
|
||||||
|
validation.get('words count'),\
|
||||||
|
validation.get('popup')
|
||||||
if signin_view.access_key_button.is_element_displayed():
|
if signin_view.access_key_button.is_element_displayed():
|
||||||
signin_view.access_key_button.click()
|
signin_view.access_key_button.click()
|
||||||
if recover_access_view.enter_seed_phrase_button.is_element_displayed():
|
if recover_access_view.enter_seed_phrase_button.is_element_displayed():
|
||||||
recover_access_view.enter_seed_phrase_button.click()
|
recover_access_view.enter_seed_phrase_button.click()
|
||||||
|
|
||||||
recover_access_view.send_as_keyevent(phrase)
|
recover_access_view.send_as_keyevent(phrase)
|
||||||
recover_access_view.next_button.click()
|
|
||||||
|
|
||||||
if not elm.is_element_displayed():
|
# TODO: uncomment after 8567 fix
|
||||||
self.errors.append('"{}" message is not shown'.format(msg))
|
#if msg and not elm.is_element_displayed():
|
||||||
|
# self.errors.append('"{}" message is not shown'.format(msg))
|
||||||
|
|
||||||
|
# check that words count is shown
|
||||||
|
if words_count == 1:
|
||||||
|
if not signin_view.element_by_text('%s word' % words_count):
|
||||||
|
self.errors.append('"%s word" is not shown ' % words_count)
|
||||||
|
else:
|
||||||
|
if not signin_view.element_by_text('%s words' % words_count):
|
||||||
|
self.errors.append('"%s words" is not shown ' % words_count)
|
||||||
|
|
||||||
|
# check that "Next" is disabled unless we use allowed count of words
|
||||||
|
if words_count != 12 or 15 or 18 or 21 or 24:
|
||||||
|
recover_access_view.next_button.click()
|
||||||
|
if recover_access_view.reencrypt_your_key_button.is_element_displayed():
|
||||||
|
self.errors.append("Possible to create account with wrong count (%s) of words" % words_count)
|
||||||
|
|
||||||
|
# check behavior for popup "Custom seed phrase"
|
||||||
|
if popup:
|
||||||
|
text = 'some words are misspelled.'
|
||||||
|
common_password = 'qwerty'
|
||||||
|
if not recover_access_view.find_full_text(text):
|
||||||
|
self.errors.append('"%s" text is not shown' % text)
|
||||||
|
recover_access_view.cancel_custom_seed_phrase_button.click()
|
||||||
|
recover_access_view.next_button.click()
|
||||||
|
recover_access_view.continue_custom_seed_phrase_button.click()
|
||||||
|
recover_access_view.reencrypt_your_key_button.click()
|
||||||
|
recover_access_view.next_button.click()
|
||||||
|
recover_access_view.create_password_input.set_value(common_password)
|
||||||
|
recover_access_view.next_button.click()
|
||||||
|
recover_access_view.confirm_your_password_input.set_value(common_password)
|
||||||
|
recover_access_view.next_button.click_until_presence_of_element(recover_access_view.home_button)
|
||||||
|
else:
|
||||||
recover_access_view.click_system_back_button()
|
recover_access_view.click_system_back_button()
|
||||||
|
|
||||||
signin_view.access_key_button.click()
|
|
||||||
recover_access_view.enter_seed_phrase_button.click()
|
|
||||||
recover_access_view.send_as_keyevent(phrase_outside_the_mnemonic)
|
|
||||||
recover_access_view.next_button.click()
|
|
||||||
recover_access_view.recover_account_password_input.click()
|
|
||||||
recover_access_view.send_as_keyevent('123456')
|
|
||||||
recover_access_view.sign_in_button.click()
|
|
||||||
recover_access_view.cancel_button.click()
|
|
||||||
|
|
||||||
if recover_access_view.cancel_button.is_element_displayed():
|
|
||||||
self.errors.append('Something went wrong. Probably, the confirmation pop up did not disappear')
|
|
||||||
|
|
||||||
recover_access_view.click_system_back_button()
|
|
||||||
|
|
||||||
signin_view.access_key_button.click()
|
|
||||||
recover_access_view.enter_seed_phrase_button.click()
|
|
||||||
recover_access_view.send_as_keyevent(phrase_outside_the_mnemonic)
|
|
||||||
recover_access_view.next_button.click()
|
|
||||||
recover_access_view.recover_account_password_input.click()
|
|
||||||
recover_access_view.send_as_keyevent('123456')
|
|
||||||
recover_access_view.sign_in_button.click()
|
|
||||||
home_view = recover_access_view.confirm_phrase_button.click()
|
|
||||||
|
|
||||||
if not home_view.profile_button.is_element_displayed():
|
|
||||||
self.errors.append('Something went wrong. Probably, could not reach the home screen out.')
|
|
||||||
|
|
||||||
self.verify_no_errors()
|
self.verify_no_errors()
|
||||||
|
|
||||||
@marks.testrail_id(5499)
|
@marks.testrail_id(5499)
|
||||||
|
|
|
@ -29,6 +29,18 @@ class ConfirmRecoverAccess(BaseButton):
|
||||||
super(ConfirmRecoverAccess, self).__init__(driver)
|
super(ConfirmRecoverAccess, self).__init__(driver)
|
||||||
self.locator = self.Locator.xpath_selector("//android.widget.TextView[@text='RECOVER ACCESS']")
|
self.locator = self.Locator.xpath_selector("//android.widget.TextView[@text='RECOVER ACCESS']")
|
||||||
|
|
||||||
|
class ContinueCustomSeedPhraseButton(BaseButton):
|
||||||
|
|
||||||
|
def __init__(self, driver):
|
||||||
|
super(ContinueCustomSeedPhraseButton, self).__init__(driver)
|
||||||
|
self.locator = self.Locator.accessibility_id("continue-custom-seed-phrase")
|
||||||
|
|
||||||
|
class CancelCustomSeedPhraseButton(BaseButton):
|
||||||
|
|
||||||
|
def __init__(self, driver):
|
||||||
|
super(CancelCustomSeedPhraseButton, self).__init__(driver)
|
||||||
|
self.locator = self.Locator.accessibility_id("cancel-custom-seed-phrase")
|
||||||
|
|
||||||
|
|
||||||
class RequiredField(BaseElement):
|
class RequiredField(BaseElement):
|
||||||
def __init__(self, driver):
|
def __init__(self, driver):
|
||||||
|
@ -92,3 +104,5 @@ class RecoverAccessView(SignInView):
|
||||||
self.warnings = Warnings(self.driver)
|
self.warnings = Warnings(self.driver)
|
||||||
self.confirm_phrase_button = ConfirmPhraseButton(self.driver)
|
self.confirm_phrase_button = ConfirmPhraseButton(self.driver)
|
||||||
self.cancel_button = CancelPhraseButton(self.driver)
|
self.cancel_button = CancelPhraseButton(self.driver)
|
||||||
|
self.continue_custom_seed_phrase_button = ContinueCustomSeedPhraseButton(self.driver)
|
||||||
|
self.cancel_custom_seed_phrase_button = CancelCustomSeedPhraseButton(self.driver)
|
||||||
|
|
|
@ -1095,5 +1095,10 @@
|
||||||
"your-data-belongs-to-you": "If you lose your seed phrase you lose your data and funds",
|
"your-data-belongs-to-you": "If you lose your seed phrase you lose your data and funds",
|
||||||
"your-data-belongs-to-you-description": "Status can’t help you recover your multiaccount if you lose your seed phrase. You are in charge of the security of your data, and backing up your seed phrase is the best safeguard.",
|
"your-data-belongs-to-you-description": "Status can’t help you recover your multiaccount if you lose your seed phrase. You are in charge of the security of your data, and backing up your seed phrase is the best safeguard.",
|
||||||
"your-recovery-phrase": "Your seed phrase",
|
"your-recovery-phrase": "Your seed phrase",
|
||||||
"your-recovery-phrase-description": "This is your seed phrase. You use it to prove that this is your wallet. You only get to see it once! Write it on paper and keep it in a secure place. You will need it if you lose or reinstall your wallet."
|
"your-recovery-phrase-description": "This is your seed phrase. You use it to prove that this is your wallet. You only get to see it once! Write it on paper and keep it in a secure place. You will need it if you lose or reinstall your wallet.",
|
||||||
|
"custom-seed-phrase": "Custom seed phrase",
|
||||||
|
"custom-seed-phrase-text-1": "This looks like a custom seed phrase and doesn't match the Status dictionary. This could also mean ",
|
||||||
|
"custom-seed-phrase-text-2": "some words are misspelled.",
|
||||||
|
"custom-seed-phrase-text-3": " If so, you'll end up creating a",
|
||||||
|
"custom-seed-phrase-text-4": " new account"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue