facett-coderoom-view/tests/robot.rs
//! **Robot world-coderoom scenario for the shared pane** (`egui_kittest`).
//!
//! Renders the REAL [`facett_coderoom_view::CoderoomPane`] body over the stub
//! [`CoderoomDataSeam`] and asserts, off the live AccessKit tree + the pane's
//! `state_json`, that every render layer reaches the surface a robot (or a screen
//! reader) sees:
//! 1. **Dependency graph** — the REUSED `DecoratedGraphView` receives + paints the
//! laid-out scene (5 crate nodes, 4 compat edges); its header reaches the tree.
//! 2. **Compat edges** — the 3 breaking edges paint **red**, the 1 compatible edge
//! **green** (asserted as data off the pane's `state_json`).
//! 3. **What-breaks mode** — selecting the dropped-symbol crate (`a@0.2.0`) lights
//! exactly the 3 impacted callers `{b, c, d}` (a red ring in the scene) and
//! dims the rest; `e` (a dependent that does NOT break) is not impacted.
//! 4. **Summary line** — `N crates · M breaking edges · K impacted callers` paints
//! as real widgets.
//! 5. No crash: the whole pane body runs through the harness without panicking.
//!
//! This is the L4 UI proof for the suite's world-coderoom surface: nornir/dwarves
//! delete any bespoke dep/compat body and mount THIS one (implementing
//! `CoderoomDataSeam` over the real coderoom warehouse), so this test guards
//! theirs. RED-when-broken (verified by neutralizing): if the graph, the breaking
//! colour, the impact highlight, or the summary regressed, an assertion goes red.
//! One matrix cell `facett-coderoom-view/coderoom-pane`.
use std::cell::RefCell;
use std::rc::Rc;
use egui_kittest::kittest::Queryable;
use egui_kittest::Harness;
use facett_coderoom_view::{CoderoomMode, CoderoomPane, Facet, StubCoderoomData, GREEN, RED, STUB_TARGET};
/// Emit shim (real row with `--features testmatrix`, no-op otherwise).
#[cfg(feature = "testmatrix")]
fn emit(check: &str, ok: bool, detail: &str) {
nornir_testmatrix::functional_status("facett-coderoom-view", check, ok, detail);
}
#[cfg(not(feature = "testmatrix"))]
fn emit(_check: &str, _ok: bool, _detail: &str) {}
fn hex(c: facett_coderoom_view::Color) -> String {
format!("#{:02x}{:02x}{:02x}", c.r, c.g, c.b)
}
#[test]
fn coderoom_pane_renders_graph_colours_and_what_breaks() {
let pane = Rc::new(RefCell::new(CoderoomPane::new(Box::new(StubCoderoomData))));
let mut harness = Harness::builder().with_size(egui::vec2(760.0, 720.0)).build_ui({
let pane = Rc::clone(&pane);
move |ui| {
egui::ScrollArea::both().show(ui, |ui| {
pane.borrow_mut().ui(ui);
});
}
});
// ≥1 = present (a label can ride in as multiple nodes).
let seen = |h: &Harness, label: &str| h.query_all_by_label(label).count() >= 1;
// Run a frame — proves the whole body paints without panicking (no crash).
harness.run();
// (1) The REUSED dep-graph viz received + painted the scene.
let graph_ok = {
let guard = pane.borrow_mut();
let s = guard.state_json();
s["graph_nodes"] == 5 && s["graph_edges"] == 4
};
assert!(graph_ok, "the reused DecoratedGraphView holds all 5 nodes + 4 edges");
// Its header (a real widget) reaches the a11y tree.
let graph_header_seen = seen(&harness, "5 nodes · 4 edges");
assert!(graph_header_seen, "the reused graph header paints");
// (2) Compat edges: 3 breaking (red), 1 compatible (green) — asserted as data.
let colours_ok = {
let guard = pane.borrow_mut();
let s = guard.state_json();
let edges = s["edges"].as_array().unwrap().clone();
let breaking: Vec<_> = edges.iter().filter(|e| e["breaking"] == true).collect();
let compat: Vec<_> = edges.iter().filter(|e| e["breaking"] == false).collect();
breaking.len() == 3
&& breaking.iter().all(|e| e["color"] == hex(RED))
&& compat.len() == 1
&& compat[0]["color"] == hex(GREEN)
};
assert!(colours_ok, "3 breaking edges paint red, 1 compatible edge green");
// (3) The base summary line paints (real widgets).
let base_summary_seen = seen(&harness, "5 crates") && seen(&harness, "3 breaking edges");
assert!(base_summary_seen, "the base summary line paints");
// (4) What-breaks mode: select the dropped-symbol crate `a@0.2.0`.
pane.borrow_mut().select_target(Some(STUB_TARGET.to_string()));
harness.run();
let (impact_ok, rings_ok) = {
let guard = pane.borrow_mut();
let s = guard.state_json();
let impacted: std::collections::BTreeSet<String> =
s["impacted"].as_array().unwrap().iter().map(|v| v.as_str().unwrap().to_string()).collect();
let want: std::collections::BTreeSet<String> =
["b@1.0.0", "c@1.1.0", "d@0.9.0"].iter().map(|s| s.to_string()).collect();
let impact_ok = s["impacted_count"] == 3 && impacted == want && !impacted.contains("e@2.0.0");
// The 3 impacted callers carry a red ring in the scene handed to the viz.
let decos = &guard.graph_decorations();
let rings_ok = ["b@1.0.0", "c@1.1.0", "d@0.9.0"]
.iter()
.all(|id| decos.get(*id).and_then(|d| d.ring) == Some(RED))
&& decos.get("e@2.0.0").and_then(|d| d.ring).is_none();
(impact_ok, rings_ok)
};
assert!(impact_ok, "selecting a@0.2.0 highlights exactly the 3 impacted callers");
assert!(rings_ok, "the 3 impacted callers get a red ring; the non-breaking dependent none");
// (5) The what-breaks summary line paints the impacted count.
let impacted_summary_seen = seen(&harness, "3 impacted callers");
assert!(impacted_summary_seen, "the what-breaks summary line paints the impacted count");
let ok = graph_ok
&& graph_header_seen
&& colours_ok
&& base_summary_seen
&& impact_ok
&& rings_ok
&& impacted_summary_seen;
emit(
"coderoom-pane",
ok,
&format!(
"graph={graph_ok} header={graph_header_seen} colours={colours_ok} \
base_summary={base_summary_seen} impact={impact_ok} rings={rings_ok} \
impacted_summary={impacted_summary_seen}"
),
);
assert!(ok, "every world-coderoom layer reached the render");
}
/// **What-changed (snapshot-diff) mode** — the second matrix cell. Renders the REAL
/// pane over the stub seam, toggles into the snapshot-diff view, and asserts off the
/// live AccessKit tree + `state_json` that:
/// 1. the stub diff reaches the pane (`f@0.1.0` added, `d@0.9.0` removed, `a@0.2.0`
/// changed with `+2 −1 ~3` symbol churn);
/// 2. each row paints as a real widget (green `+ added`, red `− removed`, amber
/// `~ changed`), and the `1 added · 1 removed · 1 changed` summary paints;
/// 3. toggling back to the graph works (the reused graph header paints again);
/// 4. no crash across every frame.
///
/// RED-when-broken: neutralize the diff, a row, or the toggle and an assertion fails.
/// Matrix cell `facett-coderoom-view/coderoom-what-changed`.
#[test]
fn coderoom_pane_what_changed_mode() {
let pane = Rc::new(RefCell::new(CoderoomPane::new(Box::new(StubCoderoomData))));
let mut harness = Harness::builder().with_size(egui::vec2(760.0, 720.0)).build_ui({
let pane = Rc::clone(&pane);
move |ui| {
egui::ScrollArea::both().show(ui, |ui| {
pane.borrow_mut().ui(ui);
});
}
});
let seen = |h: &Harness, label: &str| h.query_all_by_label(label).count() >= 1;
// First frame in the default graph mode — proves the toggle offer paints.
harness.run();
let toggle_offered = seen(&harness, "What changed");
assert!(toggle_offered, "the what-changed toggle is offered (the host feeds a diff)");
// Toggle into the snapshot-diff view.
pane.borrow_mut().set_mode(CoderoomMode::WhatChanged);
harness.run();
// (1) The stub diff reached the pane (asserted off state_json).
let diff_ok = {
let guard = pane.borrow_mut();
let s = guard.state_json();
let d = &s["snapshot_diff"];
s["mode"] == "what_changed"
&& s["diff_added"] == 1
&& s["diff_removed"] == 1
&& s["diff_changed"] == 1
&& d["crates_added"][0] == "f@0.1.0"
&& d["crates_removed"][0] == "d@0.9.0"
&& d["crates_changed"][0]["crate_id"] == "a@0.2.0"
&& d["crates_changed"][0]["symbols_added"] == 2
&& d["crates_changed"][0]["symbols_removed"] == 1
&& d["crates_changed"][0]["symbols_changed"] == 3
};
assert!(diff_ok, "the stub snapshot diff reaches the pane");
// (2) Each diff row + the summary paint as real widgets.
let added_seen = seen(&harness, "+ f@0.1.0");
let removed_seen = seen(&harness, "− d@0.9.0");
let changed_seen = seen(&harness, "~ a@0.2.0 +2 −1 ~3");
let summary_seen = seen(&harness, "1 added · 1 removed · 1 changed");
assert!(added_seen, "the added crate row paints (green +)");
assert!(removed_seen, "the removed crate row paints (red −)");
assert!(changed_seen, "the changed crate row paints with its symbol churn counts");
assert!(summary_seen, "the what-changed summary line paints");
// (3) Toggle back to the graph — the reused graph header paints again.
pane.borrow_mut().set_mode(CoderoomMode::Graph);
harness.run();
let back_to_graph = seen(&harness, "5 nodes · 4 edges");
assert!(back_to_graph, "toggling back to the graph re-paints the dependency graph");
let ok = toggle_offered
&& diff_ok
&& added_seen
&& removed_seen
&& changed_seen
&& summary_seen
&& back_to_graph;
emit(
"coderoom-what-changed",
ok,
&format!(
"toggle={toggle_offered} diff={diff_ok} added={added_seen} removed={removed_seen} \
changed={changed_seen} summary={summary_seen} back_to_graph={back_to_graph}"
),
);
assert!(ok, "the snapshot-diff what-changed mode reached the render");
}