mod refund;
use super::{INTERRUPT, NOWHERE, NUDGE, SEND, render, start};
use crate::paint_probe::frame::Window;
use crate::test_support::window::{click, pane, seated};
use crate::ui::Model;
use serde_json::json;
#[test]
fn what_the_composer_is_for_follows_from_what_is_selected() {
for (model, expected) in [
(Model::default(), NOWHERE),
(
Model {
aim: None,
..seated()
},
NOWHERE,
),
(
Model {
conversation: None,
..seated()
},
start::START,
),
(seated(), SEND),
] {
let mut model = model;
let painted = pane(|ui| render(ui, &mut model));
assert!(
painted.lines().any(|line| line == expected),
"{expected:?}:\n{painted}"
);
}
}
#[test]
fn sending_composes_the_deposit_the_command_line_would_have_and_posts_nothing() {
let mut model = seated();
model.draft = "ship it".to_owned();
let window = Window::new();
click(&window, SEND, |ctx| {
egui::CentralPanel::default().show(ctx, |ui| render(ui, &mut model));
});
assert_eq!(
model.outbox,
vec![crate::ui::Posted::act(
json!({"op": "message", "workspace": "home",
"agent": "20260830T051200Z-a1b2", "content": "ship it"})
)]
);
assert_eq!(model.draft, "", "what was sent is no longer a draft");
}
#[test]
fn cutting_composes_the_interrupt_off_the_same_box_the_deposit_spends() {
let mut model = seated();
model.draft = "no, this".to_owned();
let window = Window::new();
click(&window, INTERRUPT, |ctx| {
egui::CentralPanel::default().show(ctx, |ui| render(ui, &mut model));
});
assert_eq!(
model.outbox,
vec![crate::ui::Posted::act(
json!({"op": "interrupt", "workspace": "home",
"agent": "20260830T051200Z-a1b2", "content": "no, this"})
)]
);
assert_eq!(model.draft, "", "what was said is no longer a draft");
}
#[test]
fn an_empty_draft_cuts_nothing_because_that_gesture_has_its_own_control() {
let mut model = seated();
let window = Window::new();
click(&window, INTERRUPT, |ctx| {
egui::CentralPanel::default().show(ctx, |ui| render(ui, &mut model));
});
assert!(model.outbox.is_empty(), "{:?}", model.outbox);
}
#[test]
fn an_empty_draft_fires_nothing_and_costs_nothing() {
let mut model = seated();
model.draft = " ".to_owned();
let window = Window::new();
click(&window, SEND, |ctx| {
egui::CentralPanel::default().show(ctx, |ui| render(ui, &mut model));
});
assert!(model.outbox.is_empty(), "{:?}", model.outbox);
assert_eq!(model.draft, " ", "the draft is still the operator's");
}
#[test]
fn the_deposit_carries_the_address_the_channel_resolves() {
let mut model = seated();
model.aim = Some(crate::ui::Aim {
channel: "home".to_owned(),
address: "home".to_owned(),
});
model.draft = "ship it".to_owned();
let window = Window::new();
click(&window, SEND, |ctx| {
egui::CentralPanel::default().show(ctx, |ui| render(ui, &mut model));
});
assert_eq!(model.outbox[0].envelope["workspace"], json!("home"));
}
#[test]
fn enter_sends_what_was_typed() {
let mut model = seated();
let window = Window::new();
let mut body = |ctx: &egui::Context| {
egui::CentralPanel::default().show(ctx, |ui| render(ui, &mut model));
};
let at = crate::paint_probe::frame::locate_in(&window, SEND, &mut body).expect("the hint");
crate::paint_probe::frame::click(&window, at, &mut body);
window.frame(vec![egui::Event::Text("ship it".to_owned())], &mut body);
window.frame(
vec![crate::paint_probe::frame::press(egui::Key::Enter)],
&mut body,
);
window.frame(Vec::new(), &mut body);
assert_eq!(
model.outbox,
vec![crate::ui::Posted::act(
json!({"op": "message", "workspace": "home",
"agent": "20260830T051200Z-a1b2", "content": "ship it"})
)]
);
}
#[test]
fn tab_and_space_fire_the_composer_s_controls_with_no_binding_of_their_own() {
let mut model = seated();
model.draft = "ship it".to_owned();
let window = Window::new();
let mut reached = Vec::new();
{
let mut body = |ctx: &egui::Context| {
egui::CentralPanel::default().show(ctx, |ui| render(ui, &mut model));
};
window.frame(Vec::new(), &mut body);
for _ in 0..6 {
window.frame(
vec![crate::paint_probe::frame::press(egui::Key::Tab)],
&mut body,
);
reached.push(window.focused());
window.frame(
vec![crate::paint_probe::frame::press(egui::Key::Space)],
&mut body,
);
window.frame(Vec::new(), &mut body);
}
}
assert!(
reached.contains(&Some(egui::Id::new(crate::ui::keys::BOX_ID))),
"the box is in the tab order too: {reached:?}"
);
for op in ["message", "nudge"] {
assert!(
model
.outbox
.iter()
.any(|said| said.envelope["op"] == json!(op)),
"{op:?} was never fired from the keyboard: {:?}",
model.outbox
);
}
}
#[test]
fn the_advance_composes_its_own_gesture_and_takes_no_draft() {
let mut model = seated();
model.draft = "not this".to_owned();
let window = Window::new();
click(&window, NUDGE, |ctx| {
egui::CentralPanel::default().show(ctx, |ui| render(ui, &mut model));
});
assert_eq!(
model.outbox,
vec![crate::ui::Posted::act(
json!({"op": "nudge", "workspace": "home",
"agent": "20260830T051200Z-a1b2"})
)]
);
assert_eq!(model.draft, "not this", "the draft is untouched");
}
#[test]
fn a_started_conversation_the_engine_cannot_resolve_yet_has_no_box() {
let mut model = Model {
conversation: Some("brisk-otter".to_owned()),
start: Some(crate::ui::model::Start {
address: "home".to_owned(),
goal: "port it".to_owned(),
phase: crate::ui::model::Phase::Started("brisk-otter".to_owned()),
spread: None,
}),
..seated()
};
let painted = pane(|ui| render(ui, &mut model));
assert!(
painted.contains("started «brisk-otter» in home"),
"{painted}"
);
for gone in [SEND, NUDGE] {
assert!(
!painted.lines().any(|line| line == gone),
"{gone:?} is still on the glass: {painted}"
);
}
assert!(model.outbox.is_empty());
}