1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-22 23:48:12 +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 from __future__ import annotations
import typing as t
from enum import Enum from enum import Enum
from typing import TYPE_CHECKING
from trezorlib.debuglink import LayoutType from trezorlib.debuglink import LayoutType
from .. import buttons from .. import buttons
from .. import translations as TR from .. import translations as TR
if TYPE_CHECKING: if t.TYPE_CHECKING:
from trezorlib.debuglink import DebugLink, LayoutContent from trezorlib.debuglink import DebugLink, LayoutContent
AllActionsType = t.List[t.Union[str, t.Tuple[str, ...]]]
# Passphrases and addresses for both models # Passphrases and addresses for both models
class CommonPass: class CommonPass:
@ -82,7 +84,7 @@ def go_back(debug: "DebugLink", r_middle: bool = False) -> LayoutContent:
def navigate_to_action_and_press( def navigate_to_action_and_press(
debug: "DebugLink", debug: "DebugLink",
wanted_action: str, wanted_action: str,
all_actions: list[str], all_actions: AllActionsType,
is_carousel: bool = True, is_carousel: bool = True,
hold_ms: int = 0, hold_ms: int = 0,
) -> None: ) -> None:
@ -129,16 +131,19 @@ def unlock_gesture(debug: "DebugLink") -> LayoutContent:
raise RuntimeError("Unknown model") 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""" """Get index of the action in the list of all actions"""
if wanted_action in all_actions: if wanted_action in all_actions:
return all_actions.index(wanted_action) return all_actions.index(wanted_action)
else: for index, action in enumerate(all_actions):
# It may happen that one action item can mean multiple actions if not isinstance(action, tuple):
# (e.g. "CANCEL|DELETE" in the passphrase layout - both actions are on the same button) action = (action,)
for index, action in enumerate(all_actions): for subaction in action:
subactions = action.split("|") try:
if wanted_action in subactions: tr = TR.translate(subaction)
except KeyError:
continue
if tr == wanted_action:
return index return index
raise ValueError(f"Action {wanted_action} is not supported in {all_actions}") raise ValueError(f"Action {wanted_action} is not supported in {all_actions}")
@ -148,7 +153,7 @@ def _move_one_closer(
debug: "DebugLink", debug: "DebugLink",
wanted_action: str, wanted_action: str,
current_action: str, current_action: str,
all_actions: list[str], all_actions: AllActionsType,
is_carousel: bool, is_carousel: bool,
) -> LayoutContent: ) -> LayoutContent:
"""Pressing either left or right regarding to the current situation""" """Pressing either left or right regarding to the current situation"""
@ -169,7 +174,3 @@ def _move_one_closer(
return debug.press_left() return debug.press_left()
else: else:
return debug.press_right() 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 buttons
from .. import translations as TR from .. import translations as TR
from .common import get_possible_btn_texts, go_next from .common import go_next
if TYPE_CHECKING: if TYPE_CHECKING:
from trezorlib.debuglink import DebugLink, LayoutContent from trezorlib.debuglink import DebugLink, LayoutContent
DELETE_BTN_TEXTS = get_possible_btn_texts("inputs__delete") + get_possible_btn_texts( DELETE_BTN_TEXTS = ("inputs__delete", "inputs__previous")
"inputs__previous"
)
def enter_word( def enter_word(
@ -50,7 +48,7 @@ def enter_word(
def confirm_recovery(debug: "DebugLink", title: str = "recovery__title") -> None: def confirm_recovery(debug: "DebugLink", title: str = "recovery__title") -> None:
layout = debug.read_layout() layout = debug.read_layout()
TR.assert_equals(layout.title(), title) assert TR.translate(title) == layout.title()
if debug.layout_type is LayoutType.TT: if debug.layout_type is LayoutType.TT:
debug.click(buttons.OK) debug.click(buttons.OK)
elif debug.layout_type is LayoutType.Mercury: elif debug.layout_type is LayoutType.Mercury:
@ -108,11 +106,11 @@ def select_number_of_words(
return debug.click(coords) return debug.click(coords)
if debug.layout_type is LayoutType.TT: 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() layout = select_tt()
elif debug.layout_type is LayoutType.TR: elif debug.layout_type is LayoutType.TR:
layout = debug.press_right() layout = debug.press_right()
TR.assert_equals(layout.title(), "word_count__title") assert layout.title() == TR.word_count__title
layout = select_tr() layout = select_tr()
elif debug.layout_type is LayoutType.Mercury: elif debug.layout_type is LayoutType.Mercury:
layout = select_mercury() layout = select_mercury()
@ -121,30 +119,24 @@ def select_number_of_words(
if unlock_repeated_backup: if unlock_repeated_backup:
if debug.layout_type is LayoutType.TR: 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: else:
TR.assert_in_multiple( assert (
layout.text_content(), TR.recovery__only_first_n_letters in layout.text_content()
["recovery__only_first_n_letters", "recovery__enter_each_word"], or TR.recovery__enter_each_word in layout.text_content()
) )
elif num_of_words in (20, 33): elif num_of_words in (20, 33):
TR.assert_in_multiple( assert (
layout.text_content(), TR.recovery__enter_backup in layout.text_content()
[ or TR.recovery__enter_any_share in layout.text_content()
"recovery__enter_backup", or TR.recovery__only_first_n_letters in layout.text_content()
"recovery__enter_any_share", or TR.recovery__enter_each_word in layout.text_content()
"recovery__only_first_n_letters",
"recovery__enter_each_word",
],
) )
else: # BIP-39 else: # BIP-39
TR.assert_in_multiple( assert (
layout.text_content(), TR.recovery__enter_backup in layout.text_content()
[ or TR.recovery__only_first_n_letters in layout.text_content()
"recovery__enter_backup", or TR.recovery__enter_each_word in layout.text_content()
"recovery__only_first_n_letters",
"recovery__enter_each_word",
],
) )
@ -155,14 +147,14 @@ def enter_share(
before_title: str = "recovery__title_recover", before_title: str = "recovery__title_recover",
) -> "LayoutContent": ) -> "LayoutContent":
if debug.layout_type is LayoutType.TR: 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() layout = debug.read_layout()
for _ in range(layout.page_count()): for _ in range(layout.page_count()):
layout = debug.press_right() layout = debug.press_right()
elif debug.layout_type is LayoutType.Mercury: elif debug.layout_type is LayoutType.Mercury:
layout = debug.swipe_up() layout = debug.swipe_up()
else: 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) layout = debug.click(buttons.OK)
assert "MnemonicKeyboard" in layout.all_components() assert "MnemonicKeyboard" in layout.all_components()
@ -180,15 +172,12 @@ def enter_shares(
text: str = "recovery__enter_any_share", text: str = "recovery__enter_any_share",
after_layout_text: str = "recovery__wallet_recovered", after_layout_text: str = "recovery__wallet_recovered",
) -> None: ) -> None:
TR.assert_in_multiple( assert (
debug.read_layout().text_content(), TR.recovery__enter_backup in debug.read_layout().text_content()
[ or TR.recovery__enter_any_share in debug.read_layout().text_content()
"recovery__enter_backup", or TR.recovery__only_first_n_letters in debug.read_layout().text_content()
"recovery__enter_any_share", or TR.recovery__enter_each_word in debug.read_layout().text_content()
"recovery__only_first_n_letters", or TR.translate(text) in debug.read_layout().text_content()
"recovery__enter_each_word",
text,
],
) )
for index, share in enumerate(shares): for index, share in enumerate(shares):
enter_share( enter_share(
@ -196,14 +185,14 @@ def enter_shares(
) )
if index < len(shares) - 1: if index < len(shares) - 1:
# FIXME: when ui-t3t1 done for shamir, we want to check the template below # 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( # TR.assert_in(
# debug.read_layout().text_content(), # debug.read_layout().text_content(),
# "recovery__x_of_y_entered_template", # "recovery__x_of_y_entered_template",
# template=(index + 1, len(shares)), # 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( def enter_seed(
@ -218,7 +207,7 @@ def enter_seed(
for word in seed_words: for word in seed_words:
enter_word(debug, word, is_slip39=is_slip39) 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( def enter_seed_previous_correct(
@ -244,12 +233,18 @@ def enter_seed_previous_correct(
elif debug.layout_type is LayoutType.TR: elif debug.layout_type is LayoutType.TR:
layout = debug.read_layout() 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_right()
layout = debug.press_middle() layout = debug.press_middle()
for _ in range(len(bad_word)): 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_left()
layout = debug.press_middle() layout = debug.press_middle()
elif debug.layout_type is LayoutType.Mercury: elif debug.layout_type is LayoutType.Mercury:
@ -273,14 +268,11 @@ def enter_seed_previous_correct(
def prepare_enter_seed( def prepare_enter_seed(
debug: "DebugLink", layout_text: str = "recovery__enter_backup" debug: "DebugLink", layout_text: str = "recovery__enter_backup"
) -> None: ) -> None:
TR.assert_in_multiple( assert (
debug.read_layout().text_content(), TR.recovery__enter_backup in debug.read_layout().text_content()
[ or TR.recovery__only_first_n_letters in debug.read_layout().text_content()
"recovery__enter_backup", or TR.recovery__enter_each_word in debug.read_layout().text_content()
"recovery__only_first_n_letters", or TR.translate(layout_text) in debug.read_layout().text_content()
"recovery__enter_each_word",
layout_text,
],
) )
if debug.layout_type is LayoutType.TT: if debug.layout_type is LayoutType.TT:
debug.click(buttons.OK) debug.click(buttons.OK)

View File

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

View File

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

View File

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

View File

@ -21,7 +21,6 @@ import pytest
from trezorlib import exceptions from trezorlib import exceptions
from .. import translations as TR
from ..common import get_test_address from ..common import get_test_address
from .common import ( from .common import (
CommonPass, CommonPass,
@ -56,20 +55,11 @@ assert len(AAA_51) == 51
assert AAA_51_ADDRESS == AAA_50_ADDRESS assert AAA_51_ADDRESS == AAA_50_ADDRESS
def _get_possible_btns(path: str) -> str: BACK = "inputs__back"
return "|".join(TR.translate(path)) SHOW = "inputs__show"
ENTER = "inputs__enter"
SPACE = "inputs__space"
def _get_cancel_or_delete() -> str: CANCEL_OR_DELETE = ("inputs__cancel", "inputs__delete")
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()
# fmt: off # fmt: off
MENU_ACTIONS = [SHOW, CANCEL_OR_DELETE, ENTER, "abc", "ABC", "123", "#$!", SPACE] MENU_ACTIONS = [SHOW, CANCEL_OR_DELETE, ENTER, "abc", "ABC", "123", "#$!", SPACE]
DIGITS_ACTIONS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", BACK] 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 buttons
from .. import translations as TR from .. import translations as TR
from .common import ( from .common import go_back, go_next, navigate_to_action_and_press
get_possible_btn_texts,
go_back,
go_next,
navigate_to_action_and_press,
)
if TYPE_CHECKING: if TYPE_CHECKING:
from trezorlib.debuglink import DebugLink from trezorlib.debuglink import DebugLink
@ -48,9 +43,9 @@ PIN24 = "875163065288639289952973"
PIN50 = "31415926535897932384626433832795028841971693993751" PIN50 = "31415926535897932384626433832795028841971693993751"
PIN60 = PIN50 + "9" * 10 PIN60 = PIN50 + "9" * 10
DELETE = get_possible_btn_texts("inputs__delete") DELETE = "inputs__delete"
SHOW = get_possible_btn_texts("inputs__show") SHOW = "inputs__show"
ENTER = get_possible_btn_texts("inputs__enter") ENTER = "inputs__enter"
TR_PIN_ACTIONS = [ TR_PIN_ACTIONS = [
@ -103,8 +98,9 @@ def prepare(
elif situation == Situation.PIN_SETUP: elif situation == Situation.PIN_SETUP:
# Set new PIN # Set new PIN
device_handler.run(device.change_pin) # type: ignore device_handler.run(device.change_pin) # type: ignore
TR.assert_in_multiple( assert (
debug.read_layout().text_content(), ["pin__turn_on", "pin__info"] 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): if debug.layout_type in (LayoutType.TT, LayoutType.Mercury):
go_next(debug) go_next(debug)
@ -117,7 +113,7 @@ def prepare(
# Change PIN # Change PIN
device_handler.run(device.change_pin) # type: ignore device_handler.run(device.change_pin) # type: ignore
_input_see_confirm(debug, old_pin) _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) go_next(debug)
_input_see_confirm(debug, old_pin) _input_see_confirm(debug, old_pin)
elif situation == Situation.WIPE_CODE_SETUP: elif situation == Situation.WIPE_CODE_SETUP:
@ -125,7 +121,7 @@ def prepare(
device_handler.run(device.change_wipe_code) # type: ignore device_handler.run(device.change_wipe_code) # type: ignore
if old_pin: if old_pin:
_input_see_confirm(debug, 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) go_next(debug)
if debug.layout_type is LayoutType.TR: if debug.layout_type is LayoutType.TR:
go_next(debug) go_next(debug)

View File

@ -59,13 +59,13 @@ def test_reset_bip39(device_handler: "BackgroundDeviceHandler"):
# confirm backup intro # confirm backup intro
# parametrized string # parametrized string
TR.assert_template( assert TR.regexp("backup__info_single_share_backup").match(
debug.read_layout().text_content(), "backup__info_single_share_backup" debug.read_layout().text_content()
) )
reset.confirm_read(debug) reset.confirm_read(debug)
# confirm backup warning # 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) reset.confirm_read(debug, middle_r=True)
# read words # read words

View File

@ -39,17 +39,17 @@ def test_tutorial_ignore_menu(device_handler: "BackgroundDeviceHandler"):
device_handler.run(device.show_device_tutorial) device_handler.run(device.show_device_tutorial)
layout = debug.read_layout() 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) 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() 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() 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() 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) 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() debug.swipe_up()
device_handler.result() device_handler.result()
@ -60,23 +60,23 @@ def test_tutorial_menu_open_close(device_handler: "BackgroundDeviceHandler"):
device_handler.run(device.show_device_tutorial) device_handler.run(device.show_device_tutorial)
layout = debug.read_layout() 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) 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() 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() 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) 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) 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() 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) 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() debug.swipe_up()
device_handler.result() device_handler.result()
@ -87,20 +87,20 @@ def test_tutorial_menu_exit(device_handler: "BackgroundDeviceHandler"):
device_handler.run(device.show_device_tutorial) device_handler.run(device.show_device_tutorial)
layout = debug.read_layout() 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) 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() 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() 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) 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]) 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) 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() debug.swipe_up()
device_handler.result() device_handler.result()
@ -111,27 +111,27 @@ def test_tutorial_menu_repeat(device_handler: "BackgroundDeviceHandler"):
device_handler.run(device.show_device_tutorial) device_handler.run(device.show_device_tutorial)
layout = debug.read_layout() 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) 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() 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() 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) 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]) 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() 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() 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() 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) 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() debug.swipe_up()
device_handler.result() device_handler.result()
@ -142,31 +142,29 @@ def test_tutorial_menu_funfact(device_handler: "BackgroundDeviceHandler"):
device_handler.run(device.show_device_tutorial) device_handler.run(device.show_device_tutorial)
layout = debug.read_layout() 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) 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() 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() 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) 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]) layout = debug.click(buttons.VERTICAL_MENU[0])
text_content = [ text_content = [s.replace("\n", " ") for s in TR.tutorial__first_wallet]
s.replace("\n", " ") for s in TR.translate("tutorial__first_wallet")
]
assert layout.text_content() in text_content assert layout.text_content() in text_content
layout = debug.click(buttons.CORNER_BUTTON) 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) 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() 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) 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() debug.swipe_up()
device_handler.result() device_handler.result()

View File

@ -57,7 +57,7 @@ def go_through_tutorial_tr(debug: "DebugLink") -> None:
debug.press_right() debug.press_right()
debug.press_right() debug.press_right()
layout = debug.press_middle() 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"): def test_tutorial_finish(device_handler: "BackgroundDeviceHandler"):

View File

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

View File

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

View File

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

View File

@ -28,6 +28,7 @@ from ...input_flows import (
InputFlowSlip39BasicRecovery, InputFlowSlip39BasicRecovery,
InputFlowSlip39BasicResetRecovery, InputFlowSlip39BasicResetRecovery,
) )
from ...translations import set_language
@pytest.mark.models("core") @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")) address_before = btc.get_address(client, "Bitcoin", parse_path("m/44h/0h/0h/0/0"))
for share_subset in itertools.combinations(mnemonics, 3): for share_subset in itertools.combinations(mnemonics, 3):
lang = client.features.language or "en"
device.wipe(client) device.wipe(client)
set_language(client, lang[:2])
selected_mnemonics = share_subset selected_mnemonics = share_subset
recover(client, selected_mnemonics) recover(client, selected_mnemonics)
address_after = btc.get_address( 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 assert client.features.recovery_status == messages.RecoveryStatus.Backup
layout = client.debug.read_layout() 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 # 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 assert client.features.recovery_status == messages.RecoveryStatus.Backup
layout = client.debug.read_layout() 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 # 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 assert client.features.recovery_status == messages.RecoveryStatus.Backup
# we are still on the confirmation screen! # we are still on the confirmation screen!
TR.assert_in( assert (
client.debug.read_layout().text_content(), "recovery__unlock_repeated_backup" 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") client.debug.input("1234")
yield # do you really want to enable SD protection 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() client.debug.press_yes()
yield # enter current PIN yield # enter current PIN
@ -67,7 +67,7 @@ def test_sd_protect_unlock(client: Client):
client.debug.input("1234") client.debug.input("1234")
yield # you have successfully enabled SD protection 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() client.debug.press_yes()
with client: with client:
@ -77,7 +77,7 @@ def test_sd_protect_unlock(client: Client):
def input_flow_change_pin(): def input_flow_change_pin():
yield # do you really want to 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() client.debug.press_yes()
yield # enter current PIN yield # enter current PIN
@ -93,7 +93,7 @@ def test_sd_protect_unlock(client: Client):
client.debug.input("1234") client.debug.input("1234")
yield # Pin change successful yield # Pin change successful
TR.assert_in(layout().text_content(), "pin__changed") assert TR.pin__changed in layout().text_content()
client.debug.press_yes() client.debug.press_yes()
with client: with client:
@ -105,7 +105,7 @@ def test_sd_protect_unlock(client: Client):
def input_flow_change_pin_format(): def input_flow_change_pin_format():
yield # do you really want to 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() client.debug.press_yes()
yield # enter current PIN yield # enter current PIN
@ -113,9 +113,9 @@ def test_sd_protect_unlock(client: Client):
client.debug.input("1234") client.debug.input("1234")
yield # SD card problem yield # SD card problem
TR.assert_in_multiple( assert (
layout().text_content(), TR.sd_card__unplug_and_insert_correct in layout().text_content()
["sd_card__unplug_and_insert_correct", "sd_card__insert_correct_card"], or TR.sd_card__insert_correct_card in layout().text_content()
) )
client.debug.press_no() # close client.debug.press_no() # close

View File

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

View File

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

View File

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

View File

@ -1,4 +1,6 @@
import json import json
import re
import threading
import typing as t import typing as t
from hashlib import sha256 from hashlib import sha256
from pathlib import Path from pathlib import Path
@ -12,11 +14,13 @@ from . import common
HERE = Path(__file__).resolve().parent HERE = Path(__file__).resolve().parent
ROOT = HERE.parent ROOT = HERE.parent
TRANSLATIONS = ROOT / "core" / "translations" TRANSLATIONS_DIR = ROOT / "core" / "translations"
FONTS_DIR = TRANSLATIONS / "fonts" FONTS_DIR = TRANSLATIONS_DIR / "fonts"
ORDER_FILE = TRANSLATIONS / "order.json" 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( def prepare_blob(
@ -66,110 +70,57 @@ def set_language(client: Client, lang: str):
language_data = build_and_sign_blob(lang, client) language_data = build_and_sign_blob(lang, client)
with client: with client:
device.change_language(client, language_data) # type: ignore device.change_language(client, language_data) # type: ignore
_CURRENT_TRANSLATION.TR = TRANSLATIONS[lang]
def get_lang_json(lang: str) -> translations.JsonDef: def get_lang_json(lang: str) -> translations.JsonDef:
assert lang in LANGUAGES 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: if (fonts_safe3 := lang_json.get("fonts", {}).get("##Safe3")) is not None:
lang_json["fonts"]["T2B1"] = fonts_safe3 lang_json["fonts"]["T2B1"] = fonts_safe3
lang_json["fonts"]["T3B1"] = fonts_safe3 lang_json["fonts"]["T3B1"] = fonts_safe3
return lang_json return lang_json
def _get_all_language_data() -> list[dict[str, str]]: class Translation:
return [_get_language_data(language) for language in LANGUAGES] 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]: TRANSLATIONS = {lang: Translation(lang) for lang in LANGUAGES}
return get_lang_json(lang)["translations"]
all_language_data = _get_all_language_data() def translate(key: str) -> str:
return _CURRENT_TRANSLATION.TR.translate(key)
def _resolve_path_to_texts( def regexp(key: str) -> re.Pattern:
path: str, template: t.Iterable[t.Any] = (), lower: bool = True return _CURRENT_TRANSLATION.TR.as_regexp(key)
) -> 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 assert_equals(text: str, path: str, template: t.Iterable[t.Any] = ()) -> None: def __getattr__(key: str) -> str:
# TODO: we can directly pass in the current device language return translate(key)
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)

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_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_device_pin": "2e261c7367373888adc9d30baf7be99f89da114364a54dd096305ac76f5b02eb",
"T2T1_cs_reset_recovery-test_reset_bip39_t2.py::test_reset_failed_check": "c15845c4d1a46c654b5bb4babed93974d4b7c68b6bb120c74d46a1ec924152ce", "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_bip39.py::test_reset_recovery": "7423b5a3985389a8d290b1bf46a63e895c1d10f5506c62db963cae47dc29ef54",
"T2T1_cs_reset_recovery-test_reset_recovery_slip39_advanced.py::test_reset_recovery": "2d9efb84f2d69506875061f833cdf2dc5d717c006d4e021346c4c1b15a77fe17", "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": "5af5fa1fb4e69036a2cb77234059253f539bc2024d2f5895559950876897eab8", "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_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": "d9f64781fddcd204ec6be552593edfbf6a9f680f6f4fd79058b94317ed29fa23",
"T2T1_cs_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic_256": "8d61ac2c62b0009340d7c2a3559440a24f74af3c0a783a35df26ab72f12f7bf7", "T2T1_cs_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic_256": "8d61ac2c62b0009340d7c2a3559440a24f74af3c0a783a35df26ab72f12f7bf7",