tests: use read_layout everywhere

pull/3686/merge^2
matejcik 7 months ago
parent 7bcbd5a42c
commit 4bfa7857b0

@ -45,26 +45,24 @@ def get_char_category(char: str) -> PassphraseCategory:
return PassphraseCategory.SPECIAL
def go_next(debug: "DebugLink", wait: bool = False) -> "LayoutContent" | None:
def go_next(debug: "DebugLink") -> "LayoutContent":
if debug.model in (models.T2T1, models.T3T1):
return debug.click(buttons.OK, wait=wait) # type: ignore
return debug.click(buttons.OK)
elif debug.model in (models.T2B1,):
return debug.press_right(wait=wait) # type: ignore
return debug.press_right()
else:
raise RuntimeError("Unknown model")
def go_back(
debug: "DebugLink", wait: bool = False, r_middle: bool = False
) -> "LayoutContent" | None:
def go_back(debug: "DebugLink", r_middle: bool = False) -> "LayoutContent":
if debug.model in (models.T2T1, models.T3T1):
return debug.click(buttons.CANCEL, wait=wait) # type: ignore
return debug.click(buttons.CANCEL)
elif debug.model in (models.T2B1,):
if r_middle:
return debug.press_middle(wait=wait) # type: ignore
return debug.press_middle()
else:
return debug.press_left(wait=wait) # type: ignore
return debug.press_left()
else:
raise RuntimeError("Unknown model")
@ -105,10 +103,7 @@ def navigate_to_action_and_press(
)
# Press or hold
if hold_ms:
debug.press_middle_htc(1000)
else:
debug.press_middle(wait=True)
debug.press_middle(hold_ms=hold_ms)
def _get_action_index(wanted_action: str, all_actions: list[str]) -> int:
@ -140,17 +135,17 @@ def _move_one_closer(
if not is_carousel:
# Simply move according to the index in a closed list
if index_diff > 0:
return debug.press_right(wait=True)
return debug.press_right()
else:
return debug.press_left(wait=True)
return debug.press_left()
else:
# Carousel can move in a circle - over the edges
# Always move the shortest way
action_half = len(all_actions) // 2
if index_diff > action_half or -action_half < index_diff < 0:
return debug.press_left(wait=True)
return debug.press_left()
else:
return debug.press_right(wait=True)
return debug.press_right()
def get_possible_btn_texts(path: str) -> str:

@ -23,7 +23,7 @@ def enter_word(
for coords in buttons.type_word(typed_word, is_slip39=is_slip39):
debug.click(coords)
return debug.click(buttons.CONFIRM_WORD, wait=True)
return debug.click(buttons.CONFIRM_WORD)
elif debug.model in (models.T2B1,):
letter_index = 0
layout = debug.read_layout()
@ -32,35 +32,31 @@ def enter_word(
while layout.find_values_by_key("letter_choices"):
letter = word[letter_index]
while not layout.get_middle_choice() == letter:
layout = debug.press_right(wait=True)
layout = debug.press_right()
layout = debug.press_middle(wait=True)
layout = debug.press_middle()
letter_index += 1
# Word choices
while not layout.get_middle_choice() == word:
layout = debug.press_right(wait=True)
layout = debug.press_right()
return debug.press_middle(wait=True)
return debug.press_middle()
else:
raise ValueError("Unknown model")
def confirm_recovery(debug: "DebugLink") -> None:
layout = debug.wait_layout()
layout = debug.read_layout()
TR.assert_equals(layout.title(), "recovery__title")
if debug.model in (models.T2T1, models.T3T1):
debug.click(buttons.OK, wait=True)
debug.click(buttons.OK)
elif debug.model in (models.T2B1,):
debug.press_right(wait=True)
debug.press_right()
debug.press_right()
def select_number_of_words(
debug: "DebugLink", num_of_words: int = 20, wait: bool = True
) -> None:
if wait:
debug.wait_layout()
def select_number_of_words(debug: "DebugLink", num_of_words: int = 20) -> None:
TR.assert_equals(debug.read_layout().text_content(), "recovery__num_of_words")
if debug.model in (models.T2T1, models.T3T1):
# click the number
@ -70,17 +66,17 @@ def select_number_of_words(
num_of_words
) # raises if num of words is invalid
coords = buttons.grid34(index % 3, index // 3)
layout = debug.click(coords, wait=True)
layout = debug.click(coords)
elif debug.model in (models.T2B1,):
layout = debug.press_right(wait=True)
layout = debug.press_right()
TR.assert_equals(layout.title(), "word_count__title")
# navigate to the number and confirm it
word_options = (12, 18, 20, 24, 33)
index = word_options.index(num_of_words)
for _ in range(index):
debug.press_right(wait=True)
layout = debug.press_middle(wait=True)
debug.press_right()
layout = debug.press_middle()
else:
raise ValueError("Unknown model")
@ -95,11 +91,11 @@ def enter_share(
) -> "LayoutContent":
TR.assert_in(debug.read_layout().title(), "recovery__title_recover")
if debug.model in (models.T2B1,):
layout = debug.wait_layout()
layout = debug.read_layout()
for _ in range(layout.page_count()):
layout = debug.press_right(wait=True)
layout = debug.press_right()
else:
layout = debug.click(buttons.OK, wait=True)
layout = debug.click(buttons.OK)
assert "MnemonicKeyboard" in layout.all_components()
@ -149,20 +145,20 @@ def enter_seed_previous_correct(
if go_back:
go_back = False
if debug.model in (models.T2T1, models.T3T1):
debug.swipe_right(wait=True)
debug.swipe_right()
for _ in range(len(bad_word)):
debug.click(buttons.RECOVERY_DELETE, wait=True)
debug.click(buttons.RECOVERY_DELETE)
elif debug.model in (models.T2B1,):
layout = debug.read_layout()
while layout.get_middle_choice() not in DELETE_BTN_TEXTS:
layout = debug.press_right(wait=True)
layout = debug.press_middle(wait=True)
layout = debug.press_right()
layout = debug.press_middle()
for _ in range(len(bad_word)):
while layout.get_middle_choice() not in DELETE_BTN_TEXTS:
layout = debug.press_left(wait=True)
layout = debug.press_middle(wait=True)
layout = debug.press_left()
layout = debug.press_middle()
continue
if i in bad_indexes:
@ -180,16 +176,16 @@ def enter_seed_previous_correct(
def prepare_enter_seed(debug: "DebugLink") -> None:
TR.assert_in(debug.read_layout().text_content(), "recovery__enter_backup")
if debug.model in (models.T2T1, models.T3T1):
debug.click(buttons.OK, wait=True)
debug.click(buttons.OK)
elif debug.model in (models.T2B1,):
debug.press_right(wait=True)
debug.press_right()
TR.assert_equals(debug.read_layout().title(), "recovery__title_recover")
debug.press_right()
layout = debug.press_right(wait=True)
layout = debug.press_right()
assert "MnemonicKeyboard" in layout.all_components()
def finalize(debug: "DebugLink") -> None:
layout = go_next(debug, wait=True)
layout = go_next(debug)
assert layout is not None
assert layout.main_component() == "Homescreen"

@ -18,24 +18,24 @@ def confirm_new_wallet(debug: "DebugLink") -> None:
["reset__title_create_wallet", "reset__title_create_wallet_shamir"],
)
if debug.model in (models.T2T1, models.T3T1):
debug.click(buttons.OK, wait=True)
debug.click(buttons.OK)
elif debug.model in (models.T2B1,):
debug.press_right(wait=True)
debug.press_right(wait=True)
debug.press_right()
debug.press_right()
def confirm_read(debug: "DebugLink", middle_r: bool = False) -> None:
if debug.model in (models.T2T1, models.T3T1):
debug.click(buttons.OK, wait=True)
debug.click(buttons.OK)
elif debug.model in (models.T2B1,):
page_count = debug.read_layout().page_count()
if page_count > 1:
for _ in range(page_count - 1):
debug.press_right(wait=True)
debug.press_right()
if middle_r:
debug.press_middle(wait=True)
debug.press_middle()
else:
debug.press_right(wait=True)
debug.press_right()
def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> None:
@ -43,22 +43,22 @@ def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> Non
assert "NumberInputDialog" in debug.read_layout().all_components()
for _ in range(diff):
debug.click(button)
debug.click(buttons.OK, wait=True)
debug.click(buttons.OK)
elif debug.model in (models.T2B1,):
layout = debug.read_layout()
if layout.title() in TR.translate(
"reset__title_number_of_shares"
) + TR.translate("words__title_threshold"):
# Special info screens
layout = debug.press_right(wait=True)
layout = debug.press_right()
assert "NumberInput" in layout.all_components()
if button == buttons.RESET_MINUS:
for _ in range(diff):
debug.press_left(wait=True)
debug.press_left()
else:
for _ in range(diff):
debug.press_right(wait=True)
debug.press_middle(wait=True)
debug.press_right()
debug.press_middle()
def read_words(
@ -67,13 +67,13 @@ def read_words(
words: list[str] = []
if debug.model in (models.T2B1,):
debug.press_right(wait=True)
debug.press_right()
# Swiping through all the pages and loading the words
layout = debug.read_layout()
for _ in range(layout.page_count() - 1):
words.extend(layout.seed_words())
layout = debug.swipe_up(wait=True)
layout = debug.swipe_up()
assert layout is not None
if debug.model in (models.T2T1, models.T3T1):
words.extend(layout.seed_words())
@ -81,9 +81,9 @@ def read_words(
# There is hold-to-confirm button
if do_htc:
if debug.model in (models.T2T1, models.T3T1):
debug.click_hold(buttons.OK, hold_ms=1500)
debug.click(buttons.OK, hold_ms=1500)
elif debug.model in (models.T2B1,):
debug.press_right_htc(1200)
debug.press_right(hold_ms=1200)
else:
# It would take a very long time to test 16-of-16 with doing 1500 ms HTC after
# each word set
@ -93,13 +93,13 @@ def read_words(
def confirm_words(debug: "DebugLink", words: list[str]) -> None:
layout = debug.wait_layout()
layout = debug.read_layout()
if debug.model in (models.T2T1, models.T3T1):
TR.assert_template(layout.text_content(), "reset__select_word_x_of_y_template")
for _ in range(3):
# "Select word 3 of 20"
# ^
word_pos_match = re.search(r"\d+", debug.wait_layout().text_content())
word_pos_match = re.search(r"\d+", debug.read_layout().text_content())
assert word_pos_match is not None
word_pos = int(word_pos_match.group(0))
# Unifying both the buttons and words to lowercase
@ -108,10 +108,10 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None:
]
wanted_word = words[word_pos - 1].lower()
button_pos = btn_texts.index(wanted_word)
layout = debug.click(buttons.RESET_WORD_CHECK[button_pos], wait=True)
layout = debug.click(buttons.RESET_WORD_CHECK[button_pos])
elif debug.model in (models.T2B1,):
TR.assert_in(layout.text_content(), "reset__select_correct_word")
layout = debug.press_right(wait=True)
layout = debug.press_right()
for _ in range(3):
# "SELECT 2ND WORD"
# ^
@ -122,9 +122,9 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None:
wanted_word = words[word_pos - 1].lower()
while not layout.get_middle_choice() == wanted_word:
layout = debug.press_right(wait=True)
layout = debug.press_right()
layout = debug.press_middle(wait=True)
layout = debug.press_middle()
def validate_mnemonics(mnemonics: list[str], expected_ems: bytes) -> None:

@ -60,16 +60,16 @@ def set_autolock_delay(device_handler: "BackgroundDeviceHandler", delay_ms: int)
device_handler.run(device.apply_settings, auto_lock_delay_ms=delay_ms) # type: ignore
assert "PinKeyboard" in debug.wait_layout().all_components()
assert "PinKeyboard" in debug.read_layout().all_components()
debug.input("1234")
TR.assert_template(
debug.wait_layout().text_content(),
debug.read_layout().text_content(),
"auto_lock__change_template",
)
layout = go_next(debug, wait=True)
layout = go_next(debug)
assert layout.main_component() == "Homescreen"
assert device_handler.result() == "Settings applied"
@ -99,17 +99,17 @@ def test_autolock_interrupts_signing(device_handler: "BackgroundDeviceHandler"):
assert (
"1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1"
in debug.wait_layout().text_content().replace(" ", "")
in debug.read_layout().text_content().replace(" ", "")
)
if debug.model in (models.T2T1, models.T3T1):
debug.click(buttons.OK, wait=True)
layout = debug.click(buttons.OK, wait=True)
debug.click(buttons.OK)
layout = debug.click(buttons.OK)
TR.assert_in(layout.text_content(), "send__total_amount")
assert "0.0039 BTC" in layout.text_content()
elif debug.model in (models.T2B1,):
debug.press_right(wait=True)
layout = debug.press_right(wait=True)
debug.press_right()
layout = debug.press_right()
TR.assert_in(layout.text_content(), "send__total_amount")
assert "0.0039 BTC" in layout.text_content()
@ -146,17 +146,17 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa
assert (
"1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1"
in debug.wait_layout().text_content().replace(" ", "")
in debug.read_layout().text_content().replace(" ", "")
)
if debug.model in (models.T2T1, models.T3T1):
debug.click(buttons.OK, wait=True)
layout = debug.click(buttons.OK, wait=True)
debug.click(buttons.OK)
layout = debug.click(buttons.OK)
TR.assert_in(layout.text_content(), "send__total_amount")
assert "0.0039 BTC" in layout.text_content()
elif debug.model in (models.T2B1,):
debug.press_right(wait=True)
layout = debug.press_right(wait=True)
debug.press_right()
layout = debug.press_right()
TR.assert_in(layout.text_content(), "send__total_amount")
assert "0.0039 BTC" in layout.text_content()
@ -169,9 +169,9 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa
device_handler.client.set_filter(messages.TxAck, sleepy_filter)
# confirm transaction
if debug.model in (models.T2T1, models.T3T1):
debug.click(buttons.OK)
debug.click(buttons.OK, wait=False)
elif debug.model in (models.T2B1,):
debug.press_middle()
debug.press_middle(wait=False)
signatures, tx = device_handler.result()
assert len(signatures) == 1
@ -188,7 +188,7 @@ def test_autolock_passphrase_keyboard(device_handler: "BackgroundDeviceHandler")
# get address
device_handler.run(common.get_test_address) # type: ignore
assert "PassphraseKeyboard" in debug.wait_layout().all_components()
assert "PassphraseKeyboard" in debug.read_layout().all_components()
if debug.model in (models.T2B1,):
# Going into the selected character category
@ -209,9 +209,9 @@ def test_autolock_passphrase_keyboard(device_handler: "BackgroundDeviceHandler")
# Send the passphrase to the client (TT has it clicked already, TR needs to input it)
if debug.model in (models.T2T1, models.T3T1):
debug.click(buttons.OK, wait=True)
debug.click(buttons.OK)
elif debug.model in (models.T2B1,):
debug.input("j" * 8, wait=True)
debug.input("j" * 8)
# address corresponding to "jjjjjjjj" passphrase
assert device_handler.result() == "mnF4yRWJXmzRB6EuBzuVigqeqTqirQupxJ"
@ -225,7 +225,7 @@ def test_autolock_interrupts_passphrase(device_handler: "BackgroundDeviceHandler
# get address
device_handler.run(common.get_test_address) # type: ignore
assert "PassphraseKeyboard" in debug.wait_layout().all_components()
assert "PassphraseKeyboard" in debug.read_layout().all_components()
if debug.model in (models.T2B1,):
# Going into the selected character category
@ -242,17 +242,17 @@ def test_autolock_interrupts_passphrase(device_handler: "BackgroundDeviceHandler
# wait for autolock to kick in
time.sleep(10.1)
assert debug.wait_layout().main_component() == "Lockscreen"
assert debug.read_layout().main_component() == "Lockscreen"
with pytest.raises(exceptions.Cancelled):
device_handler.result()
def unlock_dry_run(debug: "DebugLink") -> "LayoutContent":
TR.assert_in(debug.wait_layout().text_content(), "recovery__check_dry_run")
layout = go_next(debug, wait=True)
TR.assert_in(debug.read_layout().text_content(), "recovery__check_dry_run")
layout = go_next(debug)
assert "PinKeyboard" in layout.all_components()
layout = debug.input(PIN4, wait=True)
layout = debug.input(PIN4)
assert layout is not None
return layout
@ -265,27 +265,27 @@ 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.wait_layout().text_content(), "recovery__num_of_words")
TR.assert_in(debug.read_layout().text_content(), "recovery__num_of_words")
if debug.model in (models.T2B1,):
debug.press_right(wait=True)
debug.press_right()
# wait for autolock to trigger
time.sleep(10.1)
assert debug.wait_layout().main_component() == "Lockscreen"
assert debug.read_layout().main_component() == "Lockscreen"
with pytest.raises(exceptions.Cancelled):
device_handler.result()
# unlock
# lockscreen triggered automatically
debug.wait_layout(wait_for_external_change=True)
layout = go_next(debug, wait=True)
# debug.wait_layout()
layout = go_next(debug)
assert "PinKeyboard" in layout.all_components()
layout = debug.input(PIN4, wait=True)
layout = debug.input(PIN4)
assert layout is not None
# we are back at homescreen
TR.assert_in(debug.wait_layout().text_content(), "recovery__num_of_words")
TR.assert_in(debug.read_layout().text_content(), "recovery__num_of_words")
@pytest.mark.setup_client(pin=PIN4)
@ -301,15 +301,15 @@ def test_dryrun_locks_at_word_entry(device_handler: "BackgroundDeviceHandler"):
recovery.select_number_of_words(debug, 20)
if debug.model in (models.T2T1, models.T3T1):
layout = debug.click(buttons.OK, wait=True)
layout = debug.click(buttons.OK)
assert layout.main_component() == "MnemonicKeyboard"
elif debug.model in (models.T2B1,):
layout = debug.press_right(wait=True)
layout = debug.press_right()
assert "MnemonicKeyboard" in layout.all_components()
# make sure keyboard locks
time.sleep(10.1)
assert debug.wait_layout().main_component() == "Lockscreen"
assert debug.read_layout().main_component() == "Lockscreen"
with pytest.raises(exceptions.Cancelled):
device_handler.result()
@ -327,25 +327,25 @@ def test_dryrun_enter_word_slowly(device_handler: "BackgroundDeviceHandler"):
recovery.select_number_of_words(debug, 20)
if debug.model in (models.T2T1, models.T3T1):
layout = debug.click(buttons.OK, wait=True)
layout = debug.click(buttons.OK)
assert layout.main_component() == "MnemonicKeyboard"
# type the word OCEAN slowly
for coords in buttons.type_word("ocea", is_slip39=True):
time.sleep(9)
debug.click(coords)
layout = debug.click(buttons.CONFIRM_WORD, wait=True)
layout = debug.click(buttons.CONFIRM_WORD)
# should not have locked, even though we took 9 seconds to type each letter
assert layout.main_component() == "MnemonicKeyboard"
elif debug.model in (models.T2B1,):
layout = debug.press_right(wait=True)
layout = debug.press_right()
assert "MnemonicKeyboard" in layout.all_components()
# pressing middle button three times
for _ in range(3):
time.sleep(9)
debug.press_middle()
layout = debug.wait_layout()
layout = debug.read_layout()
# should not have locked, even though we took 9 seconds to type each letter
assert "MnemonicKeyboard" in layout.all_components()
@ -373,7 +373,7 @@ def test_autolock_does_not_interrupt_preauthorized(
coin_name="Testnet",
script_type=messages.InputScriptType.SPENDTAPROOT,
)
debug.press_yes(wait=True)
debug.press_yes()
device_handler.result()
inputs = [

@ -47,19 +47,19 @@ def test_hold_to_lock(device_handler: "BackgroundDeviceHandler"):
models.T3T1: 3500,
}[debug.model]
def hold(duration: int, wait: bool = True) -> None:
def hold(duration: int) -> None:
if debug.model in (models.T2B1,):
debug.press_right_htc(hold_ms=duration)
debug.press_right(hold_ms=duration)
else:
debug.input(x=13, y=37, hold_ms=duration, wait=wait)
debug.click((13, 37), hold_ms=duration)
assert device_handler.features().unlocked is False
# unlock with message
device_handler.run(common.get_test_address)
assert "PinKeyboard" in debug.wait_layout().all_components()
debug.input("1234", wait=True)
assert "PinKeyboard" in debug.read_layout().all_components()
debug.input("1234")
assert device_handler.result()
assert device_handler.features().unlocked is True
@ -77,12 +77,11 @@ def test_hold_to_lock(device_handler: "BackgroundDeviceHandler"):
# unlock by touching
if debug.model in (models.T2B1,):
# Doing a short HTC to simulate a click
debug.press_right_htc(hold_ms=100)
layout = debug.wait_layout()
layout = debug.press_right(hold_ms=100)
else:
layout = debug.click(buttons.INFO, wait=True)
layout = debug.click(buttons.INFO)
assert "PinKeyboard" in layout.all_components()
debug.input("1234", wait=True)
debug.input("1234")
assert device_handler.features().unlocked is True

@ -102,7 +102,7 @@ def prepare_passphrase_dialogue(
) -> Generator["DebugLink", None, None]:
debug = device_handler.debuglink()
device_handler.run(get_test_address) # type: ignore
layout = debug.wait_layout()
layout = debug.read_layout()
assert "PassphraseKeyboard" in layout.all_components()
assert layout.passphrase() == ""
assert _current_category(debug) == PassphraseCategory.MENU

@ -70,7 +70,7 @@ def prepare_passphrase_dialogue(
) -> Generator["DebugLink", None, None]:
debug = device_handler.debuglink()
device_handler.run(get_test_address) # type: ignore
assert debug.wait_layout().main_component() == "PassphraseKeyboard"
assert debug.read_layout().main_component() == "PassphraseKeyboard"
# Resetting the category as it could have been changed by previous tests
global TT_CATEGORY
@ -96,10 +96,10 @@ def go_to_category(debug: "DebugLink", category: PassphraseCategory) -> None:
target_index = TT_CATEGORIES.index(category)
if target_index > current_index:
for _ in range(target_index - current_index):
debug.swipe_left(wait=True)
debug.swipe_left()
else:
for _ in range(current_index - target_index):
debug.swipe_right(wait=True)
debug.swipe_right()
TT_CATEGORY = category # type: ignore
# Category changed, reset coordinates
TT_COORDS_PREV = (0, 0) # type: ignore
@ -125,7 +125,7 @@ def press_char(debug: "DebugLink", char: str) -> None:
time.sleep(1.1)
TT_COORDS_PREV = coords # type: ignore
for _ in range(amount):
debug.click(coords, wait=True)
debug.click(coords)
def input_passphrase(debug: "DebugLink", passphrase: str, check: bool = True) -> None:
@ -142,13 +142,13 @@ def input_passphrase(debug: "DebugLink", passphrase: str, check: bool = True) ->
def enter_passphrase(debug: "DebugLink") -> None:
"""Enter a passphrase"""
coords = buttons.pin_passphrase_grid(11)
debug.click(coords, wait=True)
debug.click(coords)
def delete_char(debug: "DebugLink") -> None:
"""Deletes the last char"""
coords = buttons.pin_passphrase_grid(9)
debug.click(coords, wait=True)
debug.click(coords)
VECTORS = ( # passphrase, address
@ -185,7 +185,6 @@ def test_passphrase_delete(device_handler: "BackgroundDeviceHandler"):
for _ in range(4):
delete_char(debug)
debug.wait_layout()
input_passphrase(debug, CommonPass.SHORT[8 - 4 :])
enter_passphrase(debug)
@ -215,7 +214,6 @@ def test_passphrase_loop_all_characters(device_handler: "BackgroundDeviceHandler
PassphraseCategory.SPECIAL,
):
go_to_category(debug, category)
debug.wait_layout()
enter_passphrase(debug)
coords = buttons.pin_passphrase_grid(11)

@ -95,37 +95,35 @@ def prepare(
elif situation == Situation.PIN_SETUP:
# Set new PIN
device_handler.run(device.change_pin) # type: ignore
TR.assert_in(debug.wait_layout().text_content(), "pin__turn_on")
TR.assert_in(debug.read_layout().text_content(), "pin__turn_on")
if debug.model in (models.T2T1, models.T3T1):
go_next(debug)
elif debug.model in (models.T2B1,):
go_next(debug, wait=True)
go_next(debug, wait=True)
go_next(debug, wait=True)
go_next(debug, wait=True)
go_next(debug)
go_next(debug)
go_next(debug)
go_next(debug)
elif situation == Situation.PIN_CHANGE:
# Change PIN
device_handler.run(device.change_pin) # type: ignore
_input_see_confirm(debug, old_pin)
TR.assert_in(debug.wait_layout().text_content(), "pin__change")
go_next(debug, wait=True)
TR.assert_in(debug.read_layout().text_content(), "pin__change")
go_next(debug)
_input_see_confirm(debug, old_pin)
elif situation == Situation.WIPE_CODE_SETUP:
# Set wipe code
device_handler.run(device.change_wipe_code) # type: ignore
if old_pin:
_input_see_confirm(debug, old_pin)
TR.assert_in(debug.wait_layout().text_content(), "wipe_code__turn_on")
go_next(debug, wait=True)
TR.assert_in(debug.read_layout().text_content(), "wipe_code__turn_on")
go_next(debug)
if debug.model in (models.T2B1,):
go_next(debug, wait=True)
go_next(debug, wait=True)
go_next(debug, wait=True)
go_next(debug)
go_next(debug)
go_next(debug)
if old_pin:
debug.wait_layout()
_input_see_confirm(debug, old_pin)
debug.wait_layout()
_assert_pin_entry(debug)
yield debug
go_next(debug)
@ -146,7 +144,7 @@ def _input_pin(debug: "DebugLink", pin: str, check: bool = False) -> None:
for digit in pin:
digit_index = digits_order.index(digit)
coords = buttons.pin_passphrase_index(digit_index)
debug.click(coords, wait=True)
debug.click(coords)
elif debug.model in (models.T2B1,):
for digit in pin:
navigate_to_action_and_press(debug, digit, TR_PIN_ACTIONS)
@ -159,7 +157,7 @@ def _input_pin(debug: "DebugLink", pin: str, check: bool = False) -> None:
def _see_pin(debug: "DebugLink") -> None:
"""Navigate to "SHOW" and press it"""
if debug.model in (models.T2T1, models.T3T1):
debug.click(buttons.TOP_ROW, wait=True)
debug.click(buttons.TOP_ROW)
elif debug.model in (models.T2B1,):
navigate_to_action_and_press(debug, SHOW, TR_PIN_ACTIONS)
@ -171,7 +169,7 @@ def _delete_pin(debug: "DebugLink", digits_to_delete: int, check: bool = True) -
for _ in range(digits_to_delete):
if debug.model in (models.T2T1, models.T3T1):
debug.click(buttons.pin_passphrase_grid(9), wait=True)
debug.click(buttons.pin_passphrase_grid(9))
elif debug.model in (models.T2B1,):
navigate_to_action_and_press(debug, DELETE, TR_PIN_ACTIONS)
@ -183,7 +181,7 @@ def _delete_pin(debug: "DebugLink", digits_to_delete: int, check: bool = True) -
def _delete_all(debug: "DebugLink", check: bool = True) -> None:
"""Navigate to "DELETE" and hold it until all digits are deleted"""
if debug.model in (models.T2T1, models.T3T1):
debug.click_hold(buttons.pin_passphrase_grid(9), hold_ms=1500)
debug.click(buttons.pin_passphrase_grid(9), hold_ms=1500)
elif debug.model in (models.T2B1,):
navigate_to_action_and_press(debug, DELETE, TR_PIN_ACTIONS, hold_ms=1000)
@ -202,7 +200,7 @@ def _cancel_pin(debug: "DebugLink") -> None:
def _confirm_pin(debug: "DebugLink") -> None:
"""Navigate to "ENTER" and press it"""
if debug.model in (models.T2T1, models.T3T1):
debug.click(buttons.pin_passphrase_grid(11), wait=True)
debug.click(buttons.pin_passphrase_grid(11))
elif debug.model in (models.T2B1,):
navigate_to_action_and_press(debug, ENTER, TR_PIN_ACTIONS)
@ -218,7 +216,7 @@ def _enter_two_times(debug: "DebugLink", pin1: str, pin2: str) -> None:
if debug.model in (models.T2B1,):
# Please re-enter
go_next(debug, wait=True)
go_next(debug)
_input_see_confirm(debug, pin2)
@ -342,7 +340,7 @@ def test_wipe_code_same_as_pin(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler, Situation.WIPE_CODE_SETUP, old_pin="1") as debug:
_input_see_confirm(debug, "1")
# Try again
go_next(debug, wait=True)
go_next(debug)
_enter_two_times(debug, "2", "2")

@ -51,12 +51,12 @@ def prepare_tutorial_and_cancel_after_it(
def go_through_tutorial(debug: "DebugLink") -> None:
debug.press_right(wait=True)
debug.press_right(wait=True)
debug.press_right_htc(hold_ms=1000)
debug.press_right(wait=True)
debug.press_right(wait=True)
layout = debug.press_middle(wait=True)
debug.press_right()
debug.press_right()
debug.press_right(hold_ms=1000)
debug.press_right()
debug.press_right()
layout = debug.press_middle()
TR.assert_equals(layout.title(), "tutorial__title_tutorial_complete")
@ -67,15 +67,15 @@ def test_tutorial_finish(device_handler: "BackgroundDeviceHandler"):
go_through_tutorial(debug)
# FINISH
debug.press_right(wait=True)
debug.press_right()
@pytest.mark.setup_client(uninitialized=True)
def test_tutorial_skip(device_handler: "BackgroundDeviceHandler"):
with prepare_tutorial_and_cancel_after_it(device_handler, cancelled=True) as debug:
# SKIP
debug.press_left(wait=True)
debug.press_right(wait=True)
debug.press_left()
debug.press_right()
@pytest.mark.setup_client(uninitialized=True)
@ -85,8 +85,8 @@ def test_tutorial_again_and_skip(device_handler: "BackgroundDeviceHandler"):
go_through_tutorial(debug)
# AGAIN
debug.press_left(wait=True)
debug.press_left()
# SKIP
debug.press_left(wait=True)
debug.press_right(wait=True)
debug.press_left()
debug.press_right()

@ -205,10 +205,8 @@ def read_and_confirm_mnemonic_tt(
br = yield
assert br.pages is not None
debug.wait_layout()
for i in range(br.pages):
words = debug.wait_layout().seed_words()
words = debug.read_layout().seed_words()
mnemonic.extend(words)
# Not swiping on the last page
if i < br.pages - 1:
@ -219,7 +217,7 @@ def read_and_confirm_mnemonic_tt(
# check share
for _ in range(3):
# Word position is the first number in the text
word_pos_match = re.search(r"\d+", debug.wait_layout().text_content())
word_pos_match = re.search(r"\d+", debug.read_layout().text_content())
assert word_pos_match is not None
word_pos = int(word_pos_match.group(0))
@ -242,7 +240,7 @@ def read_and_confirm_mnemonic_tr(
br = yield
assert br.pages is not None
for _ in range(br.pages - 1):
layout = debug.wait_layout()
layout = debug.read_layout()
words = layout.seed_words()
mnemonic.extend(words)
debug.press_right()
@ -253,7 +251,7 @@ def read_and_confirm_mnemonic_tr(
# check share
for _ in range(3):
word_pos_match = re.search(r"\d+", debug.wait_layout().title())
word_pos_match = re.search(r"\d+", debug.read_layout().title())
assert word_pos_match is not None
word_pos = int(word_pos_match.group(0))
index = word_pos - 1
@ -269,8 +267,8 @@ def read_and_confirm_mnemonic_tr(
def click_info_button_tt(debug: "DebugLink"):
"""Click Shamir backup info button and return back."""
debug.press_info()
yield # Info screen with text
debug.press_yes()
yield
def check_pin_backoff_time(attempts: int, start: float) -> None:
@ -298,12 +296,12 @@ def compact_size(n: int) -> bytes:
def get_text_possible_pagination(debug: "DebugLink", br: messages.ButtonRequest) -> str:
text = debug.wait_layout().text_content()
text = debug.read_layout().text_content()
if br.pages is not None:
for _ in range(br.pages - 1):
debug.swipe_up()
text += " "
text += debug.wait_layout().text_content()
text += debug.read_layout().text_content()
return text

@ -48,6 +48,11 @@ if TYPE_CHECKING:
HERE = Path(__file__).resolve().parent
CORE = HERE.parent / "core"
# So that we see details of failed asserts from this module
pytest.register_assert_rewrite("tests.common")
pytest.register_assert_rewrite("tests.input_flows")
pytest.register_assert_rewrite("tests.input_flows_helpers")
def _emulator_wrapper_main_args() -> list[str]:
"""Look at TREZOR_PROFILING env variable, so that we can generate coverage reports."""

@ -31,7 +31,7 @@ pytestmark = [
def show_details_input_flow(client: Client):
yield
client.debug.wait_layout()
client.debug.read_layout()
# Touch screen click vs pressing right for T2B1
if client.model in (models.T2T1, models.T3T1):
SHOW_ALL_BUTTON_POSITION = (143, 167)

@ -63,17 +63,23 @@ def test_busy_state(client: Client):
@pytest.mark.flaky(max_runs=5)
def test_busy_expiry(client: Client):
WAIT_TIME_MS = 1500
TOLERANCE = 1000
_assert_busy(client, False)
# Start a timer
start = time.monotonic()
# Show the busy dialog.
device.set_busy(client, expiry_ms=1500)
device.set_busy(client, expiry_ms=WAIT_TIME_MS)
_assert_busy(client, True)
# Hasn't expired yet.
time.sleep(0.1)
_assert_busy(client, True)
# Wait until the layout changes
client.debug.wait_layout()
end = time.monotonic()
# Wait for it to expire. Add some tolerance to account for CI/hardware slowness.
time.sleep(4.0)
# Check that the busy dialog was shown for at least WAIT_TIME_MS.
duration = (end - start) * 1000
assert WAIT_TIME_MS <= duration <= WAIT_TIME_MS + TOLERANCE
# Check that the device is no longer busy.
# Also needs to come back to Homescreen (for UI tests).

@ -68,7 +68,7 @@ def client(client: Client) -> Iterator[Client]:
def _check_ping_screen_texts(client: Client, title: str, right_button: str) -> None:
def ping_input_flow(client: Client, title: str, right_button: str):
yield
layout = client.debug.wait_layout()
layout = client.debug.read_layout()
assert layout.title().upper() == title.upper()
assert layout.button_contents()[-1] == right_button.upper()
client.debug.press_yes()

@ -51,7 +51,7 @@ def test_sd_no_format(client: Client):
@pytest.mark.sd_card
@pytest.mark.setup_client(pin="1234")
def test_sd_protect_unlock(client: Client):
layout = client.debug.wait_layout
layout = client.debug.read_layout
def input_flow_enable_sd_protect():
yield # Enter PIN to unlock device

@ -399,7 +399,7 @@ def test_hide_passphrase_from_host(client: Client):
def input_flow():
yield
TR.assert_in(
client.debug.wait_layout().text_content(),
client.debug.read_layout().text_content(),
"passphrase__access_hidden_wallet",
)
if client.model in (models.T2T1, models.T3T1):
@ -436,17 +436,17 @@ def test_hide_passphrase_from_host(client: Client):
def input_flow():
yield
TR.assert_in(
client.debug.wait_layout().text_content(),
client.debug.read_layout().text_content(),
"passphrase__next_screen_will_show_passphrase",
)
client.debug.press_yes()
yield
TR.assert_equals(
client.debug.wait_layout().title(),
client.debug.read_layout().title(),
"passphrase__title_confirm",
)
assert passphrase in client.debug.wait_layout().text_content()
assert passphrase in client.debug.read_layout().text_content()
client.debug.press_yes()
client.watch_layout()

@ -75,16 +75,16 @@ class InputFlowBase:
raise NotImplementedError
def text_content(self) -> str:
return self.debug.wait_layout().text_content()
return self.debug.read_layout().text_content()
def main_component(self) -> str:
return self.debug.wait_layout().main_component()
return self.debug.read_layout().main_component()
def all_components(self) -> list[str]:
return self.debug.wait_layout().all_components()
return self.debug.read_layout().all_components()
def title(self) -> str:
return self.debug.wait_layout().title()
return self.debug.read_layout().title()
class InputFlowSetupDevicePINWIpeCode(InputFlowBase):
@ -215,13 +215,13 @@ class InputFlowSignMessagePagination(InputFlowBase):
layouts: list[LayoutContent] = []
br = yield # confirm address
self.debug.wait_layout()
self.debug.read_layout()
self.debug.press_yes()
br = yield
assert br.pages is not None
for i in range(br.pages):
layout = self.debug.wait_layout()
layout = self.debug.read_layout()
layouts.append(layout)
if i < br.pages - 1:
@ -259,13 +259,13 @@ class InputFlowSignMessagePagination(InputFlowBase):
layouts: list[LayoutContent] = []
br = yield # confirm address
self.debug.wait_layout()
self.debug.read_layout()
self.debug.press_yes()
br = yield
assert br.pages is not None
for i in range(br.pages):
layout = self.debug.wait_layout()
layout = self.debug.read_layout()
layouts.append(layout)
if i < br.pages - 1:
@ -283,9 +283,9 @@ class InputFlowSignMessageInfo(InputFlowBase):
def input_flow_tt(self) -> BRGeneratorType:
yield
# show address/message info
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.press_no(wait=True)
self.debug.click(buttons.CORNER_BUTTON)
self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_no()
self.debug.synchronize_at("IconDialog")
# address mismatch?
self.debug.press_no()
@ -294,15 +294,15 @@ class InputFlowSignMessageInfo(InputFlowBase):
yield
self.debug.press_no()
yield
self.debug.press_no(wait=True)
self.debug.press_yes(wait=True)
self.debug.press_no()
self.debug.press_yes()
def input_flow_t3t1(self) -> BRGeneratorType:
yield
# show address/message info
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.press_no(wait=True)
self.debug.click(buttons.CORNER_BUTTON)
self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_no()
self.debug.synchronize_at("IconDialog")
# address mismatch?
self.debug.press_no()
@ -311,8 +311,8 @@ class InputFlowSignMessageInfo(InputFlowBase):
yield
self.debug.press_no()
yield
self.debug.press_no(wait=True)
self.debug.press_yes(wait=True)
self.debug.press_no()
self.debug.press_yes()
class InputFlowShowAddressQRCode(InputFlowBase):
@ -321,16 +321,16 @@ class InputFlowShowAddressQRCode(InputFlowBase):
def input_flow_tt(self) -> BRGeneratorType:
yield
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON)
# synchronize; TODO get rid of this once we have single-global-layout
self.debug.synchronize_at("SimplePage")
self.debug.swipe_left(wait=True)
self.debug.swipe_right(wait=True)
self.debug.swipe_left(wait=True)
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.press_no(wait=True)
self.debug.press_no(wait=True)
self.debug.swipe_left()
self.debug.swipe_right()
self.debug.swipe_left()
self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_no()
self.debug.press_no()
self.debug.press_yes()
def input_flow_tr(self) -> BRGeneratorType:
@ -356,16 +356,16 @@ class InputFlowShowAddressQRCode(InputFlowBase):
def input_flow_t3t1(self) -> BRGeneratorType:
yield
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON)
# synchronize; TODO get rid of this once we have single-global-layout
self.debug.synchronize_at("SimplePage")
self.debug.swipe_left(wait=True)
self.debug.swipe_right(wait=True)
self.debug.swipe_left(wait=True)
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.press_no(wait=True)
self.debug.press_no(wait=True)
self.debug.swipe_left()
self.debug.swipe_right()
self.debug.swipe_left()
self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_no()
self.debug.press_no()
self.debug.press_yes()
@ -375,13 +375,13 @@ class InputFlowShowAddressQRCodeCancel(InputFlowBase):
def input_flow_tt(self) -> BRGeneratorType:
yield
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON)
# synchronize; TODO get rid of this once we have single-global-layout
self.debug.synchronize_at("SimplePage")
self.debug.swipe_left(wait=True)
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.press_no(wait=True)
self.debug.swipe_left()
self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_no()
self.debug.press_yes()
def input_flow_tr(self) -> BRGeneratorType:
@ -393,7 +393,7 @@ class InputFlowShowAddressQRCodeCancel(InputFlowBase):
self.debug.press_left()
self.debug.press_left()
# Cancel
self.debug.press_left(wait=True)
self.debug.press_left()
# Confirm address mismatch
# Clicking right twice, as some languages can have two pages
self.debug.press_right()
@ -401,13 +401,13 @@ class InputFlowShowAddressQRCodeCancel(InputFlowBase):
def input_flow_t3t1(self) -> BRGeneratorType:
yield
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON)
# synchronize; TODO get rid of this once we have single-global-layout
self.debug.synchronize_at("SimplePage")
self.debug.swipe_left(wait=True)
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.press_no(wait=True)
self.debug.swipe_left()
self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_no()
self.debug.press_yes()
@ -423,7 +423,7 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
self.debug.press_yes()
yield # show address
layout = self.debug.wait_layout()
layout = self.debug.read_layout()
TR.assert_in(layout.title(), "address__title_receive_address")
assert "(MULTISIG)" in layout.title()
assert layout.text_content().replace(" ", "") == self.address
@ -431,24 +431,26 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
self.debug.click(buttons.CORNER_BUTTON)
assert "Qr" in self.all_components()
layout = self.debug.swipe_left(wait=True)
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")
# Three xpub pages with the same testing logic
for xpub_num in range(3):
expected_title = f"MULTISIG XPUB #{xpub_num + 1}"
layout = self.debug.swipe_left(wait=True)
assert expected_title in layout.title()
expected_title = f"MULTISIG XPUB #{xpub_num + 1}\n" + (
"(YOURS)" if self.index == xpub_num else "(COSIGNER)"
)
layout = self.debug.swipe_left()
assert expected_title == layout.title()
content = layout.text_content().replace(" ", "")
assert self.xpubs[xpub_num] in content
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON)
# show address
self.debug.press_no(wait=True)
self.debug.press_no()
# address mismatch
self.debug.press_no(wait=True)
self.debug.press_no()
# show address
self.debug.press_yes()
@ -457,7 +459,7 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
self.debug.press_middle()
yield # show address
layout = self.debug.wait_layout()
layout = self.debug.read_layout()
TR.assert_in(layout.title(), "address__title_receive_address")
assert "(MULTISIG)" in layout.title()
assert layout.text_content().replace(" ", "") == self.address
@ -465,22 +467,24 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
self.debug.press_right()
assert "Qr" in self.all_components()
layout = self.debug.press_right(wait=True)
layout = self.debug.press_right()
# address details
# TODO: locate it more precisely
assert "Multisig 2 of 3" in layout.json_str
# Three xpub pages with the same testing logic
for xpub_num in range(3):
expected_title = f"MULTISIG XPUB #{xpub_num + 1}"
layout = self.debug.press_right(wait=True)
expected_title = f"MULTISIG XPUB #{xpub_num + 1} " + (
"(YOURS)" if self.index == xpub_num else "(COSIGNER)"
)
layout = self.debug.press_right()
assert expected_title in layout.title()
xpub_part_1 = layout.text_content().replace(" ", "")
# Press "SHOW MORE"
layout = self.debug.press_middle(wait=True)
layout = self.debug.press_middle()
xpub_part_2 = layout.text_content().replace(" ", "")
# Go back
self.debug.press_left(wait=True)
self.debug.press_left()
assert self.xpubs[xpub_num] == xpub_part_1 + xpub_part_2
for _ in range(5):
@ -497,7 +501,7 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
self.debug.press_yes()
yield # show address
layout = self.debug.wait_layout()
layout = self.debug.read_layout()
TR.assert_in(layout.title(), "address__title_receive_address")
assert "(MULTISIG)" in layout.title()
assert layout.text_content().replace(" ", "") == self.address
@ -505,7 +509,7 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
self.debug.click(buttons.CORNER_BUTTON)
assert "Qr" in self.all_components()
layout = self.debug.swipe_left(wait=True)
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")
@ -513,16 +517,16 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
# Three xpub pages with the same testing logic
for xpub_num in range(3):
expected_title = f"MULTISIG XPUB #{xpub_num + 1}"
layout = self.debug.swipe_left(wait=True)
layout = self.debug.swipe_left()
assert expected_title in layout.title()
content = layout.text_content().replace(" ", "")
assert self.xpubs[xpub_num] in content
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON)
# show address
self.debug.press_no(wait=True)
self.debug.press_no()
# address mismatch
self.debug.press_no(wait=True)
self.debug.press_no()
# show address
self.debug.press_yes()
@ -540,23 +544,23 @@ class InputFlowShowXpubQRCode(InputFlowBase):
self.debug.press_yes()
br = yield
layout = self.debug.wait_layout()
layout = self.debug.read_layout()
if "coinjoin" in layout.title().lower() or br.code == B.UnknownDerivationPath:
self.debug.press_yes()
br = yield
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON)
# synchronize; TODO get rid of this once we have single-global-layout
self.debug.synchronize_at("SimplePage")
self.debug.swipe_left(wait=True)
self.debug.swipe_right(wait=True)
self.debug.swipe_left(wait=True)
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.press_no(wait=True)
self.debug.press_no(wait=True)
self.debug.swipe_left()
self.debug.swipe_right()
self.debug.swipe_left()
self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_no()
self.debug.press_no()
for _ in range(br.pages - 1):
self.debug.swipe_up(wait=True)
self.debug.swipe_up()
self.debug.press_yes()
def input_flow_tr(self) -> BRGeneratorType:
@ -567,19 +571,19 @@ class InputFlowShowXpubQRCode(InputFlowBase):
self.debug.press_right()
br = yield
layout = self.debug.wait_layout()
layout = self.debug.read_layout()
if "coinjoin" in layout.title().lower() or br.code == B.UnknownDerivationPath:
self.debug.press_yes()
br = yield
# Go into details
self.debug.press_right(wait=True)
self.debug.press_right()
# Go through details and back
self.debug.press_right(wait=True)
self.debug.press_right(wait=True)
self.debug.press_right(wait=True)
self.debug.press_left(wait=True)
self.debug.press_left(wait=True)
self.debug.press_right()
self.debug.press_right()
self.debug.press_right()
self.debug.press_left()
self.debug.press_left()
assert br.pages is not None
for _ in range(br.pages - 1):
self.debug.press_right()
@ -594,23 +598,23 @@ class InputFlowShowXpubQRCode(InputFlowBase):
self.debug.press_yes()
br = yield
layout = self.debug.wait_layout()
layout = self.debug.read_layout()
if "coinjoin" in layout.title().lower() or br.code == B.UnknownDerivationPath:
self.debug.press_yes()
br = yield
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON)
# synchronize; TODO get rid of this once we have single-global-layout
self.debug.synchronize_at("SimplePage")
self.debug.swipe_left(wait=True)
self.debug.swipe_right(wait=True)
self.debug.swipe_left(wait=True)
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.press_no(wait=True)
self.debug.press_no(wait=True)
self.debug.swipe_left()
self.debug.swipe_right()
self.debug.swipe_left()
self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_no()
self.debug.press_no()
for _ in range(br.pages - 1):
self.debug.swipe_up(wait=True)
self.debug.swipe_up()
self.debug.press_yes()
@ -621,21 +625,21 @@ class InputFlowPaymentRequestDetails(InputFlowBase):
def input_flow_tt(self) -> BRGeneratorType:
yield # request to see details
self.debug.wait_layout()
self.debug.read_layout()
self.debug.press_info()
yield # confirm first output
assert self.outputs[0].address[:16] in self.text_content() # type: ignore
self.debug.press_yes()
yield # confirm first output
self.debug.wait_layout()
self.debug.read_layout()
self.debug.press_yes()
yield # confirm second output
assert self.outputs[1].address[:16] in self.text_content() # type: ignore
self.debug.press_yes()
yield # confirm second output
self.debug.wait_layout()
self.debug.read_layout()
self.debug.press_yes()
yield # confirm transaction
@ -645,21 +649,21 @@ class InputFlowPaymentRequestDetails(InputFlowBase):
def input_flow_t3t1(self) -> BRGeneratorType:
yield # request to see details
self.debug.wait_layout()
self.debug.read_layout()
self.debug.press_info()
yield # confirm first output
assert self.outputs[0].address[:16] in self.text_content() # type: ignore
self.debug.press_yes()
yield # confirm first output
self.debug.wait_layout()
self.debug.read_layout()
self.debug.press_yes()
yield # confirm second output
assert self.outputs[1].address[:16] in self.text_content() # type: ignore
self.debug.press_yes()
yield # confirm second output
self.debug.wait_layout()
self.debug.read_layout()
self.debug.press_yes()
yield # confirm transaction
@ -693,20 +697,20 @@ class InputFlowSignTxHighFee(InputFlowBase):
def sign_tx_go_to_info(client: Client) -> Generator[None, None, str]:
yield # confirm output
client.debug.wait_layout()
client.debug.read_layout()
client.debug.press_yes()
yield # confirm output
client.debug.wait_layout()
client.debug.read_layout()
client.debug.press_yes()
yield # confirm transaction
client.debug.wait_layout()
client.debug.read_layout()
client.debug.press_info()
layout = client.debug.wait_layout()
layout = client.debug.read_layout()
content = layout.text_content()
client.debug.click(buttons.CORNER_BUTTON, wait=True)
client.debug.click(buttons.CORNER_BUTTON)
return content
@ -715,24 +719,24 @@ def sign_tx_go_to_info_tr(
client: Client,
) -> Generator[None, None, str]:
yield # confirm address
client.debug.wait_layout()
client.debug.read_layout()
client.debug.press_yes() # CONTINUE
yield # confirm amount
client.debug.wait_layout()
client.debug.read_layout()
client.debug.press_yes() # CONFIRM
screen_texts: list[str] = []
yield # confirm total
layout = client.debug.wait_layout()
layout = client.debug.read_layout()
if "multiple accounts" in layout.text_content().lower():
client.debug.press_middle()
yield
layout = client.debug.press_right(wait=True)
layout = client.debug.press_right()
screen_texts.append(layout.text_content())
layout = client.debug.press_right(wait=True)
layout = client.debug.press_right()
screen_texts.append(layout.text_content())
client.debug.press_left()
@ -833,15 +837,16 @@ class InputFlowSignTxInformationReplacement(InputFlowBase):
yield # confirm address
self.debug.press_yes()
# go back to address
yield
self.debug.press_no()
# confirm address
self.debug.press_yes()
yield # confirm amount
# confirm amount
self.debug.press_yes()
yield # transaction summary, press info
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON)
self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_yes()
def input_flow_tr(self) -> BRGeneratorType:
@ -871,8 +876,8 @@ class InputFlowSignTxInformationReplacement(InputFlowBase):
self.debug.press_yes()
yield # transaction summary, press info
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.click(buttons.CORNER_BUTTON)
self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_yes()
@ -882,10 +887,10 @@ def lock_time_input_flow_tt(
double_confirm: bool = False,
) -> BRGeneratorType:
yield # confirm output
debug.wait_layout()
debug.read_layout()
debug.press_yes()
yield # confirm output
debug.wait_layout()
debug.read_layout()
debug.press_yes()
br = yield # confirm locktime
@ -904,10 +909,10 @@ def lock_time_input_flow_tr(
layout_assert_func: Callable[[DebugLink, messages.ButtonRequest], None],
) -> BRGeneratorType:
yield # confirm address
debug.wait_layout()
debug.read_layout()
debug.press_yes()
yield # confirm amount
debug.wait_layout()
debug.read_layout()
debug.press_yes()
br = yield # confirm locktime
@ -982,7 +987,7 @@ class InputFlowEIP712ShowMore(InputFlowBase):
self.debug.press_yes()
yield # confirm domain
self.debug.wait_layout()
self.debug.read_layout()
self._confirm_show_more()
# confirm domain properties
@ -991,11 +996,11 @@ class InputFlowEIP712ShowMore(InputFlowBase):
self.debug.press_yes()
yield # confirm message
self.debug.wait_layout()
self.debug.read_layout()
self._confirm_show_more()
yield # confirm message.from
self.debug.wait_layout()
self.debug.read_layout()
self._confirm_show_more()
# confirm message.from properties
@ -1004,7 +1009,7 @@ class InputFlowEIP712ShowMore(InputFlowBase):
self.debug.press_yes()
yield # confirm message.to
self.debug.wait_layout()
self.debug.read_layout()
self._confirm_show_more()
# confirm message.to properties
@ -1238,15 +1243,15 @@ class InputFlowSlip39BasicBackup(InputFlowBase):
def input_flow_tt(self) -> BRGeneratorType:
yield # 1. Checklist
self.debug.press_yes()
yield # 2. Number of shares (5)
if self.click_info:
yield from click_info_button_tt(self.debug)
yield # 2. Number of shares (5)
self.debug.press_yes()
yield # 3. Checklist
self.debug.press_yes()
yield # 4. Threshold (3)
if self.click_info:
yield from click_info_button_tt(self.debug)
yield # 4. Threshold (3)
self.debug.press_yes()
yield # 5. Checklist
self.debug.press_yes()
@ -1409,26 +1414,26 @@ class InputFlowSlip39AdvancedBackup(InputFlowBase):
def input_flow_tt(self) -> BRGeneratorType:
yield # 1. Checklist
self.debug.press_yes()
yield # 2. Set and confirm group count
if self.click_info:
yield from click_info_button_tt(self.debug)
yield # 2. Set and confirm group count
self.debug.press_yes()
yield # 3. Checklist
self.debug.press_yes()
yield # 4. Set and confirm group threshold
if self.click_info:
yield from click_info_button_tt(self.debug)
yield # 4. Set and confirm group threshold
self.debug.press_yes()
yield # 5. Checklist
self.debug.press_yes()
for _ in range(5): # for each of 5 groups
yield # Set & Confirm number of shares
if self.click_info:
yield from click_info_button_tt(self.debug)
yield # Set & Confirm number of shares
self.debug.press_yes()
yield # Set & confirm share threshold value
if self.click_info:
yield from click_info_button_tt(self.debug)
yield # Set & confirm share threshold value
self.debug.press_yes()
yield # Confirm show seeds
self.debug.press_yes()

@ -16,16 +16,16 @@ class PinFlow:
self, pin: str, second_different_pin: str | None = None
) -> BRGeneratorType:
yield # Enter PIN
assert "PinKeyboard" in self.debug.wait_layout().all_components()
assert "PinKeyboard" in self.debug.read_layout().all_components()
self.debug.input(pin)
if self.client.model is models.T2B1:
yield # Reenter PIN
TR.assert_in(
self.debug.wait_layout().text_content(), "pin__reenter_to_confirm"
self.debug.read_layout().text_content(), "pin__reenter_to_confirm"
)
self.debug.press_yes()
yield # Enter PIN again
assert "PinKeyboard" in self.debug.wait_layout().all_components()
assert "PinKeyboard" in self.debug.read_layout().all_components()
if second_different_pin is not None:
self.debug.input(second_different_pin)
else:
@ -39,7 +39,7 @@ class BackupFlow:
def confirm_new_wallet(self) -> BRGeneratorType:
yield
TR.assert_in(self.debug.wait_layout().text_content(), "reset__by_continuing")
TR.assert_in(self.debug.read_layout().text_content(), "reset__by_continuing")
if self.client.model is models.T2B1:
self.debug.press_right()
self.debug.press_yes()
@ -51,7 +51,7 @@ class RecoveryFlow:
self.debug = self.client.debug
def _text_content(self) -> str:
return self.debug.wait_layout().text_content()
return self.debug.read_layout().text_content()
def confirm_recovery(self) -> BRGeneratorType:
yield
@ -86,26 +86,26 @@ class RecoveryFlow:
yield
TR.assert_in(self._text_content(), "recovery__enter_backup")
is_dry_run = any(
title in self.debug.wait_layout().title().lower()
title in self.debug.read_layout().title().lower()
for title in TR.translate("recovery__title_dry_run", lower=True)
)
if self.client.model is models.T2B1 and not is_dry_run:
# Normal recovery has extra info (not dry run)
self.debug.press_right(wait=True)
self.debug.press_right(wait=True)
self.debug.press_right()
self.debug.press_right()
self.debug.press_yes()
def enter_any_share(self) -> BRGeneratorType:
yield
TR.assert_in(self._text_content(), "recovery__enter_any_share")
is_dry_run = any(
title in self.debug.wait_layout().title().lower()
title in self.debug.read_layout().title().lower()
for title in TR.translate("recovery__title_dry_run", lower=True)
)
if self.client.model is models.T2B1 and not is_dry_run:
# Normal recovery has extra info (not dry run)
self.debug.press_right(wait=True)
self.debug.press_right(wait=True)
self.debug.press_right()
self.debug.press_right()
self.debug.press_yes()
def abort_recovery(self, confirm: bool) -> BRGeneratorType:
@ -129,7 +129,7 @@ class RecoveryFlow:
br = yield
assert br.code == B.MnemonicWordCount
if self.client.model is models.T2B1:
TR.assert_in(self.debug.wait_layout().title(), "word_count__title")
TR.assert_in(self.debug.read_layout().title(), "word_count__title")
else:
TR.assert_in(self._text_content(), "recovery__num_of_words")
self.debug.input(str(num_words))
@ -223,7 +223,7 @@ class RecoveryFlow:
def input_mnemonic(self, mnemonic: list[str]) -> BRGeneratorType:
br = yield
assert br.code == B.MnemonicInput
assert "MnemonicKeyboard" in self.debug.wait_layout().all_components()
assert "MnemonicKeyboard" in self.debug.read_layout().all_components()
for _, word in enumerate(mnemonic):
self.debug.input(word)
@ -248,8 +248,8 @@ class RecoveryFlow:
self,
) -> BRGeneratorType:
# Moving through the INFO button
self.debug.press_info()
yield
self.debug.press_info()
self.debug.swipe_up()
self.debug.press_yes()
@ -264,7 +264,7 @@ class EthereumFlow:
def confirm_data(self, info: bool = False, cancel: bool = False) -> BRGeneratorType:
yield
TR.assert_equals(
self.debug.wait_layout().title(), "ethereum__title_confirm_data"
self.debug.read_layout().title(), "ethereum__title_confirm_data"
)
if info:
self.debug.press_info()
@ -276,11 +276,11 @@ class EthereumFlow:
def paginate_data(self) -> BRGeneratorType:
br = yield
TR.assert_equals(
self.debug.wait_layout().title(), "ethereum__title_confirm_data"
self.debug.read_layout().title(), "ethereum__title_confirm_data"
)
assert br.pages is not None
for i in range(br.pages):
self.debug.wait_layout()
self.debug.read_layout()
if i < br.pages - 1:
self.debug.swipe_up()
self.debug.press_yes()
@ -288,13 +288,13 @@ class EthereumFlow:
def paginate_data_go_back(self) -> BRGeneratorType:
br = yield
TR.assert_equals(
self.debug.wait_layout().title(), "ethereum__title_confirm_data"
self.debug.read_layout().title(), "ethereum__title_confirm_data"
)
assert br.pages is not None
assert br.pages > 2
if self.client.model in (models.T2T1, models.T3T1):
self.debug.swipe_up(wait=True)
self.debug.swipe_up(wait=True)
self.debug.swipe_up()
self.debug.swipe_up()
self.debug.click(self.GO_BACK)
else:
self.debug.press_right()
@ -310,7 +310,7 @@ class EthereumFlow:
go_back_from_summary: bool = False,
) -> BRGeneratorType:
yield
TR.assert_equals(self.debug.wait_layout().title(), "words__recipient")
TR.assert_equals(self.debug.read_layout().title(), "words__recipient")
if self.client.model in (models.T2T1, models.T3T1):
if cancel:
@ -319,10 +319,10 @@ class EthereumFlow:
self.debug.press_yes()
yield
TR.assert_equals(
self.debug.wait_layout().title(), "words__title_summary"
self.debug.read_layout().title(), "words__title_summary"
)
TR.assert_in(
self.debug.wait_layout().text_content(), "send__maximum_fee"
self.debug.read_layout().text_content(), "send__maximum_fee"
)
if go_back_from_summary:
self.debug.press_no()
@ -330,14 +330,14 @@ class EthereumFlow:
self.debug.press_yes()
yield
if info:
self.debug.press_info(wait=True)
self.debug.press_info()
TR.assert_in(
self.debug.wait_layout().text_content(), "ethereum__gas_limit"
self.debug.read_layout().text_content(), "ethereum__gas_limit"
)
TR.assert_in(
self.debug.wait_layout().text_content(), "ethereum__gas_price"
self.debug.read_layout().text_content(), "ethereum__gas_price"
)
self.debug.press_no(wait=True)
self.debug.press_no()
self.debug.press_yes()
else:
if cancel:
@ -346,7 +346,7 @@ class EthereumFlow:
self.debug.press_right()
yield
TR.assert_in(
self.debug.wait_layout().text_content(), "send__maximum_fee"
self.debug.read_layout().text_content(), "send__maximum_fee"
)
if go_back_from_summary:
self.debug.press_left()
@ -354,16 +354,16 @@ class EthereumFlow:
self.debug.press_right()
yield
if info:
self.debug.press_right(wait=True)
self.debug.press_right()
TR.assert_in(
self.debug.wait_layout().text_content(), "ethereum__gas_limit"
self.debug.read_layout().text_content(), "ethereum__gas_limit"
)
self.debug.press_right(wait=True)
self.debug.press_right()
TR.assert_in(
self.debug.wait_layout().text_content(), "ethereum__gas_price"
self.debug.read_layout().text_content(), "ethereum__gas_price"
)
self.debug.press_left(wait=True)
self.debug.press_left(wait=True)
self.debug.press_left()
self.debug.press_left()
self.debug.press_middle()
def confirm_tx_staking(
@ -372,7 +372,7 @@ class EthereumFlow:
) -> BRGeneratorType:
yield
TR.assert_equals_multiple(
self.debug.wait_layout().title(),
self.debug.read_layout().title(),
[
"ethereum__staking_stake",
"ethereum__staking_unstake",
@ -380,7 +380,7 @@ class EthereumFlow:
],
)
TR.assert_equals_multiple(
self.debug.wait_layout().text_content(),
self.debug.read_layout().text_content(),
[
"ethereum__staking_stake_intro",
"ethereum__staking_unstake_intro",
@ -390,56 +390,56 @@ class EthereumFlow:
if self.client.model in (models.T2T1, models.T3T1):
# confirm intro
if info:
self.debug.press_info(wait=True)
self.debug.press_info()
TR.assert_equals_multiple(
self.debug.wait_layout().title(),
self.debug.read_layout().title(),
[
"ethereum__staking_stake_address",
"ethereum__staking_claim_address",
],
)
self.debug.press_no(wait=True)
self.debug.press_no()
self.debug.press_yes()
yield
# confirm summary
if info:
self.debug.press_info(wait=True)
self.debug.press_info()
TR.assert_in(
self.debug.wait_layout().text_content(), "ethereum__gas_limit"
self.debug.read_layout().text_content(), "ethereum__gas_limit"
)
TR.assert_in(
self.debug.wait_layout().text_content(), "ethereum__gas_price"
self.debug.read_layout().text_content(), "ethereum__gas_price"
)
self.debug.press_no(wait=True)
self.debug.press_no()
self.debug.press_yes()
yield
else:
# confirm intro
if info:
self.debug.press_right(wait=True)
self.debug.press_right()
TR.assert_equals_multiple(
self.debug.wait_layout().title(),
self.debug.read_layout().title(),
[
"ethereum__staking_stake_address",
"ethereum__staking_claim_address",
],
)
self.debug.press_left(wait=True)
self.debug.press_left()
self.debug.press_middle()
yield
# confirm summary
if info:
self.debug.press_right(wait=True)
self.debug.press_right()
TR.assert_in(
self.debug.wait_layout().text_content(), "ethereum__gas_limit"
self.debug.read_layout().text_content(), "ethereum__gas_limit"
)
self.debug.press_right(wait=True)
self.debug.press_right()
TR.assert_in(
self.debug.wait_layout().text_content(), "ethereum__gas_price"
self.debug.read_layout().text_content(), "ethereum__gas_price"
)
self.debug.press_left(wait=True)
self.debug.press_left(wait=True)
self.debug.press_left()
self.debug.press_left()
self.debug.press_middle()
yield

@ -42,9 +42,9 @@ def test_abort(core_emulator: Emulator):
device_handler.run(device.recover, pin_protection=False)
assert debug.wait_layout().title() == "RECOVER WALLET"
assert debug.read_layout().title() == "RECOVER WALLET"
layout = debug.click(buttons.OK, wait=True)
layout = debug.click(buttons.OK)
assert "number of words" in layout.text_content()
debug = _restart(device_handler, core_emulator)
@ -55,14 +55,14 @@ def test_abort(core_emulator: Emulator):
# no waiting for layout because layout doesn't change
assert "number of words" in debug.read_layout().text_content()
# clicking at 24 in word choice (the same coords as CANCEL)
layout = debug.click(buttons.CANCEL, wait=True)
layout = debug.click(buttons.CANCEL)
# Cancelling the backup
assert "Enter your backup" in debug.read_layout().text_content()
layout = debug.click(buttons.CANCEL, wait=True)
layout = debug.click(buttons.CANCEL)
assert layout.title() in ("ABORT RECOVERY", "CANCEL RECOVERY")
layout = debug.click(buttons.OK, wait=True)
layout = debug.click(buttons.OK)
assert layout.main_component() == "Homescreen"
features = device_handler.features()
@ -89,7 +89,7 @@ def test_recovery_single_reset(core_emulator: Emulator):
assert features.recovery_mode is True
# we need to enter the number of words again, that's a feature
recovery.select_number_of_words(debug, wait=False)
recovery.select_number_of_words(debug)
recovery.enter_shares(debug, MNEMONIC_SLIP39_BASIC_20_3of6)
recovery.finalize(debug)
@ -126,7 +126,7 @@ def test_recovery_on_old_wallet(core_emulator: Emulator):
assert features.recovery_mode is True
# enter number of words
recovery.select_number_of_words(debug, wait=False)
recovery.select_number_of_words(debug)
first_share = MNEMONIC_SLIP39_BASIC_20_3of6[0]
words = first_share.split(" ")
@ -134,11 +134,11 @@ def test_recovery_on_old_wallet(core_emulator: Emulator):
# start entering first share
assert "Enter any share" in debug.read_layout().text_content()
debug.press_yes()
assert debug.wait_layout().main_component() == "MnemonicKeyboard"
assert debug.read_layout().main_component() == "MnemonicKeyboard"
# enter first word
debug.input(words[0])
layout = debug.wait_layout()
layout = debug.read_layout()
# while keyboard is open, hit the device with Initialize/GetFeatures
device_handler.client.init_device()
@ -148,7 +148,7 @@ def test_recovery_on_old_wallet(core_emulator: Emulator):
for word in words[1:]:
assert layout.main_component() == "MnemonicKeyboard"
debug.input(word)
layout = debug.wait_layout()
layout = debug.read_layout()
# check that we entered the first share successfully
assert "1 of 3 shares entered" in layout.text_content()
@ -202,7 +202,7 @@ def test_recovery_multiple_resets(core_emulator: Emulator):
assert features.recovery_mode is True
# enter the number of words again, that's a feature!
recovery.select_number_of_words(debug, wait=False)
recovery.select_number_of_words(debug)
# enter shares and restart after each one
enter_shares_with_restarts(debug)

@ -13,19 +13,15 @@ def _enter_word(
for coords in buttons.type_word(typed_word, is_slip39=is_slip39):
debug.click(coords)
return debug.click(buttons.CONFIRM_WORD, wait=True)
return debug.click(buttons.CONFIRM_WORD)
def confirm_recovery(debug: "DebugLink") -> None:
debug.click(buttons.OK, wait=True)
debug.click(buttons.OK)
def select_number_of_words(
debug: "DebugLink", num_of_words: int = 20, wait: bool = True
) -> None:
if wait:
debug.wait_layout()
debug.click(buttons.OK, wait=True)
def select_number_of_words(debug: "DebugLink", num_of_words: int = 20) -> None:
debug.click(buttons.OK)
# click the number
word_option_offset = 6
@ -34,13 +30,13 @@ def select_number_of_words(
num_of_words
) # raises if num of words is invalid
coords = buttons.grid34(index % 3, index // 3)
debug.click(coords, wait=True)
debug.click(coords)
def enter_share(
debug: "DebugLink", share: str, is_first: bool = True
) -> "LayoutContent":
layout = debug.click(buttons.OK, wait=True)
layout = debug.click(buttons.OK)
for word in share.split(" "):
layout = _enter_word(debug, word, is_slip39=True)
return layout

Loading…
Cancel
Save