Provide search functionality through the TUI #134

Merged
wojtek merged 35 commits from 24---provide-search-functionality-through-the-tui into main 2024-02-18 22:12:42 +01:00
10 changed files with 339 additions and 371 deletions
Showing only changes of commit b5afa24b7e - Show all commits

View File

@ -1,3 +1,4 @@
// FIXME: combine with mod state into a mod machine
#![allow(clippy::module_inception)] #![allow(clippy::module_inception)]
use crate::tui::{ use crate::tui::{

View File

@ -116,3 +116,14 @@ impl<BS, IS, RS, SS, ES, CS> AppState<BS, IS, RS, SS, ES, CS> {
matches!(self, AppState::Search(_)) matches!(self, AppState::Search(_))
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn app_is_state() {
let state = AppPublicState::Search("get rekt");
assert!(state.is_search());
}
}

View File

@ -496,9 +496,9 @@ impl TrackSelection {
} }
pub struct ListSelection { pub struct ListSelection {
artist: ListState, pub artist: ListState,
album: ListState, pub album: ListState,
track: ListState, pub track: ListState,
} }
impl ListSelection { impl ListSelection {

View File

@ -91,3 +91,59 @@ impl<MH: IMusicHoard> IAppInteractBrowse for AppMachine<MH, AppBrowse> {
self.into() self.into()
} }
} }
#[cfg(test)]
mod tests {
use crate::tui::app::{
state::tests::{inner, music_hoard},
IAppInteract,
};
use super::*;
#[test]
fn save_and_quit() {
let mut music_hoard = music_hoard(vec![]);
music_hoard
.expect_save_to_database()
.times(1)
.return_once(|| Ok(()));
let browse = AppMachine::browse(inner(music_hoard));
let app = browse.save_and_quit();
assert!(!app.is_running());
app.unwrap_browse();
}
#[test]
fn save_and_quit_error() {
let mut music_hoard = music_hoard(vec![]);
music_hoard
.expect_save_to_database()
.times(1)
.return_once(|| Err(musichoard::Error::DatabaseError(String::from("get rekt"))));
let browse = AppMachine::browse(inner(music_hoard));
let app = browse.save_and_quit();
assert!(app.is_running());
app.unwrap_error();
}
#[test]
fn show_info_overlay() {
let browse = AppMachine::browse(inner(music_hoard(vec![])));
let app = browse.show_info_overlay();
app.unwrap_info();
}
#[test]
fn show_reload_menu() {
let browse = AppMachine::browse(inner(music_hoard(vec![])));
let app = browse.show_reload_menu();
app.unwrap_reload();
}
}

View File

@ -44,3 +44,17 @@ impl<MH: IMusicHoard> IAppInteractCritical for AppMachine<MH, AppCritical> {
self.into() self.into()
} }
} }
#[cfg(test)]
mod tests {
use crate::tui::app::state::tests::{music_hoard, inner};
use super::*;
#[test]
fn no_op() {
let critical = AppMachine::critical(inner(music_hoard(vec![])), "get rekt");
let app = critical.no_op();
app.unwrap_critical();
}
}

View File

@ -44,3 +44,17 @@ impl<MH: IMusicHoard> IAppInteractError for AppMachine<MH, AppError> {
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()
} }
} }
#[cfg(test)]
mod tests {
use crate::tui::app::state::tests::{inner, music_hoard};
use super::*;
#[test]
fn dismiss_error() {
let error = AppMachine::error(inner(music_hoard(vec![])), "get rekt");
let app = error.dismiss_error();
app.unwrap_browse();
}
}

View File

@ -44,3 +44,17 @@ impl<MH: IMusicHoard> IAppInteractInfo for AppMachine<MH, AppInfo> {
self.into() self.into()
} }
} }
#[cfg(test)]
mod tests {
use crate::tui::app::state::tests::{music_hoard, inner};
use super::*;
#[test]
fn hide_info_overlay() {
let info = AppMachine::info(inner(music_hoard(vec![])));
let app = info.hide_info_overlay();
app.unwrap_browse();
}
}

View File

