tests/robot_misc_tabs.rs
//! **Robot-UI remaining-tabs sweep** — nav + "it drew" for every tab that lacked
//! a dedicated journey, especially the 7 tabs newly declared in `TAB_PANE_BLOCK`
//! (Metro/Helix/Bloodstream/WarehouseDeck/Build/Claude/Models). Reach + prove it
//! drew, through the real AccessKit header buttons, so each surface fills a
//! robot-ui matrix row.
//!
//! Run: `cargo test --features "viz" --test robot_misc_tabs`
#![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() -> 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-misc-{}-{n}", std::process::id()));
let _ = std::fs::create_dir_all(&root);
let app = UrdrThreadsApp::with_repos(root, "nornir".to_string(), PathBuf::new(), vec![]);
// Wide enough that the LAST second-row tab (🧠 Models) isn't clipped off the
// right edge (split=10 over 25 header tabs → 15 tabs on row two).
let harness = Harness::builder()
.with_size(egui::Vec2::new(2400.0, 900.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 nav(label: &str, id: &str) -> Step {
Step::new(format!("click {label} → {id}"))
.action(Action::ClickLabel(label.to_string()))
.assert(Assert::State {
pointer: "/tab".into(),
expected: json!(id),
})
}
/// Reach every remaining tab (incl. the 7 previously surfaceless ones) + prove it
/// drew. One scenario, many steps.
#[test]
fn robot_sweeps_remaining_tabs() {
let mut robot = robot();
let scenario = Scenario::new("misc-tabs")
.step(nav("🚇 Metro", "Metro"))
.step(Step::new("metro drew").assert(Assert::Drew))
.step(nav("🧬 Time Helix", "Helix"))
.step(Step::new("helix drew").assert(Assert::Drew))
.step(nav("🩸 Bloodstream", "Bloodstream"))
.step(Step::new("bloodstream drew").assert(Assert::Drew))
.step(nav("🏢 Wh Deck", "WarehouseDeck"))
.step(Step::new("deck drew").assert(Assert::Drew))
.step(nav("⚒ Dwarves", "Dwarves"))
.step(Step::new("dwarves drew").assert(Assert::Drew))
.step(nav("🧠 Models", "Models"))
.step(Step::new("models drew").assert(Assert::Drew))
.step(nav("🤖 Claude", "Claude"))
.step(Step::new("claude drew").assert(Assert::Drew))
.step(nav("🧵 Timeline", "Timeline"))
.step(Step::new("timeline drew").assert(Assert::Drew))
.step(nav("⏳ Time Travel", "TimeTravel"))
.step(Step::new("timetravel drew").assert(Assert::Drew))
.step(nav("📡 Live Run", "LiveRun"))
.step(Step::new("live drew").assert(Assert::Drew))
.step(nav("🗺 Knowledge", "Knowledge"))
.step(Step::new("knowledge drew").assert(Assert::Drew))
.step(nav("🛡 Security", "Security"))
.step(Step::new("security drew").assert(Assert::Drew))
.step(nav("📖 Manual", "Manual"))
.step(Step::new("manual drew").assert(Assert::Drew));
let rows = robot.run_scenario(&scenario);
for r in &rows {
assert_eq!(
r.status,
status::PASS,
"step {:?} failed: {}",
r.test_name,
r.message
);
}
}