facett-app/tests/chrome_shot.rs
//! **Screenshot PROOF that the window chrome is actually on the screen.** The whole
//! reason this layer exists is that the scene's interaction model was real in the data
//! and invisible in the UI — so it ships with a shot a human can open and look at.
//!
//! Renders one scene twice, through the real wgpu backend, with NOTHING switched on by
//! the host (no chrome flag, no opt-in). The PNGs land in the gitignored
//! `tests/snapshots/` (repo policy: reference images live in the warehouse, not git) —
//! run the test and open them:
//!
//! - `scene_chrome.png` — every pane wearing its title bar (fold ⏷ · minimise − ·
//! maximise 🗖), the `Stats` pane MINIMISED to just its bar, and a three-dot grip on
//! each draggable divider.
//! - `scene_chrome_maximized.png` — the same scene with `Canvas` maximised: the rail and
//! both of its split siblings are gone, because they were allotted zero.
//!
//! Both are asserted non-trivial (a blank/failed render fails CI) AND asserted against
//! the render's own `state_json`, so the picture and the geometry cannot drift apart.
//!
//! Run: `cargo test -p facett-app --test chrome_shot -- --nocapture`
use egui_kittest::Harness;
use facett_app::scene::{self, Axis, NavState, Pane, Scene, SceneHost};
use serde_json::Value;
struct Host;
impl SceneHost for Host {
fn slot(&mut self, id: &str, ui: &mut egui::Ui) -> Value {
ui.label(format!("— body of {id} —"));
for i in 0..3 {
ui.label(format!("row {i}"));
}
serde_json::json!({ "slot": id })
}
}
/// A rail | (canvas / log / stats) shell — the shape most nordisk apps actually have.
fn shell() -> Scene {
Scene::new(
"chrome-shot",
Pane::split(
"root",
Axis::Horizontal,
0.28,
vec![
Pane::slot("rail", "nav").title("Navigator").foldable_icon(true, "⚙"),
Pane::split_n(
"stack",
Axis::Vertical,
vec![0.45, 0.3, 0.25],
vec![
Pane::slot("canvas", "canvas").title("Canvas"),
Pane::slot("log", "log").title("Activity log"),
Pane::slot("stat", "stat").title("Stats"),
],
)
.title("Body"),
],
),
)
}
/// Render `scene` with `nav` once and save the PNG; returns the render's `state_json`.
fn shoot(scene: &Scene, mut nav: NavState, name: &str) -> Value {
let state = std::rc::Rc::new(std::cell::RefCell::new(Value::Null));
let (s, sc) = (state.clone(), scene.clone());
let mut h = Harness::builder().with_size(egui::vec2(900.0, 520.0)).build_ui(move |ui| {
*s.borrow_mut() = scene::render(&sc, &mut nav, ui, &mut Host);
});
h.run();
let img = h.render().expect("wgpu render produced an image");
let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/snapshots");
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join(format!("{name}.png"));
let (w, ht) = (img.width(), img.height());
let mut f = std::io::BufWriter::new(std::fs::File::create(&path).unwrap());
let mut enc = png::Encoder::new(&mut f, w, ht);
enc.set_color(png::ColorType::Rgba);
enc.set_depth(png::BitDepth::Eight);
enc.write_header().unwrap().write_image_data(&img).unwrap();
drop(f);
let bytes = std::fs::metadata(&path).unwrap().len();
assert!(bytes > 5_000, "golden PNG must be non-trivial (got {bytes} bytes)");
eprintln!("chrome shot → {} ({bytes} bytes)", path.display());
state.borrow().clone()
}
#[test]
fn the_window_chrome_is_visibly_on_screen() {
let scene = shell();
// ── 1. The default look: every pane chromed, `Stats` minimised to its bar. ────
let mut nav = NavState::new(&scene);
nav.set_minimized("stat", true);
let st = shoot(&scene, nav, "scene_chrome");
for p in ["rail", "canvas", "log", "stat"] {
assert_eq!(st["panes"][p]["chrome"]["spec"]["bar"], true, "`{p}` drew a title bar in the golden");
assert!(st["panes"][p]["chrome"]["buttons"]["maximize"][2].as_f64().unwrap() > 4.0, "`{p}`'s maximise control has real pixels");
}
assert_eq!(st["panes"]["stat"]["alloc"][3], scene::CHROME_BAR as f64, "the golden shows `Stats` collapsed to exactly its bar");
assert_eq!(st["panes"]["stat"]["content_height"], 0.0, "…with its body gone");
assert!(st["panes"]["stack"]["separators"].as_array().unwrap().len() == 2, "two grabbable dividers are in the picture");
// ── 2. Maximised: the same scene, the canvas taking everything. ──────────────
let mut nav = NavState::new(&scene);
nav.set_maximized("canvas");
let st = shoot(&scene, nav, "scene_chrome_maximized");
assert_eq!(st["panes"]["rail"]["alloc"][2], 0.0, "the golden shows the rail allotted ZERO width");
assert_eq!(st["panes"]["log"]["alloc"][3], 0.0, "…and the log ZERO height");
assert_eq!(st["panes"]["stat"]["alloc"][3], 0.0, "…and the stats ZERO height");
assert!(st["panes"]["canvas"]["alloc"][2].as_f64().unwrap() > 600.0, "…and the canvas spanning the window");
}