1
0
Fork 0

feat(srp): add a quiz to the SRP reveal (#19283)

* feat(srp): add a quiz to the SRP reveal

* fixed the popover header centering

* lint fixes

* converted from `ui/components/ui/popover` to `ui/components/component-library/modal`

* responded to @darkwing review

* added unit tests

* renamed the folder to 'srp-quiz-modal'

* responded to Monte's review

* using i18n-helper in the test suite

* small improvement to JSXDict comments

* wrote a new webdriver.holdMouseDownOnElement() to assist with testing the "Hold to reveal SRP" button

* Updating layout and some storybook naming and migrating to tsx

* Apply suggestions from @georgewrmarshall

Co-authored-by: George Marshall <george.marshall@consensys.net>

* Unit test searches by data-testid instead of by text

* new layout and copy for the Settings->Security page

* now with 100% test coverage for /ui/pages/settings/security-tab
fixes #16871
fixes #18140

* e2e tests to reveal SRP after quiz

* e2e- Fix lint, remove unneeded extras

* @coreyjanssen and @georgewrmarshall compromise

Co-authored-by: George Marshall <george.marshall@consensys.net>
Co-authored-by: Corey Janssen <corey.janssen@consensys.net>

* trying isRequired again

* transparent background on PNG

* [e2e] moving functions to helpers and adding testid for SRP reveal quiz (#19481)

* moving functions to helpers and adding testid

* fix lint error

* took out the IPFS gateway fixes

* lint fix

* translations of SRP Reveal Quiz

* new Spanish translation from Guto

* Update describe for e2e tests

* Apply suggestion from @georgewrmarshall

Co-authored-by: George Marshall <george.marshall@consensys.net>

* fixed the Tab key problem

---------

Co-authored-by: georgewrmarshall <george.marshall@consensys.net>
Co-authored-by: Plasma Corral <32695229+plasmacorral@users.noreply.github.com>
Co-authored-by: Corey Janssen <corey.janssen@consensys.net>
This commit is contained in:
Howard Braham 2023-06-20 11:27:10 -07:00 committed by GitHub
parent ba6a27130e
commit 9acd4b4ea1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1885 additions and 269 deletions

View File

@ -1562,6 +1562,12 @@
"message": "Betrüger aber schon.",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "Halten, um GWP anzuzeigen"
},
"holdToRevealSRPTitle": {
"message": "Bewahren Sie Ihre GWP sicher auf"
},
"ignoreAll": {
"message": "Alle ignorieren"
},
@ -3333,12 +3339,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "Das Einfügen schlug fehl, weil sie mehr als 24 Wörter enthielt. Eine geheime Wiederherstellungsphrase darf maximal 24 Wörter enthalten.",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "Sie können Ihre gesamte geheime Wiederherstellungsphrase in ein beliebiges Feld einfügen",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "Anfangen"
},
"srpSecurityQuizIntroduction": {
"message": "Zur Enthüllung Ihrer geheimen Wiederherstellungsphrase, müssen Sie zwei Fragen beantworten"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "Wenn Sie Ihre geheime Wiederherstellungsphrase verlieren, kann MetaMask ..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "Ihnen nicht helfen"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "Schreiben Sie sie auf, gravieren Sie sie in Metall ein oder bewahren Sie sie an mehreren geheimen Orten auf, damit Sie sie niemals verlieren. Sollten Sie sie verlieren, ist sie für immer weg."
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "Richtig! Niemand kann Ihnen dabei helfen, Ihre geheime Wiederherstellungsphrase zurückzubekommen"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "diese für Sie zurückzubekommen"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "Wenn Sie Ihre geheime Wiederherstellungsphrase verlieren, ist diese für immer verloren. Niemand kann Ihnen dabei helfen, sie zurückzubekommen, egal, was behauptet wird."
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "Falsch! Niemand kann Ihnen dabei helfen, Ihre geheime Wiederherstellungsphrase zurückzubekommen"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "Sollte jemand, selbst ein Support-Mitarbeiter, nach Ihrer geheimen Wiederherstellungsphrase fragen ..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "Werden Sie betrogen"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "Jeder, der behauptet, Ihre gemeine Wiederherstellungsphrase zu benötigen, lügt Sie an. Wenn Sie diese mit solchen Personen teilen, werden diese Ihre Vermögenswerte stehlen."
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "Richtig! Es ist nie eine gute Idee, Ihre geheime Wiederherstellungsphrase mit anderen zu teilen"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "Sie sollten sie dieser Person geben"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "Jeder, der behauptet, Ihre gemeine Wiederherstellungsphrase zu benötigen, lügt Sie an. Wenn Sie diese mit einer solchen Person teilen, wird sie Ihre Vermögenswerte stehlen."
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "Nein! Teilen Sie Ihre geheime Wiederherstellungsphrase mit niemandem, niemals"
},
"srpSecurityQuizTitle": {
"message": "Sicherheits-Quiz"
},
"srpToggleShow": {
"message": "Dieses Wort der geheimen Wiederherstellungsphrase anzeigen/ausblenden",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -1562,6 +1562,12 @@
"message": "αλλά οι απατεώνες μπορεί να το κάνουν.",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "Κρατήστε το πατημένο για να αποκαλυφθεί το ΜΦΑ"
},
"holdToRevealSRPTitle": {
"message": "Κρατήστε το ΜΦΑ σας ασφαλές"
},
"ignoreAll": {
"message": "Αγνόηση όλων"
},
@ -3330,12 +3336,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "Η επικόλληση απέτυχε επειδή περιείχε περισσότερες από 24 λέξεις. Μια μυστική φράση ανάκτησης μπορεί να αποτελείται από το πολύ 24 λέξεις.",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "Μπορείτε να επικολλήσετε ολόκληρη τη μυστική φράση ανάκτησής σας σε οποιοδήποτε πεδίο",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "Ξεκινήστε"
},
"srpSecurityQuizIntroduction": {
"message": "Για να σας αποκαλύψουμε τη Μυστική Φράση Ανάκτησης, πρέπει να απαντήσετε σωστά σε δύο ερωτήσεις"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "Αν χάσετε τη Μυστική Φράση Ανάκτησης, το MetaMask..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "Δεν μπορεί να σας βοηθήσει"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "Γράψτε την κάπου, χαράξτε την πάνω σε μέταλλο ή κρατήστε την σε πολλά μυστικά σημεία για να μην την χάσετε ποτέ. Αν την χάσετε, θα χαθεί για πάντα."
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "Σωστά! Κανείς δεν μπορεί να σας βοηθήσει να επαναφέρετε τη Μυστική Φράση Ανάκτησης"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "Μπορεί να την επαναφέρει για εσάς"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "Αν χάσετε τη Μυστική Φράση Ανάκτησης, θα την χάσετε για πάντα. Κανείς δεν μπορεί να σας βοηθήσει να την επαναφέρετε, ό,τι κι αν σας πει."
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "Λάθος! Κανείς δεν μπορεί να σας βοηθήσει να επαναφέρετε τη Μυστική Φράση Ανάκτησης"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "Αν κάποιος, ακόμα και ένας τεχνικός υποστήριξης, σας ζητήσει τη Μυστική Φράση Ανάκτησης..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "Σας έχουν εξαπατήσει"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "Όποιος ισχυριστεί ότι χρειάζεται τη Μυστική Φράση Ανάκτησης, σας λέει ψέματα. Αν την μοιραστείτε μαζί του, θα κλέψει τα περιουσιακά σας στοιχεία."
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "Σωστά! Το να μοιράζεστε τη Μυστική Φράση Ανάκτησης δεν είναι καλή ιδέα"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "Πρέπει να τους την δώσετε"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "Όποιος ισχυριστεί ότι χρειάζεται τη Μυστική Φράση Ανάκτησης, σας λέει ψέματα. Αν την μοιραστείτε μαζί του, θα κλέψει τα περιουσιακά σας στοιχεία."
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "Όχι! Ποτέ μα ποτέ μην μοιραστείτε με κανέναν τη Μυστική Φράση Ανάκτησης"
},
"srpSecurityQuizTitle": {
"message": "Κουίζ Ασφαλείας"
},
"srpToggleShow": {
"message": "Εμφάνιση/Απόκρυψη αυτής της λέξης από τη μυστική φράση ανάκτησης",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -4024,12 +4024,66 @@
},
"srpPasteFailedTooManyWords": {
"message": "Paste failed because it contained over 24 words. A secret recovery phrase can have a maximum of 24 words.",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "You can paste your entire secret recovery phrase into any field",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "Get started"
},
"srpSecurityQuizImgAlt": {
"message": "An eye with a keyhole in the center, and three floating password fields"
},
"srpSecurityQuizIntroduction": {
"message": "To reveal your Secret Recovery Phrase, you need to correctly answer two questions"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "If you lose your Secret Recovery Phrase, MetaMask..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "Cant help you"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "Write it down, engrave it on metal, or keep it in multiple secret spots so you never lose it. If you lose it, its gone forever."
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "Right! No one can help get your Secret Recovery Phrase back"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "Can get it back for you"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "If you lose your Secret Recovery Phrase, its gone forever. No one can help you get it back, no matter what they might say."
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "Wrong! No one can help get your Secret Recovery Phrase back"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "If anyone, even a support agent, asks for your Secret Recovery Phrase..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "Youre being scammed"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "Anyone claiming to need your Secret Recovery Phrase is lying to you. If you share it with them, they will steal your assets."
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "Correct! Sharing your Secret Recovery Phrase is never a good idea"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "You should give it to them"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "Anyone claiming to need your Secret Recovery Phrase is lying to you. If you share it with them, they will steal your assets."
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "Nope! Never share your Secret Recovery Phrase with anyone, ever"
},
"srpSecurityQuizTitle": {
"message": "Security quiz"
},
"srpToggleShow": {
"message": "Show/Hide this word of the secret recovery phrase",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -1562,6 +1562,12 @@
"message": "pero los defraudadores sí.",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "Mantén presionado para revelar su SRP"
},
"holdToRevealSRPTitle": {
"message": "Proteja su SRP"
},
"ignoreAll": {
"message": "Ignorar todo"
},
@ -3333,12 +3339,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "Pegar falló porque contenía más de 24 palabras. Una frase de recuperación secreta puede tener un máximo de 24 palabras.",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "Puede pegar toda su frase secreta de recuperación en cualquier campo",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "Iniciar"
},
"srpSecurityQuizIntroduction": {
"message": "Para revelar su frase secreta de recuperación, debe responder correctamente dos preguntas"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "Si extravía su frase secreta de recuperación, MetaMask..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "No puede ayudarlo"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "Anótela, grábela en metal o guárdela en múltiples lugares secretos para que nunca la pierda. Si la extravía, se ha ido para siempre."
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "¡Correcto! Nadie puede ayudarlo a recuperar su frase secreta de recuperación"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "Puede recuperarla para usted"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "Si pierde su frase secreta de recuperación, ésta desaparecerá para siempre. Nadie puede ayudarle a recuperarla, sin importar lo que digan."
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "¡Incorrecto! Nadie puede ayudarlo a recuperar su frase secreta de recuperación"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "Si alguien, incluso un agente de soporte, le pide su frase secreta de recuperación..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "Lo están estafando"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "Cualquiera que afirme necesitar su frase secreta de recuperación le está mintiendo. Si la comparte, le robarán sus activos."
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "¡Correcto! Compartir su frase secreta de recuperación nunca es una buena idea"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "Debiera brindársela"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "Cualquiera que afirme necesitar su frase secreta de recuperación le está mintiendo. Si la comparte, le robarán sus activos."
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "¡No! Nunca comparta su frase secreta de recuperación con nadie, nunca"
},
"srpSecurityQuizTitle": {
"message": "Cuestionario de seguridad"
},
"srpToggleShow": {
"message": "Mostrar/Ocultar esta palabra de la frase secreta de recuperación",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -1562,6 +1562,12 @@
"message": "mais les hameçonneurs pourraient le faire.",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "Appuyez longuement pour révéler PSR"
},
"holdToRevealSRPTitle": {
"message": "Protégez votre PSR"
},
"ignoreAll": {
"message": "Ignorer tout"
},
@ -3333,12 +3339,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "Le collage a échoué parce que la phrase contenait plus de 24 mots. Une phrase secrète de récupération peut contenir un maximum de 24 mots.",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "Vous pouvez coller toute votre phrase de récupération secrète dans nimporte quel champ",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "Commencer"
},
"srpSecurityQuizIntroduction": {
"message": "Pour révéler votre Phrase secrète de récupération, vous devez répondre correctement à deux questions"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "Si vous perdez votre Phrase secrète de récupération, MetaMask..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "Ne pourra pas vous aider"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "Gravez-la sur une plaque en métal ou inscrivez-la sur plusieurs bouts de papier et cachez-les dans différents endroits secrets pour ne jamais la perdre. Si vous la perdez, il n'y a aucun moyen de la récupérer."
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "En effet ! Personne ne peut vous aider à récupérer votre Phrase secrète de récupération."
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "Pourra la récupérer pour vous"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "Personne ne peut vous aider à récupérer votre phrase secrète de récupération si jamais vous la perdez."
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "C'est faux ! Personne ne peut vous aider à récupérer votre Phrase secrète de récupération."
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "Si un membre du service d'assistance ou toute autre personne vous demande votre Phrase secrète de récupération..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "Ne la lui fournissez pas, car cette personne essaie de vous arnaquer."
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "Toute personne qui vous demande votre phrase secrète de récupération, que ce soit pour des raisons de sécurité ou autre, essaie de vous arnaquer."
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "C'est exact ! Vous ne devez jamais partager votre Phrase secrète de récupération avec qui que ce soit."
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "Vous devez la lui fournir"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "Toute personne qui vous demande votre phrase secrète de récupération, que ce soit pour des raisons de sécurité ou autre, essaie de vous arnaquer."
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "C'est faux ! Vous ne devez jamais partager votre Phrase secrète de récupération avec qui que ce soit."
},
"srpSecurityQuizTitle": {
"message": "Quiz sur la sécurité"
},
"srpToggleShow": {
"message": "Afficher / Masquer ce mot de la phrase de récupération secrète",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -1562,6 +1562,12 @@
"message": "लेकिन फिशर कर सकते हैं।",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "SRP दिखाने के लिए होल्ड करें"
},
"holdToRevealSRPTitle": {
"message": "अपना SRP सुरक्षित रखें"
},
"ignoreAll": {
"message": "सभी को अनदेखा करें"
},
@ -3333,12 +3339,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "पेस्ट विफल हुआ क्योंकि उसमें 24 से ज़्यादा शब्द हैं। सीक्रेट रिकवरी फ़्रेज़ में अधिकतम 24 शब्द हो सकते हैं।",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "आप अपना पूरा सीक्रेट रिकवरी फ़्रेज किसी भी फ़ील्ड में पेस्ट कर सकते हैं",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "शुरू करें"
},
"srpSecurityQuizIntroduction": {
"message": "अपना सीक्रेट रिकवरी फ्रेज़ प्रकट करने के लिए, आपको दो प्रश्नों का सही उत्तर देना होगा"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "यदि आप अपना सीक्रेट रिकवरी फ्रेज़ खो देते हैं, तो MetaMask..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "आपकी मदद नहीं कर सकता"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "इसे लिख लें, इसे किसी धातु पर उकेर दें, या इसे कई गुप्त स्थानों पर रखें ताकि आप इसे कभी न खोएं। यदि आप इसे खो देते हैं, तो यह हमेशा के लिए चला जाता है।"
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "सही! आपके सीक्रेट रिकवरी फ्रेज़ को वापस पाने में कोई भी सहायता नहीं कर सकता"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "आपके लिए इसे वापस ला सकते हैं"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "यदि आप अपना सीक्रेट रिकवरी फ्रेज़ खो देते हैं, तो यह हमेशा के लिए चला जाता है। इसे वापस पाने में कोई भी आपकी मदद नहीं कर सकता, चाहे वे कुछ भी कहें।"
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "गलत! आपके सीक्रेट रिकवरी फ्रेज़ को वापस पाने में कोई भी सहायता नहीं कर सकता"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "यदि कोई, यहां तक कि एक सहायक एजेंट भी, आपका सीक्रेट रिकवरी फ्रेज़ मांगता है..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "तो आपके साथ धोखा किया जा रहा है"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "आपके सीक्रेट रिकवरी फ्रेज़ की आवश्यकता का दावा करने वाला कोई भी व्यक्ति आपसे झूठ बोल रहा है। यदि आप इसे उनके साथ साझा करते हैं, तो वे आपकी संपत्ति चुरा लेंगे।"
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "सही! अपना सीक्रेट रिकवरी फ्रेज़ साझा करना कभी भी अच्छा विचार नहीं है"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "आपको उन्हें यह देना चाहिए"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "आपके सीक्रेट रिकवरी फ्रेज़ की आवश्यकता का दावा करने वाला कोई भी व्यक्ति आपसे झूठ बोल रहा है। यदि आप इसे उनके साथ साझा करते हैं, तो वे आपकी संपत्तियां चुरा लेंगे।"
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "नहीं! अपने सीक्रेट रिकवरी फ्रेज़ को कभी भी किसी के साथ साझा न करें"
},
"srpSecurityQuizTitle": {
"message": "सुरक्षा प्रश्नोत्तरी"
},
"srpToggleShow": {
"message": "सीक्रेट रिकवरी फ़्रेज का ये शब्द दिखाएं/छुपाएं",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -1562,6 +1562,12 @@
"message": "tetapi penipu akan mencoba memintanya.",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "Tahan untuk mengungkap FPR"
},
"holdToRevealSRPTitle": {
"message": "Jaga keamanan FPR Anda"
},
"ignoreAll": {
"message": "Abaikan semua"
},
@ -3333,12 +3339,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "Gagal ditempel karena memuat lebih dari 24 kata. Frasa pemulihan rahasia dapat memuat maksimum 24 kata.",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "Anda bisa menempelkan seluruh frasa pemulihan rahasia ke bagian mana pun",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "Mulai"
},
"srpSecurityQuizIntroduction": {
"message": "Untuk mengungkapkan Frasa Pemulihan Rahasia, Anda perlu menjawab dua pertanyaan dengan benar"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "Jika Anda kehilangan Frasa Pemulihan Rahasia, MetaMask..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "Tidak dapat membantu Anda"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "Catat, ukir pada logam, atau simpan di beberapa tempat rahasia agar Anda tidak pernah kehilangan. Jika Anda kehilangan, maka akan hilang selamanya."
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "Benar! Tidak ada yang dapat membantu mengembalikan Frasa Pemulihan Rahasia Anda"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "Dapat mengembalikannya untuk Anda"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "Jika Anda kehilangan Frasa Pemulihan Rahasia, maka akan hilang selamanya. Tidak ada yang dapat membantu Anda mengembalikannya, apa pun yang mereka katakan."
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "Salah! Tidak ada yang dapat membantu mengembalikan Frasa Pemulihan Rahasia Anda"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "Jika ada yang menanyakan Frasa Pemulihan Rahasia Anda, bahkan agen pendukung,..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "Anda ditipu"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "Siapa pun yang mengaku membutuhkan Frasa Pemulihan Rahasia, mereka berbohong kepada Anda. Jika membaginya, mereka akan mencuri aset Anda."
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "Benar! Membagikan Frasa Pemulihan Rahasia bukanlah ide yang baik"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "Anda harus memberikannya kepada mereka"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "Siapa pun yang mengaku membutuhkan Frasa Pemulihan Rahasia, mereka berbohong kepada Anda. Jika membaginya, mereka akan mencuri aset Anda."
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "Tidak! Jangan pernah membagikan Frasa Pemulihan Rahasia kepada siapa pun"
},
"srpSecurityQuizTitle": {
"message": "Kuis keamanan"
},
"srpToggleShow": {
"message": "Tampilkan/Sembunyikan kata dari frasa pemulihan rahasia ini",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -1562,6 +1562,12 @@
"message": "もし尋ねられた場合はフィッシング詐欺の可能性があります。",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "長押ししてSRPを表示"
},
"holdToRevealSRPTitle": {
"message": "SRPは安全に保管してください"
},
"ignoreAll": {
"message": "すべて無視"
},
@ -3333,12 +3339,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "24 を超える単語が含まれていたため、貼り付けに失敗しました。秘密のリカバリーフレーズは 24 語までです。",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "秘密のリカバリーフレーズ全体をいずれかのフィールドに張り付けできます。",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "開始"
},
"srpSecurityQuizIntroduction": {
"message": "秘密のリカバリーフレーズを表示するには、2 つの質問に正しく答える必要があります。"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "秘密のリカバリーフレーズをなくした場合、MetaMask は..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "どうすることもできません"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "書き留めたり金属に掘ったり、いくつかの秘密の場所に保管したりして、絶対になくさないようにしてください。なくした場合、一生戻ってきません。"
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "正解です!秘密のリカバリーフレーズは誰にも取り戻すことができません"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "それを取り戻すことができます"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "秘密のリカバリーフレーズをなくした場合、一生戻ってきません。誰が何と言おうと、誰にも取り戻すことはできません。"
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "不正解!秘密のリカバリーフレーズは誰にも取り戻せません"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "誰かに秘密のリカバリーフレーズを尋ねられたら、それがサポート担当者であっても..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "あなたは騙されようとしています"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "秘密のリカバリーフレーズが必要だと言われたら、それは嘘です。教えてしまったら資産を盗まれます。"
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "正解です!秘密のリカバリーフレーズは決して誰にも教えてはいけません"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "教えるべきです"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "秘密のリカバリーフレーズが必要だと言われたら、それは嘘です。教えてしまったら資産を盗まれます。"
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "不正解!秘密のリカバリーフレーズは決して誰にも教えないでください"
},
"srpSecurityQuizTitle": {
"message": "セキュリティの質問"
},
"srpToggleShow": {
"message": "秘密のリカバリーフレーズのこの単語を表示・非表示",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -1562,6 +1562,12 @@
"message": "오히려 피싱 사기꾼들이 요구할 수 있으니 주의가 필요합니다.",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "눌러서 SRP 정보를 확인하세요"
},
"holdToRevealSRPTitle": {
"message": "SRP 정보를 안전하게 보관하세요"
},
"ignoreAll": {
"message": "모두 무시"
},
@ -3333,12 +3339,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "단어가 24개를 초과하여 붙여넣기에 실패했습니다. 비밀 복구 구문은 24개 이하의 단어로 이루어집니다.",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "비밀 복구 구문 전체를 아무 입력란에 붙여넣을 수 있습니다",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "시작하기"
},
"srpSecurityQuizIntroduction": {
"message": "비밀 복구 구문을 찾으려면 두 가지 질문에 올바르게 답해야 합니다"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "비밀 복구 구문을 분실하시면 MetaMask가..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "도와드릴 수 없습니다"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "따라서 이를 적거나, 금속 등에 새기거나, 여러 비밀 장소에 보관하여 절대로 잃어버리지 않도록 하세요. 한 번 잃어버리면 영원히 찾을 수 없습니다."
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "맞습니다! 아무도 본인의 비밀 복구 구문을 복구할 수 없습니다"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "찾아드릴 수 있습니다"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "비밀 복구 구문은 한 번 잃어버리면 영원히 찾을 수 없습니다. 누가 뭐라고 해도 아무도 이를 찾아드리지 못합니다."
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "아닙니다! 아무도 본인의 비밀 복구 구문을 복구할 수 없습니다"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "누군가, 심지어 고객 센터 직원이라고 해도 여러분의 비밀 복구 구문을 물어본다면..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "이는 반드시 사기입니다"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "비밀 복구 구문이 필요하다고 하는 사람은 모두 거짓말쟁이입니다. 그런 자들과 비밀 복구 구문을 공유하면 자산을 도둑맞게 됩니다."
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "맞습니다! 비밀 복구 구문은 아무와도 공유하면 안 됩니다"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "주어야 합니다"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "비밀 복구 구문이 필요하다고 하는 사람은 모두 거짓말쟁이입니다. 그런 자들과 비밀 복구 구문을 공유하면 자산을 도둑맞게 됩니다."
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "맞습니다! 비밀 복구 구문은 절대로 아무와도 공유하면 안 됩니다"
},
"srpSecurityQuizTitle": {
"message": "보안 퀴즈"
},
"srpToggleShow": {
"message": "비밀 복구 구문 중에서 이 단어 공개하기/숨기기",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -1562,6 +1562,12 @@
"message": "mas os phishers talvez solicitem.",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "Mantenha pressionado para revelar FRS"
},
"holdToRevealSRPTitle": {
"message": "Mantenha sua FRS protegida"
},
"ignoreAll": {
"message": "Ignorar tudo"
},
@ -3333,12 +3339,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "A função colar falhou porque continha mais de 24 palavras. Uma frase secreta de recuperação pode ter no máximo 24 palavras.",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "Você pode colar a sua frase secreta de recuperação inteira em qualquer campo",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "Começar"
},
"srpSecurityQuizIntroduction": {
"message": "Para revelar sua Frase de Recuperação Secreta, você precisa responder corretamente duas perguntas"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "Se você perder sua Frase de Recuperação Secreta, a MetaMask..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "Não poderá ajudar"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "Anote-a, grave em metal ou guarde-a em diversos lugares secretos para que nunca a perca. Se perdê-la, é para sempre."
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "Certo! Ninguém pode ajudar a recuperar sua Frase de Recuperação Secreta"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "Poderá recuperá-la para você"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "Se você perder sua Frase de Recuperação Secreta, é para sempre. Ninguém consegue ajudar a recuperá-la, não importa o que digam."
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "Errado! Ninguém consegue recuperar sua Frase de Recuperação Secreta"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "Se alguém, até mesmo um atendente do suporte, pedir sua Frase de Recuperação Secreta..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "Você estará sendo vítima de um golpe"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "Qualquer pessoa que afirme precisar da sua Frase de Recuperação Secreta está mentindo. Se você compartilhar com ela, seus ativos serão roubados."
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "Correto! Compartilhar sua Frase de Recuperação Secreta nunca é uma boa ideia"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "Você deverá revelar"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "Qualquer pessoa que afirme precisar da sua Frase de Recuperação Secreta está mentindo. Se você compartilhar com ela, seus ativos serão roubados."
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "Não! Não compartilhe sua Frase de Recuperação Secreta com ninguém, nunca"
},
"srpSecurityQuizTitle": {
"message": "Quiz de segurança"
},
"srpToggleShow": {
"message": "Mostrar/Ocultar esta palavra da frase secreta de recuperação",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -1562,6 +1562,12 @@
"message": "но злоумышленники-фишеры могут.",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "Удерживайте, чтобы показать СФВ"
},
"holdToRevealSRPTitle": {
"message": "Храните СФВ в безопасности"
},
"ignoreAll": {
"message": "Игнорировать все"
},
@ -3333,12 +3339,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "Не удалось вставить, так как он содержит более 24 слов. Секретная фраза для восстановления может содержать не более 24 слов.",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "Вы можете вставить всю свою секретную фразу для восстановления в любое поле",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "Начать"
},
"srpSecurityQuizIntroduction": {
"message": "Чтобы увидеть свою секретную фразу для восстановления, вам нужно правильно ответить на два вопроса"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "Если вы потеряете свою секретную фразу для восстановления, MetaMask..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "Не сможет вам помочь"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "Запишите ее, выгравируйте ее на металле или храните в нескольких потайных местах, чтобы никогда не потерять. Если вы потеряете ее, она пропадет навсегда."
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "Правильно! Никто не может помочь вернуть вашу секретную фразу для восстановления"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "Не сможет вернуть ее вам"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "Если вы потеряете свою секретную фразу для восстановления, она пропадет навсегда. Никто не может помочь вам вернуть ее, что бы кто ни говорил."
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "Неправильно! Никто не может помочь вернуть вашу секретную фразу для восстановления"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "Если кто-нибудь, даже представитель службы поддержки, попросит вашу секретную фразу для восстановления..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "Вас обманывают"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "Любой, кто утверждает, что ему нужна ваша секретная фраза для восстановления, лжет вам. Если вы сообщите эту фразу ему (ей), он(-а) украдет ваши активы."
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "Правильно! Сообщать кому-либо своей секретную фразу для восстановления — это всегда плохая идея"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "Вы должны сообщите фразу ему (ей)"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "Любой, кто утверждает, что ему нужна ваша секретная фраза для восстановления, лжет вам. Если вы сообщите эту фразу ему (ей), он(-а) украдет ваши активы."
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "Нет! Никогда никому не сообщайте никому свою секретную фразу для восстановления"
},
"srpSecurityQuizTitle": {
"message": "Тест по безопасности"
},
"srpToggleShow": {
"message": "Показать/скрыть это слово секретной фразы для восстановления",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -1562,6 +1562,12 @@
"message": "ngunit maaring hingin ng mga phisher.",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "I-hold para ipakita ang SRP"
},
"holdToRevealSRPTitle": {
"message": "Panatilihing ligtas ang iyong SRP"
},
"ignoreAll": {
"message": "Huwag pansinin ang lahat"
},
@ -3333,12 +3339,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "Nabigong i-paste dahil naglalaman ito ng higit sa 24 na salita. Ang secret recovery phrase ay mayroong hanggang 24 na salita lamang.",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "Maaari mong i-paste ang iyong buong secret recovery phrase sa alinmang patlang",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "Magsimula"
},
"srpSecurityQuizIntroduction": {
"message": "Upang ipakita ang iyong Secret Recovery Phrase, kailangan mong sagutin nang tama ang dalawang tanong"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "Kung mawala mo ang iyong Secret Recovery Phrase, ang MetaMask ay..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "Hindi ka matutulungan"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "Isulat ito, iukit sa metal, o itago ito sa maraming lihim na lugar upang hindi ito mawala. Kung nawala mo ito, wala na ito ng tuluyan."
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "Tama! Walang makakatulong na maibalik ang iyong Secret Recovery Phrase"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "Maaari itong ibalik para sa iyo"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "Kung nawala mo ang iyong Secret Recovery Phrase mawawala na ito nang tuluyan. Walang makakatulong sa iyo na maibalik ito, anuman ang maaaring sabihin nila."
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "Mali! Walang makakatulong na maibalik ang iyong Secret Recovery Phrase"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "Kung sinuman, kahit isang ahente ng suporta, ay humingi ng iyong Secret Recovery Phrase..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "Niloloko ka"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "Sinumang nagsasabing nangangailangan ng iyong Secret Recovery Phrase ay nagsisinungaling sa iyo. Kung ibabahagi mo ito sa kanila, nanakawin nila ng iyong mga ari-arian."
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "Tama! Ang pagbabahagi ng iyong Secret Recovery Phrase ay hindi kailanman isang magandang ideya"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "Dapat mong ibigay sa kanila"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "Sinumang nagsasabing nangangailangan ng iyong Secret Recovery Phrase ay nagsisinungaling sa iyo. Kung ibabahagi mo ito sa kanila, nanakawin nila ng iyong mga ari-arian."
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "Hindi! Huwag kailanman ibahagi ang iyong Secret Recovery Phrase sa sinuman, kailanman"
},
"srpSecurityQuizTitle": {
"message": "Pagsusulit sa seguridad"
},
"srpToggleShow": {
"message": "Ipakita/Itago ang salitang ito ng secret recovery phrase",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -1562,6 +1562,12 @@
"message": "ancak dolandırıcılar talep edilebilir.",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "GKİ bilgisinin gösterilmesi için tut"
},
"holdToRevealSRPTitle": {
"message": "GKİ bilgini güvende tut"
},
"ignoreAll": {
"message": "Tümünü yoksay"
},
@ -3333,12 +3339,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "24'ten fazla sözcük içerdiği için yapıştırma başarısız oldu. Gizli bir kurtarma ifadesi en fazla 24 sözcükten oluşabilir.",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "Gizli kurtarma ifadenin tamamını herhangi bir alana yapıştırabilirsin",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "Başla"
},
"srpSecurityQuizIntroduction": {
"message": "Gizli Kurtarma İfadenizi görmek için iki soruyu doğru cevaplamanız gerekmektedir"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "Gizli Kurtarma İfadenizi kaybederseniz MetaMask..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "Size yardımcı olamaz"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "Bir yere yazın, bir metalin üzerine kazıyın veya asla kaybetmemeniz için birden fazla noktada saklayın. Kaybederseniz sonsuza dek kaybolur."
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "Doğru! Hiç kimse Gizli Kurtarma İfadenizi geri almanıza yardımcı olamaz"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "Size onu tekrar verebilir"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "Gizli Kurtarma İfadenizi kaybederseniz sonsuza dek kaybolur. Söylediklerinin ne olduğuna bakılmaksızın hiç kimse onu geri almanıza yardımcı olamaz."
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "Yanlış! Hiç kimse Gizli Kurtarma İfadenizi geri almanıza yardımcı olamaz"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "Herhangi birisi, bir destek temsilcisi bile sizden Gizli Kurtarma İfadenizi isterse..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "Dolandırılıyorsunuzdur"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "Gizli Kurtarma İfadenizi isteyen kişi size yalan söylüyordur. Kendisi ile paylaşırsanız varlıklarınızı çalacaktır."
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "Doğru! Gizli Kurtarma İfadenizi paylaşmak asla iyi bir fikir değildir"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "Kendisine vermelisiniz"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "Gizli Kurtarma İfadenizi isteyen kişi size yalan söylüyordur. Kendisi ile paylaşırsanız varlıklarınızı çalacaktır."
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "Hayır! Gizli Kurtarma İfadenizi asla hiç kimse ile paylaşmayın, asla"
},
"srpSecurityQuizTitle": {
"message": "Güvenlik testi"
},
"srpToggleShow": {
"message": "Gizli kurtarma ifadesinin bu sözcüğünü göster/gizle",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -1562,6 +1562,12 @@
"message": "nhưng những kẻ lừa đảo qua mạng thì có.",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "Giữ để hiển thị SRP"
},
"holdToRevealSRPTitle": {
"message": "Đảm bảo an toàn cho SRP của bạn"
},
"ignoreAll": {
"message": "Bỏ qua tất cả"
},
@ -3333,12 +3339,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "Dán không thành công vì cụm từ có nhiều hơn 24 từ. Cụm từ khôi phục bí mật chỉ có tối đa 24 từ.",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "Bạn có thể dán toàn bộ cụm từ khôi phục bí mật vào bất kỳ trường nào",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "Bắt đầu"
},
"srpSecurityQuizIntroduction": {
"message": "Để hiển thị Cụm từ khôi phục bí mật, bạn cần trả lời đúng hai câu hỏi"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "Nếu bạn làm mất Cụm từ khôi phục bí mật, MetaMask..."
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "Không thể giúp bạn"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "Hãy viết ra, khắc lên kim loại hoặc cất giữ ở nhiều nơi bí mật để bạn không bao giờ làm mất nó. Nếu bạn làm mất, nó sẽ bị mất vĩnh viễn."
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "Đúng! Không ai có thể giúp bạn lấy lại Cụm từ khôi phục bí mật"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "Có thể lấy lại cho bạn"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "Nếu bạn làm mất Cụm từ khôi phục bí mật, nó sẽ bị mất vĩnh viễn. Dù mọi người có nói gì, thì cũng không ai có thể giúp bạn lấy lại."
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "Sai! Không ai có thể giúp bạn lấy lại Cụm từ khôi phục bí mật"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "Nếu có bất kỳ ai, kể cả nhân viên hỗ trợ, hỏi về Cụm từ khôi phục bí mật của bạn..."
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "Bạn đang bị lừa đảo"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "Bất kỳ ai nói rằng họ cần Cụm từ khôi phục bí mật của bạn thì đều đang nói dối bạn. Nếu bạn chia sẻ với họ thì họ sẽ đánh cắp tài sản của bạn."
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "Chính xác! Chia sẻ Cụm từ khôi phục bí mật chưa bao giờ là một ý hay"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "Bạn nên đưa nó cho họ"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "Bất kỳ ai nói rằng họ cần Cụm từ khôi phục bí mật của bạn thì đều đang nói dối bạn. Nếu bạn chia sẻ với họ thì họ sẽ đánh cắp tài sản của bạn."
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "Không! Tuyệt đối không bao giờ chia sẻ Cụm từ khôi phục bí mật của bạn với bất kỳ ai"
},
"srpSecurityQuizTitle": {
"message": "Câu hỏi bảo mật"
},
"srpToggleShow": {
"message": "Hiện/Ẩn từ này của cụm từ khôi phục bí mật",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

View File

@ -1562,6 +1562,12 @@
"message": "但网络钓鱼者可能会。",
"description": "The text link in 'holdToRevealContent3'"
},
"holdToRevealSRP": {
"message": "按住以显示 助记词"
},
"holdToRevealSRPTitle": {
"message": "保护您的 助记词 安全"
},
"ignoreAll": {
"message": "忽略所有"
},
@ -3333,12 +3339,63 @@
},
"srpPasteFailedTooManyWords": {
"message": "粘贴失败因为它包含超过24个单词。一个助记词最多可包含24个单词。",
"description": "Description of SRP paste erorr when the pasted content has too many words"
"description": "Description of SRP paste error when the pasted content has too many words"
},
"srpPasteTip": {
"message": "您可以将整个助记词粘贴到任何字段中",
"description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly."
},
"srpSecurityQuizGetStarted": {
"message": "开始"
},
"srpSecurityQuizIntroduction": {
"message": "要查看助记词,您需要答对两个问题"
},
"srpSecurityQuizQuestionOneQuestion": {
"message": "如果您丢失了助记词MetaMask......"
},
"srpSecurityQuizQuestionOneRightAnswer": {
"message": "无法帮助您"
},
"srpSecurityQuizQuestionOneRightAnswerDescription": {
"message": "将它写下来、刻在金属上,或保存在多个秘密位置,这样您就不会丢失它。如果丢失了,它就会永远消失。"
},
"srpSecurityQuizQuestionOneRightAnswerTitle": {
"message": "答对了!没有人能够帮您找回您的助记词"
},
"srpSecurityQuizQuestionOneWrongAnswer": {
"message": "可以为您找回来"
},
"srpSecurityQuizQuestionOneWrongAnswerDescription": {
"message": "一旦遗失助记词,它将永远消失。无论他人如何保证,无人能够帮您找回。"
},
"srpSecurityQuizQuestionOneWrongAnswerTitle": {
"message": "答错了!没有人能够帮您找回您的助记词"
},
"srpSecurityQuizQuestionTwoQuestion": {
"message": "如果有人(即使是技术支持人员)查问您的助记词......"
},
"srpSecurityQuizQuestionTwoRightAnswer": {
"message": "就是在对您进行诈骗"
},
"srpSecurityQuizQuestionTwoRightAnswerDescription": {
"message": "任何声称需要您的助记词的人都在对您进行欺诈。如果您与他们分享助记词,他们就会偷窃您的资产。"
},
"srpSecurityQuizQuestionTwoRightAnswerTitle": {
"message": "答对了!分享您的助记词绝对不是个好主意"
},
"srpSecurityQuizQuestionTwoWrongAnswer": {
"message": "您应该交给他们"
},
"srpSecurityQuizQuestionTwoWrongAnswerDescription": {
"message": "任何声称需要您的助记词的人都在对您进行欺诈。如果您与他们分享助记词,他们就会偷窃您的资产。"
},
"srpSecurityQuizQuestionTwoWrongAnswerTitle": {
"message": "不!永远不要与任何人分享您的助记词"
},
"srpSecurityQuizTitle": {
"message": "安全问答"
},
"srpToggleShow": {
"message": "显示/隐藏助记词中的这个单词",
"description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase"

BIN
app/images/reveal-srp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -183,8 +183,10 @@ async function verifyEnglishLocale() {
[
'ui/**/*.js',
'ui/**/*.ts',
'ui/**/*.tsx',
'shared/**/*.js',
'shared/**/*.ts',
'shared/**/*.tsx',
'app/scripts/constants/**/*.js',
'app/scripts/constants/**/*.ts',
'app/scripts/platforms/**/*.js',

View File

@ -425,6 +425,7 @@
"@types/react": "^16.9.53",
"@types/react-dom": "^17.0.11",
"@types/react-redux": "^7.1.25",
"@types/react-router-dom": "^5.3.3",
"@types/remote-redux-devtools": "^0.5.5",
"@types/sass": "^1.43.1",
"@types/sinon": "^10.0.13",

View File

@ -4,6 +4,7 @@ const { promises: fs } = require('fs');
const BigNumber = require('bignumber.js');
const mockttp = require('mockttp');
const createStaticServer = require('../../development/create-static-server');
const { tEn } = require('../lib/i18n-helpers');
const { setupMocking } = require('./mock-e2e');
const Ganache = require('./ganache');
const FixtureServer = require('./fixture-server');
@ -384,6 +385,56 @@ const testSRPDropdownIterations = async (options, driver, iterations) => {
}
};
const passwordUnlockOpenSRPRevealQuiz = async (driver) => {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
// navigate settings to reveal SRP
await driver.clickElement('[data-testid="account-options-menu-button"]');
await driver.clickElement({ text: 'Settings', tag: 'div' });
await driver.clickElement({ text: 'Security & privacy', tag: 'div' });
await driver.clickElement('[data-testid="reveal-seed-words"]');
};
const completeSRPRevealQuiz = async (driver) => {
// start quiz
await driver.clickElement('[data-testid="srp-quiz-get-started"]');
// tap correct answer 1
await driver.clickElement('[data-testid="srp-quiz-right-answer"]');
// tap Continue 1
await driver.clickElement('[data-testid="srp-quiz-continue"]');
// tap correct answer 2
await driver.clickElement('[data-testid="srp-quiz-right-answer"]');
// tap Continue 2
await driver.clickElement('[data-testid="srp-quiz-continue"]');
};
const tapAndHoldToRevealSRP = async (driver) => {
await driver.holdMouseDownOnElement(
{
text: tEn('holdToRevealSRP'),
tag: 'span',
},
2000,
);
};
const closeSRPReveal = async (driver) => {
await driver.clickElement({
text: tEn('close'),
tag: 'button',
});
await driver.findVisibleElement({
text: tEn('tokens'),
tag: 'button',
});
};
const DAPP_URL = 'http://127.0.0.1:8080';
const DAPP_ONE_URL = 'http://127.0.0.1:8081';
@ -641,6 +692,10 @@ module.exports = {
completeImportSRPOnboardingFlow,
completeImportSRPOnboardingFlowWordByWord,
completeCreateNewWalletOnboardingFlow,
passwordUnlockOpenSRPRevealQuiz,
completeSRPRevealQuiz,
closeSRPReveal,
tapAndHoldToRevealSRP,
createDownloadFolder,
importWrongSRPOnboardingFlow,
testSRPDropdownIterations,

View File

@ -0,0 +1,135 @@
const { strict: assert } = require('assert');
const {
withFixtures,
passwordUnlockOpenSRPRevealQuiz,
completeSRPRevealQuiz,
tapAndHoldToRevealSRP,
closeSRPReveal,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
const { tEn } = require('../../lib/i18n-helpers');
describe('Reveal SRP through settings', function () {
const testPassword = 'correct horse battery staple';
const wrongTestPassword = 'test test test test';
const seedPhraseWords =
'spread raise short crane omit tent fringe mandate neglect detail suspect cradle';
it('should not reveal SRP text with incorrect password', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
title: this.test.title,
failOnConsoleError: false,
},
async ({ driver }) => {
await passwordUnlockOpenSRPRevealQuiz(driver);
await completeSRPRevealQuiz(driver);
await driver.fill('#password-box', wrongTestPassword);
await driver.press('#password-box', driver.Key.ENTER);
await driver.isElementPresent(
{
css: '.mm-help-text',
text: 'Incorrect password',
},
true,
);
},
);
});
it('completes quiz and reveals SRP text', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
title: this.test.title,
},
async ({ driver }) => {
await passwordUnlockOpenSRPRevealQuiz(driver);
await completeSRPRevealQuiz(driver);
// enter password
await driver.fill('#password-box', testPassword);
await driver.press('#password-box', driver.Key.ENTER);
await tapAndHoldToRevealSRP(driver);
// confirm SRP text matches expected
const displayedSRP = await driver.findVisibleElement(
'[data-testid="srp_text"]',
);
assert.equal(await displayedSRP.getText(), seedPhraseWords);
// copy SRP text to clipboard
await driver.clickElement({
text: tEn('copyToClipboard'),
tag: 'button',
});
await driver.findVisibleElement({
text: tEn('copiedExclamation'),
tag: 'button',
});
// confirm that CTA returns user to wallet view
await closeSRPReveal(driver);
},
);
});
it('completes quiz and reveals SRP QR after wrong answers', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
title: this.test.title,
},
async ({ driver }) => {
await passwordUnlockOpenSRPRevealQuiz(driver);
// start quiz
await driver.clickElement('[data-testid="srp-quiz-get-started"]');
// tap incorrect answer 1
await driver.clickElement('[data-testid="srp-quiz-wrong-answer"]');
// try again
await driver.clickElement('[data-testid="srp-quiz-try-again"]');
// tap correct answer 1
await driver.clickElement('[data-testid="srp-quiz-right-answer"]');
// tap Continue 1
await driver.clickElement('[data-testid="srp-quiz-continue"]');
// tap incorrect answer 2
await driver.clickElement('[data-testid="srp-quiz-wrong-answer"]');
// try again
await driver.clickElement('[data-testid="srp-quiz-try-again"]');
// tap correct answer 1
await driver.clickElement('[data-testid="srp-quiz-right-answer"]');
// tap Continue 2
await driver.clickElement('[data-testid="srp-quiz-continue"]');
// enter password
await driver.fill('#password-box', testPassword);
await driver.press('#password-box', driver.Key.ENTER);
// tap and hold to reveal
await tapAndHoldToRevealSRP(driver);
// confirm SRP QR is displayed
await driver.clickElement({
text: 'QR',
tag: 'button',
});
const qrCode = await driver.findElement('[data-testid="qr-srp"]');
assert.equal(await qrCode.isDisplayed(), true);
// confirm that CTA returns user to wallet view
await closeSRPReveal(driver);
},
);
});
});

