facett-demo/tests/responsive_fold.rs
//! **facett-demo — the responsive MOBILE/WASM fold proof (#30)** (headless, no window).
//!
//! The deployed wasm demo showed every control button but the demo CANVAS sat BELOW
//! the phone screen fold: the control strip (look / native / palette pickers + the fx +
//! region buttons) is a fixed strip that wraps into many rows and eats a narrow
//! viewport. `draw_app` now classifies the viewport width through the SHARED scene
//! machinery ([`facett_app::scene::Device`]) and, on a phone-class width, FOLDS the whole
//! strip behind a `⚙` drawer (default closed) so the CANVAS gets the screen (canvas-first).
//!
//! This drives the exact `DemoState::draw_app` render path the eframe binary uses, at a
//! phone-width and a desktop-width viewport, and asserts the fold as DATA read back from
//! `state_json.shell`. RED-when-broken: if the strip stops folding on a phone the canvas
//! is shoved down (its height fraction collapses) and `controls_folded` goes false.
use facett_demo::DemoState;
use std::cell::RefCell;
use std::rc::Rc;
/// Run `draw_app` at a given viewport size (optionally with the phone drawer forced
/// open) and return the demo's `state_json.shell` object.
fn shell_at(width: f32, height: f32, drawer_open: bool) -> serde_json::Value {
let mut demo = DemoState::new();
demo.controls_open = drawer_open;
let cell = Rc::new(RefCell::new(serde_json::Value::Null));
let c = cell.clone();
let mut h = egui_kittest::Harness::builder()
.with_size(egui::vec2(width, height))
.build_ui_state(
move |ui, demo: &mut DemoState| {
demo.draw_app(ui);
*c.borrow_mut() = demo.state_json();
},
demo,
);
// The live sim requests a repaint every frame, so drive a few fixed frames
// (deferred fit / layout settles) instead of `run()`-to-quiescence.
h.run_steps(4);
let s = cell.borrow();
s["shell"].clone()
}
#[test]
fn phone_viewport_folds_the_controls_and_gives_the_canvas_the_screen() {
// ── PHONE (380 pt wide, drawer default CLOSED) ──────────────────────────────────
// The strip folds behind the `⚙` drawer: the shell reports phone-class + folded,
// and the canvas (deck) OWNS most of the viewport height — it is NOT below the fold.
let phone = shell_at(380.0, 780.0, false);
eprintln!("[responsive] phone(folded) shell = {phone}");
assert_eq!(phone["device"], "phone", "a 380 pt viewport is phone-class");
assert_eq!(phone["controls_folded"], true, "phone-class + drawer shut ⇒ the control strip folds away");
let phone_canvas = phone["canvas_height_frac"].as_f64().unwrap();
let phone_top = phone["canvas_top_frac"].as_f64().unwrap();
assert!(phone_canvas > 0.7, "the canvas owns most of the phone viewport height (got {phone_canvas:.3})");
assert!(phone_top < 0.2, "the canvas starts near the top — not pushed below the fold (top_frac {phone_top:.3})");
// ── PHONE, drawer OPEN ─────────────────────────────────────────────────────────
// Opening the `⚙` drawer un-folds the strip; the many control rows now eat into the
// viewport, so the canvas gets LESS of the screen than when folded. This is the
// whole point of the fold: closing it RECLAIMS the canvas.
let phone_open = shell_at(380.0, 780.0, true);
eprintln!("[responsive] phone(open) shell = {phone_open}");
assert_eq!(phone_open["controls_folded"], false, "an open drawer un-folds the strip");
let open_canvas = phone_open["canvas_height_frac"].as_f64().unwrap();
assert!(
phone_canvas > open_canvas,
"folding the strip RECLAIMS canvas: folded {phone_canvas:.3} > open {open_canvas:.3}"
);
// ── DESKTOP (1280 pt wide) ──────────────────────────────────────────────────────
// A wide viewport is desktop-class: the control strip is always shown (never folded).
let desk = shell_at(1280.0, 820.0, false);
eprintln!("[responsive] desktop shell = {desk}");
assert_eq!(desk["device"], "desktop", "a 1280 pt viewport is desktop-class");
assert_eq!(desk["controls_folded"], false, "desktop always shows the control strip");
#[cfg(feature = "testmatrix")]
facett_core::testmatrix::emit(
"facett-demo",
"responsive_fold_30",
phone["controls_folded"] == true && phone_canvas > 0.7 && desk["controls_folded"] == false,
&format!(
"phone_canvas={phone_canvas:.3} phone_open_canvas={open_canvas:.3} desktop_folded={}",
desk["controls_folded"]
),
);
}