feat(core/eckhart): pin and wipe code device menu tests
What changed, and why it matters
This commit adds and updates automated user-interface tests for the PIN and wipe-code menus on the Trezor Safe 7 (Eckhart) hardware wallet. It also renames some Spanish translation strings for consistency. There is no change to the firmware's security logic, cryptography, or device behavior—only test code and translations are modified.
No security action required. This is a routine test-coverage and translation update. Reviewers may verify the new tests exercise the intended UI flows and that translation changes are intentional.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff is a test-only/feature-test commit for the ‘eckhart’ model. It introduces tests/click_tests/device_menu/test_pin.py and tests/click_tests/device_menu/test_wipe_code.py, renames test_wipe.py to test_wipe_device.py, refactors common test helpers to raise custom exceptions instead of using bare asserts, generalizes PIN-entry helpers to handle both PIN and wipe code, updates Spanish translation keys for wipe-code labels, and refreshes UI test fixture hashes in tests/ui_tests/fixtures.json. No firmware source code is changed.
Changed components
tests/click_tests/device_menu/common.pytests/click_tests/device_menu/test_pin.pytests/click_tests/device_menu/test_wipe_code.pytests/click_tests/device_menu/test_wipe_device.pytests/click_tests/test_pin.pytests/ui_tests/fixtures.jsoncore/translations/es.jsoncore/translations/signatures.jsonInspect captured patch +817 / −140
diff --git a/core/translations/es.json b/core/translations/es.json
index d41a21ed..833df3a4 100644
--- a/core/translations/es.json
+++ b/core/translations/es.json
@@ -1402,18 +1402,8 @@
"Delizia": "Cancelar la configuración del código de borrado",
"Eckhart": "¿Cancelar la configuración del código de borrado?"
},
- "wipe_code__change": {
- "Bolt": "Cambiar el código de borrar",
- "Caesar": "Cambiar el código de borrar",
- "Delizia": "Cambiar el código de borrar",
- "Eckhart": "Cambiar el código de restablecimiento"
- },
- "wipe_code__change_question": {
- "Bolt": "¿Cambiar el código de borrar?",
- "Caesar": "¿Cambiar el código de borrar?",
- "Delizia": "¿Cambiar el código de borrar?",
- "Eckhart": "¿Cambiar el código de restablecimiento?"
- },
+ "wipe_code__change": "Cambiar el código de borrar",
+ "wipe_code__change_question": "¿Cambiar el código de borrar?",
"wipe_code__changed": "El código de borrar se ha cambiado.",
"wipe_code__diff_from_pin": "El código de borrar debe ser diferente del PIN.",
"wipe_code__disabled": "El código de borrar se ha desactivado.",
@@ -1434,13 +1424,13 @@
"Bolt": "",
"Caesar": "",
"Delizia": "",
- "Eckhart": "Eliminar código de restablecimiento"
+ "Eckhart": "Eliminar código de borrar"
},
"wipe_code__title": {
"Bolt": "",
"Caesar": "",
"Delizia": "",
- "Eckhart": "Código de restablecimiento"
+ "Eckhart": "Código de borrar"
},
"wipe_code__title_check": "Revisar cód. borrar",
"wipe_code__title_invalid": "Cód. borrar no válido",
diff --git a/core/translations/signatures.json b/core/translations/signatures.json
index f7c57f2c..6da3b541 100644
--- a/core/translations/signatures.json
+++ b/core/translations/signatures.json
@@ -1,8 +1,8 @@
{
"current": {
- "merkle_root": "50ba0cc18622c3c5aa8dcc5957ae87089ffb166690d91d6c3d8afdcdfa6f30dc",
- "datetime": "2025-10-03T18:06:56.227560+00:00",
- "commit": "625ffadfb87b854043a0422eefd88647c709fc52"
+ "merkle_root": "ccb0df2c3dcb857a8e10efd5f679236aa561a02c972e378394afd480eebff165",
+ "datetime": "2025-10-06T16:50:46.913463+00:00",
+ "commit": "cda35eaaf44d28373fa92ef886d57c05a8a8372e"
},
"history": [
{
diff --git a/tests/click_tests/device_menu/common.py b/tests/click_tests/device_menu/common.py
index da77c88d..018e89c4 100644
--- a/tests/click_tests/device_menu/common.py
+++ b/tests/click_tests/device_menu/common.py
@@ -269,13 +269,17 @@ class Menu(Enum):
menu = debug.read_layout().vertical_menu_content()
path = self.path_from_root()
matches = [(idx, x) for idx, x in enumerate(path) if x in menu]
- assert len(matches) == 1
+
+ if len(matches) < 1:
+ raise PathNotFound(self.name)
+ elif len(matches) > 1:
+ raise PathNotUnique(self.name)
start_idx = matches[0][0]
# Follow the path
for label in self.path_from_root()[start_idx:]:
menu = debug.read_layout().vertical_menu_content()
- idx = menu.index(label)
+ idx = menu_idx(label, menu)
debug.button_actions.navigate_to_menu_item(idx)
assert_device_screen(debug, self)
@@ -284,9 +288,11 @@ class Menu(Enum):
expected = self.content(features)
if self == Menu.PAIR_AND_CONNECT:
# The connection menu has two permanent items at the end
- assert menu[-2:] == expected
+ if menu[-2:] != expected:
+ raise MenuContentNotMatching(menu[-2:], expected)
else:
- assert menu == expected
+ if menu != expected:
+ raise MenuContentNotMatching(menu, expected)
return menu
@classmethod
@@ -425,6 +431,43 @@ class Menu(Enum):
menu = root_child.navigate_back(debug, features)
+class NoSecuritySettings(Exception):
+ pass
+
+
+class MenuItemNotFound(Exception):
+ def __init__(self, item_name: str):
+ self.item_name = item_name
+
+ def __str__(self):
+ return f"Menu item '{self.item_name}' not found"
+
+
+class PathNotFound(Exception):
+ def __init__(self, item_name: str):
+ self.item_name = item_name
+
+ def __str__(self):
+ return f"Path to '{self.item_name}' not found"
+
+
+class PathNotUnique(Exception):
+ def __init__(self, item_name: str):
+ self.item_name = item_name
+
+ def __str__(self):
+ return f"Path to '{self.item_name}' is not unique"
+
+
+class MenuContentNotMatching(Exception):
+ def __init__(self, actual: list[str] | None, expected: list[str] | None):
+ self.actual = actual
+ self.expected = expected
+
+ def __str__(self):
+ return f"Menu content for '{self.actual}' does not match expected '{self.expected}'"
+
+
def open_device_menu(debug: "DebugLink"):
# Start at homescreen
debug.synchronize_at("Homescreen")
@@ -450,6 +493,12 @@ def assert_device_screen(debug: "DebugLink", menu: Menu):
)
+def menu_idx(item: str, menu: list[str]) -> int:
+ if item not in menu:
+ raise MenuItemNotFound(item)
+ return menu.index(item)
+
+
def enter_pin(device_handler: "BackgroundDeviceHandler", pin: str = PIN4):
debug = device_handler.debuglink()
device_handler.get_session()
diff --git a/tests/click_tests/device_menu/test_auto_lock.py b/tests/click_tests/device_menu/test_auto_lock.py
index fa8c669d..db6ceb67 100644
--- a/tests/click_tests/device_menu/test_auto_lock.py
+++ b/tests/click_tests/device_menu/test_auto_lock.py
@@ -22,6 +22,7 @@ from ... import translations as TR
from ..test_pin import PIN4
from .common import (
Menu,
+ MenuItemNotFound,
assert_device_screen,
close_device_menu,
enter_pin,
@@ -60,7 +61,7 @@ def test_auto_lock_uninitialized(device_handler: "BackgroundDeviceHandler"):
assert features.pin_protection is False
# device is uninitialized, security menu is not accessible
- with pytest.raises(ValueError, match=f"'{TR.words__security}' is not in"):
+ with pytest.raises(MenuItemNotFound, match=TR.words__security):
prepare_auto_lock(debug, features)
@@ -72,7 +73,7 @@ def test_auto_lock_pin_not_set(device_handler: "BackgroundDeviceHandler"):
assert features.pin_protection is False
# device is uninitialized, auto-lock menu is not accessible
- with pytest.raises(ValueError, match=f"'{TR.auto_lock__title}' is not in"):
+ with pytest.raises(MenuItemNotFound, match=TR.auto_lock__title):
prepare_auto_lock(debug, features)
diff --git a/tests/click_tests/device_menu/test_check_backup.py b/tests/click_tests/device_menu/test_check_backup.py
index e874ac4d..20f8d524 100644
--- a/tests/click_tests/device_menu/test_check_backup.py
+++ b/tests/click_tests/device_menu/test_check_backup.py
@@ -22,7 +22,14 @@ from trezorlib import messages
from ... import translations as TR
from ...common import MNEMONIC12
-from .common import Menu, assert_device_screen, close_device_menu, open_device_menu
+from .common import (
+ Menu,
+ MenuItemNotFound,
+ NoSecuritySettings,
+ assert_device_screen,
+ close_device_menu,
+ open_device_menu,
+)
if TYPE_CHECKING:
from trezorlib.debuglink import DebugLink
@@ -33,17 +40,14 @@ if TYPE_CHECKING:
pytestmark = [pytest.mark.models("eckhart")]
-class NoSecuritySettings(Exception):
- pass
-
-
def prepare_check_backup(debug: "DebugLink", features: "Features") -> None:
check_backup_title = TR.reset__check_backup_title
security_content = Menu.SECURITY.content(features)
if security_content is None:
raise NoSecuritySettings
- assert check_backup_title in Menu.SECURITY.content(features)
+ if check_backup_title not in Menu.SECURITY.content(features):
+ raise MenuItemNotFound(check_backup_title)
# Open device menu
open_device_menu(debug)
@@ -77,7 +81,7 @@ def test_backup_needed_fails(device_handler: "BackgroundDeviceHandler"):
assert features.backup_availability == messages.BackupAvailability.Required
# Device needs backup, check backup is not accessible in the security settings
- with pytest.raises(AssertionError, match=f"'{TR.reset__check_backup_title}' in"):
+ with pytest.raises(MenuItemNotFound, match=TR.reset__check_backup_title):
prepare_check_backup(debug, features)
@@ -88,7 +92,7 @@ def test_no_backup_fails(device_handler: "BackgroundDeviceHandler"):
assert features.no_backup is True
# Device has no backup, check backup is not accessible in the security settings
- with pytest.raises(AssertionError, match=f"'{TR.reset__check_backup_title}' in"):
+ with pytest.raises(MenuItemNotFound, match=TR.reset__check_backup_title):
prepare_check_backup(debug, features)
diff --git a/tests/click_tests/device_menu/test_label.py b/tests/click_tests/device_menu/test_label.py
index 7fc49fea..9e95b034 100644
--- a/tests/click_tests/device_menu/test_label.py
+++ b/tests/click_tests/device_menu/test_label.py
@@ -20,7 +20,13 @@ import pytest
from ... import translations as TR
from ..common import KeyboardCategory, delete_char, go_to_category, press_char
-from .common import Menu, assert_device_screen, close_device_menu, open_device_menu
+from .common import (
+ Menu,
+ MenuItemNotFound,
+ assert_device_screen,
+ close_device_menu,
+ open_device_menu,
+)
if TYPE_CHECKING:
from trezorlib.debuglink import DebugLink
@@ -77,7 +83,8 @@ def cancel_label(debug: "DebugLink") -> None:
def prepare_label_dialogue(debug: "DebugLink", features: "Features") -> None:
label_title = TR.words__name
- assert label_title in Menu.DEVICE.content(features)
+ if label_title not in Menu.DEVICE.content(features):
+ raise MenuItemNotFound(label_title)
# Open device menu
open_device_menu(debug)
@@ -100,7 +107,7 @@ def test_label_uninitialized(device_handler: "BackgroundDeviceHandler"):
assert features.unfinished_backup is False
# device is uninitialized, device name is not accessible in the device menu
- with pytest.raises(AssertionError, match=f"'{TR.words__name}' in"):
+ with pytest.raises(MenuItemNotFound, match=TR.words__name):
prepare_label_dialogue(debug, features)
diff --git a/tests/click_tests/device_menu/test_notifications.py b/tests/click_tests/device_menu/test_notifications.py
index c0a1e352..c8c55f4f 100644
--- a/tests/click_tests/device_menu/test_notifications.py
+++ b/tests/click_tests/device_menu/test_notifications.py
@@ -23,7 +23,14 @@ from trezorlib import device, messages
from ... import translations as TR
from .. import reset
from ..test_pin import PIN4, _assert_pin_entry, _enter_two_times
-from .common import Menu, assert_device_screen, close_device_menu, open_device_menu
+from .common import (
+ Menu,
+ MenuItemNotFound,
+ assert_device_screen,
+ close_device_menu,
+ menu_idx,
+ open_device_menu,
+)
if TYPE_CHECKING:
@@ -113,8 +120,8 @@ def test_seedless(device_handler: "BackgroundDeviceHandler"):
layout = debug.read_layout()
# No "Backup needed" notification should be present
- with pytest.raises(ValueError, match=f"'{backup_notification}' is not in"):
- layout.vertical_menu_content().index(backup_notification)
+ with pytest.raises(MenuItemNotFound, match=backup_notification):
+ menu_idx(backup_notification, layout.vertical_menu_content())
@pytest.mark.setup_client(needs_backup=True)
diff --git a/tests/click_tests/device_menu/test_pin.py b/tests/click_tests/device_menu/test_pin.py
new file mode 100644
index 00000000..1a538aa9
--- /dev/null
+++ b/tests/click_tests/device_menu/test_pin.py
@@ -0,0 +1,221 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 2012-2025 SatoshiLabs and contributors
+#
+# This library is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the License along with this library.
+# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
+
+from enum import Enum
+from typing import TYPE_CHECKING
+
+import pytest
+
+from ... import translations as TR
+from ..common import go_next
+from ..test_pin import (
+ PIN1,
+ PIN4,
+ _cancel_code,
+ _delete_code,
+ _enter_two_times,
+ _input_code,
+ _input_see_confirm,
+ _see_code,
+)
+from .common import (
+ Menu,
+ NoSecuritySettings,
+ assert_device_screen,
+ close_device_menu,
+ enter_pin,
+ open_device_menu,
+)
+
+if TYPE_CHECKING:
+ from trezorlib.debuglink import DebugLink
+ from trezorlib.messages import Features
+
+ from ...device_handler import BackgroundDeviceHandler
+# Trezor Safe 7 only
+pytestmark = [pytest.mark.models("eckhart")]
+
+PIN_TITLE = "pin__title"
+
+
+class Situation(Enum):
+ SETUP = 1
+ CHANGE = 2
+ REMOVE = 3
+
+
+def prepare_pin_dialogue(
+ debug: "DebugLink", features: "Features", situation: Situation
+) -> None:
+ pin_title = TR.translate(PIN_TITLE)
+ security_content = Menu.SECURITY.content(features)
+ if security_content is None:
+ raise NoSecuritySettings
+
+ assert pin_title in security_content
+
+ # Open device menu
+ open_device_menu(debug)
+
+ if situation is Situation.SETUP:
+ assert features.pin_protection is False
+ # Pin submenu shouldn't be present
+ assert Menu.PIN.content(features) is None
+ # Navigate to security menu
+ Menu.SECURITY.navigate_to(debug, features)
+
+ title = TR.translate(PIN_TITLE)
+ else:
+ assert features.pin_protection is True
+ # Navigate to pin menu
+ Menu.PIN.navigate_to(debug, features)
+
+ if situation is Situation.CHANGE:
+ title = TR.pin__change
+ elif situation is Situation.REMOVE:
+ title = TR.pin__remove
+ else:
+ raise RuntimeError("Unknown situation")
+
+ # Trigger pin action
+ layout = debug.read_layout()
+ pin_idx = layout.vertical_menu_content().index(title)
+ debug.button_actions.navigate_to_menu_item(pin_idx)
+
+
+@pytest.mark.setup_client(uninitialized=True)
+def test_pin_uninitialized(device_handler: "BackgroundDeviceHandler"):
+ debug = device_handler.debuglink()
+ features = device_handler.features()
+
+ # Device is uninitialized, the entire security menu is not accessible
+ with pytest.raises(NoSecuritySettings):
+ prepare_pin_dialogue(debug, features, Situation.SETUP)
+
+
+@pytest.mark.setup_client(pin=None)
+def test_pin_setup(device_handler: "BackgroundDeviceHandler"):
+ debug = device_handler.debuglink()
+ features = device_handler.features()
+
+ prepare_pin_dialogue(debug, features, Situation.SETUP)
+
+ # Intro screen
+ go_next(debug)
+ # Enter pin twice
+ _enter_two_times(debug, PIN4, PIN4)
+ # Confirmation screen
+ go_next(debug)
+
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.pin_protection is True
+
+
+@pytest.mark.setup_client(pin=None)
+def test_pin_cancel_intro(device_handler: "BackgroundDeviceHandler"):
+
+ debug = device_handler.debuglink()
+ features = device_handler.features()
+
+ prepare_pin_dialogue(debug, features, Situation.SETUP)
+
+ # Go to menu in the intro screen
+ debug.click(debug.screen_buttons.menu())
+ # Press cancel
+ debug.button_actions.navigate_to_menu_item(0)
+ # Confirm cancel
+ go_next(debug)
+
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.pin_protection is False
+
+
+@pytest.mark.setup_client(pin=None)
+def test_pin_cancel_keyboard(device_handler: "BackgroundDeviceHandler"):
+
+ debug = device_handler.debuglink()
+ features = device_handler.features()
+
+ prepare_pin_dialogue(debug, features, Situation.SETUP)
+
+ # Intro screen
+ go_next(debug)
+ # Enter PIN, then cancel
+ _input_code(debug, PIN1)
+ _see_code(debug)
+ _delete_code(debug, len(PIN1))
+ _see_code(debug)
+ _cancel_code(debug)
+
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.pin_protection is False
+
+
+@pytest.mark.setup_client(pin=PIN4)
+def test_pin_change(device_handler: "BackgroundDeviceHandler"):
+ enter_pin(device_handler)
+
+ debug = device_handler.debuglink()
+ features = device_handler.features()
+ assert features.pin_protection is True
+ prepare_pin_dialogue(debug, features, Situation.CHANGE)
+
+ # Intro screen
+ go_next(debug)
+ # Input old pin to authorize
+ _input_see_confirm(debug, PIN4)
+ # Input new pin 2 times
+ _enter_two_times(debug, PIN1, PIN1)
+ # Confirmation screen
+ go_next(debug)
+
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.pin_protection is True
+
+
+@pytest.mark.setup_client(pin=PIN4)
+def test_pin_remove(device_handler: "BackgroundDeviceHandler"):
+ enter_pin(device_handler)
+
+ debug = device_handler.debuglink()
+ features = device_handler.features()
+
+ prepare_pin_dialogue(debug, features, Situation.REMOVE)
+
+ # Intro screen
+ go_next(debug)
+ # Enter pin to remove
+ _input_see_confirm(debug, PIN4)
+ # Confirmation screen
+ go_next(debug)
+
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.pin_protection is False
diff --git a/tests/click_tests/device_menu/test_wipe.py b/tests/click_tests/device_menu/test_wipe.py
deleted file mode 100644
index 9e21f698..00000000
--- a/tests/click_tests/device_menu/test_wipe.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# This file is part of the Trezor project.
-#
-# Copyright (C) 2012-2025 SatoshiLabs and contributors
-#
-# This library is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License version 3
-# as published by the Free Software Foundation.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the License along with this library.
-# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
-
-from typing import TYPE_CHECKING
-
-import pytest
-
-from ... import translations as TR
-from .common import PIN4, Menu, enter_pin, open_device_menu
-
-if TYPE_CHECKING:
- from ...device_handler import BackgroundDeviceHandler
-
-# Trezor Safe 7 only
-pytestmark = [pytest.mark.models("eckhart")]
-
-
-@pytest.mark.invalidate_client
-@pytest.mark.setup_client(pin=PIN4)
-def test_wipe(device_handler: "BackgroundDeviceHandler"):
- enter_pin(device_handler)
- debug = device_handler.debuglink()
-
- features = device_handler.features()
- assert features.initialized is True
- assert features.pin_protection is True
-
- wipe_title = TR.wipe__title
- assert wipe_title in Menu.DEVICE.content(features)
-
- open_device_menu(debug)
-
- # Navigate to device menu
- Menu.DEVICE.navigate_to(debug, features)
-
- # Trigger wipe
- layout = debug.read_layout()
- wipe_idx = layout.vertical_menu_content().index(wipe_title)
- debug.button_actions.navigate_to_menu_item(wipe_idx)
-
- # Confirm wipe
- debug.synchronize_at(wipe_title)
- debug.click(debug.screen_buttons.ok())
-
- # Wait for the homescreen to appear
- debug.synchronize_at("Homescreen")
-
- device_handler.client = device_handler.client.get_new_client()
- features = device_handler.features()
- assert features.initialized is False
- assert features.pin_protection is False
diff --git a/tests/click_tests/device_menu/test_wipe_code.py b/tests/click_tests/device_menu/test_wipe_code.py
new file mode 100644
index 00000000..0b68e319
--- /dev/null
+++ b/tests/click_tests/device_menu/test_wipe_code.py
@@ -0,0 +1,313 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 2012-2025 SatoshiLabs and contributors
+#
+# This library is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the License along with this library.
+# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
+
+from typing import TYPE_CHECKING
+
+import pytest
+
+from ... import translations as TR
+from ..common import go_next
+from ..test_pin import (
+ PIN1,
+ PIN4,
+ _cancel_code,
+ _delete_code,
+ _enter_two_times,
+ _input_code,
+ _input_see_confirm,
+ _see_code,
+)
+from .common import (
+ Menu,
+ MenuItemNotFound,
+ assert_device_screen,
+ close_device_menu,
+ enter_pin,
+ open_device_menu,
+)
+from .test_pin import Situation, prepare_pin_dialogue
+
+if TYPE_CHECKING:
+ from trezorlib.debuglink import DebugLink
+
+ from ...device_handler import BackgroundDeviceHandler
+# Trezor Safe 7 only
+pytestmark = [pytest.mark.models("eckhart")]
+
+WIPE_CODE = "2"
+WIPE_CODE_TITLE = "wipe_code__title"
+
+
+def change_wipe_code(
+ debug: "DebugLink", pin: str = PIN4, wipe_code: str = WIPE_CODE
+) -> None:
+ # Intro screen
+ go_next(debug)
+ # Enter pin to authorize
+ _input_see_confirm(debug, pin)
+ # Enter new wipe code twice
+ _enter_two_times(debug, wipe_code, wipe_code)
+ # Confirmation screen
+ go_next(debug)
+
+
+def prepare_wipe_dialogue(
+ device_handler: "BackgroundDeviceHandler", situation: Situation
+) -> None:
+
+ debug = device_handler.debuglink()
+ features = device_handler.features()
+ wipe_title = TR.translate(WIPE_CODE_TITLE)
+ if wipe_title not in Menu.SECURITY.content(features):
+ raise MenuItemNotFound(wipe_title)
+
+ if situation in (Situation.REMOVE, Situation.CHANGE):
+ assert features.wipe_code_protection is False
+ open_device_menu(debug)
+ Menu.SECURITY.navigate_to(debug, features)
+ layout = debug.read_layout()
+ idx = layout.vertical_menu_content().index(TR.translate(WIPE_CODE_TITLE))
+ debug.button_actions.navigate_to_menu_item(idx)
+
+ change_wipe_code(debug)
+
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.wipe_code_protection is True
+
+ # Open device menu
+ open_device_menu(debug)
+
+ assert features.pin_protection is True
+
+ if situation is Situation.SETUP:
+ assert features.wipe_code_protection is False
+ # Wipe code submenu shouldn't be present
+ assert Menu.WIPE_CODE.content(features) is None
+ # Navigate to security menu
+ Menu.SECURITY.navigate_to(debug, features)
+ title = TR.translate(WIPE_CODE_TITLE)
+ else:
+ assert features.wipe_code_protection is True
+ # Navigate to wipe code menu
+ Menu.WIPE_CODE.navigate_to(debug, features)
+
+ if situation is Situation.CHANGE:
+ title = TR.wipe_code__change
+
+ elif situation is Situation.REMOVE:
+ title = TR.wipe_code__remove
+ else:
+ raise RuntimeError("Unknown situation")
+
+ # Trigger wipe code dialogue
+ layout = debug.read_layout()
+ idx = layout.vertical_menu_content().index(title)
+ debug.button_actions.navigate_to_menu_item(idx)
+
+
+@pytest.mark.setup_client(pin=None)
+def test_wipe_code_setup_pin_unset(device_handler: "BackgroundDeviceHandler"):
+
+ # Device doesn't have the pin set, the wipe code button is not accessible
+ with pytest.raises(
+ MenuItemNotFound,
+ match=TR.translate("wipe_code__title"),
+ ):
+ prepare_wipe_dialogue(device_handler, Situation.SETUP)
+
+
+@pytest.mark.setup_client(pin=PIN4)
+def test_wipe_code_setup(device_handler: "BackgroundDeviceHandler"):
+ enter_pin(device_handler)
+
+ debug = device_handler.debuglink()
+ features = device_handler.features()
+
+ assert features.wipe_code_protection is False
+
+ prepare_wipe_dialogue(device_handler, Situation.SETUP)
+
+ change_wipe_code(debug, wipe_code=WIPE_CODE)
+
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.wipe_code_protection is True
+
+
+@pytest.mark.setup_client(pin=PIN4)
+def test_wipe_code_same_as_pin(device_handler: "BackgroundDeviceHandler"):
+ enter_pin(device_handler)
+
+ debug = device_handler.debuglink()
+ prepare_wipe_dialogue(device_handler, Situation.SETUP)
+
+ # Intro screen
+ go_next(debug)
+ # Enter pin to authorize
+ _input_see_confirm(debug, PIN4)
+ # Input wipe code same as pin
+ _input_see_confirm(debug, PIN4)
+ # Try again
+ go_next(debug)
+ _enter_two_times(debug, WIPE_CODE, WIPE_CODE)
+ # Confirmation screen
+ go_next(debug)
+
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.wipe_code_protection is True
+
+
+@pytest.mark.setup_client(pin=PIN4)
+def test_wipe_code_cancel_intro(device_handler: "BackgroundDeviceHandler"):
+ enter_pin(device_handler)
+
+ debug = device_handler.debuglink()
+ features = device_handler.features()
+
+ assert features.wipe_code_protection is False
+
+ prepare_wipe_dialogue(device_handler, Situation.SETUP)
+
+ # Go to menu in the intro screen
+ debug.click(debug.screen_buttons.menu())
+ # Press cancel
+ debug.button_actions.navigate_to_menu_item(0)
+ # Confirm cancel
+ go_next(debug)
+
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.wipe_code_protection is False
+
+
+@pytest.mark.setup_client(pin=PIN4)
+def test_wipe_code_cancel_keyboard(device_handler: "BackgroundDeviceHandler"):
+ enter_pin(device_handler)
+
+ debug = device_handler.debuglink()
+ features = device_handler.features()
+
+ assert features.wipe_code_protection is False
+
+ prepare_wipe_dialogue(device_handler, Situation.SETUP)
+
+ # Intro screen
+ go_next(debug)
+ # Enter pin to authorize
+ _input_see_confirm(debug, PIN4)
+ # Enter wipe code, then cancel
+ _input_code(debug, PIN1)
+ _see_code(debug)
+ _delete_code(debug, len(PIN1))
+ _see_code(debug)
+ _cancel_code(debug)
+
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.wipe_code_protection is False
+
+
+@pytest.mark.setup_client(pin=PIN4)
+def test_wipe_code_change(device_handler: "BackgroundDeviceHandler"):
+ enter_pin(device_handler)
+
+ debug = device_handler.debuglink()
+
+ prepare_wipe_dialogue(device_handler, Situation.CHANGE)
+
+ change_wipe_code(debug, wipe_code="3")
+
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.wipe_code_protection is True
+
+
+@pytest.mark.setup_client(pin=PIN4)
+def test_wipe_code_remove(device_handler: "BackgroundDeviceHandler"):
+ enter_pin(device_handler)
+
+ debug = device_handler.debuglink()
+ features = device_handler.features()
+
+ prepare_wipe_dialogue(device_handler, Situation.REMOVE)
+
+ # Intro screen
+ go_next(debug)
+ # Enter pin to authorize
+ _input_see_confirm(debug, PIN4)
+ # Confirm removal
+ go_next(debug)
+
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.wipe_code_protection is False
+
+
+@pytest.mark.setup_client(pin=PIN4)
+def test_remove_pin_without_removing_wipe_code(
+ device_handler: "BackgroundDeviceHandler",
+):
+ enter_pin(device_handler)
+
+ debug = device_handler.debuglink()
+ features = device_handler.features()
+
+ # Setup wipe code first
+ open_device_menu(debug)
+ Menu.SECURITY.navigate_to(debug, features)
+ layout = debug.read_layout()
+ idx = layout.vertical_menu_content().index(TR.translate(WIPE_CODE_TITLE))
+ debug.button_actions.navigate_to_menu_item(idx)
+ change_wipe_code(debug)
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.pin_protection is True
+ assert features.wipe_code_protection is True
+
+ # Remove pin
+ prepare_pin_dialogue(debug, features, Situation.REMOVE)
+
+ # Intro screen
+ go_next(debug)
+
+ # Warning about existing wipe code
+ assert TR.pin__wipe_code_exists_description in debug.read_layout().text_content()
+ go_next(debug)
+
+ assert_device_screen(debug, Menu.SECURITY)
+ close_device_menu(debug)
+
+ features = device_handler.features()
+ assert features.pin_protection is True
+ assert features.wipe_code_protection is True
diff --git a/tests/click_tests/device_menu/test_wipe_device.py b/tests/click_tests/device_menu/test_wipe_device.py
new file mode 100644
index 00000000..9e21f698
--- /dev/null
+++ b/tests/click_tests/device_menu/test_wipe_device.py
@@ -0,0 +1,64 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 2012-2025 SatoshiLabs and contributors
+#
+# This library is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the License along with this library.
+# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
+
+from typing import TYPE_CHECKING
+
+import pytest
+
+from ... import translations as TR
+from .common import PIN4, Menu, enter_pin, open_device_menu
+
+if TYPE_CHECKING:
+ from ...device_handler import BackgroundDeviceHandler
+
+# Trezor Safe 7 only
+pytestmark = [pytest.mark.models("eckhart")]
+
+
+@pytest.mark.invalidate_client
+@pytest.mark.setup_client(pin=PIN4)
+def test_wipe(device_handler: "BackgroundDeviceHandler"):
+ enter_pin(device_handler)
+ debug = device_handler.debuglink()
+
+ features = device_handler.features()
+ assert features.initialized is True
+ assert features.pin_protection is True
+
+ wipe_title = TR.wipe__title
+ assert wipe_title in Menu.DEVICE.content(features)
+
+ open_device_menu(debug)
+
+ # Navigate to device menu
+ Menu.DEVICE.navigate_to(debug, features)
+
+ # Trigger wipe
+ layout = debug.read_layout()
+ wipe_idx = layout.vertical_menu_content().index(wipe_title)
+ debug.button_actions.navigate_to_menu_item(wipe_idx)
+
+ # Confirm wipe
+ debug.synchronize_at(wipe_title)
+ debug.click(debug.screen_buttons.ok())
+
+ # Wait for the homescreen to appear
+ debug.synchronize_at("Homescreen")
+
+ device_handler.client = device_handler.client.get_new_client()
+ features = device_handler.features()
+ assert features.initialized is False
+ assert features.pin_protection is False
diff --git a/tests/click_tests/test_pin.py b/tests/click_tests/test_pin.py
index 45c8372f..b1afa4f1 100644
--- a/tests/click_tests/test_pin.py
+++ b/tests/click_tests/test_pin.py
@@ -42,6 +42,7 @@ PIN_INVALID = pytest.raises(exceptions.TrezorFailure, match="PIN invalid")
DELAY_S = 1.1
PIN4 = "1234"
+PIN1 = "1"
PIN24 = "875163065288639289952973"
PIN50 = "31415926535897932384626433832795028841971693993751"
PIN60 = PIN50 + "9" * 10
@@ -171,8 +172,8 @@ def _assert_pin_entry(debug: "DebugLink") -> None:
assert "PinKeyboard" in debug.read_layout().all_components()
-def _input_pin(debug: "DebugLink", pin: str, check: bool = False) -> None:
- """Input the PIN"""
+def _input_code(debug: "DebugLink", pin: str, check: bool = False) -> None:
+ """Input the PIN or Wipe code"""
before = debug.read_layout().pin()
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
digits_order = debug.read_layout().tt_pin_digits_order()
@@ -193,7 +194,7 @@ def _input_pin(debug: "DebugLink", pin: str, check: bool = False) -> None:
assert before + pin == after
-def _see_pin(debug: "DebugLink") -> None:
+def _see_code(debug: "DebugLink") -> None:
"""Navigate to "SHOW" and press it"""
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
@@ -209,7 +210,7 @@ def _see_pin(debug: "DebugLink") -> None:
raise RuntimeError("Unknown model")
-def _delete_pin(debug: "DebugLink", digits_to_delete: int, check: bool = True) -> None:
+def _delete_code(debug: "DebugLink", digits_to_delete: int, check: bool = True) -> None:
"""Navigate to "DELETE" and press it how many times requested"""
if check:
before = debug.read_layout().pin()
@@ -248,11 +249,11 @@ def _delete_all(debug: "DebugLink", check: bool = True) -> None:
assert after == ""
-def _cancel_pin(debug: "DebugLink") -> None:
+def _cancel_code(debug: "DebugLink") -> None:
"""Navigate to "CANCEL" and press it"""
# It is the same button as DELETE
# TODO: implement cancel PIN for TR?
- _delete_pin(debug, 1, check=False)
+ _delete_code(debug, 1, check=False)
# Note: `prepare()` context manager will send a tap after PIN cancellation,
# so we make sure the lockscreen is already up to receive it -- otherwise
@@ -271,8 +272,8 @@ def _confirm_pin(debug: "DebugLink") -> None:
def _input_see_confirm(debug: "DebugLink", pin: str) -> None:
- _input_pin(debug, pin)
- _see_pin(debug)
+ _input_code(debug, pin)
+ _see_code(debug)
_confirm_pin(debug)
@@ -308,11 +309,11 @@ def test_pin_empty_cannot_send(device_handler: "BackgroundDeviceHandler"):
@pytest.mark.setup_client(pin=PIN24)
def test_pin_long_delete(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler) as debug:
- _input_pin(debug, PIN24)
- _see_pin(debug)
+ _input_code(debug, PIN24)
+ _see_code(debug)
- _delete_pin(debug, 10)
- _see_pin(debug)
+ _delete_code(debug, 10)
+ _see_code(debug)
_input_see_confirm(debug, PIN24[-10:])
@@ -320,8 +321,8 @@ def test_pin_long_delete(device_handler: "BackgroundDeviceHandler"):
@pytest.mark.setup_client(pin=PIN4)
def test_pin_delete_hold(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler) as debug:
- _input_pin(debug, PIN4)
- _see_pin(debug)
+ _input_code(debug, PIN4)
+ _see_code(debug)
_delete_all(debug)
@@ -331,13 +332,13 @@ def test_pin_delete_hold(device_handler: "BackgroundDeviceHandler"):
@pytest.mark.setup_client(pin=PIN60[:MAX_PIN_LEN])
def test_pin_longer_than_max(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler) as debug:
- _input_pin(debug, PIN60, check=False)
+ _input_code(debug, PIN60, check=False)
# What is over 50 digits was not entered
# TODO: do some UI change when limit is reached?
assert debug.read_layout().pin() == PIN60[:MAX_PIN_LEN]
- _see_pin(debug)
+ _see_code(debug)
_confirm_pin(debug)
@@ -352,11 +353,11 @@ def test_pin_incorrect(device_handler: "BackgroundDeviceHandler"):
@pytest.mark.setup_client(pin=PIN4)
def test_pin_cancel(device_handler: "BackgroundDeviceHandler"):
with PIN_CANCELLED, prepare(device_handler, Situation.PIN_INPUT_CANCEL) as debug:
- _input_pin(debug, PIN4)
- _see_pin(debug)
- _delete_pin(debug, len(PIN4))
- _see_pin(debug)
- _cancel_pin(debug)
+ _input_code(debug, PIN4)
+ _see_code(debug)
+ _delete_code(debug, len(PIN4))
+ _see_code(debug)
+ _cancel_code(debug)
@pytest.mark.setup_client()
@@ -375,7 +376,7 @@ def test_pin_setup_mismatch(device_handler: "BackgroundDeviceHandler"):
LayoutType.Eckhart,
):
go_next(debug)
- _cancel_pin(debug)
+ _cancel_code(debug)
elif debug.layout_type is LayoutType.Caesar:
debug.press_middle()
debug.press_no()
@@ -419,12 +420,12 @@ def test_wipe_code_same_as_pin(device_handler: "BackgroundDeviceHandler"):
@pytest.mark.setup_client(pin=PIN4)
def test_last_digit_timeout(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler) as debug:
- _input_pin(debug, PIN4)
+ _input_code(debug, PIN4)
# wait until the last digit is hidden
time.sleep(DELAY_S)
assert debug.read_layout().display_style() is DisplayStyle.Hidden
# show the entire PIN
- _see_pin(debug)
+ _see_code(debug)
_confirm_pin(debug)
@@ -432,7 +433,7 @@ def test_last_digit_timeout(device_handler: "BackgroundDeviceHandler"):
@pytest.mark.setup_client(pin=PIN4)
def test_show_pin_issue5328(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler) as debug:
- _input_pin(debug, PIN4)
+ _input_code(debug, PIN4)
pos = debug.screen_buttons.pin_passphrase_input()
assert debug.read_layout().display_style() is DisplayStyle.LastOnly
# Hold the PIN area to show the PIN
@@ -455,7 +456,7 @@ def test_long_press_digit(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler) as debug:
# Input the PIN except the last digit
- _input_pin(debug, PIN4[:-1])
+ _input_code(debug, PIN4[:-1])
# Prepare last digit for long press
digits_order = debug.read_layout().tt_pin_digits_order()
diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json
index 0dbb1b3d..88310577 100644
--- a/tests/ui_tests/fixtures.json
+++ b/tests/ui_tests/fixtures.json
@@ -28846,10 +28846,24 @@
"T3W1_cs_device_menu-test_notifications.py::test_backup_needed": "3e9219faf28dc045827113d9a0ebec8e42437bc39cb3e437cbdb6b3992e89538",
"T3W1_cs_device_menu-test_notifications.py::test_pin_not_set": "f28e4c91d4ef1c82508397da3c6210c01a1b742b4e274d1a0dcee336251b943e",
"T3W1_cs_device_menu-test_notifications.py::test_seedless": "f47325f31b7cff66ad1ed9a2d98659b7161306440ae40e503ceac2407f8ffa92",
+"T3W1_cs_device_menu-test_pin.py::test_pin_cancel_intro": "4686fb8dc9482f4ad59b5af0768cc8be7f13c51f95a8b01c447e0d55a12559ae",
+"T3W1_cs_device_menu-test_pin.py::test_pin_cancel_keyboard": "35a24ebe4efe93ad4f4fe3aad0c5a2b165be5dc0972f341849d6d2fb5fc01c3d",
+"T3W1_cs_device_menu-test_pin.py::test_pin_change": "c0ebfeda518be967ee6c3967dc0aabb58fb684a573069c4427c6114e72f29fcf",
+"T3W1_cs_device_menu-test_pin.py::test_pin_remove": "20c541cbbc96b0112fc6aab6cec4051cb36207522e0ff5669b235b4db26b3b1b",
+"T3W1_cs_device_menu-test_pin.py::test_pin_setup": "e1b5dc2ecdfeb48d7457b493f3725d44695048422c93cf62001e7af35e0c1552",
+"T3W1_cs_device_menu-test_pin.py::test_pin_uninitialized": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
"T3W1_cs_device_menu-test_traverse_menu.py::test_traverse_initialized": "716bfac10048633597af6de93eb0068b9502e15af40a166ace4679f6f12fdacc",
"T3W1_cs_device_menu-test_traverse_menu.py::test_traverse_initialized_no_pin": "bc6c21f398423cb7eb7abc0b5ceba298ce6cf0b75017012f0c47d43098b6e773",
"T3W1_cs_device_menu-test_traverse_menu.py::test_traverse_uninitialized": "0599789a63673b017399934e68c65dd75c0f986de6aac4a0b946ca498bddfca0",
-"T3W1_cs_device_menu-test_wipe.py::test_wipe": "131557b26dd0c90ec5697de3ed2319e503392d4b1b2bc5797c10767679215182",
+"T3W1_cs_device_menu-test_wipe_code.py::test_remove_pin_without_removing_wipe_code": "e48837ff83ec2ce29e327dc72afc828eb4b60e08a5c793026b686ba372ebff4d",
+"T3W1_cs_device_menu-test_wipe_code.py::test_wipe_code_cancel_intro": "2dc9376ee993e60e4f749fe0c61d7df362161aa6cbe9ad9fc29bdffc7cc6c97a",
+"T3W1_cs_device_menu-test_wipe_code.py::test_wipe_code_cancel_keyboard": "2b60747c90a1c3d8801a0db10c55404c262318f0f1260ce097ec883e3dc22db7",
+"T3W1_cs_device_menu-test_wipe_code.py::test_wipe_code_change": "8ef81255af2869592dbcabe34dab22c382785b16fe3d65976f3a268e8c81fe17",
+"T3W1_cs_device_menu-test_wipe_code.py::test_wipe_code_remove": "0ed44359ffc22a0d4ec9f860931abf6be18a0453fb5f7d8bf453ea10e4427c21",
+"T3W1_cs_device_menu-test_wipe_code.py::test_wipe_code_same_as_pin": "2fc630b1925c654c5c5e9ccf4e2a7aab048f000d5622bc2ca52a9793c77fe1eb",
+"T3W1_cs_device_menu-test_wipe_code.py::test_wipe_code_setup": "2e7a3bb93f6a0eaba7ed4eca428640bdaf6b0b8ac84106f36633231a3171fb4c",
+"T3W1_cs_device_menu-test_wipe_code.py::test_wipe_code_setup_pin_unset": "c24521e569c08e3605b164212c876f8ac57c5eef6cca6f2ca53a635a883ebc4b",
+"T3W1_cs_device_menu-test_wipe_device.py::test_wipe": "131557b26dd0c90ec5697de3ed2319e503392d4b1b2bc5797c10767679215182",
"T3W1_cs_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "05f82756d2be9b1842f2958425a5d96540afb19395128dfc913969e0e1d889c5",
"T3W1_cs_test_autolock.py::test_autolock_does_not_interrupt_signing": "3731af33a6802c51b41bb7be3abf61589796f3810560ade4dcb6241466af43d4",
"T3W1_cs_test_autolock.py::test_autolock_interrupts_passphrase": "0de83d4610ebc0ef73b375f400cf1d1ad4fbf7f97278bdae9197079194b72025",
@@ -28936,10 +28950,24 @@
"T3W1_de_device_menu-test_notifications.py::test_backup_needed": "ecb0fe08d4aa61ebfb7c91da6eb271744f2d50e9dd3e71552a75703417c17d61",
"T3W1_de_device_menu-test_notifications.py::test_pin_not_set": "516e96f72ebec818a28a981efbd0f4ec65f7cff2478c053656759a76db20d033",
"T3W1_de_device_menu-test_notifications.py::test_seedless": "a5f1836c258f20a18e2be5dffc5583f684df48c7688a734f3fca89c01d613996",
+"T3W1_de_device_menu-test_pin.py::test_pin_cancel_intro": "811e224335cb6dc87b8fe96eb3f4b273b93facf84edb38240b1abaef66bf3aa7",
+"T3W1_de_device_menu-test_pin.py::test_pin_cancel_keyboard": "cde300f7bd136681b703da07acb5c7114dc2bf751a7a452814226f29e734577c",
+"T3W1_de_device_menu-test_pin.py::test_pin_change": "6577771d92baeebb1c5a93ada8b3f8759bbd5dbfe8b2a4b24329c87062d145c8",
+"T3W1_de_device_menu-test_pin.py::test_pin_remove": "be08940dab0f1045daebab70773e84b637def09a9e2ea1e889e709a167fd60c8",
+"T3W1_de_device_menu-test_pin.py::test_pin_setup": "860168d20a14418d610178416feff290273c0d7dd622875a0b50b9dd7a75b7b3",
+"T3W1_de_device_menu-test_pin.py::test_pin_uninitialized": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
"T3W1_de_device_menu-test_traverse_menu.py::test_traverse_initialized": "3b46e7026e35427f5fa6a6ac5c1db635524339e39282517be26f6517dbfa167a",
"T3W1_de_device_menu-test_traverse_menu.py::test_traverse_initialized_no_pin": "1b23eead0921890a4020371a75632a555177eea8c1ba2343141b75c723e06c25",
"T3W1_de_device_menu-test_traverse_menu.py::test_traverse_uninitialized": "0839769a66e0c7888a78c263ae2d413652be782d92c8f67f594897fdd3a4569f",
-"T3W1_de_device_menu-test_wipe.py::test_wipe": "c84827376be3c1e60f261a61a7835ea47af3124ed434a8bb2d286c732e5e869f",
+"T3W1_de_device_menu-test_wipe_code.py::test_remove_pin_without_removing_wipe_code": "18b8b0f016d2b81a3d01594a8d01d0319d1d6c940a8a574dc5cace8e80fbd903",
+"T3W1_de_device_menu-test_wipe_code.py::test_wipe_code_cancel_intro": "36d99ce41f4a7ec04c554aa6e8684a87ecfb45d84a95d10b0b522ba146b8d2bc",
+"T3W1_de_device_menu-test_wipe_code.py::test_wipe_code_cancel_keyboard": "b3e5d8ab7f2520defbcd936c4b1981005231ad0d2e62673097d3e9432f31c190",
+"T3W1_de_device_menu-test_wipe_code.py::test_wipe_code_change": "d5439b79e5c2915985eb3a141af8bdee1091479c78ca1be7bfbd0a291b8e29ba",
+"T3W1_de_device_menu-test_wipe_code.py::test_wipe_code_remove": "649420e87a8a0446f1b87c71ebd9f24f50b5abb23b6807c2e885fafc03940f2c",
+"T3W1_de_device_menu-test_wipe_code.py::test_wipe_code_same_as_pin": "79c67dc74f15e504f0476a3058291b16b316acde1450bd35017254bd886e1caa",
+"T3W1_de_device_menu-test_wipe_code.py::test_wipe_code_setup": "22d5589fec422988e3a7ed88b1e41d3333cca4bd141f95b080543eba128172df",
+"T3W1_de_device_menu-test_wipe_code.py::test_wipe_code_setup_pin_unset": "98735f888f827501b25c78c6a737c1983a800c620f709c65b1d95f6c8c9a2b90",
+"T3W1_de_device_menu-test_wipe_device.py::test_wipe": "c84827376be3c1e60f261a61a7835ea47af3124ed434a8bb2d286c732e5e869f",
"T3W1_de_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "5249c3c7d554f13ae6a1a1e812378727aa7fd355c7a58d858dde3043287b0281",
"T3W1_de_test_autolock.py::test_autolock_does_not_interrupt_signing": "d1b09532eda0b3c87a2564674189966e253a5511cfcf31da448dd47f2a132713",
"T3W1_de_test_autolock.py::test_autolock_interrupts_passphrase": "30304ff880f4fcdc7bb9988b61bfa6b1a41523f48e1e26845a926417046f4cbf",
@@ -29026,10 +29054,24 @@
"T3W1_en_device_menu-test_notifications.py::test_backup_needed": "f55c9030025edec1c3b08bfd444d2c77c232a7f7c22fda6f340bd602ef853a73",
"T3W1_en_device_menu-test_notifications.py::test_pin_not_set": "d216b5f6b6c266a1a5079ad6686696d04ff0f6fc478f69ed583b3aa47fa60e5c",
"T3W1_en_device_menu-test_notifications.py::test_seedless": "30782c88c129824c038c14d999b63b6c99f486333e1fa1dc4af8e76f9d42c3ec",
+"T3W1_en_device_menu-test_pin.py::test_pin_cancel_intro": "c68ff0581cfbf28db5af833a2bef2e1e81fd916dea7decc436816407f4a78291",
+"T3W1_en_device_menu-test_pin.py::test_pin_cancel_keyboard": "992f655cbc937de156be83f62f6a2e304c8a55284b30a230a869ade0cb706b8c",
+"T3W1_en_device_menu-test_pin.py::test_pin_change": "ac682a9667dcc3025b2dccc131590a04081cbdab8148d09f211e99751892f203",
+"T3W1_en_device_menu-test_pin.py::test_pin_remove": "6a18af66a90770c7e6e68793abf3e136accc1ee80f7ccf7ee442941807842d9d",
+"T3W1_en_device_menu-test_pin.py::test_pin_setup": "8cf3aac2d5f5f3b99b4e45dc2b5d99d100982b8a5bcd8b377e68a7c708a703b0",
+"T3W1_en_device_menu-test_pin.py::test_pin_uninitialized": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
"T3W1_en_device_menu-test_traverse_menu.py::test_traverse_initialized": "750a5628d1d608271b43f63834a28c0dd98cc9f979030a94ee5d830bb970cdb9",
"T3W1_en_device_menu-test_traverse_menu.py::test_traverse_initialized_no_pin": "8db314448adfce04958bf61d970ece995403c72b410d9c0e651084af8a239837",
"T3W1_en_device_menu-test_traverse_menu.py::test_traverse_uninitialized": "a7e3d2f7ea4286114dc6f0006f24a05360fdc7bee217e123d67520b02c32e986",
-"T3W1_en_device_menu-test_wipe.py::test_wipe": "65c0d8f5ac88dd693907dee1348d2b451228f8e2bacb24b169cad3f225831901",
+"T3W1_en_device_menu-test_wipe_code.py::test_remove_pin_without_removing_wipe_code": "cf9853bd74dea6e26223010f98cfcb4a24db978e74e9e071e1c0c1d2f801e6ac",
+"T3W1_en_device_menu-test_wipe_code.py::test_wipe_code_cancel_intro": "6cdb2d89851bccf778a3c11e9be729937af4e0baf49fc1c162af9aea8a87bf9d",
+"T3W1_en_device_menu-test_wipe_code.py::test_wipe_code_cancel_keyboard": "fc1b450581e48d19b33cc9b186a30e80eac65739b2e7d1fd7d1741a59154e5a1",
+"T3W1_en_device_menu-test_wipe_code.py::test_wipe_code_change": "fa6dfc7b0295433bbc9ba8a1a861c078af0f8a9b30594a586f3fb2828341dfe8",
+"T3W1_en_device_menu-test_wipe_code.py::test_wipe_code_remove": "2e009bc91759662d0992a0ed3aff40901054ac8747c64f24b011bbc39cec4e96",
+"T3W1_en_device_menu-test_wipe_code.py::test_wipe_code_same_as_pin": "0a46ac112005db9ef29f50a227e0b55a3321f5cf3db02ceb4c8aa5a1efae43c6",
+"T3W1_en_device_menu-test_wipe_code.py::test_wipe_code_setup": "527b9802c6ae56a5c626d073222f9c365768d6ebae09f9e29c06fd3545fc0fed",
+"T3W1_en_device_menu-test_wipe_code.py::test_wipe_code_setup_pin_unset": "931d9afceb0ba1e4faae891775819277242d889644d5c0c5863fc8c9fcf859b1",
+"T3W1_en_device_menu-test_wipe_device.py::test_wipe": "65c0d8f5ac88dd693907dee1348d2b451228f8e2bacb24b169cad3f225831901",
"T3W1_en_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "9dc06089d9e4aab916262d0e796581be4014dc80e890cc35986765f19da7eaff",
"T3W1_en_test_autolock.py::test_autolock_does_not_interrupt_signing": "22ba2f492900a9cc04c9eea2c4f7593ee6446732c69df0f1b72bc6202c3c0fb8",
"T3W1_en_test_autolock.py::test_autolock_interrupts_passphrase": "1c211d78fbc0f0a42268c3603e4770f658a62275f87aacb0d5ff26ddb82be830",
@@ -29090,12 +29132,12 @@
"T3W1_en_test_tutorial_eckhart.py::test_tutorial_menu_close": "99cb0bed4bfdf7b67142b093c9b827a8fbf103c511efab534846f3fafbc0a362",
"T3W1_en_test_tutorial_eckhart.py::test_tutorial_menu_tropic": "a6c907391f99f5517ffe0bad2f4234aef4e3bfc5fedb5152f151ca291d90c1cb",
"T3W1_en_test_tutorial_eckhart.py::test_tutorial_restart": "5b3eff72e15fb7221cb6c6b16a9ebacd53a502c788a941bc38b5f352934aec41",
-"T3W1_es_device_menu-test_auto_lock.py::test_auto_lock_battery_cancel": "29179a21fe8d98a741ea0585b1228dfbfe19ea50dd79278973d3d2e60db5e8e3",
-"T3W1_es_device_menu-test_auto_lock.py::test_auto_lock_battery_change": "db6dd3dc93a4ee23061778c468ebe0a8e1b423862d51c5861a2b5cf5bbe53959",
+"T3W1_es_device_menu-test_auto_lock.py::test_auto_lock_battery_cancel": "6ebab05e43b60d3f49e6d11d81727b5ba212f40405142d0e4652fb30353848c6",
+"T3W1_es_device_menu-test_auto_lock.py::test_auto_lock_battery_change": "822cd5e2f003b08e66b62247a01cf096d48461764433984807ff9042a79394d0",
"T3W1_es_device_menu-test_auto_lock.py::test_auto_lock_pin_not_set": "61a51a060aebe45e6b08670001917c67ee22da70843d90b6a2a7a160c52daada",
"T3W1_es_device_menu-test_auto_lock.py::test_auto_lock_uninitialized": "57bfeec20e3b76eff5ec3e4c51c625b0121dab5b409287d540dd3287f8f04f93",
-"T3W1_es_device_menu-test_auto_lock.py::test_auto_lock_usb_cancel": "81e999aad6e0e57010367cf6903545277d46e480139bb1c0a5f86a4958b8ba0a",
-"T3W1_es_device_menu-test_auto_lock.py::test_auto_lock_usb_change": "fe551007509c5b0021912574ee6d50724b3d8e4c6e073415d826970f46d83d8e",
+"T3W1_es_device_menu-test_auto_lock.py::test_auto_lock_usb_cancel": "2d08bed529c1b413f861588cd16c2b7d53ac76140c88addcb7fa6fcea9aebe5c",
+"T3W1_es_device_menu-test_auto_lock.py::test_auto_lock_usb_change": "66bd68c48728fa354f04719789a3a80d410a7714890e8837157577f07aa0f19f",
"T3W1_es_device_menu-test_check_backup.py::test_backup_check_cancel": "b3f6ffadaca8f775511561604baf7f20087bda3befd06cb935033312a0d342a1",
"T3W1_es_device_menu-test_check_backup.py::test_backup_needed_fails": "3bfddf8b7921ff08a6a9f36a1ef82bca928dca2948d71ca6506cca0aaf0d5455",
"T3W1_es_device_menu-test_check_backup.py::test_no_backup_fails": "b793498c5928d31ce35348ef10b92121136c03de9ad435c906ee8091b79aa553",
@@ -29114,12 +29156,26 @@
"T3W1_es_device_menu-test_label.py::test_label_uninitialized": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
"T3W1_es_device_menu-test_notifications.py::test_backup_failed": "badb51ff76c502ceb5897776edcb0786260a036f20d911dca7be0c67d0865530",
"T3W1_es_device_menu-test_notifications.py::test_backup_needed": "6aa38827cb41ae91cd198cc32c55daa19850442faf10587b751ef3f7ffb803bb",
-"T3W1_es_device_menu-test_notifications.py::test_pin_not_set": "961bb51c186666144ba3a890fab16d4bf1334f30ac3a8355c1dd8c66e996f8d0",
+"T3W1_es_device_menu-test_notifications.py::test_pin_not_set": "406b48a3f931b837eee81c89244bce74280a583e9247588aaa6a9595a6218a1e",
"T3W1_es_device_menu-test_notifications.py::test_seedless": "bbe2a9d5b69d39d082b3c798a613e150a3ba9ce31e4c3dcff38a4d169f699ce3",
-"T3W1_es_device_menu-test_traverse_menu.py::test_traverse_initialized": "21e758278da9273c4644a09d6acc660f912348c96e2790be29f83e51c3d6d640",
+"T3W1_es_device_menu-test_pin.py::test_pin_cancel_intro": "8d56570730d36e4532f51d34665f8ddce15f44c6268982040a9c1967ad3c796c",
+"T3W1_es_device_menu-test_pin.py::test_pin_cancel_keyboard": "e7d94450a091f1e672cb2aca19699d4e9cf3182b9ea936f13b97b10ee9114fc4",
+"T3W1_es_device_menu-test_pin.py::test_pin_change": "82646d1f14f1a4b54a7deb2179eb25152aff8aef1c430b55cf3abdf815594880",
+"T3W1_es_device_menu-test_pin.py::test_pin_remove": "72ab1dc582330142caf1b3c136b28f59b39d99dd08f67c08415dbdbc8d74cdf9",
+"T3W1_es_device_menu-test_pin.py::test_pin_setup": "63f5b7bd04c841106644bf2426c27356583943fc954f8f6a206668dec2349631",
+"T3W1_es_device_menu-test_pin.py::test_pin_uninitialized": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
+"T3W1_es_device_menu-test_traverse_menu.py::test_traverse_initialized": "31711dfab1e8fe5e3b05811ab5979900a0dc9a1e1c4206190eb7d2b26cf284d7",
"T3W1_es_device_menu-test_traverse_menu.py::test_traverse_initialized_no_pin": "bb61cef39e8dc5d93d2edeb55dc8bca68b80459eb990e8d3b8f65afcc125f7b4",
"T3W1_es_device_menu-test_traverse_menu.py::test_traverse_uninitialized": "d7d31ba029f8a9575cff8aab8caf5504088e5c7e66c098af6d6fa58d43fd6081",
-"T3W1_es_device_menu-test_wipe.py::test_wipe": "929b3703d0d5983e911dc75cfcc4a05e9d9a80b4e78c65c101c99f14502e92e3",
+"T3W1_es_device_menu-test_wipe_code.py::test_remove_pin_without_removing_wipe_code": "9b991f03a30849920dba59fcec1b2ac0b18459679cb2d3de7e741672ad10fccb",
+"T3W1_es_device_menu-test_wipe_code.py::test_wipe_code_cancel_intro": "b220c2381b4d5b6e7969a2b2e1580d9c69a40e9efcf1bb0b9dff7c2e29ade330",
+"T3W1_es_device_menu-test_wipe_code.py::test_wipe_code_cancel_keyboard": "a512597b6f2926127e8cc29a44b9bf06a5e7f73d03c0c20a13980f6906d2dbcd",
+"T3W1_es_device_menu-test_wipe_code.py::test_wipe_code_change": "a16730775ebee19e2062311eb59555054e017bcee850d7c90e54bc6964b79820",
+"T3W1_es_device_menu-test_wipe_code.py::test_wipe_code_remove": "ff809e12f8814a098853ecdac70c7d14e7329abd1b1cb62dd0269315d0d48a28",
+"T3W1_es_device_menu-test_wipe_code.py::test_wipe_code_same_as_pin": "1ae25141a87f4ca14ad8cb948fbb868b0ad625cfcb2c3cdc6ce2ec22d3b6c19d",
+"T3W1_es_device_menu-test_wipe_code.py::test_wipe_code_setup": "00fac5994d4645c813c098fd0e38bf78a24d92d0e69f94543a81fb2fe2c39e23",
+"T3W1_es_device_menu-test_wipe_code.py::test_wipe_code_setup_pin_unset": "56536ae9cd7c4ff8022def4ec3350031d3614064d961f53a2850f2be425af201",
+"T3W1_es_device_menu-test_wipe_device.py::test_wipe": "929b3703d0d5983e911dc75cfcc4a05e9d9a80b4e78c65c101c99f14502e92e3",
"T3W1_es_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "4eaaa078d6cbcd5340ec55454da8e2cc2213f8f9b450199f26ad925e70ff0dc3",
"T3W1_es_test_autolock.py::test_autolock_does_not_interrupt_signing": "f98a2c4675a3f8f7c8834fd87d7d407cd9433b872ba4d78f6bcc1b2fc086400b",
"T3W1_es_test_autolock.py::test_autolock_interrupts_passphrase": "850d4dd25b80a79a1a93409f213a8427e4eb07eafcff61026c963eeb6aecf5cd",
@@ -29206,10 +29262,24 @@
"T3W1_fr_device_menu-test_notifications.py::test_backup_needed": "55f2d653365928a4598ac9f4cb769dc4d72558d50011f5e27ecfa11ec1ef763e",
"T3W1_fr_device_menu-test_notifications.py::test_pin_not_set": "d0a0d1c905d165578252aa4abb7e52c4d2bf669ae6ecf8188757a58c611129d8",
"T3W1_fr_device_menu-test_notifications.py::test_seedless": "e6dd2332fbc9992e5418d109ba6d3b9089712500dc0ba9cb4a33fcf3d91ba552",
+"T3W1_fr_device_menu-test_pin.py::test_pin_cancel_intro": "98d6b0bc56a286ae60c96217c2840fe8a735db4a8b112c9e43e88a1c88b83a2e",
+"T3W1_fr_device_menu-test_pin.py::test_pin_cancel_keyboard": "af4b4745ca5fc4f29a6c9e254f035f3b7ecc6ff9e2cfc7663a4a8d3b6c607d67",
+"T3W1_fr_device_menu-test_pin.py::test_pin_change": "ff6d41d78bdac3541de10f798e17659933470820550aefa4671e78b3275367d9",
+"T3W1_fr_device_menu-test_pin.py::test_pin_remove": "e18ba94c4ba695ed602347dda720abf201c679dcfd7434af02609d0d4c799bdb",
+"T3W1_fr_device_menu-test_pin.py::test_pin_setup": "0742a20dfcc0710b07c4ac4d91e97bcad6713b46930b8ec5fe1e3ffeb81bc7de",
+"T3W1_fr_device_menu-test_pin.py::test_pin_uninitialized": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
"T3W1_fr_device_menu-test_traverse_menu.py::test_traverse_initialized": "4d27629610918103d9cde19415dea5d71897e92295b20a680ea036400e1177ce",
"T3W1_fr_device_menu-test_traverse_menu.py::test_traverse_initialized_no_pin": "12079b10041fd70849a86902bfa6babff5a38b8681f590b84012cecf5381b7bc",
"T3W1_fr_device_menu-test_traverse_menu.py::test_traverse_uninitialized": "ffa7def66488b3d02b9d8bfecd609236e1274c8a206e6be783579131e890a1f7",
-"T3W1_fr_device_menu-test_wipe.py::test_wipe": "0c909881037c5a50a6a67c13243227a18250f1536cfb17a11e4258d87714348c",
+"T3W1_fr_device_menu-test_wipe_code.py::test_remove_pin_without_removing_wipe_code": "f3ae9cc8d2e72582b89511391b49a27184ab0cc5dddc0a2f109d81549709ccb6",
+"T3W1_fr_device_menu-test_wipe_code.py::test_wipe_code_cancel_intro": "40a94c47293bac123986b1b20f5253a931353c1b91c60d6dffeb66b1aafc65cb",
+"T3W1_fr_device_menu-test_wipe_code.py::test_wipe_code_cancel_keyboard": "4d63ab9c1e942ec56a4a612b1e2d5c5d92c41e30b2a88710c635b2402f206d0b",
+"T3W1_fr_device_menu-test_wipe_code.py::test_wipe_code_change": "09c28702e5cc16267835f5c3282f350052bf277a995c204c3c09e95affbfa638",
+"T3W1_fr_device_menu-test_wipe_code.py::test_wipe_code_remove": "4f66d1924c1f63ae60543c6af539240db4e4b859aa80f6d81948a22251a7364b",
+"T3W1_fr_device_menu-test_wipe_code.py::test_wipe_code_same_as_pin": "344c7d38e11b8fdc04115ec29df4f4151f7e37b3183da29cdcb70a2ada361734",
+"T3W1_fr_device_menu-test_wipe_code.py::test_wipe_code_setup": "531c862bf4d237c03eb607bafbd9d8996683bd2ccdfabe438478036103c53afc",
+"T3W1_fr_device_menu-test_wipe_code.py::test_wipe_code_setup_pin_unset": "e8156cf4eda1d29060f05b4a18d50032b0b344230609b7de7cababfe0a86b20b",
+"T3W1_fr_device_menu-test_wipe_device.py::test_wipe": "0c909881037c5a50a6a67c13243227a18250f1536cfb17a11e4258d87714348c",
"T3W1_fr_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "ba55d3c99dc9cbc8712098927ec01b7d846c1481722d8dabe92271d24a1bec62",
"T3W1_fr_test_autolock.py::test_autolock_does_not_interrupt_signing": "e859c8d90853f4b8d267b6aa40559a574fd6ba3f3636bef08d59dc6f11a49a89",
"T3W1_fr_test_autolock.py::test_autolock_interrupts_passphrase": "40f4ab3a116fcdce69f128faadf325b0d947e4d37ac4dadf04c62c0a9eb4c497",
@@ -29296,10 +29366,24 @@
"T3W1_pt_device_menu-test_notifications.py::test_backup_needed": "29bc804809d98b87ecb26b473ae96585859e4f85cc9704c346531a312e27ef3b",
"T3W1_pt_device_menu-test_notifications.py::test_pin_not_set": "eea3f3d8021cf53499cd2fb1d1a568e74d5021ca9604b9d0acfc817177ce2009",
"T3W1_pt_device_menu-test_notifications.py::test_seedless": "27b59acec95fd7a89dbd4b4b85c45f4e21313c568c84c1de0a984eed084506ad",
+"T3W1_pt_device_menu-test_pin.py::test_pin_cancel_intro": "913a36d93fa8fdb1016c33dbbef23ef282751c9dd449fefbf2c4ed01741d2488",
+"T3W1_pt_device_menu-test_pin.py::test_pin_cancel_keyboard": "5a93f40bc777142ea3eb6a3ad1168adee213de490499d7d3573f14a4c58b8cef",
+"T3W1_pt_device_menu-test_pin.py::test_pin_change": "6145cfa2e9479496a7936b8c6e32a97cb76a20bb24ba2f743de8ec1d58f066f6",
+"T3W1_pt_device_menu-test_pin.py::test_pin_remove": "a31d7e6cd2f749dc9ff4a4daac581f01cfbba511378fc876229e373390988a81",
+"T3W1_pt_device_menu-test_pin.py::test_pin_setup": "60f06a31ea272e674d1bd400581a50cb1057accc5d474ecfc872f20781230043",
+"T3W1_pt_device_menu-test_pin.py::test_pin_uninitialized": "987d1c62e6576b9cc24373e00df522efdc9736af978d6032f7d319af8daaef5d",
"T3W1_pt_device_menu-test_traverse_menu.py::test_traverse_initialized": "9e95a65e8e39fe0227a0d85a5ab97f71e2015faaf55433537ff2c32296ad0290",
"T3W1_pt_device_menu-test_traverse_menu.py::test_traverse_initialized_no_pin": "6a33d0010d199fbb811738ce81e38da3618a5534f994d902cbd6cd4d5697fdf4",
"T3W1_pt_device_menu-test_traverse_menu.py::test_traverse_uninitialized": "cc52e67c94e6181f8f7bcbcb6c62a459d8671dec477df5cf27b662c6e6e2e49c",
-"T3W1_pt_device_menu-test_wipe.py::test_wipe": "0f0f6c2b372ec2fee36d4bf8f142e151191274a81d50a115a1f962a2fb857dbc",
+"T3W1_pt_device_menu-test_wipe_code.py::test_remove_pin_without_removing_wipe_code": "4f95e8e147cebed5ac2e9c5d65fb73c77e28832013d565ed69a860cd3d93db33",
+"T3W1_pt_device_menu-test_wipe_code.py::test_wipe_code_cancel_intro": "fec5aa7188c1903027f121387217f20877378dfb1ab629d8c2a98d4ccf494c4c",
+"T3W1_pt_device_menu-test_wipe_code.py::test_wipe_code_cancel_keyboard": "d379555eb4f9ed17ed54f646a57df22d06b225315b0544ce20adf0908191a0a6",
+"T3W1_pt_device_menu-test_wipe_code.py::test_wipe_code_change": "2d55f487d3b8388d43bbfe4f330f3af9d1440e3ae8aa5596ef197e55b076ca93",
+"T3W1_pt_device_menu-test_wipe_code.py::test_wipe_code_remove": "fb1f3c7bdbceecf504b3624e8ab96f725975ae8b8a262d485458b6e4a3eb194c",
+"T3W1_pt_device_menu-test_wipe_code.py::test_wipe_code_same_as_pin": "c2a57cd58873841f65629f45ec7c7986c620e65b47b7b35cabd570e02fea8e64",
+"T3W1_pt_device_menu-test_wipe_code.py::test_wipe_code_setup": "6566532cc0a5735f6332b149f4cac52d73eaea43e68a7f54c261b0d0a0c7a4ec",
+"T3W1_pt_device_menu-test_wipe_code.py::test_wipe_code_setup_pin_unset": "1c74667c078e25e7e0d37c7b2aa35f7c8ab02cd88e74695ecb355c82a293b0cc",
+"T3W1_pt_device_menu-test_wipe_device.py::test_wipe": "0f0f6c2b372ec2fee36d4bf8f142e151191274a81d50a115a1f962a2fb857dbc",
"T3W1_pt_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "a230dd8129112b2f3a72590c6119fe7a09499dff79419f83d2db4d396c274660",
"T3W1_pt_test_autolock.py::test_autolock_does_not_interrupt_signing": "36e7e6ec29962b9d04ddcfe4177b77385ea25d8cbac480fee61db4bcf919edb4",
"T3W1_pt_test_autolock.py::test_autolock_interrupts_passphrase": "888d34039054eae88ddaa3cc3b1dea88fd0c87b37e8e812f2adbc1e5906c9ce2",
@@ -35237,7 +35321,7 @@
"T3W1_es_test_msg_backup_device.py::test_interrupt_backup_fails": "30e0aa0d1c7836a22f490f6cde99e7899438c75e4c726c4ef72ddc9d6e93c410",
"T3W1_es_test_msg_backup_device.py::test_no_backup_fails": "b793498c5928d31ce35348ef10b92121136c03de9ad435c906ee8091b79aa553",
"T3W1_es_test_msg_change_wipe_code_t2.py::test_set_remove_pin_without_removing_wipe_code": "1df7f0ee253035a48cba677cf8c073bfec19aa677b4bd775c14a3e016bc3e2d6",
-"T3W1_es_test_msg_change_wipe_code_t2.py::test_set_remove_wipe_code": "f402b9eefbfc0ca9327cf7c6395869e0bfdb236a95734e0396a6a55ed831dbd3",
+"T3W1_es_test_msg_change_wipe_code_t2.py::test_set_remove_wipe_code": "51380d910980c157c0e37a51807239e0c493e747a5f19462f0299145d0ec6ead",
"T3W1_es_test_msg_change_wipe_code_t2.py::test_set_wipe_code_cancel": "483aeb1ab1b630c674f6eabcb1962977b5d475bb84ee00bd79d2bc0383d1ef1e",
"T3W1_es_test_msg_change_wipe_code_t2.py::test_set_wipe_code_mismatch": "a4bb9c331802b3bfc78d1fb0907b33c08711b74631d032bc65f32c13845ebdc9",
"T3W1_es_test_msg_change_wipe_code_t2.py::test_set_wipe_code_to_pin": "9f817a0fa863ad10d319b47a069289ccb0ef4b9c4f23c6ad7e74d4191c263db6",
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.