View File

@ -261,6 +261,18 @@ class Driver {
.perform();
}
async holdMouseDownOnElement(rawLocator, ms) {
const locator = this.buildLocator(rawLocator);
const element = await this.findClickableElement(locator);
await this.driver
.actions()
.move({ origin: element, x: 1, y: 1 })
.press()
.pause(ms)
.release()
.perform();
}
async scrollToElement(element) {
await this.driver.executeScript(
'arguments[0].scrollIntoView(true)',

6
test/lib/i18n-helpers.js Normal file
View File

@ -0,0 +1,6 @@
import { getMessage } from '../../ui/helpers/utils/i18n-helper';
import * as en from '../../app/_locales/en/messages.json';
export function tEn(key) {
return getMessage('en', en, key);
}

View File

@ -89,7 +89,7 @@ export default function HoldToRevealButton({ buttonText, onLongPressed }) {
setHasTriggeredUnlock(true);
preventPropogation(e);
},
[onLongPressed],
[onLongPressed, trackEvent],
);
/**

View File

@ -2,10 +2,10 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../../store/actions';
import isMobileView from '../../../helpers/utils/is-mobile-view';
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
import isMobileView from '../../../helpers/utils/is-mobile-view';
import * as actions from '../../../store/actions';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { mmiActionsFactory } from '../../../store/institutional/institution-background';
///: END:ONLY_INCLUDE_IN
@ -13,31 +13,31 @@ import { mmiActionsFactory } from '../../../store/institutional/institution-back
// Modal Components
import AddNetworkModal from '../../../pages/onboarding-flow/add-network-modal';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import ComplianceDetailsModal from '../../institutional/compliance-details';
import ComplianceModal from '../../institutional/compliance-modal';
import ConfirmRemoveJWT from '../../institutional/confirm-remove-jwt-modal';
import TransactionFailed from '../../institutional/transaction-failed-modal';
import CustodyConfirmLink from '../../institutional/custody-confirm-link-modal';
import InteractiveReplacementTokenModal from '../../institutional/interactive-replacement-token-modal';
import ComplianceModal from '../../institutional/compliance-modal';
import ComplianceDetailsModal from '../../institutional/compliance-details';
import TransactionFailed from '../../institutional/transaction-failed-modal';
///: END:ONLY_INCLUDE_IN
import AccountDetailsModal from './account-details-modal';
import ExportPrivateKeyModal from './export-private-key-modal';
import HideTokenConfirmationModal from './hide-token-confirmation-modal';
import QRScanner from './qr-scanner';
import HoldToRevealModal from './hold-to-reveal-modal';
import ConfirmRemoveAccount from './confirm-remove-account';
import ConfirmResetAccount from './confirm-reset-account';
import HoldToRevealModal from './hold-to-reveal-modal';
import TransactionConfirmed from './transaction-confirmed';
import FadeModal from './fade-modal';
import RejectTransactions from './reject-transactions';
import ConfirmDeleteNetwork from './confirm-delete-network';
import EditApprovalPermission from './edit-approval-permission';
import NewAccountModal from './new-account-modal';
import CustomizeNonceModal from './customize-nonce';
import ConvertTokenToNftModal from './convert-token-to-nft-modal/convert-token-to-nft-modal';
import CustomizeNonceModal from './customize-nonce';
import EditApprovalPermission from './edit-approval-permission';
import EthSignModal from './eth-sign-modal/eth-sign-modal';
import FadeModal from './fade-modal';
import NewAccountModal from './new-account-modal';
import RejectTransactions from './reject-transactions';
const modalContainerBaseStyle = {
transform: 'translate3d(-50%, 0, 0px)',

View File

@ -0,0 +1,73 @@
import React from 'react';
import {
AlignItems,
BlockSize,
Display,
FlexDirection,
JustifyContent,
TextAlign,
TextVariant,
} from '../../../../helpers/constants/design-system';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import { Button, Text, Box } from '../../../component-library';
import { IQuizInformationProps } from '../types';
export default function QuizContent({
icon,
image,
content,
moreContent,
buttons,
}: IQuizInformationProps) {
const t = useI18nContext();
return (
<>
{icon && (
<Box
display={Display.Flex}
flexDirection={FlexDirection.Row}
alignItems={AlignItems.center}
justifyContent={JustifyContent.center}
>
{icon}
</Box>
)}
{image && (
<Box display={Display.Flex} margin="auto" textAlign={TextAlign.Center}>
<img
src={image}
alt={t('srpSecurityQuizImgAlt')}
width="300"
style={{ maxWidth: '100%' }} // should probably be in a className instead
/>
</Box>
)}
<Text
variant={TextVariant.bodyLgMedium}
textAlign={TextAlign.Center}
color={icon?.props.color} // Inherit this text color from the icon's color
>
{content}
</Text>
{moreContent && (
<Text variant={TextVariant.bodyMd} textAlign={TextAlign.Center}>
{moreContent}
</Text>
)}
{buttons.map((btn, idx) => (
<Button
key={idx}
size={btn.size}
onClick={btn.onClick}
label={btn.label}
variant={btn.variant}
width={BlockSize.Full}
data-testid={btn['data-testid']}
>
{btn.label}
</Button>
))}
</>
);
}

View File

@ -0,0 +1 @@
export { default } from './QuizContent';

View File

@ -0,0 +1,35 @@
import React from 'react';
import { StoryFn, Meta } from '@storybook/react';
import { useArgs } from '@storybook/client-api';
import { Button } from '../../../component-library';
import SRPQuiz from '.';
export default {
title: 'Components/App/SRPQuizModal',
component: SRPQuiz,
argTypes: {
isShowingModal: {
control: 'boolean',
},
},
} as Meta<typeof SRPQuiz>;
export const DefaultStory: StoryFn<typeof SRPQuiz> = () => {
const [{ isShowingModal }, updateArgs] = useArgs();
return (
<>
<Button onClick={() => updateArgs({ isShowingModal: true })}>
Open modal
</Button>
{isShowingModal && (
<SRPQuiz
isOpen={isShowingModal}
onClose={() => updateArgs({ isShowingModal: false })}
/>
)}
</>
);
};
DefaultStory.storyName = 'Default';

View File

@ -0,0 +1,95 @@
import { fireEvent, screen, waitFor } from '@testing-library/react';
import React from 'react';
import mockState from '../../../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../../../test/jest';
import ZENDESK_URLS from '../../../../helpers/constants/zendesk-url';
import configureStore from '../../../../store/store';
import { QuizStage } from '../types';
import SRPQuiz from './SRPQuiz';
const store = configureStore({
metamask: {
...mockState.metamask,
},
});
let openTabSpy;
jest.mock('react-router-dom', () => {
const original = jest.requireActual('react-router-dom');
return {
...original,
useHistory: () => ({
push: jest.fn(),
}),
};
});
async function waitForStage(stage) {
return await waitFor(() => {
expect(screen.getByTestId(`srp_stage_${stage}`)).toBeInTheDocument();
});
}
function clickButton(id) {
fireEvent.click(screen.getByTestId(id));
}
describe('srp-reveal-quiz', () => {
beforeAll(() => {
global.platform = { openTab: jest.fn() };
openTabSpy = jest.spyOn(global.platform, 'openTab');
});
it('should go through the full sequence of steps', async () => {
renderWithProvider(<SRPQuiz isOpen />, store);
expect(screen.queryByTestId('srp-quiz-get-started')).toBeInTheDocument();
expect(
screen.queryByTestId('srp-quiz-right-answer'),
).not.toBeInTheDocument();
clickButton('srp-quiz-learn-more');
await waitFor(() =>
expect(openTabSpy).toHaveBeenCalledWith({
url: expect.stringMatching(ZENDESK_URLS.PASSWORD_AND_SRP_ARTICLE),
}),
);
clickButton('srp-quiz-get-started');
await waitForStage(QuizStage.questionOne);
clickButton('srp-quiz-wrong-answer');
await waitForStage(QuizStage.wrongAnswerQuestionOne);
clickButton('srp-quiz-try-again');
await waitForStage(QuizStage.questionOne);
clickButton('srp-quiz-right-answer');
await waitForStage(QuizStage.rightAnswerQuestionOne);
clickButton('srp-quiz-continue');
await waitForStage(QuizStage.questionTwo);
clickButton('srp-quiz-wrong-answer');
await waitForStage(QuizStage.wrongAnswerQuestionTwo);
clickButton('srp-quiz-try-again');
await waitForStage(QuizStage.questionTwo);
clickButton('srp-quiz-right-answer');
await waitForStage(QuizStage.rightAnswerQuestionTwo);
clickButton('srp-quiz-continue');
});
});

View File

@ -0,0 +1,298 @@
/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports, import/no-commonjs */
import React, { useCallback, useContext, useEffect, useState } from 'react';
import { useHistory } from 'react-router-dom';
import {
MetaMetricsEventCategory,
MetaMetricsEventKeyType,
MetaMetricsEventName,
} from '../../../../../shared/constants/metametrics';
import { MetaMetricsContext } from '../../../../contexts/metametrics';
import {
BlockSize,
Display,
FlexDirection,
IconColor,
TextAlign,
} from '../../../../helpers/constants/design-system';
import { REVEAL_SEED_ROUTE } from '../../../../helpers/constants/routes';
import ZENDESK_URLS from '../../../../helpers/constants/zendesk-url';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import {
BUTTON_SIZES,
BUTTON_VARIANT,
Icon,
IconName,
IconSize,
Modal,
ModalContent,
ModalHeader,
ModalOverlay,
} from '../../../component-library';
import QuizContent from '../QuizContent';
import { JSXDict, QuizStage } from '../types';
const wrongAnswerIcon = (
<Icon
size={IconSize.Xl}
name={IconName.Warning}
color={IconColor.errorDefault}
textAlign={TextAlign.Center}
width={BlockSize.OneTwelfth}
/>
);
const rightAnswerIcon = (
<Icon
size={IconSize.Xl}
name={IconName.Confirmation}
color={IconColor.successDefault}
textAlign={TextAlign.Center}
width={BlockSize.OneTwelfth}
/>
);
const openSupportArticle = (): void => {
global.platform.openTab({
url: ZENDESK_URLS.PASSWORD_AND_SRP_ARTICLE,
});
};
export default function SRPQuiz(props: any) {
const [stage, setStage] = useState<QuizStage>(QuizStage.introduction);
const trackEvent = useContext(MetaMetricsContext);
const history = useHistory();
const t = useI18nContext();
// This should not be a state variable, because it's derivable from the state variable `stage`
// (Making it a state variable forces the component to render twice)
let title = '';
// Using a dictionary of JSX elements eliminates the need for a switch statement
const stages: JSXDict = {};
stages[QuizStage.introduction] = () => {
title = t('srpSecurityQuizTitle');
return (
<QuizContent
image={'images/reveal-srp.png'}
content={t('srpSecurityQuizIntroduction')}
buttons={[
{
label: t('srpSecurityQuizGetStarted'),
onClick: () => setStage(QuizStage.questionOne),
variant: BUTTON_VARIANT.PRIMARY,
size: BUTTON_SIZES.LG,
'data-testid': 'srp-quiz-get-started',
},
{
label: t('learnMoreUpperCase'),
onClick: openSupportArticle,
variant: BUTTON_VARIANT.LINK,
'data-testid': 'srp-quiz-learn-more',
},
]}
/>
);
};
stages[QuizStage.questionOne] = () => {
title = `1 ${t('ofTextNofM')} 2`;
return (
<QuizContent
content={t('srpSecurityQuizQuestionOneQuestion')}
buttons={[
{
label: t('srpSecurityQuizQuestionOneWrongAnswer'),
onClick: () => setStage(QuizStage.wrongAnswerQuestionOne),
variant: BUTTON_VARIANT.SECONDARY,
size: BUTTON_SIZES.LG,
'data-testid': 'srp-quiz-wrong-answer',
},
{
label: t('srpSecurityQuizQuestionOneRightAnswer'),
onClick: () => setStage(QuizStage.rightAnswerQuestionOne),
variant: BUTTON_VARIANT.SECONDARY,
size: BUTTON_SIZES.LG,
'data-testid': 'srp-quiz-right-answer',
},
{
label: t('learnMoreUpperCase'),
onClick: openSupportArticle,
variant: BUTTON_VARIANT.LINK,
},
]}
/>
);
};
stages[QuizStage.rightAnswerQuestionOne] = () => {
title = `1 ${t('ofTextNofM')} 2`;
return (
<QuizContent
icon={rightAnswerIcon}
content={t('srpSecurityQuizQuestionOneRightAnswerTitle')}
moreContent={t('srpSecurityQuizQuestionOneRightAnswerDescription')}
buttons={[
{
label: t('continue'),
onClick: () => setStage(QuizStage.questionTwo),
variant: BUTTON_VARIANT.PRIMARY,
size: BUTTON_SIZES.LG,
'data-testid': 'srp-quiz-continue',
},
{
label: t('learnMoreUpperCase'),
onClick: openSupportArticle,
variant: BUTTON_VARIANT.LINK,
},
]}
/>
);
};
stages[QuizStage.wrongAnswerQuestionOne] = () => {
title = `1 ${t('ofTextNofM')} 2`;
return (
<QuizContent
icon={wrongAnswerIcon}
content={t('srpSecurityQuizQuestionOneWrongAnswerTitle')}
moreContent={t('srpSecurityQuizQuestionOneWrongAnswerDescription')}
buttons={[
{
label: t('tryAgain'),
onClick: () => setStage(QuizStage.questionOne),
variant: BUTTON_VARIANT.PRIMARY,
size: BUTTON_SIZES.LG,
'data-testid': 'srp-quiz-try-again',
},
{
label: t('learnMoreUpperCase'),
onClick: openSupportArticle,
variant: BUTTON_VARIANT.LINK,
},
]}
/>
);
};
stages[QuizStage.questionTwo] = () => {
title = `2 ${t('ofTextNofM')} 2`;
return (
<QuizContent
content={t('srpSecurityQuizQuestionTwoQuestion')}
buttons={[
{
label: t('srpSecurityQuizQuestionTwoRightAnswer'),
onClick: () => setStage(QuizStage.rightAnswerQuestionTwo),
variant: BUTTON_VARIANT.SECONDARY,
size: BUTTON_SIZES.LG,
'data-testid': 'srp-quiz-right-answer',
},
{
label: t('srpSecurityQuizQuestionTwoWrongAnswer'),
onClick: () => setStage(QuizStage.wrongAnswerQuestionTwo),
variant: BUTTON_VARIANT.SECONDARY,
size: BUTTON_SIZES.LG,
'data-testid': 'srp-quiz-wrong-answer',
},
{
label: t('learnMoreUpperCase'),
onClick: openSupportArticle,
variant: BUTTON_VARIANT.LINK,
},
]}
/>
);
};
stages[QuizStage.rightAnswerQuestionTwo] = () => {
title = `2 ${t('ofTextNofM')} 2`;
return (
<QuizContent
icon={rightAnswerIcon}
content={t('srpSecurityQuizQuestionTwoRightAnswerTitle')}
moreContent={t('srpSecurityQuizQuestionTwoRightAnswerDescription')}
buttons={[
{
label: t('continue'),
onClick: () => history.push(REVEAL_SEED_ROUTE),
variant: BUTTON_VARIANT.PRIMARY,
size: BUTTON_SIZES.LG,
'data-testid': 'srp-quiz-continue',
},
{
label: t('learnMoreUpperCase'),
onClick: openSupportArticle,
variant: BUTTON_VARIANT.LINK,
},
]}
/>
);
};
stages[QuizStage.wrongAnswerQuestionTwo] = () => {
title = `2 ${t('ofTextNofM')} 2`;
return (
<QuizContent
icon={wrongAnswerIcon}
content={t('srpSecurityQuizQuestionTwoWrongAnswerTitle')}
moreContent={t('srpSecurityQuizQuestionTwoWrongAnswerDescription')}
buttons={[
{
label: t('tryAgain'),
onClick: () => setStage(QuizStage.questionTwo),
variant: BUTTON_VARIANT.PRIMARY,
size: BUTTON_SIZES.LG,
'data-testid': 'srp-quiz-try-again',
},
{
label: t('learnMoreUpperCase'),
onClick: openSupportArticle,
variant: BUTTON_VARIANT.LINK,
},
]}
/>
);
};
// trackEvent shortcut specific to the SRP quiz
const trackEventSrp = useCallback((location) => {
trackEvent(
{
category: MetaMetricsEventCategory.Keys,
event: MetaMetricsEventName.KeyExportSelected,
properties: {
key_type: MetaMetricsEventKeyType.Srp,
location,
},
},
{},
);
}, []);
useEffect(() => {
trackEventSrp(`stage_${stage}`); // Call MetaMetrics based on the current stage
}, [stage]); // Only call this when the stage changes
const quizContent = stages[stage](); // Pick the content using the right stage from the JSXDict
return (
<Modal isOpen={props.isOpen} onClose={props.onClose}>
<ModalOverlay />
<ModalContent
modalDialogProps={{
display: Display.Flex,
flexDirection: FlexDirection.Column,
gap: 4,
}}
>
<ModalHeader onClose={props.onClose} data-testid="srp-quiz-header">
{title}
</ModalHeader>
<span data-testid={`srp_stage_${stage}`} />
{quizContent}
</ModalContent>
</Modal>
);
}

View File

@ -0,0 +1 @@
export { default } from './SRPQuiz';

View File

@ -0,0 +1 @@
export { default } from './SRPQuiz';

View File

@ -0,0 +1,40 @@
export enum QuizStage {
introduction = 'introduction',
questionOne = 'question_one',
wrongAnswerQuestionOne = 'wrong_answer_question_one',
rightAnswerQuestionOne = 'right_answer_question_one',
questionTwo = 'question_two',
wrongAnswerQuestionTwo = 'wrong_answer_question_two',
rightAnswerQuestionTwo = 'right_answer_question_two',
}
export interface IQuizInformationProps {
/**
* The icon to display in the modal should use <Icon /> component
*/
icon?: any;
/**
* The image to display in the modal
*/
image?: string;
/**
* The text content to go inside of the <Text /> component
*/
content: string;
/**
* More text content to go inside of the <Text /> component
*/
moreContent?: string;
/**
* Array of <Button /> component props
*/
buttons: {
onClick: () => void;
label: string;
variant: string;
size?: string;
'data-testid'?: string;
}[];
}
export type JSXDict = { [key: string]: () => JSX.Element };

View File

@ -35,6 +35,7 @@ function ExportTextContainer({ text = '', onClickCopy = null }) {
justifyContent={JustifyContent.CENTER}
className="notranslate"
variant={TextVariant.bodyLgMedium}
data-testid="srp_text"
>
{text}
</Text>

View File

@ -20,12 +20,10 @@ const ZENDESK_URLS = {
LEGACY_WEB3: 'https://metamask.zendesk.com/hc/en-us/articles/360053147012',
NFT_TOKENS:
'https://metamask.zendesk.com/hc/en-us/articles/360058238591-NFT-tokens-in-MetaMask-wallet',
PASSWORD_ARTICLE:
PASSWORD_AND_SRP_ARTICLE:
'https://metamask.zendesk.com/hc/en-us/articles/4404722782107',
SECRET_RECOVERY_PHRASE:
'https://metamask.zendesk.com/hc/en-us/articles/360060826432-What-is-a-Secret-Recovery-Phrase-and-how-to-keep-your-crypto-wallet-secure',
SECRET_RECOVERY_PHRASE_USER_GUIDE:
'https://metamask.zendesk.com/hc/en-us/articles/4404722782107-User-guide-Secret-Recovery-Phrase-password-and-private-keys',
NON_CUSTODIAL_WALLET:
'https://metamask.zendesk.com/hc/en-us/articles/360059952212-MetaMask-is-a-non-custodial-wallet',
SPEEDUP_CANCEL:

View File

@ -1,3 +0,0 @@
export function addUrlProtocolPrefix(urlString) {
return urlString.match(/^https?:\/\//u) ? urlString : `https://${urlString}`;
}

View File

@ -171,7 +171,7 @@ const RevealSeedPage = () => {
key_type: MetaMetricsEventKeyType.Srp,
},
});
} else if (tabName === 'qr-seed') {
} else if (tabName === 'qr-srp') {
trackEvent({
category: MetaMetricsEventCategory.Keys,
event: MetaMetricsEventName.SrpViewsSrpQR,
@ -215,13 +215,14 @@ const RevealSeedPage = () => {
name={t('revealSeedWordsQR')}
className="reveal-seed__tab"
activeClassName="reveal-seed__active-tab"
tabKey="qr-seed"
tabKey="qr-srp"
>
<Box
display={DISPLAY.FLEX}
justifyContent={JustifyContent.center}
alignItems={AlignItems.center}
paddingTop={4}
data-testid="qr-srp"
>
<div
dangerouslySetInnerHTML={{

View File

@ -257,7 +257,7 @@ export default function CreatePassword({
<a
onClick={(e) => e.stopPropagation()}
key="create-password__link-text"
href={ZENDESK_URLS.PASSWORD_ARTICLE}
href={ZENDESK_URLS.PASSWORD_AND_SRP_ARTICLE}
target="_blank"
rel="noopener noreferrer"
>

View File

@ -1,46 +1,43 @@
import React, { useState, useContext } from 'react';
import { useHistory } from 'react-router-dom';
import React, { useContext, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import Box from '../../../components/ui/box/box';
import Button from '../../../components/ui/button';
import Typography from '../../../components/ui/typography';
import { useHistory } from 'react-router-dom';
import { addUrlProtocolPrefix } from '../../../../app/scripts/lib/util';
import {
FONT_WEIGHT,
TextColor,
TypographyVariant,
} from '../../../helpers/constants/design-system';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { addUrlProtocolPrefix } from '../../../helpers/utils/ipfs';
import {
setCompletedOnboarding,
setFeatureFlag,
setUseMultiAccountBalanceChecker,
setUsePhishDetect,
setUseTokenDetection,
showModal,
setIpfsGateway,
setUseCurrencyRateCheck,
toggleNetworkMenu,
} from '../../../store/actions';
import { getCurrentNetwork } from '../../../selectors';
import { ONBOARDING_PIN_EXTENSION_ROUTE } from '../../../helpers/constants/routes';
import {
TextField,
PickerNetwork,
} from '../../../components/component-library';
MetaMetricsEventCategory,
MetaMetricsEventName,
} from '../../../../shared/constants/metametrics';
import {
COINGECKO_LINK,
CRYPTOCOMPARE_LINK,
PRIVACY_POLICY_LINK,
} from '../../../../shared/lib/ui-utils';
import {
PickerNetwork,
TextField,
} from '../../../components/component-library';
import Box from '../../../components/ui/box/box';
import Button from '../../../components/ui/button';
import Typography from '../../../components/ui/typography';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import {
MetaMetricsEventCategory,
MetaMetricsEventName,
} from '../../../../shared/constants/metametrics';
FONT_WEIGHT,
TextColor,
TypographyVariant,
} from '../../../helpers/constants/design-system';
import { ONBOARDING_PIN_EXTENSION_ROUTE } from '../../../helpers/constants/routes';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { getCurrentNetwork } from '../../../selectors';
import {
setCompletedOnboarding,
setFeatureFlag,
setIpfsGateway,
setUseCurrencyRateCheck,
setUseMultiAccountBalanceChecker,
setUsePhishDetect,
setUseTokenDetection,
showModal,
toggleNetworkMenu,
} from '../../../store/actions';
import { Setting } from './setting';
export default function PrivacySettings() {

View File

@ -5,41 +5,31 @@ exports[`Security Tab should match snapshot 1`] = `
<div
class="settings-page__body"
>
<div
class="settings-tab__error"
>
warning
</div>
<span
class="settings-page__security-tab-sub-header__bold"
>
Security
</span>
<div
class="settings-page__security-tab-sub-header"
>
Secret Recovery Phrase
</div>
<div
class="settings-page__content-padded"
>
<div
class="settings-page__content-row"
<button
class="box mm-text mm-button-base mm-button-base--size-lg mm-button-primary mm-text--body-md box--padding-right-4 box--padding-left-4 box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-inverse box--background-color-primary-default box--rounded-pill"
data-testid="reveal-seed-words"
type="danger"
>
<div
class="settings-page__content-item"
>
<span>
Reveal Secret Recovery Phrase
</span>
</div>
<div
class="settings-page__content-item"
>
<div
class="settings-page__content-item-col"
>
<button
class="button btn--rounded btn-danger btn--large"
data-testid="reveal-seed-words"
role="button"
tabindex="0"
>
Reveal Secret Recovery Phrase
</button>
</div>
</div>
</div>
Reveal Secret Recovery Phrase
</button>
</div>
<span
class="settings-page__security-tab-sub-header__bold"
@ -76,6 +66,7 @@ exports[`Security Tab should match snapshot 1`] = `
>
<div
class="settings-page__content-item-col"
data-testid="usePhishingDetection"
>
<label
class="toggle-button toggle-button--on"
@ -182,6 +173,7 @@ exports[`Security Tab should match snapshot 1`] = `
>
<div
class="settings-page__content-item-col"
data-testid="currencyRateCheckToggle"
>
<label
class="toggle-button toggle-button--on"
@ -271,9 +263,10 @@ exports[`Security Tab should match snapshot 1`] = `
>
<div
class="settings-page__content-item-col"
data-testid="showIncomingTransactions"
>
<label
class="toggle-button toggle-button--on"
class="toggle-button toggle-button--off"
tabindex="0"
>
<div
@ -283,23 +276,23 @@ exports[`Security Tab should match snapshot 1`] = `
style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(242, 244, 246);"
>
<div
style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;"
style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;"
/>
<div
style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgba(255, 255, 255, 0.6); bottom: 0px; margin-top: auto; margin-bottom: auto; padding-right: 5px; line-height: 0; width: 26px; height: 20px; opacity: 0;"
style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgba(255, 255, 255, 0.6); bottom: 0px; margin-top: auto; margin-bottom: auto; padding-right: 5px; line-height: 0; width: 26px; height: 20px; opacity: 1;"
/>
</div>
<div
style="position: absolute; height: 100%; top: 0px; left: 0px; display: flex; flex: 1; align-self: stretch; align-items: center; justify-content: flex-start;"
>
<div
style="width: 18px; height: 18px; display: flex; align-self: center; box-shadow: none; border-radius: 50%; box-sizing: border-box; position: relative; background-color: rgb(3, 125, 214); left: 18px;"
style="width: 18px; height: 18px; display: flex; align-self: center; box-shadow: none; border-radius: 50%; box-sizing: border-box; position: relative; background-color: rgb(106, 115, 125); left: 3px;"
/>
</div>
<input
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: absolute; width: 1px;"
type="checkbox"
value="true"
value="false"
/>
</div>
<div
@ -364,9 +357,8 @@ exports[`Security Tab should match snapshot 1`] = `
class="settings-page__content-item-col"
>
<button
class="button btn--rounded btn-secondary settings-page__button"
role="button"
tabindex="0"
class="box mm-text mm-button-base mm-button-base--size-md settings-page__button mm-button-primary mm-text--body-md box--padding-right-4 box--padding-left-4 box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-inverse box--background-color-primary-default box--rounded-pill"
type="secondary"
>
Add custom network
</button>
@ -456,6 +448,7 @@ exports[`Security Tab should match snapshot 1`] = `
>
<div
class="settings-page__content-item-col"
data-testid="autoDetectTokens"
>
<label
class="toggle-button toggle-button--on"
@ -525,6 +518,7 @@ exports[`Security Tab should match snapshot 1`] = `
>
<div
class="settings-page__content-item-col"
data-testid="useMultiAccountBalanceChecker"
>
<label
class="toggle-button toggle-button--off"
@ -605,6 +599,7 @@ exports[`Security Tab should match snapshot 1`] = `
>
<div
class="settings-page__content-item-col"
data-testid="participateInMetaMetrics"
>
<label
class="toggle-button toggle-button--off"

View File

@ -1,35 +1,36 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { startCase } from 'lodash';
import ToggleButton from '../../../components/ui/toggle-button';
import TextField from '../../../components/ui/text-field';
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import {
ADD_POPULAR_CUSTOM_NETWORK,
REVEAL_SEED_ROUTE,
} from '../../../helpers/constants/routes';
import Button from '../../../components/ui/button';
import {
getNumberOfSettingsInSection,
handleSettingsRefs,
} from '../../../helpers/utils/settings-search';
addUrlProtocolPrefix,
getEnvironmentType,
} from '../../../../app/scripts/lib/util';
import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
import {
MetaMetricsEventCategory,
MetaMetricsEventKeyType,
MetaMetricsEventName,
} from '../../../../shared/constants/metametrics';
import {
COINGECKO_LINK,
CRYPTOCOMPARE_LINK,
PRIVACY_POLICY_LINK,
AUTO_DETECT_TOKEN_LEARN_MORE_LINK,
COINGECKO_LINK,
CONSENSYS_PRIVACY_LINK,
CRYPTOCOMPARE_LINK,
ETHERSCAN_PRIVACY_LINK,
PRIVACY_POLICY_LINK,
} from '../../../../shared/lib/ui-utils';
import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
import SRPQuiz from '../../../components/app/srp-quiz-modal/SRPQuiz';
import {
addUrlProtocolPrefix,
getEnvironmentType,
} from '../../../../app/scripts/lib/util';
BUTTON_SIZES,
Button,
} from '../../../components/component-library/button';
import TextField from '../../../components/ui/text-field';
import ToggleButton from '../../../components/ui/toggle-button';
import { ADD_POPULAR_CUSTOM_NETWORK } from '../../../helpers/constants/routes';
import {
getNumberOfSettingsInSection,
handleSettingsRefs,
} from '../../../helpers/utils/settings-search';
export default class SecurityTab extends PureComponent {
static contextTypes = {
@ -59,6 +60,7 @@ export default class SecurityTab extends PureComponent {
state = {
ipfsGateway: this.props.ipfsGateway,
ipfsGatewayError: '',
srpQuizModalVisible: false,
};
settingsRefCounter = 0;
@ -96,47 +98,52 @@ export default class SecurityTab extends PureComponent {
toggleMethod(!value);
}
hideSrpQuizModal = () => this.setState({ srpQuizModalVisible: false });
renderSeedWords() {
const { t } = this.context;
const { history } = this.props;
return (
<div ref={this.settingsRefs[0]} className="settings-page__content-row">
<div className="settings-page__content-item">
<span>{t('revealSeedWords')}</span>
<>
<div className="settings-page__security-tab-sub-header">
{t('secretRecoveryPhrase')}
</div>
<div className="settings-page__content-item">
<div className="settings-page__content-item-col">
<Button
data-testid="reveal-seed-words"
type="danger"
large
onClick={(event) => {
event.preventDefault();
this.context.trackEvent({
category: MetaMetricsEventCategory.Settings,
event: MetaMetricsEventName.KeyExportSelected,
properties: {
key_type: MetaMetricsEventKeyType.Srp,
location: 'Settings',
},
});
this.context.trackEvent({
category: MetaMetricsEventCategory.Settings,
event: MetaMetricsEventName.SrpRevealClicked,
properties: {
key_type: MetaMetricsEventKeyType.Srp,
location: 'Settings',
},
});
history.push(REVEAL_SEED_ROUTE);
}}
>
{t('revealSeedWords')}
</Button>
</div>
<div className="settings-page__content-padded">
<Button
data-testid="reveal-seed-words"
type="danger"
size={BUTTON_SIZES.LG}
onClick={(event) => {
event.preventDefault();
this.context.trackEvent({
category: MetaMetricsEventCategory.Settings,
event: MetaMetricsEventName.KeyExportSelected,
properties: {
key_type: MetaMetricsEventKeyType.Srp,
location: 'Settings',
},
});
this.context.trackEvent({
category: MetaMetricsEventCategory.Settings,
event: MetaMetricsEventName.SrpRevealClicked,
properties: {
key_type: MetaMetricsEventKeyType.Srp,
location: 'Settings',
},
});
this.setState({ srpQuizModalVisible: true });
}}
>
{t('revealSeedWords')}
</Button>
{this.state.srpQuizModalVisible && (
<SRPQuiz
isOpen={this.state.srpQuizModalVisible}
onClose={this.hideSrpQuizModal}
/>
)}
</div>
</div>
</>
);
}
@ -173,7 +180,10 @@ export default class SecurityTab extends PureComponent {
</div>
</div>
<div className="settings-page__content-item">
<div className="settings-page__content-item-col">
<div
className="settings-page__content-item-col"
data-testid="showIncomingTransactions"
>
<ToggleButton
value={showIncomingTransactions}
onToggle={(value) =>
@ -201,7 +211,10 @@ export default class SecurityTab extends PureComponent {
</div>
</div>
<div className="settings-page__content-item">
<div className="settings-page__content-item-col">
<div
className="settings-page__content-item-col"
data-testid="usePhishingDetection"
>
<ToggleButton
value={usePhishDetect}
onToggle={(value) => setUsePhishDetect(!value)}
@ -228,7 +241,10 @@ export default class SecurityTab extends PureComponent {
</div>
</div>
<div className="settings-page__content-item">
<div className="settings-page__content-item-col">
<div
className="settings-page__content-item-col"
data-testid="participateInMetaMetrics"
>
<ToggleButton
value={participateInMetaMetrics}
onToggle={(value) => setParticipateInMetaMetrics(!value)}
@ -355,7 +371,7 @@ export default class SecurityTab extends PureComponent {
);
}
renderAutoDectectTokensToggle() {
renderAutoDetectTokensToggle() {
const { t } = this.context;
const { useTokenDetection, setUseTokenDetection } = this.props;
@ -382,7 +398,10 @@ export default class SecurityTab extends PureComponent {
</div>
</div>
<div className="settings-page__content-item">
<div className="settings-page__content-item-col">
<div
className="settings-page__content-item-col"
data-testid="autoDetectTokens"
>
<ToggleButton
value={useTokenDetection}
onToggle={(value) => {
@ -416,7 +435,10 @@ export default class SecurityTab extends PureComponent {
</div>
</div>
<div className="settings-page__content-item">
<div className="settings-page__content-item-col">
<div
className="settings-page__content-item-col"
data-testid="useMultiAccountBalanceChecker"
>
<ToggleButton
value={useMultiAccountBalanceChecker}
onToggle={(value) => {
@ -474,7 +496,10 @@ export default class SecurityTab extends PureComponent {
</div>
</div>
<div className="settings-page__content-item">
<div className="settings-page__content-item-col">
<div
className="settings-page__content-item-col"
data-testid="currencyRateCheckToggle"
>
<ToggleButton
value={useCurrencyRateCheck}
onToggle={(value) => setUseCurrencyRateCheck(!value)}
@ -492,13 +517,11 @@ export default class SecurityTab extends PureComponent {
return (
<div className="settings-page__body">
{warning ? <div className="settings-tab__error">{warning}</div> : null}
{warning && <div className="settings-tab__error">{warning}</div>}
<span className="settings-page__security-tab-sub-header__bold">
{this.context.t('security')}
</span>
<div className="settings-page__content-padded">
{this.renderSeedWords()}
</div>
{this.renderSeedWords()}
<span className="settings-page__security-tab-sub-header__bold">
{this.context.t('privacy')}
</span>
@ -527,7 +550,7 @@ export default class SecurityTab extends PureComponent {
{this.context.t('tokenAutoDetection')}
</span>
<div className="settings-page__content-padded">
{this.renderAutoDectectTokensToggle()}
{this.renderAutoDetectTokensToggle()}
{this.renderBatchAccountBalanceRequestsToggle()}
</div>
<span className="settings-page__security-tab-sub-header">

View File

@ -1,14 +1,14 @@
import { compose } from 'redux';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { compose } from 'redux';
import {
setFeatureFlag,
setIpfsGateway,
setParticipateInMetaMetrics,
setUseCurrencyRateCheck,
setUseMultiAccountBalanceChecker,
setUsePhishDetect,
setUseTokenDetection,
setIpfsGateway,
setUseMultiAccountBalanceChecker,
setUseCurrencyRateCheck,
} from '../../../store/actions';
import SecurityTab from './security-tab.component';

View File

@ -1,26 +1,44 @@
import { fireEvent, queryByRole, screen } from '@testing-library/react';
import React from 'react';
import { fireEvent } from '@testing-library/react';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import mockState from '../../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../../test/lib/render-helpers';
import SecurityTab from './security-tab.container';
const mockSetFeatureFlag = jest.fn();
const mockSetParticipateInMetaMetrics = jest.fn();
const mockSetUsePhishDetect = jest.fn();
const mockSetUseCurrencyRateCheck = jest.fn();
jest.mock('../../../../app/scripts/lib/util', () => {
const originalModule = jest.requireActual('../../../../app/scripts/lib/util');
jest.mock('../../../store/actions.ts', () => {
return {
setFeatureFlag: () => mockSetFeatureFlag,
setParticipateInMetaMetrics: () => mockSetParticipateInMetaMetrics,
setUsePhishDetect: () => mockSetUsePhishDetect,
setUseCurrencyRateCheck: () => mockSetUseCurrencyRateCheck,
...originalModule,
getEnvironmentType: jest.fn(),
};
});
describe('Security Tab', () => {
const mockStore = configureMockStore()(mockState);
delete mockState.metamask.featureFlags; // Unset featureFlags in order to test the default value
mockState.appState.warning = 'warning'; // This tests an otherwise untested render branch
const mockStore = configureMockStore([thunk])(mockState);
function toggleCheckbox(testId, initialState) {
renderWithProvider(<SecurityTab />, mockStore);
const container = screen.getByTestId(testId);
const checkbox = queryByRole(container, 'checkbox');
expect(checkbox).toHaveAttribute('value', initialState ? 'true' : 'false');
fireEvent.click(checkbox); // This fires the onToggle method of the ToggleButton, but it doesn't change the value of the checkbox
fireEvent.change(checkbox, {
target: { value: !initialState }, // This changes the value of the checkbox
});
expect(checkbox).toHaveAttribute('value', initialState ? 'false' : 'true');
return true;
}
it('should match snapshot', () => {
const { container } = renderWithProvider(<SecurityTab />, mockStore);
@ -28,99 +46,49 @@ describe('Security Tab', () => {
expect(container).toMatchSnapshot();
});
it('navigates to reveal seed words page', () => {
const { queryByTestId, history } = renderWithProvider(
<SecurityTab />,
mockStore,
it('toggles phishing detection', async () => {
expect(await toggleCheckbox('usePhishingDetection', true)).toBe(true);
});
it('toggles balance and token price checker', async () => {
expect(await toggleCheckbox('currencyRateCheckToggle', true)).toBe(true);
});
it('toggles incoming txs', async () => {
expect(await toggleCheckbox('showIncomingTransactions', false)).toBe(true);
});
it('should toggle token detection', async () => {
expect(await toggleCheckbox('autoDetectTokens', true)).toBe(true);
});
it('toggles batch balance checks', async () => {
expect(await toggleCheckbox('useMultiAccountBalanceChecker', false)).toBe(
true,
);
expect(history.location.pathname).toStrictEqual('/');
fireEvent.click(queryByTestId('reveal-seed-words'));
expect(history.location.pathname).toStrictEqual('/seed');
});
it('toggles incoming txs', () => {
const { queryAllByRole } = renderWithProvider(<SecurityTab />, mockStore);
const checkboxes = queryAllByRole('checkbox');
const showIncomingCheckbox = checkboxes[1];
expect(showIncomingCheckbox).toHaveAttribute('value', 'true');
fireEvent.change(showIncomingCheckbox, {
target: { value: false },
});
expect(showIncomingCheckbox).toHaveAttribute('value', 'false');
it('toggles metaMetrics', async () => {
expect(await toggleCheckbox('participateInMetaMetrics', false)).toBe(true);
});
it('toggles phishing detection', () => {
const { queryAllByRole } = renderWithProvider(<SecurityTab />, mockStore);
it('toggles SRP Quiz', async () => {
renderWithProvider(<SecurityTab />, mockStore);
const checkboxes = queryAllByRole('checkbox');
const togglePhishingCheckbox = checkboxes[0];
expect(
screen.queryByTestId(`srp_stage_introduction`),
).not.toBeInTheDocument();
expect(togglePhishingCheckbox).toHaveAttribute('value', 'true');
fireEvent.click(screen.getByTestId('reveal-seed-words'));
fireEvent.change(togglePhishingCheckbox, {
target: { value: false },
});
expect(screen.getByTestId(`srp_stage_introduction`)).toBeInTheDocument();
expect(togglePhishingCheckbox).toHaveAttribute('value', 'false');
});
const container = screen.getByTestId('srp-quiz-header');
const checkbox = queryByRole(container, 'button');
fireEvent.click(checkbox);
it('toggles metaMetrics', () => {
const { queryAllByRole } = renderWithProvider(<SecurityTab />, mockStore);
const checkboxes = queryAllByRole('checkbox');
const index = 5;
const toggleMetaMetricsCheckbox = checkboxes[index];
expect(toggleMetaMetricsCheckbox).toHaveAttribute('value', 'false');
fireEvent.change(toggleMetaMetricsCheckbox, {
target: { value: true },
});
expect(toggleMetaMetricsCheckbox).toHaveAttribute('value', 'true');
});
it('toggles batch balance checks', () => {
const { queryAllByRole } = renderWithProvider(<SecurityTab />, mockStore);
const checkboxes = queryAllByRole('checkbox');
const batchBalanceChecksCheckbox = checkboxes[4];
expect(batchBalanceChecksCheckbox).toHaveAttribute('value', 'false');
fireEvent.change(batchBalanceChecksCheckbox, {
target: { value: true },
});
expect(batchBalanceChecksCheckbox).toHaveAttribute('value', 'true');
});
it('should toggle token detection', () => {
const { queryAllByRole } = renderWithProvider(<SecurityTab />, mockStore);
const checkboxes = queryAllByRole('checkbox');
const tokenDetectionToggle = checkboxes[2];
expect(tokenDetectionToggle).toHaveAttribute('value', 'true');
fireEvent.change(tokenDetectionToggle, {
target: { value: false },
});
expect(tokenDetectionToggle).toHaveAttribute('value', 'false');
fireEvent.change(tokenDetectionToggle, {
target: { value: true },
});
expect(tokenDetectionToggle).toHaveAttribute('value', 'true');
expect(
screen.queryByTestId(`srp_stage_introduction`),
).not.toBeInTheDocument();
});
});

View File

@ -7665,6 +7665,13 @@ __metadata:
languageName: node
linkType: hard
"@types/history@npm:^4.7.11":
version: 4.7.11
resolution: "@types/history@npm:4.7.11"
checksum: c92e2ba407dcab0581a9afdf98f533aa41b61a71133420a6d92b1ca9839f741ab1f9395b17454ba5b88cb86020b70b22d74a1950ccfbdfd9beeaa5459fdc3464
languageName: node
linkType: hard
"@types/hoist-non-react-statics@npm:^3.3.0":
version: 3.3.1
resolution: "@types/hoist-non-react-statics@npm:3.3.1"
@ -8050,6 +8057,27 @@ __metadata:
languageName: node
linkType: hard
"@types/react-router-dom@npm:^5.3.3":
version: 5.3.3
resolution: "@types/react-router-dom@npm:5.3.3"
dependencies:
"@types/history": ^4.7.11
"@types/react": "*"
"@types/react-router": "*"
checksum: 28c4ea48909803c414bf5a08502acbb8ba414669b4b43bb51297c05fe5addc4df0b8fd00e0a9d1e3535ec4073ef38aaafac2c4a2b95b787167d113bc059beff3
languageName: node
linkType: hard
"@types/react-router@npm:*":
version: 5.1.20
resolution: "@types/react-router@npm:5.1.20"
dependencies:
"@types/history": ^4.7.11
"@types/react": "*"
checksum: 128764143473a5e9457ddc715436b5d49814b1c214dde48939b9bef23f0e77f52ffcdfa97eb8d3cc27e2c229869c0cdd90f637d887b62f2c9f065a87d6425419
languageName: node
linkType: hard
"@types/react-transition-group@npm:^4.2.0, @types/react-transition-group@npm:^4.4.0":
version: 4.4.6
resolution: "@types/react-transition-group@npm:4.4.6"
@ -24401,6 +24429,7 @@ __metadata:
"@types/react": ^16.9.53
"@types/react-dom": ^17.0.11
"@types/react-redux": ^7.1.25
"@types/react-router-dom": ^5.3.3
"@types/remote-redux-devtools": ^0.5.5
"@types/sass": ^1.43.1
"@types/sinon": ^10.0.13