mod menu;
use super::{DELETE, PLAN, REFRESH, fire, matching, parsed, rows};
use crate::reply::agent::Offer;
use crate::test_support::window::{pinned, seated};
use crate::ui::Model;
use serde_json::json;
fn words(model: &Model) -> Vec<&'static str> {
rows(model).into_iter().map(|row| row.word).collect()
}
#[test]
fn a_line_parses_to_its_word_and_the_rest() {
assert_eq!(parsed("/flag it loops"), Some(("flag", "it loops")));
assert_eq!(parsed("/stop"), Some(("stop", "")));
assert_eq!(parsed("/"), Some(("", "")));
assert_eq!(parsed("ship it"), None);
assert_eq!(parsed(""), None);
}
#[test]
fn a_row_says_what_lernie_help_says() {
let model = seated();
let stop = rows(&model)
.into_iter()
.find(|row| row.word == crate::verbs::STOP.word)
.expect("stop is offered on a seated window");
assert_eq!(stop.summary, crate::verbs::STOP.summary);
assert_eq!(
stop.line(),
format!("/stop {}", crate::verbs::STOP.summary)
);
assert_eq!(stop.ops, &[crate::verbs::STOP.word]);
}
#[test]
fn what_is_offered_follows_the_aim_and_the_selection() {
let bare = words(&Model::default());
assert!(bare.contains(&REFRESH) && bare.contains(&crate::ui::queue::WORD));
assert!(!bare.contains(&PLAN) && !bare.contains(&crate::ui::records::WORD));
assert!(!bare.contains(&crate::verbs::PIN.word));
let mut starting = seated();
starting.conversation = None;
let start = words(&starting);
assert!(start.contains(&PLAN) && start.contains(&crate::verbs::PIN.word));
assert!(!start.contains(&crate::ui::records::WORD));
let talking = words(&seated());
for word in [
crate::ui::records::WORD,
crate::verbs::INTERRUPT.word,
crate::verbs::STOP.word,
crate::verbs::NUDGE.word,
crate::verbs::FLAG.word,
DELETE,
crate::ui::enroll::WORD,
crate::ui::unmake::WORD,
] {
assert!(talking.contains(&word), "{word}: {talking:?}");
}
assert!(!talking.contains(&PLAN));
assert!(words(&pinned()).contains(&crate::verbs::UNPIN.word));
}
#[test]
fn the_turn_s_verbs_follow_the_engine_s_offers() {
let mut model = seated();
let mut row = crate::test_support::window::panes::records::own_row();
row.offers = vec![Offer::Nudge];
model.records.agent = Some(row);
let offered = words(&model);
assert!(offered.contains(&crate::verbs::NUDGE.word));
assert!(!offered.contains(&crate::verbs::STOP.word));
assert!(!offered.contains(&crate::verbs::INTERRUPT.word));
}
#[test]
fn the_word_narrows_the_menu() {
let model = seated();
assert_eq!(matching(&model, "/").len(), rows(&model).len());
let re: Vec<&str> = matching(&model, "/re")
.into_iter()
.map(|row| row.word)
.collect();
assert_eq!(
re,
vec!["records", "revoke", "restore", "retarget", "refresh"]
);
assert!(matching(&model, "/zzz").is_empty());
assert!(matching(&model, "no slash").is_empty());
}
#[test]
fn firing_composes_the_envelope_the_strip_composed() {
let mut model = seated();
assert!(fire(&mut model, crate::verbs::FLAG.word, "it loops"));
assert!(fire(&mut model, crate::verbs::STOP.word, ""));
assert!(fire(&mut model, crate::verbs::INTERRUPT.word, "no, this"));
assert!(fire(&mut model, DELETE, "port the paint probe"));
assert!(fire(&mut model, crate::verbs::NUDGE.word, ""));
assert!(fire(&mut model, crate::verbs::REVOKE.word, ""));
assert!(fire(&mut model, crate::verbs::RESTORE.word, ""));
assert!(fire(&mut model, crate::verbs::RETARGET.word, ""));
let ops: Vec<_> = model
.outbox
.iter()
.map(|posted| posted.envelope["op"].clone())
.collect();
assert_eq!(
ops,
vec![
json!("flag"),
json!("stop"),
json!("interrupt"),
json!("delete-agent"),
json!("nudge"),
json!("revoke"),
json!("restore"),
json!("retarget")
]
);
assert_eq!(model.outbox[0].envelope["reason"], json!("it loops"));
assert_eq!(model.outbox[2].envelope["content"], json!("no, this"));
assert_eq!(
model.outbox[3].envelope["typed"],
json!("port the paint probe")
);
let before = model.outbox.len();
assert!(
!fire(&mut model, crate::verbs::FLAG.word, ""),
"a flag needs its reason"
);
assert!(
!fire(&mut model, crate::verbs::INTERRUPT.word, ""),
"a cut needs its words"
);
assert!(!fire(&mut model, "no-such-verb", ""));
assert_eq!(model.outbox.len(), before);
}
#[test]
fn every_opener_opens_what_its_word_names() {
let mut model = seated();
for (word, open) in [
(
crate::ui::queue::WORD,
(|m: &Model| m.showing(crate::ui::Listing::Queue)) as fn(&Model) -> bool,
),
(crate::ui::records::WORD, |m: &Model| {
m.showing(crate::ui::Listing::Records)
}),
(crate::ui::tuning::WORD, |m: &Model| m.tuning.is_some()),
(crate::ui::login::WORD, |m: &Model| m.login.is_some()),
(crate::ui::clients::WORD, |m: &Model| {
m.showing(crate::ui::Listing::Clients)
}),
(crate::ui::config::WORD, |m: &Model| m.configuring.is_some()),
(crate::ui::fleet::WORD, |m: &Model| m.fleet.is_some()),
(crate::ui::unmake::WORD, |m: &Model| m.unmaking.is_some()),
(crate::ui::commands::WORD, Model::commanding),
(crate::ui::doctor::WORD, Model::doctoring),
(crate::ui::trail::WORD, Model::trailing),
(crate::ui::board::WORD, Model::boarding),
(crate::ui::prices::WORD, Model::pricing),
(crate::ui::find::WORD, Model::finding),
(crate::ui::enroll::WORD, |m: &Model| m.enroll.is_some()),
] {
assert!(fire(&mut model, word, ""), "{word}");
assert!(open(&model), "{word} opens its pane");
model.escape();
assert!(!model.covered(), "{word}: Escape puts it down");
}
let mut model = seated();
assert!(fire(&mut model, crate::ui::find::WORD, "needle"));
assert_eq!(model.needle, "needle");
assert_eq!(
model.outbox.last().map(|p| p.envelope["op"].clone()),
Some(json!("search"))
);
assert!(fire(&mut model, crate::verbs::PIN.word, ""));
assert_eq!(
model.outbox.last().map(|p| p.envelope["op"].clone()),
Some(json!("pin"))
);
let mut pinned = pinned();
assert!(fire(&mut pinned, crate::verbs::UNPIN.word, ""));
assert_eq!(
pinned.outbox.last().map(|p| p.envelope["op"].clone()),
Some(json!("unpin"))
);
let before = model.outbox.len();
assert!(fire(&mut model, REFRESH, ""));
assert!(model.outbox.len() > before, "refresh asks again");
let mut starting = seated();
starting.conversation = None;
assert!(fire(&mut starting, PLAN, ""));
assert!(starting.plan);
assert!(fire(&mut starting, PLAN, ""));
assert!(!starting.plan);
}