@ -69,8 +69,7 @@ mod tests {
app::{ app::{
app::App, app::App,
selection::{Category, Delta}, selection::{Category, Delta},
AppPublicState, AppState, IAppInteract, IAppInteractBrowse, IAppInteractError, AppState, IAppInteract, IAppInteractBrowse,
IAppInteractInfo, IAppInteractReload, IAppInteractSearch,
}, },
lib::MockIMusicHoard, lib::MockIMusicHoard,
testmod::COLLECTION, testmod::COLLECTION,
@ -79,42 +78,42 @@ mod tests {
use super::*; use super::*;
impl<BS, IS, RS, SS, ES, CS> AppState<BS, IS, RS, SS, ES, CS> { impl<BS, IS, RS, SS, ES, CS> AppState<BS, IS, RS, SS, ES, CS> {
fn unwrap_browse(self) -> BS { pub fn unwrap_browse(self) -> BS {
match self { match self {
AppState::Browse(browse) => browse, AppState::Browse(browse) => browse,
_ => panic!(), _ => panic!(),
} }
} }
fn unwrap_info(self) -> IS { pub fn unwrap_info(self) -> IS {
match self { match self {
AppState::Info(info) => info, AppState::Info(info) => info,
_ => panic!(), _ => panic!(),
} }
} }
fn unwrap_reload(self) -> RS { pub fn unwrap_reload(self) -> RS {
match self { match self {
AppState::Reload(reload) => reload, AppState::Reload(reload) => reload,
_ => panic!(), _ => panic!(),
} }
} }
fn unwrap_search(self) -> SS { pub fn unwrap_search(self) -> SS {
match self { match self {
AppState::Search(search) => search, AppState::Search(search) => search,
_ => panic!(), _ => panic!(),
} }
} }
fn unwrap_error(self) -> ES { pub fn unwrap_error(self) -> ES {
match self { match self {
AppState::Error(error) => error, AppState::Error(error) => error,
_ => panic!(), _ => panic!(),
} }
} }
fn unwrap_critical(self) -> CS { pub fn unwrap_critical(self) -> CS {
match self { match self {
AppState::Critical(critical) => critical, AppState::Critical(critical) => critical,
_ => panic!(), _ => panic!(),
@ -122,7 +121,7 @@ mod tests {
} }
} }
fn music_hoard(collection: Collection) -> MockIMusicHoard { fn music_hoard_app(collection: Collection) -> MockIMusicHoard {
let mut music_hoard = MockIMusicHoard::new(); let mut music_hoard = MockIMusicHoard::new();
music_hoard music_hoard
@ -138,58 +137,20 @@ mod tests {
music_hoard music_hoard
} }
#[test] pub fn music_hoard(collection: Collection) -> MockIMusicHoard {
fn app_is_state() { let mut music_hoard = MockIMusicHoard::new();
let state = AppPublicState::Search("get rekt"); music_hoard.expect_get_collection().return_const(collection);
assert!(state.is_search());
}
#[test]
fn running_quit() {
let mut music_hoard = music_hoard(COLLECTION.to_owned());
music_hoard music_hoard
.expect_save_to_database()
.times(1)
.return_once(|| Ok(()));
let app = App::new(music_hoard);
assert!(app.is_running());
let browse = app.unwrap_browse();
let app = browse.save_and_quit();
assert!(!app.is_running());
} }
#[test] pub fn inner(music_hoard: MockIMusicHoard) -> AppInner<MockIMusicHoard> {
fn error_quit() { AppInner::new(music_hoard)
let mut music_hoard = music_hoard(COLLECTION.to_owned());
music_hoard
.expect_save_to_database()
.times(1)
.return_once(|| Ok(()));
let app = App::new(music_hoard);
assert!(app.is_running());
let app = App::Error(AppMachine::error(
app.unwrap_browse().inner,
String::from("get rekt"),
));
let error = app.unwrap_error();
let browse = error.dismiss_error().unwrap_browse();
let app = browse.save_and_quit();
assert!(!app.is_running());
} }
#[test] #[test]
fn running_force_quit() { fn running_force_quit() {
let app = App::new(music_hoard(COLLECTION.to_owned())); let app = App::new(music_hoard_app(COLLECTION.to_owned()));
assert!(app.is_running()); assert!(app.is_running());
let app = app.force_quit(); let app = app.force_quit();
@ -198,46 +159,15 @@ mod tests {
#[test] #[test]
fn error_force_quit() { fn error_force_quit() {
let app = App::new(music_hoard(COLLECTION.to_owned())); let mut app = App::new(music_hoard_app(COLLECTION.to_owned()));
assert!(app.is_running()); assert!(app.is_running());
let app = App::Error(AppMachine::error( app = AppMachine::error(app.unwrap_browse().inner, "get rekt").into();
app.unwrap_browse().inner,
String::from("get rekt"),
));
let app = app.force_quit(); app = app.force_quit();
assert!(!app.is_running()); assert!(!app.is_running());
} }
#[test]
fn save() {
let mut music_hoard = music_hoard(COLLECTION.to_owned());
music_hoard
.expect_save_to_database()
.times(1)
.return_once(|| Ok(()));
let browse = App::new(music_hoard).unwrap_browse();
browse.save_and_quit().unwrap_browse();
}
#[test]
fn save_error() {
let mut music_hoard = music_hoard(COLLECTION.to_owned());
music_hoard
.expect_save_to_database()
.times(1)
.return_once(|| Err(musichoard::Error::DatabaseError(String::from("get rekt"))));
let browse = App::new(music_hoard).unwrap_browse();
browse.save_and_quit().unwrap_error();
}
#[test] #[test]
fn init_error() { fn init_error() {
let mut music_hoard = MockIMusicHoard::new(); let mut music_hoard = MockIMusicHoard::new();
@ -256,7 +186,7 @@ mod tests {
#[test] #[test]
fn modifiers() { fn modifiers() {
let app = App::new(music_hoard(COLLECTION.to_owned())); let app = App::new(music_hoard_app(COLLECTION.to_owned()));
assert!(app.is_running()); assert!(app.is_running());
let browse = app.unwrap_browse(); let browse = app.unwrap_browse();
@ -363,7 +293,7 @@ mod tests {
let mut collection = COLLECTION.to_owned(); let mut collection = COLLECTION.to_owned();
collection[0].albums[0].tracks = vec![]; collection[0].albums[0].tracks = vec![];
let app = App::new(music_hoard(collection)); let app = App::new(music_hoard_app(collection));
assert!(app.is_running()); assert!(app.is_running());
let browse = app.unwrap_browse(); let browse = app.unwrap_browse();
@ -397,7 +327,7 @@ mod tests {
let mut collection = COLLECTION.to_owned(); let mut collection = COLLECTION.to_owned();
collection[0].albums = vec![]; collection[0].albums = vec![];
let app = App::new(music_hoard(collection)); let app = App::new(music_hoard_app(collection));
assert!(app.is_running()); assert!(app.is_running());
let browse = app.unwrap_browse(); let browse = app.unwrap_browse();
@ -443,7 +373,7 @@ mod tests {
#[test] #[test]
fn no_artists() { fn no_artists() {
let app = App::new(music_hoard(vec![])); let app = App::new(music_hoard_app(vec![]));
assert!(app.is_running()); assert!(app.is_running());
let browse = app.unwrap_browse(); let browse = app.unwrap_browse();
@ -500,281 +430,4 @@ mod tests {
assert_eq!(selection.artist.album.state.list.selected(), None); assert_eq!(selection.artist.album.state.list.selected(), None);
assert_eq!(selection.artist.album.track.state.list.selected(), None); assert_eq!(selection.artist.album.track.state.list.selected(), None);
} }
#[test]
fn info_overlay() {
let browse = App::new(music_hoard(COLLECTION.to_owned())).unwrap_browse();
let info = browse.show_info_overlay().unwrap_info();
info.hide_info_overlay().unwrap_browse();
}
#[test]
fn reload_hide_menu() {
let browse = App::new(music_hoard(COLLECTION.to_owned())).unwrap_browse();
let reload = browse.show_reload_menu().unwrap_reload();
reload.hide_reload_menu().unwrap_browse();
}
#[test]
fn reload_database() {
let mut music_hoard = music_hoard(COLLECTION.to_owned());
music_hoard
.expect_load_from_database()
.times(1)
.return_once(|| Ok(()));
let browse = App::new(music_hoard).unwrap_browse();
let reload = browse.show_reload_menu().unwrap_reload();
reload.reload_database().unwrap_browse();
}
#[test]
fn reload_library() {
let mut music_hoard = music_hoard(COLLECTION.to_owned());
music_hoard
.expect_rescan_library()
.times(1)
.return_once(|| Ok(()));
let browse = App::new(music_hoard).unwrap_browse();
let reload = browse.show_reload_menu().unwrap_reload();
reload.reload_library().unwrap_browse();
}
#[test]
fn reload_error() {
let mut music_hoard = music_hoard(COLLECTION.to_owned());
music_hoard
.expect_load_from_database()
.times(1)
.return_once(|| Err(musichoard::Error::DatabaseError(String::from("get rekt"))));
let browse = App::new(music_hoard).unwrap_browse();
let reload = browse.show_reload_menu().unwrap_reload();
let error = reload.reload_database().unwrap_error();
error.dismiss_error().unwrap_browse();
}
#[test]
fn search() {
let browse = App::new(music_hoard(COLLECTION.to_owned())).unwrap_browse();
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let browse = browse.increment_category().unwrap_browse();
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(1));
let search = browse.begin_search().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.append_character('a').unwrap_search();
let search = search.append_character('l').unwrap_search();
let search = search.append_character('b').unwrap_search();
let search = search.append_character('u').unwrap_search();
let search = search.append_character('m').unwrap_search();
let search = search.append_character('_').unwrap_search();
let search = search.append_character('a').unwrap_search();
let search = search.append_character('r').unwrap_search();
let search = search.append_character('t').unwrap_search();
let search = search.append_character('i').unwrap_search();
let search = search.append_character('s').unwrap_search();
let search = search.append_character('t').unwrap_search();
let search = search.append_character(' ').unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.append_character('\'').unwrap_search();
let search = search.append_character('c').unwrap_search();
let search = search.append_character('\'').unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
let search = search.step_back().unwrap_search();
let search = search.step_back().unwrap_search();
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.append_character('\'').unwrap_search();
let search = search.append_character('b').unwrap_search();
let search = search.append_character('\'').unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(1));
let browse = search.finish_search().unwrap_browse();
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(1));
}
#[test]
fn search_next() {
let browse = App::new(music_hoard(COLLECTION.to_owned())).unwrap_browse();
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let browse = browse.increment_category().unwrap_browse();
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(1));
let search = browse.begin_search().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.append_character('a').unwrap_search();
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(1));
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(3));
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(3));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(3));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(1));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
}
#[test]
fn cancel_search() {
let browse = App::new(music_hoard(COLLECTION.to_owned())).unwrap_browse();
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let browse = browse.increment_category().unwrap_browse();
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(1));
let search = browse.begin_search().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.append_character('a').unwrap_search();
let search = search.append_character('l').unwrap_search();
let search = search.append_character('b').unwrap_search();
let search = search.append_character('u').unwrap_search();
let search = search.append_character('m').unwrap_search();
let search = search.append_character('_').unwrap_search();
let search = search.append_character('a').unwrap_search();
let search = search.append_character('r').unwrap_search();
let search = search.append_character('t').unwrap_search();
let search = search.append_character('i').unwrap_search();
let search = search.append_character('s').unwrap_search();
let search = search.append_character('t').unwrap_search();
let search = search.append_character(' ').unwrap_search();
let search = search.append_character('\'').unwrap_search();
let search = search.append_character('c').unwrap_search();
let search = search.append_character('\'').unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
let browse = search.cancel_search().unwrap_browse();
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(1));
}
#[test]
fn empty_search() {
let browse = App::new(music_hoard(vec![])).unwrap_browse();
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let browse = browse.increment_category().unwrap_browse();
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), None);
let search = browse.begin_search().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
let search = search.append_character('a').unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.active, Category::Album);
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
let browse = search.cancel_search().unwrap_browse();
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), None);
}
} }

