use super::super::{RESPONSE, TRAIL};
use crate::paint_probe::frame::Window;
use crate::paint_probe::{fills_of, seen_of};
use crate::reply::transcript::{Block, Entry, EntryKind, Transcript, Usage};
use crate::test_support::window::{click, said, seated};
use crate::ui::Model;
use crate::ui::theme::{self, glyph};
fn entry(kind: EntryKind) -> Entry {
Entry {
name: "003-model-a.json".to_owned(),
raw: "{}".to_owned(),
kind,
}
}
fn working() -> Entry {
entry(EntryKind::Model {
model_id: "model-a".to_owned(),
blocks: vec![
Block::Thinking("weighing two seams".to_owned()),
Block::ToolUse {
id: "tu-1".to_owned(),
name: "read".to_owned(),
input: "src/ui.rs".to_owned(),
},
],
usage: Usage::new(),
})
}
fn prose(text: &str) -> Entry {
entry(EntryKind::Model {
model_id: "model-a".to_owned(),
blocks: vec![Block::Text(text.to_owned())],
usage: Usage::new(),
})
}
fn result(content: &str, is_error: bool) -> Entry {
entry(EntryKind::ToolResult {
tool_use_id: "tu-1".to_owned(),
content: content.to_owned(),
is_error,
})
}
fn with(entries: Vec<Entry>) -> Model {
Model {
transcript: Transcript { entries },
..seated()
}
}
fn glass(model: &mut Model) -> egui::FullOutput {
let window = Window::sized(1400.0, 900.0);
window.frame(Vec::new(), |ctx| crate::ui::render(ctx, model));
window.frame(Vec::new(), |ctx| crate::ui::render(ctx, model))
}
fn texts(output: &egui::FullOutput) -> Vec<String> {
seen_of(output).into_iter().map(|run| run.text).collect()
}
#[test]
fn the_gutter_says_who_spoke() {
let mut model = with(vec![
said("op", "port it"),
said("judge-one", "seconded"),
prose("the seam is real"),
]);
let output = glass(&mut model);
let ink = |text: &str| {
seen_of(&output)
.into_iter()
.filter(|run| run.text == text)
.map(|run| run.ink)
.collect::<Vec<_>>()
};
assert!(
ink(glyph::PROMPT).contains(&theme::BRAND),
"{:?}",
texts(&output)
);
assert!(
ink(glyph::MODEL).contains(&theme::INK_WEAK),
"{:?}",
texts(&output)
);
assert!(
ink(glyph::NOTE).contains(&theme::INK_WEAK),
"{:?}",
texts(&output)
);
}
#[test]
fn the_trail_stands_under_one_fold_and_the_head_shuts_it() {
let mut model = with(vec![
said("op", "port it"),
working(),
result("done", false),
prose("the seam is real"),
]);
let output = glass(&mut model);
let seen = texts(&output);
let head = format!("{} {TRAIL} (1)", glyph::OPEN);
for word in [&head, glyph::CALL, glyph::DONE, "read(src/ui.rs)"] {
assert!(seen.iter().any(|t| t == word), "{word:?}: {seen:?}");
}
let window = Window::sized(1400.0, 900.0);
click(&window, &head, |ctx| crate::ui::render(ctx, &mut model));
let shut = window.text(|ctx| crate::ui::render(ctx, &mut model));
assert!(
shut.contains(&format!("{} {TRAIL} (1)", glyph::SHUT)),
"{shut}"
);
assert!(!shut.contains("read(src/ui.rs)"), "{shut}");
assert!(!seen.iter().any(|t| t.contains("tu-1")), "no id: {seen:?}");
}
#[test]
fn a_response_line_and_a_hairline_part_what_follows() {
let mut one = with(vec![said("op", "port it")]);
let quiet = glass(&mut one);
assert!(!texts(&quiet).iter().any(|t| t == RESPONSE));
let hairlines = |output: &egui::FullOutput| {
fills_of(output)
.iter()
.filter(|(rect, ink)| {
rect.height() <= 1.5 && rect.width() > 300.0 && *ink == theme::HAIRLINE
})
.count()
};
let before = hairlines(&quiet);
let mut two = with(vec![
said("op", "port it"),
working(),
result("done", false),
prose("the seam is real"),
said("op", "and again"),
]);
let parted = glass(&mut two);
assert!(
texts(&parted).iter().any(|t| t == RESPONSE),
"{:?}",
texts(&parted)
);
assert!(
hairlines(&parted) > before,
"a second turn is parted by a hairline"
);
}
#[test]
fn a_failed_step_wears_the_error_accent() {
let mut model = with(vec![working(), result("no such file", true)]);
let output = glass(&mut model);
let red = theme::accent(theme::State::Error);
assert!(
seen_of(&output)
.iter()
.any(|run| run.text == glyph::FAILED && run.ink == red),
"{:?}",
texts(&output)
);
}
#[test]
fn a_call_in_flight_wears_no_ending() {
let mut model = with(vec![working()]);
let output = glass(&mut model);
let seen = texts(&output);
assert!(seen.iter().any(|t| t == "read(src/ui.rs)"), "{seen:?}");
assert!(
!seen.iter().any(|t| t == glyph::DONE || t == glyph::FAILED),
"{seen:?}"
);
}