use super::{ShellState, render};
use crate::AppModel;
use crate::app::Roots;
use crate::cli_outbound::Cli;
use crate::git_tree::tests::fixture::Fixture;
use crate::keymap::InspectorTab;
use crate::projects::runner::BlCli;
use crate::ui_state::SystemClock;
use crate::xdg::Env;
use std::path::PathBuf;
use std::sync::Arc;
use tempfile::{TempDir, tempdir};
struct World {
_root: TempDir,
_fx: Fixture,
model: AppModel,
state: ShellState,
ws: PathBuf,
}
fn world() -> World {
let root = tempdir().unwrap();
let roots = Roots {
yog_data: root.path().join("yog"),
lernie_data: root.path().join("lernie"),
yog_state: root.path().join("state"),
balls_clones: root.path().join("balls").join("clones"),
home: root.path().join("home"),
};
std::fs::create_dir_all(roots.lernie_data.join("workspaces")).unwrap();
std::fs::create_dir_all(&roots.yog_state).unwrap();
let fx = Fixture::new();
fx.build_agent("c-1", "hello");
let messages = fx.path.join("agents/c-1/messages");
std::fs::create_dir_all(&messages).unwrap();
std::fs::write(messages.join("001-user.md"), "please ping").unwrap();
std::fs::write(
messages.join("002-opus.json"),
br#"{"content":[{"type":"text","text":"pong reply"}]}"#,
)
.unwrap();
let step = fx.path.join("steps/c-1/001");
std::fs::create_dir_all(step.join("tools/toolu_1")).unwrap();
std::fs::write(
step.join("response.json"),
b"{\"type\":\"usage\",\"input_tokens\":10,\"output_tokens\":5}\n{\"type\":\"error\",\"kind\":\"auth\",\"message\":\"no credential for this provider\",\"provider_detail\":null}\n{\"type\":\"end\"}\n",
)
.unwrap();
std::fs::write(step.join("tools/toolu_1/input.json"), br#"{"name":"Read"}"#).unwrap();
std::fs::write(
step.join("tools/toolu_1/output.json"),
br#"{"exit_code":0}"#,
)
.unwrap();
fx.deposit_message(
"c-1",
"user-001.md",
"---\nfrom: user\ndeposited_at: t0\n---\nfollow-up message",
);
let ws = roots.lernie_data.join("workspaces").join("ws");
std::os::unix::fs::symlink(&fx.path, &ws).unwrap();
let balls = Box::new(BlCli::new(Cli::new("bl")));
let model = AppModel::new(
roots,
Some(ws.clone()),
Arc::new(SystemClock),
balls,
Box::new(crate::test_support::OkProbe),
Some("me".into()),
);
let env = Env::from_pairs([("HOME", root.path().display().to_string())]);
let state = ShellState::new(&env).unwrap();
World {
_root: root,
_fx: fx,
model,
state,
ws,
}
}
fn input() -> egui::RawInput {
egui::RawInput {
screen_rect: Some(egui::Rect::from_min_size(
egui::Pos2::ZERO,
egui::vec2(1600.0, 2400.0),
)),
..Default::default()
}
}
fn collect_text(shape: &egui::Shape, out: &mut String) {
match shape {
egui::Shape::Text(t) => {
out.push_str(t.galley.text());
out.push('\n');
}
egui::Shape::Vec(shapes) => shapes.iter().for_each(|s| collect_text(s, out)),
_ => {}
}
}
fn painted(world: &mut World, lernie: &Cli, bl: &Cli) -> String {
let bz = Cli::new("bz");
let ctx = egui::Context::default();
let _ = ctx.run(input(), |ctx| {
render(ctx, &mut world.model, &mut world.state, lernie, bl, &bz);
});
let out = ctx.run(input(), |ctx| {
render(ctx, &mut world.model, &mut world.state, lernie, bl, &bz);
});
let mut text = String::new();
for clipped in out.shapes {
collect_text(&clipped.shape, &mut text);
}
text
}
#[test]
fn full_window_reaches_every_data_surface() {
let (lernie, bl) = (Cli::new("lernie"), Cli::new("bl"));
let mut world = world();
let ws = world.ws.clone();
world.model.focus_agent(&ws, "c-1");
world.state.inspector.step_sel = Some(0);
world.model.select_tab(InspectorTab::Transcript);
let base = painted(&mut world, &lernie, &bl);
assert!(base.contains("need attention"), "attention strip:\n{base}");
assert!(base.contains('+'), "the tab bar's + mint");
assert!(base.contains('⋯'), "the foreign/replay overflow menu");
assert!(base.contains("+ conversation"), "the list's + affordance");
assert!(base.contains("balls"), "the collapsible balls section");
assert!(base.contains("⚙ Config"), "Config entry");
assert!(
base.contains("→ message c-1"),
"composer targets the selection"
);
assert!(base.contains("Message"), "message verb:\n{base}");
assert!(base.contains("Scan"), "scan verb");
assert!(base.contains("activity · 0 ops"), "activity chip:\n{base}");
assert!(base.contains("c-1"), "conversation row / header");
assert!(base.contains("budget"), "budget-spent header");
assert!(
base.contains("failed on credentials"),
"auth banner:\n{base}"
);
assert!(base.contains("Login (bz device flow)"), "login surface");
assert!(
base.contains("please ping"),
"transcript delivered:\n{base}"
);
assert!(base.contains("pong reply"), "transcript model reply");
world.model.select_tab(InspectorTab::Steps);
world.state.inspector.step_tab = crate::steps_view::StepTab::Tools;
let steps = painted(&mut world, &lernie, &bl);
assert!(steps.contains("tok"), "steps token figure:\n{steps}");
assert!(steps.contains("toolu_1"), "steps tool i/o");
world.model.select_tab(InspectorTab::Inbox);
assert!(
painted(&mut world, &lernie, &bl).contains("follow-up message"),
"inbox deposit"
);
world.model.select_tab(InspectorTab::Files);
world.state.inspector.files_sel = Some(0);
let files = painted(&mut world, &lernie, &bl);
assert!(
files.contains("goal.md"),
"files worktree listing:\n{files}"
);
assert!(files.contains("hello"), "goal.md preview:\n{files}");
world.model.select_tab(InspectorTab::Config);
assert!(
painted(&mut world, &lernie, &bl).contains("policy frozen at"),
"governing config"
);
world.state.config.active = true;
let editors = painted(&mut world, &lernie, &bl);
assert!(
editors.contains("brazen config.toml"),
"brazen editor:\n{editors}"
);
assert!(
editors.contains("lernie global config"),
"lernie-global editor"
);
assert!(
editors.contains("workspace config branches"),
"config-branch editor"
);
assert!(editors.contains("config/default"), "config branch listing");
}