1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-08-01 03:18:12 +00:00

tests: use read_layout everywhere

This commit is contained in:
matejcik 2023-11-21 13:51:59 +01:00
parent e2547db43d
commit 162f13c6ea
18 changed files with 315 additions and 320 deletions

View File

@ -42,25 +42,23 @@ def get_char_category(char: str) -> PassphraseCategory:
return PassphraseCategory.SPECIAL return PassphraseCategory.SPECIAL
def go_next(debug: "DebugLink", wait: bool = False) -> "LayoutContent" | None: def go_next(debug: "DebugLink") -> "LayoutContent":
if debug.model == "T": if debug.model == "T":
return debug.click(buttons.OK, wait=wait) # type: ignore return debug.click(buttons.OK)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
return debug.press_right(wait=wait) # type: ignore return debug.press_right()
else: else:
raise RuntimeError("Unknown model") raise RuntimeError("Unknown model")
def go_back( def go_back(debug: "DebugLink", r_middle: bool = False) -> "LayoutContent":
debug: "DebugLink", wait: bool = False, r_middle: bool = False
) -> "LayoutContent" | None:
if debug.model == "T": if debug.model == "T":
return debug.click(buttons.CANCEL, wait=wait) # type: ignore return debug.click(buttons.CANCEL)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
if r_middle: if r_middle:
return debug.press_middle(wait=wait) # type: ignore return debug.press_middle()
else: else:
return debug.press_left(wait=wait) # type: ignore return debug.press_left()
else: else:
raise RuntimeError("Unknown model") raise RuntimeError("Unknown model")
@ -101,10 +99,7 @@ def navigate_to_action_and_press(
) )
# Press or hold # Press or hold
if hold_ms: debug.press_middle(hold_ms=hold_ms)
debug.press_middle_htc(1000)
else:
debug.press_middle(wait=True)
def _get_action_index(wanted_action: str, all_actions: list[str]) -> int: def _get_action_index(wanted_action: str, all_actions: list[str]) -> int:
@ -136,14 +131,14 @@ def _move_one_closer(
if not is_carousel: if not is_carousel:
# Simply move according to the index in a closed list # Simply move according to the index in a closed list
if index_diff > 0: if index_diff > 0:
return debug.press_right(wait=True) return debug.press_right()
else: else:
return debug.press_left(wait=True) return debug.press_left()
else: else:
# Carousel can move in a circle - over the edges # Carousel can move in a circle - over the edges
# Always move the shortest way # Always move the shortest way
action_half = len(all_actions) // 2 action_half = len(all_actions) // 2
if index_diff > action_half or -action_half < index_diff < 0: if index_diff > action_half or -action_half < index_diff < 0:
return debug.press_left(wait=True) return debug.press_left()
else: else:
return debug.press_right(wait=True) return debug.press_right()

View File

@ -15,7 +15,7 @@ def enter_word(
for coords in buttons.type_word(typed_word, is_slip39=is_slip39): for coords in buttons.type_word(typed_word, is_slip39=is_slip39):
debug.click(coords) debug.click(coords)
return debug.click(buttons.CONFIRM_WORD, wait=True) return debug.click(buttons.CONFIRM_WORD)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
letter_index = 0 letter_index = 0
layout = debug.read_layout() layout = debug.read_layout()
@ -24,36 +24,32 @@ def enter_word(
while layout.find_values_by_key("letter_choices"): while layout.find_values_by_key("letter_choices"):
letter = word[letter_index] letter = word[letter_index]
while not layout.get_middle_choice() == letter: 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 letter_index += 1
# Word choices # Word choices
while not layout.get_middle_choice() == word: 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: else:
raise ValueError("Unknown model") raise ValueError("Unknown model")
def confirm_recovery(debug: "DebugLink") -> None: def confirm_recovery(debug: "DebugLink") -> None:
layout = debug.wait_layout() layout = debug.read_layout()
if debug.model == "T": if debug.model == "T":
assert layout.title().startswith(("RECOVER WALLET", "BACKUP CHECK")) assert layout.title().startswith(("RECOVER WALLET", "BACKUP CHECK"))
debug.click(buttons.OK, wait=True) debug.click(buttons.OK)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
assert layout.title() == "RECOVER WALLET" assert layout.title() == "RECOVER WALLET"
debug.press_right(wait=True) debug.press_right()
debug.press_right() debug.press_right()
def select_number_of_words( def select_number_of_words(debug: "DebugLink", num_of_words: int = 20) -> None:
debug: "DebugLink", num_of_words: int = 20, wait: bool = True
) -> None:
if wait:
debug.wait_layout()
if debug.model == "T": if debug.model == "T":
assert "number of words" in debug.read_layout().text_content() assert "number of words" in debug.read_layout().text_content()
assert debug.read_layout().title() in ( assert debug.read_layout().title() in (
@ -68,10 +64,10 @@ def select_number_of_words(
num_of_words num_of_words
) # raises if num of words is invalid ) # raises if num of words is invalid
coords = buttons.grid34(index % 3, index // 3) coords = buttons.grid34(index % 3, index // 3)
layout = debug.click(coords, wait=True) layout = debug.click(coords)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
assert "number of words" in debug.read_layout().text_content() assert "number of words" in debug.read_layout().text_content()
layout = debug.press_right(wait=True) layout = debug.press_right()
assert layout.title() == "NUMBER OF WORDS" assert layout.title() == "NUMBER OF WORDS"
@ -79,8 +75,8 @@ def select_number_of_words(
word_options = (12, 18, 20, 24, 33) word_options = (12, 18, 20, 24, 33)
index = word_options.index(num_of_words) index = word_options.index(num_of_words)
for _ in range(index): for _ in range(index):
debug.press_right(wait=True) debug.press_right()
layout = debug.press_middle(wait=True) layout = debug.press_middle()
else: else:
raise ValueError("Unknown model") raise ValueError("Unknown model")
@ -94,7 +90,7 @@ def enter_share(
debug: "DebugLink", share: str, is_first: bool = True debug: "DebugLink", share: str, is_first: bool = True
) -> "LayoutContent": ) -> "LayoutContent":
if debug.model == "T": if debug.model == "T":
layout = debug.click(buttons.OK, wait=True) layout = debug.click(buttons.OK)
assert layout.main_component() == "MnemonicKeyboard" assert layout.main_component() == "MnemonicKeyboard"
for word in share.split(" "): for word in share.split(" "):
@ -102,12 +98,12 @@ def enter_share(
return layout return layout
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
assert "RECOVER WALLET" in debug.wait_layout().title() assert "RECOVER WALLET" in debug.read_layout().title()
layout = debug.press_right(wait=True) layout = debug.press_right()
if is_first: if is_first:
# Word entering info # Word entering info
debug.press_right() debug.press_right()
layout = debug.press_right(wait=True) layout = debug.press_right()
assert "MnemonicKeyboard" in layout.all_components() assert "MnemonicKeyboard" in layout.all_components()
for word in share.split(" "): for word in share.split(" "):
@ -132,14 +128,14 @@ def enter_shares(debug: "DebugLink", shares: list[str]) -> None:
def enter_seed(debug: "DebugLink", seed_words: list[str]) -> None: def enter_seed(debug: "DebugLink", seed_words: list[str]) -> None:
assert "Enter" in debug.read_layout().text_content() assert "Enter" in debug.read_layout().text_content()
if debug.model == "T": if debug.model == "T":
layout = debug.click(buttons.OK, wait=True) layout = debug.click(buttons.OK)
assert layout.main_component() == "MnemonicKeyboard" assert layout.main_component() == "MnemonicKeyboard"
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
layout = debug.press_right(wait=True) layout = debug.press_right()
assert "RECOVER WALLET" in layout.title() assert "RECOVER WALLET" in layout.title()
debug.press_right() debug.press_right()
layout = debug.press_right(wait=True) layout = debug.press_right()
assert "MnemonicKeyboard" in layout.all_components() assert "MnemonicKeyboard" in layout.all_components()
for word in seed_words: for word in seed_words:
@ -149,6 +145,6 @@ def enter_seed(debug: "DebugLink", seed_words: list[str]) -> None:
def finalize(debug: "DebugLink") -> None: def finalize(debug: "DebugLink") -> None:
layout = go_next(debug, wait=True) layout = go_next(debug)
assert layout is not None assert layout is not None
assert layout.main_component() == "Homescreen" assert layout.main_component() == "Homescreen"

View File

@ -11,13 +11,13 @@ if TYPE_CHECKING:
def confirm_new_wallet(debug: "DebugLink") -> None: def confirm_new_wallet(debug: "DebugLink") -> None:
layout = debug.wait_layout() layout = debug.read_layout()
assert layout.title().startswith("CREATE WALLET") assert layout.title().startswith("CREATE WALLET")
if debug.model == "T": if debug.model == "T":
debug.click(buttons.OK, wait=True) debug.click(buttons.OK)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
debug.press_right(wait=True) debug.press_right()
debug.press_right(wait=True) debug.press_right()
def confirm_read(debug: "DebugLink", title: str, middle_r: bool = False) -> None: def confirm_read(debug: "DebugLink", title: str, middle_r: bool = False) -> None:
@ -42,14 +42,14 @@ def confirm_read(debug: "DebugLink", title: str, middle_r: bool = False) -> None
assert title.upper() in layout.title() assert title.upper() in layout.title()
if debug.model == "T": if debug.model == "T":
debug.click(buttons.OK, wait=True) debug.click(buttons.OK)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
if layout.page_count() > 1: if layout.page_count() > 1:
debug.press_right(wait=True) debug.press_right()
if middle_r: if middle_r:
debug.press_middle(wait=True) debug.press_middle()
else: else:
debug.press_right(wait=True) debug.press_right()
def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> None: def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> None:
@ -57,20 +57,20 @@ def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> Non
assert "NumberInputDialog" in debug.read_layout().all_components() assert "NumberInputDialog" in debug.read_layout().all_components()
for _ in range(diff): for _ in range(diff):
debug.click(button) debug.click(button)
debug.click(buttons.OK, wait=True) debug.click(buttons.OK)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
layout = debug.read_layout() layout = debug.read_layout()
if layout.title() in ("NUMBER OF SHARES", "THRESHOLD"): if layout.title() in ("NUMBER OF SHARES", "THRESHOLD"):
# Special info screens # Special info screens
layout = debug.press_right(wait=True) layout = debug.press_right()
assert "NumberInput" in layout.all_components() assert "NumberInput" in layout.all_components()
if button == buttons.RESET_MINUS: if button == buttons.RESET_MINUS:
for _ in range(diff): for _ in range(diff):
debug.press_left(wait=True) debug.press_left()
else: else:
for _ in range(diff): for _ in range(diff):
debug.press_right(wait=True) debug.press_right()
debug.press_middle(wait=True) debug.press_middle()
def read_words( def read_words(
@ -95,12 +95,12 @@ def read_words(
assert layout.title() == "STANDARD BACKUP" assert layout.title() == "STANDARD BACKUP"
assert "Write down" in layout.text_content() assert "Write down" in layout.text_content()
layout = debug.press_right(wait=True) layout = debug.press_right()
# Swiping through all the pages and loading the words # Swiping through all the pages and loading the words
for _ in range(layout.page_count() - 1): for _ in range(layout.page_count() - 1):
words.extend(layout.seed_words()) words.extend(layout.seed_words())
layout = debug.swipe_up(wait=True) layout = debug.swipe_up()
assert layout is not None assert layout is not None
if debug.model == "T": if debug.model == "T":
words.extend(layout.seed_words()) words.extend(layout.seed_words())
@ -108,9 +108,9 @@ def read_words(
# There is hold-to-confirm button # There is hold-to-confirm button
if do_htc: if do_htc:
if debug.model == "T": if debug.model == "T":
debug.click_hold(buttons.OK, hold_ms=1500) debug.click(buttons.OK, hold_ms=1500)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
debug.press_right_htc(1200) debug.press_right(hold_ms=1200)
else: else:
# It would take a very long time to test 16-of-16 with doing 1500 ms HTC after # It would take a very long time to test 16-of-16 with doing 1500 ms HTC after
# each word set # each word set
@ -120,7 +120,7 @@ def read_words(
def confirm_words(debug: "DebugLink", words: list[str]) -> None: def confirm_words(debug: "DebugLink", words: list[str]) -> None:
layout = debug.wait_layout() layout = debug.read_layout()
if debug.model == "T": if debug.model == "T":
assert "Select word" in layout.text_content() assert "Select word" in layout.text_content()
for _ in range(3): for _ in range(3):
@ -133,10 +133,10 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None:
] ]
wanted_word = words[word_pos - 1].lower() wanted_word = words[word_pos - 1].lower()
button_pos = btn_texts.index(wanted_word) 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 == "Safe 3": elif debug.model == "Safe 3":
assert "Select the correct word" in layout.text_content() assert "Select the correct word" in layout.text_content()
layout = debug.press_right(wait=True) layout = debug.press_right()
for _ in range(3): for _ in range(3):
# "SELECT 2ND WORD" # "SELECT 2ND WORD"
# ^ # ^
@ -144,9 +144,9 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None:
wanted_word = words[word_pos - 1].lower() wanted_word = words[word_pos - 1].lower()
while not layout.get_middle_choice() == wanted_word: 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: def validate_mnemonics(mnemonics: list[str], expected_ems: bytes) -> None:

View File

@ -59,15 +59,15 @@ def set_autolock_delay(device_handler: "BackgroundDeviceHandler", delay_ms: int)
device_handler.run(device.apply_settings, auto_lock_delay_ms=delay_ms) # type: ignore 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") debug.input("1234")
assert ( assert (
f"Auto-lock your Trezor after {delay_ms // 1000} seconds" f"Auto-lock your Trezor after {delay_ms // 1000} seconds"
in debug.wait_layout().text_content() in debug.read_layout().text_content()
) )
layout = go_next(debug, wait=True) layout = go_next(debug)
assert layout.main_component() == "Homescreen" assert layout.main_component() == "Homescreen"
assert device_handler.result() == "Settings applied" assert device_handler.result() == "Settings applied"
@ -97,16 +97,16 @@ def test_autolock_interrupts_signing(device_handler: "BackgroundDeviceHandler"):
assert ( assert (
"1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1" "1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1"
in debug.wait_layout().text_content().replace(" ", "") in debug.read_layout().text_content().replace(" ", "")
) )
if debug.model == "T": if debug.model == "T":
debug.click(buttons.OK, wait=True) debug.click(buttons.OK)
layout = debug.click(buttons.OK, wait=True) layout = debug.click(buttons.OK)
assert "Total amount: 0.0039 BTC" in layout.text_content() assert "Total amount: 0.0039 BTC" in layout.text_content()
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
debug.press_right(wait=True) debug.press_right()
layout = debug.press_right(wait=True) layout = debug.press_right()
assert "Total amount: 0.0039 BTC" in layout.text_content() assert "Total amount: 0.0039 BTC" in layout.text_content()
# wait for autolock to kick in # wait for autolock to kick in
@ -142,16 +142,16 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa
assert ( assert (
"1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1" "1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1"
in debug.wait_layout().text_content().replace(" ", "") in debug.read_layout().text_content().replace(" ", "")
) )
if debug.model == "T": if debug.model == "T":
debug.click(buttons.OK, wait=True) debug.click(buttons.OK)
layout = debug.click(buttons.OK, wait=True) layout = debug.click(buttons.OK)
assert "Total amount: 0.0039 BTC" in layout.text_content() assert "Total amount: 0.0039 BTC" in layout.text_content()
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
debug.press_right(wait=True) debug.press_right()
layout = debug.press_right(wait=True) layout = debug.press_right()
assert "Total amount: 0.0039 BTC" in layout.text_content() assert "Total amount: 0.0039 BTC" in layout.text_content()
def sleepy_filter(msg: MessageType) -> MessageType: def sleepy_filter(msg: MessageType) -> MessageType:
@ -163,9 +163,9 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa
device_handler.client.set_filter(messages.TxAck, sleepy_filter) device_handler.client.set_filter(messages.TxAck, sleepy_filter)
# confirm transaction # confirm transaction
if debug.model == "T": if debug.model == "T":
debug.click(buttons.OK) debug.click(buttons.OK, wait=False)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
debug.press_middle() debug.press_middle(wait=False)
signatures, tx = device_handler.result() signatures, tx = device_handler.result()
assert len(signatures) == 1 assert len(signatures) == 1
@ -182,7 +182,7 @@ def test_autolock_passphrase_keyboard(device_handler: "BackgroundDeviceHandler")
# get address # get address
device_handler.run(common.get_test_address) # type: ignore 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 == "Safe 3": if debug.model == "Safe 3":
# Going into the selected character category # Going into the selected character category
@ -203,9 +203,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) # Send the passphrase to the client (TT has it clicked already, TR needs to input it)
if debug.model == "T": if debug.model == "T":
debug.click(buttons.OK, wait=True) debug.click(buttons.OK)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
debug.input("j" * 8, wait=True) debug.input("j" * 8)
# address corresponding to "jjjjjjjj" passphrase # address corresponding to "jjjjjjjj" passphrase
assert device_handler.result() == "mnF4yRWJXmzRB6EuBzuVigqeqTqirQupxJ" assert device_handler.result() == "mnF4yRWJXmzRB6EuBzuVigqeqTqirQupxJ"
@ -219,7 +219,7 @@ def test_autolock_interrupts_passphrase(device_handler: "BackgroundDeviceHandler
# get address # get address
device_handler.run(common.get_test_address) # type: ignore 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 == "Safe 3": if debug.model == "Safe 3":
# Going into the selected character category # Going into the selected character category
@ -236,17 +236,17 @@ def test_autolock_interrupts_passphrase(device_handler: "BackgroundDeviceHandler
# wait for autolock to kick in # wait for autolock to kick in
time.sleep(10.1) time.sleep(10.1)
assert debug.wait_layout().main_component() == "Lockscreen" assert debug.read_layout().main_component() == "Lockscreen"
with pytest.raises(exceptions.Cancelled): with pytest.raises(exceptions.Cancelled):
device_handler.result() device_handler.result()
def unlock_dry_run(debug: "DebugLink") -> "LayoutContent": def unlock_dry_run(debug: "DebugLink") -> "LayoutContent":
assert "Check your backup?" in debug.wait_layout().text_content() assert "Check your backup?" in debug.read_layout().text_content()
layout = go_next(debug, wait=True) layout = go_next(debug)
assert "PinKeyboard" in layout.all_components() assert "PinKeyboard" in layout.all_components()
layout = debug.input(PIN4, wait=True) layout = debug.input(PIN4)
assert layout is not None assert layout is not None
return layout return layout
@ -262,20 +262,20 @@ def test_dryrun_locks_at_number_of_words(device_handler: "BackgroundDeviceHandle
assert "number of words" in layout.text_content() assert "number of words" in layout.text_content()
if debug.model == "Safe 3": if debug.model == "Safe 3":
debug.press_right(wait=True) debug.press_right()
# wait for autolock to trigger # wait for autolock to trigger
time.sleep(10.1) time.sleep(10.1)
assert debug.wait_layout().main_component() == "Lockscreen" assert debug.read_layout().main_component() == "Lockscreen"
with pytest.raises(exceptions.Cancelled): with pytest.raises(exceptions.Cancelled):
device_handler.result() device_handler.result()
# unlock # unlock
# lockscreen triggered automatically # lockscreen triggered automatically
debug.wait_layout(wait_for_external_change=True) # debug.wait_layout()
layout = go_next(debug, wait=True) layout = go_next(debug)
assert "PinKeyboard" in layout.all_components() assert "PinKeyboard" in layout.all_components()
layout = debug.input(PIN4, wait=True) layout = debug.input(PIN4)
assert layout is not None assert layout is not None
# we are back at homescreen # we are back at homescreen
@ -295,15 +295,15 @@ def test_dryrun_locks_at_word_entry(device_handler: "BackgroundDeviceHandler"):
recovery.select_number_of_words(debug, 20) recovery.select_number_of_words(debug, 20)
if debug.model == "T": if debug.model == "T":
layout = debug.click(buttons.OK, wait=True) layout = debug.click(buttons.OK)
assert layout.main_component() == "MnemonicKeyboard" assert layout.main_component() == "MnemonicKeyboard"
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
layout = debug.press_right(wait=True) layout = debug.press_right()
assert "MnemonicKeyboard" in layout.all_components() assert "MnemonicKeyboard" in layout.all_components()
# make sure keyboard locks # make sure keyboard locks
time.sleep(10.1) time.sleep(10.1)
assert debug.wait_layout().main_component() == "Lockscreen" assert debug.read_layout().main_component() == "Lockscreen"
with pytest.raises(exceptions.Cancelled): with pytest.raises(exceptions.Cancelled):
device_handler.result() device_handler.result()
@ -321,25 +321,25 @@ def test_dryrun_enter_word_slowly(device_handler: "BackgroundDeviceHandler"):
recovery.select_number_of_words(debug, 20) recovery.select_number_of_words(debug, 20)
if debug.model == "T": if debug.model == "T":
layout = debug.click(buttons.OK, wait=True) layout = debug.click(buttons.OK)
assert layout.main_component() == "MnemonicKeyboard" assert layout.main_component() == "MnemonicKeyboard"
# type the word OCEAN slowly # type the word OCEAN slowly
for coords in buttons.type_word("ocea", is_slip39=True): for coords in buttons.type_word("ocea", is_slip39=True):
time.sleep(9) time.sleep(9)
debug.click(coords) 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 # should not have locked, even though we took 9 seconds to type each letter
assert layout.main_component() == "MnemonicKeyboard" assert layout.main_component() == "MnemonicKeyboard"
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
layout = debug.press_right(wait=True) layout = debug.press_right()
assert "MnemonicKeyboard" in layout.all_components() assert "MnemonicKeyboard" in layout.all_components()
# pressing middle button three times # pressing middle button three times
for _ in range(3): for _ in range(3):
time.sleep(9) time.sleep(9)
debug.press_middle() 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 # should not have locked, even though we took 9 seconds to type each letter
assert "MnemonicKeyboard" in layout.all_components() assert "MnemonicKeyboard" in layout.all_components()
@ -367,7 +367,7 @@ def test_autolock_does_not_interrupt_preauthorized(
coin_name="Testnet", coin_name="Testnet",
script_type=messages.InputScriptType.SPENDTAPROOT, script_type=messages.InputScriptType.SPENDTAPROOT,
) )
debug.press_yes(wait=True) debug.press_yes()
device_handler.result() device_handler.result()
inputs = [ inputs = [

View File

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

View File

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

View File

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

View File

@ -84,37 +84,35 @@ 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
assert "Turn on" in debug.wait_layout().text_content() assert "Turn on" in debug.read_layout().text_content()
if debug.model == "T": if debug.model == "T":
go_next(debug) go_next(debug)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
go_next(debug, wait=True) go_next(debug)
go_next(debug, wait=True) go_next(debug)
go_next(debug, wait=True) go_next(debug)
go_next(debug, wait=True) go_next(debug)
elif situation == Situation.PIN_CHANGE: elif situation == Situation.PIN_CHANGE:
# 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)
assert "Change PIN" in debug.read_layout().text_content() assert "Change PIN" in debug.read_layout().text_content()
go_next(debug, wait=True) 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:
# Set wipe code # Set wipe code
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)
assert "Turn on" in debug.wait_layout().text_content() assert "Turn on" in debug.read_layout().text_content()
go_next(debug, wait=True) go_next(debug)
if debug.model == "Safe 3": if debug.model == "Safe 3":
go_next(debug, wait=True) go_next(debug)
go_next(debug, wait=True) go_next(debug)
go_next(debug, wait=True) go_next(debug)
if old_pin: if old_pin:
debug.wait_layout()
_input_see_confirm(debug, old_pin) _input_see_confirm(debug, old_pin)
debug.wait_layout()
_assert_pin_entry(debug) _assert_pin_entry(debug)
yield debug yield debug
go_next(debug) go_next(debug)
@ -135,7 +133,7 @@ def _input_pin(debug: "DebugLink", pin: str, check: bool = False) -> None:
for digit in pin: for digit in pin:
digit_index = digits_order.index(digit) digit_index = digits_order.index(digit)
coords = buttons.pin_passphrase_index(digit_index) coords = buttons.pin_passphrase_index(digit_index)
debug.click(coords, wait=True) debug.click(coords)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
for digit in pin: for digit in pin:
navigate_to_action_and_press(debug, digit, TR_PIN_ACTIONS) navigate_to_action_and_press(debug, digit, TR_PIN_ACTIONS)
@ -148,7 +146,7 @@ def _input_pin(debug: "DebugLink", pin: str, check: bool = False) -> None:
def _see_pin(debug: "DebugLink") -> None: def _see_pin(debug: "DebugLink") -> None:
"""Navigate to "SHOW" and press it""" """Navigate to "SHOW" and press it"""
if debug.model == "T": if debug.model == "T":
debug.click(buttons.TOP_ROW, wait=True) debug.click(buttons.TOP_ROW)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
navigate_to_action_and_press(debug, "SHOW", TR_PIN_ACTIONS) navigate_to_action_and_press(debug, "SHOW", TR_PIN_ACTIONS)
@ -160,7 +158,7 @@ def _delete_pin(debug: "DebugLink", digits_to_delete: int, check: bool = True) -
for _ in range(digits_to_delete): for _ in range(digits_to_delete):
if debug.model == "T": if debug.model == "T":
debug.click(buttons.pin_passphrase_grid(9), wait=True) debug.click(buttons.pin_passphrase_grid(9))
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
navigate_to_action_and_press(debug, "DELETE", TR_PIN_ACTIONS) navigate_to_action_and_press(debug, "DELETE", TR_PIN_ACTIONS)
@ -172,7 +170,7 @@ def _delete_pin(debug: "DebugLink", digits_to_delete: int, check: bool = True) -
def _delete_all(debug: "DebugLink", check: bool = True) -> None: def _delete_all(debug: "DebugLink", check: bool = True) -> None:
"""Navigate to "DELETE" and hold it until all digits are deleted""" """Navigate to "DELETE" and hold it until all digits are deleted"""
if debug.model == "T": if debug.model == "T":
debug.click_hold(buttons.pin_passphrase_grid(9), hold_ms=1500) debug.click(buttons.pin_passphrase_grid(9), hold_ms=1500)
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
navigate_to_action_and_press(debug, "DELETE", TR_PIN_ACTIONS, hold_ms=1000) navigate_to_action_and_press(debug, "DELETE", TR_PIN_ACTIONS, hold_ms=1000)
@ -191,7 +189,7 @@ def _cancel_pin(debug: "DebugLink") -> None:
def _confirm_pin(debug: "DebugLink") -> None: def _confirm_pin(debug: "DebugLink") -> None:
"""Navigate to "ENTER" and press it""" """Navigate to "ENTER" and press it"""
if debug.model == "T": if debug.model == "T":
debug.click(buttons.pin_passphrase_grid(11), wait=True) debug.click(buttons.pin_passphrase_grid(11))
elif debug.model == "Safe 3": elif debug.model == "Safe 3":
navigate_to_action_and_press(debug, "ENTER", TR_PIN_ACTIONS) navigate_to_action_and_press(debug, "ENTER", TR_PIN_ACTIONS)
@ -207,7 +205,7 @@ def _enter_two_times(debug: "DebugLink", pin1: str, pin2: str) -> None:
if debug.model == "Safe 3": if debug.model == "Safe 3":
# Please re-enter # Please re-enter
go_next(debug, wait=True) go_next(debug)
_input_see_confirm(debug, pin2) _input_see_confirm(debug, pin2)
@ -331,7 +329,7 @@ def test_wipe_code_same_as_pin(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler, Situation.WIPE_CODE_SETUP, old_pin="1") as debug: with prepare(device_handler, Situation.WIPE_CODE_SETUP, old_pin="1") as debug:
_input_see_confirm(debug, "1") _input_see_confirm(debug, "1")
# Try again # Try again
go_next(debug, wait=True) go_next(debug)
_enter_two_times(debug, "2", "2") _enter_two_times(debug, "2", "2")

View File

@ -49,12 +49,12 @@ def prepare_tutorial_and_cancel_after_it(
def go_through_tutorial(debug: "DebugLink") -> None: def go_through_tutorial(debug: "DebugLink") -> None:
debug.press_right(wait=True) debug.press_right()
debug.press_right(wait=True) debug.press_right()
debug.press_right_htc(hold_ms=1000) debug.press_right(hold_ms=1000)
debug.press_right(wait=True) debug.press_right()
debug.press_right(wait=True) debug.press_right()
layout = debug.press_middle(wait=True) layout = debug.press_middle()
assert layout.title() == "TUTORIAL COMPLETE" assert layout.title() == "TUTORIAL COMPLETE"
@ -65,15 +65,15 @@ def test_tutorial_finish(device_handler: "BackgroundDeviceHandler"):
go_through_tutorial(debug) go_through_tutorial(debug)
# FINISH # FINISH
debug.press_right(wait=True) debug.press_right()
@pytest.mark.setup_client(uninitialized=True) @pytest.mark.setup_client(uninitialized=True)
def test_tutorial_skip(device_handler: "BackgroundDeviceHandler"): def test_tutorial_skip(device_handler: "BackgroundDeviceHandler"):
with prepare_tutorial_and_cancel_after_it(device_handler, cancelled=True) as debug: with prepare_tutorial_and_cancel_after_it(device_handler, cancelled=True) as debug:
# SKIP # SKIP
debug.press_left(wait=True) debug.press_left()
debug.press_right(wait=True) debug.press_right()
@pytest.mark.setup_client(uninitialized=True) @pytest.mark.setup_client(uninitialized=True)
@ -83,8 +83,8 @@ def test_tutorial_again_and_skip(device_handler: "BackgroundDeviceHandler"):
go_through_tutorial(debug) go_through_tutorial(debug)
# AGAIN # AGAIN
debug.press_left(wait=True) debug.press_left()
# SKIP # SKIP
debug.press_left(wait=True) debug.press_left()
debug.press_right(wait=True) debug.press_right()

View File

@ -188,10 +188,8 @@ def read_and_confirm_mnemonic_tt(
br = yield br = yield
assert br.pages is not None assert br.pages is not None
debug.wait_layout()
for i in range(br.pages): for i in range(br.pages):
words = debug.wait_layout().seed_words() words = debug.read_layout().seed_words()
mnemonic.extend(words) mnemonic.extend(words)
# Not swiping on the last page # Not swiping on the last page
if i < br.pages - 1: if i < br.pages - 1:
@ -201,7 +199,9 @@ def read_and_confirm_mnemonic_tt(
# check share # check share
for _ in range(3): for _ in range(3):
word_pos = int(debug.wait_layout().text_content().split()[2]) text_content = debug.read_layout().text_content()
assert text_content.startswith("Select word ")
word_pos = int(text_content.split()[2])
index = word_pos - 1 index = word_pos - 1
if choose_wrong: if choose_wrong:
debug.input(mnemonic[(index + 1) % len(mnemonic)]) debug.input(mnemonic[(index + 1) % len(mnemonic)])
@ -221,7 +221,7 @@ def read_and_confirm_mnemonic_tr(
br = yield br = yield
assert br.pages is not None assert br.pages is not None
for _ in range(br.pages - 1): for _ in range(br.pages - 1):
layout = debug.wait_layout() layout = debug.read_layout()
words = layout.seed_words() words = layout.seed_words()
mnemonic.extend(words) mnemonic.extend(words)
debug.press_right() debug.press_right()
@ -232,7 +232,7 @@ def read_and_confirm_mnemonic_tr(
# check share # check share
for _ in range(3): 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 assert word_pos_match is not None
word_pos = int(word_pos_match.group(0)) word_pos = int(word_pos_match.group(0))
index = word_pos - 1 index = word_pos - 1
@ -248,8 +248,8 @@ def read_and_confirm_mnemonic_tr(
def click_info_button_tt(debug: "DebugLink"): def click_info_button_tt(debug: "DebugLink"):
"""Click Shamir backup info button and return back.""" """Click Shamir backup info button and return back."""
debug.press_info() debug.press_info()
yield # Info screen with text
debug.press_yes() debug.press_yes()
yield
def check_pin_backoff_time(attempts: int, start: float) -> None: def check_pin_backoff_time(attempts: int, start: float) -> None:

View File

@ -46,6 +46,8 @@ HERE = Path(__file__).resolve().parent
# So that we see details of failed asserts from this module # So that we see details of failed asserts from this module
pytest.register_assert_rewrite("tests.common") 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]: def _emulator_wrapper_main_args() -> list[str]:

View File

@ -31,7 +31,7 @@ pytestmark = [
def show_details_input_flow(client: Client): def show_details_input_flow(client: Client):
yield yield
client.debug.wait_layout() client.debug.read_layout()
# Clicking for model T, pressing right for model R # Clicking for model T, pressing right for model R
if client.features.model == "T": if client.features.model == "T":
SHOW_ALL_BUTTON_POSITION = (143, 167) SHOW_ALL_BUTTON_POSITION = (143, 167)

View File

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

View File

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

View File

@ -395,7 +395,7 @@ def test_hide_passphrase_from_host(client: Client):
def input_flow(): def input_flow():
yield yield
layout = client.debug.wait_layout() layout = client.debug.read_layout()
if client.debug.model == "T": if client.debug.model == "T":
assert ( assert (
"Passphrase provided by host will be used but will not be displayed due to the device settings." "Passphrase provided by host will be used but will not be displayed due to the device settings."
@ -403,7 +403,7 @@ def test_hide_passphrase_from_host(client: Client):
) )
client.debug.press_yes() client.debug.press_yes()
elif client.debug.model == "Safe 3": elif client.debug.model == "Safe 3":
layout = client.debug.wait_layout() layout = client.debug.read_layout()
assert "will not be displayed" in layout.text_content() assert "will not be displayed" in layout.text_content()
client.debug.press_right() client.debug.press_right()
client.debug.press_right() client.debug.press_right()
@ -433,12 +433,12 @@ def test_hide_passphrase_from_host(client: Client):
def input_flow(): def input_flow():
yield yield
layout = client.debug.wait_layout() layout = client.debug.read_layout()
assert "Next screen will show the passphrase" in layout.text_content() assert "Next screen will show the passphrase" in layout.text_content()
client.debug.press_yes() client.debug.press_yes()
yield yield
layout = client.debug.wait_layout() layout = client.debug.read_layout()
assert "confirm passphrase" in layout.title().lower() assert "confirm passphrase" in layout.title().lower()
assert passphrase in layout.text_content() assert passphrase in layout.text_content()

View File

@ -75,16 +75,16 @@ class InputFlowBase:
raise NotImplementedError raise NotImplementedError
def text_content(self) -> str: def text_content(self) -> str:
return self.debug.wait_layout().text_content() return self.debug.read_layout().text_content()
def main_component(self) -> str: 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]: def all_components(self) -> list[str]:
return self.debug.wait_layout().all_components() return self.debug.read_layout().all_components()
def title(self) -> str: def title(self) -> str:
return self.debug.wait_layout().title() return self.debug.read_layout().title()
class InputFlowSetupDevicePINWIpeCode(InputFlowBase): class InputFlowSetupDevicePINWIpeCode(InputFlowBase):
@ -215,13 +215,13 @@ class InputFlowSignMessagePagination(InputFlowBase):
layouts: list[LayoutContent] = [] layouts: list[LayoutContent] = []
br = yield # confirm address br = yield # confirm address
self.debug.wait_layout() self.debug.read_layout()
self.debug.press_yes() self.debug.press_yes()
br = yield br = yield
assert br.pages is not None assert br.pages is not None
for i in range(br.pages): for i in range(br.pages):
layout = self.debug.wait_layout() layout = self.debug.read_layout()
layouts.append(layout) layouts.append(layout)
if i < br.pages - 1: if i < br.pages - 1:
@ -261,9 +261,9 @@ class InputFlowSignMessageInfo(InputFlowBase):
def input_flow_tt(self) -> BRGeneratorType: def input_flow_tt(self) -> BRGeneratorType:
yield yield
# show address/message info # show address/message info
self.debug.click(buttons.CORNER_BUTTON, wait=True) self.debug.click(buttons.CORNER_BUTTON)
self.debug.click(buttons.CORNER_BUTTON, wait=True) self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_no(wait=True) self.debug.press_no()
self.debug.synchronize_at("mismatch") self.debug.synchronize_at("mismatch")
# address mismatch? # address mismatch?
self.debug.press_no() self.debug.press_no()
@ -272,8 +272,8 @@ class InputFlowSignMessageInfo(InputFlowBase):
yield yield
self.debug.press_no() self.debug.press_no()
yield yield
self.debug.press_no(wait=True) self.debug.press_no()
self.debug.press_yes(wait=True) self.debug.press_yes()
class InputFlowShowAddressQRCode(InputFlowBase): class InputFlowShowAddressQRCode(InputFlowBase):
@ -282,16 +282,16 @@ class InputFlowShowAddressQRCode(InputFlowBase):
def input_flow_tt(self) -> BRGeneratorType: def input_flow_tt(self) -> BRGeneratorType:
yield 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 # synchronize; TODO get rid of this once we have single-global-layout
self.debug.synchronize_at("SimplePage") self.debug.synchronize_at("SimplePage")
self.debug.swipe_left(wait=True) self.debug.swipe_left()
self.debug.swipe_right(wait=True) self.debug.swipe_right()
self.debug.swipe_left(wait=True) self.debug.swipe_left()
self.debug.click(buttons.CORNER_BUTTON, wait=True) self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_no(wait=True) self.debug.press_no()
self.debug.press_no(wait=True) self.debug.press_no()
self.debug.press_yes() self.debug.press_yes()
def input_flow_tr(self) -> BRGeneratorType: def input_flow_tr(self) -> BRGeneratorType:
@ -322,13 +322,13 @@ class InputFlowShowAddressQRCodeCancel(InputFlowBase):
def input_flow_tt(self) -> BRGeneratorType: def input_flow_tt(self) -> BRGeneratorType:
yield 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 # synchronize; TODO get rid of this once we have single-global-layout
self.debug.synchronize_at("SimplePage") self.debug.synchronize_at("SimplePage")
self.debug.swipe_left(wait=True) self.debug.swipe_left()
self.debug.click(buttons.CORNER_BUTTON, wait=True) self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_no(wait=True) self.debug.press_no()
self.debug.press_yes() self.debug.press_yes()
def input_flow_tr(self) -> BRGeneratorType: def input_flow_tr(self) -> BRGeneratorType:
@ -357,14 +357,14 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
self.debug.press_yes() self.debug.press_yes()
yield # show address yield # show address
layout = self.debug.wait_layout() layout = self.debug.read_layout()
assert "RECEIVE ADDRESS\n(MULTISIG)" == layout.title() assert "RECEIVE ADDRESS\n(MULTISIG)" == 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)
assert "Qr" in self.all_components() assert "Qr" in self.all_components()
layout = self.debug.swipe_left(wait=True) 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()
assert "Derivation path:" in layout.screen_content() assert "Derivation path:" in layout.screen_content()
@ -374,16 +374,16 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
expected_title = f"MULTISIG XPUB #{xpub_num + 1}\n" + ( expected_title = f"MULTISIG XPUB #{xpub_num + 1}\n" + (
"(YOURS)" if self.index == xpub_num else "(COSIGNER)" "(YOURS)" if self.index == xpub_num else "(COSIGNER)"
) )
layout = self.debug.swipe_left(wait=True) layout = self.debug.swipe_left()
assert expected_title == layout.title() assert expected_title == layout.title()
content = layout.text_content().replace(" ", "") content = layout.text_content().replace(" ", "")
assert self.xpubs[xpub_num] in content assert self.xpubs[xpub_num] in content
self.debug.click(buttons.CORNER_BUTTON, wait=True) self.debug.click(buttons.CORNER_BUTTON)
# show address # show address
self.debug.press_no(wait=True) self.debug.press_no()
# address mismatch # address mismatch
self.debug.press_no(wait=True) self.debug.press_no()
# show address # show address
self.debug.press_yes() self.debug.press_yes()
@ -392,14 +392,14 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
self.debug.press_middle() self.debug.press_middle()
yield # show address yield # show address
layout = self.debug.wait_layout() layout = self.debug.read_layout()
assert "RECEIVE ADDRESS (MULTISIG)" in layout.title() assert "RECEIVE ADDRESS (MULTISIG)" in layout.title()
assert layout.text_content().replace(" ", "") == self.address assert layout.text_content().replace(" ", "") == self.address
self.debug.press_right() self.debug.press_right()
assert "Qr" in self.all_components() assert "Qr" in self.all_components()
layout = self.debug.press_right(wait=True) layout = self.debug.press_right()
# address details # address details
# TODO: locate it more precisely # TODO: locate it more precisely
assert "Multisig 2 of 3" in layout.json_str assert "Multisig 2 of 3" in layout.json_str
@ -409,14 +409,14 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
expected_title = f"MULTISIG XPUB #{xpub_num + 1} " + ( expected_title = f"MULTISIG XPUB #{xpub_num + 1} " + (
"(YOURS)" if self.index == xpub_num else "(COSIGNER)" "(YOURS)" if self.index == xpub_num else "(COSIGNER)"
) )
layout = self.debug.press_right(wait=True) layout = self.debug.press_right()
assert expected_title in layout.title() assert expected_title in layout.title()
xpub_part_1 = layout.text_content().replace(" ", "") xpub_part_1 = layout.text_content().replace(" ", "")
# Press "SHOW MORE" # Press "SHOW MORE"
layout = self.debug.press_middle(wait=True) layout = self.debug.press_middle()
xpub_part_2 = layout.text_content().replace(" ", "") xpub_part_2 = layout.text_content().replace(" ", "")
# Go back # Go back
self.debug.press_left(wait=True) self.debug.press_left()
assert self.xpubs[xpub_num] == xpub_part_1 + xpub_part_2 assert self.xpubs[xpub_num] == xpub_part_1 + xpub_part_2
for _ in range(5): for _ in range(5):
@ -442,23 +442,23 @@ class InputFlowShowXpubQRCode(InputFlowBase):
self.debug.press_yes() self.debug.press_yes()
br = yield br = yield
layout = self.debug.wait_layout() layout = self.debug.read_layout()
if "coinjoin" in layout.title().lower() or br.code == B.UnknownDerivationPath: if "coinjoin" in layout.title().lower() or br.code == B.UnknownDerivationPath:
self.debug.press_yes() self.debug.press_yes()
br = yield 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 # synchronize; TODO get rid of this once we have single-global-layout
self.debug.synchronize_at("SimplePage") self.debug.synchronize_at("SimplePage")
self.debug.swipe_left(wait=True) self.debug.swipe_left()
self.debug.swipe_right(wait=True) self.debug.swipe_right()
self.debug.swipe_left(wait=True) self.debug.swipe_left()
self.debug.click(buttons.CORNER_BUTTON, wait=True) self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_no(wait=True) self.debug.press_no()
self.debug.press_no(wait=True) self.debug.press_no()
for _ in range(br.pages - 1): for _ in range(br.pages - 1):
self.debug.swipe_up(wait=True) self.debug.swipe_up()
self.debug.press_yes() self.debug.press_yes()
def input_flow_tr(self) -> BRGeneratorType: def input_flow_tr(self) -> BRGeneratorType:
@ -469,18 +469,18 @@ class InputFlowShowXpubQRCode(InputFlowBase):
self.debug.press_right() self.debug.press_right()
br = yield br = yield
layout = self.debug.wait_layout() layout = self.debug.read_layout()
if "coinjoin" in layout.title().lower() or br.code == B.UnknownDerivationPath: if "coinjoin" in layout.title().lower() or br.code == B.UnknownDerivationPath:
self.debug.press_yes() self.debug.press_yes()
br = yield br = yield
# Go into details # Go into details
self.debug.press_right(wait=True) self.debug.press_right()
# Go through details and back # Go through details and back
self.debug.press_right(wait=True) self.debug.press_right()
self.debug.press_right(wait=True) self.debug.press_right()
self.debug.press_left(wait=True) self.debug.press_left()
self.debug.press_left(wait=True) self.debug.press_left()
assert br.pages is not None assert br.pages is not None
for _ in range(br.pages - 1): for _ in range(br.pages - 1):
self.debug.press_right() self.debug.press_right()
@ -495,21 +495,21 @@ class InputFlowPaymentRequestDetails(InputFlowBase):
def input_flow_tt(self) -> BRGeneratorType: def input_flow_tt(self) -> BRGeneratorType:
yield # request to see details yield # request to see details
self.debug.wait_layout() self.debug.read_layout()
self.debug.press_info() self.debug.press_info()
yield # confirm first output yield # confirm first output
assert self.outputs[0].address[:16] in self.text_content() # type: ignore assert self.outputs[0].address[:16] in self.text_content() # type: ignore
self.debug.press_yes() self.debug.press_yes()
yield # confirm first output yield # confirm first output
self.debug.wait_layout() self.debug.read_layout()
self.debug.press_yes() self.debug.press_yes()
yield # confirm second output yield # confirm second output
assert self.outputs[1].address[:16] in self.text_content() # type: ignore assert self.outputs[1].address[:16] in self.text_content() # type: ignore
self.debug.press_yes() self.debug.press_yes()
yield # confirm second output yield # confirm second output
self.debug.wait_layout() self.debug.read_layout()
self.debug.press_yes() self.debug.press_yes()
yield # confirm transaction yield # confirm transaction
@ -543,20 +543,20 @@ class InputFlowSignTxHighFee(InputFlowBase):
def sign_tx_go_to_info(client: Client) -> Generator[None, None, str]: def sign_tx_go_to_info(client: Client) -> Generator[None, None, str]:
yield # confirm output yield # confirm output
client.debug.wait_layout() client.debug.read_layout()
client.debug.press_yes() client.debug.press_yes()
yield # confirm output yield # confirm output
client.debug.wait_layout() client.debug.read_layout()
client.debug.press_yes() client.debug.press_yes()
yield # confirm transaction yield # confirm transaction
client.debug.wait_layout() client.debug.read_layout()
client.debug.press_info() client.debug.press_info()
layout = client.debug.wait_layout() layout = client.debug.read_layout()
content = layout.text_content().lower() content = layout.text_content().lower()
client.debug.click(buttons.CORNER_BUTTON, wait=True) client.debug.click(buttons.CORNER_BUTTON)
return content return content
@ -565,24 +565,24 @@ def sign_tx_go_to_info_tr(
client: Client, client: Client,
) -> Generator[None, None, str]: ) -> Generator[None, None, str]:
yield # confirm address yield # confirm address
client.debug.wait_layout() client.debug.read_layout()
client.debug.press_yes() # CONTINUE client.debug.press_yes() # CONTINUE
yield # confirm amount yield # confirm amount
client.debug.wait_layout() client.debug.read_layout()
client.debug.press_yes() # CONFIRM client.debug.press_yes() # CONFIRM
screen_texts: list[str] = [] screen_texts: list[str] = []
yield # confirm total yield # confirm total
layout = client.debug.wait_layout() layout = client.debug.read_layout()
if "multiple accounts" in layout.text_content().lower(): if "multiple accounts" in layout.text_content().lower():
client.debug.press_middle() client.debug.press_middle()
yield yield
layout = client.debug.press_right(wait=True) layout = client.debug.press_right()
screen_texts.append(layout.text_content()) screen_texts.append(layout.text_content())
layout = client.debug.press_right(wait=True) layout = client.debug.press_right()
screen_texts.append(layout.text_content()) screen_texts.append(layout.text_content())
client.debug.press_left() client.debug.press_left()
@ -660,15 +660,16 @@ class InputFlowSignTxInformationReplacement(InputFlowBase):
yield # confirm address yield # confirm address
self.debug.press_yes() self.debug.press_yes()
# go back to address # go back to address
yield
self.debug.press_no() self.debug.press_no()
# confirm address # confirm address
self.debug.press_yes() self.debug.press_yes()
yield # confirm amount # confirm amount
self.debug.press_yes() self.debug.press_yes()
yield # transaction summary, press info yield # transaction summary, press info
self.debug.click(buttons.CORNER_BUTTON, wait=True) self.debug.click(buttons.CORNER_BUTTON)
self.debug.click(buttons.CORNER_BUTTON, wait=True) self.debug.click(buttons.CORNER_BUTTON)
self.debug.press_yes() self.debug.press_yes()
def input_flow_tr(self) -> BRGeneratorType: def input_flow_tr(self) -> BRGeneratorType:
@ -691,10 +692,10 @@ def lock_time_input_flow_tt(
double_confirm: bool = False, double_confirm: bool = False,
) -> BRGeneratorType: ) -> BRGeneratorType:
yield # confirm output yield # confirm output
debug.wait_layout() debug.read_layout()
debug.press_yes() debug.press_yes()
yield # confirm output yield # confirm output
debug.wait_layout() debug.read_layout()
debug.press_yes() debug.press_yes()
yield # confirm locktime yield # confirm locktime
@ -712,10 +713,10 @@ def lock_time_input_flow_tr(
debug: DebugLink, layout_assert_func: Callable[[DebugLink], None] debug: DebugLink, layout_assert_func: Callable[[DebugLink], None]
) -> BRGeneratorType: ) -> BRGeneratorType:
yield # confirm address yield # confirm address
debug.wait_layout() debug.read_layout()
debug.press_yes() debug.press_yes()
yield # confirm amount yield # confirm amount
debug.wait_layout() debug.read_layout()
debug.press_yes() debug.press_yes()
yield # confirm locktime yield # confirm locktime
@ -732,7 +733,7 @@ class InputFlowLockTimeBlockHeight(InputFlowBase):
self.block_height = block_height self.block_height = block_height
def assert_func(self, debug: DebugLink) -> None: def assert_func(self, debug: DebugLink) -> None:
layout_text = debug.wait_layout().text_content() layout_text = debug.read_layout().text_content()
assert "blockheight" in layout_text assert "blockheight" in layout_text
assert self.block_height in layout_text assert self.block_height in layout_text
@ -751,7 +752,7 @@ class InputFlowLockTimeDatetime(InputFlowBase):
self.lock_time_str = lock_time_str self.lock_time_str = lock_time_str
def assert_func(self, debug: DebugLink): def assert_func(self, debug: DebugLink):
layout_text = debug.wait_layout().text_content() layout_text = debug.read_layout().text_content()
assert "Locktime" in layout_text assert "Locktime" in layout_text
assert self.lock_time_str in layout_text assert self.lock_time_str in layout_text
@ -782,7 +783,7 @@ class InputFlowEIP712ShowMore(InputFlowBase):
self.debug.press_yes() self.debug.press_yes()
yield # confirm domain yield # confirm domain
self.debug.wait_layout() self.debug.read_layout()
self._confirm_show_more() self._confirm_show_more()
# confirm domain properties # confirm domain properties
@ -791,11 +792,11 @@ class InputFlowEIP712ShowMore(InputFlowBase):
self.debug.press_yes() self.debug.press_yes()
yield # confirm message yield # confirm message
self.debug.wait_layout() self.debug.read_layout()
self._confirm_show_more() self._confirm_show_more()
yield # confirm message.from yield # confirm message.from
self.debug.wait_layout() self.debug.read_layout()
self._confirm_show_more() self._confirm_show_more()
# confirm message.from properties # confirm message.from properties
@ -804,7 +805,7 @@ class InputFlowEIP712ShowMore(InputFlowBase):
self.debug.press_yes() self.debug.press_yes()
yield # confirm message.to yield # confirm message.to
self.debug.wait_layout() self.debug.read_layout()
self._confirm_show_more() self._confirm_show_more()
# confirm message.to properties # confirm message.to properties
@ -1023,15 +1024,15 @@ class InputFlowSlip39BasicBackup(InputFlowBase):
def input_flow_tt(self) -> BRGeneratorType: def input_flow_tt(self) -> BRGeneratorType:
yield # 1. Checklist yield # 1. Checklist
self.debug.press_yes() self.debug.press_yes()
yield # 2. Number of shares (5)
if self.click_info: if self.click_info:
yield from click_info_button_tt(self.debug) yield from click_info_button_tt(self.debug)
yield # 2. Number of shares (5)
self.debug.press_yes() self.debug.press_yes()
yield # 3. Checklist yield # 3. Checklist
self.debug.press_yes() self.debug.press_yes()
yield # 4. Threshold (3)
if self.click_info: if self.click_info:
yield from click_info_button_tt(self.debug) yield from click_info_button_tt(self.debug)
yield # 4. Threshold (3)
self.debug.press_yes() self.debug.press_yes()
yield # 5. Checklist yield # 5. Checklist
self.debug.press_yes() self.debug.press_yes()
@ -1151,26 +1152,26 @@ class InputFlowSlip39AdvancedBackup(InputFlowBase):
def input_flow_tt(self) -> BRGeneratorType: def input_flow_tt(self) -> BRGeneratorType:
yield # 1. Checklist yield # 1. Checklist
self.debug.press_yes() self.debug.press_yes()
yield # 2. Set and confirm group count
if self.click_info: if self.click_info:
yield from click_info_button_tt(self.debug) yield from click_info_button_tt(self.debug)
yield # 2. Set and confirm group count
self.debug.press_yes() self.debug.press_yes()
yield # 3. Checklist yield # 3. Checklist
self.debug.press_yes() self.debug.press_yes()
yield # 4. Set and confirm group threshold
if self.click_info: if self.click_info:
yield from click_info_button_tt(self.debug) yield from click_info_button_tt(self.debug)
yield # 4. Set and confirm group threshold
self.debug.press_yes() self.debug.press_yes()
yield # 5. Checklist yield # 5. Checklist
self.debug.press_yes() self.debug.press_yes()
for _ in range(5): # for each of 5 groups for _ in range(5): # for each of 5 groups
if self.click_info:
yield from click_info_button_tt(self.debug)
yield # Set & Confirm number of shares yield # Set & Confirm number of shares
self.debug.press_yes()
if self.click_info: if self.click_info:
yield from click_info_button_tt(self.debug) yield from click_info_button_tt(self.debug)
self.debug.press_yes()
yield # Set & confirm share threshold value yield # Set & confirm share threshold value
if self.click_info:
yield from click_info_button_tt(self.debug)
self.debug.press_yes() self.debug.press_yes()
yield # Confirm show seeds yield # Confirm show seeds
self.debug.press_yes() self.debug.press_yes()

View File

@ -15,14 +15,14 @@ class PinFlow:
self, pin: str, second_different_pin: str | None = None self, pin: str, second_different_pin: str | None = None
) -> BRGeneratorType: ) -> BRGeneratorType:
yield # Enter PIN 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) self.debug.input(pin)
if self.debug.model == "Safe 3": if self.debug.model == "Safe 3":
yield # Reenter PIN yield # Reenter PIN
assert "re-enter PIN" in self.debug.wait_layout().text_content() assert "re-enter PIN" in self.debug.read_layout().text_content()
self.debug.press_yes() self.debug.press_yes()
yield # Enter PIN again 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: if second_different_pin is not None:
self.debug.input(second_different_pin) self.debug.input(second_different_pin)
else: else:
@ -36,7 +36,7 @@ class BackupFlow:
def confirm_new_wallet(self) -> BRGeneratorType: def confirm_new_wallet(self) -> BRGeneratorType:
yield yield
assert "By continuing you agree" in self.debug.wait_layout().text_content() assert "By continuing you agree" in self.debug.read_layout().text_content()
if self.debug.model == "Safe 3": if self.debug.model == "Safe 3":
self.debug.press_right() self.debug.press_right()
self.debug.press_yes() self.debug.press_yes()
@ -49,14 +49,14 @@ class RecoveryFlow:
def confirm_recovery(self) -> BRGeneratorType: def confirm_recovery(self) -> BRGeneratorType:
yield yield
assert "By continuing you agree" in self.debug.wait_layout().text_content() assert "By continuing you agree" in self.debug.read_layout().text_content()
if self.debug.model == "Safe 3": if self.debug.model == "Safe 3":
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:
yield yield
assert "Check your backup" in self.debug.wait_layout().text_content() assert "Check your backup" in self.debug.read_layout().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:
@ -73,43 +73,43 @@ class RecoveryFlow:
def tr_recovery_homescreen(self) -> BRGeneratorType: def tr_recovery_homescreen(self) -> BRGeneratorType:
yield yield
assert "number of words" in self.debug.wait_layout().text_content() assert "number of words" in self.debug.read_layout().text_content()
self.debug.press_yes() self.debug.press_yes()
def enter_your_backup(self) -> BRGeneratorType: def enter_your_backup(self) -> BRGeneratorType:
yield yield
assert "Enter your backup" in self.debug.wait_layout().text_content() assert "Enter your backup" in self.debug.read_layout().text_content()
if ( if (
self.debug.model == "Safe 3" self.debug.model == "Safe 3"
and "BACKUP CHECK" not in self.debug.wait_layout().title() and "BACKUP CHECK" not in self.debug.read_layout().title()
): ):
# Normal recovery has extra info (not dry run) # Normal recovery has extra info (not dry run)
self.debug.press_right(wait=True) self.debug.press_right()
self.debug.press_right(wait=True) self.debug.press_right()
self.debug.press_yes() self.debug.press_yes()
def enter_any_share(self) -> BRGeneratorType: def enter_any_share(self) -> BRGeneratorType:
yield yield
assert "Enter any share" in self.debug.wait_layout().text_content() assert "Enter any share" in self.debug.read_layout().text_content()
if ( if (
self.debug.model == "Safe 3" self.debug.model == "Safe 3"
and "BACKUP CHECK" not in self.debug.wait_layout().title() and "BACKUP CHECK" not in self.debug.read_layout().title()
): ):
# Normal recovery has extra info (not dry run) # Normal recovery has extra info (not dry run)
self.debug.press_right(wait=True) self.debug.press_right()
self.debug.press_right(wait=True) self.debug.press_right()
self.debug.press_yes() self.debug.press_yes()
def abort_recovery(self, confirm: bool) -> BRGeneratorType: def abort_recovery(self, confirm: bool) -> BRGeneratorType:
yield yield
if self.debug.model == "Safe 3": if self.debug.model == "Safe 3":
assert "number of words" in self.debug.wait_layout().text_content() assert "number of words" in self.debug.read_layout().text_content()
else: else:
assert "Enter any share" in self.debug.wait_layout().text_content() assert "Enter any share" in self.debug.read_layout().text_content()
self.debug.press_no() self.debug.press_no()
yield yield
assert "cancel the recovery" in self.debug.wait_layout().text_content() assert "cancel the recovery" in self.debug.read_layout().text_content()
if self.debug.model == "Safe 3": if self.debug.model == "Safe 3":
self.debug.press_right() self.debug.press_right()
if confirm: if confirm:
@ -121,33 +121,33 @@ class RecoveryFlow:
br = yield br = yield
assert br.code == B.MnemonicWordCount assert br.code == B.MnemonicWordCount
if self.debug.model == "Safe 3": if self.debug.model == "Safe 3":
assert "NUMBER OF WORDS" in self.debug.wait_layout().title() assert "NUMBER OF WORDS" in self.debug.read_layout().title()
else: else:
assert "number of words" in self.debug.wait_layout().text_content() assert "number of words" in self.debug.read_layout().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
assert "Invalid recovery seed" in self.debug.wait_layout().text_content() assert "Invalid recovery seed" in self.debug.read_layout().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
assert "Invalid recovery share" in self.debug.wait_layout().text_content() assert "Invalid recovery share" in self.debug.read_layout().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
assert "Group threshold reached" in self.debug.wait_layout().text_content() assert "Group threshold reached" in self.debug.read_layout().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
assert "Share already entered" in self.debug.wait_layout().text_content() assert "Share already entered" in self.debug.read_layout().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:
@ -155,46 +155,46 @@ class RecoveryFlow:
assert br.code == B.Warning assert br.code == B.Warning
assert ( assert (
"You have entered a share from another Shamir Backup" "You have entered a share from another Shamir Backup"
in self.debug.wait_layout().text_content() in self.debug.read_layout().text_content()
) )
self.debug.press_yes() self.debug.press_yes()
def success_share_group_entered(self) -> BRGeneratorType: def success_share_group_entered(self) -> BRGeneratorType:
yield yield
assert "You have entered" in self.debug.wait_layout().text_content() assert "You have entered" in self.debug.read_layout().text_content()
assert "Group" in self.debug.wait_layout().text_content() assert "Group" in self.debug.read_layout().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
assert ( assert (
"Wallet recovered successfully" in self.debug.wait_layout().text_content() "Wallet recovered successfully" in self.debug.read_layout().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:
br = yield br = yield
assert br.code == B.Success assert br.code == B.Success
assert "recovery seed is valid" in self.debug.wait_layout().text_content() assert "recovery seed is valid" in self.debug.read_layout().text_content()
self.debug.press_yes() self.debug.press_yes()
def success_slip39_dryrun_valid(self) -> BRGeneratorType: def success_slip39_dryrun_valid(self) -> BRGeneratorType:
br = yield br = yield
assert br.code == B.Success assert br.code == B.Success
assert "recovery shares are valid" in self.debug.wait_layout().text_content() assert "recovery shares are valid" in self.debug.read_layout().text_content()
self.debug.press_yes() self.debug.press_yes()
def warning_slip39_dryrun_mismatch(self) -> BRGeneratorType: def warning_slip39_dryrun_mismatch(self) -> BRGeneratorType:
br = yield br = yield
assert br.code == B.Warning assert br.code == B.Warning
assert "do not match" in self.debug.wait_layout().text_content() assert "do not match" in self.debug.read_layout().text_content()
self.debug.press_yes() self.debug.press_yes()
def warning_bip39_dryrun_mismatch(self) -> BRGeneratorType: def warning_bip39_dryrun_mismatch(self) -> BRGeneratorType:
br = yield br = yield
assert br.code == B.Warning assert br.code == B.Warning
assert "does not match" in self.debug.wait_layout().text_content() assert "does not match" in self.debug.read_layout().text_content()
self.debug.press_yes() self.debug.press_yes()
def success_more_shares_needed( def success_more_shares_needed(
@ -202,23 +202,23 @@ class RecoveryFlow:
) -> BRGeneratorType: ) -> BRGeneratorType:
yield yield
assert ( assert (
"1 more share needed" in self.debug.wait_layout().text_content().lower() "1 more share needed" in self.debug.read_layout().text_content().lower()
or "more shares needed" in self.debug.wait_layout().text_content().lower() or "more shares needed" in self.debug.read_layout().text_content().lower()
) )
if count_needed is not None: if count_needed is not None:
assert str(count_needed) in self.debug.wait_layout().text_content() assert str(count_needed) in self.debug.read_layout().text_content()
self.debug.press_yes() self.debug.press_yes()
def input_mnemonic(self, mnemonic: list[str]) -> BRGeneratorType: def input_mnemonic(self, mnemonic: list[str]) -> BRGeneratorType:
br = yield br = yield
assert br.code == B.MnemonicInput assert br.code == B.MnemonicInput
assert "MnemonicKeyboard" in self.debug.wait_layout().all_components() assert "MnemonicKeyboard" in self.debug.read_layout().all_components()
for index, word in enumerate(mnemonic): for index, word in enumerate(mnemonic):
if self.debug.model == "Safe 3": if self.debug.model == "Safe 3":
assert f"WORD {index + 1}" in self.debug.wait_layout().title() assert f"WORD {index + 1}" in self.debug.read_layout().title()
else: else:
assert ( assert (
f"Type word {index + 1}" in self.debug.wait_layout().text_content() f"Type word {index + 1}" in self.debug.read_layout().text_content()
) )
self.debug.input(word) self.debug.input(word)
@ -243,8 +243,8 @@ class RecoveryFlow:
self, self,
) -> BRGeneratorType: ) -> BRGeneratorType:
# Moving through the INFO button # Moving through the INFO button
self.debug.press_info()
yield yield
self.debug.press_info()
self.debug.swipe_up() self.debug.swipe_up()
self.debug.press_yes() self.debug.press_yes()
@ -258,8 +258,8 @@ class EthereumFlow:
def confirm_data(self, info: bool = False, cancel: bool = False) -> BRGeneratorType: def confirm_data(self, info: bool = False, cancel: bool = False) -> BRGeneratorType:
yield yield
assert self.debug.wait_layout().title() == "CONFIRM DATA" assert self.debug.read_layout().title() == "CONFIRM DATA"
assert "Size:" in self.debug.wait_layout().text_content() assert "Size:" in self.debug.read_layout().text_content()
if info: if info:
self.debug.press_info() self.debug.press_info()
elif cancel: elif cancel:
@ -269,22 +269,22 @@ class EthereumFlow:
def paginate_data(self) -> BRGeneratorType: def paginate_data(self) -> BRGeneratorType:
br = yield br = yield
assert self.debug.wait_layout().title() == "CONFIRM DATA" assert self.debug.read_layout().title() == "CONFIRM DATA"
assert br.pages is not None assert br.pages is not None
for i in range(br.pages): for i in range(br.pages):
self.debug.wait_layout() self.debug.read_layout()
if i < br.pages - 1: if i < br.pages - 1:
self.debug.swipe_up() self.debug.swipe_up()
self.debug.press_yes() self.debug.press_yes()
def paginate_data_go_back(self) -> BRGeneratorType: def paginate_data_go_back(self) -> BRGeneratorType:
br = yield br = yield
assert self.debug.wait_layout().title() == "CONFIRM DATA" assert self.debug.read_layout().title() == "CONFIRM DATA"
assert br.pages is not None assert br.pages is not None
assert br.pages > 2 assert br.pages > 2
if self.debug.model == "T": if self.debug.model == "T":
self.debug.swipe_up(wait=True) self.debug.swipe_up()
self.debug.swipe_up(wait=True) self.debug.swipe_up()
self.debug.click(self.GO_BACK) self.debug.click(self.GO_BACK)
else: else:
self.debug.press_right() self.debug.press_right()
@ -295,7 +295,7 @@ class EthereumFlow:
def confirm_tx(self, cancel: bool = False, info: bool = False) -> BRGeneratorType: def confirm_tx(self, cancel: bool = False, info: bool = False) -> BRGeneratorType:
yield yield
assert self.debug.wait_layout().title() == "RECIPIENT" assert self.debug.read_layout().title() == "RECIPIENT"
if self.debug.model == "T": if self.debug.model == "T":
if cancel: if cancel:
@ -303,25 +303,25 @@ class EthereumFlow:
else: else:
self.debug.press_yes() self.debug.press_yes()
yield yield
assert self.debug.wait_layout().title() == "SUMMARY" assert self.debug.read_layout().title() == "SUMMARY"
assert "Maximum fee:" in self.debug.wait_layout().text_content() assert "Maximum fee:" in self.debug.read_layout().text_content()
if info: if info:
self.debug.press_info(wait=True) self.debug.press_info()
assert "Gas limit:" in self.debug.wait_layout().text_content() assert "Gas limit:" in self.debug.read_layout().text_content()
assert "Gas price:" in self.debug.wait_layout().text_content() assert "Gas price:" in self.debug.read_layout().text_content()
self.debug.press_no(wait=True) self.debug.press_no()
self.debug.press_yes() self.debug.press_yes()
else: else:
if cancel: if cancel:
self.debug.press_left() self.debug.press_left()
else: else:
self.debug.press_right() self.debug.press_right()
assert "Maximum fee:" in self.debug.wait_layout().text_content() assert "Maximum fee:" in self.debug.read_layout().text_content()
if info: if info:
self.debug.press_right(wait=True) self.debug.press_right()
assert "Gas limit:" in self.debug.wait_layout().text_content() assert "Gas limit:" in self.debug.read_layout().text_content()
self.debug.press_right(wait=True) self.debug.press_right()
assert "Gas price:" in self.debug.wait_layout().text_content() assert "Gas price:" in self.debug.read_layout().text_content()
self.debug.press_left(wait=True) self.debug.press_left()
self.debug.press_left(wait=True) self.debug.press_left()
self.debug.press_middle() self.debug.press_middle()

View File

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