mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 04:18:10 +00:00
fix(core/ui): T3T1 device tests
[no changelog]
This commit is contained in:
parent
c03781aef9
commit
69e406f7cf
2
.github/workflows/core.yml
vendored
2
.github/workflows/core.yml
vendored
@ -266,6 +266,7 @@ jobs:
|
||||
needs:
|
||||
- param
|
||||
- core_emu
|
||||
timeout-minutes: 90
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@ -350,6 +351,7 @@ jobs:
|
||||
needs:
|
||||
- param
|
||||
- core_emu
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
@ -569,8 +569,8 @@ impl EventCtx {
|
||||
}
|
||||
|
||||
pub fn set_page_count(&mut self, count: usize) {
|
||||
#[cfg(feature = "ui_debug")]
|
||||
assert!(self.page_count.is_none());
|
||||
// #[cfg(feature = "ui_debug")]
|
||||
// assert!(self.page_count.unwrap_or(count) == count);
|
||||
self.page_count = Some(count);
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,7 @@ impl<T: Component + Paginate + Clone> Component for SwipePage<T> {
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
ctx.set_page_count(self.pages);
|
||||
if let Some(t) = &mut self.transition {
|
||||
let finished = Self::handle_transition(ctx, event, t);
|
||||
if finished {
|
||||
|
@ -136,7 +136,7 @@ where
|
||||
}
|
||||
|
||||
pub fn with_description(self, description: impl Into<TString<'static>>) -> Self {
|
||||
self.with_text(&theme::TEXT_NORMAL_OFF_WHITE, description)
|
||||
self.with_text(&theme::TEXT_NORMAL_GREY_EXTRA_LIGHT, description)
|
||||
}
|
||||
|
||||
pub fn with_value(self, value: impl Into<TString<'static>>) -> Self {
|
||||
@ -154,9 +154,9 @@ where
|
||||
theme::BG,
|
||||
)),
|
||||
paragraphs: ParagraphVecShort::from_iter([
|
||||
Paragraph::new(&theme::TEXT_NORMAL_OFF_WHITE, l0).centered(),
|
||||
Paragraph::new(&theme::TEXT_NORMAL_GREY_EXTRA_LIGHT, l0).centered(),
|
||||
Paragraph::new(&theme::TEXT_DEMIBOLD, l1).centered(),
|
||||
Paragraph::new(&theme::TEXT_NORMAL_OFF_WHITE, l2).centered(),
|
||||
Paragraph::new(&theme::TEXT_NORMAL_GREY_EXTRA_LIGHT, l2).centered(),
|
||||
Paragraph::new(&theme::TEXT_DEMIBOLD, l3).centered(),
|
||||
])
|
||||
.into_paragraphs()
|
||||
|
@ -76,7 +76,7 @@ pub struct PassphraseKeyboard {
|
||||
input_prompt: Child<Label<'static>>,
|
||||
erase_btn: Child<Maybe<Button>>,
|
||||
cancel_btn: Child<Maybe<Button>>,
|
||||
confirm_btn: Child<Maybe<Button>>,
|
||||
confirm_btn: Child<Button>,
|
||||
next_btn: Child<Button>,
|
||||
keys: [Child<Button>; KEY_COUNT],
|
||||
active_layout: KeyboardLayout,
|
||||
@ -87,8 +87,8 @@ const PAGE_COUNT: usize = 4;
|
||||
const KEY_COUNT: usize = 10;
|
||||
#[rustfmt::skip]
|
||||
const KEYBOARD: [[&str; KEY_COUNT]; PAGE_COUNT] = [
|
||||
["abc", "def", "ghi", "jkl", "mno", "pq", "rst", "uvq", "xyz", " *#"],
|
||||
["ABC", "DEF", "GHI", "JKL", "MNO", "PQ", "RST", "UVQ", "XYZ", " *#"],
|
||||
["abc", "def", "ghi", "jkl", "mno", "pq", "rst", "uvw", "xyz", " *#"],
|
||||
["ABC", "DEF", "GHI", "JKL", "MNO", "PQ", "RST", "UVW", "XYZ", " *#"],
|
||||
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
|
||||
["_<>", ".:@", "/|\\", "!()", "+%&", "-[]", "?{}", ",'`", ";\"~", "$^="],
|
||||
];
|
||||
@ -102,8 +102,7 @@ impl PassphraseKeyboard {
|
||||
let confirm_btn = Button::with_icon(theme::ICON_CONFIRM)
|
||||
.styled(theme::button_passphrase_confirm())
|
||||
.with_radius(15)
|
||||
.initially_enabled(false);
|
||||
let confirm_btn = Maybe::hidden(theme::BG, confirm_btn).into_child();
|
||||
.into_child();
|
||||
|
||||
let next_btn = Button::new(active_layout.next().into())
|
||||
.styled(theme::button_passphrase_next())
|
||||
@ -206,10 +205,6 @@ impl PassphraseKeyboard {
|
||||
btn.show_if(ctx, is_empty);
|
||||
btn.inner_mut().enable_if(ctx, is_empty);
|
||||
});
|
||||
self.confirm_btn.mutate(ctx, |ctx, btn| {
|
||||
btn.show_if(ctx, !is_empty);
|
||||
btn.inner_mut().enable_if(ctx, !is_empty);
|
||||
});
|
||||
|
||||
self.update_input_btns_state(ctx);
|
||||
}
|
||||
@ -381,11 +376,11 @@ impl Component for PassphraseKeyboard {
|
||||
self.input.render(target);
|
||||
self.next_btn.render(target);
|
||||
self.erase_btn.render(target);
|
||||
self.confirm_btn.render(target);
|
||||
if self.input.inner().textbox.is_empty() {
|
||||
self.cancel_btn.render(target);
|
||||
self.input_prompt.render(target);
|
||||
} else {
|
||||
self.confirm_btn.render(target);
|
||||
// FIXME: when prompt fixed in Figma
|
||||
// self.input_prompt.render(target);
|
||||
}
|
||||
for btn in &self.keys {
|
||||
btn.render(target);
|
||||
|
@ -6,6 +6,7 @@ use crate::{
|
||||
geometry::{Alignment2D, Offset, Rect},
|
||||
shape,
|
||||
shape::Renderer,
|
||||
util::animation_disabled,
|
||||
},
|
||||
};
|
||||
|
||||
@ -97,6 +98,9 @@ impl Component for PromptScreen {
|
||||
(DismissType::Hold, Some(ButtonMsg::LongPressed)) => {
|
||||
return Some(());
|
||||
}
|
||||
(DismissType::Hold, Some(ButtonMsg::Clicked)) if animation_disabled() => {
|
||||
return Some(());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
None
|
||||
|
@ -205,5 +205,6 @@ impl<'a> crate::trace::Trace for ShareWords<'a> {
|
||||
let content =
|
||||
word.map(|w| build_string!(50, inttostr!(self.page_index as u8 + 1), ". ", w, "\n"));
|
||||
t.string("screen_content", content.as_str().into());
|
||||
t.int("page_count", self.share_words.len() as i64)
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,10 @@ use crate::{
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
button_request::ButtonRequestCode,
|
||||
component::{
|
||||
text::paragraphs::{Paragraph, Paragraphs},
|
||||
ComponentExt, SwipeDirection,
|
||||
ButtonRequestExt, ComponentExt, SwipeDirection,
|
||||
},
|
||||
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
|
||||
},
|
||||
@ -86,7 +87,8 @@ impl ConfirmResetCreate {
|
||||
let content_intro = Frame::left_aligned(title, SwipePage::vertical(paragraphs))
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info));
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info))
|
||||
.one_button_request(ButtonRequestCode::ResetDevice.with_type("setup_device"));
|
||||
|
||||
let content_menu = Frame::left_aligned(
|
||||
"".into(),
|
||||
@ -106,7 +108,8 @@ impl ConfirmResetCreate {
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||
_ => Some(FlowMsg::Cancelled),
|
||||
});
|
||||
})
|
||||
.one_button_request(ButtonRequestCode::ResetDevice.with_type("confirm_setup_device"));
|
||||
|
||||
let store = flow_store()
|
||||
.add(content_intro)?
|
||||
|
@ -2,9 +2,10 @@ use crate::{
|
||||
error,
|
||||
translations::TR,
|
||||
ui::{
|
||||
button_request::ButtonRequestCode,
|
||||
component::{
|
||||
text::paragraphs::{Paragraph, Paragraphs},
|
||||
ComponentExt, SwipeDirection,
|
||||
ButtonRequestExt, ComponentExt, SwipeDirection,
|
||||
},
|
||||
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
|
||||
},
|
||||
@ -80,7 +81,8 @@ impl ConfirmResetRecover {
|
||||
)
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info));
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info))
|
||||
.one_button_request(ButtonRequestCode::ProtectCall.with_type("recover_device"));
|
||||
|
||||
let content_menu = Frame::left_aligned(
|
||||
"".into(),
|
||||
|
@ -4,9 +4,10 @@ use crate::{
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
button_request::ButtonRequestCode,
|
||||
component::{
|
||||
text::paragraphs::{Paragraph, Paragraphs},
|
||||
ComponentExt, SwipeDirection,
|
||||
ButtonRequestExt, ComponentExt, SwipeDirection,
|
||||
},
|
||||
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
|
||||
layout::obj::LayoutObj,
|
||||
@ -79,6 +80,7 @@ impl ShowShareWords {
|
||||
let share_words_vec: Vec<TString, 33> = util::iter_into_vec(share_words_obj)?;
|
||||
let text_info: TString = kwargs.get(Qstr::MP_QSTR_text_info)?.try_into()?;
|
||||
let text_confirm: TString = kwargs.get(Qstr::MP_QSTR_text_confirm)?.try_into()?;
|
||||
let nwords = share_words_vec.len();
|
||||
|
||||
let content_instruction = Frame::left_aligned(
|
||||
title,
|
||||
@ -89,7 +91,9 @@ impl ShowShareWords {
|
||||
)
|
||||
.with_subtitle(TR::words__instructions.into())
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.map(|msg| matches!(msg, FrameMsg::Content(_)).then_some(FlowMsg::Confirmed));
|
||||
.map(|msg| matches!(msg, FrameMsg::Content(_)).then_some(FlowMsg::Confirmed))
|
||||
.one_button_request(ButtonRequestCode::ResetDevice.with_type("share_words"))
|
||||
.with_pages(move |_| nwords + 2);
|
||||
|
||||
let content_words = Frame::left_aligned(title, ShareWords::new(share_words_vec))
|
||||
.with_subtitle(subtitle)
|
||||
|
@ -47,11 +47,7 @@ pub const ORANGE_LIGHT: Color = Color::rgb(0xFF, 0x8D, 0x6A); // cancel button
|
||||
pub const RED: Color = Color::rgb(0xE7, 0x0E, 0x0E); // button
|
||||
pub const RED_DARK: Color = Color::rgb(0xAE, 0x09, 0x09); // button pressed
|
||||
pub const YELLOW: Color = Color::rgb(0xD9, 0x9E, 0x00); // button
|
||||
pub const YELLOW_DARK: Color = Color::rgb(0x7A, 0x58, 0x00); // button pressed
|
||||
pub const BLUE: Color = Color::rgb(0x06, 0x1E, 0xAD); // button
|
||||
pub const BLUE_DARK: Color = Color::rgb(0x04, 0x10, 0x58); // button pressed
|
||||
pub const OFF_WHITE: Color = Color::rgb(0xDE, 0xDE, 0xDE); // very light grey
|
||||
pub const GREY_MEDIUM: Color = Color::rgb(0x4F, 0x4F, 0x4F); // button pressed
|
||||
pub const VIOLET: Color = Color::rgb(0x95, 0x00, 0xCA);
|
||||
|
||||
pub const FATAL_ERROR_COLOR: Color = Color::rgb(0xE7, 0x0E, 0x0E);
|
||||
@ -189,7 +185,7 @@ pub const fn label_default() -> TextStyle {
|
||||
}
|
||||
|
||||
pub const fn label_keyboard() -> TextStyle {
|
||||
TextStyle::new(Font::DEMIBOLD, OFF_WHITE, BG, GREY_LIGHT, GREY_LIGHT)
|
||||
TextStyle::new(Font::DEMIBOLD, GREY_EXTRA_LIGHT, BG, GREY_LIGHT, GREY_LIGHT)
|
||||
}
|
||||
|
||||
pub const fn label_keyboard_prompt() -> TextStyle {
|
||||
@ -197,11 +193,11 @@ pub const fn label_keyboard_prompt() -> TextStyle {
|
||||
}
|
||||
|
||||
pub const fn label_keyboard_warning() -> TextStyle {
|
||||
TextStyle::new(Font::DEMIBOLD, RED, BG, GREY_LIGHT, GREY_LIGHT)
|
||||
TextStyle::new(Font::DEMIBOLD, ORANGE_LIGHT, BG, GREY_LIGHT, GREY_LIGHT)
|
||||
}
|
||||
|
||||
pub const fn label_keyboard_minor() -> TextStyle {
|
||||
TEXT_NORMAL_OFF_WHITE
|
||||
TEXT_NORMAL_GREY_EXTRA_LIGHT
|
||||
}
|
||||
|
||||
pub const fn label_warning() -> TextStyle {
|
||||
@ -209,7 +205,7 @@ pub const fn label_warning() -> TextStyle {
|
||||
}
|
||||
|
||||
pub const fn label_warning_value() -> TextStyle {
|
||||
TEXT_NORMAL_OFF_WHITE
|
||||
TEXT_NORMAL_GREY_EXTRA_LIGHT
|
||||
}
|
||||
|
||||
pub const fn label_recovery_title() -> TextStyle {
|
||||
@ -217,7 +213,7 @@ pub const fn label_recovery_title() -> TextStyle {
|
||||
}
|
||||
|
||||
pub const fn label_recovery_description() -> TextStyle {
|
||||
TEXT_NORMAL_OFF_WHITE
|
||||
TEXT_NORMAL_GREY_EXTRA_LIGHT
|
||||
}
|
||||
|
||||
pub const fn label_progress() -> TextStyle {
|
||||
@ -363,21 +359,21 @@ pub const fn button_cancel() -> ButtonStyleSheet {
|
||||
normal: &ButtonStyle {
|
||||
font: Font::BOLD,
|
||||
text_color: FG,
|
||||
button_color: RED,
|
||||
button_color: ORANGE_LIGHT,
|
||||
icon_color: GREY_LIGHT,
|
||||
background_color: BG,
|
||||
},
|
||||
active: &ButtonStyle {
|
||||
font: Font::BOLD,
|
||||
text_color: FG,
|
||||
button_color: RED_DARK,
|
||||
button_color: ORANGE_DIMMED,
|
||||
icon_color: GREY_LIGHT,
|
||||
background_color: BG,
|
||||
},
|
||||
disabled: &ButtonStyle {
|
||||
font: Font::BOLD,
|
||||
text_color: GREY_LIGHT,
|
||||
button_color: RED,
|
||||
button_color: ORANGE_DIMMED,
|
||||
icon_color: GREY_LIGHT,
|
||||
background_color: BG,
|
||||
},
|
||||
@ -791,8 +787,8 @@ pub fn textstyle_number(num: i32) -> &'static TextStyle {
|
||||
}
|
||||
}
|
||||
|
||||
pub const TEXT_NORMAL_OFF_WHITE: TextStyle =
|
||||
TextStyle::new(Font::NORMAL, OFF_WHITE, BG, GREY_LIGHT, GREY_LIGHT);
|
||||
pub const TEXT_NORMAL_GREY_EXTRA_LIGHT: TextStyle =
|
||||
TextStyle::new(Font::NORMAL, GREY_EXTRA_LIGHT, BG, GREY_LIGHT, GREY_LIGHT);
|
||||
pub const TEXT_CHECKLIST_DEFAULT: TextStyle = TextStyle::new(Font::SUB, GREY, BG, GREY, GREY);
|
||||
pub const TEXT_CHECKLIST_SELECTED: TextStyle =
|
||||
TextStyle::new(Font::NORMAL, GREY_LIGHT, BG, GREY_LIGHT, GREY_LIGHT);
|
||||
|
@ -91,6 +91,7 @@ async def request_mnemonic(
|
||||
# show_identifier_mismatch
|
||||
await show_recovery_warning(
|
||||
"warning_mismatched_share",
|
||||
"",
|
||||
TR.recovery__share_from_another_multi_share_backup,
|
||||
)
|
||||
return None
|
||||
@ -129,14 +130,14 @@ async def show_invalid_mnemonic(word_count: int) -> None:
|
||||
if backup_types.is_slip39_word_count(word_count):
|
||||
await show_recovery_warning(
|
||||
"warning_invalid_share",
|
||||
TR.recovery__invalid_share_entered,
|
||||
TR.words__please_try_again,
|
||||
TR.recovery__invalid_share_entered,
|
||||
)
|
||||
else:
|
||||
await show_recovery_warning(
|
||||
"warning_invalid_seed",
|
||||
TR.recovery__invalid_wallet_backup_entered,
|
||||
TR.words__please_try_again,
|
||||
TR.recovery__invalid_wallet_backup_entered,
|
||||
)
|
||||
|
||||
|
||||
|
@ -348,19 +348,11 @@ async def confirm_single(
|
||||
async def confirm_reset_device(_title: str, recovery: bool = False) -> None:
|
||||
if recovery:
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(trezorui2.flow_confirm_reset_recover()),
|
||||
"recover_device",
|
||||
ButtonRequestType.ProtectCall,
|
||||
)
|
||||
RustLayout(trezorui2.flow_confirm_reset_recover()),
|
||||
)
|
||||
else:
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(trezorui2.flow_confirm_reset_create()),
|
||||
"setup_device",
|
||||
ButtonRequestType.ResetDevice,
|
||||
)
|
||||
RustLayout(trezorui2.flow_confirm_reset_create()),
|
||||
)
|
||||
|
||||
|
||||
|
@ -158,8 +158,10 @@ async def show_recovery_warning(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.show_warning(
|
||||
title=content,
|
||||
value="Try again", # TODO: use TR
|
||||
title=content or TR.words__warning,
|
||||
value=subheader or "",
|
||||
button=button,
|
||||
description="",
|
||||
)
|
||||
),
|
||||
br_type,
|
||||
|
@ -35,18 +35,14 @@ async def show_share_words(
|
||||
text_info = TR.reset__write_down_words_template.format(words_count)
|
||||
text_confirm = TR.reset__words_written_down_template.format(words_count)
|
||||
|
||||
result = await interact(
|
||||
RustLayout(
|
||||
trezorui2.flow_show_share_words(
|
||||
title=title,
|
||||
subtitle=subtitle,
|
||||
words=share_words,
|
||||
text_info=text_info,
|
||||
text_confirm=text_confirm,
|
||||
)
|
||||
),
|
||||
"backup_words",
|
||||
ButtonRequestType.ResetDevice,
|
||||
result = await RustLayout(
|
||||
trezorui2.flow_show_share_words(
|
||||
title=title,
|
||||
subtitle=subtitle,
|
||||
words=share_words,
|
||||
text_info=text_info,
|
||||
text_confirm=text_confirm,
|
||||
)
|
||||
)
|
||||
|
||||
if result != CONFIRMED:
|
||||
@ -341,10 +337,10 @@ async def show_reset_warning(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.show_warning(
|
||||
title=subheader or "",
|
||||
title=content or TR.words__warning,
|
||||
description="",
|
||||
value=content,
|
||||
button="",
|
||||
value=subheader or "",
|
||||
button=button,
|
||||
allow_cancel=False,
|
||||
)
|
||||
),
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"current": {
|
||||
"merkle_root": "e79fdd3c9052dfd140aef2b4800b6b0b8000ae99822cf3e8be083da0dc2b376c",
|
||||
"datetime": "2024-05-26T15:18:17.124044",
|
||||
"commit": "3e9598245d57044418f4eeb5ce5bc792f20587c3"
|
||||
"merkle_root": "e406bb0127f82bf476884b46c05ca86993c22e1373c4274443f2aef20b138a8e",
|
||||
"datetime": "2024-05-28T00:05:41.698473",
|
||||
"commit": "342b88f62bcd34595ea81ced550bb579b4e4a813"
|
||||
},
|
||||
"history": [
|
||||
{
|
||||
|
@ -31,8 +31,24 @@ CORNER_BUTTON = (215, 25)
|
||||
CONFIRM_WORD = (MID, TOP)
|
||||
TOP_ROW = (MID, TOP)
|
||||
|
||||
RESET_MINUS = (LEFT, grid(DISPLAY_HEIGHT, 5, 1))
|
||||
RESET_PLUS = (RIGHT, grid(DISPLAY_HEIGHT, 5, 1))
|
||||
|
||||
def reset_minus(model_internal_name: str) -> Coords:
|
||||
RESET_MINUS_T3T1 = (LEFT, grid(DISPLAY_HEIGHT, 5, 3))
|
||||
RESET_MINUS = (LEFT, grid(DISPLAY_HEIGHT, 5, 1))
|
||||
if model_internal_name == "T3T1":
|
||||
return RESET_MINUS_T3T1
|
||||
else:
|
||||
return RESET_MINUS
|
||||
|
||||
|
||||
def reset_plus(model_internal_name: str) -> Coords:
|
||||
RESET_PLUS_T3T1 = (RIGHT, grid(DISPLAY_HEIGHT, 5, 3))
|
||||
RESET_PLUS = (RIGHT, grid(DISPLAY_HEIGHT, 5, 1))
|
||||
if model_internal_name == "T3T1":
|
||||
return RESET_PLUS_T3T1
|
||||
else:
|
||||
return RESET_PLUS
|
||||
|
||||
|
||||
RESET_WORD_CHECK = [
|
||||
(MID, grid(DISPLAY_HEIGHT, 5, 2)),
|
||||
|
@ -22,7 +22,9 @@ def enter_word(
|
||||
typed_word = word[:4]
|
||||
for coords in buttons.type_word(typed_word, is_slip39=is_slip39):
|
||||
debug.click(coords)
|
||||
|
||||
if debug.model is models.T3T1 and not is_slip39 and len(word) > 4:
|
||||
# T3T1 (mercury) 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, wait=True)
|
||||
return debug.click(buttons.CONFIRM_WORD, wait=True)
|
||||
elif debug.model in (models.T2B1,):
|
||||
letter_index = 0
|
||||
@ -49,8 +51,10 @@ def enter_word(
|
||||
def confirm_recovery(debug: "DebugLink") -> None:
|
||||
layout = debug.wait_layout()
|
||||
TR.assert_equals(layout.title(), "recovery__title")
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
if debug.model in (models.T2T1,):
|
||||
debug.click(buttons.OK, wait=True)
|
||||
elif debug.model in (models.T3T1,):
|
||||
debug.swipe_up(wait=True)
|
||||
elif debug.model in (models.T2B1,):
|
||||
debug.press_right(wait=True)
|
||||
debug.press_right()
|
||||
@ -99,20 +103,29 @@ def select_number_of_words(
|
||||
raise ValueError("Unknown model")
|
||||
|
||||
if num_of_words in (20, 33):
|
||||
TR.assert_in(layout.text_content(), "recovery__enter_any_share")
|
||||
TR.assert_in_multiple(
|
||||
layout.text_content(),
|
||||
["recovery__enter_any_share", "recovery__only_first_n_letters"],
|
||||
)
|
||||
else:
|
||||
TR.assert_in(layout.text_content(), "recovery__enter_backup")
|
||||
TR.assert_in_multiple(
|
||||
layout.text_content(),
|
||||
["recovery__enter_backup", "recovery__only_first_n_letters"],
|
||||
)
|
||||
|
||||
|
||||
def enter_share(
|
||||
debug: "DebugLink", share: str, is_first: bool = True
|
||||
) -> "LayoutContent":
|
||||
TR.assert_in(debug.read_layout().title(), "recovery__title_recover")
|
||||
if debug.model in (models.T2B1,):
|
||||
TR.assert_in(debug.read_layout().title(), "recovery__title_recover")
|
||||
layout = debug.wait_layout()
|
||||
for _ in range(layout.page_count()):
|
||||
layout = debug.press_right(wait=True)
|
||||
elif debug.model in (models.T3T1,):
|
||||
layout = debug.swipe_up(wait=True)
|
||||
else:
|
||||
TR.assert_in(debug.read_layout().title(), "recovery__title_recover")
|
||||
layout = debug.click(buttons.OK, wait=True)
|
||||
|
||||
assert "MnemonicKeyboard" in layout.all_components()
|
||||
@ -124,15 +137,20 @@ def enter_share(
|
||||
|
||||
|
||||
def enter_shares(debug: "DebugLink", shares: list[str]) -> None:
|
||||
TR.assert_in(debug.read_layout().text_content(), "recovery__enter_any_share")
|
||||
TR.assert_in_multiple(
|
||||
debug.read_layout().text_content(),
|
||||
["recovery__enter_any_share", "recovery__only_first_n_letters"],
|
||||
)
|
||||
for index, share in enumerate(shares):
|
||||
enter_share(debug, share, is_first=index == 0)
|
||||
if index < len(shares) - 1:
|
||||
TR.assert_in(
|
||||
debug.read_layout().text_content(),
|
||||
"recovery__x_of_y_entered_template",
|
||||
template=(index + 1, len(shares)),
|
||||
)
|
||||
# FIXME: when ui-t3t1 done for shamir, we want to check the template below
|
||||
TR.assert_in(debug.read_layout().title(), "recovery__title_recover")
|
||||
# TR.assert_in(
|
||||
# debug.read_layout().text_content(),
|
||||
# "recovery__x_of_y_entered_template",
|
||||
# template=(index + 1, len(shares)),
|
||||
# )
|
||||
|
||||
TR.assert_in(debug.read_layout().text_content(), "recovery__wallet_recovered")
|
||||
|
||||
@ -188,13 +206,19 @@ def enter_seed_previous_correct(
|
||||
i += 1
|
||||
layout = enter_word(debug, word, is_slip39=False)
|
||||
|
||||
TR.assert_in(debug.read_layout().text_content(), "recovery__wallet_recovered")
|
||||
# TR.assert_in(debug.read_layout().text_content(), "recovery__wallet_recovered")
|
||||
|
||||
|
||||
def prepare_enter_seed(debug: "DebugLink") -> None:
|
||||
TR.assert_in(debug.read_layout().text_content(), "recovery__enter_backup")
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
TR.assert_in_multiple(
|
||||
debug.read_layout().text_content(),
|
||||
["recovery__enter_backup", "recovery__only_first_n_letters"],
|
||||
)
|
||||
if debug.model in (models.T2T1,):
|
||||
debug.click(buttons.OK, wait=True)
|
||||
elif debug.model in (models.T3T1,):
|
||||
debug.swipe_up(wait=True)
|
||||
debug.swipe_up(wait=True)
|
||||
elif debug.model in (models.T2B1,):
|
||||
debug.press_right(wait=True)
|
||||
TR.assert_equals(debug.read_layout().title(), "recovery__title_recover")
|
||||
|
@ -14,16 +14,22 @@ if TYPE_CHECKING:
|
||||
|
||||
def confirm_new_wallet(debug: "DebugLink") -> None:
|
||||
TR.assert_equals(debug.read_layout().title(), "reset__title_create_wallet")
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
if debug.model in (models.T2T1,):
|
||||
debug.click(buttons.OK, wait=True)
|
||||
elif debug.model in (models.T3T1,):
|
||||
debug.swipe_up(wait=True)
|
||||
debug.click(buttons.TAP_TO_CONFIRM, wait=True)
|
||||
debug.swipe_up(wait=True) # Wallet created
|
||||
elif debug.model in (models.T2B1,):
|
||||
debug.press_right(wait=True)
|
||||
debug.press_right(wait=True)
|
||||
|
||||
|
||||
def confirm_read(debug: "DebugLink", middle_r: bool = False) -> None:
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
if debug.model in (models.T2T1,):
|
||||
debug.click(buttons.OK, wait=True)
|
||||
elif debug.model in (models.T3T1,):
|
||||
debug.swipe_up(wait=True)
|
||||
elif debug.model in (models.T2B1,):
|
||||
page_count = debug.read_layout().page_count()
|
||||
if page_count > 1:
|
||||
@ -36,18 +42,28 @@ def confirm_read(debug: "DebugLink", middle_r: bool = False) -> None:
|
||||
|
||||
|
||||
def cancel_backup(debug: "DebugLink", middle_r: bool = False) -> None:
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
if debug.model in (models.T2T1,):
|
||||
debug.click(buttons.CANCEL, wait=True)
|
||||
debug.click(buttons.CANCEL, wait=True)
|
||||
elif debug.model in (models.T3T1,):
|
||||
debug.click(buttons.CORNER_BUTTON, wait=True)
|
||||
debug.click(buttons.VERTICAL_MENU[0], wait=True)
|
||||
debug.swipe_up(wait=True)
|
||||
debug.click(buttons.TAP_TO_CONFIRM)
|
||||
elif debug.model in (models.T2B1,):
|
||||
debug.press_left(wait=True)
|
||||
debug.press_left(wait=True)
|
||||
|
||||
|
||||
def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> None:
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
assert "NumberInputDialog" in debug.read_layout().all_components()
|
||||
for _ in range(diff):
|
||||
debug.click(button)
|
||||
debug.click(buttons.OK, wait=True)
|
||||
debug.click(button, wait=True)
|
||||
if debug.model in (models.T2T1,):
|
||||
debug.click(buttons.OK, wait=True)
|
||||
else:
|
||||
debug.swipe_up(wait=True)
|
||||
elif debug.model in (models.T2B1,):
|
||||
layout = debug.read_layout()
|
||||
if layout.title() in TR.translate(
|
||||
@ -56,7 +72,7 @@ def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> Non
|
||||
# Special info screens
|
||||
layout = debug.press_right(wait=True)
|
||||
assert "NumberInput" in layout.all_components()
|
||||
if button == buttons.RESET_MINUS:
|
||||
if button == buttons.reset_minus(debug.model.internal_name):
|
||||
for _ in range(diff):
|
||||
debug.press_left(wait=True)
|
||||
else:
|
||||
@ -72,6 +88,8 @@ def read_words(
|
||||
|
||||
if debug.model in (models.T2B1,):
|
||||
debug.press_right(wait=True)
|
||||
elif debug.model in (models.T3T1,):
|
||||
debug.swipe_up(wait=True)
|
||||
|
||||
# Swiping through all the pages and loading the words
|
||||
layout = debug.read_layout()
|
||||
@ -82,10 +100,15 @@ def read_words(
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
words.extend(layout.seed_words())
|
||||
|
||||
if debug.model in (models.T3T1,):
|
||||
debug.swipe_up(wait=True)
|
||||
|
||||
# There is hold-to-confirm button
|
||||
if do_htc:
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
if debug.model in (models.T2T1,):
|
||||
debug.click_hold(buttons.OK, hold_ms=1500)
|
||||
elif debug.model in (models.T3T1,):
|
||||
debug.click_hold(buttons.TAP_TO_CONFIRM, hold_ms=1500)
|
||||
elif debug.model in (models.T2B1,):
|
||||
debug.press_right_htc(1200)
|
||||
else:
|
||||
@ -97,6 +120,9 @@ def read_words(
|
||||
|
||||
|
||||
def confirm_words(debug: "DebugLink", words: list[str]) -> None:
|
||||
if debug.model in (models.T3T1,):
|
||||
debug.swipe_up(wait=True)
|
||||
|
||||
layout = debug.wait_layout()
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
TR.assert_template(layout.text_content(), "reset__select_word_x_of_y_template")
|
||||
@ -112,7 +138,11 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None:
|
||||
]
|
||||
wanted_word = words[word_pos - 1].lower()
|
||||
button_pos = btn_texts.index(wanted_word)
|
||||
layout = debug.click(buttons.RESET_WORD_CHECK[button_pos], wait=True)
|
||||
if debug.model is models.T3T1:
|
||||
btn_positions = buttons.VERTICAL_MENU
|
||||
else:
|
||||
btn_positions = buttons.RESET_WORD_CHECK
|
||||
layout = debug.click(btn_positions[button_pos], wait=True)
|
||||
elif debug.model in (models.T2B1,):
|
||||
TR.assert_in(layout.text_content(), "reset__select_correct_word")
|
||||
layout = debug.press_right(wait=True)
|
||||
|
@ -102,11 +102,16 @@ def test_autolock_interrupts_signing(device_handler: "BackgroundDeviceHandler"):
|
||||
in debug.wait_layout().text_content().replace(" ", "")
|
||||
)
|
||||
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
if debug.model in (models.T2T1,):
|
||||
debug.click(buttons.OK, wait=True)
|
||||
layout = debug.click(buttons.OK, wait=True)
|
||||
TR.assert_in(layout.text_content(), "send__total_amount")
|
||||
assert "0.0039 BTC" in layout.text_content()
|
||||
elif debug.model in (models.T3T1,):
|
||||
debug.swipe_up(wait=True)
|
||||
layout = debug.swipe_up(wait=True)
|
||||
TR.assert_in(layout.text_content(), "send__total_amount")
|
||||
assert "0.0039 BTC" in layout.text_content()
|
||||
elif debug.model in (models.T2B1,):
|
||||
debug.press_right(wait=True)
|
||||
layout = debug.press_right(wait=True)
|
||||
@ -149,11 +154,17 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa
|
||||
in debug.wait_layout().text_content().replace(" ", "")
|
||||
)
|
||||
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
if debug.model in (models.T2T1,):
|
||||
debug.click(buttons.OK, wait=True)
|
||||
layout = debug.click(buttons.OK, wait=True)
|
||||
TR.assert_in(layout.text_content(), "send__total_amount")
|
||||
assert "0.0039 BTC" in layout.text_content()
|
||||
elif debug.model in (models.T3T1,):
|
||||
debug.swipe_up(wait=True)
|
||||
layout = debug.swipe_up(wait=True)
|
||||
TR.assert_in(layout.text_content(), "send__total_amount")
|
||||
assert "0.0039 BTC" in layout.text_content()
|
||||
debug.swipe_up(wait=True)
|
||||
elif debug.model in (models.T2B1,):
|
||||
debug.press_right(wait=True)
|
||||
layout = debug.press_right(wait=True)
|
||||
@ -168,8 +179,10 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa
|
||||
with device_handler.client:
|
||||
device_handler.client.set_filter(messages.TxAck, sleepy_filter)
|
||||
# confirm transaction
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
if debug.model in (models.T2T1,):
|
||||
debug.click(buttons.OK)
|
||||
elif debug.model in (models.T3T1,):
|
||||
debug.click(buttons.TAP_TO_CONFIRM)
|
||||
elif debug.model in (models.T2B1,):
|
||||
debug.press_middle()
|
||||
|
||||
@ -197,9 +210,12 @@ def test_autolock_passphrase_keyboard(device_handler: "BackgroundDeviceHandler")
|
||||
# enter passphrase - slowly
|
||||
# keep clicking for long enough to trigger the autolock if it incorrectly ignored key presses
|
||||
for _ in range(math.ceil(11 / 1.5)):
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
if debug.model in (models.T2T1,):
|
||||
# click at "j"
|
||||
debug.click(CENTER_BUTTON)
|
||||
elif debug.model in (models.T3T1,):
|
||||
# click at "j"
|
||||
debug.click((20, 120))
|
||||
elif debug.model in (models.T2B1,):
|
||||
# just go right
|
||||
# NOTE: because of passphrase randomization it would be a pain to input
|
||||
@ -208,8 +224,10 @@ def test_autolock_passphrase_keyboard(device_handler: "BackgroundDeviceHandler")
|
||||
time.sleep(1.5)
|
||||
|
||||
# Send the passphrase to the client (TT has it clicked already, TR needs to input it)
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
if debug.model in (models.T2T1,):
|
||||
debug.click(buttons.OK, wait=True)
|
||||
elif debug.model in (models.T3T1,):
|
||||
debug.click(buttons.CORNER_BUTTON, wait=True)
|
||||
elif debug.model in (models.T2B1,):
|
||||
debug.input("j" * 8, wait=True)
|
||||
|
||||
@ -327,10 +345,21 @@ def test_dryrun_enter_word_slowly(device_handler: "BackgroundDeviceHandler"):
|
||||
# select 20 words
|
||||
recovery.select_number_of_words(debug, 20)
|
||||
|
||||
if debug.model in (models.T2T1, models.T3T1):
|
||||
if debug.model in (models.T2T1,):
|
||||
layout = debug.click(buttons.OK, wait=True)
|
||||
assert layout.main_component() == "MnemonicKeyboard"
|
||||
|
||||
# type the word OCEAN slowly
|
||||
for coords in buttons.type_word("ocea", is_slip39=True):
|
||||
time.sleep(9)
|
||||
debug.click(coords)
|
||||
layout = debug.click(buttons.CONFIRM_WORD, wait=True)
|
||||
# should not have locked, even though we took 9 seconds to type each letter
|
||||
assert layout.main_component() == "MnemonicKeyboard"
|
||||
elif debug.model in (models.T3T1,):
|
||||
layout = debug.swipe_up(wait=True)
|
||||
assert layout.main_component() == "MnemonicKeyboard"
|
||||
|
||||
# type the word OCEAN slowly
|
||||
for coords in buttons.type_word("ocea", is_slip39=True):
|
||||
time.sleep(9)
|
||||
|
@ -64,9 +64,6 @@ def test_backup_slip39_custom(
|
||||
# cancel back up
|
||||
reset.cancel_backup(debug)
|
||||
|
||||
# confirm cancel
|
||||
reset.cancel_backup(debug)
|
||||
|
||||
assert device_handler.result() == "Initialized"
|
||||
|
||||
device_handler.run(
|
||||
|
327
tests/click_tests/test_passphrase_mercury.py
Normal file
327
tests/click_tests/test_passphrase_mercury.py
Normal file
@ -0,0 +1,327 @@
|
||||
# This file is part of the Trezor project.
|
||||
#
|
||||
# Copyright (C) 2012-2023 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>.
|
||||
|
||||
import time
|
||||
from contextlib import contextmanager
|
||||
from typing import TYPE_CHECKING, Generator, Optional, Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from trezorlib import exceptions
|
||||
|
||||
from .. import buttons
|
||||
from ..common import get_test_address
|
||||
from .common import CommonPass, PassphraseCategory, get_char_category
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezorlib.debuglink import DebugLink
|
||||
|
||||
from ..device_handler import BackgroundDeviceHandler
|
||||
|
||||
|
||||
pytestmark = [pytest.mark.skip_t1b1, pytest.mark.skip_t2b1, pytest.mark.skip_t2t1]
|
||||
|
||||
PASSPHRASE_CANCELLED = pytest.raises(exceptions.Cancelled, match="")
|
||||
|
||||
MERCURY_CATEGORIES = [
|
||||
PassphraseCategory.LOWERCASE,
|
||||
PassphraseCategory.UPPERCASE,
|
||||
PassphraseCategory.DIGITS,
|
||||
PassphraseCategory.SPECIAL,
|
||||
]
|
||||
|
||||
# fmt: off
|
||||
PASSPHRASE_LOWERCASE = ("abc", "def", "ghi", "jkl", "mno", "pq", "rst", "uvw", "xyz", " *#")
|
||||
PASSPHRASE_UPPERCASE = ("ABC", "DEF", "GHI", "JKL", "MNO", "PQ", "RST", "UVW", "XYZ", " *#")
|
||||
PASSPHRASE_DIGITS = ("1", "2", "3", "4", "5", "6", "7", "8", "9", "0")
|
||||
PASSPHRASE_SPECIAL = ("_<>", ".:@", "/|\\", "!()", "+%&", "-[]", "?{}", ",'`", ";\"~", "$^=")
|
||||
# fmt: on
|
||||
|
||||
# TODO: better read this from the trace
|
||||
MERCURY_CATEGORY = PassphraseCategory.LOWERCASE
|
||||
MERCURY_COORDS_PREV: buttons.Coords = (0, 0)
|
||||
|
||||
# Testing the maximum length is really 50
|
||||
|
||||
DA_50 = 25 * "da"
|
||||
DA_50_ADDRESS = "mg5L2i8HZKUvceK1sfmGHhE4gichFSsdvm"
|
||||
assert len(DA_50) == 50
|
||||
|
||||
DA_49 = DA_50[:-1]
|
||||
DA_49_ADDRESS = "mxrB75ydMS3ZzqmYKK28fj4bNMEx7dDw6e"
|
||||
assert len(DA_49) == 49
|
||||
assert DA_49_ADDRESS != DA_50_ADDRESS
|
||||
|
||||
DA_51 = DA_50 + "d"
|
||||
DA_51_ADDRESS = DA_50_ADDRESS
|
||||
assert len(DA_51) == 51
|
||||
assert DA_51_ADDRESS == DA_50_ADDRESS
|
||||
|
||||
|
||||
def get_passphrase_choices(char: str) -> tuple[str, ...]:
|
||||
if char in " *#":
|
||||
return PASSPHRASE_LOWERCASE
|
||||
|
||||
if char.islower():
|
||||
return PASSPHRASE_LOWERCASE
|
||||
elif char.isupper():
|
||||
return PASSPHRASE_UPPERCASE
|
||||
elif char.isdigit():
|
||||
return PASSPHRASE_DIGITS
|
||||
else:
|
||||
return PASSPHRASE_SPECIAL
|
||||
|
||||
|
||||
def passphrase(char: str) -> Tuple[buttons.Coords, int]:
|
||||
choices = get_passphrase_choices(char)
|
||||
idx = next(i for i, letters in enumerate(choices) if char in letters)
|
||||
click_amount = choices[idx].index(char) + 1
|
||||
return buttons.pin_passphrase_index(idx), click_amount
|
||||
|
||||
|
||||
@contextmanager
|
||||
def prepare_passphrase_dialogue(
|
||||
device_handler: "BackgroundDeviceHandler", address: Optional[str] = None
|
||||
) -> Generator["DebugLink", None, None]:
|
||||
debug = device_handler.debuglink()
|
||||
device_handler.run(get_test_address) # type: ignore
|
||||
assert debug.wait_layout().main_component() == "PassphraseKeyboard"
|
||||
|
||||
# Resetting the category as it could have been changed by previous tests
|
||||
global MERCURY_CATEGORY
|
||||
MERCURY_CATEGORY = PassphraseCategory.LOWERCASE # type: ignore
|
||||
|
||||
yield debug
|
||||
|
||||
result = device_handler.result()
|
||||
if address is not None:
|
||||
assert result == address
|
||||
|
||||
|
||||
def go_to_category(debug: "DebugLink", category: PassphraseCategory) -> None:
|
||||
"""Go to a specific category"""
|
||||
global MERCURY_CATEGORY
|
||||
global MERCURY_COORDS_PREV
|
||||
|
||||
# Already there
|
||||
if MERCURY_CATEGORY == category:
|
||||
return
|
||||
|
||||
current_index = MERCURY_CATEGORIES.index(MERCURY_CATEGORY)
|
||||
target_index = MERCURY_CATEGORIES.index(category)
|
||||
if target_index > current_index:
|
||||
for _ in range(target_index - current_index):
|
||||
debug.swipe_left(wait=True)
|
||||
else:
|
||||
for _ in range(current_index - target_index):
|
||||
debug.swipe_right(wait=True)
|
||||
MERCURY_CATEGORY = category # type: ignore
|
||||
# Category changed, reset coordinates
|
||||
MERCURY_COORDS_PREV = (0, 0) # type: ignore
|
||||
|
||||
|
||||
def press_char(debug: "DebugLink", char: str) -> None:
|
||||
"""Press a character"""
|
||||
global MERCURY_COORDS_PREV
|
||||
|
||||
# Space and couple others are a special case
|
||||
if char in " *#":
|
||||
char_category = PassphraseCategory.LOWERCASE
|
||||
else:
|
||||
char_category = get_char_category(char)
|
||||
|
||||
go_to_category(debug, char_category)
|
||||
|
||||
coords, amount = passphrase(char)
|
||||
# If the button is the same as for the previous char,
|
||||
# waiting a second before pressing it again.
|
||||
if coords == MERCURY_COORDS_PREV:
|
||||
time.sleep(1.1)
|
||||
MERCURY_COORDS_PREV = coords # type: ignore
|
||||
for _ in range(amount):
|
||||
debug.click(coords, wait=True)
|
||||
|
||||
|
||||
def input_passphrase(debug: "DebugLink", passphrase: str, check: bool = True) -> None:
|
||||
"""Input a passphrase with validation it got added"""
|
||||
if check:
|
||||
before = debug.read_layout().passphrase()
|
||||
for char in passphrase:
|
||||
press_char(debug, char)
|
||||
if check:
|
||||
after = debug.read_layout().passphrase()
|
||||
assert after == before + passphrase
|
||||
|
||||
|
||||
def enter_passphrase(debug: "DebugLink") -> None:
|
||||
"""Enter a passphrase"""
|
||||
coords = buttons.grid35(2, 0) # top-right corner
|
||||
debug.click(coords, wait=True)
|
||||
|
||||
|
||||
def delete_char(debug: "DebugLink") -> None:
|
||||
"""Deletes the last char"""
|
||||
coords = buttons.pin_passphrase_grid(9)
|
||||
debug.click(coords, wait=True)
|
||||
|
||||
|
||||
VECTORS = ( # passphrase, address
|
||||
(CommonPass.SHORT, CommonPass.SHORT_ADDRESS),
|
||||
(CommonPass.WITH_SPACE, CommonPass.WITH_SPACE_ADDRESS),
|
||||
(CommonPass.RANDOM_25, CommonPass.RANDOM_25_ADDRESS),
|
||||
(DA_49, DA_49_ADDRESS),
|
||||
(DA_50, DA_50_ADDRESS),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("passphrase, address", VECTORS)
|
||||
@pytest.mark.setup_client(passphrase=True)
|
||||
def test_passphrase_input(
|
||||
device_handler: "BackgroundDeviceHandler", passphrase: str, address: str
|
||||
):
|
||||
with prepare_passphrase_dialogue(device_handler, address) as debug:
|
||||
input_passphrase(debug, passphrase)
|
||||
enter_passphrase(debug)
|
||||
|
||||
|
||||
@pytest.mark.setup_client(passphrase=True)
|
||||
def test_passphrase_input_over_50_chars(device_handler: "BackgroundDeviceHandler"):
|
||||
with prepare_passphrase_dialogue(device_handler, DA_51_ADDRESS) as debug: # type: ignore
|
||||
input_passphrase(debug, DA_51, check=False)
|
||||
assert debug.read_layout().passphrase() == DA_50
|
||||
enter_passphrase(debug)
|
||||
|
||||
|
||||
@pytest.mark.setup_client(passphrase=True)
|
||||
def test_passphrase_delete(device_handler: "BackgroundDeviceHandler"):
|
||||
with prepare_passphrase_dialogue(device_handler, CommonPass.SHORT_ADDRESS) as debug:
|
||||
input_passphrase(debug, CommonPass.SHORT[:8])
|
||||
|
||||
for _ in range(4):
|
||||
delete_char(debug)
|
||||
debug.wait_layout()
|
||||
|
||||
input_passphrase(debug, CommonPass.SHORT[8 - 4 :])
|
||||
enter_passphrase(debug)
|
||||
|
||||
|
||||
@pytest.mark.setup_client(passphrase=True)
|
||||
def test_passphrase_delete_all(
|
||||
device_handler: "BackgroundDeviceHandler",
|
||||
):
|
||||
with prepare_passphrase_dialogue(device_handler, CommonPass.EMPTY_ADDRESS) as debug:
|
||||
passphrase = "trezor"
|
||||
input_passphrase(debug, passphrase)
|
||||
|
||||
for _ in range(len(passphrase)):
|
||||
delete_char(debug)
|
||||
|
||||
enter_passphrase(debug)
|
||||
|
||||
|
||||
@pytest.mark.setup_client(passphrase=True)
|
||||
def test_passphrase_loop_all_characters(device_handler: "BackgroundDeviceHandler"):
|
||||
with prepare_passphrase_dialogue(device_handler, CommonPass.EMPTY_ADDRESS) as debug:
|
||||
for category in (
|
||||
PassphraseCategory.DIGITS,
|
||||
PassphraseCategory.LOWERCASE,
|
||||
PassphraseCategory.UPPERCASE,
|
||||
PassphraseCategory.SPECIAL,
|
||||
):
|
||||
go_to_category(debug, category)
|
||||
debug.wait_layout()
|
||||
|
||||
enter_passphrase(debug)
|
||||
coords = buttons.pin_passphrase_grid(11)
|
||||
debug.click(coords)
|
||||
|
||||
|
||||
@pytest.mark.setup_client(passphrase=True)
|
||||
def test_passphrase_click_same_button_many_times(
|
||||
device_handler: "BackgroundDeviceHandler",
|
||||
):
|
||||
with prepare_passphrase_dialogue(device_handler) as debug:
|
||||
a_coords, _ = buttons.passphrase("a")
|
||||
for _ in range(10):
|
||||
debug.click(a_coords)
|
||||
|
||||
enter_passphrase(debug)
|
||||
|
||||
|
||||
@pytest.mark.setup_client(passphrase=True)
|
||||
def test_passphrase_prompt_disappears(
|
||||
device_handler: "BackgroundDeviceHandler",
|
||||
):
|
||||
with prepare_passphrase_dialogue(device_handler) as debug:
|
||||
input_passphrase(debug, "a")
|
||||
|
||||
# Wait a second for the prompt to disappear
|
||||
time.sleep(1.1)
|
||||
|
||||
enter_passphrase(debug)
|
||||
|
||||
|
||||
@pytest.mark.setup_client(passphrase=True)
|
||||
def test_passphrase_long_spaces_deletion(
|
||||
device_handler: "BackgroundDeviceHandler",
|
||||
):
|
||||
with prepare_passphrase_dialogue(device_handler) as debug:
|
||||
input_passphrase(
|
||||
debug,
|
||||
"a"
|
||||
+ " " * 7
|
||||
+ "b"
|
||||
+ " " * 7
|
||||
+ "c"
|
||||
+ " " * 7
|
||||
+ "d"
|
||||
+ " " * 7
|
||||
+ "e"
|
||||
+ " " * 7
|
||||
+ "f",
|
||||
)
|
||||
for _ in range(12):
|
||||
delete_char(debug)
|
||||
|
||||
enter_passphrase(debug)
|
||||
|
||||
|
||||
@pytest.mark.setup_client(passphrase=True)
|
||||
def test_passphrase_dollar_sign_deletion(
|
||||
device_handler: "BackgroundDeviceHandler",
|
||||
):
|
||||
# Checks that dollar signs will not leave one pixel on the top after deleting
|
||||
# (was a bug previously on model T)
|
||||
with prepare_passphrase_dialogue(device_handler, CommonPass.EMPTY_ADDRESS) as debug:
|
||||
passphrase = "$$ I want $$"
|
||||
input_passphrase(debug, passphrase)
|
||||
|
||||
for _ in range(len(passphrase)):
|
||||
delete_char(debug)
|
||||
|
||||
enter_passphrase(debug)
|
||||
|
||||
|
||||
@pytest.mark.setup_client(passphrase=True)
|
||||
def test_cycle_through_last_character(
|
||||
device_handler: "BackgroundDeviceHandler",
|
||||
):
|
||||
# Checks that we can cycle through the last (50th) passphrase character
|
||||
# (was a bug previously)
|
||||
with prepare_passphrase_dialogue(device_handler) as debug:
|
||||
passphrase = DA_49 + "i" # for i we need to cycle through "ghi" three times
|
||||
input_passphrase(debug, passphrase)
|
||||
enter_passphrase(debug)
|
@ -30,7 +30,7 @@ if TYPE_CHECKING:
|
||||
from ..device_handler import BackgroundDeviceHandler
|
||||
|
||||
|
||||
pytestmark = [pytest.mark.skip_t1b1, pytest.mark.skip_t2b1]
|
||||
pytestmark = [pytest.mark.skip_t1b1, pytest.mark.skip_t2b1, pytest.mark.skip_t3t1]
|
||||
|
||||
# TODO: it is not possible to cancel the passphrase entry on TT
|
||||
# NOTE: the prompt (underscoring) is not there when a space is entered
|
||||
|
@ -68,10 +68,12 @@ def test_reset_slip39_advanced(
|
||||
reset.confirm_read(debug)
|
||||
|
||||
# set num of groups - default is 5
|
||||
assert debug.model is not None
|
||||
model_name: str = debug.model.internal_name
|
||||
if group_count < 5:
|
||||
reset.set_selection(debug, buttons.RESET_MINUS, 5 - group_count)
|
||||
reset.set_selection(debug, buttons.reset_minus(model_name), 5 - group_count)
|
||||
else:
|
||||
reset.set_selection(debug, buttons.RESET_PLUS, group_count - 5)
|
||||
reset.set_selection(debug, buttons.reset_plus(model_name), group_count - 5)
|
||||
|
||||
# confirm checklist
|
||||
reset.confirm_read(debug)
|
||||
@ -79,9 +81,9 @@ def test_reset_slip39_advanced(
|
||||
# set group threshold
|
||||
# TODO: could make it general as well
|
||||
if group_count == 2 and group_threshold == 2:
|
||||
reset.set_selection(debug, buttons.RESET_PLUS, 0)
|
||||
reset.set_selection(debug, buttons.reset_plus(model_name), 0)
|
||||
elif group_count == 16 and group_threshold == 16:
|
||||
reset.set_selection(debug, buttons.RESET_PLUS, 11)
|
||||
reset.set_selection(debug, buttons.reset_plus(model_name), 11)
|
||||
else:
|
||||
raise RuntimeError("not a supported combination")
|
||||
|
||||
@ -92,16 +94,16 @@ def test_reset_slip39_advanced(
|
||||
for _ in range(group_count):
|
||||
# set num of shares - default is 5
|
||||
if share_count < 5:
|
||||
reset.set_selection(debug, buttons.RESET_MINUS, 5 - share_count)
|
||||
reset.set_selection(debug, buttons.reset_minus(model_name), 5 - share_count)
|
||||
else:
|
||||
reset.set_selection(debug, buttons.RESET_PLUS, share_count - 5)
|
||||
reset.set_selection(debug, buttons.reset_plus(model_name), share_count - 5)
|
||||
|
||||
# set share threshold
|
||||
# TODO: could make it general as well
|
||||
if share_count == 2 and share_threshold == 2:
|
||||
reset.set_selection(debug, buttons.RESET_PLUS, 0)
|
||||
reset.set_selection(debug, buttons.reset_plus(model_name), 0)
|
||||
elif share_count == 16 and share_threshold == 16:
|
||||
reset.set_selection(debug, buttons.RESET_PLUS, 11)
|
||||
reset.set_selection(debug, buttons.reset_plus(model_name), 11)
|
||||
else:
|
||||
raise RuntimeError("not a supported combination")
|
||||
|
||||
|
@ -65,10 +65,12 @@ def test_reset_slip39_basic(
|
||||
reset.confirm_read(debug)
|
||||
|
||||
# set num of shares - default is 5
|
||||
assert debug.model is not None
|
||||
model_name: str = debug.model.internal_name
|
||||
if num_of_shares < 5:
|
||||
reset.set_selection(debug, buttons.RESET_MINUS, 5 - num_of_shares)
|
||||
reset.set_selection(debug, buttons.reset_minus(model_name), 5 - num_of_shares)
|
||||
else:
|
||||
reset.set_selection(debug, buttons.RESET_PLUS, num_of_shares - 5)
|
||||
reset.set_selection(debug, buttons.reset_plus(model_name), num_of_shares - 5)
|
||||
|
||||
# confirm checklist
|
||||
reset.confirm_read(debug)
|
||||
@ -76,9 +78,9 @@ def test_reset_slip39_basic(
|
||||
# set threshold
|
||||
# TODO: could make it general as well
|
||||
if num_of_shares == 1 and threshold == 1:
|
||||
reset.set_selection(debug, buttons.RESET_PLUS, 0)
|
||||
reset.set_selection(debug, buttons.reset_plus(model_name), 0)
|
||||
elif num_of_shares == 16 and threshold == 16:
|
||||
reset.set_selection(debug, buttons.RESET_PLUS, 11)
|
||||
reset.set_selection(debug, buttons.reset_plus(model_name), 11)
|
||||
else:
|
||||
raise RuntimeError("not a supported combination")
|
||||
|
||||
|
@ -25,6 +25,8 @@ import pytest
|
||||
|
||||
from trezorlib import btc, messages, models, tools
|
||||
|
||||
from . import buttons
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _pytest.mark.structures import MarkDecorator
|
||||
|
||||
@ -255,12 +257,16 @@ def read_mnemonic_from_screen_mercury(
|
||||
assert br.pages is not None
|
||||
|
||||
debug.wait_layout()
|
||||
debug.swipe_up()
|
||||
|
||||
for _ in range(br.pages):
|
||||
for _ in range(br.pages - 2):
|
||||
words = debug.wait_layout().seed_words()
|
||||
mnemonic.extend(words)
|
||||
debug.swipe_up()
|
||||
|
||||
debug.wait_layout()
|
||||
debug.press_yes()
|
||||
|
||||
return mnemonic
|
||||
|
||||
|
||||
@ -298,6 +304,15 @@ def click_info_button_tt(debug: "DebugLink"):
|
||||
debug.press_yes()
|
||||
|
||||
|
||||
def click_info_button_mercury(debug: "DebugLink"):
|
||||
"""Click Shamir backup info button and return back."""
|
||||
debug.click(buttons.CORNER_BUTTON, wait=True)
|
||||
debug.synchronize_at("VerticalMenu")
|
||||
debug.click(buttons.VERTICAL_MENU[0], wait=True)
|
||||
debug.click(buttons.CORNER_BUTTON, wait=True)
|
||||
debug.click(buttons.CORNER_BUTTON)
|
||||
|
||||
|
||||
def check_pin_backoff_time(attempts: int, start: float) -> None:
|
||||
"""Helper to assert the exponentially growing delay after incorrect PIN attempts"""
|
||||
expected = (2**attempts) - 1
|
||||
|
@ -21,6 +21,7 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
|
||||
from trezorlib.exceptions import TrezorFailure
|
||||
|
||||
from ...common import parametrize_using_common_fixtures
|
||||
from ...input_flows import InputFlowConfirmAllWarnings
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.altcoin,
|
||||
@ -53,7 +54,11 @@ def show_details_input_flow(client: Client):
|
||||
"cardano/sign_tx.slip39.json",
|
||||
)
|
||||
def test_cardano_sign_tx(client: Client, parameters, result):
|
||||
response = call_sign_tx(client, parameters)
|
||||
response = call_sign_tx(
|
||||
client,
|
||||
parameters,
|
||||
input_flow=lambda client: InputFlowConfirmAllWarnings(client).get(),
|
||||
)
|
||||
assert response == _transform_expected_result(result)
|
||||
|
||||
|
||||
|
@ -73,6 +73,7 @@ def test_secret(client: Client, shares: list[str], secret: str):
|
||||
_test_secret(client, shares, secret)
|
||||
|
||||
|
||||
@pytest.mark.skip_t3t1(reason="currently broken on T3T1")
|
||||
@pytest.mark.parametrize("shares, secret", VECTORS)
|
||||
@pytest.mark.setup_client(uninitialized=True)
|
||||
def test_secret_click_info_button(client: Client, shares: list[str], secret: str):
|
||||
|
@ -22,6 +22,8 @@ from trezorlib import btc, device, exceptions, messages, misc, models
|
||||
from trezorlib.debuglink import TrezorClientDebugLink as Client
|
||||
from trezorlib.tools import parse_path
|
||||
|
||||
from ..input_flows import InputFlowConfirmAllWarnings
|
||||
|
||||
HERE = Path(__file__).parent.resolve()
|
||||
|
||||
|
||||
@ -364,6 +366,8 @@ def test_safety_checks(client: Client):
|
||||
client.set_expected_responses(
|
||||
[messages.ButtonRequest, messages.ButtonRequest, messages.Address]
|
||||
)
|
||||
IF = InputFlowConfirmAllWarnings(client)
|
||||
client.set_input_flow(IF.get())
|
||||
get_bad_address()
|
||||
|
||||
with client:
|
||||
@ -388,6 +392,9 @@ def test_safety_checks(client: Client):
|
||||
client.set_expected_responses(
|
||||
[messages.ButtonRequest, messages.ButtonRequest, messages.Address]
|
||||
)
|
||||
if client.model is not models.T1B1:
|
||||
IF = InputFlowConfirmAllWarnings(client)
|
||||
client.set_input_flow(IF.get())
|
||||
get_bad_address()
|
||||
|
||||
|
||||
|
@ -24,6 +24,7 @@ from . import translations as TR
|
||||
from .common import (
|
||||
BRGeneratorType,
|
||||
check_pin_backoff_time,
|
||||
click_info_button_mercury,
|
||||
click_info_button_tt,
|
||||
click_through,
|
||||
get_text_possible_pagination,
|
||||
@ -1307,7 +1308,7 @@ class InputFlowBip39ResetBackup(InputFlowBase):
|
||||
# 1. Confirm Reset x3
|
||||
# 2. Backup your seed
|
||||
# 3. Confirm warning
|
||||
yield from click_through(self.debug, screens=3, code=B.ResetDevice)
|
||||
yield from click_through(self.debug, screens=4, code=B.ResetDevice)
|
||||
|
||||
# mnemonic phrases and rest
|
||||
self.mnemonic = yield from get_mnemonic_and_confirm_success(self.debug)
|
||||
@ -1329,6 +1330,11 @@ class InputFlowBip39ResetPIN(InputFlowBase):
|
||||
assert br.code == B.ResetDevice
|
||||
self.debug.press_yes()
|
||||
|
||||
if self.debug.model is models.T3T1:
|
||||
br = yield # Wallet created
|
||||
assert br.code == B.ResetDevice
|
||||
self.debug.press_yes()
|
||||
|
||||
br = yield # Backup your seed
|
||||
assert br.code == B.ResetDevice
|
||||
self.debug.press_yes()
|
||||
@ -1355,10 +1361,12 @@ class InputFlowBip39ResetFailedCheck(InputFlowBase):
|
||||
self.mnemonic = None
|
||||
|
||||
def input_flow_common(self) -> BRGeneratorType:
|
||||
screens = 4 if self.debug.model is models.T3T1 else 3
|
||||
# 1. Confirm Reset
|
||||
# 1a. (T3T1) done
|
||||
# 2. Backup your seed
|
||||
# 3. Confirm warning
|
||||
yield from click_through(self.debug, screens=3, code=B.ResetDevice)
|
||||
yield from click_through(self.debug, screens=screens, code=B.ResetDevice)
|
||||
|
||||
# mnemonic phrases, wrong answer
|
||||
self.mnemonic = yield from read_and_confirm_mnemonic(
|
||||
@ -1458,21 +1466,22 @@ class InputFlowSlip39BasicBackup(InputFlowBase):
|
||||
|
||||
def input_flow_t3t1(self) -> BRGeneratorType:
|
||||
yield # 1. Checklist
|
||||
self.debug.press_yes()
|
||||
self.debug.wait_layout()
|
||||
self.debug.swipe_up(wait=True)
|
||||
if self.click_info:
|
||||
yield from click_info_button_tt(self.debug)
|
||||
click_info_button_mercury(self.debug)
|
||||
yield # 2. Number of shares (5)
|
||||
self.debug.press_yes()
|
||||
self.debug.swipe_up()
|
||||
yield # 3. Checklist
|
||||
self.debug.press_yes()
|
||||
self.debug.swipe_up(wait=True)
|
||||
if self.click_info:
|
||||
yield from click_info_button_tt(self.debug)
|
||||
click_info_button_mercury(self.debug)
|
||||
yield # 4. Threshold (3)
|
||||
self.debug.press_yes()
|
||||
self.debug.swipe_up()
|
||||
yield # 5. Checklist
|
||||
self.debug.press_yes()
|
||||
self.debug.swipe_up()
|
||||
yield # 6. Confirm show seeds
|
||||
self.debug.press_yes()
|
||||
self.debug.swipe_up()
|
||||
|
||||
# Mnemonic phrases
|
||||
self.mnemonics = yield from load_N_shares(self.debug, 5)
|
||||
@ -1543,7 +1552,7 @@ class InputFlowSlip39BasicResetRecovery(InputFlowBase):
|
||||
# 6. threshold info
|
||||
# 7. Set & confirm threshold value
|
||||
# 8. Confirm show seeds
|
||||
yield from click_through(self.debug, screens=8, code=B.ResetDevice)
|
||||
yield from click_through(self.debug, screens=9, code=B.ResetDevice)
|
||||
|
||||
# Mnemonic phrases
|
||||
self.mnemonics = yield from load_N_shares(self.debug, 5)
|
||||
@ -1695,28 +1704,28 @@ class InputFlowSlip39AdvancedBackup(InputFlowBase):
|
||||
|
||||
def input_flow_t3t1(self) -> BRGeneratorType:
|
||||
yield # 1. Checklist
|
||||
self.debug.press_yes()
|
||||
self.debug.swipe_up(wait=True)
|
||||
if self.click_info:
|
||||
yield from click_info_button_tt(self.debug)
|
||||
click_info_button_mercury(self.debug)
|
||||
yield # 2. Set and confirm group count
|
||||
self.debug.press_yes()
|
||||
self.debug.swipe_up()
|
||||
yield # 3. Checklist
|
||||
self.debug.press_yes()
|
||||
self.debug.swipe_up(wait=True)
|
||||
if self.click_info:
|
||||
yield from click_info_button_tt(self.debug)
|
||||
click_info_button_mercury(self.debug)
|
||||
yield # 4. Set and confirm group threshold
|
||||
self.debug.press_yes()
|
||||
self.debug.swipe_up()
|
||||
yield # 5. Checklist
|
||||
self.debug.press_yes()
|
||||
for _ in range(5): # for each of 5 groups
|
||||
self.debug.swipe_up(wait=True)
|
||||
for _i in range(5): # for each of 5 groups
|
||||
if self.click_info:
|
||||
yield from click_info_button_tt(self.debug)
|
||||
click_info_button_mercury(self.debug)
|
||||
yield # Set & Confirm number of shares
|
||||
self.debug.press_yes()
|
||||
self.debug.swipe_up(wait=True)
|
||||
if self.click_info:
|
||||
yield from click_info_button_tt(self.debug)
|
||||
click_info_button_mercury(self.debug)
|
||||
yield # Set & confirm share threshold value
|
||||
self.debug.press_yes()
|
||||
self.debug.swipe_up(wait=_i != 4)
|
||||
yield # Confirm show seeds
|
||||
self.debug.press_yes()
|
||||
|
||||
@ -1801,7 +1810,7 @@ class InputFlowSlip39AdvancedResetRecovery(InputFlowBase):
|
||||
# 1. Set & Confirm number of shares
|
||||
# 2. Set & confirm share threshold value
|
||||
# 18. Confirm show seeds
|
||||
yield from click_through(self.debug, screens=18, code=B.ResetDevice)
|
||||
yield from click_through(self.debug, screens=19, code=B.ResetDevice)
|
||||
|
||||
# Mnemonic phrases - show & confirm shares for all groups
|
||||
self.mnemonics = yield from load_5_groups_5_shares(self.debug)
|
||||
@ -2119,22 +2128,38 @@ class InputFlowResetSkipBackup(InputFlowBase):
|
||||
def __init__(self, client: Client):
|
||||
super().__init__(client)
|
||||
|
||||
def input_flow_common(self) -> BRGeneratorType:
|
||||
def input_flow_tt(self) -> BRGeneratorType:
|
||||
yield from self.BAK.confirm_new_wallet()
|
||||
yield # Skip Backup
|
||||
info_path = (
|
||||
"backup__new_wallet_created"
|
||||
if self.model() is models.T2B1
|
||||
else "backup__new_wallet_successfully_created"
|
||||
)
|
||||
TR.assert_in(self.text_content(), info_path)
|
||||
if self.model() is models.T2B1:
|
||||
self.debug.press_right()
|
||||
TR.assert_in(self.text_content(), "backup__new_wallet_successfully_created")
|
||||
self.debug.press_no()
|
||||
yield # Confirm skip backup
|
||||
TR.assert_in(self.text_content(), "backup__want_to_skip")
|
||||
self.debug.press_no()
|
||||
|
||||
def input_flow_tr(self) -> BRGeneratorType:
|
||||
yield from self.BAK.confirm_new_wallet()
|
||||
yield # Skip Backup
|
||||
TR.assert_in(self.text_content(), "backup__new_wallet_created")
|
||||
self.debug.press_right()
|
||||
self.debug.press_no()
|
||||
yield # Confirm skip backup
|
||||
TR.assert_in(self.text_content(), "backup__want_to_skip")
|
||||
self.debug.press_no()
|
||||
|
||||
def input_flow_t3t1(self) -> BRGeneratorType:
|
||||
yield from self.BAK.confirm_new_wallet()
|
||||
yield # Skip Backup
|
||||
TR.assert_in(self.text_content(), "backup__new_wallet_created")
|
||||
self.debug.swipe_up()
|
||||
yield
|
||||
self.debug.click(buttons.CORNER_BUTTON)
|
||||
self.debug.synchronize_at("VerticalMenu")
|
||||
self.debug.click(buttons.VERTICAL_MENU[0])
|
||||
self.debug.swipe_up()
|
||||
self.debug.synchronize_at("PromptScreen")
|
||||
self.debug.click(buttons.TAP_TO_CONFIRM)
|
||||
|
||||
|
||||
class InputFlowConfirmAllWarnings(InputFlowBase):
|
||||
def __init__(self, client: Client):
|
||||
@ -2164,7 +2189,16 @@ class InputFlowConfirmAllWarnings(InputFlowBase):
|
||||
layout = self.debug.read_layout()
|
||||
text = layout.text_content().lower()
|
||||
# hi priority warning
|
||||
if ("wrong derivation path" in text) or ("to a multisig" in text):
|
||||
hi_prio = (
|
||||
TR.translate("addr_mismatch__wrong_derivation_path")
|
||||
+ TR.translate("send__receiving_to_multisig")
|
||||
+ [
|
||||
"witness path",
|
||||
"certificate path",
|
||||
"pool owner staking path",
|
||||
]
|
||||
)
|
||||
if any(needle.lower() in text for needle in hi_prio):
|
||||
self.debug.click(buttons.CORNER_BUTTON, wait=True)
|
||||
self.debug.synchronize_at("VerticalMenu")
|
||||
self.debug.click(buttons.VERTICAL_MENU[1])
|
||||
|
@ -1,6 +1,7 @@
|
||||
from trezorlib import messages, models
|
||||
from trezorlib.debuglink import TrezorClientDebugLink as Client
|
||||
|
||||
from . import buttons
|
||||
from . import translations as TR
|
||||
from .common import BRGeneratorType, get_text_possible_pagination
|
||||
|
||||
@ -51,7 +52,8 @@ class RecoveryFlow:
|
||||
self.debug = self.client.debug
|
||||
|
||||
def _text_content(self) -> str:
|
||||
return self.debug.wait_layout().text_content()
|
||||
layout = self.debug.wait_layout()
|
||||
return layout.title() + " " + layout.text_content()
|
||||
|
||||
def confirm_recovery(self) -> BRGeneratorType:
|
||||
yield
|
||||
@ -84,7 +86,10 @@ class RecoveryFlow:
|
||||
|
||||
def enter_your_backup(self) -> BRGeneratorType:
|
||||
yield
|
||||
TR.assert_in(self._text_content(), "recovery__enter_backup")
|
||||
if self.debug.model is models.T3T1:
|
||||
TR.assert_in(self._text_content(), "recovery__only_first_n_letters")
|
||||
else:
|
||||
TR.assert_in(self._text_content(), "recovery__enter_backup")
|
||||
is_dry_run = any(
|
||||
title in self.debug.wait_layout().title().lower()
|
||||
for title in TR.translate("recovery__title_dry_run", lower=True)
|
||||
@ -97,7 +102,10 @@ class RecoveryFlow:
|
||||
|
||||
def enter_any_share(self) -> BRGeneratorType:
|
||||
yield
|
||||
TR.assert_in(self._text_content(), "recovery__enter_any_share")
|
||||
TR.assert_in_multiple(
|
||||
self._text_content(),
|
||||
["recovery__enter_any_share", "recovery__only_first_n_letters"],
|
||||
)
|
||||
is_dry_run = any(
|
||||
title in self.debug.wait_layout().title().lower()
|
||||
for title in TR.translate("recovery__title_dry_run", lower=True)
|
||||
@ -112,6 +120,8 @@ class RecoveryFlow:
|
||||
yield
|
||||
if self.client.model is models.T2B1:
|
||||
TR.assert_in(self._text_content(), "recovery__num_of_words")
|
||||
elif self.client.model is models.T3T1:
|
||||
TR.assert_in(self._text_content(), "recovery__only_first_n_letters")
|
||||
else:
|
||||
TR.assert_in(self._text_content(), "recovery__enter_any_share")
|
||||
self.debug.press_no()
|
||||
@ -242,8 +252,11 @@ class RecoveryFlow:
|
||||
if index < len(shares) - 1:
|
||||
if has_groups:
|
||||
yield from self.success_share_group_entered()
|
||||
if self.client.model in (models.T2T1, models.T3T1) and click_info:
|
||||
yield from self.tt_click_info()
|
||||
if click_info:
|
||||
if self.client.model is models.T2T1:
|
||||
yield from self.tt_click_info()
|
||||
elif self.client.model is models.T3T1:
|
||||
self.mercury_click_info()
|
||||
yield from self.success_more_shares_needed()
|
||||
|
||||
def tt_click_info(
|
||||
@ -255,6 +268,13 @@ class RecoveryFlow:
|
||||
self.debug.swipe_up()
|
||||
self.debug.press_yes()
|
||||
|
||||
def mercury_click_info(self):
|
||||
self.debug.click(buttons.CORNER_BUTTON, wait=True)
|
||||
self.debug.synchronize_at("VerticalMenu")
|
||||
self.debug.click(buttons.VERTICAL_MENU[0], wait=True)
|
||||
self.debug.click(buttons.CORNER_BUTTON, wait=True)
|
||||
self.debug.click(buttons.CORNER_BUTTON, wait=True)
|
||||
|
||||
|
||||
class EthereumFlow:
|
||||
GO_BACK = (16, 220)
|
||||
@ -392,7 +412,7 @@ class EthereumFlow:
|
||||
if self.client.model in (models.T2T1, models.T3T1):
|
||||
# confirm intro
|
||||
if info:
|
||||
self.debug.press_info(wait=True)
|
||||
self.debug.click(buttons.CORNER_BUTTON, wait=True)
|
||||
TR.assert_equals_multiple(
|
||||
self.debug.wait_layout().title(),
|
||||
[
|
||||
@ -405,7 +425,7 @@ class EthereumFlow:
|
||||
yield
|
||||
|
||||
# confirm summary
|
||||
if info:
|
||||
if info and self.client.model != models.T3T1:
|
||||
self.debug.press_info(wait=True)
|
||||
TR.assert_in(
|
||||
self.debug.wait_layout().text_content(), "ethereum__gas_limit"
|
||||
|
@ -14,7 +14,9 @@
|
||||
# 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 trezorlib import device
|
||||
import pytest
|
||||
|
||||
from trezorlib import device, models
|
||||
from trezorlib.debuglink import DebugLink
|
||||
|
||||
from .. import buttons
|
||||
@ -38,6 +40,9 @@ def test_abort(core_emulator: Emulator):
|
||||
debug = device_handler.debuglink()
|
||||
features = device_handler.features()
|
||||
|
||||
if debug.model is models.T3T1:
|
||||
pytest.skip("abort not supported on T3T1")
|
||||
|
||||
assert features.recovery_mode is False
|
||||
|
||||
device_handler.run(device.recover, pin_protection=False)
|
||||
@ -132,7 +137,7 @@ def test_recovery_on_old_wallet(core_emulator: Emulator):
|
||||
words = first_share.split(" ")
|
||||
|
||||
# start entering first share
|
||||
assert "Enter any share" in debug.read_layout().text_content()
|
||||
assert "the first 2-4 letters" in debug.read_layout().text_content()
|
||||
debug.press_yes()
|
||||
assert debug.wait_layout().main_component() == "MnemonicKeyboard"
|
||||
|
||||
@ -151,7 +156,7 @@ def test_recovery_on_old_wallet(core_emulator: Emulator):
|
||||
layout = debug.wait_layout()
|
||||
|
||||
# check that we entered the first share successfully
|
||||
assert "1 of 3 shares entered" in layout.text_content()
|
||||
assert "2 more shares needed" in layout.text_content()
|
||||
|
||||
# try entering the remaining shares
|
||||
for share in MNEMONIC_SLIP39_BASIC_20_3of6[1:3]:
|
||||
@ -170,7 +175,7 @@ def test_recovery_multiple_resets(core_emulator: Emulator):
|
||||
def enter_shares_with_restarts(debug: DebugLink) -> None:
|
||||
shares = MNEMONIC_SLIP39_ADVANCED_20
|
||||
layout = debug.read_layout()
|
||||
expected_text = "Enter any share"
|
||||
expected_text = "the first 2-4 letters"
|
||||
remaining = len(shares)
|
||||
for share in shares:
|
||||
assert expected_text in layout.text_content()
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user