View File

@ -80,3 +80,60 @@ impl<MH: IMusicHoard> IAppInteractReloadPrivate<MH> for AppMachine<MH, AppReload
} }
} }
} }
#[cfg(test)]
mod tests {
use crate::tui::app::state::tests::{inner, music_hoard};
use super::*;
#[test]
fn hide_reload_menu() {
let reload = AppMachine::reload(inner(music_hoard(vec![])));
let app = reload.hide_reload_menu();
app.unwrap_browse();
}
#[test]
fn reload_database() {
let mut music_hoard = music_hoard(vec![]);
music_hoard
.expect_load_from_database()
.times(1)
.return_once(|| Ok(()));
let reload = AppMachine::reload(inner(music_hoard));
let app = reload.reload_database();
app.unwrap_browse();
}
#[test]
fn reload_library() {
let mut music_hoard = music_hoard(vec![]);
music_hoard
.expect_rescan_library()
.times(1)
.return_once(|| Ok(()));
let reload = AppMachine::reload(inner(music_hoard));
let app = reload.reload_library();
app.unwrap_browse();
}
#[test]
fn reload_error() {
let mut music_hoard = music_hoard(vec![]);
music_hoard
.expect_load_from_database()
.times(1)
.return_once(|| Err(musichoard::Error::DatabaseError(String::from("get rekt"))));
let reload = AppMachine::reload(inner(music_hoard));
let app = reload.reload_database();
app.unwrap_error();
}
}

