use super::super::fixture::World;
use super::super::screen::{Screen, click};
use super::CHILD;
use crate::nav::convs::ConvRow;
use crate::paint_probe::Painted;
pub(super) const SECOND: &str = "c-2";
pub(super) fn driven_world() -> (World, Screen) {
let mut world = super::nested_world();
world.add_root(SECOND, "second-root");
let ws = world.ws.clone();
world.model.mark_dirty([ws]);
world.converge();
let screen = Screen::new();
screen.idle(&mut world);
(world, screen)
}
pub(super) fn column(screen: &Screen, world: &mut World) -> Vec<Painted> {
screen.idle(world);
let shapes = screen.shapes(world, Vec::new());
let edge = screen.column();
let mut out = Vec::new();
for clipped in &shapes {
crate::paint_probe::collect(&clipped.shape, &mut out);
}
let out: Vec<Painted> = out.into_iter().filter(|(_, r)| r.left() < edge).collect();
one_title_each(&out, &visible(world));
out
}
pub(super) fn reads_as(text: &str, name: &str) -> bool {
text == name
|| text
.strip_suffix('…')
.is_some_and(|head| !head.is_empty() && name.starts_with(head))
}
pub(super) fn one_title_each(painted: &[Painted], rows: &[ConvRow]) {
let names: std::collections::BTreeSet<String> =
rows.iter().map(ConvRow::display_name).collect();
for (text, _) in painted {
let reads: Vec<&String> = names.iter().filter(|n| reads_as(text, n)).collect();
assert!(
reads.len() <= 1,
"the frame paints {text:?}, which reads as more than one row's title: {reads:?}"
);
}
}
pub(super) fn at(painted: &[Painted], needle: &str) -> Option<egui::Rect> {
painted
.iter()
.filter(|(text, _)| reads_as(text, needle))
.map(|(_, rect)| *rect)
.reduce(|a, b| if a.left() <= b.left() { a } else { b })
}
pub(super) fn seat(painted: &[Painted], needle: &str) -> egui::Rect {
at(painted, needle).unwrap_or_else(|| {
panic!(
"the conversation column paints no {needle:?}:\n{:?}",
painted.iter().map(|(t, _)| t).collect::<Vec<_>>()
)
})
}
pub(super) fn elbows(painted: &[Painted]) -> usize {
painted
.iter()
.filter(|(text, _)| text.ends_with(crate::theme::ELBOW))
.count()
}
pub(super) fn visible(world: &World) -> Vec<ConvRow> {
world
.model
.visible_conversations(crate::shell::now_unix(), &world.state.expanded)
}
pub(super) fn band(painted: &[Painted], rect: egui::Rect) -> Vec<Painted> {
painted
.iter()
.filter(|(_, r)| r.center().y > rect.top() && r.center().y < rect.bottom())
.cloned()
.collect()
}
#[test]
fn clicking_the_field_unfolds_the_row_and_folds_it_again_without_selecting_it() {
let (mut world, screen) = driven_world();
let child = super::name_of(&world, CHILD);
let shut = column(&screen, &mut world);
let arrow = seat(&shut, "▶ 1/2").center();
assert!(
at(&shut, &child).is_none(),
"the descent starts folded away, so the column paints no {child}"
);
click(&screen, &mut world, arrow);
let open = column(&screen, &mut world);
assert!(
world.state.expanded.contains("c-1"),
"the click flips the row's id in the expanded set: {:?}",
world.state.expanded
);
assert_eq!(
world.model.focused_agent().map(|a| a.agent_id.clone()),
None,
"and selects nothing — the field is its own control, not a strip of the row"
);
let title = seat(&open, "hello");
let revealed = seat(&open, &child);
assert!(
revealed.left() > title.left() + 1.0,
"the revealed child hangs indented under its parent: {revealed:?} vs {title:?}"
);
assert_eq!(elbows(&open), 1, "wearing the one reply elbow it earned");
assert!(
at(&open, "▼ 1/2").is_some() && at(&open, "▶ 1/2").is_none(),
"and the arrow the click was aimed at now points down"
);
click(&screen, &mut world, seat(&open, "▼ 1/2").center());
let shut = column(&screen, &mut world);
assert!(
!world.state.expanded.contains("c-1"),
"the second click folds it back: {:?}",
world.state.expanded
);
assert!(
at(&shut, &child).is_none() && elbows(&shut) == 0,
"so the column is the one-row list it started as:\n{:?}",
shut.iter().map(|(t, _)| t).collect::<Vec<_>>()
);
}
#[test]
fn the_fields_hover_states_both_numbers_and_the_keys_that_press_it() {
let mut world = super::nested_world();
let (lernie, bl, bz) = (
crate::cli_outbound::Cli::new("yog-absent-lernie"),
crate::cli_outbound::Cli::new("yog-absent-bl"),
crate::cli_outbound::Cli::new("yog-absent-bz"),
);
let ctx = egui::Context::default();
ctx.memory_mut(|m| m.set_everything_is_visible(true));
let mut frame = || {
let out = ctx.run(super::super::input(), |ctx| {
super::super::super::render(ctx, &mut world.model, &mut world.state, &lernie, &bl, &bz);
});
crate::paint_probe::text_of(&out)
};
frame();
let painted = frame();
for phrase in [
"1 dispatched by this agent itself",
"2 under it altogether at any depth",
"→ unfolds the selected row",
"← folds it shut",
"← on a child pages the selection up to its parent",
] {
assert!(
painted.contains(phrase),
"the subagent field's hover must say {phrase:?}:\n{painted}"
);
}
}