1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-26 01:18:28 +00:00

feat(tests): simplify asserts on translated strings

This commit is contained in:
matejcik 2024-11-14 15:11:03 +01:00
parent 5c8edfaac6
commit a1033c0e5f
21 changed files with 337 additions and 433 deletions

View File

@ -1,16 +1,18 @@
from __future__ import annotations
import typing as t
from enum import Enum
from typing import TYPE_CHECKING
from trezorlib.debuglink import LayoutType
from .. import buttons
from .. import translations as TR
if TYPE_CHECKING:
if t.TYPE_CHECKING:
from trezorlib.debuglink import DebugLink, LayoutContent
AllActionsType = t.List[t.Union[str, t.Tuple[str, ...]]]
# Passphrases and addresses for both models
class CommonPass:
@ -82,7 +84,7 @@ def go_back(debug: "DebugLink", r_middle: bool = False) -> LayoutContent:
def navigate_to_action_and_press(
debug: "DebugLink",
wanted_action: str,
all_actions: list[str],
all_actions: AllActionsType,
is_carousel: bool = True,
hold_ms: int = 0,
) -> None:
@ -129,16 +131,19 @@ def unlock_gesture(debug: "DebugLink") -> LayoutContent:
raise RuntimeError("Unknown model")
def _get_action_index(wanted_action: str, all_actions: list[str]) -> int:
def _get_action_index(wanted_action: str, all_actions: AllActionsType) -> int:
"""Get index of the action in the list of all actions"""
if wanted_action in all_actions:
return all_actions.index(wanted_action)
else:
# It may happen that one action item can mean multiple actions
# (e.g. "CANCEL|DELETE" in the passphrase layout - both actions are on the same button)
for index, action in enumerate(all_actions):
subactions = action.split("|")
if wanted_action in subactions:
for index, action in enumerate(all_actions):
if not isinstance(action, tuple):
action = (action,)
for subaction in action:
try:
tr = TR.translate(subaction)
except KeyError:
continue
if tr == wanted_action:
return index
raise ValueError(f"Action {wanted_action} is not supported in {all_actions}")
@ -148,7 +153,7 @@ def _move_one_closer(
debug: "DebugLink",
wanted_action: str,
current_action: str,
all_actions: list[str],
all_actions: AllActionsType,
is_carousel: bool,
) -> LayoutContent:
"""Pressing either left or right regarding to the current situation"""
@ -169,7 +174,3 @@ def _move_one_closer(
return debug.press_left()
else:
return debug.press_right()
def get_possible_btn_texts(path: str) -> str:
return "|".join(TR.translate(path))

View File

@ -4,15 +4,13 @@ from trezorlib.debuglink import LayoutType
from .. import buttons
from .. import translations as TR
from .common import get_possible_btn_texts, go_next
from .common import go_next
if TYPE_CHECKING:
from trezorlib.debuglink import DebugLink, LayoutContent
DELETE_BTN_TEXTS = get_possible_btn_texts("inputs__delete") + get_possible_btn_texts(
"inputs__previous"
)
DELETE_BTN_TEXTS = ("inputs__delete", "inputs__previous")
def enter_word(
@ -50,7 +48,7 @@ def enter_word(
def confirm_recovery(debug: "DebugLink", title: str = "recovery__title") -> None:
layout = debug.read_layout()
TR.assert_equals(layout.title(), title)
assert TR.translate(title) == layout.title()
if debug.layout_type is LayoutType.TT:
debug.click(buttons.OK)
elif debug.layout_type is LayoutType.Mercury:
@ -108,11 +106,11 @@ def select_number_of_words(
return debug.click(coords)
if debug.layout_type is LayoutType.TT:
TR.assert_equals(debug.read_layout().text_content(), "recovery__num_of_words")
assert debug.read_layout().text_content() == TR.recovery__num_of_words
layout = select_tt()
elif debug.layout_type is LayoutType.TR:
layout = debug.press_right()
TR.assert_equals(layout.title(), "word_count__title")
assert layout.title() == TR.word_count__title
layout = select_tr()
elif debug.layout_type is LayoutType.Mercury:
layout = select_mercury()
@ -121,30 +119,24 @@ def select_number_of_words(
if unlock_repeated_backup:
if debug.layout_type is LayoutType.TR:
TR.assert_in(layout.text_content(), "recovery__enter_backup")
assert TR.recovery__enter_backup in layout.text_content()
else:
TR.assert_in_multiple(
layout.text_content(),
["recovery__only_first_n_letters", "recovery__enter_each_word"],
assert (
TR.recovery__only_first_n_letters in layout.text_content()
or TR.recovery__enter_each_word in layout.text_content()
)
elif num_of_words in (20, 33):
TR.assert_in_multiple(
layout.text_content(),
[
"recovery__enter_backup",
"recovery__enter_any_share",
"recovery__only_first_n_letters",
"recovery__enter_each_word",
],
assert (
TR.recovery__enter_backup in layout.text_content()
or TR.recovery__enter_any_share in layout.text_content()
or TR.recovery__only_first_n_letters in layout.text_content()
or TR.recovery__enter_each_word in layout.text_content()
)
else: # BIP-39
TR.assert_in_multiple(
layout.text_content(),
[
"recovery__enter_backup",
"recovery__only_first_n_letters",
"recovery__enter_each_word",
],
assert (
TR.recovery__enter_backup in layout.text_content()
or TR.recovery__only_first_n_letters in layout.text_content()
or TR.recovery__enter_each_word in layout.text_content()
)
@ -155,14 +147,14 @@ def enter_share(
before_title: str = "recovery__title_recover",
) -> "LayoutContent":
if debug.layout_type is LayoutType.TR:
TR.assert_in(debug.read_layout().title(), before_title)
assert TR.translate(before_title) in debug.read_layout().title()
layout = debug.read_layout()
for _ in range(layout.page_count()):
layout = debug.press_right()
elif debug.layout_type is LayoutType.Mercury:
layout = debug.swipe_up()
else:
TR.assert_in(debug.read_layout().title(), before_title)
assert TR.translate(before_title) in debug.read_layout().title()
layout = debug.click(buttons.OK)
assert "MnemonicKeyboard" in layout.all_components()
@ -180,15 +172,12 @@ def enter_shares(
text: str = "recovery__enter_any_share",
after_layout_text: str = "recovery__wallet_recovered",
) -> None:
TR.assert_in_multiple(
debug.read_layout().text_content(),
[
"recovery__enter_backup",
"recovery__enter_any_share",
"recovery__only_first_n_letters",
"recovery__enter_each_word",
text,
],
assert (
TR.recovery__enter_backup in debug.read_layout().text_content()
or TR.recovery__enter_any_share in debug.read_layout().text_content()
or TR.recovery__only_first_n_letters in debug.read_layout().text_content()
or TR.recovery__enter_each_word in debug.read_layout().text_content()
or TR.translate(text) in debug.read_layout().text_content()
)
for index, share in enumerate(shares):
enter_share(
@ -196,14 +185,14 @@ def enter_shares(
)
if index < len(shares) - 1:
# FIXME: when ui-t3t1 done for shamir, we want to check the template below
TR.assert_in(debug.read_layout().title(), enter_share_before_title)
assert TR.translate(enter_share_before_title) in debug.read_layout().title()
# TR.assert_in(
# debug.read_layout().text_content(),
# "recovery__x_of_y_entered_template",
# template=(index + 1, len(shares)),
# )
TR.assert_in(debug.read_layout().text_content(), after_layout_text)
assert TR.translate(after_layout_text) in debug.read_layout().text_content()
def enter_seed(
@ -218,7 +207,7 @@ def enter_seed(
for word in seed_words:
enter_word(debug, word, is_slip39=is_slip39)
TR.assert_in(debug.read_layout().text_content(), after_layout_text)
assert TR.translate(after_layout_text) in debug.read_layout().text_content()
def enter_seed_previous_correct(
@ -244,12 +233,18 @@ def enter_seed_previous_correct(
elif debug.layout_type is LayoutType.TR:
layout = debug.read_layout()
while layout.get_middle_choice() not in DELETE_BTN_TEXTS:
middle_choice = layout.get_middle_choice()
while not any(
TR.translate(btn) == middle_choice for btn in DELETE_BTN_TEXTS
):
layout = debug.press_right()
layout = debug.press_middle()
for _ in range(len(bad_word)):
while layout.get_middle_choice() not in DELETE_BTN_TEXTS:
middle_choice = layout.get_middle_choice()
while not any(
TR.translate(btn) == middle_choice for btn in DELETE_BTN_TEXTS
):
layout = debug.press_left()
layout = debug.press_middle()
elif debug.layout_type is LayoutType.Mercury:
@ -273,14 +268,11 @@ def enter_seed_previous_correct(
def prepare_enter_seed(
debug: "DebugLink", layout_text: str = "recovery__enter_backup"
) -> None:
TR.assert_in_multiple(
debug.read_layout().text_content(),
[
"recovery__enter_backup",
"recovery__only_first_n_letters",
"recovery__enter_each_word",
layout_text,
],
assert (
TR.recovery__enter_backup in debug.read_layout().text_content()
or TR.recovery__only_first_n_letters in debug.read_layout().text_content()
or TR.recovery__enter_each_word in debug.read_layout().text_content()
or TR.translate(layout_text) in debug.read_layout().text_content()
)
if debug.layout_type is LayoutType.TT:
debug.click(buttons.OK)

View File

@ -13,7 +13,7 @@ if TYPE_CHECKING:
def confirm_new_wallet(debug: "DebugLink") -> None:
TR.assert_equals(debug.read_layout().title(), "reset__title_create_wallet")
assert debug.read_layout().title() == TR.reset__title_create_wallet
if debug.layout_type is LayoutType.TT:
debug.click(buttons.OK)
elif debug.layout_type is LayoutType.Mercury:
@ -22,9 +22,9 @@ def confirm_new_wallet(debug: "DebugLink") -> None:
elif debug.layout_type is LayoutType.TR:
debug.press_right()
debug.press_right()
TR.assert_in_multiple(
debug.read_layout().text_content(),
["backup__new_wallet_successfully_created", "backup__new_wallet_created"],
assert (
TR.backup__new_wallet_successfully_created in debug.read_layout().text_content()
or TR.backup__new_wallet_created in debug.read_layout().text_content()
)
if debug.layout_type is LayoutType.Mercury:
debug.swipe_up()
@ -74,9 +74,10 @@ def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> Non
debug.swipe_up()
elif debug.layout_type is LayoutType.TR:
layout = debug.read_layout()
if layout.title() in TR.translate(
"reset__title_number_of_shares"
) + TR.translate("words__title_threshold"):
if (
layout.title()
in TR.reset__title_number_of_shares + TR.words__title_threshold
):
# Special info screens
layout = debug.press_right()
assert "NumberInput" in layout.all_components()
@ -131,7 +132,9 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None:
layout = debug.read_layout()
if debug.layout_type is LayoutType.TT:
TR.assert_template(layout.text_content(), "reset__select_word_x_of_y_template")
assert TR.regexp("reset__select_word_x_of_y_template").match(
layout.text_content()
)
for _ in range(3):
# "Select word 3 of 20"
# ^
@ -146,7 +149,7 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None:
button_pos = btn_texts.index(wanted_word)
layout = debug.click(buttons.RESET_WORD_CHECK[button_pos])
elif debug.layout_type is LayoutType.Mercury:
TR.assert_template(layout.subtitle(), "reset__select_word_x_of_y_template")
assert TR.regexp("reset__select_word_x_of_y_template").match(layout.subtitle())
for _ in range(3):
# "Select word 3 of 20"
# ^
@ -161,7 +164,7 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None:
button_pos = btn_texts.index(wanted_word)
layout = debug.click(buttons.VERTICAL_MENU[button_pos])
elif debug.layout_type is LayoutType.TR:
TR.assert_in(layout.text_content(), "reset__select_correct_word")
assert TR.reset__select_correct_word in layout.text_content()
layout = debug.press_right()
for _ in range(3):
# "SELECT 2ND WORD"

View File

@ -65,9 +65,8 @@ def set_autolock_delay(device_handler: "BackgroundDeviceHandler", delay_ms: int)
debug.input("1234")
TR.assert_template(
debug.read_layout().text_content(),
"auto_lock__change_template",
assert TR.regexp("auto_lock__change_template").match(
debug.read_layout().text_content()
)
layout = go_next(debug)
@ -108,17 +107,17 @@ def test_autolock_interrupts_signing(device_handler: "BackgroundDeviceHandler"):
if debug.layout_type is LayoutType.TT:
debug.click(buttons.OK)
layout = debug.click(buttons.OK)
TR.assert_in(layout.text_content(), "send__total_amount")
assert TR.send__total_amount in layout.text_content()
assert "0.0039 BTC" in layout.text_content()
elif debug.layout_type is LayoutType.Mercury:
debug.swipe_up()
layout = debug.swipe_up()
TR.assert_in(layout.text_content(), "send__total_amount")
assert TR.send__total_amount in layout.text_content()
assert "0.0039 BTC" in layout.text_content()
elif debug.layout_type is LayoutType.TR:
debug.press_right()
layout = debug.press_right()
TR.assert_in(layout.text_content(), "send__total_amount")
assert TR.send__total_amount in layout.text_content()
assert "0.0039 BTC" in layout.text_content()
# wait for autolock to kick in
@ -160,18 +159,18 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa
if debug.layout_type is LayoutType.TT:
debug.click(buttons.OK)
layout = debug.click(buttons.OK)
TR.assert_in(layout.text_content(), "send__total_amount")
assert TR.send__total_amount in layout.text_content()
assert "0.0039 BTC" in layout.text_content()
elif debug.layout_type is LayoutType.Mercury:
debug.swipe_up()
layout = debug.swipe_up()
TR.assert_in(layout.text_content(), "send__total_amount")
assert TR.send__total_amount in layout.text_content()
assert "0.0039 BTC" in layout.text_content()
debug.swipe_up()
elif debug.layout_type is LayoutType.TR:
debug.press_right()
layout = debug.press_right()
TR.assert_in(layout.text_content(), "send__total_amount")
assert TR.send__total_amount in layout.text_content()
assert "0.0039 BTC" in layout.text_content()
def sleepy_filter(msg: MessageType) -> MessageType:
@ -274,7 +273,7 @@ def test_autolock_interrupts_passphrase(device_handler: "BackgroundDeviceHandler
def unlock_dry_run(debug: "DebugLink") -> "LayoutContent":
TR.assert_in(debug.read_layout().text_content(), "recovery__check_dry_run")
assert TR.recovery__check_dry_run in debug.read_layout().text_content()
layout = go_next(debug)
assert "PinKeyboard" in layout.all_components()
@ -291,7 +290,7 @@ def test_dryrun_locks_at_number_of_words(device_handler: "BackgroundDeviceHandle
device_handler.run(device.recover, dry_run=True) # type: ignore
layout = unlock_dry_run(debug)
TR.assert_in(debug.read_layout().text_content(), "recovery__num_of_words")
assert TR.recovery__num_of_words in debug.read_layout().text_content()
if debug.layout_type is LayoutType.TR:
debug.press_right()
@ -312,7 +311,7 @@ def test_dryrun_locks_at_number_of_words(device_handler: "BackgroundDeviceHandle
assert layout is not None
# we are back at homescreen
TR.assert_in(debug.read_layout().text_content(), "recovery__num_of_words")
assert TR.recovery__num_of_words in debug.read_layout().text_content()
@pytest.mark.setup_client(pin=PIN4)

View File

@ -76,18 +76,17 @@ def test_backup_slip39_custom(
# confirm backup configuration
if share_count > 1:
TR.assert_template(
debug.read_layout().text_content(),
"reset__create_x_of_y_multi_share_backup_template",
assert TR.regexp("reset__create_x_of_y_multi_share_backup_template").match(
debug.read_layout().text_content()
)
else:
TR.assert_template(
debug.read_layout().text_content(), "backup__info_single_share_backup"
assert TR.regexp("backup__info_single_share_backup").match(
debug.read_layout().text_content()
)
reset.confirm_read(debug)
# confirm backup intro
TR.assert_in(debug.read_layout().text_content(), "reset__never_make_digital_copy")
assert TR.reset__never_make_digital_copy in debug.read_layout().text_content()
reset.confirm_read(debug, middle_r=True)
all_words: list[str] = []

View File

@ -21,7 +21,6 @@ import pytest
from trezorlib import exceptions
from .. import translations as TR
from ..common import get_test_address
from .common import (
CommonPass,
@ -56,20 +55,11 @@ assert len(AAA_51) == 51
assert AAA_51_ADDRESS == AAA_50_ADDRESS
def _get_possible_btns(path: str) -> str:
return "|".join(TR.translate(path))
def _get_cancel_or_delete() -> str:
paths = ["inputs__cancel", "inputs__delete"]
return "|".join(_get_possible_btns(path) for path in paths)
BACK = _get_possible_btns("inputs__back")
SHOW = _get_possible_btns("inputs__show")
ENTER = _get_possible_btns("inputs__enter")
SPACE = _get_possible_btns("inputs__space")
CANCEL_OR_DELETE = _get_cancel_or_delete()
BACK = "inputs__back"
SHOW = "inputs__show"
ENTER = "inputs__enter"
SPACE = "inputs__space"
CANCEL_OR_DELETE = ("inputs__cancel", "inputs__delete")
# fmt: off
MENU_ACTIONS = [SHOW, CANCEL_OR_DELETE, ENTER, "abc", "ABC", "123", "#$!", SPACE]
DIGITS_ACTIONS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", BACK]

View File

@ -25,12 +25,7 @@ from trezorlib.debuglink import LayoutType
from .. import buttons
from .. import translations as TR
from .common import (
get_possible_btn_texts,
go_back,
go_next,
navigate_to_action_and_press,
)
from .common import go_back, go_next, navigate_to_action_and_press
if TYPE_CHECKING:
from trezorlib.debuglink import DebugLink
@ -48,9 +43,9 @@ PIN24 = "875163065288639289952973"
PIN50 = "31415926535897932384626433832795028841971693993751"
PIN60 = PIN50 + "9" * 10
DELETE = get_possible_btn_texts("inputs__delete")
SHOW = get_possible_btn_texts("inputs__show")
ENTER = get_possible_btn_texts("inputs__enter")
DELETE = "inputs__delete"
SHOW = "inputs__show"
ENTER = "inputs__enter"
TR_PIN_ACTIONS = [
@ -103,8 +98,9 @@ def prepare(
elif situation == Situation.PIN_SETUP:
# Set new PIN
device_handler.run(device.change_pin) # type: ignore
TR.assert_in_multiple(
debug.read_layout().text_content(), ["pin__turn_on", "pin__info"]
assert (
TR.pin__turn_on in debug.read_layout().text_content()
or TR.pin__info in debug.read_layout().text_content()
)
if debug.layout_type in (LayoutType.TT, LayoutType.Mercury):
go_next(debug)
@ -117,7 +113,7 @@ def prepare(
# Change PIN
device_handler.run(device.change_pin) # type: ignore
_input_see_confirm(debug, old_pin)
TR.assert_in(debug.read_layout().text_content(), "pin__change")
assert TR.pin__change in debug.read_layout().text_content()
go_next(debug)
_input_see_confirm(debug, old_pin)
elif situation == Situation.WIPE_CODE_SETUP:
@ -125,7 +121,7 @@ def prepare(
device_handler.run(device.change_wipe_code) # type: ignore
if old_pin:
_input_see_confirm(debug, old_pin)
TR.assert_in(debug.read_layout().text_content(), "wipe_code__turn_on")
assert TR.wipe_code__turn_on in debug.read_layout().text_content()
go_next(debug)
if debug.layout_type is LayoutType.TR:
go_next(debug)

View File

@ -59,13 +59,13 @@ def test_reset_bip39(device_handler: "BackgroundDeviceHandler"):
# confirm backup intro
# parametrized string
TR.assert_template(
debug.read_layout().text_content(), "backup__info_single_share_backup"
assert TR.regexp("backup__info_single_share_backup").match(
debug.read_layout().text_content()
)
reset.confirm_read(debug)
# confirm backup warning
TR.assert_in(debug.read_layout().text_content(), "reset__never_make_digital_copy")
assert TR.reset__never_make_digital_copy in debug.read_layout().text_content()
reset.confirm_read(debug, middle_r=True)
# read words

View File

@ -39,17 +39,17 @@ def test_tutorial_ignore_menu(device_handler: "BackgroundDeviceHandler"):
device_handler.run(device.show_device_tutorial)
layout = debug.read_layout()
TR.assert_equals(layout.title(), "tutorial__welcome_safe5")
assert layout.title() == TR.tutorial__welcome_safe5
layout = debug.click(buttons.TAP_TO_CONFIRM)
TR.assert_equals(layout.title(), "tutorial__title_lets_begin")
assert layout.title() == TR.tutorial__title_lets_begin
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_easy_navigation")
assert layout.title() == TR.tutorial__title_easy_navigation
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_handy_menu")
assert layout.title() == TR.tutorial__title_handy_menu
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_hold")
assert layout.title() == TR.tutorial__title_hold
layout = debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
TR.assert_equals(layout.title(), "tutorial__title_well_done")
assert layout.title() == TR.tutorial__title_well_done
debug.swipe_up()
device_handler.result()
@ -60,23 +60,23 @@ def test_tutorial_menu_open_close(device_handler: "BackgroundDeviceHandler"):
device_handler.run(device.show_device_tutorial)
layout = debug.read_layout()
TR.assert_equals(layout.title(), "tutorial__welcome_safe5")
assert layout.title() == TR.tutorial__welcome_safe5
layout = debug.click(buttons.TAP_TO_CONFIRM)
TR.assert_equals(layout.title(), "tutorial__title_lets_begin")
assert layout.title() == TR.tutorial__title_lets_begin
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_easy_navigation")
assert layout.title() == TR.tutorial__title_easy_navigation
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_handy_menu")
assert layout.title() == TR.tutorial__title_handy_menu
layout = debug.click(buttons.CORNER_BUTTON)
TR.assert_in(layout.text_content(), "tutorial__did_you_know")
assert TR.tutorial__did_you_know in layout.text_content()
layout = debug.click(buttons.CORNER_BUTTON)
TR.assert_equals(layout.title(), "tutorial__title_handy_menu")
assert layout.title() == TR.tutorial__title_handy_menu
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_hold")
assert layout.title() == TR.tutorial__title_hold
layout = debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
TR.assert_equals(layout.title(), "tutorial__title_well_done")
assert layout.title() == TR.tutorial__title_well_done
debug.swipe_up()
device_handler.result()
@ -87,20 +87,20 @@ def test_tutorial_menu_exit(device_handler: "BackgroundDeviceHandler"):
device_handler.run(device.show_device_tutorial)
layout = debug.read_layout()
TR.assert_equals(layout.title(), "tutorial__welcome_safe5")
assert layout.title() == TR.tutorial__welcome_safe5
layout = debug.click(buttons.TAP_TO_CONFIRM)
TR.assert_equals(layout.title(), "tutorial__title_lets_begin")
assert layout.title() == TR.tutorial__title_lets_begin
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_easy_navigation")
assert layout.title() == TR.tutorial__title_easy_navigation
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_handy_menu")
assert layout.title() == TR.tutorial__title_handy_menu
layout = debug.click(buttons.CORNER_BUTTON)
TR.assert_in(layout.text_content(), "tutorial__did_you_know")
assert TR.tutorial__did_you_know in layout.text_content()
layout = debug.click(buttons.VERTICAL_MENU[2])
TR.assert_in(layout.footer(), "instructions__hold_to_exit_tutorial")
assert TR.instructions__hold_to_exit_tutorial in layout.footer()
layout = debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
TR.assert_equals(layout.title(), "tutorial__title_well_done")
assert layout.title() == TR.tutorial__title_well_done
debug.swipe_up()
device_handler.result()
@ -111,27 +111,27 @@ def test_tutorial_menu_repeat(device_handler: "BackgroundDeviceHandler"):
device_handler.run(device.show_device_tutorial)
layout = debug.read_layout()
TR.assert_equals(layout.title(), "tutorial__welcome_safe5")
assert layout.title() == TR.tutorial__welcome_safe5
layout = debug.click(buttons.TAP_TO_CONFIRM)
TR.assert_equals(layout.title(), "tutorial__title_lets_begin")
assert layout.title() == TR.tutorial__title_lets_begin
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_easy_navigation")
assert layout.title() == TR.tutorial__title_easy_navigation
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_handy_menu")
assert layout.title() == TR.tutorial__title_handy_menu
layout = debug.click(buttons.CORNER_BUTTON)
TR.assert_in(layout.text_content(), "tutorial__did_you_know")
assert TR.tutorial__did_you_know in layout.text_content()
layout = debug.click(buttons.VERTICAL_MENU[1])
TR.assert_equals(layout.title(), "tutorial__title_lets_begin")
assert layout.title() == TR.tutorial__title_lets_begin
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_easy_navigation")
assert layout.title() == TR.tutorial__title_easy_navigation
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_handy_menu")
assert layout.title() == TR.tutorial__title_handy_menu
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_hold")
assert layout.title() == TR.tutorial__title_hold
layout = debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
TR.assert_equals(layout.title(), "tutorial__title_well_done")
assert layout.title() == TR.tutorial__title_well_done
debug.swipe_up()
device_handler.result()
@ -142,31 +142,29 @@ def test_tutorial_menu_funfact(device_handler: "BackgroundDeviceHandler"):
device_handler.run(device.show_device_tutorial)
layout = debug.read_layout()
TR.assert_equals(layout.title(), "tutorial__welcome_safe5")
assert layout.title() == TR.tutorial__welcome_safe5
layout = debug.click(buttons.TAP_TO_CONFIRM)
TR.assert_equals(layout.title(), "tutorial__title_lets_begin")
assert layout.title() == TR.tutorial__title_lets_begin
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_easy_navigation")
assert layout.title() == TR.tutorial__title_easy_navigation
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_handy_menu")
assert layout.title() == TR.tutorial__title_handy_menu
layout = debug.click(buttons.CORNER_BUTTON)
TR.assert_in(layout.text_content(), "tutorial__did_you_know")
assert TR.tutorial__did_you_know in layout.text_content()
layout = debug.click(buttons.VERTICAL_MENU[0])
text_content = [
s.replace("\n", " ") for s in TR.translate("tutorial__first_wallet")
]
text_content = [s.replace("\n", " ") for s in TR.tutorial__first_wallet]
assert layout.text_content() in text_content
layout = debug.click(buttons.CORNER_BUTTON)
TR.assert_in(layout.text_content(), "tutorial__did_you_know")
assert TR.tutorial__did_you_know in layout.text_content()
layout = debug.click(buttons.CORNER_BUTTON)
TR.assert_equals(layout.title(), "tutorial__title_handy_menu")
assert layout.title() == TR.tutorial__title_handy_menu
layout = debug.swipe_up()
TR.assert_equals(layout.title(), "tutorial__title_hold")
assert layout.title() == TR.tutorial__title_hold
layout = debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
TR.assert_equals(layout.title(), "tutorial__title_well_done")
assert layout.title() == TR.tutorial__title_well_done
debug.swipe_up()
device_handler.result()

View File

@ -57,7 +57,7 @@ def go_through_tutorial_tr(debug: "DebugLink") -> None:
debug.press_right()
debug.press_right()
layout = debug.press_middle()
TR.assert_equals(layout.title(), "tutorial__title_tutorial_complete")
assert layout.title() == TR.tutorial__title_tutorial_complete
def test_tutorial_finish(device_handler: "BackgroundDeviceHandler"):

View File

@ -309,8 +309,7 @@ def client(
if _raw_client.model is not models.T1B1:
lang = request.session.config.getoption("lang") or "en"
assert isinstance(lang, str)
if lang != "en":
translations.set_language(_raw_client, lang)
translations.set_language(_raw_client, lang)
setup_params = dict(
uninitialized=False,

View File

@ -23,6 +23,7 @@ from trezorlib.tools import parse_path
from ...common import WITH_MOCK_URANDOM
from ...input_flows import InputFlowBip39Recovery, InputFlowBip39ResetBackup
from ...translations import set_language
@pytest.mark.models("core")
@ -31,7 +32,9 @@ def test_reset_recovery(client: Client):
mnemonic = reset(client)
address_before = btc.get_address(client, "Bitcoin", parse_path("m/44h/0h/0h/0/0"))
lang = client.features.language or "en"
device.wipe(client)
set_language(client, lang[:2])
recover(client, mnemonic)
address_after = btc.get_address(client, "Bitcoin", parse_path("m/44h/0h/0h/0/0"))
assert address_before == address_after

View File

@ -26,6 +26,7 @@ from ...input_flows import (
InputFlowSlip39AdvancedRecovery,
InputFlowSlip39AdvancedResetRecovery,
)
from ...translations import set_language
@pytest.mark.models("core")
@ -49,7 +50,10 @@ def test_reset_recovery(client: Client):
+ mnemonics[22:25],
]
for combination in test_combinations:
lang = client.features.language or "en"
device.wipe(client)
set_language(client, lang[:2])
recover(client, combination)
address_after = btc.get_address(
client, "Bitcoin", parse_path("m/44h/0h/0h/0/0")

View File

@ -28,6 +28,7 @@ from ...input_flows import (
InputFlowSlip39BasicRecovery,
InputFlowSlip39BasicResetRecovery,
)
from ...translations import set_language
@pytest.mark.models("core")
@ -38,7 +39,9 @@ def test_reset_recovery(client: Client):
address_before = btc.get_address(client, "Bitcoin", parse_path("m/44h/0h/0h/0/0"))
for share_subset in itertools.combinations(mnemonics, 3):
lang = client.features.language or "en"
device.wipe(client)
set_language(client, lang[:2])
selected_mnemonics = share_subset
recover(client, selected_mnemonics)
address_after = btc.get_address(

View File

@ -162,7 +162,7 @@ def test_repeated_backup_cancel(client: Client):
assert client.features.recovery_status == messages.RecoveryStatus.Backup
layout = client.debug.read_layout()
TR.assert_in(layout.text_content(), "recovery__unlock_repeated_backup")
assert TR.recovery__unlock_repeated_backup in layout.text_content()
# send a Cancel message
@ -218,7 +218,7 @@ def test_repeated_backup_send_disallowed_message(client: Client):
assert client.features.recovery_status == messages.RecoveryStatus.Backup
layout = client.debug.read_layout()
TR.assert_in(layout.text_content(), "recovery__unlock_repeated_backup")
assert TR.recovery__unlock_repeated_backup in layout.text_content()
# send a GetAddress message
@ -237,6 +237,6 @@ def test_repeated_backup_send_disallowed_message(client: Client):
assert client.features.recovery_status == messages.RecoveryStatus.Backup
# we are still on the confirmation screen!
TR.assert_in(
client.debug.read_layout().text_content(), "recovery__unlock_repeated_backup"
assert (
TR.recovery__unlock_repeated_backup in client.debug.read_layout().text_content()
)

View File

@ -59,7 +59,7 @@ def test_sd_protect_unlock(client: Client):
client.debug.input("1234")
yield # do you really want to enable SD protection
TR.assert_in(layout().text_content(), "sd_card__enable")
assert TR.sd_card__enable in layout().text_content()
client.debug.press_yes()
yield # enter current PIN
@ -67,7 +67,7 @@ def test_sd_protect_unlock(client: Client):
client.debug.input("1234")
yield # you have successfully enabled SD protection
TR.assert_in(layout().text_content(), "sd_card__enabled")
assert TR.sd_card__enabled in layout().text_content()
client.debug.press_yes()
with client:
@ -77,7 +77,7 @@ def test_sd_protect_unlock(client: Client):
def input_flow_change_pin():
yield # do you really want to change PIN?
TR.assert_equals(layout().title(), "pin__title_settings")
assert layout().title() == TR.pin__title_settings
client.debug.press_yes()
yield # enter current PIN
@ -93,7 +93,7 @@ def test_sd_protect_unlock(client: Client):
client.debug.input("1234")
yield # Pin change successful
TR.assert_in(layout().text_content(), "pin__changed")
assert TR.pin__changed in layout().text_content()
client.debug.press_yes()
with client:
@ -105,7 +105,7 @@ def test_sd_protect_unlock(client: Client):
def input_flow_change_pin_format():
yield # do you really want to change PIN?
TR.assert_equals(layout().title(), "pin__title_settings")
assert layout().title() == TR.pin__title_settings
client.debug.press_yes()
yield # enter current PIN
@ -113,9 +113,9 @@ def test_sd_protect_unlock(client: Client):
client.debug.input("1234")
yield # SD card problem
TR.assert_in_multiple(
layout().text_content(),
["sd_card__unplug_and_insert_correct", "sd_card__insert_correct_card"],
assert (
TR.sd_card__unplug_and_insert_correct in layout().text_content()
or TR.sd_card__insert_correct_card in layout().text_content()
)
client.debug.press_no() # close

View File

@ -398,10 +398,7 @@ def test_hide_passphrase_from_host(client: Client):
def input_flow():
yield
content = client.debug.read_layout().text_content().lower()
assert any(
(s[:50].lower() in content)
for s in TR.translate("passphrase__from_host_not_shown")
)
assert TR.passphrase__from_host_not_shown[:50].lower() in content
if client.layout_type in (LayoutType.TT, LayoutType.Mercury):
client.debug.press_yes()
elif client.layout_type is LayoutType.TR:
@ -435,17 +432,14 @@ def test_hide_passphrase_from_host(client: Client):
def input_flow():
yield
TR.assert_in(
client.debug.read_layout().text_content(),
"passphrase__next_screen_will_show_passphrase",
assert (
TR.passphrase__next_screen_will_show_passphrase
in client.debug.read_layout().text_content()
)
client.debug.press_yes()
yield
TR.assert_equals(
client.debug.read_layout().title(),
"passphrase__title_confirm",
)
assert client.debug.read_layout().title() == TR.passphrase__title_confirm
assert passphrase in client.debug.read_layout().text_content()
client.debug.press_yes()

View File

@ -428,9 +428,9 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
expected_title = f"MULTISIG XPUB #{xpub_num + 1}"
assert expected_title in title
if self.index == xpub_num:
TR.assert_in(title, "address__title_yours")
assert TR.address__title_yours in title
else:
TR.assert_in(title, "address__title_cosigner")
assert TR.address__title_cosigner in title
def input_flow_tt(self) -> BRGeneratorType:
yield # multisig address warning
@ -438,7 +438,7 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
yield # show address
layout = self.debug.read_layout()
TR.assert_in(layout.title(), "address__title_receive_address")
assert TR.address__title_receive_address in layout.title()
assert "(MULTISIG)" in layout.title()
assert layout.text_content().replace(" ", "") == self.address
@ -448,7 +448,7 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
layout = self.debug.swipe_left()
# address details
assert "Multisig 2 of 3" in layout.screen_content()
TR.assert_in(layout.screen_content(), "address_details__derivation_path")
assert TR.address_details__derivation_path in layout.screen_content()
# Three xpub pages with the same testing logic
for xpub_num in range(3):
@ -471,7 +471,7 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
yield # show address
layout = self.debug.read_layout()
TR.assert_in(layout.title(), "address__title_receive_address")
assert TR.address__title_receive_address in layout.title()
assert "(MULTISIG)" in layout.title()
assert layout.text_content().replace(" ", "") == self.address
@ -512,7 +512,7 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
yield # show address
layout = self.debug.read_layout()
TR.assert_in(layout.title(), "address__title_receive_address")
assert TR.address__title_receive_address in layout.title()
assert layout.text_content().replace(" ", "") == self.address
self.debug.click(buttons.CORNER_BUTTON)
@ -529,7 +529,7 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
layout = self.debug.synchronize_at("AddressDetails")
# address details
assert "Multisig 2 of 3" in layout.screen_content()
TR.assert_in(layout.screen_content(), "address_details__derivation_path")
assert TR.address_details__derivation_path in layout.screen_content()
# three xpub pages with the same testing logic
for _xpub_num in range(3):
@ -629,7 +629,7 @@ class InputFlowShowXpubQRCode(InputFlowBase):
br = yield
layout = self.debug.read_layout()
assert layout.title() in TR.translate("address__public_key") + ["XPUB"]
assert layout.title() in (TR.address__public_key, ["XPUB"])
self.debug.click(buttons.CORNER_BUTTON)
assert "VerticalMenu" in self.all_components()
@ -644,7 +644,7 @@ class InputFlowShowXpubQRCode(InputFlowBase):
self.debug.click(buttons.VERTICAL_MENU[1])
layout = self.debug.synchronize_at("AddressDetails")
# address details
TR.assert_in(layout.screen_content(), "address_details__derivation_path")
assert TR.address_details__derivation_path in layout.screen_content()
self.debug.click(buttons.CORNER_BUTTON)
layout = self.debug.synchronize_at("VerticalMenu")
@ -859,9 +859,9 @@ class InputFlowSignTxInformation(InputFlowBase):
super().__init__(client)
def assert_content(self, content: str, title_path: str) -> None:
TR.assert_in(content, title_path)
assert TR.translate(title_path) in content
assert "Legacy #6" in content
TR.assert_in(content, "confirm_total__fee_rate")
assert TR.confirm_total__fee_rate in content
assert "71.56 sat" in content
def input_flow_tt(self) -> BRGeneratorType:
@ -887,9 +887,9 @@ class InputFlowSignTxInformationMixed(InputFlowBase):
super().__init__(client)
def assert_content(self, content: str, title_path: str) -> None:
TR.assert_in(content, title_path)
TR.assert_in(content, "bitcoin__multiple_accounts")
TR.assert_in(content, "confirm_total__fee_rate")
assert TR.translate(title_path) in content
assert TR.bitcoin__multiple_accounts in content
assert TR.confirm_total__fee_rate in content
assert "18.33 sat" in content
def input_flow_tt(self) -> BRGeneratorType:
@ -1049,7 +1049,7 @@ class InputFlowLockTimeBlockHeight(InputFlowBase):
def assert_func(self, debug: DebugLink, br: messages.ButtonRequest) -> None:
layout_text = get_text_possible_pagination(debug, br)
TR.assert_in(layout_text, "bitcoin__locktime_set_to_blockheight")
assert TR.bitcoin__locktime_set_to_blockheight in layout_text
assert self.block_height in layout_text
def input_flow_tt(self) -> BRGeneratorType:
@ -1073,7 +1073,7 @@ class InputFlowLockTimeDatetime(InputFlowBase):
def assert_func(self, debug: DebugLink, br: messages.ButtonRequest) -> None:
layout_text = get_text_possible_pagination(debug, br)
TR.assert_in(layout_text, "bitcoin__locktime_set_to")
assert TR.bitcoin__locktime_set_to in layout_text
assert self.lock_time_str.replace(" ", "") in layout_text.replace(" ", "")
def input_flow_tt(self) -> BRGeneratorType:
@ -2207,26 +2207,26 @@ class InputFlowResetSkipBackup(InputFlowBase):
def input_flow_tt(self) -> BRGeneratorType:
yield from self.BAK.confirm_new_wallet()
yield # Skip Backup
TR.assert_in(self.text_content(), "backup__new_wallet_successfully_created")
assert TR.backup__new_wallet_successfully_created in self.text_content()
self.debug.press_no()
yield # Confirm skip backup
TR.assert_in(self.text_content(), "backup__want_to_skip")
assert TR.backup__want_to_skip in self.text_content()
self.debug.press_no()
def input_flow_tr(self) -> BRGeneratorType:
yield from self.BAK.confirm_new_wallet()
yield # Skip Backup
TR.assert_in(self.text_content(), "backup__new_wallet_created")
assert TR.backup__new_wallet_created in self.text_content()
self.debug.press_right()
self.debug.press_no()
yield # Confirm skip backup
TR.assert_in(self.text_content(), "backup__want_to_skip")
assert TR.backup__want_to_skip in self.text_content()
self.debug.press_no()
def input_flow_t3t1(self) -> BRGeneratorType:
yield from self.BAK.confirm_new_wallet()
yield # Skip Backup
TR.assert_in(self.text_content(), "backup__new_wallet_created")
assert TR.backup__new_wallet_created in self.text_content()
self.debug.swipe_up()
yield
self.debug.click(buttons.CORNER_BUTTON)
@ -2266,14 +2266,12 @@ class InputFlowConfirmAllWarnings(InputFlowBase):
text = layout.text_content().lower()
# hi priority warning
hi_prio = (
TR.translate("ethereum__unknown_contract_address")
+ TR.translate("addr_mismatch__wrong_derivation_path")
+ TR.translate("send__receiving_to_multisig")
+ [
"witness path",
"certificate path",
"pool owner staking path",
]
TR.ethereum__unknown_contract_address,
TR.addr_mismatch__wrong_derivation_path,
TR.send__receiving_to_multisig,
"witness path",
"certificate path",
"pool owner staking path",
)
if any(needle.lower() in text for needle in hi_prio):
self.debug.click(buttons.CORNER_BUTTON)

View File

@ -28,8 +28,9 @@ class PinFlow:
self.debug.input(pin)
if self.client.layout_type is LayoutType.TR:
assert (yield).name == f"reenter_{what}" # Reenter PIN
TR.assert_in(
self.debug.read_layout().text_content(), f"{what}__reenter_to_confirm"
assert (
TR.translate(f"{what}__reenter_to_confirm")
in self.debug.read_layout().text_content()
)
self.debug.press_yes()
assert (yield).name == "pin_device" # Enter PIN again
@ -47,7 +48,7 @@ class BackupFlow:
def confirm_new_wallet(self) -> BRGeneratorType:
yield
TR.assert_in(self.debug.read_layout().text_content(), "reset__by_continuing")
assert TR.reset__by_continuing in self.debug.read_layout().text_content()
if self.client.layout_type is LayoutType.TR:
self.debug.press_right()
self.debug.press_yes()
@ -64,14 +65,14 @@ class RecoveryFlow:
def confirm_recovery(self) -> BRGeneratorType:
assert (yield).name == "recover_device"
TR.assert_in(self._text_content(), "reset__by_continuing")
assert TR.reset__by_continuing in self._text_content()
if self.client.layout_type is LayoutType.TR:
self.debug.press_right()
self.debug.press_yes()
def confirm_dry_run(self) -> BRGeneratorType:
assert (yield).name == "confirm_seedcheck"
TR.assert_in(self._text_content(), "recovery__check_dry_run")
assert TR.recovery__check_dry_run in self._text_content()
self.debug.press_yes()
def setup_slip39_recovery(self, num_words: int) -> BRGeneratorType:
@ -94,18 +95,18 @@ class RecoveryFlow:
def tr_recovery_homescreen(self) -> BRGeneratorType:
yield
TR.assert_in(self._text_content(), "recovery__num_of_words")
assert TR.recovery__num_of_words in self._text_content()
self.debug.press_yes()
def enter_your_backup(self) -> BRGeneratorType:
assert (yield).name == "recovery"
if self.debug.layout_type is LayoutType.Mercury:
TR.assert_in(self._text_content(), "recovery__enter_each_word")
assert TR.recovery__enter_each_word in self._text_content()
else:
TR.assert_in(self._text_content(), "recovery__enter_backup")
is_dry_run = any(
title in self.debug.read_layout().title().lower()
for title in TR.translate("recovery__title_dry_run", lower=True)
assert TR.recovery__enter_backup in self._text_content()
is_dry_run = (
TR.recovery__title_dry_run.lower()
in self.debug.read_layout().title().lower()
)
if self.client.layout_type is LayoutType.TR and not is_dry_run:
# Normal recovery has extra info (not dry run)
@ -115,13 +116,13 @@ class RecoveryFlow:
def enter_any_share(self) -> BRGeneratorType:
assert (yield).name == "recovery"
TR.assert_in_multiple(
self._text_content(),
["recovery__enter_any_share", "recovery__enter_each_word"],
assert (
TR.recovery__enter_any_share in self._text_content()
or TR.recovery__enter_each_word in self._text_content()
)
is_dry_run = any(
title in self.debug.read_layout().title().lower()
for title in TR.translate("recovery__title_dry_run", lower=True)
is_dry_run = (
TR.recovery__title_dry_run.lower()
in self.debug.read_layout().title().lower()
)
if self.client.layout_type is LayoutType.TR and not is_dry_run:
# Normal recovery has extra info (not dry run)
@ -132,17 +133,17 @@ class RecoveryFlow:
def abort_recovery(self, confirm: bool) -> BRGeneratorType:
yield
if self.client.layout_type is LayoutType.TR:
TR.assert_in(self._text_content(), "recovery__num_of_words")
assert TR.recovery__num_of_words in self._text_content()
self.debug.press_no()
yield
TR.assert_in(self._text_content(), "recovery__wanna_cancel_recovery")
assert TR.recovery__wanna_cancel_recovery in self._text_content()
self.debug.press_right()
if confirm:
self.debug.press_yes()
else:
self.debug.press_no()
elif self.client.layout_type is LayoutType.Mercury:
TR.assert_in(self._text_content(), "recovery__enter_each_word")
assert TR.recovery__enter_each_word in self._text_content()
self.debug.click(buttons.CORNER_BUTTON)
self.debug.synchronize_at("VerticalMenu")
if confirm:
@ -150,10 +151,10 @@ class RecoveryFlow:
else:
self.debug.click(buttons.CORNER_BUTTON)
else:
TR.assert_in(self._text_content(), "recovery__enter_any_share")
assert TR.recovery__enter_any_share in self._text_content()
self.debug.press_no()
yield
TR.assert_in(self._text_content(), "recovery__wanna_cancel_recovery")
assert TR.recovery__wanna_cancel_recovery in self._text_content()
if confirm:
self.debug.press_yes()
else:
@ -162,32 +163,32 @@ class RecoveryFlow:
def abort_recovery_between_shares(self) -> BRGeneratorType:
yield
if self.client.layout_type is LayoutType.TR:
TR.assert_template(
self._text_content(), "recovery__x_of_y_entered_template"
assert TR.regexp("recovery__x_of_y_entered_template").search(
self._text_content()
)
self.debug.press_no()
assert (yield).name == "abort_recovery"
TR.assert_in(self._text_content(), "recovery__wanna_cancel_recovery")
assert TR.recovery__wanna_cancel_recovery in self._text_content()
self.debug.press_right()
self.debug.press_yes()
elif self.client.layout_type is LayoutType.Mercury:
TR.assert_template(
self._text_content(), "recovery__x_of_y_entered_template"
assert TR.regexp("recovery__x_of_y_entered_template").search(
self._text_content()
)
self.debug.click(buttons.CORNER_BUTTON)
self.debug.synchronize_at("VerticalMenu")
self.debug.click(buttons.VERTICAL_MENU[0])
assert (yield).name == "abort_recovery"
layout = self.debug.swipe_up()
TR.assert_equals(layout.title(), "recovery__title_cancel_recovery")
assert layout.title() == TR.recovery__title_cancel_recovery
self.debug.click(buttons.TAP_TO_CONFIRM)
else:
TR.assert_template(
self._text_content(), "recovery__x_of_y_entered_template"
assert TR.regexp("recovery__x_of_y_entered_template").search(
self._text_content()
)
self.debug.press_no()
assert (yield).name == "abort_recovery"
TR.assert_in(self._text_content(), "recovery__wanna_cancel_recovery")
assert TR.recovery__wanna_cancel_recovery in self._text_content()
self.debug.press_yes()
def input_number_of_words(self, num_words: int) -> BRGeneratorType:
@ -195,52 +196,52 @@ class RecoveryFlow:
assert br.code == B.MnemonicWordCount
assert br.name == "recovery_word_count"
if self.client.layout_type is LayoutType.TR:
TR.assert_in(self.debug.read_layout().title(), "word_count__title")
assert TR.word_count__title in self.debug.read_layout().title()
else:
TR.assert_in(self._text_content(), "recovery__num_of_words")
assert TR.recovery__num_of_words in self._text_content()
self.debug.input(str(num_words))
def warning_invalid_recovery_seed(self) -> BRGeneratorType:
br = yield
assert br.code == B.Warning
TR.assert_in(self._text_content(), "recovery__invalid_wallet_backup_entered")
assert TR.recovery__invalid_wallet_backup_entered in self._text_content()
self.debug.press_yes()
def warning_invalid_recovery_share(self) -> BRGeneratorType:
br = yield
assert br.code == B.Warning
TR.assert_in(self._text_content(), "recovery__invalid_share_entered")
assert TR.recovery__invalid_share_entered in self._text_content()
self.debug.press_yes()
def warning_group_threshold_reached(self) -> BRGeneratorType:
br = yield
assert br.code == B.Warning
TR.assert_in(self._text_content(), "recovery__group_threshold_reached")
assert TR.recovery__group_threshold_reached in self._text_content()
self.debug.press_yes()
def warning_share_already_entered(self) -> BRGeneratorType:
br = yield
assert br.code == B.Warning
TR.assert_in(self._text_content(), "recovery__share_already_entered")
assert TR.recovery__share_already_entered in self._text_content()
self.debug.press_yes()
def warning_share_from_another_shamir(self) -> BRGeneratorType:
br = yield
assert br.code == B.Warning
TR.assert_in(
self._text_content(), "recovery__share_from_another_multi_share_backup"
assert (
TR.recovery__share_from_another_multi_share_backup in self._text_content()
)
self.debug.press_yes()
def success_share_group_entered(self) -> BRGeneratorType:
assert (yield).name == "share_success"
TR.assert_in(self._text_content(), "recovery__you_have_entered")
assert TR.recovery__you_have_entered in self._text_content()
self.debug.press_yes()
def success_wallet_recovered(self) -> BRGeneratorType:
br = yield
assert br.code == B.Success
TR.assert_in(self._text_content(), "recovery__wallet_recovered")
assert TR.recovery__wallet_recovered in self._text_content()
self.debug.press_yes()
def success_bip39_dry_run_valid(self) -> BRGeneratorType:
@ -249,7 +250,7 @@ class RecoveryFlow:
text = get_text_possible_pagination(self.debug, br)
# TODO: make sure the translations fit on one page
if self.client.layout_type not in (LayoutType.TT, LayoutType.Mercury):
TR.assert_in(text, "recovery__dry_run_bip39_valid_match")
assert TR.recovery__dry_run_bip39_valid_match in text
self.debug.press_yes()
def success_slip39_dryrun_valid(self) -> BRGeneratorType:
@ -258,7 +259,7 @@ class RecoveryFlow:
text = get_text_possible_pagination(self.debug, br)
# TODO: make sure the translations fit on one page
if self.client.layout_type not in (LayoutType.TT, LayoutType.Mercury):
TR.assert_in(text, "recovery__dry_run_slip39_valid_match")
assert TR.recovery__dry_run_slip39_valid_match in text
self.debug.press_yes()
def warning_slip39_dryrun_mismatch(self) -> BRGeneratorType:
@ -267,7 +268,7 @@ class RecoveryFlow:
text = get_text_possible_pagination(self.debug, br)
# TODO: make sure the translations fit on one page on TT
if self.client.layout_type not in (LayoutType.TT, LayoutType.Mercury):
TR.assert_in(text, "recovery__dry_run_slip39_valid_mismatch")
assert TR.recovery__dry_run_slip39_valid_mismatch in text
self.debug.press_yes()
def warning_bip39_dryrun_mismatch(self) -> BRGeneratorType:
@ -276,7 +277,7 @@ class RecoveryFlow:
text = get_text_possible_pagination(self.debug, br)
# TODO: make sure the translations fit on one page
if self.client.layout_type not in (LayoutType.TT, LayoutType.Mercury):
TR.assert_in(text, "recovery__dry_run_bip39_valid_mismatch")
assert TR.recovery__dry_run_bip39_valid_mismatch in text
self.debug.press_yes()
def success_more_shares_needed(
@ -352,7 +353,7 @@ class EthereumFlow:
def confirm_data(self, info: bool = False, cancel: bool = False) -> BRGeneratorType:
assert (yield).name == "confirm_data"
TR.assert_equals(self.debug.read_layout().title(), "ethereum__title_input_data")
assert self.debug.read_layout().title() == TR.ethereum__title_input_data
if info:
self.debug.press_info()
elif cancel:
@ -364,7 +365,7 @@ class EthereumFlow:
br = yield
assert br.name == "confirm_data"
assert br.pages is not None
TR.assert_equals(self.debug.read_layout().title(), "ethereum__title_input_data")
assert self.debug.read_layout().title() == TR.ethereum__title_input_data
for _ in range(br.pages):
self.debug.read_layout()
go_next(self.debug)
@ -380,7 +381,7 @@ class EthereumFlow:
assert br.name == "confirm_data"
assert br.pages is not None
assert br.pages > 2
TR.assert_equals(self.debug.read_layout().title(), "ethereum__title_input_data")
assert self.debug.read_layout().title() == TR.ethereum__title_input_data
if self.client.layout_type is LayoutType.TR:
self.debug.press_right()
self.debug.press_right()
@ -398,15 +399,15 @@ class EthereumFlow:
self, cancel: bool, info: bool, go_back_from_summary: bool
) -> BRGeneratorType:
assert (yield).name == "confirm_ethereum_tx"
TR.assert_equals(self.debug.read_layout().title(), "words__address")
assert self.debug.read_layout().title() == TR.words__address
if cancel:
self.debug.press_no()
return
self.debug.press_yes()
assert (yield).name == "confirm_ethereum_tx"
TR.assert_equals(self.debug.read_layout().title(), "words__title_summary")
TR.assert_in(self.debug.read_layout().text_content(), "send__maximum_fee")
assert self.debug.read_layout().title() == TR.words__title_summary
assert TR.send__maximum_fee in self.debug.read_layout().text_content()
if go_back_from_summary:
self.debug.press_no()
assert (yield).name == "confirm_ethereum_tx"
@ -414,8 +415,8 @@ class EthereumFlow:
assert (yield).name == "confirm_ethereum_tx"
if info:
self.debug.press_info()
TR.assert_in(self.debug.read_layout().text_content(), "ethereum__gas_limit")
TR.assert_in(self.debug.read_layout().text_content(), "ethereum__gas_price")
assert TR.ethereum__gas_limit in self.debug.read_layout().text_content()
assert TR.ethereum__gas_price in self.debug.read_layout().text_content()
self.debug.press_no()
self.debug.press_yes()
assert (yield).name == "confirm_ethereum_tx"
@ -424,16 +425,16 @@ class EthereumFlow:
self, cancel: bool, info: bool, go_back_from_summary: bool
) -> BRGeneratorType:
assert (yield).name == "confirm_ethereum_tx"
TR.assert_in_multiple(
self.debug.read_layout().title(),
["ethereum__interaction_contract", "words__recipient"],
assert (
TR.ethereum__interaction_contract in self.debug.read_layout().title()
or TR.words__recipient in self.debug.read_layout().title()
)
if cancel:
self.debug.press_left()
return
self.debug.press_right()
assert (yield).name == "confirm_ethereum_tx"
TR.assert_in(self.debug.read_layout().text_content(), "send__maximum_fee")
assert TR.send__maximum_fee in self.debug.read_layout().text_content()
if go_back_from_summary:
self.debug.press_left()
assert (yield).name == "confirm_ethereum_tx"
@ -441,9 +442,9 @@ class EthereumFlow:
assert (yield).name == "confirm_ethereum_tx"
if info:
self.debug.press_right()
TR.assert_in(self.debug.read_layout().text_content(), "ethereum__gas_limit")
assert TR.ethereum__gas_limit in self.debug.read_layout().text_content()
self.debug.press_right()
TR.assert_in(self.debug.read_layout().text_content(), "ethereum__gas_price")
assert TR.ethereum__gas_price in self.debug.read_layout().text_content()
self.debug.press_left()
self.debug.press_left()
self.debug.press_middle()
@ -454,8 +455,8 @@ class EthereumFlow:
) -> BRGeneratorType:
assert (yield).name == "confirm_output"
title = self.debug.read_layout().title()
TR.assert_in(title, "words__address")
TR.assert_in(title, "words__recipient")
assert TR.words__address in title
assert TR.words__recipient in title
if cancel:
self.debug.press_no()
@ -464,8 +465,8 @@ class EthereumFlow:
self.debug.swipe_up()
assert (yield).name == "confirm_total"
layout = self.debug.read_layout()
TR.assert_equals(layout.title(), "words__title_summary")
TR.assert_in(layout.text_content(), "send__maximum_fee")
assert layout.title() == TR.words__title_summary
assert TR.send__maximum_fee in layout.text_content()
if go_back_from_summary:
self.debug.press_no()
assert (yield).name == "confirm_ethereum_tx"
@ -476,8 +477,8 @@ class EthereumFlow:
self.debug.synchronize_at("VerticalMenu")
self.debug.click(buttons.VERTICAL_MENU[0])
text = self.debug.read_layout().text_content()
TR.assert_in(text, "ethereum__gas_limit")
TR.assert_in(text, "ethereum__gas_price")
assert TR.ethereum__gas_limit in text
assert TR.ethereum__gas_price in text
self.debug.click(buttons.CORNER_BUTTON)
self.debug.click(buttons.CORNER_BUTTON)
self.debug.swipe_up()
@ -507,21 +508,15 @@ class EthereumFlow:
br = yield
assert br.code == B.SignTx
assert br.name == "confirm_ethereum_staking_tx"
TR.assert_equals_multiple(
self.debug.read_layout().title(),
[
"ethereum__staking_stake",
"ethereum__staking_unstake",
"ethereum__staking_claim",
],
assert self.debug.read_layout().title() in (
TR.ethereum__staking_stake,
TR.ethereum__staking_unstake,
TR.ethereum__staking_claim,
)
TR.assert_equals_multiple(
self.debug.read_layout().text_content(),
[
"ethereum__staking_stake_intro",
"ethereum__staking_unstake_intro",
"ethereum__staking_claim_intro",
],
assert self.debug.read_layout().text_content() in (
TR.ethereum__staking_stake_intro,
TR.ethereum__staking_unstake_intro,
TR.ethereum__staking_claim_intro,
)
if self.client.layout_type is LayoutType.TT:
# confirm intro
@ -529,12 +524,9 @@ class EthereumFlow:
self.debug.click(
buttons.CORNER_BUTTON,
)
TR.assert_equals_multiple(
self.debug.read_layout().title(),
[
"ethereum__staking_stake_address",
"ethereum__staking_claim_address",
],
assert self.debug.read_layout().title() in (
TR.ethereum__staking_stake_address,
TR.ethereum__staking_claim_address,
)
self.debug.press_no()
self.debug.press_yes()
@ -543,12 +535,8 @@ class EthereumFlow:
# confirm summary
if info:
self.debug.press_info()
TR.assert_in(
self.debug.read_layout().text_content(), "ethereum__gas_limit"
)
TR.assert_in(
self.debug.read_layout().text_content(), "ethereum__gas_price"
)
assert TR.ethereum__gas_limit in self.debug.read_layout().text_content()
assert TR.ethereum__gas_price in self.debug.read_layout().text_content()
self.debug.press_no()
self.debug.press_yes()
yield
@ -561,12 +549,9 @@ class EthereumFlow:
self.debug.click(buttons.CORNER_BUTTON)
self.debug.synchronize_at("VerticalMenu")
self.debug.click(buttons.VERTICAL_MENU[0])
TR.assert_equals_multiple(
self.debug.read_layout().title(),
[
"ethereum__staking_stake_address",
"ethereum__staking_claim_address",
],
assert self.debug.read_layout().title() in (
TR.ethereum__staking_stake_address,
TR.ethereum__staking_claim_address,
)
self.debug.click(buttons.CORNER_BUTTON)
self.debug.click(buttons.CORNER_BUTTON)
@ -581,12 +566,8 @@ class EthereumFlow:
self.debug.click(buttons.CORNER_BUTTON)
self.debug.synchronize_at("VerticalMenu")
self.debug.click(buttons.VERTICAL_MENU[0])
TR.assert_in(
self.debug.read_layout().text_content(), "ethereum__gas_limit"
)
TR.assert_in(
self.debug.read_layout().text_content(), "ethereum__gas_price"
)
assert TR.ethereum__gas_limit in self.debug.read_layout().text_content()
assert TR.ethereum__gas_price in self.debug.read_layout().text_content()
self.debug.click(buttons.CORNER_BUTTON)
self.debug.click(buttons.CORNER_BUTTON)
self.debug.swipe_up()
@ -598,12 +579,9 @@ class EthereumFlow:
# confirm intro
if info:
self.debug.press_right()
TR.assert_equals_multiple(
self.debug.read_layout().title(),
[
"ethereum__staking_stake_address",
"ethereum__staking_claim_address",
],
assert self.debug.read_layout().title() in (
TR.ethereum__staking_stake_address,
TR.ethereum__staking_claim_address,
)
self.debug.press_left()
self.debug.press_middle()
@ -612,13 +590,9 @@ class EthereumFlow:
# confirm summary
if info:
self.debug.press_right()
TR.assert_in(
self.debug.read_layout().text_content(), "ethereum__gas_limit"
)
assert TR.ethereum__gas_limit in self.debug.read_layout().text_content()
self.debug.press_right()
TR.assert_in(
self.debug.read_layout().text_content(), "ethereum__gas_price"
)
assert TR.ethereum__gas_price in self.debug.read_layout().text_content()
self.debug.press_left()
self.debug.press_left()
self.debug.press_middle()

View File

@ -1,4 +1,6 @@
import json
import re
import threading
import typing as t
from hashlib import sha256
from pathlib import Path
@ -12,11 +14,13 @@ from . import common
HERE = Path(__file__).resolve().parent
ROOT = HERE.parent
TRANSLATIONS = ROOT / "core" / "translations"
FONTS_DIR = TRANSLATIONS / "fonts"
ORDER_FILE = TRANSLATIONS / "order.json"
TRANSLATIONS_DIR = ROOT / "core" / "translations"
FONTS_DIR = TRANSLATIONS_DIR / "fonts"
ORDER_FILE = TRANSLATIONS_DIR / "order.json"
LANGUAGES = [file.stem for file in TRANSLATIONS.glob("??.json")]
LANGUAGES = [file.stem for file in TRANSLATIONS_DIR.glob("??.json")]
_CURRENT_TRANSLATION = threading.local()
def prepare_blob(
@ -66,110 +70,57 @@ def set_language(client: Client, lang: str):
language_data = build_and_sign_blob(lang, client)
with client:
device.change_language(client, language_data) # type: ignore
_CURRENT_TRANSLATION.TR = TRANSLATIONS[lang]
def get_lang_json(lang: str) -> translations.JsonDef:
assert lang in LANGUAGES
lang_json = json.loads((TRANSLATIONS / f"{lang}.json").read_text())
lang_json = json.loads((TRANSLATIONS_DIR / f"{lang}.json").read_text())
if (fonts_safe3 := lang_json.get("fonts", {}).get("##Safe3")) is not None:
lang_json["fonts"]["T2B1"] = fonts_safe3
lang_json["fonts"]["T3B1"] = fonts_safe3
return lang_json
def _get_all_language_data() -> list[dict[str, str]]:
return [_get_language_data(language) for language in LANGUAGES]
class Translation:
FORMAT_STR_RE = re.compile(r"\\{\d+\\}")
def __init__(self, lang: str) -> None:
self.lang = lang
self.lang_json = get_lang_json(lang)
@property
def translations(self) -> dict[str, str]:
return self.lang_json["translations"]
def _translate_raw(self, key: str) -> str:
tr = self.translations.get(key)
if tr is not None:
return tr
if self.lang != "en":
return TRANSLATIONS["en"]._translate_raw(key)
raise KeyError(key)
def translate(self, key: str) -> str:
tr = self._translate_raw(key)
return tr.replace("\xa0", " ").strip()
def as_regexp(self, key: str) -> re.Pattern:
tr = self.translate(key)
re_safe = re.escape(tr)
return re.compile(self.FORMAT_STR_RE.sub(r".*?", re_safe))
def _get_language_data(lang: str) -> dict[str, str]:
return get_lang_json(lang)["translations"]
TRANSLATIONS = {lang: Translation(lang) for lang in LANGUAGES}
all_language_data = _get_all_language_data()
def translate(key: str) -> str:
return _CURRENT_TRANSLATION.TR.translate(key)
def _resolve_path_to_texts(
path: str, template: t.Iterable[t.Any] = (), lower: bool = True
) -> list[str]:
texts: list[str] = []
lookups = path.split(".")
for language_data in all_language_data:
language_data_missing = False
data: dict[str, t.Any] | str = language_data
for lookup in lookups:
assert isinstance(data, dict), f"{lookup} is not a dict"
if lookup not in data:
language_data_missing = True
break
data = data[lookup]
if language_data_missing:
continue
assert isinstance(data, str), f"{path} is not a string"
if template:
data = data.format(*template)
texts.append(data)
if lower:
texts = [t.lower() for t in texts]
texts = [t.replace("\xa0", " ").strip() for t in texts]
return texts
def regexp(key: str) -> re.Pattern:
return _CURRENT_TRANSLATION.TR.as_regexp(key)
def assert_equals(text: str, path: str, template: t.Iterable[t.Any] = ()) -> None:
# TODO: we can directly pass in the current device language
texts = _resolve_path_to_texts(path, template)
assert text.lower() in texts, f"{text} not found in {texts}"
def assert_equals_multiple(
text: str, paths: list[str], template: t.Iterable[t.Any] = ()
) -> None:
texts: list[str] = []
for path in paths:
texts += _resolve_path_to_texts(path, template)
assert text.lower() in texts, f"{text} not found in {texts}"
def assert_in(text: str, path: str, template: t.Iterable[t.Any] = ()) -> None:
texts = _resolve_path_to_texts(path, template)
for tt in texts:
if tt in text.lower():
return
assert False, f"{text} not found in {texts}"
def assert_in_multiple(
text: str, paths: list[str], template: t.Iterable[t.Any] = ()
) -> None:
texts: list[str] = []
for path in paths:
texts += _resolve_path_to_texts(path, template)
for tt in texts:
if tt in text.lower():
return
assert False, f"{text} not found in {texts}"
def assert_startswith(text: str, path: str, template: t.Iterable[t.Any] = ()) -> None:
texts = _resolve_path_to_texts(path, template)
for tt in texts:
if text.lower().startswith(tt):
return
assert False, f"{text} not found in {texts}"
def assert_template(text: str, template_path: str) -> None:
templates = _resolve_path_to_texts(template_path)
for tt in templates:
# Checking at least the first part
first_part = tt.split("{")[0]
if text.lower().startswith(first_part):
return
assert False, f"{text} not found in {templates}"
def translate(
path: str, template: t.Iterable[t.Any] = (), lower: bool = False
) -> list[str]:
# Do not converting to lowercase, we want the exact value
return _resolve_path_to_texts(path, template, lower=lower)
def __getattr__(key: str) -> str:
return translate(key)

View File

@ -2140,9 +2140,9 @@
"T2T1_cs_reset_recovery-test_reset_bip39_t2.py::test_reset_device_192": "c3b4126112107622a4ae14343700000e67787f175c7273c0b81fc30320fd4659",
"T2T1_cs_reset_recovery-test_reset_bip39_t2.py::test_reset_device_pin": "2e261c7367373888adc9d30baf7be99f89da114364a54dd096305ac76f5b02eb",
"T2T1_cs_reset_recovery-test_reset_bip39_t2.py::test_reset_failed_check": "c15845c4d1a46c654b5bb4babed93974d4b7c68b6bb120c74d46a1ec924152ce",
"T2T1_cs_reset_recovery-test_reset_recovery_bip39.py::test_reset_recovery": "3d3c9ddad06542ed169c761779f8bd6a65484c23462aefba05b90df6135421c2",
"T2T1_cs_reset_recovery-test_reset_recovery_slip39_advanced.py::test_reset_recovery": "2d9efb84f2d69506875061f833cdf2dc5d717c006d4e021346c4c1b15a77fe17",
"T2T1_cs_reset_recovery-test_reset_recovery_slip39_basic.py::test_reset_recovery": "5af5fa1fb4e69036a2cb77234059253f539bc2024d2f5895559950876897eab8",
"T2T1_cs_reset_recovery-test_reset_recovery_bip39.py::test_reset_recovery": "7423b5a3985389a8d290b1bf46a63e895c1d10f5506c62db963cae47dc29ef54",
"T2T1_cs_reset_recovery-test_reset_recovery_slip39_advanced.py::test_reset_recovery": "3dd4bfeb88c50e5cd8cef1930c152b0d00f65a4ceed8ab47ab1915dc02c9064d",
"T2T1_cs_reset_recovery-test_reset_recovery_slip39_basic.py::test_reset_recovery": "13432d5369619793e6d362259b004a249b00ef0d0505e15b65b0b2d9f1181653",
"T2T1_cs_reset_recovery-test_reset_slip39_advanced.py::test_reset_device_slip39_advanced": "747fa7c7cea4b886102440f1b56f84f00f60609243c7044c42a1d65261d6b93b",
"T2T1_cs_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic": "d9f64781fddcd204ec6be552593edfbf6a9f680f6f4fd79058b94317ed29fa23",
"T2T1_cs_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic_256": "8d61ac2c62b0009340d7c2a3559440a24f74af3c0a783a35df26ab72f12f7bf7",