View File

@ -98,3 +98,151 @@ impl<MH: IMusicHoard> IAppInteractSearch for AppMachine<MH, AppSearch> {
self.into() self.into()
} }
} }
#[cfg(test)]
mod tests {
use ratatui::widgets::ListState;
use crate::tui::{
app::state::tests::{inner, music_hoard},
testmod::COLLECTION,
};
use super::*;
fn orig(index: Option<usize>) -> ListSelection {
let mut artist = ListState::default();
artist.select(index);
ListSelection {
artist,
album: ListState::default(),
track: ListState::default(),
}
}
#[test]
fn search() {
let search = AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.append_character('a').unwrap_search();
let search = search.append_character('l').unwrap_search();
let search = search.append_character('b').unwrap_search();
let search = search.append_character('u').unwrap_search();
let search = search.append_character('m').unwrap_search();
let search = search.append_character('_').unwrap_search();
let search = search.append_character('a').unwrap_search();
let search = search.append_character('r').unwrap_search();
let search = search.append_character('t').unwrap_search();
let search = search.append_character('i').unwrap_search();
let search = search.append_character('s').unwrap_search();
let search = search.append_character('t').unwrap_search();
let search = search.append_character(' ').unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.append_character('\'').unwrap_search();
let search = search.append_character('c').unwrap_search();
let search = search.append_character('\'').unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
let search = search.step_back().unwrap_search();
let search = search.step_back().unwrap_search();
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.append_character('\'').unwrap_search();
let search = search.append_character('b').unwrap_search();
let search = search.append_character('\'').unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(1));
let app = search.finish_search();
let browse = app.unwrap_browse();
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(1));
}
#[test]
fn search_next_step_back() {
let search = AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.append_character('a').unwrap_search();
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(1));
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(3));
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(3));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(3));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(1));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
}
#[test]
fn cancel_search() {
let search = AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
let search = search.append_character('a').unwrap_search();
let search = search.append_character('l').unwrap_search();
let search = search.append_character('b').unwrap_search();
let search = search.append_character('u').unwrap_search();
let search = search.append_character('m').unwrap_search();
let search = search.append_character('_').unwrap_search();
let search = search.append_character('a').unwrap_search();
let search = search.append_character('r').unwrap_search();
let search = search.append_character('t').unwrap_search();
let search = search.append_character('i').unwrap_search();
let search = search.append_character('s').unwrap_search();
let search = search.append_character('t').unwrap_search();
let search = search.append_character(' ').unwrap_search();
let search = search.append_character('\'').unwrap_search();
let search = search.append_character('b').unwrap_search();
let search = search.append_character('\'').unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(1));
let browse = search.cancel_search().unwrap_browse();
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(2));
}
#[test]
fn empty_search() {
let search = AppMachine::search(inner(music_hoard(vec![])), orig(None));
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
let search = search.append_character('a').unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
let browse = search.cancel_search().unwrap_browse();
assert_eq!(browse.inner.selection.artist.state.list.selected(), None);
}
}