mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 20:38: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:
|
needs:
|
||||||
- param
|
- param
|
||||||
- core_emu
|
- core_emu
|
||||||
|
timeout-minutes: 90
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -350,6 +351,7 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- param
|
- param
|
||||||
- core_emu
|
- core_emu
|
||||||
|
timeout-minutes: 30
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
@ -569,8 +569,8 @@ impl EventCtx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_page_count(&mut self, count: usize) {
|
pub fn set_page_count(&mut self, count: usize) {
|
||||||
#[cfg(feature = "ui_debug")]
|
// #[cfg(feature = "ui_debug")]
|
||||||
assert!(self.page_count.is_none());
|
// assert!(self.page_count.unwrap_or(count) == count);
|
||||||
self.page_count = Some(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> {
|
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 {
|
if let Some(t) = &mut self.transition {
|
||||||
let finished = Self::handle_transition(ctx, event, t);
|
let finished = Self::handle_transition(ctx, event, t);
|
||||||
if finished {
|
if finished {
|
||||||
|
@ -136,7 +136,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_description(self, description: impl Into<TString<'static>>) -> Self {
|
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 {
|
pub fn with_value(self, value: impl Into<TString<'static>>) -> Self {
|
||||||
@ -154,9 +154,9 @@ where
|
|||||||
theme::BG,
|
theme::BG,
|
||||||
)),
|
)),
|
||||||
paragraphs: ParagraphVecShort::from_iter([
|
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_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(),
|
Paragraph::new(&theme::TEXT_DEMIBOLD, l3).centered(),
|
||||||
])
|
])
|
||||||
.into_paragraphs()
|
.into_paragraphs()
|
||||||
|
@ -76,7 +76,7 @@ pub struct PassphraseKeyboard {
|
|||||||
input_prompt: Child<Label<'static>>,
|
input_prompt: Child<Label<'static>>,
|
||||||
erase_btn: Child<Maybe<Button>>,
|
erase_btn: Child<Maybe<Button>>,
|
||||||
cancel_btn: Child<Maybe<Button>>,
|
cancel_btn: Child<Maybe<Button>>,
|
||||||
confirm_btn: Child<Maybe<Button>>,
|
confirm_btn: Child<Button>,
|
||||||
next_btn: Child<Button>,
|
next_btn: Child<Button>,
|
||||||
keys: [Child<Button>; KEY_COUNT],
|
keys: [Child<Button>; KEY_COUNT],
|
||||||
active_layout: KeyboardLayout,
|
active_layout: KeyboardLayout,
|
||||||
@ -87,8 +87,8 @@ const PAGE_COUNT: usize = 4;
|
|||||||
const KEY_COUNT: usize = 10;
|
const KEY_COUNT: usize = 10;
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
const KEYBOARD: [[&str; KEY_COUNT]; PAGE_COUNT] = [
|
const KEYBOARD: [[&str; KEY_COUNT]; PAGE_COUNT] = [
|
||||||
["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", "UVQ", "XYZ", " *#"],
|
["ABC", "DEF", "GHI", "JKL", "MNO", "PQ", "RST", "UVW", "XYZ", " *#"],
|
||||||
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
|
["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)
|
let confirm_btn = Button::with_icon(theme::ICON_CONFIRM)
|
||||||
.styled(theme::button_passphrase_confirm())
|
.styled(theme::button_passphrase_confirm())
|
||||||
.with_radius(15)
|
.with_radius(15)
|
||||||
.initially_enabled(false);
|
.into_child();
|
||||||
let confirm_btn = Maybe::hidden(theme::BG, confirm_btn).into_child();
|
|
||||||
|
|
||||||
let next_btn = Button::new(active_layout.next().into())
|
let next_btn = Button::new(active_layout.next().into())
|
||||||
.styled(theme::button_passphrase_next())
|
.styled(theme::button_passphrase_next())
|
||||||
@ -206,10 +205,6 @@ impl PassphraseKeyboard {
|
|||||||
btn.show_if(ctx, is_empty);
|
btn.show_if(ctx, is_empty);
|
||||||
btn.inner_mut().enable_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);
|
self.update_input_btns_state(ctx);
|
||||||
}
|
}
|
||||||
@ -381,11 +376,11 @@ impl Component for PassphraseKeyboard {
|
|||||||
self.input.render(target);
|
self.input.render(target);
|
||||||
self.next_btn.render(target);
|
self.next_btn.render(target);
|
||||||
self.erase_btn.render(target);
|
self.erase_btn.render(target);
|
||||||
|
self.confirm_btn.render(target);
|
||||||
if self.input.inner().textbox.is_empty() {
|
if self.input.inner().textbox.is_empty() {
|
||||||
self.cancel_btn.render(target);
|
self.cancel_btn.render(target);
|
||||||
self.input_prompt.render(target);
|
// FIXME: when prompt fixed in Figma
|
||||||
} else {
|
// self.input_prompt.render(target);
|
||||||
self.confirm_btn.render(target);
|
|
||||||
}
|
}
|
||||||
for btn in &self.keys {
|
for btn in &self.keys {
|
||||||
btn.render(target);
|
btn.render(target);
|
||||||
|
@ -6,6 +6,7 @@ use crate::{
|
|||||||
geometry::{Alignment2D, Offset, Rect},
|
geometry::{Alignment2D, Offset, Rect},
|
||||||
shape,
|
shape,
|
||||||
shape::Renderer,
|
shape::Renderer,
|
||||||
|
util::animation_disabled,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -97,6 +98,9 @@ impl Component for PromptScreen {
|
|||||||
(DismissType::Hold, Some(ButtonMsg::LongPressed)) => {
|
(DismissType::Hold, Some(ButtonMsg::LongPressed)) => {
|
||||||
return Some(());
|
return Some(());
|
||||||
}
|
}
|
||||||
|
(DismissType::Hold, Some(ButtonMsg::Clicked)) if animation_disabled() => {
|
||||||
|
return Some(());
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -205,5 +205,6 @@ impl<'a> crate::trace::Trace for ShareWords<'a> {
|
|||||||
let content =
|
let content =
|
||||||
word.map(|w| build_string!(50, inttostr!(self.page_index as u8 + 1), ". ", w, "\n"));
|
word.map(|w| build_string!(50, inttostr!(self.page_index as u8 + 1), ". ", w, "\n"));
|
||||||
t.string("screen_content", content.as_str().into());
|
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,
|
strutil::TString,
|
||||||
translations::TR,
|
translations::TR,
|
||||||
ui::{
|
ui::{
|
||||||
|
button_request::ButtonRequestCode,
|
||||||
component::{
|
component::{
|
||||||
text::paragraphs::{Paragraph, Paragraphs},
|
text::paragraphs::{Paragraph, Paragraphs},
|
||||||
ComponentExt, SwipeDirection,
|
ButtonRequestExt, ComponentExt, SwipeDirection,
|
||||||
},
|
},
|
||||||
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
|
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))
|
let content_intro = Frame::left_aligned(title, SwipePage::vertical(paragraphs))
|
||||||
.with_menu_button()
|
.with_menu_button()
|
||||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
.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(
|
let content_menu = Frame::left_aligned(
|
||||||
"".into(),
|
"".into(),
|
||||||
@ -106,7 +108,8 @@ impl ConfirmResetCreate {
|
|||||||
.map(|msg| match msg {
|
.map(|msg| match msg {
|
||||||
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||||
_ => Some(FlowMsg::Cancelled),
|
_ => Some(FlowMsg::Cancelled),
|
||||||
});
|
})
|
||||||
|
.one_button_request(ButtonRequestCode::ResetDevice.with_type("confirm_setup_device"));
|
||||||
|
|
||||||
let store = flow_store()
|
let store = flow_store()
|
||||||
.add(content_intro)?
|
.add(content_intro)?
|
||||||
|
@ -2,9 +2,10 @@ use crate::{
|
|||||||
error,
|
error,
|
||||||
translations::TR,
|
translations::TR,
|
||||||
ui::{
|
ui::{
|
||||||
|
button_request::ButtonRequestCode,
|
||||||
component::{
|
component::{
|
||||||
text::paragraphs::{Paragraph, Paragraphs},
|
text::paragraphs::{Paragraph, Paragraphs},
|
||||||
ComponentExt, SwipeDirection,
|
ButtonRequestExt, ComponentExt, SwipeDirection,
|
||||||
},
|
},
|
||||||
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
|
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
|
||||||
},
|
},
|
||||||
@ -80,7 +81,8 @@ impl ConfirmResetRecover {
|
|||||||
)
|
)
|
||||||
.with_menu_button()
|
.with_menu_button()
|
||||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
.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(
|
let content_menu = Frame::left_aligned(
|
||||||
"".into(),
|
"".into(),
|
||||||
|
@ -4,9 +4,10 @@ use crate::{
|
|||||||
strutil::TString,
|
strutil::TString,
|
||||||
translations::TR,
|
translations::TR,
|
||||||
ui::{
|
ui::{
|
||||||
|
button_request::ButtonRequestCode,
|
||||||
component::{
|
component::{
|
||||||
text::paragraphs::{Paragraph, Paragraphs},
|
text::paragraphs::{Paragraph, Paragraphs},
|
||||||
ComponentExt, SwipeDirection,
|
ButtonRequestExt, ComponentExt, SwipeDirection,
|
||||||
},
|
},
|
||||||
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
|
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
|
||||||
layout::obj::LayoutObj,
|
layout::obj::LayoutObj,
|
||||||
@ -79,6 +80,7 @@ impl ShowShareWords {
|
|||||||
let share_words_vec: Vec<TString, 33> = util::iter_into_vec(share_words_obj)?;
|
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_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 text_confirm: TString = kwargs.get(Qstr::MP_QSTR_text_confirm)?.try_into()?;
|
||||||
|
let nwords = share_words_vec.len();
|
||||||
|
|
||||||
let content_instruction = Frame::left_aligned(
|
let content_instruction = Frame::left_aligned(
|
||||||
title,
|
title,
|
||||||
@ -89,7 +91,9 @@ impl ShowShareWords {
|
|||||||
)
|
)
|
||||||
.with_subtitle(TR::words__instructions.into())
|
.with_subtitle(TR::words__instructions.into())
|
||||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
.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))
|
let content_words = Frame::left_aligned(title, ShareWords::new(share_words_vec))
|
||||||
.with_subtitle(subtitle)
|
.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: Color = Color::rgb(0xE7, 0x0E, 0x0E); // button
|
||||||
pub const RED_DARK: Color = Color::rgb(0xAE, 0x09, 0x09); // button pressed
|
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: 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: 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 VIOLET: Color = Color::rgb(0x95, 0x00, 0xCA);
|
||||||
|
|
||||||
pub const FATAL_ERROR_COLOR: Color = Color::rgb(0xE7, 0x0E, 0x0E);
|
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 {
|
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 {
|
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 {
|
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 {
|
pub const fn label_keyboard_minor() -> TextStyle {
|
||||||
TEXT_NORMAL_OFF_WHITE
|
TEXT_NORMAL_GREY_EXTRA_LIGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn label_warning() -> TextStyle {
|
pub const fn label_warning() -> TextStyle {
|
||||||
@ -209,7 +205,7 @@ pub const fn label_warning() -> TextStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const fn label_warning_value() -> TextStyle {
|
pub const fn label_warning_value() -> TextStyle {
|
||||||
TEXT_NORMAL_OFF_WHITE
|
TEXT_NORMAL_GREY_EXTRA_LIGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn label_recovery_title() -> TextStyle {
|
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 {
|
pub const fn label_recovery_description() -> TextStyle {
|
||||||
TEXT_NORMAL_OFF_WHITE
|
TEXT_NORMAL_GREY_EXTRA_LIGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn label_progress() -> TextStyle {
|
pub const fn label_progress() -> TextStyle {
|
||||||
@ -363,21 +359,21 @@ pub const fn button_cancel() -> ButtonStyleSheet {
|
|||||||
normal: &ButtonStyle {
|
normal: &ButtonStyle {
|
||||||
font: Font::BOLD,
|
font: Font::BOLD,
|
||||||
text_color: FG,
|
text_color: FG,
|
||||||
button_color: RED,
|
button_color: ORANGE_LIGHT,
|
||||||
icon_color: GREY_LIGHT,
|
icon_color: GREY_LIGHT,
|
||||||
background_color: BG,
|
background_color: BG,
|
||||||
},
|
},
|
||||||
active: &ButtonStyle {
|
active: &ButtonStyle {
|
||||||
font: Font::BOLD,
|
font: Font::BOLD,
|
||||||
text_color: FG,
|
text_color: FG,
|
||||||
button_color: RED_DARK,
|
button_color: ORANGE_DIMMED,
|
||||||
icon_color: GREY_LIGHT,
|
icon_color: GREY_LIGHT,
|
||||||
background_color: BG,
|
background_color: BG,
|
||||||
},
|
},
|
||||||
disabled: &ButtonStyle {
|
disabled: &ButtonStyle {
|
||||||
font: Font::BOLD,
|
font: Font::BOLD,
|
||||||
text_color: GREY_LIGHT,
|
text_color: GREY_LIGHT,
|
||||||
button_color: RED,
|
button_color: ORANGE_DIMMED,
|
||||||
icon_color: GREY_LIGHT,
|
icon_color: GREY_LIGHT,
|
||||||
background_color: BG,
|
background_color: BG,
|
||||||
},
|
},
|
||||||
@ -791,8 +787,8 @@ pub fn textstyle_number(num: i32) -> &'static TextStyle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const TEXT_NORMAL_OFF_WHITE: TextStyle =
|
pub const TEXT_NORMAL_GREY_EXTRA_LIGHT: TextStyle =
|
||||||
TextStyle::new(Font::NORMAL, OFF_WHITE, BG, GREY_LIGHT, GREY_LIGHT);
|
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_DEFAULT: TextStyle = TextStyle::new(Font::SUB, GREY, BG, GREY, GREY);
|
||||||
pub const TEXT_CHECKLIST_SELECTED: TextStyle =
|
pub const TEXT_CHECKLIST_SELECTED: TextStyle =
|
||||||
TextStyle::new(Font::NORMAL, GREY_LIGHT, BG, GREY_LIGHT, GREY_LIGHT);
|
TextStyle::new(Font::NORMAL, GREY_LIGHT, BG, GREY_LIGHT, GREY_LIGHT);
|
||||||
|
@ -91,6 +91,7 @@ async def request_mnemonic(
|
|||||||
# show_identifier_mismatch
|
# show_identifier_mismatch
|
||||||
await show_recovery_warning(
|
await show_recovery_warning(
|
||||||
"warning_mismatched_share",
|
"warning_mismatched_share",
|
||||||
|
"",
|
||||||
TR.recovery__share_from_another_multi_share_backup,
|
TR.recovery__share_from_another_multi_share_backup,
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
@ -129,14 +130,14 @@ async def show_invalid_mnemonic(word_count: int) -> None:
|
|||||||
if backup_types.is_slip39_word_count(word_count):
|
if backup_types.is_slip39_word_count(word_count):
|
||||||
await show_recovery_warning(
|
await show_recovery_warning(
|
||||||
"warning_invalid_share",
|
"warning_invalid_share",
|
||||||
TR.recovery__invalid_share_entered,
|
|
||||||
TR.words__please_try_again,
|
TR.words__please_try_again,
|
||||||
|
TR.recovery__invalid_share_entered,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
await show_recovery_warning(
|
await show_recovery_warning(
|
||||||
"warning_invalid_seed",
|
"warning_invalid_seed",
|
||||||
TR.recovery__invalid_wallet_backup_entered,
|
|
||||||
TR.words__please_try_again,
|
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:
|
async def confirm_reset_device(_title: str, recovery: bool = False) -> None:
|
||||||
if recovery:
|
if recovery:
|
||||||
await raise_if_not_confirmed(
|
await raise_if_not_confirmed(
|
||||||
interact(
|
RustLayout(trezorui2.flow_confirm_reset_recover()),
|
||||||
RustLayout(trezorui2.flow_confirm_reset_recover()),
|
|
||||||
"recover_device",
|
|
||||||
ButtonRequestType.ProtectCall,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
await raise_if_not_confirmed(
|
await raise_if_not_confirmed(
|
||||||
interact(
|
RustLayout(trezorui2.flow_confirm_reset_create()),
|
||||||
RustLayout(trezorui2.flow_confirm_reset_create()),
|
|
||||||
"setup_device",
|
|
||||||
ButtonRequestType.ResetDevice,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -158,8 +158,10 @@ async def show_recovery_warning(
|
|||||||
interact(
|
interact(
|
||||||
RustLayout(
|
RustLayout(
|
||||||
trezorui2.show_warning(
|
trezorui2.show_warning(
|
||||||
title=content,
|
title=content or TR.words__warning,
|
||||||
value="Try again", # TODO: use TR
|
value=subheader or "",
|
||||||
|
button=button,
|
||||||
|
description="",
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
br_type,
|
br_type,
|
||||||
|
@ -35,18 +35,14 @@ async def show_share_words(
|
|||||||
text_info = TR.reset__write_down_words_template.format(words_count)
|
text_info = TR.reset__write_down_words_template.format(words_count)
|
||||||
text_confirm = TR.reset__words_written_down_template.format(words_count)
|
text_confirm = TR.reset__words_written_down_template.format(words_count)
|
||||||
|
|
||||||
result = await interact(
|
result = await RustLayout(
|
||||||
RustLayout(
|
trezorui2.flow_show_share_words(
|
||||||
trezorui2.flow_show_share_words(
|
title=title,
|
||||||
title=title,
|
subtitle=subtitle,
|
||||||
subtitle=subtitle,
|
words=share_words,
|
||||||
words=share_words,
|
text_info=text_info,
|
||||||
text_info=text_info,
|
text_confirm=text_confirm,
|
||||||
text_confirm=text_confirm,
|
)
|
||||||
)
|
|
||||||
),
|
|
||||||
"backup_words",
|
|
||||||
ButtonRequestType.ResetDevice,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if result != CONFIRMED:
|
if result != CONFIRMED:
|
||||||
@ -341,10 +337,10 @@ async def show_reset_warning(
|
|||||||
interact(
|
interact(
|
||||||
RustLayout(
|
RustLayout(
|
||||||
trezorui2.show_warning(
|
trezorui2.show_warning(
|
||||||
title=subheader or "",
|
title=content or TR.words__warning,
|
||||||
description="",
|
description="",
|
||||||
value=content,
|
value=subheader or "",
|
||||||
button="",
|
button=button,
|
||||||
allow_cancel=False,
|
allow_cancel=False,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"current": {
|
"current": {
|
||||||
"merkle_root": "e79fdd3c9052dfd140aef2b4800b6b0b8000ae99822cf3e8be083da0dc2b376c",
|
"merkle_root": "e406bb0127f82bf476884b46c05ca86993c22e1373c4274443f2aef20b138a8e",
|
||||||
"datetime": "2024-05-26T15:18:17.124044",
|
"datetime": "2024-05-28T00:05:41.698473",
|
||||||
"commit": "3e9598245d57044418f4eeb5ce5bc792f20587c3"
|
"commit": "342b88f62bcd34595ea81ced550bb579b4e4a813"
|
||||||
},
|
},
|
||||||
"history": [
|
"history": [
|
||||||
{
|
{
|
||||||
|
@ -31,8 +31,24 @@ CORNER_BUTTON = (215, 25)
|
|||||||
CONFIRM_WORD = (MID, TOP)
|
CONFIRM_WORD = (MID, TOP)
|
||||||
TOP_ROW = (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 = [
|
RESET_WORD_CHECK = [
|
||||||
(MID, grid(DISPLAY_HEIGHT, 5, 2)),
|
(MID, grid(DISPLAY_HEIGHT, 5, 2)),
|
||||||
|
@ -22,7 +22,9 @@ def enter_word(
|
|||||||
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)
|
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)
|
return debug.click(buttons.CONFIRM_WORD, wait=True)
|
||||||
elif debug.model in (models.T2B1,):
|
elif debug.model in (models.T2B1,):
|
||||||
letter_index = 0
|
letter_index = 0
|
||||||
@ -49,8 +51,10 @@ def enter_word(
|
|||||||
def confirm_recovery(debug: "DebugLink") -> None:
|
def confirm_recovery(debug: "DebugLink") -> None:
|
||||||
layout = debug.wait_layout()
|
layout = debug.wait_layout()
|
||||||
TR.assert_equals(layout.title(), "recovery__title")
|
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)
|
debug.click(buttons.OK, wait=True)
|
||||||
|
elif debug.model in (models.T3T1,):
|
||||||
|
debug.swipe_up(wait=True)
|
||||||
elif debug.model in (models.T2B1,):
|
elif debug.model in (models.T2B1,):
|
||||||
debug.press_right(wait=True)
|
debug.press_right(wait=True)
|
||||||
debug.press_right()
|
debug.press_right()
|
||||||
@ -99,20 +103,29 @@ def select_number_of_words(
|
|||||||
raise ValueError("Unknown model")
|
raise ValueError("Unknown model")
|
||||||
|
|
||||||
if num_of_words in (20, 33):
|
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:
|
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(
|
def enter_share(
|
||||||
debug: "DebugLink", share: str, is_first: bool = True
|
debug: "DebugLink", share: str, is_first: bool = True
|
||||||
) -> "LayoutContent":
|
) -> "LayoutContent":
|
||||||
TR.assert_in(debug.read_layout().title(), "recovery__title_recover")
|
|
||||||
if debug.model in (models.T2B1,):
|
if debug.model in (models.T2B1,):
|
||||||
|
TR.assert_in(debug.read_layout().title(), "recovery__title_recover")
|
||||||
layout = debug.wait_layout()
|
layout = debug.wait_layout()
|
||||||
for _ in range(layout.page_count()):
|
for _ in range(layout.page_count()):
|
||||||
layout = debug.press_right(wait=True)
|
layout = debug.press_right(wait=True)
|
||||||
|
elif debug.model in (models.T3T1,):
|
||||||
|
layout = debug.swipe_up(wait=True)
|
||||||
else:
|
else:
|
||||||
|
TR.assert_in(debug.read_layout().title(), "recovery__title_recover")
|
||||||
layout = debug.click(buttons.OK, wait=True)
|
layout = debug.click(buttons.OK, wait=True)
|
||||||
|
|
||||||
assert "MnemonicKeyboard" in layout.all_components()
|
assert "MnemonicKeyboard" in layout.all_components()
|
||||||
@ -124,15 +137,20 @@ def enter_share(
|
|||||||
|
|
||||||
|
|
||||||
def enter_shares(debug: "DebugLink", shares: list[str]) -> None:
|
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):
|
for index, share in enumerate(shares):
|
||||||
enter_share(debug, share, is_first=index == 0)
|
enter_share(debug, share, is_first=index == 0)
|
||||||
if index < len(shares) - 1:
|
if index < len(shares) - 1:
|
||||||
TR.assert_in(
|
# FIXME: when ui-t3t1 done for shamir, we want to check the template below
|
||||||
debug.read_layout().text_content(),
|
TR.assert_in(debug.read_layout().title(), "recovery__title_recover")
|
||||||
"recovery__x_of_y_entered_template",
|
# TR.assert_in(
|
||||||
template=(index + 1, len(shares)),
|
# 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")
|
TR.assert_in(debug.read_layout().text_content(), "recovery__wallet_recovered")
|
||||||
|
|
||||||
@ -188,13 +206,19 @@ def enter_seed_previous_correct(
|
|||||||
i += 1
|
i += 1
|
||||||
layout = enter_word(debug, word, is_slip39=False)
|
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:
|
def prepare_enter_seed(debug: "DebugLink") -> None:
|
||||||
TR.assert_in(debug.read_layout().text_content(), "recovery__enter_backup")
|
TR.assert_in_multiple(
|
||||||
if debug.model in (models.T2T1, models.T3T1):
|
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)
|
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,):
|
elif debug.model in (models.T2B1,):
|
||||||
debug.press_right(wait=True)
|
debug.press_right(wait=True)
|
||||||
TR.assert_equals(debug.read_layout().title(), "recovery__title_recover")
|
TR.assert_equals(debug.read_layout().title(), "recovery__title_recover")
|
||||||
|
@ -14,16 +14,22 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
def confirm_new_wallet(debug: "DebugLink") -> None:
|
def confirm_new_wallet(debug: "DebugLink") -> None:
|
||||||
TR.assert_equals(debug.read_layout().title(), "reset__title_create_wallet")
|
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)
|
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,):
|
elif debug.model in (models.T2B1,):
|
||||||
debug.press_right(wait=True)
|
debug.press_right(wait=True)
|
||||||
debug.press_right(wait=True)
|
debug.press_right(wait=True)
|
||||||
|
|
||||||
|
|
||||||
def confirm_read(debug: "DebugLink", middle_r: bool = False) -> None:
|
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)
|
debug.click(buttons.OK, wait=True)
|
||||||
|
elif debug.model in (models.T3T1,):
|
||||||
|
debug.swipe_up(wait=True)
|
||||||
elif debug.model in (models.T2B1,):
|
elif debug.model in (models.T2B1,):
|
||||||
page_count = debug.read_layout().page_count()
|
page_count = debug.read_layout().page_count()
|
||||||
if page_count > 1:
|
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:
|
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)
|
||||||
|
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,):
|
elif debug.model in (models.T2B1,):
|
||||||
debug.press_left(wait=True)
|
debug.press_left(wait=True)
|
||||||
|
debug.press_left(wait=True)
|
||||||
|
|
||||||
|
|
||||||
def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> None:
|
def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> None:
|
||||||
if debug.model in (models.T2T1, models.T3T1):
|
if debug.model in (models.T2T1, models.T3T1):
|
||||||
assert "NumberInputDialog" in debug.read_layout().all_components()
|
assert "NumberInputDialog" in debug.read_layout().all_components()
|
||||||
for _ in range(diff):
|
for _ in range(diff):
|
||||||
debug.click(button)
|
debug.click(button, wait=True)
|
||||||
debug.click(buttons.OK, 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,):
|
elif debug.model in (models.T2B1,):
|
||||||
layout = debug.read_layout()
|
layout = debug.read_layout()
|
||||||
if layout.title() in TR.translate(
|
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
|
# Special info screens
|
||||||
layout = debug.press_right(wait=True)
|
layout = debug.press_right(wait=True)
|
||||||
assert "NumberInput" in layout.all_components()
|
assert "NumberInput" in layout.all_components()
|
||||||
if button == buttons.RESET_MINUS:
|
if button == buttons.reset_minus(debug.model.internal_name):
|
||||||
for _ in range(diff):
|
for _ in range(diff):
|
||||||
debug.press_left(wait=True)
|
debug.press_left(wait=True)
|
||||||
else:
|
else:
|
||||||
@ -72,6 +88,8 @@ def read_words(
|
|||||||
|
|
||||||
if debug.model in (models.T2B1,):
|
if debug.model in (models.T2B1,):
|
||||||
debug.press_right(wait=True)
|
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
|
# Swiping through all the pages and loading the words
|
||||||
layout = debug.read_layout()
|
layout = debug.read_layout()
|
||||||
@ -82,10 +100,15 @@ def read_words(
|
|||||||
if debug.model in (models.T2T1, models.T3T1):
|
if debug.model in (models.T2T1, models.T3T1):
|
||||||
words.extend(layout.seed_words())
|
words.extend(layout.seed_words())
|
||||||
|
|
||||||
|
if debug.model in (models.T3T1,):
|
||||||
|
debug.swipe_up(wait=True)
|
||||||
|
|
||||||
# There is hold-to-confirm button
|
# There is hold-to-confirm button
|
||||||
if do_htc:
|
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)
|
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,):
|
elif debug.model in (models.T2B1,):
|
||||||
debug.press_right_htc(1200)
|
debug.press_right_htc(1200)
|
||||||
else:
|
else:
|
||||||
@ -97,6 +120,9 @@ def read_words(
|
|||||||
|
|
||||||
|
|
||||||
def confirm_words(debug: "DebugLink", words: list[str]) -> None:
|
def confirm_words(debug: "DebugLink", words: list[str]) -> None:
|
||||||
|
if debug.model in (models.T3T1,):
|
||||||
|
debug.swipe_up(wait=True)
|
||||||
|
|
||||||
layout = debug.wait_layout()
|
layout = debug.wait_layout()
|
||||||
if debug.model in (models.T2T1, models.T3T1):
|
if debug.model in (models.T2T1, models.T3T1):
|
||||||
TR.assert_template(layout.text_content(), "reset__select_word_x_of_y_template")
|
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()
|
wanted_word = words[word_pos - 1].lower()
|
||||||
button_pos = btn_texts.index(wanted_word)
|
button_pos = btn_texts.index(wanted_word)
|
||||||
layout = debug.click(buttons.RESET_WORD_CHECK[button_pos], wait=True)
|
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,):
|
elif debug.model in (models.T2B1,):
|
||||||
TR.assert_in(layout.text_content(), "reset__select_correct_word")
|
TR.assert_in(layout.text_content(), "reset__select_correct_word")
|
||||||
layout = debug.press_right(wait=True)
|
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(" ", "")
|
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)
|
debug.click(buttons.OK, wait=True)
|
||||||
layout = debug.click(buttons.OK, wait=True)
|
layout = debug.click(buttons.OK, wait=True)
|
||||||
TR.assert_in(layout.text_content(), "send__total_amount")
|
TR.assert_in(layout.text_content(), "send__total_amount")
|
||||||
assert "0.0039 BTC" in layout.text_content()
|
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,):
|
elif debug.model in (models.T2B1,):
|
||||||
debug.press_right(wait=True)
|
debug.press_right(wait=True)
|
||||||
layout = 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(" ", "")
|
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)
|
debug.click(buttons.OK, wait=True)
|
||||||
layout = debug.click(buttons.OK, wait=True)
|
layout = debug.click(buttons.OK, wait=True)
|
||||||
TR.assert_in(layout.text_content(), "send__total_amount")
|
TR.assert_in(layout.text_content(), "send__total_amount")
|
||||||
assert "0.0039 BTC" in layout.text_content()
|
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,):
|
elif debug.model in (models.T2B1,):
|
||||||
debug.press_right(wait=True)
|
debug.press_right(wait=True)
|
||||||
layout = 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:
|
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
|
||||||
if debug.model in (models.T2T1, models.T3T1):
|
if debug.model in (models.T2T1,):
|
||||||
debug.click(buttons.OK)
|
debug.click(buttons.OK)
|
||||||
|
elif debug.model in (models.T3T1,):
|
||||||
|
debug.click(buttons.TAP_TO_CONFIRM)
|
||||||
elif debug.model in (models.T2B1,):
|
elif debug.model in (models.T2B1,):
|
||||||
debug.press_middle()
|
debug.press_middle()
|
||||||
|
|
||||||
@ -197,9 +210,12 @@ def test_autolock_passphrase_keyboard(device_handler: "BackgroundDeviceHandler")
|
|||||||
# enter passphrase - slowly
|
# enter passphrase - slowly
|
||||||
# keep clicking for long enough to trigger the autolock if it incorrectly ignored key presses
|
# keep clicking for long enough to trigger the autolock if it incorrectly ignored key presses
|
||||||
for _ in range(math.ceil(11 / 1.5)):
|
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"
|
# click at "j"
|
||||||
debug.click(CENTER_BUTTON)
|
debug.click(CENTER_BUTTON)
|
||||||
|
elif debug.model in (models.T3T1,):
|
||||||
|
# click at "j"
|
||||||
|
debug.click((20, 120))
|
||||||
elif debug.model in (models.T2B1,):
|
elif debug.model in (models.T2B1,):
|
||||||
# just go right
|
# just go right
|
||||||
# NOTE: because of passphrase randomization it would be a pain to input
|
# 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)
|
time.sleep(1.5)
|
||||||
|
|
||||||
# Send the passphrase to the client (TT has it clicked already, TR needs to input it)
|
# Send the passphrase to the client (TT has it clicked already, TR needs to input it)
|
||||||
if debug.model in (models.T2T1, models.T3T1):
|
if debug.model in (models.T2T1,):
|
||||||
debug.click(buttons.OK, wait=True)
|
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,):
|
elif debug.model in (models.T2B1,):
|
||||||
debug.input("j" * 8, wait=True)
|
debug.input("j" * 8, wait=True)
|
||||||
|
|
||||||
@ -327,10 +345,21 @@ def test_dryrun_enter_word_slowly(device_handler: "BackgroundDeviceHandler"):
|
|||||||
# select 20 words
|
# select 20 words
|
||||||
recovery.select_number_of_words(debug, 20)
|
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)
|
layout = debug.click(buttons.OK, wait=True)
|
||||||
assert layout.main_component() == "MnemonicKeyboard"
|
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
|
# 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)
|
||||||
|
@ -64,9 +64,6 @@ def test_backup_slip39_custom(
|
|||||||
# cancel back up
|
# cancel back up
|
||||||
reset.cancel_backup(debug)
|
reset.cancel_backup(debug)
|
||||||
|
|
||||||
# confirm cancel
|
|
||||||
reset.cancel_backup(debug)
|
|
||||||
|
|
||||||
assert device_handler.result() == "Initialized"
|
assert device_handler.result() == "Initialized"
|
||||||
|
|
||||||
device_handler.run(
|
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
|
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
|
# TODO: it is not possible to cancel the passphrase entry on TT
|
||||||
# NOTE: the prompt (underscoring) is not there when a space is entered
|
# 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)
|
reset.confirm_read(debug)
|
||||||
|
|
||||||
# set num of groups - default is 5
|
# set num of groups - default is 5
|
||||||
|
assert debug.model is not None
|
||||||
|
model_name: str = debug.model.internal_name
|
||||||
if group_count < 5:
|
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:
|
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
|
# confirm checklist
|
||||||
reset.confirm_read(debug)
|
reset.confirm_read(debug)
|
||||||
@ -79,9 +81,9 @@ def test_reset_slip39_advanced(
|
|||||||
# set group threshold
|
# set group threshold
|
||||||
# TODO: could make it general as well
|
# TODO: could make it general as well
|
||||||
if group_count == 2 and group_threshold == 2:
|
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:
|
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:
|
else:
|
||||||
raise RuntimeError("not a supported combination")
|
raise RuntimeError("not a supported combination")
|
||||||
|
|
||||||
@ -92,16 +94,16 @@ def test_reset_slip39_advanced(
|
|||||||
for _ in range(group_count):
|
for _ in range(group_count):
|
||||||
# set num of shares - default is 5
|
# set num of shares - default is 5
|
||||||
if share_count < 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:
|
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
|
# set share threshold
|
||||||
# TODO: could make it general as well
|
# TODO: could make it general as well
|
||||||
if share_count == 2 and share_threshold == 2:
|
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:
|
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:
|
else:
|
||||||
raise RuntimeError("not a supported combination")
|
raise RuntimeError("not a supported combination")
|
||||||
|
|
||||||
|
@ -65,10 +65,12 @@ def test_reset_slip39_basic(
|
|||||||
reset.confirm_read(debug)
|
reset.confirm_read(debug)
|
||||||
|
|
||||||
# set num of shares - default is 5
|
# 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:
|
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:
|
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
|
# confirm checklist
|
||||||
reset.confirm_read(debug)
|
reset.confirm_read(debug)
|
||||||
@ -76,9 +78,9 @@ def test_reset_slip39_basic(
|
|||||||
# set threshold
|
# set threshold
|
||||||
# TODO: could make it general as well
|
# TODO: could make it general as well
|
||||||
if num_of_shares == 1 and threshold == 1:
|
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:
|
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:
|
else:
|
||||||
raise RuntimeError("not a supported combination")
|
raise RuntimeError("not a supported combination")
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ import pytest
|
|||||||
|
|
||||||
from trezorlib import btc, messages, models, tools
|
from trezorlib import btc, messages, models, tools
|
||||||
|
|
||||||
|
from . import buttons
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from _pytest.mark.structures import MarkDecorator
|
from _pytest.mark.structures import MarkDecorator
|
||||||
|
|
||||||
@ -255,12 +257,16 @@ def read_mnemonic_from_screen_mercury(
|
|||||||
assert br.pages is not None
|
assert br.pages is not None
|
||||||
|
|
||||||
debug.wait_layout()
|
debug.wait_layout()
|
||||||
|
debug.swipe_up()
|
||||||
|
|
||||||
for _ in range(br.pages):
|
for _ in range(br.pages - 2):
|
||||||
words = debug.wait_layout().seed_words()
|
words = debug.wait_layout().seed_words()
|
||||||
mnemonic.extend(words)
|
mnemonic.extend(words)
|
||||||
debug.swipe_up()
|
debug.swipe_up()
|
||||||
|
|
||||||
|
debug.wait_layout()
|
||||||
|
debug.press_yes()
|
||||||
|
|
||||||
return mnemonic
|
return mnemonic
|
||||||
|
|
||||||
|
|
||||||
@ -298,6 +304,15 @@ def click_info_button_tt(debug: "DebugLink"):
|
|||||||
debug.press_yes()
|
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:
|
def check_pin_backoff_time(attempts: int, start: float) -> None:
|
||||||
"""Helper to assert the exponentially growing delay after incorrect PIN attempts"""
|
"""Helper to assert the exponentially growing delay after incorrect PIN attempts"""
|
||||||
expected = (2**attempts) - 1
|
expected = (2**attempts) - 1
|
||||||
|
@ -21,6 +21,7 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
|
|||||||
from trezorlib.exceptions import TrezorFailure
|
from trezorlib.exceptions import TrezorFailure
|
||||||
|
|
||||||
from ...common import parametrize_using_common_fixtures
|
from ...common import parametrize_using_common_fixtures
|
||||||
|
from ...input_flows import InputFlowConfirmAllWarnings
|
||||||
|
|
||||||
pytestmark = [
|
pytestmark = [
|
||||||
pytest.mark.altcoin,
|
pytest.mark.altcoin,
|
||||||
@ -53,7 +54,11 @@ def show_details_input_flow(client: Client):
|
|||||||
"cardano/sign_tx.slip39.json",
|
"cardano/sign_tx.slip39.json",
|
||||||
)
|
)
|
||||||
def test_cardano_sign_tx(client: Client, parameters, result):
|
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)
|
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)
|
_test_secret(client, shares, secret)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip_t3t1(reason="currently broken on T3T1")
|
||||||
@pytest.mark.parametrize("shares, secret", VECTORS)
|
@pytest.mark.parametrize("shares, secret", VECTORS)
|
||||||
@pytest.mark.setup_client(uninitialized=True)
|
@pytest.mark.setup_client(uninitialized=True)
|
||||||
def test_secret_click_info_button(client: Client, shares: list[str], secret: str):
|
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.debuglink import TrezorClientDebugLink as Client
|
||||||
from trezorlib.tools import parse_path
|
from trezorlib.tools import parse_path
|
||||||
|
|
||||||
|
from ..input_flows import InputFlowConfirmAllWarnings
|
||||||
|
|
||||||
HERE = Path(__file__).parent.resolve()
|
HERE = Path(__file__).parent.resolve()
|
||||||
|
|
||||||
|
|
||||||
@ -364,6 +366,8 @@ def test_safety_checks(client: Client):
|
|||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[messages.ButtonRequest, messages.ButtonRequest, messages.Address]
|
[messages.ButtonRequest, messages.ButtonRequest, messages.Address]
|
||||||
)
|
)
|
||||||
|
IF = InputFlowConfirmAllWarnings(client)
|
||||||
|
client.set_input_flow(IF.get())
|
||||||
get_bad_address()
|
get_bad_address()
|
||||||
|
|
||||||
with client:
|
with client:
|
||||||
@ -388,6 +392,9 @@ def test_safety_checks(client: Client):
|
|||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[messages.ButtonRequest, messages.ButtonRequest, messages.Address]
|
[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()
|
get_bad_address()
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ from . import translations as TR
|
|||||||
from .common import (
|
from .common import (
|
||||||
BRGeneratorType,
|
BRGeneratorType,
|
||||||
check_pin_backoff_time,
|
check_pin_backoff_time,
|
||||||
|
click_info_button_mercury,
|
||||||
click_info_button_tt,
|
click_info_button_tt,
|
||||||
click_through,
|
click_through,
|
||||||
get_text_possible_pagination,
|
get_text_possible_pagination,
|
||||||
@ -1307,7 +1308,7 @@ class InputFlowBip39ResetBackup(InputFlowBase):
|
|||||||
# 1. Confirm Reset x3
|
# 1. Confirm Reset x3
|
||||||
# 2. Backup your seed
|
# 2. Backup your seed
|
||||||
# 3. Confirm warning
|
# 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
|
# mnemonic phrases and rest
|
||||||
self.mnemonic = yield from get_mnemonic_and_confirm_success(self.debug)
|
self.mnemonic = yield from get_mnemonic_and_confirm_success(self.debug)
|
||||||
@ -1329,6 +1330,11 @@ class InputFlowBip39ResetPIN(InputFlowBase):
|
|||||||
assert br.code == B.ResetDevice
|
assert br.code == B.ResetDevice
|
||||||
self.debug.press_yes()
|
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
|
br = yield # Backup your seed
|
||||||
assert br.code == B.ResetDevice
|
assert br.code == B.ResetDevice
|
||||||
self.debug.press_yes()
|
self.debug.press_yes()
|
||||||
@ -1355,10 +1361,12 @@ class InputFlowBip39ResetFailedCheck(InputFlowBase):
|
|||||||
self.mnemonic = None
|
self.mnemonic = None
|
||||||
|
|
||||||
def input_flow_common(self) -> BRGeneratorType:
|
def input_flow_common(self) -> BRGeneratorType:
|
||||||
|
screens = 4 if self.debug.model is models.T3T1 else 3
|
||||||
# 1. Confirm Reset
|
# 1. Confirm Reset
|
||||||
|
# 1a. (T3T1) done
|
||||||
# 2. Backup your seed
|
# 2. Backup your seed
|
||||||
# 3. Confirm warning
|
# 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
|
# mnemonic phrases, wrong answer
|
||||||
self.mnemonic = yield from read_and_confirm_mnemonic(
|
self.mnemonic = yield from read_and_confirm_mnemonic(
|
||||||
@ -1458,21 +1466,22 @@ class InputFlowSlip39BasicBackup(InputFlowBase):
|
|||||||
|
|
||||||
def input_flow_t3t1(self) -> BRGeneratorType:
|
def input_flow_t3t1(self) -> BRGeneratorType:
|
||||||
yield # 1. Checklist
|
yield # 1. Checklist
|
||||||
self.debug.press_yes()
|
self.debug.wait_layout()
|
||||||
|
self.debug.swipe_up(wait=True)
|
||||||
if self.click_info:
|
if self.click_info:
|
||||||
yield from click_info_button_tt(self.debug)
|
click_info_button_mercury(self.debug)
|
||||||
yield # 2. Number of shares (5)
|
yield # 2. Number of shares (5)
|
||||||
self.debug.press_yes()
|
self.debug.swipe_up()
|
||||||
yield # 3. Checklist
|
yield # 3. Checklist
|
||||||
self.debug.press_yes()
|
self.debug.swipe_up(wait=True)
|
||||||
if self.click_info:
|
if self.click_info:
|
||||||
yield from click_info_button_tt(self.debug)
|
click_info_button_mercury(self.debug)
|
||||||
yield # 4. Threshold (3)
|
yield # 4. Threshold (3)
|
||||||
self.debug.press_yes()
|
self.debug.swipe_up()
|
||||||
yield # 5. Checklist
|
yield # 5. Checklist
|
||||||
self.debug.press_yes()
|
self.debug.swipe_up()
|
||||||
yield # 6. Confirm show seeds
|
yield # 6. Confirm show seeds
|
||||||
self.debug.press_yes()
|
self.debug.swipe_up()
|
||||||
|
|
||||||
# Mnemonic phrases
|
# Mnemonic phrases
|
||||||
self.mnemonics = yield from load_N_shares(self.debug, 5)
|
self.mnemonics = yield from load_N_shares(self.debug, 5)
|
||||||
@ -1543,7 +1552,7 @@ class InputFlowSlip39BasicResetRecovery(InputFlowBase):
|
|||||||
# 6. threshold info
|
# 6. threshold info
|
||||||
# 7. Set & confirm threshold value
|
# 7. Set & confirm threshold value
|
||||||
# 8. Confirm show seeds
|
# 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
|
# Mnemonic phrases
|
||||||
self.mnemonics = yield from load_N_shares(self.debug, 5)
|
self.mnemonics = yield from load_N_shares(self.debug, 5)
|
||||||
@ -1695,28 +1704,28 @@ class InputFlowSlip39AdvancedBackup(InputFlowBase):
|
|||||||
|
|
||||||
def input_flow_t3t1(self) -> BRGeneratorType:
|
def input_flow_t3t1(self) -> BRGeneratorType:
|
||||||
yield # 1. Checklist
|
yield # 1. Checklist
|
||||||
self.debug.press_yes()
|
self.debug.swipe_up(wait=True)
|
||||||
if self.click_info:
|
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
|
yield # 2. Set and confirm group count
|
||||||
self.debug.press_yes()
|
self.debug.swipe_up()
|
||||||
yield # 3. Checklist
|
yield # 3. Checklist
|
||||||
self.debug.press_yes()
|
self.debug.swipe_up(wait=True)
|
||||||
if self.click_info:
|
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
|
yield # 4. Set and confirm group threshold
|
||||||
self.debug.press_yes()
|
self.debug.swipe_up()
|
||||||
yield # 5. Checklist
|
yield # 5. Checklist
|
||||||
self.debug.press_yes()
|
self.debug.swipe_up(wait=True)
|
||||||
for _ in range(5): # for each of 5 groups
|
for _i in range(5): # for each of 5 groups
|
||||||
if self.click_info:
|
if self.click_info:
|
||||||
yield from click_info_button_tt(self.debug)
|
click_info_button_mercury(self.debug)
|
||||||
yield # Set & Confirm number of shares
|
yield # Set & Confirm number of shares
|
||||||
self.debug.press_yes()
|
self.debug.swipe_up(wait=True)
|
||||||
if self.click_info:
|
if self.click_info:
|
||||||
yield from click_info_button_tt(self.debug)
|
click_info_button_mercury(self.debug)
|
||||||
yield # Set & confirm share threshold value
|
yield # Set & confirm share threshold value
|
||||||
self.debug.press_yes()
|
self.debug.swipe_up(wait=_i != 4)
|
||||||
yield # Confirm show seeds
|
yield # Confirm show seeds
|
||||||
self.debug.press_yes()
|
self.debug.press_yes()
|
||||||
|
|
||||||
@ -1801,7 +1810,7 @@ class InputFlowSlip39AdvancedResetRecovery(InputFlowBase):
|
|||||||
# 1. Set & Confirm number of shares
|
# 1. Set & Confirm number of shares
|
||||||
# 2. Set & confirm share threshold value
|
# 2. Set & confirm share threshold value
|
||||||
# 18. Confirm show seeds
|
# 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
|
# Mnemonic phrases - show & confirm shares for all groups
|
||||||
self.mnemonics = yield from load_5_groups_5_shares(self.debug)
|
self.mnemonics = yield from load_5_groups_5_shares(self.debug)
|
||||||
@ -2119,22 +2128,38 @@ class InputFlowResetSkipBackup(InputFlowBase):
|
|||||||
def __init__(self, client: Client):
|
def __init__(self, client: Client):
|
||||||
super().__init__(client)
|
super().__init__(client)
|
||||||
|
|
||||||
def input_flow_common(self) -> BRGeneratorType:
|
def input_flow_tt(self) -> BRGeneratorType:
|
||||||
yield from self.BAK.confirm_new_wallet()
|
yield from self.BAK.confirm_new_wallet()
|
||||||
yield # Skip Backup
|
yield # Skip Backup
|
||||||
info_path = (
|
TR.assert_in(self.text_content(), "backup__new_wallet_successfully_created")
|
||||||
"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()
|
|
||||||
self.debug.press_no()
|
self.debug.press_no()
|
||||||
yield # Confirm skip backup
|
yield # Confirm skip backup
|
||||||
TR.assert_in(self.text_content(), "backup__want_to_skip")
|
TR.assert_in(self.text_content(), "backup__want_to_skip")
|
||||||
self.debug.press_no()
|
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):
|
class InputFlowConfirmAllWarnings(InputFlowBase):
|
||||||
def __init__(self, client: Client):
|
def __init__(self, client: Client):
|
||||||
@ -2164,7 +2189,16 @@ class InputFlowConfirmAllWarnings(InputFlowBase):
|
|||||||
layout = self.debug.read_layout()
|
layout = self.debug.read_layout()
|
||||||
text = layout.text_content().lower()
|
text = layout.text_content().lower()
|
||||||
# hi priority warning
|
# 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.click(buttons.CORNER_BUTTON, wait=True)
|
||||||
self.debug.synchronize_at("VerticalMenu")
|
self.debug.synchronize_at("VerticalMenu")
|
||||||
self.debug.click(buttons.VERTICAL_MENU[1])
|
self.debug.click(buttons.VERTICAL_MENU[1])
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from trezorlib import messages, models
|
from trezorlib import messages, models
|
||||||
from trezorlib.debuglink import TrezorClientDebugLink as Client
|
from trezorlib.debuglink import TrezorClientDebugLink as Client
|
||||||
|
|
||||||
|
from . import buttons
|
||||||
from . import translations as TR
|
from . import translations as TR
|
||||||
from .common import BRGeneratorType, get_text_possible_pagination
|
from .common import BRGeneratorType, get_text_possible_pagination
|
||||||
|
|
||||||
@ -51,7 +52,8 @@ class RecoveryFlow:
|
|||||||
self.debug = self.client.debug
|
self.debug = self.client.debug
|
||||||
|
|
||||||
def _text_content(self) -> str:
|
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:
|
def confirm_recovery(self) -> BRGeneratorType:
|
||||||
yield
|
yield
|
||||||
@ -84,7 +86,10 @@ class RecoveryFlow:
|
|||||||
|
|
||||||
def enter_your_backup(self) -> BRGeneratorType:
|
def enter_your_backup(self) -> BRGeneratorType:
|
||||||
yield
|
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(
|
is_dry_run = any(
|
||||||
title in self.debug.wait_layout().title().lower()
|
title in self.debug.wait_layout().title().lower()
|
||||||
for title in TR.translate("recovery__title_dry_run", lower=True)
|
for title in TR.translate("recovery__title_dry_run", lower=True)
|
||||||
@ -97,7 +102,10 @@ class RecoveryFlow:
|
|||||||
|
|
||||||
def enter_any_share(self) -> BRGeneratorType:
|
def enter_any_share(self) -> BRGeneratorType:
|
||||||
yield
|
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(
|
is_dry_run = any(
|
||||||
title in self.debug.wait_layout().title().lower()
|
title in self.debug.wait_layout().title().lower()
|
||||||
for title in TR.translate("recovery__title_dry_run", lower=True)
|
for title in TR.translate("recovery__title_dry_run", lower=True)
|
||||||
@ -112,6 +120,8 @@ class RecoveryFlow:
|
|||||||
yield
|
yield
|
||||||
if self.client.model is models.T2B1:
|
if self.client.model is models.T2B1:
|
||||||
TR.assert_in(self._text_content(), "recovery__num_of_words")
|
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:
|
else:
|
||||||
TR.assert_in(self._text_content(), "recovery__enter_any_share")
|
TR.assert_in(self._text_content(), "recovery__enter_any_share")
|
||||||
self.debug.press_no()
|
self.debug.press_no()
|
||||||
@ -242,8 +252,11 @@ class RecoveryFlow:
|
|||||||
if index < len(shares) - 1:
|
if index < len(shares) - 1:
|
||||||
if has_groups:
|
if has_groups:
|
||||||
yield from self.success_share_group_entered()
|
yield from self.success_share_group_entered()
|
||||||
if self.client.model in (models.T2T1, models.T3T1) and click_info:
|
if click_info:
|
||||||
yield from self.tt_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()
|
yield from self.success_more_shares_needed()
|
||||||
|
|
||||||
def tt_click_info(
|
def tt_click_info(
|
||||||
@ -255,6 +268,13 @@ class RecoveryFlow:
|
|||||||
self.debug.swipe_up()
|
self.debug.swipe_up()
|
||||||
self.debug.press_yes()
|
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:
|
class EthereumFlow:
|
||||||
GO_BACK = (16, 220)
|
GO_BACK = (16, 220)
|
||||||
@ -392,7 +412,7 @@ class EthereumFlow:
|
|||||||
if self.client.model in (models.T2T1, models.T3T1):
|
if self.client.model in (models.T2T1, models.T3T1):
|
||||||
# confirm intro
|
# confirm intro
|
||||||
if info:
|
if info:
|
||||||
self.debug.press_info(wait=True)
|
self.debug.click(buttons.CORNER_BUTTON, wait=True)
|
||||||
TR.assert_equals_multiple(
|
TR.assert_equals_multiple(
|
||||||
self.debug.wait_layout().title(),
|
self.debug.wait_layout().title(),
|
||||||
[
|
[
|
||||||
@ -405,7 +425,7 @@ class EthereumFlow:
|
|||||||
yield
|
yield
|
||||||
|
|
||||||
# confirm summary
|
# confirm summary
|
||||||
if info:
|
if info and self.client.model != models.T3T1:
|
||||||
self.debug.press_info(wait=True)
|
self.debug.press_info(wait=True)
|
||||||
TR.assert_in(
|
TR.assert_in(
|
||||||
self.debug.wait_layout().text_content(), "ethereum__gas_limit"
|
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.
|
# 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>.
|
# 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 trezorlib.debuglink import DebugLink
|
||||||
|
|
||||||
from .. import buttons
|
from .. import buttons
|
||||||
@ -38,6 +40,9 @@ def test_abort(core_emulator: Emulator):
|
|||||||
debug = device_handler.debuglink()
|
debug = device_handler.debuglink()
|
||||||
features = device_handler.features()
|
features = device_handler.features()
|
||||||
|
|
||||||
|
if debug.model is models.T3T1:
|
||||||
|
pytest.skip("abort not supported on T3T1")
|
||||||
|
|
||||||
assert features.recovery_mode is False
|
assert features.recovery_mode is False
|
||||||
|
|
||||||
device_handler.run(device.recover, pin_protection=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(" ")
|
words = first_share.split(" ")
|
||||||
|
|
||||||
# start entering first share
|
# 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()
|
debug.press_yes()
|
||||||
assert debug.wait_layout().main_component() == "MnemonicKeyboard"
|
assert debug.wait_layout().main_component() == "MnemonicKeyboard"
|
||||||
|
|
||||||
@ -151,7 +156,7 @@ def test_recovery_on_old_wallet(core_emulator: Emulator):
|
|||||||
layout = debug.wait_layout()
|
layout = debug.wait_layout()
|
||||||
|
|
||||||
# check that we entered the first share successfully
|
# check that we entered the first share successfully
|
||||||
assert "1 of 3 shares entered" in layout.text_content()
|
assert "2 more shares needed" in layout.text_content()
|
||||||
|
|
||||||
# try entering the remaining shares
|
# try entering the remaining shares
|
||||||
for share in MNEMONIC_SLIP39_BASIC_20_3of6[1:3]:
|
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:
|
def enter_shares_with_restarts(debug: DebugLink) -> None:
|
||||||
shares = MNEMONIC_SLIP39_ADVANCED_20
|
shares = MNEMONIC_SLIP39_ADVANCED_20
|
||||||
layout = debug.read_layout()
|
layout = debug.read_layout()
|
||||||
expected_text = "Enter any share"
|
expected_text = "the first 2-4 letters"
|
||||||
remaining = len(shares)
|
remaining = len(shares)
|
||||||
for share in shares:
|
for share in shares:
|
||||||
assert expected_text in layout.text_content()
|
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