tests/robot_arch_metro.rs
//! **Robot-UI Architecture sub-view walk + Metro** — drive 🏛 Architecture's
//! sub-view selector through every lens (Deps → Live → Wiring → Boundaries →
//! CallGraph → Metro), then reload + fit, and nav the standalone 🚇 Metro tab.
//! Fills a robot-ui row per Architecture lens.
//!
//! Run: `cargo test --features "viz" --test robot_arch_metro`
#![cfg(feature = "viz")]
use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, Ordering};
use egui_kittest::Harness;
use nornir::viz::UrdrThreadsApp;
use nornir_robotui::egui;
use nornir_robotui::{Action, Assert, Observable, RobotSession, Scenario, Step, status};
use serde_json::json;
struct VizApp(UrdrThreadsApp);
impl Observable for VizApp {
fn state_json(&self) -> serde_json::Value {
self.0.state_json()
}
}
static SEQ: AtomicU64 = AtomicU64::new(0);
fn robot(tag: &str) -> RobotSession<'static, VizApp> {
unsafe { std::env::set_var("NORNIR_VIZ_NO_DURABLE_ACTIONLOG", "1") };
let n = SEQ.fetch_add(1, Ordering::Relaxed);
let root = std::env::temp_dir().join(format!("nornir-arch-{tag}-{}-{n}", std::process::id()));
let _ = std::fs::create_dir_all(&root);
let app = UrdrThreadsApp::with_repos(root, "nornir".to_string(), PathBuf::new(), vec![]);
let harness = Harness::builder()
.with_size(egui::Vec2::new(1600.0, 1000.0))
.build_ui_state(|ui, app: &mut VizApp| app.0.draw_ui(ui), VizApp(app));
let mut r = RobotSession::new(harness, "nornir");
r.run(4);
r
}
fn assert_all_pass(rows: &[nornir_robotui::TestResultRow]) {
for r in rows {
assert_eq!(
r.status,
status::PASS,
"step {:?} failed: {}",
r.test_name,
r.message
);
}
}
#[test]
fn robot_architecture_subview_walk() {
let mut robot = robot("walk");
robot
.click_label("🏛 Architecture")
.expect("Architecture header");
robot.run(2);
let subviews = [
("🔗 Dep Graph", "Deps"),
("📡 Live system", "Live"),
("🏛 Wiring", "Wiring"),
("📍 Hållpunkter", "Boundaries"),
("🕸 Call Graph", "CallGraph"),
("🚇 Metro (call path)", "Metro"),
];
let mut scenario = Scenario::new("arch-subviews");
for (label, id) in subviews {
scenario = scenario.step(
Step::new(format!("select {label} → sub_view == {id}"))
.action(Action::ClickLabel(label.to_string()))
.assert(Assert::State {
pointer: "/architecture/sub_view".into(),
expected: json!(id),
}),
);
}
scenario = scenario
// Return to a graph lens (the ⊙ fit / arch control strip only renders on
// the wiring/live graph views, not the Metro call-path lens).
.step(
Step::new("back to 🏛 Wiring lens → sub_view == Wiring")
.action(Action::ClickLabel("🏛 Wiring".into()))
.assert(Assert::State {
pointer: "/architecture/sub_view".into(),
expected: json!("Wiring"),
}),
)
.step(
Step::new("⊙ fit the graph")
.action(Action::ClickLabel("⊙ fit".into()))
.assert(Assert::Drew),
);
assert_all_pass(&robot.run_scenario(&scenario));
}
#[test]
fn robot_metro_tab_nav() {
let mut robot = robot("metro");
let scenario = Scenario::new("metro-journey")
.step(
Step::new("nav 🚇 Metro → Metro")
.action(Action::ClickLabel("🚇 Metro".into()))
.assert(Assert::State {
pointer: "/tab".into(),
expected: json!("Metro"),
}),
)
.step(Step::new("metro pane drew").assert(Assert::Drew));
assert_all_pass(&robot.run_scenario(&scenario));
}