mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-14 16:42:02 +00:00
test(core): don't fetch full DebugLinkState
by default
In case the main workflow is restarting after a `DebugLinkDecision`, sending the response of `DebugLinkGetState` may get interrupted. We are making the state fetching explicit, in order to avoid the "restart" race condition (as described in #4401). Following the above change, text-based layout recording is removed. [no changelog]
This commit is contained in:
parent
85bbc89eed
commit
061e71213e
@ -110,6 +110,8 @@ message DebugLinkGetState {
|
|||||||
// trezor-core only - wait until current layout changes
|
// trezor-core only - wait until current layout changes
|
||||||
// changed in 2.6.4: multiple wait types instead of true/false.
|
// changed in 2.6.4: multiple wait types instead of true/false.
|
||||||
optional DebugWaitType wait_layout = 3 [default=IMMEDIATE];
|
optional DebugWaitType wait_layout = 3 [default=IMMEDIATE];
|
||||||
|
// Responds immediately with an empty `DebugLinkState` (used for client-side synchronization).
|
||||||
|
optional bool return_empty_state = 4 [default=false];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -204,12 +204,12 @@ test_emu_persistence_ui: ## run persistence tests with UI testing
|
|||||||
|
|
||||||
test_emu_ui: ## run ui integration tests
|
test_emu_ui: ## run ui integration tests
|
||||||
$(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests $(TESTOPTS) \
|
$(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests $(TESTOPTS) \
|
||||||
--ui=test --ui-check-missing --record-text-layout --do-master-diff \
|
--ui=test --ui-check-missing --do-master-diff \
|
||||||
--lang=$(TEST_LANG)
|
--lang=$(TEST_LANG)
|
||||||
|
|
||||||
test_emu_ui_multicore: ## run ui integration tests using multiple cores
|
test_emu_ui_multicore: ## run ui integration tests using multiple cores
|
||||||
$(PYTEST) -n $(MULTICORE) $(TESTPATH)/device_tests $(TESTOPTS) --timeout $(PYTEST_TIMEOUT) \
|
$(PYTEST) -n $(MULTICORE) $(TESTPATH)/device_tests $(TESTOPTS) --timeout $(PYTEST_TIMEOUT) \
|
||||||
--ui=test --ui-check-missing --record-text-layout --do-master-diff \
|
--ui=test --ui-check-missing --do-master-diff \
|
||||||
--control-emulators --model=core --random-order-seed=$(RANDOM) \
|
--control-emulators --model=core --random-order-seed=$(RANDOM) \
|
||||||
--lang=$(TEST_LANG)
|
--lang=$(TEST_LANG)
|
||||||
|
|
||||||
|
@ -268,6 +268,11 @@ if __debug__:
|
|||||||
async def dispatch_DebugLinkGetState(
|
async def dispatch_DebugLinkGetState(
|
||||||
msg: DebugLinkGetState,
|
msg: DebugLinkGetState,
|
||||||
) -> DebugLinkState | None:
|
) -> DebugLinkState | None:
|
||||||
|
if msg.return_empty_state:
|
||||||
|
from trezor.messages import DebugLinkState
|
||||||
|
|
||||||
|
return DebugLinkState()
|
||||||
|
|
||||||
if msg.wait_layout == DebugWaitType.IMMEDIATE:
|
if msg.wait_layout == DebugWaitType.IMMEDIATE:
|
||||||
return _state()
|
return _state()
|
||||||
|
|
||||||
|
2
core/src/trezor/messages.py
generated
2
core/src/trezor/messages.py
generated
@ -2898,11 +2898,13 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
class DebugLinkGetState(protobuf.MessageType):
|
class DebugLinkGetState(protobuf.MessageType):
|
||||||
wait_layout: "DebugWaitType"
|
wait_layout: "DebugWaitType"
|
||||||
|
return_empty_state: "bool"
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
wait_layout: "DebugWaitType | None" = None,
|
wait_layout: "DebugWaitType | None" = None,
|
||||||
|
return_empty_state: "bool | None" = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -64,8 +64,7 @@ if TYPE_CHECKING:
|
|||||||
def __call__(
|
def __call__(
|
||||||
self,
|
self,
|
||||||
hold_ms: int | None = None,
|
hold_ms: int | None = None,
|
||||||
wait: bool | None = None,
|
) -> "None": ...
|
||||||
) -> "LayoutContent": ...
|
|
||||||
|
|
||||||
InputFlowType = Generator[None, messages.ButtonRequest, None]
|
InputFlowType = Generator[None, messages.ButtonRequest, None]
|
||||||
|
|
||||||
@ -416,11 +415,10 @@ def _make_input_func(
|
|||||||
def input_func(
|
def input_func(
|
||||||
self: "DebugLink",
|
self: "DebugLink",
|
||||||
hold_ms: int | None = None,
|
hold_ms: int | None = None,
|
||||||
wait: bool | None = None,
|
) -> None:
|
||||||
) -> LayoutContent:
|
|
||||||
__tracebackhide__ = True # for pytest # pylint: disable=W0612
|
__tracebackhide__ = True # for pytest # pylint: disable=W0612
|
||||||
decision.hold_ms = hold_ms
|
decision.hold_ms = hold_ms
|
||||||
return self._decision(decision, wait=wait)
|
self._decision(decision)
|
||||||
|
|
||||||
return input_func # type: ignore [Parameter name mismatch]
|
return input_func # type: ignore [Parameter name mismatch]
|
||||||
|
|
||||||
@ -442,12 +440,7 @@ class DebugLink:
|
|||||||
self.t1_screenshot_directory: Path | None = None
|
self.t1_screenshot_directory: Path | None = None
|
||||||
self.t1_screenshot_counter = 0
|
self.t1_screenshot_counter = 0
|
||||||
|
|
||||||
# Optional file for saving text representation of the screen
|
|
||||||
self.screen_text_file: Path | None = None
|
|
||||||
self.last_screen_content = ""
|
|
||||||
|
|
||||||
self.waiting_for_layout_change = False
|
self.waiting_for_layout_change = False
|
||||||
self.layout_dirty = True
|
|
||||||
|
|
||||||
self.input_wait_type = DebugWaitType.IMMEDIATE
|
self.input_wait_type = DebugWaitType.IMMEDIATE
|
||||||
|
|
||||||
@ -477,11 +470,6 @@ class DebugLink:
|
|||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
return LayoutType.from_model(self.model)
|
return LayoutType.from_model(self.model)
|
||||||
|
|
||||||
def set_screen_text_file(self, file_path: Path | None) -> None:
|
|
||||||
if file_path is not None:
|
|
||||||
file_path.write_bytes(b"")
|
|
||||||
self.screen_text_file = file_path
|
|
||||||
|
|
||||||
def open(self) -> None:
|
def open(self) -> None:
|
||||||
self.transport.begin_session()
|
self.transport.begin_session()
|
||||||
|
|
||||||
@ -543,8 +531,19 @@ class DebugLink:
|
|||||||
raise TrezorFailure(result)
|
raise TrezorFailure(result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def read_layout(self) -> LayoutContent:
|
def read_layout(self, wait: bool | None = None) -> LayoutContent:
|
||||||
return LayoutContent(self.state().tokens)
|
"""
|
||||||
|
Force waiting for the layout by setting `wait=True`. Force not waiting by
|
||||||
|
setting `wait=False` -- useful when, e.g., you are causing the next layout to be
|
||||||
|
deliberately delayed.
|
||||||
|
"""
|
||||||
|
if wait is True:
|
||||||
|
wait_type = DebugWaitType.CURRENT_LAYOUT
|
||||||
|
elif wait is False:
|
||||||
|
wait_type = DebugWaitType.IMMEDIATE
|
||||||
|
else:
|
||||||
|
wait_type = None
|
||||||
|
return LayoutContent(self.state(wait_type=wait_type).tokens)
|
||||||
|
|
||||||
def wait_layout(self, wait_for_external_change: bool = False) -> LayoutContent:
|
def wait_layout(self, wait_for_external_change: bool = False) -> LayoutContent:
|
||||||
# Next layout change will be caused by external event
|
# Next layout change will be caused by external event
|
||||||
@ -558,18 +557,12 @@ class DebugLink:
|
|||||||
obj = self._call(
|
obj = self._call(
|
||||||
messages.DebugLinkGetState(wait_layout=DebugWaitType.NEXT_LAYOUT)
|
messages.DebugLinkGetState(wait_layout=DebugWaitType.NEXT_LAYOUT)
|
||||||
)
|
)
|
||||||
self.layout_dirty = True
|
|
||||||
if isinstance(obj, messages.Failure):
|
if isinstance(obj, messages.Failure):
|
||||||
raise TrezorFailure(obj)
|
raise TrezorFailure(obj)
|
||||||
return LayoutContent(obj.tokens)
|
return LayoutContent(obj.tokens)
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def wait_for_layout_change(self) -> Iterator[LayoutContent]:
|
def wait_for_layout_change(self) -> Iterator[None]:
|
||||||
# set up a dummy layout content object to be yielded
|
|
||||||
layout_content = LayoutContent(
|
|
||||||
["DUMMY CONTENT, WAIT UNTIL THE END OF THE BLOCK :("]
|
|
||||||
)
|
|
||||||
|
|
||||||
# make sure some current layout is up by issuing a dummy GetState
|
# make sure some current layout is up by issuing a dummy GetState
|
||||||
self.state()
|
self.state()
|
||||||
|
|
||||||
@ -579,18 +572,14 @@ class DebugLink:
|
|||||||
# allow the block to proceed
|
# allow the block to proceed
|
||||||
self.waiting_for_layout_change = True
|
self.waiting_for_layout_change = True
|
||||||
try:
|
try:
|
||||||
yield layout_content
|
yield
|
||||||
finally:
|
finally:
|
||||||
self.waiting_for_layout_change = False
|
self.waiting_for_layout_change = False
|
||||||
self.layout_dirty = True
|
|
||||||
|
|
||||||
# wait for the reply
|
# wait for the reply
|
||||||
resp = self._read()
|
resp = self._read()
|
||||||
assert isinstance(resp, messages.DebugLinkState)
|
assert isinstance(resp, messages.DebugLinkState)
|
||||||
|
|
||||||
# replace contents of the yielded object with the new thing
|
|
||||||
layout_content.__init__(resp.tokens)
|
|
||||||
|
|
||||||
def reset_debug_events(self) -> None:
|
def reset_debug_events(self) -> None:
|
||||||
# Only supported on TT and above certain version
|
# Only supported on TT and above certain version
|
||||||
if (self.model is not models.T1B1) and not self.legacy_debug:
|
if (self.model is not models.T1B1) and not self.legacy_debug:
|
||||||
@ -634,44 +623,24 @@ class DebugLink:
|
|||||||
state = self._call(messages.DebugLinkGetState(wait_word_list=True))
|
state = self._call(messages.DebugLinkGetState(wait_word_list=True))
|
||||||
return state.reset_word
|
return state.reset_word
|
||||||
|
|
||||||
def _decision(
|
def _decision(self, decision: messages.DebugLinkDecision) -> None:
|
||||||
self, decision: messages.DebugLinkDecision, wait: bool | None = None
|
"""Send a debuglink decision.
|
||||||
) -> LayoutContent:
|
|
||||||
"""Send a debuglink decision and returns the resulting layout.
|
|
||||||
|
|
||||||
If hold_ms is set, an additional 200ms is added to account for processing
|
If hold_ms is set, an additional 200ms is added to account for processing
|
||||||
delays. (This is needed for hold-to-confirm to trigger reliably.)
|
delays. (This is needed for hold-to-confirm to trigger reliably.)
|
||||||
|
|
||||||
If `wait` is unset, the following wait mode is used:
|
|
||||||
|
|
||||||
- `IMMEDIATE`, when in normal tests, which never deadlocks the device, but may
|
|
||||||
return an empty layout in case the next one didn't come up immediately. (E.g.,
|
|
||||||
in SignTx flow, the device is waiting for more TxRequest/TxAck exchanges
|
|
||||||
before showing the next UI layout.)
|
|
||||||
- `CURRENT_LAYOUT`, when in tests running through a `DeviceHandler`. This mode
|
|
||||||
returns the current layout or waits for some layout to come up if there is
|
|
||||||
none at the moment. The assumption is that wirelink is communicating on
|
|
||||||
another thread and won't be blocked by waiting on debuglink.
|
|
||||||
|
|
||||||
Force waiting for the layout by setting `wait=True`. Force not waiting by
|
|
||||||
setting `wait=False` -- useful when, e.g., you are causing the next layout to be
|
|
||||||
deliberately delayed.
|
|
||||||
"""
|
"""
|
||||||
if not self.allow_interactions:
|
if not self.allow_interactions:
|
||||||
return self.wait_layout()
|
self.wait_layout()
|
||||||
|
return
|
||||||
|
|
||||||
if decision.hold_ms is not None:
|
if decision.hold_ms is not None:
|
||||||
decision.hold_ms += 200
|
decision.hold_ms += 200
|
||||||
|
|
||||||
self._write(decision)
|
self._write(decision)
|
||||||
self.layout_dirty = True
|
if self.model is models.T1B1:
|
||||||
if wait is True:
|
return
|
||||||
wait_type = DebugWaitType.CURRENT_LAYOUT
|
# When the call below returns, we know that `decision` has been processed in Core.
|
||||||
elif wait is False:
|
self._call(messages.DebugLinkGetState(return_empty_state=True))
|
||||||
wait_type = DebugWaitType.IMMEDIATE
|
|
||||||
else:
|
|
||||||
wait_type = self.input_wait_type
|
|
||||||
return self._snapshot_core(wait_type)
|
|
||||||
|
|
||||||
press_yes = _make_input_func(button=messages.DebugButton.YES)
|
press_yes = _make_input_func(button=messages.DebugButton.YES)
|
||||||
"""Confirm current layout. See `_decision` for more details."""
|
"""Confirm current layout. See `_decision` for more details."""
|
||||||
@ -698,58 +667,14 @@ class DebugLink:
|
|||||||
)
|
)
|
||||||
"""Press right button. See `_decision` for more details."""
|
"""Press right button. See `_decision` for more details."""
|
||||||
|
|
||||||
def input(self, word: str, wait: bool | None = None) -> LayoutContent:
|
def input(self, word: str) -> None:
|
||||||
"""Send text input to the device. See `_decision` for more details."""
|
"""Send text input to the device. See `_decision` for more details."""
|
||||||
return self._decision(messages.DebugLinkDecision(input=word), wait)
|
self._decision(messages.DebugLinkDecision(input=word))
|
||||||
|
|
||||||
def click(
|
def click(self, click: Tuple[int, int], hold_ms: int | None = None) -> None:
|
||||||
self,
|
|
||||||
click: Tuple[int, int],
|
|
||||||
hold_ms: int | None = None,
|
|
||||||
wait: bool | None = None,
|
|
||||||
) -> LayoutContent:
|
|
||||||
"""Send a click to the device. See `_decision` for more details."""
|
"""Send a click to the device. See `_decision` for more details."""
|
||||||
x, y = click
|
x, y = click
|
||||||
return self._decision(
|
self._decision(messages.DebugLinkDecision(x=x, y=y, hold_ms=hold_ms))
|
||||||
messages.DebugLinkDecision(x=x, y=y, hold_ms=hold_ms), wait
|
|
||||||
)
|
|
||||||
|
|
||||||
def _snapshot_core(
|
|
||||||
self, wait_type: DebugWaitType = DebugWaitType.IMMEDIATE
|
|
||||||
) -> LayoutContent:
|
|
||||||
"""Save text and image content of the screen to relevant directories."""
|
|
||||||
# skip the snapshot if we are on T1
|
|
||||||
if self.model is models.T1B1:
|
|
||||||
return LayoutContent([])
|
|
||||||
|
|
||||||
# take the snapshot
|
|
||||||
state = self.state(wait_type)
|
|
||||||
layout = LayoutContent(state.tokens)
|
|
||||||
|
|
||||||
if state.tokens and self.layout_dirty:
|
|
||||||
# save it, unless we already did or unless it's empty
|
|
||||||
self.save_debug_screen(layout.visible_screen())
|
|
||||||
self.layout_dirty = False
|
|
||||||
|
|
||||||
# return the layout
|
|
||||||
return layout
|
|
||||||
|
|
||||||
def save_debug_screen(self, screen_content: str) -> None:
|
|
||||||
if self.screen_text_file is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not self.screen_text_file.exists():
|
|
||||||
self.screen_text_file.write_bytes(b"")
|
|
||||||
|
|
||||||
# Not writing the same screen twice
|
|
||||||
if screen_content == self.last_screen_content:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.last_screen_content = screen_content
|
|
||||||
|
|
||||||
with open(self.screen_text_file, "a") as f:
|
|
||||||
f.write(screen_content)
|
|
||||||
f.write("\n" + 80 * "/" + "\n")
|
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
self._write(messages.DebugLinkStop())
|
self._write(messages.DebugLinkStop())
|
||||||
@ -882,7 +807,8 @@ class DebugUI:
|
|||||||
# Paginating (going as further as possible) and pressing Yes
|
# Paginating (going as further as possible) and pressing Yes
|
||||||
if br.pages is not None:
|
if br.pages is not None:
|
||||||
for _ in range(br.pages - 1):
|
for _ in range(br.pages - 1):
|
||||||
self.debuglink.swipe_up(wait=True)
|
self.debuglink.swipe_up()
|
||||||
|
|
||||||
if self.debuglink.model is models.T3T1:
|
if self.debuglink.model is models.T3T1:
|
||||||
layout = self.debuglink.read_layout()
|
layout = self.debuglink.read_layout()
|
||||||
if "PromptScreen" in layout.all_components():
|
if "PromptScreen" in layout.all_components():
|
||||||
|
3
python/src/trezorlib/messages.py
generated
3
python/src/trezorlib/messages.py
generated
@ -4136,6 +4136,7 @@ class DebugLinkGetState(protobuf.MessageType):
|
|||||||
1: protobuf.Field("wait_word_list", "bool", repeated=False, required=False, default=None),
|
1: protobuf.Field("wait_word_list", "bool", repeated=False, required=False, default=None),
|
||||||
2: protobuf.Field("wait_word_pos", "bool", repeated=False, required=False, default=None),
|
2: protobuf.Field("wait_word_pos", "bool", repeated=False, required=False, default=None),
|
||||||
3: protobuf.Field("wait_layout", "DebugWaitType", repeated=False, required=False, default=DebugWaitType.IMMEDIATE),
|
3: protobuf.Field("wait_layout", "DebugWaitType", repeated=False, required=False, default=DebugWaitType.IMMEDIATE),
|
||||||
|
4: protobuf.Field("return_empty_state", "bool", repeated=False, required=False, default=False),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -4144,10 +4145,12 @@ class DebugLinkGetState(protobuf.MessageType):
|
|||||||
wait_word_list: Optional["bool"] = None,
|
wait_word_list: Optional["bool"] = None,
|
||||||
wait_word_pos: Optional["bool"] = None,
|
wait_word_pos: Optional["bool"] = None,
|
||||||
wait_layout: Optional["DebugWaitType"] = DebugWaitType.IMMEDIATE,
|
wait_layout: Optional["DebugWaitType"] = DebugWaitType.IMMEDIATE,
|
||||||
|
return_empty_state: Optional["bool"] = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.wait_word_list = wait_word_list
|
self.wait_word_list = wait_word_list
|
||||||
self.wait_word_pos = wait_word_pos
|
self.wait_word_pos = wait_word_pos
|
||||||
self.wait_layout = wait_layout
|
self.wait_layout = wait_layout
|
||||||
|
self.return_empty_state = return_empty_state
|
||||||
|
|
||||||
|
|
||||||
class DebugLinkState(protobuf.MessageType):
|
class DebugLinkState(protobuf.MessageType):
|
||||||
|
@ -1128,6 +1128,8 @@ pub struct DebugLinkGetState {
|
|||||||
pub wait_word_pos: ::std::option::Option<bool>,
|
pub wait_word_pos: ::std::option::Option<bool>,
|
||||||
// @@protoc_insertion_point(field:hw.trezor.messages.debug.DebugLinkGetState.wait_layout)
|
// @@protoc_insertion_point(field:hw.trezor.messages.debug.DebugLinkGetState.wait_layout)
|
||||||
pub wait_layout: ::std::option::Option<::protobuf::EnumOrUnknown<debug_link_get_state::DebugWaitType>>,
|
pub wait_layout: ::std::option::Option<::protobuf::EnumOrUnknown<debug_link_get_state::DebugWaitType>>,
|
||||||
|
// @@protoc_insertion_point(field:hw.trezor.messages.debug.DebugLinkGetState.return_empty_state)
|
||||||
|
pub return_empty_state: ::std::option::Option<bool>,
|
||||||
// special fields
|
// special fields
|
||||||
// @@protoc_insertion_point(special_field:hw.trezor.messages.debug.DebugLinkGetState.special_fields)
|
// @@protoc_insertion_point(special_field:hw.trezor.messages.debug.DebugLinkGetState.special_fields)
|
||||||
pub special_fields: ::protobuf::SpecialFields,
|
pub special_fields: ::protobuf::SpecialFields,
|
||||||
@ -1204,8 +1206,27 @@ impl DebugLinkGetState {
|
|||||||
self.wait_layout = ::std::option::Option::Some(::protobuf::EnumOrUnknown::new(v));
|
self.wait_layout = ::std::option::Option::Some(::protobuf::EnumOrUnknown::new(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optional bool return_empty_state = 4;
|
||||||
|
|
||||||
|
pub fn return_empty_state(&self) -> bool {
|
||||||
|
self.return_empty_state.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_return_empty_state(&mut self) {
|
||||||
|
self.return_empty_state = ::std::option::Option::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_return_empty_state(&self) -> bool {
|
||||||
|
self.return_empty_state.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Param is passed by value, moved
|
||||||
|
pub fn set_return_empty_state(&mut self, v: bool) {
|
||||||
|
self.return_empty_state = ::std::option::Option::Some(v);
|
||||||
|
}
|
||||||
|
|
||||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||||
let mut fields = ::std::vec::Vec::with_capacity(3);
|
let mut fields = ::std::vec::Vec::with_capacity(4);
|
||||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||||
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
|
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
|
||||||
"wait_word_list",
|
"wait_word_list",
|
||||||
@ -1222,6 +1243,11 @@ impl DebugLinkGetState {
|
|||||||
|m: &DebugLinkGetState| { &m.wait_layout },
|
|m: &DebugLinkGetState| { &m.wait_layout },
|
||||||
|m: &mut DebugLinkGetState| { &mut m.wait_layout },
|
|m: &mut DebugLinkGetState| { &mut m.wait_layout },
|
||||||
));
|
));
|
||||||
|
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
|
||||||
|
"return_empty_state",
|
||||||
|
|m: &DebugLinkGetState| { &m.return_empty_state },
|
||||||
|
|m: &mut DebugLinkGetState| { &mut m.return_empty_state },
|
||||||
|
));
|
||||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<DebugLinkGetState>(
|
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<DebugLinkGetState>(
|
||||||
"DebugLinkGetState",
|
"DebugLinkGetState",
|
||||||
fields,
|
fields,
|
||||||
@ -1249,6 +1275,9 @@ impl ::protobuf::Message for DebugLinkGetState {
|
|||||||
24 => {
|
24 => {
|
||||||
self.wait_layout = ::std::option::Option::Some(is.read_enum_or_unknown()?);
|
self.wait_layout = ::std::option::Option::Some(is.read_enum_or_unknown()?);
|
||||||
},
|
},
|
||||||
|
32 => {
|
||||||
|
self.return_empty_state = ::std::option::Option::Some(is.read_bool()?);
|
||||||
|
},
|
||||||
tag => {
|
tag => {
|
||||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||||
},
|
},
|
||||||
@ -1270,6 +1299,9 @@ impl ::protobuf::Message for DebugLinkGetState {
|
|||||||
if let Some(v) = self.wait_layout {
|
if let Some(v) = self.wait_layout {
|
||||||
my_size += ::protobuf::rt::int32_size(3, v.value());
|
my_size += ::protobuf::rt::int32_size(3, v.value());
|
||||||
}
|
}
|
||||||
|
if let Some(v) = self.return_empty_state {
|
||||||
|
my_size += 1 + 1;
|
||||||
|
}
|
||||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||||
self.special_fields.cached_size().set(my_size as u32);
|
self.special_fields.cached_size().set(my_size as u32);
|
||||||
my_size
|
my_size
|
||||||
@ -1285,6 +1317,9 @@ impl ::protobuf::Message for DebugLinkGetState {
|
|||||||
if let Some(v) = self.wait_layout {
|
if let Some(v) = self.wait_layout {
|
||||||
os.write_enum(3, ::protobuf::EnumOrUnknown::value(&v))?;
|
os.write_enum(3, ::protobuf::EnumOrUnknown::value(&v))?;
|
||||||
}
|
}
|
||||||
|
if let Some(v) = self.return_empty_state {
|
||||||
|
os.write_bool(4, v)?;
|
||||||
|
}
|
||||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||||
::std::result::Result::Ok(())
|
::std::result::Result::Ok(())
|
||||||
}
|
}
|
||||||
@ -1305,6 +1340,7 @@ impl ::protobuf::Message for DebugLinkGetState {
|
|||||||
self.wait_word_list = ::std::option::Option::None;
|
self.wait_word_list = ::std::option::Option::None;
|
||||||
self.wait_word_pos = ::std::option::Option::None;
|
self.wait_word_pos = ::std::option::Option::None;
|
||||||
self.wait_layout = ::std::option::Option::None;
|
self.wait_layout = ::std::option::Option::None;
|
||||||
|
self.return_empty_state = ::std::option::Option::None;
|
||||||
self.special_fields.clear();
|
self.special_fields.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1313,6 +1349,7 @@ impl ::protobuf::Message for DebugLinkGetState {
|
|||||||
wait_word_list: ::std::option::Option::None,
|
wait_word_list: ::std::option::Option::None,
|
||||||
wait_word_pos: ::std::option::Option::None,
|
wait_word_pos: ::std::option::Option::None,
|
||||||
wait_layout: ::std::option::Option::None,
|
wait_layout: ::std::option::Option::None,
|
||||||
|
return_empty_state: ::std::option::Option::None,
|
||||||
special_fields: ::protobuf::SpecialFields::new(),
|
special_fields: ::protobuf::SpecialFields::new(),
|
||||||
};
|
};
|
||||||
&instance
|
&instance
|
||||||
@ -3650,39 +3687,40 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
|||||||
\x01\x20\x03(\tR\x06tokens:\x02\x18\x01\"-\n\x15DebugLinkReseedRandom\
|
\x01\x20\x03(\tR\x06tokens:\x02\x18\x01\"-\n\x15DebugLinkReseedRandom\
|
||||||
\x12\x14\n\x05value\x18\x01\x20\x01(\rR\x05value\"j\n\x15DebugLinkRecord\
|
\x12\x14\n\x05value\x18\x01\x20\x01(\rR\x05value\"j\n\x15DebugLinkRecord\
|
||||||
Screen\x12)\n\x10target_directory\x18\x01\x20\x01(\tR\x0ftargetDirectory\
|
Screen\x12)\n\x10target_directory\x18\x01\x20\x01(\tR\x0ftargetDirectory\
|
||||||
\x12&\n\rrefresh_index\x18\x02\x20\x01(\r:\x010R\x0crefreshIndex\"\x91\
|
\x12&\n\rrefresh_index\x18\x02\x20\x01(\r:\x010R\x0crefreshIndex\"\xc6\
|
||||||
\x02\n\x11DebugLinkGetState\x12(\n\x0ewait_word_list\x18\x01\x20\x01(\
|
\x02\n\x11DebugLinkGetState\x12(\n\x0ewait_word_list\x18\x01\x20\x01(\
|
||||||
\x08R\x0cwaitWordListB\x02\x18\x01\x12&\n\rwait_word_pos\x18\x02\x20\x01\
|
\x08R\x0cwaitWordListB\x02\x18\x01\x12&\n\rwait_word_pos\x18\x02\x20\x01\
|
||||||
(\x08R\x0bwaitWordPosB\x02\x18\x01\x12e\n\x0bwait_layout\x18\x03\x20\x01\
|
(\x08R\x0bwaitWordPosB\x02\x18\x01\x12e\n\x0bwait_layout\x18\x03\x20\x01\
|
||||||
(\x0e29.hw.trezor.messages.debug.DebugLinkGetState.DebugWaitType:\tIMMED\
|
(\x0e29.hw.trezor.messages.debug.DebugLinkGetState.DebugWaitType:\tIMMED\
|
||||||
IATER\nwaitLayout\"C\n\rDebugWaitType\x12\r\n\tIMMEDIATE\x10\0\x12\x0f\n\
|
IATER\nwaitLayout\x123\n\x12return_empty_state\x18\x04\x20\x01(\x08:\x05\
|
||||||
\x0bNEXT_LAYOUT\x10\x01\x12\x12\n\x0eCURRENT_LAYOUT\x10\x02\"\x97\x04\n\
|
falseR\x10returnEmptyState\"C\n\rDebugWaitType\x12\r\n\tIMMEDIATE\x10\0\
|
||||||
\x0eDebugLinkState\x12\x16\n\x06layout\x18\x01\x20\x01(\x0cR\x06layout\
|
\x12\x0f\n\x0bNEXT_LAYOUT\x10\x01\x12\x12\n\x0eCURRENT_LAYOUT\x10\x02\"\
|
||||||
\x12\x10\n\x03pin\x18\x02\x20\x01(\tR\x03pin\x12\x16\n\x06matrix\x18\x03\
|
\x97\x04\n\x0eDebugLinkState\x12\x16\n\x06layout\x18\x01\x20\x01(\x0cR\
|
||||||
\x20\x01(\tR\x06matrix\x12'\n\x0fmnemonic_secret\x18\x04\x20\x01(\x0cR\
|
\x06layout\x12\x10\n\x03pin\x18\x02\x20\x01(\tR\x03pin\x12\x16\n\x06matr\
|
||||||
\x0emnemonicSecret\x129\n\x04node\x18\x05\x20\x01(\x0b2%.hw.trezor.messa\
|
ix\x18\x03\x20\x01(\tR\x06matrix\x12'\n\x0fmnemonic_secret\x18\x04\x20\
|
||||||
ges.common.HDNodeTypeR\x04node\x123\n\x15passphrase_protection\x18\x06\
|
\x01(\x0cR\x0emnemonicSecret\x129\n\x04node\x18\x05\x20\x01(\x0b2%.hw.tr\
|
||||||
\x20\x01(\x08R\x14passphraseProtection\x12\x1d\n\nreset_word\x18\x07\x20\
|
ezor.messages.common.HDNodeTypeR\x04node\x123\n\x15passphrase_protection\
|
||||||
\x01(\tR\tresetWord\x12#\n\rreset_entropy\x18\x08\x20\x01(\x0cR\x0creset\
|
\x18\x06\x20\x01(\x08R\x14passphraseProtection\x12\x1d\n\nreset_word\x18\
|
||||||
Entropy\x12,\n\x12recovery_fake_word\x18\t\x20\x01(\tR\x10recoveryFakeWo\
|
\x07\x20\x01(\tR\tresetWord\x12#\n\rreset_entropy\x18\x08\x20\x01(\x0cR\
|
||||||
rd\x12*\n\x11recovery_word_pos\x18\n\x20\x01(\rR\x0frecoveryWordPos\x12$\
|
\x0cresetEntropy\x12,\n\x12recovery_fake_word\x18\t\x20\x01(\tR\x10recov\
|
||||||
\n\x0ereset_word_pos\x18\x0b\x20\x01(\rR\x0cresetWordPos\x12N\n\rmnemoni\
|
eryFakeWord\x12*\n\x11recovery_word_pos\x18\n\x20\x01(\rR\x0frecoveryWor\
|
||||||
c_type\x18\x0c\x20\x01(\x0e2).hw.trezor.messages.management.BackupTypeR\
|
dPos\x12$\n\x0ereset_word_pos\x18\x0b\x20\x01(\rR\x0cresetWordPos\x12N\n\
|
||||||
\x0cmnemonicType\x12\x16\n\x06tokens\x18\r\x20\x03(\tR\x06tokens\"\x0f\n\
|
\rmnemonic_type\x18\x0c\x20\x01(\x0e2).hw.trezor.messages.management.Bac\
|
||||||
\rDebugLinkStop\"P\n\x0cDebugLinkLog\x12\x14\n\x05level\x18\x01\x20\x01(\
|
kupTypeR\x0cmnemonicType\x12\x16\n\x06tokens\x18\r\x20\x03(\tR\x06tokens\
|
||||||
\rR\x05level\x12\x16\n\x06bucket\x18\x02\x20\x01(\tR\x06bucket\x12\x12\n\
|
\"\x0f\n\rDebugLinkStop\"P\n\x0cDebugLinkLog\x12\x14\n\x05level\x18\x01\
|
||||||
\x04text\x18\x03\x20\x01(\tR\x04text\"G\n\x13DebugLinkMemoryRead\x12\x18\
|
\x20\x01(\rR\x05level\x12\x16\n\x06bucket\x18\x02\x20\x01(\tR\x06bucket\
|
||||||
\n\x07address\x18\x01\x20\x01(\rR\x07address\x12\x16\n\x06length\x18\x02\
|
\x12\x12\n\x04text\x18\x03\x20\x01(\tR\x04text\"G\n\x13DebugLinkMemoryRe\
|
||||||
\x20\x01(\rR\x06length\")\n\x0fDebugLinkMemory\x12\x16\n\x06memory\x18\
|
ad\x12\x18\n\x07address\x18\x01\x20\x01(\rR\x07address\x12\x16\n\x06leng\
|
||||||
\x01\x20\x01(\x0cR\x06memory\"^\n\x14DebugLinkMemoryWrite\x12\x18\n\x07a\
|
th\x18\x02\x20\x01(\rR\x06length\")\n\x0fDebugLinkMemory\x12\x16\n\x06me\
|
||||||
ddress\x18\x01\x20\x01(\rR\x07address\x12\x16\n\x06memory\x18\x02\x20\
|
mory\x18\x01\x20\x01(\x0cR\x06memory\"^\n\x14DebugLinkMemoryWrite\x12\
|
||||||
\x01(\x0cR\x06memory\x12\x14\n\x05flash\x18\x03\x20\x01(\x08R\x05flash\"\
|
\x18\n\x07address\x18\x01\x20\x01(\rR\x07address\x12\x16\n\x06memory\x18\
|
||||||
-\n\x13DebugLinkFlashErase\x12\x16\n\x06sector\x18\x01\x20\x01(\rR\x06se\
|
\x02\x20\x01(\x0cR\x06memory\x12\x14\n\x05flash\x18\x03\x20\x01(\x08R\
|
||||||
ctor\".\n\x14DebugLinkEraseSdCard\x12\x16\n\x06format\x18\x01\x20\x01(\
|
\x05flash\"-\n\x13DebugLinkFlashErase\x12\x16\n\x06sector\x18\x01\x20\
|
||||||
\x08R\x06format\"0\n\x14DebugLinkWatchLayout\x12\x14\n\x05watch\x18\x01\
|
\x01(\rR\x06sector\".\n\x14DebugLinkEraseSdCard\x12\x16\n\x06format\x18\
|
||||||
\x20\x01(\x08R\x05watch:\x02\x18\x01\"\x1f\n\x19DebugLinkResetDebugEvent\
|
\x01\x20\x01(\x08R\x06format\"0\n\x14DebugLinkWatchLayout\x12\x14\n\x05w\
|
||||||
s:\x02\x18\x01\"\x1a\n\x18DebugLinkOptigaSetSecMaxB=\n#com.satoshilabs.t\
|
atch\x18\x01\x20\x01(\x08R\x05watch:\x02\x18\x01\"\x1f\n\x19DebugLinkRes\
|
||||||
rezor.lib.protobufB\x12TrezorMessageDebug\x80\xa6\x1d\x01\
|
etDebugEvents:\x02\x18\x01\"\x1a\n\x18DebugLinkOptigaSetSecMaxB=\n#com.s\
|
||||||
|
atoshilabs.trezor.lib.protobufB\x12TrezorMessageDebug\x80\xa6\x1d\x01\
|
||||||
";
|
";
|
||||||
|
|
||||||
/// `FileDescriptorProto` object which was a source for this generated file
|
/// `FileDescriptorProto` object which was a source for this generated file
|
||||||
|
@ -49,36 +49,27 @@ def get_char_category(char: str) -> PassphraseCategory:
|
|||||||
|
|
||||||
def go_next(debug: "DebugLink") -> LayoutContent:
|
def go_next(debug: "DebugLink") -> LayoutContent:
|
||||||
if debug.layout_type is LayoutType.Bolt:
|
if debug.layout_type is LayoutType.Bolt:
|
||||||
return debug.click(buttons.OK)
|
debug.click(buttons.OK)
|
||||||
elif debug.layout_type is LayoutType.Caesar:
|
elif debug.layout_type is LayoutType.Caesar:
|
||||||
return debug.press_right()
|
debug.press_right()
|
||||||
elif debug.layout_type is LayoutType.Delizia:
|
elif debug.layout_type is LayoutType.Delizia:
|
||||||
return debug.swipe_up()
|
debug.swipe_up()
|
||||||
else:
|
|
||||||
raise RuntimeError("Unknown model")
|
|
||||||
|
|
||||||
|
|
||||||
def tap_to_confirm(debug: "DebugLink") -> LayoutContent:
|
|
||||||
if debug.layout_type is LayoutType.Bolt:
|
|
||||||
return debug.read_layout()
|
|
||||||
elif debug.layout_type is LayoutType.Caesar:
|
|
||||||
return debug.read_layout()
|
|
||||||
elif debug.layout_type is LayoutType.Delizia:
|
|
||||||
return debug.click(buttons.TAP_TO_CONFIRM)
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Unknown model")
|
raise RuntimeError("Unknown model")
|
||||||
|
return debug.read_layout()
|
||||||
|
|
||||||
|
|
||||||
def go_back(debug: "DebugLink", r_middle: bool = False) -> LayoutContent:
|
def go_back(debug: "DebugLink", r_middle: bool = False) -> LayoutContent:
|
||||||
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
|
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
|
||||||
return debug.click(buttons.CANCEL)
|
debug.click(buttons.CANCEL)
|
||||||
elif debug.layout_type is LayoutType.Caesar:
|
elif debug.layout_type is LayoutType.Caesar:
|
||||||
if r_middle:
|
if r_middle:
|
||||||
return debug.press_middle()
|
debug.press_middle()
|
||||||
else:
|
else:
|
||||||
return debug.press_left()
|
debug.press_left()
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Unknown model")
|
raise RuntimeError("Unknown model")
|
||||||
|
return debug.read_layout()
|
||||||
|
|
||||||
|
|
||||||
def navigate_to_action_and_press(
|
def navigate_to_action_and_press(
|
||||||
@ -108,10 +99,10 @@ def navigate_to_action_and_press(
|
|||||||
|
|
||||||
if steps < 0:
|
if steps < 0:
|
||||||
for _ in range(-steps):
|
for _ in range(-steps):
|
||||||
layout = debug.press_left()
|
debug.press_left()
|
||||||
else:
|
else:
|
||||||
for _ in range(steps):
|
for _ in range(steps):
|
||||||
layout = debug.press_right()
|
debug.press_right()
|
||||||
|
|
||||||
# Press or hold
|
# Press or hold
|
||||||
debug.press_middle(hold_ms=hold_ms)
|
debug.press_middle(hold_ms=hold_ms)
|
||||||
@ -125,13 +116,14 @@ def _carousel_steps(current_index: int, wanted_index: int, length: int) -> int:
|
|||||||
|
|
||||||
def unlock_gesture(debug: "DebugLink") -> LayoutContent:
|
def unlock_gesture(debug: "DebugLink") -> LayoutContent:
|
||||||
if debug.layout_type is LayoutType.Bolt:
|
if debug.layout_type is LayoutType.Bolt:
|
||||||
return debug.click(buttons.OK)
|
debug.click(buttons.OK)
|
||||||
elif debug.layout_type is LayoutType.Caesar:
|
elif debug.layout_type is LayoutType.Caesar:
|
||||||
return debug.press_right()
|
debug.press_right()
|
||||||
elif debug.layout_type is LayoutType.Delizia:
|
elif debug.layout_type is LayoutType.Delizia:
|
||||||
return debug.click(buttons.TAP_TO_CONFIRM)
|
debug.click(buttons.TAP_TO_CONFIRM)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Unknown model")
|
raise RuntimeError("Unknown model")
|
||||||
|
return debug.read_layout()
|
||||||
|
|
||||||
|
|
||||||
def _get_action_index(wanted_action: str, all_actions: AllActionsType) -> int:
|
def _get_action_index(wanted_action: str, all_actions: AllActionsType) -> int:
|
||||||
|
@ -23,7 +23,8 @@ def enter_word(
|
|||||||
if debug.layout_type is LayoutType.Delizia and not is_slip39 and len(word) > 4:
|
if debug.layout_type is LayoutType.Delizia and not is_slip39 and len(word) > 4:
|
||||||
# T3T1 (delizia) BIP39 keyboard allows to "confirm" only if the word is fully written, you need to click the word to auto-complete
|
# T3T1 (delizia) BIP39 keyboard allows to "confirm" only if the word is fully written, you need to click the word to auto-complete
|
||||||
debug.click(buttons.CONFIRM_WORD)
|
debug.click(buttons.CONFIRM_WORD)
|
||||||
return debug.click(buttons.CONFIRM_WORD)
|
debug.click(buttons.CONFIRM_WORD)
|
||||||
|
return debug.read_layout()
|
||||||
elif debug.layout_type is LayoutType.Caesar:
|
elif debug.layout_type is LayoutType.Caesar:
|
||||||
letter_index = 0
|
letter_index = 0
|
||||||
layout = debug.read_layout()
|
layout = debug.read_layout()
|
||||||
@ -32,16 +33,20 @@ 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()
|
debug.press_right()
|
||||||
|
layout = debug.read_layout()
|
||||||
|
|
||||||
layout = debug.press_middle()
|
debug.press_middle()
|
||||||
|
layout = debug.read_layout()
|
||||||
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()
|
debug.press_right()
|
||||||
|
layout = debug.read_layout()
|
||||||
|
|
||||||
return debug.press_middle()
|
debug.press_middle()
|
||||||
|
return debug.read_layout()
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unknown model")
|
raise ValueError("Unknown model")
|
||||||
|
|
||||||
@ -78,7 +83,8 @@ def select_number_of_words(
|
|||||||
coords = coords_map.get(num_of_words)
|
coords = coords_map.get(num_of_words)
|
||||||
if coords is None:
|
if coords is None:
|
||||||
raise ValueError("Invalid num_of_words")
|
raise ValueError("Invalid num_of_words")
|
||||||
return debug.click(coords)
|
debug.click(coords)
|
||||||
|
return debug.read_layout()
|
||||||
|
|
||||||
def select_caesar() -> "LayoutContent":
|
def select_caesar() -> "LayoutContent":
|
||||||
# navigate to the number and confirm it
|
# navigate to the number and confirm it
|
||||||
@ -86,7 +92,8 @@ def select_number_of_words(
|
|||||||
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()
|
debug.press_right()
|
||||||
return debug.press_middle()
|
debug.press_middle()
|
||||||
|
return debug.read_layout()
|
||||||
|
|
||||||
def select_delizia() -> "LayoutContent":
|
def select_delizia() -> "LayoutContent":
|
||||||
# click the button from ValuePad
|
# click the button from ValuePad
|
||||||
@ -103,13 +110,15 @@ def select_number_of_words(
|
|||||||
coords = coords_map.get(num_of_words)
|
coords = coords_map.get(num_of_words)
|
||||||
if coords is None:
|
if coords is None:
|
||||||
raise ValueError("Invalid num_of_words")
|
raise ValueError("Invalid num_of_words")
|
||||||
return debug.click(coords)
|
debug.click(coords)
|
||||||
|
return debug.read_layout()
|
||||||
|
|
||||||
if debug.layout_type is LayoutType.Bolt:
|
if debug.layout_type is LayoutType.Bolt:
|
||||||
assert debug.read_layout().text_content() == TR.recovery__num_of_words
|
assert debug.read_layout().text_content() == TR.recovery__num_of_words
|
||||||
layout = select_bolt()
|
layout = select_bolt()
|
||||||
elif debug.layout_type is LayoutType.Caesar:
|
elif debug.layout_type is LayoutType.Caesar:
|
||||||
layout = debug.press_right()
|
debug.press_right()
|
||||||
|
layout = debug.read_layout()
|
||||||
assert layout.title() == TR.word_count__title
|
assert layout.title() == TR.word_count__title
|
||||||
layout = select_caesar()
|
layout = select_caesar()
|
||||||
elif debug.layout_type is LayoutType.Delizia:
|
elif debug.layout_type is LayoutType.Delizia:
|
||||||
@ -150,12 +159,15 @@ def enter_share(
|
|||||||
assert TR.translate(before_title) in debug.read_layout().title()
|
assert TR.translate(before_title) in debug.read_layout().title()
|
||||||
layout = debug.read_layout()
|
layout = debug.read_layout()
|
||||||
for _ in range(layout.page_count()):
|
for _ in range(layout.page_count()):
|
||||||
layout = debug.press_right()
|
debug.press_right()
|
||||||
|
layout = debug.read_layout()
|
||||||
elif debug.layout_type is LayoutType.Delizia:
|
elif debug.layout_type is LayoutType.Delizia:
|
||||||
layout = debug.swipe_up()
|
debug.swipe_up()
|
||||||
|
layout = debug.read_layout()
|
||||||
else:
|
else:
|
||||||
assert TR.translate(before_title) in debug.read_layout().title()
|
assert TR.translate(before_title) in debug.read_layout().title()
|
||||||
layout = debug.click(buttons.OK)
|
debug.click(buttons.OK)
|
||||||
|
layout = debug.read_layout()
|
||||||
|
|
||||||
assert "MnemonicKeyboard" in layout.all_components()
|
assert "MnemonicKeyboard" in layout.all_components()
|
||||||
|
|
||||||
@ -236,13 +248,17 @@ def enter_seed_previous_correct(
|
|||||||
layout = debug.read_layout()
|
layout = debug.read_layout()
|
||||||
|
|
||||||
while layout.get_middle_choice() not in DELETE_BTNS:
|
while layout.get_middle_choice() not in DELETE_BTNS:
|
||||||
layout = debug.press_right()
|
debug.press_right()
|
||||||
layout = debug.press_middle()
|
layout = debug.read_layout()
|
||||||
|
debug.press_middle()
|
||||||
|
layout = debug.read_layout()
|
||||||
|
|
||||||
for _ in range(len(bad_word)):
|
for _ in range(len(bad_word)):
|
||||||
while layout.get_middle_choice() not in DELETE_BTNS:
|
while layout.get_middle_choice() not in DELETE_BTNS:
|
||||||
layout = debug.press_left()
|
debug.press_left()
|
||||||
layout = debug.press_middle()
|
layout = debug.read_layout()
|
||||||
|
debug.press_middle()
|
||||||
|
layout = debug.read_layout()
|
||||||
elif debug.layout_type is LayoutType.Delizia:
|
elif debug.layout_type is LayoutType.Delizia:
|
||||||
debug.click(buttons.RECOVERY_DELETE) # Top-left
|
debug.click(buttons.RECOVERY_DELETE) # Top-left
|
||||||
for _ in range(len(bad_word)):
|
for _ in range(len(bad_word)):
|
||||||
@ -278,7 +294,8 @@ def prepare_enter_seed(
|
|||||||
elif debug.layout_type is LayoutType.Caesar:
|
elif debug.layout_type is LayoutType.Caesar:
|
||||||
debug.press_right()
|
debug.press_right()
|
||||||
debug.press_right()
|
debug.press_right()
|
||||||
layout = debug.press_right()
|
debug.press_right()
|
||||||
|
layout = debug.read_layout()
|
||||||
assert "MnemonicKeyboard" in layout.all_components()
|
assert "MnemonicKeyboard" in layout.all_components()
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,7 +79,8 @@ def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> Non
|
|||||||
in TR.reset__title_number_of_shares + TR.words__title_threshold
|
in TR.reset__title_number_of_shares + TR.words__title_threshold
|
||||||
):
|
):
|
||||||
# Special info screens
|
# Special info screens
|
||||||
layout = debug.press_right()
|
debug.press_right()
|
||||||
|
layout = debug.read_layout()
|
||||||
assert "NumberInput" in layout.all_components()
|
assert "NumberInput" in layout.all_components()
|
||||||
if button == buttons.reset_minus(debug.model.internal_name):
|
if button == buttons.reset_minus(debug.model.internal_name):
|
||||||
for _ in range(diff):
|
for _ in range(diff):
|
||||||
@ -102,7 +103,8 @@ def read_words(debug: "DebugLink", do_htc: bool = True) -> list[str]:
|
|||||||
layout = debug.read_layout()
|
layout = debug.read_layout()
|
||||||
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()
|
debug.swipe_up()
|
||||||
|
layout = debug.read_layout()
|
||||||
assert layout is not None
|
assert layout is not None
|
||||||
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
|
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
|
||||||
words.extend(layout.seed_words())
|
words.extend(layout.seed_words())
|
||||||
@ -147,7 +149,8 @@ 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])
|
debug.click(buttons.RESET_WORD_CHECK[button_pos])
|
||||||
|
layout = debug.read_layout()
|
||||||
elif debug.layout_type is LayoutType.Delizia:
|
elif debug.layout_type is LayoutType.Delizia:
|
||||||
assert TR.regexp("reset__select_word_x_of_y_template").match(layout.subtitle())
|
assert TR.regexp("reset__select_word_x_of_y_template").match(layout.subtitle())
|
||||||
for _ in range(3):
|
for _ in range(3):
|
||||||
@ -162,10 +165,12 @@ 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.VERTICAL_MENU[button_pos])
|
debug.click(buttons.VERTICAL_MENU[button_pos])
|
||||||
|
layout = debug.read_layout()
|
||||||
elif debug.layout_type is LayoutType.Caesar:
|
elif debug.layout_type is LayoutType.Caesar:
|
||||||
assert TR.reset__select_correct_word in layout.text_content()
|
assert TR.reset__select_correct_word in layout.text_content()
|
||||||
layout = debug.press_right()
|
debug.press_right()
|
||||||
|
layout = debug.read_layout()
|
||||||
for _ in range(3):
|
for _ in range(3):
|
||||||
# "SELECT 2ND WORD"
|
# "SELECT 2ND WORD"
|
||||||
# ^
|
# ^
|
||||||
@ -176,9 +181,11 @@ 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()
|
debug.press_right()
|
||||||
|
layout = debug.read_layout()
|
||||||
|
|
||||||
layout = debug.press_middle()
|
debug.press_middle()
|
||||||
|
layout = debug.read_layout()
|
||||||
|
|
||||||
|
|
||||||
def validate_mnemonics(mnemonics: list[str], expected_ems: bytes) -> None:
|
def validate_mnemonics(mnemonics: list[str], expected_ems: bytes) -> None:
|
||||||
|
@ -30,7 +30,7 @@ from .. import translations as TR
|
|||||||
from ..device_tests.bitcoin.payment_req import make_coinjoin_request
|
from ..device_tests.bitcoin.payment_req import make_coinjoin_request
|
||||||
from ..tx_cache import TxCache
|
from ..tx_cache import TxCache
|
||||||
from . import recovery
|
from . import recovery
|
||||||
from .common import go_next, tap_to_confirm, unlock_gesture
|
from .common import go_next, unlock_gesture
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from trezorlib.debuglink import DebugLink, LayoutContent
|
from trezorlib.debuglink import DebugLink, LayoutContent
|
||||||
@ -71,7 +71,8 @@ def set_autolock_delay(device_handler: "BackgroundDeviceHandler", delay_ms: int)
|
|||||||
|
|
||||||
layout = go_next(debug)
|
layout = go_next(debug)
|
||||||
if debug.layout_type is LayoutType.Delizia:
|
if debug.layout_type is LayoutType.Delizia:
|
||||||
layout = tap_to_confirm(debug)
|
debug.click(buttons.TAP_TO_CONFIRM)
|
||||||
|
layout = debug.read_layout()
|
||||||
assert layout.main_component() == "Homescreen"
|
assert layout.main_component() == "Homescreen"
|
||||||
device_handler.result()
|
device_handler.result()
|
||||||
|
|
||||||
@ -106,17 +107,20 @@ def test_autolock_interrupts_signing(device_handler: "BackgroundDeviceHandler"):
|
|||||||
|
|
||||||
if debug.layout_type is LayoutType.Bolt:
|
if debug.layout_type is LayoutType.Bolt:
|
||||||
debug.click(buttons.OK)
|
debug.click(buttons.OK)
|
||||||
layout = debug.click(buttons.OK)
|
debug.click(buttons.OK)
|
||||||
|
layout = debug.read_layout()
|
||||||
assert TR.send__total_amount in layout.text_content()
|
assert TR.send__total_amount in layout.text_content()
|
||||||
assert "0.0039 BTC" in layout.text_content()
|
assert "0.0039 BTC" in layout.text_content()
|
||||||
elif debug.layout_type is LayoutType.Delizia:
|
elif debug.layout_type is LayoutType.Delizia:
|
||||||
debug.swipe_up()
|
debug.swipe_up()
|
||||||
layout = debug.swipe_up()
|
debug.swipe_up()
|
||||||
|
layout = debug.read_layout()
|
||||||
assert TR.send__total_amount in layout.text_content()
|
assert TR.send__total_amount in layout.text_content()
|
||||||
assert "0.0039 BTC" in layout.text_content()
|
assert "0.0039 BTC" in layout.text_content()
|
||||||
elif debug.layout_type is LayoutType.Caesar:
|
elif debug.layout_type is LayoutType.Caesar:
|
||||||
debug.press_right()
|
debug.press_right()
|
||||||
layout = debug.press_right()
|
debug.press_right()
|
||||||
|
layout = debug.read_layout()
|
||||||
assert TR.send__total_amount in layout.text_content()
|
assert TR.send__total_amount in layout.text_content()
|
||||||
assert "0.0039 BTC" in layout.text_content()
|
assert "0.0039 BTC" in layout.text_content()
|
||||||
|
|
||||||
@ -158,18 +162,21 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa
|
|||||||
|
|
||||||
if debug.layout_type is LayoutType.Bolt:
|
if debug.layout_type is LayoutType.Bolt:
|
||||||
debug.click(buttons.OK)
|
debug.click(buttons.OK)
|
||||||
layout = debug.click(buttons.OK)
|
debug.click(buttons.OK)
|
||||||
|
layout = debug.read_layout()
|
||||||
assert TR.send__total_amount in layout.text_content()
|
assert TR.send__total_amount in layout.text_content()
|
||||||
assert "0.0039 BTC" in layout.text_content()
|
assert "0.0039 BTC" in layout.text_content()
|
||||||
elif debug.layout_type is LayoutType.Delizia:
|
elif debug.layout_type is LayoutType.Delizia:
|
||||||
debug.swipe_up()
|
debug.swipe_up()
|
||||||
layout = debug.swipe_up()
|
debug.swipe_up()
|
||||||
|
layout = debug.read_layout()
|
||||||
assert TR.send__total_amount in layout.text_content()
|
assert TR.send__total_amount in layout.text_content()
|
||||||
assert "0.0039 BTC" in layout.text_content()
|
assert "0.0039 BTC" in layout.text_content()
|
||||||
debug.swipe_up()
|
debug.swipe_up()
|
||||||
elif debug.layout_type is LayoutType.Caesar:
|
elif debug.layout_type is LayoutType.Caesar:
|
||||||
debug.press_right()
|
debug.press_right()
|
||||||
layout = debug.press_right()
|
debug.press_right()
|
||||||
|
layout = debug.read_layout()
|
||||||
assert TR.send__total_amount in layout.text_content()
|
assert TR.send__total_amount in layout.text_content()
|
||||||
assert "0.0039 BTC" in layout.text_content()
|
assert "0.0039 BTC" in layout.text_content()
|
||||||
|
|
||||||
@ -181,17 +188,12 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa
|
|||||||
with device_handler.client:
|
with device_handler.client:
|
||||||
device_handler.client.set_filter(messages.TxAck, sleepy_filter)
|
device_handler.client.set_filter(messages.TxAck, sleepy_filter)
|
||||||
# confirm transaction
|
# confirm transaction
|
||||||
# In all cases we set wait=False to avoid waiting for the screen and triggering
|
|
||||||
# the layout deadlock detection. In reality there is no deadlock but the
|
|
||||||
# `sleepy_filter` delays the response by 10 secs while the layout deadlock
|
|
||||||
# timeout is 3. In this test we don't need the result of the input event so
|
|
||||||
# waiting for it is not necessary.
|
|
||||||
if debug.layout_type is LayoutType.Bolt:
|
if debug.layout_type is LayoutType.Bolt:
|
||||||
debug.click(buttons.OK, wait=False)
|
debug.click(buttons.OK)
|
||||||
elif debug.layout_type is LayoutType.Delizia:
|
elif debug.layout_type is LayoutType.Delizia:
|
||||||
debug.click(buttons.TAP_TO_CONFIRM, wait=False)
|
debug.click(buttons.TAP_TO_CONFIRM)
|
||||||
elif debug.layout_type is LayoutType.Caesar:
|
elif debug.layout_type is LayoutType.Caesar:
|
||||||
debug.press_middle(wait=False)
|
debug.press_middle()
|
||||||
|
|
||||||
signatures, tx = device_handler.result()
|
signatures, tx = device_handler.result()
|
||||||
assert len(signatures) == 1
|
assert len(signatures) == 1
|
||||||
@ -277,7 +279,8 @@ def unlock_dry_run(debug: "DebugLink") -> "LayoutContent":
|
|||||||
layout = go_next(debug)
|
layout = go_next(debug)
|
||||||
assert "PinKeyboard" in layout.all_components()
|
assert "PinKeyboard" in layout.all_components()
|
||||||
|
|
||||||
layout = debug.input(PIN4)
|
debug.input(PIN4)
|
||||||
|
layout = debug.read_layout()
|
||||||
assert layout is not None
|
assert layout is not None
|
||||||
return layout
|
return layout
|
||||||
|
|
||||||
@ -307,7 +310,8 @@ def test_dryrun_locks_at_number_of_words(device_handler: "BackgroundDeviceHandle
|
|||||||
layout = unlock_gesture(debug)
|
layout = unlock_gesture(debug)
|
||||||
|
|
||||||
assert "PinKeyboard" in layout.all_components()
|
assert "PinKeyboard" in layout.all_components()
|
||||||
layout = debug.input(PIN4)
|
debug.input(PIN4)
|
||||||
|
layout = debug.read_layout()
|
||||||
assert layout is not None
|
assert layout is not None
|
||||||
|
|
||||||
# we are back at homescreen
|
# we are back at homescreen
|
||||||
@ -330,7 +334,8 @@ def test_dryrun_locks_at_word_entry(device_handler: "BackgroundDeviceHandler"):
|
|||||||
layout = go_next(debug)
|
layout = go_next(debug)
|
||||||
assert layout.main_component() == "MnemonicKeyboard"
|
assert layout.main_component() == "MnemonicKeyboard"
|
||||||
elif debug.layout_type is LayoutType.Caesar:
|
elif debug.layout_type is LayoutType.Caesar:
|
||||||
layout = debug.press_right()
|
debug.press_right()
|
||||||
|
layout = debug.read_layout()
|
||||||
assert "MnemonicKeyboard" in layout.all_components()
|
assert "MnemonicKeyboard" in layout.all_components()
|
||||||
|
|
||||||
# make sure keyboard locks
|
# make sure keyboard locks
|
||||||
@ -353,31 +358,36 @@ 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.layout_type is LayoutType.Bolt:
|
if debug.layout_type is LayoutType.Bolt:
|
||||||
layout = debug.click(buttons.OK)
|
debug.click(buttons.OK)
|
||||||
|
layout = debug.read_layout()
|
||||||
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)
|
debug.click(buttons.CONFIRM_WORD)
|
||||||
|
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 layout.main_component() == "MnemonicKeyboard"
|
assert layout.main_component() == "MnemonicKeyboard"
|
||||||
|
|
||||||
elif debug.layout_type is LayoutType.Delizia:
|
elif debug.layout_type is LayoutType.Delizia:
|
||||||
layout = debug.swipe_up()
|
debug.swipe_up()
|
||||||
|
layout = debug.read_layout()
|
||||||
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)
|
debug.click(buttons.CONFIRM_WORD)
|
||||||
|
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 layout.main_component() == "MnemonicKeyboard"
|
assert layout.main_component() == "MnemonicKeyboard"
|
||||||
|
|
||||||
elif debug.layout_type is LayoutType.Caesar:
|
elif debug.layout_type is LayoutType.Caesar:
|
||||||
layout = debug.press_right()
|
debug.press_right()
|
||||||
|
layout = debug.read_layout()
|
||||||
assert "MnemonicKeyboard" in layout.all_components()
|
assert "MnemonicKeyboard" in layout.all_components()
|
||||||
|
|
||||||
# pressing middle button three times
|
# pressing middle button three times
|
||||||
|
@ -79,9 +79,10 @@ def test_hold_to_lock(device_handler: "BackgroundDeviceHandler"):
|
|||||||
|
|
||||||
# unlock by touching
|
# unlock by touching
|
||||||
if debug.layout_type is LayoutType.Caesar:
|
if debug.layout_type is LayoutType.Caesar:
|
||||||
layout = debug.press_right()
|
debug.press_right()
|
||||||
else:
|
else:
|
||||||
layout = debug.click(buttons.INFO)
|
debug.click(buttons.INFO)
|
||||||
|
layout = debug.read_layout()
|
||||||
assert "PinKeyboard" in layout.all_components()
|
assert "PinKeyboard" in layout.all_components()
|
||||||
debug.input("1234")
|
debug.input("1234")
|
||||||
|
|
||||||
|
@ -212,6 +212,11 @@ def _cancel_pin(debug: "DebugLink") -> None:
|
|||||||
# TODO: implement cancel PIN for TR?
|
# TODO: implement cancel PIN for TR?
|
||||||
_delete_pin(debug, 1, check=False)
|
_delete_pin(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
|
||||||
|
# the input event may get lost in the loop restart.
|
||||||
|
assert debug.read_layout().main_component() != "PinKeyboard"
|
||||||
|
|
||||||
|
|
||||||
def _confirm_pin(debug: "DebugLink") -> None:
|
def _confirm_pin(debug: "DebugLink") -> None:
|
||||||
"""Navigate to "ENTER" and press it"""
|
"""Navigate to "ENTER" and press it"""
|
||||||
|
@ -56,7 +56,8 @@ def go_through_tutorial_tr(debug: "DebugLink") -> None:
|
|||||||
debug.press_right(hold_ms=1000)
|
debug.press_right(hold_ms=1000)
|
||||||
debug.press_right()
|
debug.press_right()
|
||||||
debug.press_right()
|
debug.press_right()
|
||||||
layout = debug.press_middle()
|
debug.press_middle()
|
||||||
|
layout = debug.read_layout()
|
||||||
assert layout.title() == TR.tutorial__title_tutorial_complete
|
assert layout.title() == TR.tutorial__title_tutorial_complete
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,18 +38,17 @@ def test_tutorial_ignore_menu(device_handler: "BackgroundDeviceHandler"):
|
|||||||
debug = device_handler.debuglink()
|
debug = device_handler.debuglink()
|
||||||
device_handler.run(device.show_device_tutorial)
|
device_handler.run(device.show_device_tutorial)
|
||||||
|
|
||||||
layout = debug.read_layout()
|
assert debug.read_layout().title() == TR.tutorial__welcome_safe5
|
||||||
assert layout.title() == TR.tutorial__welcome_safe5
|
debug.click(buttons.TAP_TO_CONFIRM)
|
||||||
layout = debug.click(buttons.TAP_TO_CONFIRM)
|
assert debug.read_layout().title() == TR.tutorial__title_lets_begin
|
||||||
assert layout.title() == TR.tutorial__title_lets_begin
|
debug.swipe_up()
|
||||||
layout = debug.swipe_up()
|
assert debug.read_layout().title() == TR.tutorial__title_easy_navigation
|
||||||
assert layout.title() == TR.tutorial__title_easy_navigation
|
debug.swipe_up()
|
||||||
layout = debug.swipe_up()
|
assert debug.read_layout().title() == TR.tutorial__title_handy_menu
|
||||||
assert layout.title() == TR.tutorial__title_handy_menu
|
debug.swipe_up()
|
||||||
layout = debug.swipe_up()
|
assert debug.read_layout().title() == TR.tutorial__title_hold
|
||||||
assert layout.title() == TR.tutorial__title_hold
|
debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
|
||||||
layout = debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
|
assert debug.read_layout().title() == TR.tutorial__title_well_done
|
||||||
assert layout.title() == TR.tutorial__title_well_done
|
|
||||||
debug.swipe_up()
|
debug.swipe_up()
|
||||||
|
|
||||||
device_handler.result()
|
device_handler.result()
|
||||||
@ -59,24 +58,23 @@ def test_tutorial_menu_open_close(device_handler: "BackgroundDeviceHandler"):
|
|||||||
debug = device_handler.debuglink()
|
debug = device_handler.debuglink()
|
||||||
device_handler.run(device.show_device_tutorial)
|
device_handler.run(device.show_device_tutorial)
|
||||||
|
|
||||||
layout = debug.read_layout()
|
assert debug.read_layout().title() == TR.tutorial__welcome_safe5
|
||||||
assert layout.title() == TR.tutorial__welcome_safe5
|
debug.click(buttons.TAP_TO_CONFIRM)
|
||||||
layout = debug.click(buttons.TAP_TO_CONFIRM)
|
assert debug.read_layout().title() == TR.tutorial__title_lets_begin
|
||||||
assert layout.title() == TR.tutorial__title_lets_begin
|
debug.swipe_up()
|
||||||
layout = debug.swipe_up()
|
assert debug.read_layout().title() == TR.tutorial__title_easy_navigation
|
||||||
assert layout.title() == TR.tutorial__title_easy_navigation
|
debug.swipe_up()
|
||||||
layout = debug.swipe_up()
|
assert debug.read_layout().title() == TR.tutorial__title_handy_menu
|
||||||
assert layout.title() == TR.tutorial__title_handy_menu
|
|
||||||
|
|
||||||
layout = debug.click(buttons.CORNER_BUTTON)
|
debug.click(buttons.CORNER_BUTTON)
|
||||||
assert TR.tutorial__did_you_know in layout.text_content()
|
assert TR.tutorial__did_you_know in debug.read_layout().text_content()
|
||||||
layout = debug.click(buttons.CORNER_BUTTON)
|
debug.click(buttons.CORNER_BUTTON)
|
||||||
assert layout.title() == TR.tutorial__title_handy_menu
|
assert debug.read_layout().title() == TR.tutorial__title_handy_menu
|
||||||
|
|
||||||
layout = debug.swipe_up()
|
debug.swipe_up()
|
||||||
assert layout.title() == TR.tutorial__title_hold
|
assert debug.read_layout().title() == TR.tutorial__title_hold
|
||||||
layout = debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
|
debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
|
||||||
assert layout.title() == TR.tutorial__title_well_done
|
assert debug.read_layout().title() == TR.tutorial__title_well_done
|
||||||
debug.swipe_up()
|
debug.swipe_up()
|
||||||
|
|
||||||
device_handler.result()
|
device_handler.result()
|
||||||
@ -86,21 +84,20 @@ def test_tutorial_menu_exit(device_handler: "BackgroundDeviceHandler"):
|
|||||||
debug = device_handler.debuglink()
|
debug = device_handler.debuglink()
|
||||||
device_handler.run(device.show_device_tutorial)
|
device_handler.run(device.show_device_tutorial)
|
||||||
|
|
||||||
layout = debug.read_layout()
|
assert debug.read_layout().title() == TR.tutorial__welcome_safe5
|
||||||
assert layout.title() == TR.tutorial__welcome_safe5
|
debug.click(buttons.TAP_TO_CONFIRM)
|
||||||
layout = debug.click(buttons.TAP_TO_CONFIRM)
|
assert debug.read_layout().title() == TR.tutorial__title_lets_begin
|
||||||
assert layout.title() == TR.tutorial__title_lets_begin
|
debug.swipe_up()
|
||||||
layout = debug.swipe_up()
|
assert debug.read_layout().title() == TR.tutorial__title_easy_navigation
|
||||||
assert layout.title() == TR.tutorial__title_easy_navigation
|
debug.swipe_up()
|
||||||
layout = debug.swipe_up()
|
assert debug.read_layout().title() == TR.tutorial__title_handy_menu
|
||||||
assert layout.title() == TR.tutorial__title_handy_menu
|
|
||||||
|
|
||||||
layout = debug.click(buttons.CORNER_BUTTON)
|
debug.click(buttons.CORNER_BUTTON)
|
||||||
assert TR.tutorial__did_you_know in layout.text_content()
|
assert TR.tutorial__did_you_know in debug.read_layout().text_content()
|
||||||
layout = debug.click(buttons.VERTICAL_MENU[2])
|
debug.click(buttons.VERTICAL_MENU[2])
|
||||||
assert TR.instructions__hold_to_exit_tutorial in layout.footer()
|
assert TR.instructions__hold_to_exit_tutorial in debug.read_layout().footer()
|
||||||
layout = debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
|
debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
|
||||||
assert layout.title() == TR.tutorial__title_well_done
|
assert debug.read_layout().title() == TR.tutorial__title_well_done
|
||||||
debug.swipe_up()
|
debug.swipe_up()
|
||||||
|
|
||||||
device_handler.result()
|
device_handler.result()
|
||||||
@ -110,28 +107,27 @@ def test_tutorial_menu_repeat(device_handler: "BackgroundDeviceHandler"):
|
|||||||
debug = device_handler.debuglink()
|
debug = device_handler.debuglink()
|
||||||
device_handler.run(device.show_device_tutorial)
|
device_handler.run(device.show_device_tutorial)
|
||||||
|
|
||||||
layout = debug.read_layout()
|
assert debug.read_layout().title() == TR.tutorial__welcome_safe5
|
||||||
assert layout.title() == TR.tutorial__welcome_safe5
|
debug.click(buttons.TAP_TO_CONFIRM)
|
||||||
layout = debug.click(buttons.TAP_TO_CONFIRM)
|
assert debug.read_layout().title() == TR.tutorial__title_lets_begin
|
||||||
assert layout.title() == TR.tutorial__title_lets_begin
|
debug.swipe_up()
|
||||||
layout = debug.swipe_up()
|
assert debug.read_layout().title() == TR.tutorial__title_easy_navigation
|
||||||
assert layout.title() == TR.tutorial__title_easy_navigation
|
debug.swipe_up()
|
||||||
layout = debug.swipe_up()
|
assert debug.read_layout().title() == TR.tutorial__title_handy_menu
|
||||||
assert layout.title() == TR.tutorial__title_handy_menu
|
|
||||||
|
|
||||||
layout = debug.click(buttons.CORNER_BUTTON)
|
debug.click(buttons.CORNER_BUTTON)
|
||||||
assert TR.tutorial__did_you_know in layout.text_content()
|
assert TR.tutorial__did_you_know in debug.read_layout().text_content()
|
||||||
layout = debug.click(buttons.VERTICAL_MENU[1])
|
debug.click(buttons.VERTICAL_MENU[1])
|
||||||
|
|
||||||
assert layout.title() == TR.tutorial__title_lets_begin
|
assert debug.read_layout().title() == TR.tutorial__title_lets_begin
|
||||||
layout = debug.swipe_up()
|
debug.swipe_up()
|
||||||
assert layout.title() == TR.tutorial__title_easy_navigation
|
assert debug.read_layout().title() == TR.tutorial__title_easy_navigation
|
||||||
layout = debug.swipe_up()
|
debug.swipe_up()
|
||||||
assert layout.title() == TR.tutorial__title_handy_menu
|
assert debug.read_layout().title() == TR.tutorial__title_handy_menu
|
||||||
layout = debug.swipe_up()
|
debug.swipe_up()
|
||||||
assert layout.title() == TR.tutorial__title_hold
|
assert debug.read_layout().title() == TR.tutorial__title_hold
|
||||||
layout = debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
|
debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
|
||||||
assert layout.title() == TR.tutorial__title_well_done
|
assert debug.read_layout().title() == TR.tutorial__title_well_done
|
||||||
debug.swipe_up()
|
debug.swipe_up()
|
||||||
|
|
||||||
device_handler.result()
|
device_handler.result()
|
||||||
@ -141,29 +137,30 @@ def test_tutorial_menu_funfact(device_handler: "BackgroundDeviceHandler"):
|
|||||||
debug = device_handler.debuglink()
|
debug = device_handler.debuglink()
|
||||||
device_handler.run(device.show_device_tutorial)
|
device_handler.run(device.show_device_tutorial)
|
||||||
|
|
||||||
layout = debug.read_layout()
|
assert debug.read_layout().title() == TR.tutorial__welcome_safe5
|
||||||
assert layout.title() == TR.tutorial__welcome_safe5
|
debug.click(buttons.TAP_TO_CONFIRM)
|
||||||
layout = debug.click(buttons.TAP_TO_CONFIRM)
|
assert debug.read_layout().title() == TR.tutorial__title_lets_begin
|
||||||
assert layout.title() == TR.tutorial__title_lets_begin
|
debug.swipe_up()
|
||||||
layout = debug.swipe_up()
|
assert debug.read_layout().title() == TR.tutorial__title_easy_navigation
|
||||||
assert layout.title() == TR.tutorial__title_easy_navigation
|
debug.swipe_up()
|
||||||
layout = debug.swipe_up()
|
assert debug.read_layout().title() == TR.tutorial__title_handy_menu
|
||||||
assert layout.title() == TR.tutorial__title_handy_menu
|
|
||||||
|
|
||||||
layout = debug.click(buttons.CORNER_BUTTON)
|
debug.click(buttons.CORNER_BUTTON)
|
||||||
assert TR.tutorial__did_you_know in layout.text_content()
|
assert TR.tutorial__did_you_know in debug.read_layout().text_content()
|
||||||
layout = debug.click(buttons.VERTICAL_MENU[0])
|
debug.click(buttons.VERTICAL_MENU[0])
|
||||||
assert layout.text_content() in TR.tutorial__first_wallet.replace("\n", " ")
|
assert debug.read_layout().text_content() in TR.tutorial__first_wallet.replace(
|
||||||
|
"\n", " "
|
||||||
|
)
|
||||||
|
|
||||||
layout = debug.click(buttons.CORNER_BUTTON)
|
debug.click(buttons.CORNER_BUTTON)
|
||||||
assert TR.tutorial__did_you_know in layout.text_content()
|
assert TR.tutorial__did_you_know in debug.read_layout().text_content()
|
||||||
layout = debug.click(buttons.CORNER_BUTTON)
|
debug.click(buttons.CORNER_BUTTON)
|
||||||
assert layout.title() == TR.tutorial__title_handy_menu
|
assert debug.read_layout().title() == TR.tutorial__title_handy_menu
|
||||||
|
|
||||||
layout = debug.swipe_up()
|
debug.swipe_up()
|
||||||
assert layout.title() == TR.tutorial__title_hold
|
assert debug.read_layout().title() == TR.tutorial__title_hold
|
||||||
layout = debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
|
debug.click(buttons.TAP_TO_CONFIRM, hold_ms=1000)
|
||||||
assert layout.title() == TR.tutorial__title_well_done
|
assert debug.read_layout().title() == TR.tutorial__title_well_done
|
||||||
debug.swipe_up()
|
debug.swipe_up()
|
||||||
|
|
||||||
device_handler.result()
|
device_handler.result()
|
||||||
|
@ -323,7 +323,8 @@ def click_info_button_bolt(debug: "DebugLink") -> Generator[Any, Any, ButtonRequ
|
|||||||
|
|
||||||
def click_info_button_delizia(debug: "DebugLink"):
|
def click_info_button_delizia(debug: "DebugLink"):
|
||||||
"""Click Shamir backup info button and return back."""
|
"""Click Shamir backup info button and return back."""
|
||||||
layout = debug.click(buttons.CORNER_BUTTON)
|
debug.click(buttons.CORNER_BUTTON)
|
||||||
|
layout = debug.read_layout()
|
||||||
assert "VerticalMenu" in layout.all_components()
|
assert "VerticalMenu" in layout.all_components()
|
||||||
debug.click(buttons.VERTICAL_MENU[0])
|
debug.click(buttons.VERTICAL_MENU[0])
|
||||||
debug.click(buttons.CORNER_BUTTON)
|
debug.click(buttons.CORNER_BUTTON)
|
||||||
|
@ -375,7 +375,6 @@ def pytest_sessionfinish(session: pytest.Session, exitstatus: pytest.ExitCode) -
|
|||||||
exitstatus,
|
exitstatus,
|
||||||
test_ui, # type: ignore
|
test_ui, # type: ignore
|
||||||
bool(session.config.getoption("ui_check_missing")),
|
bool(session.config.getoption("ui_check_missing")),
|
||||||
bool(session.config.getoption("record_text_layout")),
|
|
||||||
bool(session.config.getoption("do_master_diff")),
|
bool(session.config.getoption("do_master_diff")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -347,10 +347,12 @@ class InputFlowShowAddressQRCode(InputFlowBase):
|
|||||||
# really cancel
|
# really cancel
|
||||||
self.debug.click(buttons.CORNER_BUTTON)
|
self.debug.click(buttons.CORNER_BUTTON)
|
||||||
# menu
|
# menu
|
||||||
layout = self.debug.click(buttons.CORNER_BUTTON)
|
self.debug.click(buttons.CORNER_BUTTON)
|
||||||
|
|
||||||
|
layout = self.debug.read_layout()
|
||||||
while "PromptScreen" not in layout.all_components():
|
while "PromptScreen" not in layout.all_components():
|
||||||
layout = self.debug.swipe_up()
|
self.debug.swipe_up()
|
||||||
|
layout = self.debug.read_layout()
|
||||||
self.debug.synchronize_at("PromptScreen")
|
self.debug.synchronize_at("PromptScreen")
|
||||||
# tap to confirm
|
# tap to confirm
|
||||||
self.debug.click(buttons.TAP_TO_CONFIRM)
|
self.debug.click(buttons.TAP_TO_CONFIRM)
|
||||||
@ -437,14 +439,16 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
|
|||||||
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()
|
self.debug.swipe_left()
|
||||||
|
layout = self.debug.read_layout()
|
||||||
# address details
|
# address details
|
||||||
assert "Multisig 2 of 3" in layout.screen_content()
|
assert "Multisig 2 of 3" in layout.screen_content()
|
||||||
assert TR.address_details__derivation_path in layout.screen_content()
|
assert TR.address_details__derivation_path in layout.screen_content()
|
||||||
|
|
||||||
# Three xpub pages with the same testing logic
|
# Three xpub pages with the same testing logic
|
||||||
for xpub_num in range(3):
|
for xpub_num in range(3):
|
||||||
layout = self.debug.swipe_left()
|
self.debug.swipe_left()
|
||||||
|
layout = self.debug.read_layout()
|
||||||
self._assert_xpub_title(layout.title(), xpub_num)
|
self._assert_xpub_title(layout.title(), xpub_num)
|
||||||
content = layout.text_content().replace(" ", "")
|
content = layout.text_content().replace(" ", "")
|
||||||
assert self.xpubs[xpub_num] in content
|
assert self.xpubs[xpub_num] in content
|
||||||
@ -470,18 +474,21 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
|
|||||||
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()
|
self.debug.press_right()
|
||||||
|
layout = self.debug.read_layout()
|
||||||
# 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
|
||||||
|
|
||||||
# Three xpub pages with the same testing logic
|
# Three xpub pages with the same testing logic
|
||||||
for xpub_num in range(3):
|
for xpub_num in range(3):
|
||||||
layout = self.debug.press_right()
|
self.debug.press_right()
|
||||||
|
layout = self.debug.read_layout()
|
||||||
self._assert_xpub_title(layout.title(), xpub_num)
|
self._assert_xpub_title(layout.title(), xpub_num)
|
||||||
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()
|
self.debug.press_middle()
|
||||||
|
layout = self.debug.read_layout()
|
||||||
xpub_part_2 = layout.text_content().replace(" ", "")
|
xpub_part_2 = layout.text_content().replace(" ", "")
|
||||||
# Go back
|
# Go back
|
||||||
self.debug.press_left()
|
self.debug.press_left()
|
||||||
@ -525,24 +532,25 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
|
|||||||
|
|
||||||
# three xpub pages with the same testing logic
|
# three xpub pages with the same testing logic
|
||||||
for _xpub_num in range(3):
|
for _xpub_num in range(3):
|
||||||
layout = self.debug.swipe_left()
|
self.debug.swipe_left()
|
||||||
layout = self.debug.swipe_left()
|
self.debug.swipe_left()
|
||||||
|
|
||||||
self.debug.click(buttons.CORNER_BUTTON)
|
self.debug.click(buttons.CORNER_BUTTON)
|
||||||
layout = self.debug.synchronize_at("VerticalMenu")
|
self.debug.synchronize_at("VerticalMenu")
|
||||||
# menu
|
# menu
|
||||||
self.debug.click(buttons.VERTICAL_MENU[2])
|
self.debug.click(buttons.VERTICAL_MENU[2])
|
||||||
# cancel
|
# cancel
|
||||||
self.debug.swipe_up()
|
self.debug.swipe_up()
|
||||||
# really cancel
|
# really cancel
|
||||||
self.debug.click(buttons.CORNER_BUTTON)
|
self.debug.click(buttons.CORNER_BUTTON)
|
||||||
layout = self.debug.synchronize_at("VerticalMenu")
|
self.debug.synchronize_at("VerticalMenu")
|
||||||
# menu
|
# menu
|
||||||
self.debug.click(buttons.CORNER_BUTTON)
|
self.debug.click(buttons.CORNER_BUTTON)
|
||||||
layout = self.debug.synchronize_at("Paragraphs")
|
layout = self.debug.synchronize_at("Paragraphs")
|
||||||
# address
|
# address
|
||||||
while "PromptScreen" not in layout.all_components():
|
while "PromptScreen" not in layout.all_components():
|
||||||
layout = self.debug.swipe_up()
|
self.debug.swipe_up()
|
||||||
|
layout = self.debug.read_layout()
|
||||||
self.debug.synchronize_at("PromptScreen")
|
self.debug.synchronize_at("PromptScreen")
|
||||||
# tap to confirm
|
# tap to confirm
|
||||||
self.debug.press_yes()
|
self.debug.press_yes()
|
||||||
@ -652,7 +660,8 @@ class InputFlowShowXpubQRCode(InputFlowBase):
|
|||||||
layout = self.debug.synchronize_at("Paragraphs")
|
layout = self.debug.synchronize_at("Paragraphs")
|
||||||
# address
|
# address
|
||||||
while "PromptScreen" not in layout.all_components():
|
while "PromptScreen" not in layout.all_components():
|
||||||
layout = self.debug.swipe_up()
|
self.debug.swipe_up()
|
||||||
|
layout = self.debug.read_layout()
|
||||||
self.debug.synchronize_at("PromptScreen")
|
self.debug.synchronize_at("PromptScreen")
|
||||||
# tap to confirm
|
# tap to confirm
|
||||||
self.debug.press_yes()
|
self.debug.press_yes()
|
||||||
@ -834,10 +843,12 @@ def sign_tx_go_to_info_tr(
|
|||||||
client.debug.press_middle()
|
client.debug.press_middle()
|
||||||
yield
|
yield
|
||||||
|
|
||||||
layout = client.debug.press_right()
|
client.debug.press_right()
|
||||||
|
layout = client.debug.read_layout()
|
||||||
screen_texts.append(layout.visible_screen())
|
screen_texts.append(layout.visible_screen())
|
||||||
|
|
||||||
layout = client.debug.press_right()
|
client.debug.press_right()
|
||||||
|
layout = client.debug.read_layout()
|
||||||
screen_texts.append(layout.visible_screen())
|
screen_texts.append(layout.visible_screen())
|
||||||
|
|
||||||
client.debug.press_left()
|
client.debug.press_left()
|
||||||
|
@ -179,7 +179,8 @@ class RecoveryFlow:
|
|||||||
self.debug.synchronize_at("VerticalMenu")
|
self.debug.synchronize_at("VerticalMenu")
|
||||||
self.debug.click(buttons.VERTICAL_MENU[0])
|
self.debug.click(buttons.VERTICAL_MENU[0])
|
||||||
assert (yield).name == "abort_recovery"
|
assert (yield).name == "abort_recovery"
|
||||||
layout = self.debug.swipe_up()
|
self.debug.swipe_up()
|
||||||
|
layout = self.debug.read_layout()
|
||||||
assert layout.title() == TR.recovery__title_cancel_recovery
|
assert layout.title() == TR.recovery__title_cancel_recovery
|
||||||
self.debug.click(buttons.TAP_TO_CONFIRM)
|
self.debug.click(buttons.TAP_TO_CONFIRM)
|
||||||
else:
|
else:
|
||||||
|
@ -44,8 +44,6 @@ def screen_recording(
|
|||||||
yield
|
yield
|
||||||
return
|
return
|
||||||
|
|
||||||
record_text_layout = request.config.getoption("record_text_layout")
|
|
||||||
|
|
||||||
testcase = TestCase.build(client, request)
|
testcase = TestCase.build(client, request)
|
||||||
testcase.dir.mkdir(exist_ok=True, parents=True)
|
testcase.dir.mkdir(exist_ok=True, parents=True)
|
||||||
|
|
||||||
@ -55,9 +53,6 @@ def screen_recording(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
client.debug.start_recording(str(testcase.actual_dir))
|
client.debug.start_recording(str(testcase.actual_dir))
|
||||||
if record_text_layout:
|
|
||||||
client.debug.set_screen_text_file(testcase.screen_text_file)
|
|
||||||
client.debug.watch_layout(True)
|
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
client.ensure_open()
|
client.ensure_open()
|
||||||
@ -65,9 +60,6 @@ def screen_recording(
|
|||||||
# Wait for response to Initialize, which gives the emulator time to catch up
|
# Wait for response to Initialize, which gives the emulator time to catch up
|
||||||
# and redraw the homescreen. Otherwise there's a race condition between that
|
# and redraw the homescreen. Otherwise there's a race condition between that
|
||||||
# and stopping recording.
|
# and stopping recording.
|
||||||
if record_text_layout:
|
|
||||||
client.debug.set_screen_text_file(None)
|
|
||||||
client.debug.watch_layout(False)
|
|
||||||
client.init_device()
|
client.init_device()
|
||||||
client.debug.stop_recording()
|
client.debug.stop_recording()
|
||||||
|
|
||||||
@ -163,13 +155,12 @@ def sessionfinish(
|
|||||||
exitstatus: pytest.ExitCode,
|
exitstatus: pytest.ExitCode,
|
||||||
test_ui: str,
|
test_ui: str,
|
||||||
check_missing: bool,
|
check_missing: bool,
|
||||||
record_text_layout: bool,
|
|
||||||
do_master_diff: bool,
|
do_master_diff: bool,
|
||||||
) -> pytest.ExitCode:
|
) -> pytest.ExitCode:
|
||||||
if not _should_write_ui_report(exitstatus):
|
if not _should_write_ui_report(exitstatus):
|
||||||
return exitstatus
|
return exitstatus
|
||||||
|
|
||||||
testreport.generate_reports(record_text_layout, do_master_diff)
|
testreport.generate_reports(do_master_diff)
|
||||||
|
|
||||||
recents = list(TestResult.recent_results())
|
recents = list(TestResult.recent_results())
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ from collections import defaultdict
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import dominate
|
|
||||||
import dominate.tags as t
|
import dominate.tags as t
|
||||||
from dominate.tags import (
|
from dominate.tags import (
|
||||||
a,
|
a,
|
||||||
@ -31,7 +30,6 @@ from .common import REPORTS_PATH, document, generate_master_diff_report, get_dif
|
|||||||
|
|
||||||
TESTREPORT_PATH = REPORTS_PATH / "test"
|
TESTREPORT_PATH = REPORTS_PATH / "test"
|
||||||
IMAGES_PATH = TESTREPORT_PATH / "images"
|
IMAGES_PATH = TESTREPORT_PATH / "images"
|
||||||
SCREEN_TEXT_FILE = TESTREPORT_PATH / "screen_text.txt"
|
|
||||||
|
|
||||||
# These two html files are referencing each other
|
# These two html files are referencing each other
|
||||||
ALL_SCREENS = "all_screens.html"
|
ALL_SCREENS = "all_screens.html"
|
||||||
@ -201,35 +199,6 @@ def all_unique_screens() -> Path:
|
|||||||
return html.write(TESTREPORT_PATH, doc, ALL_UNIQUE_SCREENS)
|
return html.write(TESTREPORT_PATH, doc, ALL_UNIQUE_SCREENS)
|
||||||
|
|
||||||
|
|
||||||
def screen_text_report() -> None:
|
|
||||||
"""Generate a report with text representation of all screens."""
|
|
||||||
recent_results = list(TestResult.recent_results())
|
|
||||||
|
|
||||||
# Creating both a text file (suitable for offline usage)
|
|
||||||
# and an HTML file (suitable for online usage).
|
|
||||||
|
|
||||||
with open(SCREEN_TEXT_FILE, "w") as f2:
|
|
||||||
for result in recent_results:
|
|
||||||
if not result.test.screen_text_file.exists():
|
|
||||||
continue
|
|
||||||
f2.write(f"\n{result.test.id}\n")
|
|
||||||
with open(result.test.screen_text_file, "r") as f:
|
|
||||||
for line in f.readlines():
|
|
||||||
f2.write(f"\t{line}")
|
|
||||||
|
|
||||||
doc = dominate.document(title="Screen text report")
|
|
||||||
with doc:
|
|
||||||
for result in recent_results:
|
|
||||||
if not result.test.screen_text_file.exists():
|
|
||||||
continue
|
|
||||||
with a(href=f"{ALL_SCREENS}#{result.test.id}"):
|
|
||||||
h2(result.test.id)
|
|
||||||
with open(result.test.screen_text_file, "r") as f:
|
|
||||||
for line in f.readlines():
|
|
||||||
p(line)
|
|
||||||
html.write(TESTREPORT_PATH, doc, "screen_text.html")
|
|
||||||
|
|
||||||
|
|
||||||
def differing_screens() -> None:
|
def differing_screens() -> None:
|
||||||
"""Creating an HTML page showing all the unique screens that got changed."""
|
"""Creating an HTML page showing all the unique screens that got changed."""
|
||||||
unique_diffs: set[tuple[str | None, str | None]] = set()
|
unique_diffs: set[tuple[str | None, str | None]] = set()
|
||||||
@ -316,17 +285,13 @@ def master_index() -> Path:
|
|||||||
return html.write(TESTREPORT_PATH, doc, "master_index.html")
|
return html.write(TESTREPORT_PATH, doc, "master_index.html")
|
||||||
|
|
||||||
|
|
||||||
def generate_reports(
|
def generate_reports(do_master_diff: bool = False) -> None:
|
||||||
do_screen_text: bool = False, do_master_diff: bool = False
|
|
||||||
) -> None:
|
|
||||||
"""Generate HTML reports for the test."""
|
"""Generate HTML reports for the test."""
|
||||||
html.set_image_dir(IMAGES_PATH)
|
html.set_image_dir(IMAGES_PATH)
|
||||||
index()
|
index()
|
||||||
all_screens()
|
all_screens()
|
||||||
all_unique_screens()
|
all_unique_screens()
|
||||||
differing_screens()
|
differing_screens()
|
||||||
if do_screen_text:
|
|
||||||
screen_text_report()
|
|
||||||
if do_master_diff:
|
if do_master_diff:
|
||||||
master_diff()
|
master_diff()
|
||||||
master_index()
|
master_index()
|
||||||
|
@ -11,17 +11,21 @@ def _enter_word(
|
|||||||
) -> "LayoutContent":
|
) -> "LayoutContent":
|
||||||
typed_word = word[:4]
|
typed_word = word[:4]
|
||||||
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, wait=False)
|
debug.click(coords)
|
||||||
|
debug.read_layout(wait=False)
|
||||||
|
|
||||||
return debug.click(buttons.CONFIRM_WORD, wait=True)
|
debug.click(buttons.CONFIRM_WORD)
|
||||||
|
return debug.read_layout(wait=True)
|
||||||
|
|
||||||
|
|
||||||
def confirm_recovery(debug: "DebugLink") -> None:
|
def confirm_recovery(debug: "DebugLink") -> None:
|
||||||
debug.click(buttons.OK)
|
debug.click(buttons.OK)
|
||||||
|
debug.read_layout(wait=True)
|
||||||
|
|
||||||
|
|
||||||
def select_number_of_words(debug: "DebugLink", num_of_words: int = 20) -> None:
|
def select_number_of_words(debug: "DebugLink", num_of_words: int = 20) -> None:
|
||||||
debug.click(buttons.OK)
|
debug.click(buttons.OK)
|
||||||
|
debug.read_layout(wait=True)
|
||||||
|
|
||||||
# click the number
|
# click the number
|
||||||
word_option_offset = 6
|
word_option_offset = 6
|
||||||
@ -31,12 +35,12 @@ def select_number_of_words(debug: "DebugLink", num_of_words: int = 20) -> None:
|
|||||||
) # 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)
|
||||||
debug.click(coords)
|
debug.click(coords)
|
||||||
|
debug.read_layout(wait=True)
|
||||||
|
|
||||||
|
|
||||||
def enter_share(
|
def enter_share(debug: "DebugLink", share: str) -> "LayoutContent":
|
||||||
debug: "DebugLink", share: str, is_first: bool = True
|
debug.click(buttons.OK)
|
||||||
) -> "LayoutContent":
|
|
||||||
layout = debug.click(buttons.OK)
|
|
||||||
for word in share.split(" "):
|
for word in share.split(" "):
|
||||||
layout = _enter_word(debug, word, is_slip39=True)
|
_enter_word(debug, word, is_slip39=True)
|
||||||
return layout
|
|
||||||
|
return debug.read_layout(wait=True)
|
||||||
|
Loading…
Reference in New Issue
Block a user