use crate::paint_probe::frame::Window;
use crate::paint_probe::seen_of;
use crate::reply::transcript::{Block, Entry, EntryKind, Transcript, Usage};
use crate::test_support::window::{numbered, said, seated};
use crate::ui::Model;
use crate::ui::theme;
fn entry(kind: EntryKind) -> Entry {
Entry {
name: "003-entry.json".to_owned(),
raw: "{}".to_owned(),
kind,
}
}
fn turn(model_id: &str, text: &str) -> Entry {
entry(EntryKind::Model {
model_id: model_id.to_owned(),
blocks: vec![Block::Text(text.to_owned())],
usage: Usage::new(),
})
}
fn tool(content: &str, is_error: bool) -> Entry {
entry(EntryKind::ToolResult {
tool_use_id: "tu-1".to_owned(),
content: content.to_owned(),
is_error,
})
}
fn glass(entries: Vec<Entry>) -> egui::FullOutput {
let mut model = Model {
transcript: Transcript { entries },
..seated()
};
let window = Window::sized(1400.0, 900.0);
window.frame(Vec::new(), |ctx| crate::ui::render(ctx, &mut model));
window.frame(Vec::new(), |ctx| crate::ui::render(ctx, &mut model))
}
fn run(output: &egui::FullOutput, header: &str) -> crate::paint_probe::Seen {
let runs = seen_of(output);
let gutters = [
crate::ui::theme::glyph::PROMPT,
crate::ui::theme::glyph::MODEL,
crate::ui::theme::glyph::NOTE,
crate::ui::theme::glyph::CALL,
crate::ui::theme::glyph::DONE,
crate::ui::theme::glyph::FAILED,
];
runs.iter()
.filter(|seen| seen.text == header)
.find(|seen| {
runs.iter().any(|glyph| {
gutters.contains(&glyph.text.as_str())
&& glyph.laid.max.x <= seen.laid.min.x
&& glyph.laid.min.y <= seen.laid.center().y
&& seen.laid.center().y <= glyph.laid.max.y
})
})
.cloned()
.unwrap_or_else(|| panic!("nothing on the glass reads {header:?} beside a gutter"))
}
fn body(output: &egui::FullOutput, text: &str) -> crate::paint_probe::Seen {
seen_of(output)
.into_iter()
.filter(|seen| seen.text == text)
.min_by(|a, b| a.laid.min.y.total_cmp(&b.laid.min.y))
.unwrap_or_else(|| panic!("nothing on the glass reads {text:?}"))
}
fn worn(output: &egui::FullOutput, header: &str) -> egui::Color32 {
run(output, header).ink
}
#[test]
fn each_speaker_s_block_wears_its_own_weight() {
let ended = entry(EntryKind::Delivered {
sender: "child".to_owned(),
sender_name: None,
epitaph: Some("delivered".to_owned()),
body: "landed".to_owned(),
});
let output = glass(vec![
said("op", "port it"),
said("user", "and again"),
said("judge-one", "seconded"),
ended,
turn("model-a", "the seam is real"),
]);
for (header, weight) in [
("op", theme::speaker(theme::Speaker::Operator)),
("user", theme::speaker(theme::Speaker::Operator)),
("judge-one", theme::speaker(theme::Speaker::Peer)),
("child (delivered)", theme::speaker(theme::Speaker::Ended)),
("model-a", theme::speaker(theme::Speaker::Model)),
] {
assert_eq!(
worn(&output, header),
weight,
"{header:?} does not wear {weight:?}"
);
}
assert_eq!(worn(&output, "op"), theme::BRAND);
assert_eq!(worn(&output, "model-a"), theme::INK);
}
#[test]
fn a_failed_tool_result_wears_the_error_accent_and_a_returned_one_does_not() {
let red = theme::accent(theme::State::Error);
let output = glass(vec![tool("no such file", true)]);
assert_eq!(worn(&output, "tu-1 failed"), red);
assert_eq!(body(&output, crate::ui::theme::glyph::FAILED).ink, red);
let returned = glass(vec![tool("done", false)]);
assert_ne!(worn(&returned, "tu-1 returned"), red);
assert_ne!(body(&returned, crate::ui::theme::glyph::DONE).ink, red);
}
#[test]
fn a_folded_head_stands_in_weak_ink() {
let output = glass(vec![tool(&numbered(700), false)]);
let head = seen_of(&output)
.into_iter()
.find(|seen| seen.text.starts_with("line 001"))
.expect("the head of the answer is on the glass");
assert_eq!(head.ink, theme::INK_WEAK, "{:?}", head.text);
}
#[test]
fn entries_are_parted_by_air_and_never_by_a_line() {
let output = glass(vec![turn("model-a", "first"), turn("model-a", "second")]);
let first = body(&output, "first");
let second = body(&output, "second");
let gap = second.laid.min.y - first.laid.max.y;
assert!(gap >= theme::space::S - 0.5, "gap {gap}");
let dividers = crate::paint_probe::fills_of(&output)
.into_iter()
.filter(|(rect, _)| {
rect.height() <= 1.5
&& rect.width() > 100.0
&& rect.min.y > first.laid.max.y
&& rect.max.y < second.laid.min.y
})
.count();
assert_eq!(dividers, 0, "no line between two entries");
assert!(
first.laid.height() >= theme::type_scale::LEADING - 0.5,
"{:?}",
first.laid
);
}