facett-brokkrview/tests/robot.rs
//! **Multi-step JSON-drive (robot) scenario for the brokkr build-results view**,
//! driven through the SHARED generic-Facet probe in `facett_core::harness` instead
//! of hand-rolled render+emit boilerplate. This one test both (a) proves the
//! `update_json` input surface is live end-to-end — feed a scripted message list,
//! watch `state_json` transition — the robot json-drive scenario the pane was
//! missing, and (b) emits this pane's functional matrix cell via
//! `facett_core::testmatrix::probe_and_emit` (a no-op unless built with
//! `--features testmatrix`), closing the zero-cell gap. The exact path the ~14
//! other zero-cell panes adopt: build the pane via its `demo()` constructor, hand
//! it a script, done.
use facett_brokkrview::BrokkrView;
#[test]
fn robot_json_drive_scenario() {
let mut view = BrokkrView::demo("Brokkr");
// A scripted gesture chain against the object-safe `update_json` seam — the
// same messages the UI would emit, as serde-tagged JSON (no concrete Msg type
// needed at the call site). Every one should move observable state.
let script = [
r#"{"set_view":"tree"}"#, // switch to the step tree
r#"{"select_pipeline":"nornir-ci"}"#, // focus a pipeline (log breadcrumb)
r#"{"run":"nornir-ci"}"#, // press Run → RunPipeline effect + log
r#"{"set_view":"dag"}"#, // switch to the stage/step DAG
r#"{"select_run":"nornir-ci#1"}"#, // select a roster run row
];
// ONE shared call: headless-render, drive the script snapshotting state after
// each message, and emit the functional matrix cell. Returns the probe so we
// can add pane-specific assertions.
let probe = facett_core::testmatrix::probe_and_emit("facett-brokkrview", &mut view, &script);
// The initial render drew real geometry (roster rows + chips).
assert!(probe.drew(), "the brokkr results view must tessellate to vertices");
// The pane exposes non-trivial domain state.
assert!(probe.cardinality() > 0, "state_json reports domain data");
// The input surface is LIVE: at least one scripted message moved state.
assert!(
probe.responded(),
"update_json must drive state — a dead input surface is a regression"
);
// Pane-specific: switching to the tree view then the DAG view must be visible
// in the state snapshots (the `view` field tracks the active surface).
let after_tree = &probe.steps[0].state;
assert_eq!(after_tree["view"], "tree", "set_view:tree lands in state_json");
let after_dag = &probe.steps[3].state;
assert_eq!(after_dag["view"], "dag", "set_view:dag lands in state_json");
// Pressing Run appended a roster/log breadcrumb — that step changed state.
assert!(probe.steps[2].changed, "run:nornir-ci mutates the log/roster");
// Final observable state stays well-formed JSON with the DAG present.
assert!(
probe.final_state.get("dag").is_some(),
"the DAG surface stays in the model across the drive"
);
}