dwarves-gui/tests/robot_front_tree.rs
//! **🌳 Tree, end to end: UI → forge → nornir.** The tab is given ONLY the forge's
//! address; the forge (the REAL dwarves proxy with its tree lane) folds the tree from
//! the board a mock embedded nornir answers (`Coderoom.Units`) and the tab draws it on
//! the shared graph renderer.
//!
//! `live_tree_render` (ignored) does the same against a LIVE forge — a local proxy in
//! front of `$DWARVES_LIVE_FORGE` (whose code-room door carries the board to its
//! nornir) — and writes a PNG of the drawn tab to `$DWARVES_TREE_PNG`.
use std::sync::Arc;
use dwarves_gui::front_tree::FrontTree;
use facett_core::Facet;
use nornir_warehouse_embed::testkit::{spawn_mock_code_room, MockCodeRoom};
use serde_json::json;
fn board() -> serde_json::Value {
// a crate of the workspace's own that all three repos use (a registry crate would
// fold into the repos)
let core = "path+ws:core/core#core@0.1.0";
json!({
"state": "complete",
"members": [
{ "member": "a", "phase": "compiling", "sha": "1", "units": 2, "done": 1, "own_units": 1 },
{ "member": "b", "phase": "landed", "sha": "2", "units": 2, "done": 2, "own_units": 1 },
{ "member": "c", "phase": "queued", "sha": "3", "units": 1, "done": 1, "own_units": 0 },
],
"counts": { "done": 2, "compiling": 1 },
"distinct_units": 3,
"units": [
{ "key": "s", "package": core, "target": "lib:core", "mode": "check", "test": false, "features": ["std"], "members": ["a", "b", "c"], "shared_by": 3, "state": "done", "deps": [] },
{ "key": "a", "package": "path+ws:a#a@0.1.0", "target": "lib:a", "mode": "check", "test": false, "features": [], "members": ["a"], "shared_by": 1, "state": "compiling", "deps": [0] },
{ "key": "b", "package": "path+ws:b#b@0.1.0", "target": "lib:b", "mode": "check", "test": false, "features": [], "members": ["b"], "shared_by": 1, "state": "done", "deps": [0] },
],
})
}
#[test]
fn the_tree_tab_draws_what_the_forge_folds_from_nornirs_board() {
let room = MockCodeRoom::answering(&[("Coderoom.Units", board())]);
let asked = room.asked.clone();
let nornir = spawn_mock_code_room(room);
let forge = dwarves::warehouse_proxy::spawn_proxy(nornir.endpoint.clone(), String::new());
let mut tab = FrontTree::new(forge.endpoint.clone(), String::new(), "w".to_string());
tab.update_json(r#"{"wait":true}"#);
let s = tab.state_json();
assert_eq!(s["answer"]["state"], "complete", "{s}");
assert_eq!(
(s["nodes"].as_u64(), s["edges"].as_u64()),
(Some(4), Some(3)),
"{s}"
);
assert!(
s["header"].as_str().unwrap().starts_with("2/3 units done"),
"{s}"
);
assert_eq!(
s["marks"],
json!({ "compiling": 1, "done": 2, "queued": 1 })
);
assert!(
s["answer"]["via"]
.as_str()
.unwrap()
.contains(&forge.endpoint),
"asked the forge, nothing else: {s}"
);
assert_eq!(
s["first_column"],
json!(["repo:a", "repo:b", "repo:c"]),
"the repos line up in the first column: {s}"
);
// painted: the first view is framed (the fit ran on the first paint)
let tab = std::cell::RefCell::new(tab);
{
let mut h = egui_kittest::Harness::builder()
.with_size(egui::vec2(1200.0, 800.0))
.build_ui(|ui| tab.borrow_mut().ui(ui));
for _ in 0..4 {
h.step();
}
}
let mut tab = tab.into_inner();
let cam = &tab.state_json()["graph"]["camera"];
assert_eq!(cam["fit_pending"], false, "{cam}");
assert_ne!(
cam["zoom"].as_f64(),
Some(1.0),
"framed, not left at zoom 1: {cam}"
);
// drill into repo a: its own crate appears
tab.update_json(r#"{"toggle":"repo:a"}"#);
tab.update_json(r#"{"wait":true}"#);
assert_eq!(tab.state_json()["nodes"], 5);
let asked = asked.lock().unwrap().clone();
assert!(
asked.iter().all(|(_, s, _)| s == "Coderoom.Units"),
"nornir is asked the board only: {asked:?}"
);
}
/// The live render: `DWARVES_LIVE_FORGE=http://… DWARVES_TREE_PNG=/tmp/tree.png cargo test
/// -p dwarves-gui --test robot_front_tree live_tree_render -- --ignored --nocapture`.
#[test]
#[ignore]
fn live_tree_render() {
let Ok(live) = std::env::var("DWARVES_LIVE_FORGE") else {
eprintln!("DWARVES_LIVE_FORGE not set — nothing to render");
return;
};
let token = std::env::var("DWARVES_LIVE_TOKEN").unwrap_or_default();
let ws = std::env::var("DWARVES_LIVE_WORKSPACE").unwrap_or_else(|_| "nordisk".into());
let forge = dwarves::warehouse_proxy::spawn_proxy(live, token);
let tab = Arc::new(std::sync::Mutex::new(FrontTree::new(
forge.endpoint.clone(),
String::new(),
ws,
)));
tab.lock().unwrap().update_json(r#"{"wait":true}"#);
if let Ok(expand) = std::env::var("DWARVES_LIVE_EXPAND") {
for id in expand.split(',').filter(|s| !s.is_empty()) {
tab.lock()
.unwrap()
.update_json(&json!({ "toggle": id }).to_string());
tab.lock().unwrap().update_json(r#"{"wait":true}"#);
}
}
let s = tab.lock().unwrap().state_json();
println!("{}", serde_json::to_string_pretty(&json!({
"answer": s["answer"], "header": s["header"], "nodes": s["nodes"], "edges": s["edges"], "marks": s["marks"], "box": s["box"],
})).unwrap());
if let Ok(png) = std::env::var("DWARVES_TREE_PNG") {
let t2 = tab.clone();
let mut h = egui_kittest::Harness::builder()
.with_size(egui::vec2(1800.0, 1100.0))
.build_ui(move |ui| t2.lock().unwrap().ui(ui));
for _ in 0..8 {
h.step();
}
match h.render() {
Ok(img) => {
img.save(&png).expect("write png");
println!("wrote {png}");
}
Err(e) => println!("no render: {e}"),
}
}
}