use super::{Choice, HEADING, HINT, answer, choices, confirm, moved, pick, row, words};
use crate::paint_probe::frame::{Window, press};
use crate::test_support::window::{asked, click, painted, seated};
use crate::ui::{Model, theme};
use serde_json::json;
fn last_op(model: &Model) -> Option<serde_json::Value> {
model.outbox.last().map(|p| p.envelope["op"].clone())
}
#[test]
fn it_stands_on_the_selected_conversation_s_row_and_not_otherwise() {
assert!(row(&seated()).is_none());
let mut model = asked();
assert!(row(&model).is_some());
let window = Window::new();
window.text(|ctx| crate::ui::render(ctx, &mut model));
let text = window.text(|ctx| crate::ui::render(ctx, &mut model));
for word in [
HEADING,
HINT,
"1. seen",
"2. pass",
"6. hold",
"held at the boundary",
] {
assert!(text.contains(word), "{word:?}:\n{text}");
}
model.conversation = Some("c-9".to_owned());
assert!(row(&model).is_none());
let text = painted(&mut model);
assert!(!text.contains(HINT), "{text}");
}
#[test]
fn the_choices_are_the_queue_s_acts() {
let model = asked();
let (_, held) = row(&model).expect("a row");
let offered: Vec<String> = choices(&held).into_iter().map(Choice::word).collect();
assert_eq!(
offered,
vec![
"seen",
"pass",
"pass for this conversation",
"pass for this workspace",
"refuse",
"hold"
]
);
assert_eq!(Choice::Seen.op(), "seen");
assert_eq!(Choice::Verdict("pass", "call").op(), "answer");
let quiet = crate::reply::queue::QueueRow {
held: None,
..held.clone()
};
assert_eq!(choices(&quiet), vec![Choice::Seen]);
let said: Vec<String> = words(&held).into_iter().map(|(s, _)| s).collect();
assert!(said.iter().any(|s| s.starts_with("held at the boundary")));
assert!(said.iter().any(|s| s.starts_with("flagged")));
assert!(said.contains(&"Unauthorized".to_owned()));
}
#[test]
fn a_digit_the_arrows_and_a_click_each_answer() {
let mut model = asked();
assert!(pick(&mut model, 1));
assert_eq!(last_op(&model), Some(json!("seen")));
assert!(pick(&mut model, 3));
assert_eq!(last_op(&model), Some(json!("answer")));
assert_eq!(
model.outbox.last().map(|p| p.envelope["scope"].clone()),
Some(json!("conversation"))
);
assert!(!pick(&mut model, 9), "no ninth row");
assert!(!pick(&mut model, 0), "the digits count from one");
let window = Window::new();
let key = |model: &mut Model, key| {
window.frame(vec![press(key)], |ctx| crate::ui::render(ctx, model));
};
let mut model = asked();
window.frame(Vec::new(), |ctx| crate::ui::render(ctx, &mut model));
key(&mut model, egui::Key::ArrowDown);
key(&mut model, egui::Key::ArrowDown);
key(&mut model, egui::Key::ArrowUp);
key(&mut model, egui::Key::Enter);
assert_eq!(last_op(&model), Some(json!("answer")));
assert_eq!(
model.outbox.last().map(|p| p.envelope["verdict"].clone()),
Some(json!("pass"))
);
key(&mut model, egui::Key::Num5);
assert_eq!(
model.outbox.last().map(|p| p.envelope["verdict"].clone()),
Some(json!("refuse"))
);
let mut model = asked();
click(&window, "6. hold", |ctx| crate::ui::render(ctx, &mut model));
assert_eq!(
model.outbox.last().map(|p| p.envelope["verdict"].clone()),
Some(json!("hold"))
);
}
#[test]
fn the_box_keeps_the_caret_and_an_empty_draft_lends_the_ask_its_keys() {
let field = egui::Id::new(crate::ui::keys::BOX_ID);
let typed = |text: &str| egui::Event::Text(text.to_owned());
let mut model = asked();
let window = Window::new();
window.frame(Vec::new(), |ctx| crate::ui::render(ctx, &mut model));
window.frame(Vec::new(), |ctx| crate::ui::render(ctx, &mut model));
assert_eq!(window.focused(), Some(field), "the caret is in the box");
window.frame(vec![press(egui::Key::Num2), typed("2")], |ctx| {
crate::ui::render(ctx, &mut model);
});
assert_eq!(last_op(&model), Some(json!("answer")));
assert!(model.draft.is_empty(), "{:?}", model.draft);
let mut model = asked();
model.draft = "go on".to_owned();
window.focus(field);
for key in [egui::Key::Num1, egui::Key::ArrowDown] {
window.frame(vec![press(key)], |ctx| crate::ui::render(ctx, &mut model));
}
assert!(model.outbox.is_empty(), "{:?}", model.outbox);
window.frame(vec![press(egui::Key::Enter)], |ctx| {
crate::ui::render(ctx, &mut model);
});
assert_eq!(last_op(&model), Some(json!("message")));
assert_eq!(
model.outbox.len(),
1,
"the words, and no answer beside them"
);
let mut model = asked();
window.focus(egui::Id::new("a control the Tab landed on"));
window.frame(vec![press(egui::Key::Num1)], |ctx| {
crate::ui::render(ctx, &mut model);
});
assert!(model.outbox.is_empty(), "{:?}", model.outbox);
}
#[test]
fn the_hint_names_the_digits_and_the_words() {
let mut model = asked();
assert_eq!(
super::hint(&model),
"it is asking — 1–6 answers, or say it — Enter sends"
);
for section in &mut model.waiting {
for row in &mut section.rows {
row.held = None;
}
}
assert!(super::hint(&model).contains("— 1 answers, or say it"));
model.waiting.clear();
assert_eq!(super::hint(&model), "it is asking — say it — Enter sends");
let text = painted(&mut asked());
assert!(text.contains("1–6 answers"), "{text}");
}
#[test]
fn nothing_asks_nothing_answers() {
let mut model = seated();
let window = Window::new();
window.frame(Vec::new(), |ctx| {
moved(ctx, &model, 1);
confirm(ctx, &mut model);
});
assert!(model.outbox.is_empty());
assert!(!pick(&mut model, 1));
let held = row(&asked()).expect("a row");
answer(&mut model, &held, Choice::Seen);
assert_eq!(last_op(&model), Some(json!("seen")));
}
#[test]
fn the_box_wears_the_attention_accent() {
let mut model = asked();
let window = Window::new();
let output = window.frame(Vec::new(), |ctx| crate::ui::render(ctx, &mut model));
let accent = theme::accent(theme::State::Attention);
let name = crate::paint_probe::seen_of(&output)
.into_iter()
.find(|run| run.text == HEADING)
.expect("the name is on the glass");
assert_eq!(name.ink, accent);
assert!(
crate::paint_probe::strokes_of(&output)
.iter()
.any(|(_, ink)| *ink == accent),
"the frame is green"
);
}
#[test]
fn the_box_goes_down_on_the_frame_the_receipt_lands() {
let mut model = asked();
let text = painted(&mut model);
assert!(text.contains(HEADING), "{text}");
let (selected, display) = row(&model)
.map(|(_, row)| (row.agent, row.display))
.expect("a row");
let remains = json!({
"workspace": "home", "agent": "c-2", "display": "c-2", "state": "quiescent",
"uncertain": true, "preview": "", "age_secs": 7, "pending": 0, "says": "",
"signals": [], "failure": null, "flag": null, "held": null,
});
let receipt = crate::reply::read(&json!({
"ok": true, "kind": "acknowledged", "workspace": "home", "agent": selected,
"rows": [remains],
}));
model.receipt(&crate::test_support::window::own().channel, "seen", receipt);
assert!(row(&model).is_none());
let text = painted(&mut model);
assert!(!text.contains(HINT), "{text}");
assert!(text.contains(&format!("seen {display} — off")), "{text}");
assert!(!text.contains("could not read"), "{text}");
}
#[test]
fn a_seen_row_that_still_asks_is_said_to() {
let acked = crate::reply::queue::Acknowledged {
workspace: "home".to_owned(),
agent: "c-2".to_owned(),
rows: vec![crate::test_support::window::waiting("home", "c-2")],
};
let said = crate::ui::model::Notice::seen(&acked, "c-2").line();
assert!(said.contains("still asking"), "{said}");
}
#[test]
fn a_down_engine_s_ask_goes_faint_and_answers_nothing() {
let why = "nothing provisioned for this channel";
let mut model = asked();
model.roster[0].held = crate::ui::Held::Unheld(why.to_owned());
assert_eq!(super::hint(&model), why);
let window = Window::new();
window.frame(Vec::new(), |ctx| crate::ui::render(ctx, &mut model));
let seen = crate::test_support::window::seen(&window, |ctx| crate::ui::render(ctx, &mut model));
let seen_row = seen
.iter()
.find(|run| run.text == "2. pass")
.expect("the choices stand");
assert_eq!(seen_row.ink, theme::INK_FAINT);
assert!(!seen.iter().any(|run| run.text == HINT), "no key line");
for key in [egui::Key::Num2, egui::Key::Enter] {
window.frame(vec![press(key)], |ctx| crate::ui::render(ctx, &mut model));
}
click(&window, "6. hold", |ctx| crate::ui::render(ctx, &mut model));
assert!(model.outbox.is_empty(), "{:?}", model.outbox);
}