use ui::menu::*;
fn items() -> Vec<Item> {
vec![
Item::action("a"),
Item::Separator,
Item::action("b").disabled(),
Item::action("c"),
]
}
#[test]
fn entering_lands_on_the_first_row_the_direction_meets() {
assert_eq!(next_selectable(&items(), None, 1), Some(0));
assert_eq!(next_selectable(&items(), None, -1), Some(3));
let leading = vec![Item::Separator, Item::action("a")];
assert_eq!(next_selectable(&leading, None, 1), Some(1));
}
#[test]
fn stepping_skips_what_cannot_be_chosen() {
assert_eq!(next_selectable(&items(), Some(0), 1), Some(3));
assert_eq!(next_selectable(&items(), Some(3), -1), Some(0));
}
#[test]
fn both_ends_wrap() {
assert_eq!(next_selectable(&items(), Some(3), 1), Some(0));
assert_eq!(next_selectable(&items(), Some(0), -1), Some(3));
}
#[test]
fn a_menu_with_nothing_to_choose_answers_none() {
let dead = vec![Item::Separator, Item::action("x").disabled()];
assert_eq!(next_selectable(&dead, None, 1), None);
assert_eq!(next_selectable(&dead, Some(1), -1), None);
assert_eq!(next_selectable(&[], None, 1), None);
}
#[test]
fn one_selectable_row_is_its_own_neighbour() {
let lone = vec![Item::Separator, Item::action("only")];
assert_eq!(next_selectable(&lone, Some(1), 1), Some(1));
assert_eq!(next_selectable(&lone, Some(1), -1), Some(1));
}
#[test]
fn a_description_is_an_action_rows_second_line() {
let described = Item::action("Open…").with_description("Choose a file to edit");
assert!(
matches!(&described, Item::Action { description: Some(copy), .. } if copy == "Choose a file to edit")
);
assert_eq!(Item::Separator.with_description("x"), Item::Separator);
let submenu = Item::submenu("Open Recent", vec![Item::action("a")]);
assert_eq!(submenu.clone().with_description("x"), submenu);
}
#[test]
fn a_tooltip_is_an_action_rows_hover_text() {
let hinted = Item::action("Open…").with_tooltip("Choose a file to edit");
assert!(
matches!(&hinted, Item::Action { tooltip: Some(hint), .. } if hint == "Choose a file to edit")
);
assert_eq!(Item::Separator.with_tooltip("x"), Item::Separator);
let submenu = Item::submenu("Open Recent", vec![Item::action("a")]);
assert_eq!(submenu.clone().with_tooltip("x"), submenu);
}
#[test]
fn a_long_description_is_its_own_tooltip() {
let long = Item::action("Open…").with_long_description("Choose a file to edit");
assert_eq!(
long,
Item::action("Open…")
.with_description("Choose a file to edit")
.with_tooltip("Choose a file to edit")
);
}
#[test]
fn the_builders_leave_each_others_fields_alone() {
let full = Item::action("Save")
.with_icon(ui::icons::glyph::Bell)
.with_keystroke("⌘S")
.with_description("Write the file to disk")
.with_tooltip("Write the file to disk, overwriting what is there")
.checked(true)
.disabled();
assert!(matches!(
&full,
Item::Action {
icon: Some(_),
keystroke: Some(_),
description: Some(_),
tooltip: Some(_),
checked: true,
enabled: false,
..
}
));
assert!(!full.selectable(), "and disabled still means disabled");
}
#[test]
fn a_separator_carries_nothing() {
assert_eq!(Item::Separator.with_keystroke("⌘K"), Item::Separator);
assert_eq!(Item::Separator.disabled(), Item::Separator);
assert!(!Item::Separator.selectable());
assert!(Item::action("a").selectable());
assert!(!Item::action("a").disabled().selectable());
}
fn nested() -> Vec<Item> {
vec![
Item::action("a"),
Item::submenu(
"Copy As",
vec![
Item::action("Text"),
Item::submenu("More", vec![Item::action("Base64")]),
],
),
Item::Separator,
Item::action("b").disabled(),
Item::submenu("Empty", vec![]),
Item::action("c"),
]
}
#[test]
fn a_path_names_one_row_at_any_depth() {
let items = nested();
assert_eq!(at(&items, &[0]), Some(&Item::action("a")));
assert_eq!(at(&items, &[1, 0]), Some(&Item::action("Text")));
assert_eq!(at(&items, &[1, 1, 0]), Some(&Item::action("Base64")));
assert_eq!(at(&items, &[1, 9]), None);
assert_eq!(at(&items, &[0, 0]), None);
assert_eq!(at(&items, &[]), None);
assert_eq!(items_at(&items, &[]).map(<[Item]>::len), Some(6));
assert_eq!(items_at(&items, &[1, 1]).map(<[Item]>::len), Some(1));
}
#[test]
fn a_submenu_with_nothing_in_it_cannot_be_landed_on() {
let items = nested();
assert!(items[1].selectable());
assert!(!items[4].selectable());
assert!(
!Item::submenu("x", vec![Item::action("y")])
.disabled()
.selectable()
);
assert_eq!(next_selectable(&items, Some(1), 1), Some(5));
}
#[test]
fn pointing_at_a_submenu_row_is_what_opens_it() {
let items = nested();
let mut cursor = Cursor::default();
assert!(cursor.point_at(&items, &[0]));
assert_eq!(cursor.open(), &[] as &[usize]);
assert_eq!(cursor.row(), Some(0));
assert!(!cursor.point_at(&items, &[0]));
assert!(cursor.point_at(&items, &[1]));
assert_eq!(cursor.open(), &[1]);
assert_eq!(cursor.row(), None);
assert_eq!(cursor.path(), None);
assert!(cursor.nested());
assert!(cursor.point_at(&items, &[1, 0]));
assert_eq!(cursor.path(), Some(vec![1, 0]));
assert!(cursor.point_at(&items, &[0]));
assert!(!cursor.nested());
}
#[test]
fn the_arrows_walk_levels() {
let items = nested();
let mut cursor = Cursor::default();
assert!(!cursor.descend(&items));
assert!(!cursor.ascend());
cursor.step(&items, 1);
assert_eq!(cursor.row(), Some(0));
assert!(!cursor.descend(&items));
cursor.step(&items, 1);
assert_eq!(cursor.row(), Some(1));
assert!(cursor.descend(&items));
assert_eq!(cursor.open(), &[1]);
assert_eq!(cursor.row(), Some(0));
cursor.step(&items, 1);
assert!(cursor.descend(&items));
assert_eq!(cursor.path(), Some(vec![1, 1, 0]));
assert!(cursor.ascend());
assert_eq!(cursor.path(), Some(vec![1, 1]));
assert!(cursor.ascend());
assert_eq!(cursor.path(), Some(vec![1]));
assert!(!cursor.ascend());
}
#[test]
fn each_panel_lights_the_row_below_it() {
let items = nested();
let mut cursor = Cursor::default();
cursor.point_at(&items, &[1, 1, 0]);
assert_eq!(cursor.lit(0), Some(1));
assert_eq!(cursor.lit(1), Some(1));
assert_eq!(cursor.lit(2), Some(0));
assert_eq!(cursor.lit(3), None);
}
#[test]
fn a_chain_that_no_longer_resolves_moves_nothing() {
let mut cursor = Cursor::default();
cursor.point_at(&nested(), &[1, 1, 0]);
let flat = items();
cursor.step(&flat, 1);
assert_eq!(cursor.row(), Some(0));
assert!(!cursor.descend(&flat));
}