facett-sdp/tests/render.rs
//! **CPU render-proof for facett-sdp.** The inline tests cover deps parsing,
//! validation, and `state_json`; the `SdpEditor::ui()` render path (the flow-list
//! editor with its name/deps/SQL boxes + Add/Remove buttons, and the Graph DAG
//! preview) is never executed there. This drives real `egui_kittest` frames in
//! **both** modes so the painter runs and the controls ride into the AccessKit
//! tree — asserting the surface renders non-blank AND that `state_json` reflects a
//! programmatic edit.
use egui_kittest::kittest::Queryable;
use egui_kittest::Harness;
use facett_sdp::{Flow, Mode, SdpEditor};
fn sample() -> SdpEditor {
SdpEditor::new("etl").with_flows([
Flow::new("raw", Vec::<String>::new(), "SELECT * FROM read_files('/in')"),
Flow::new("clean", ["raw"], "SELECT * FROM raw WHERE ok"),
])
}
#[test]
fn edit_mode_paints_toggles_flows_and_add_button() {
let ed = sample();
// state_json exposes the pipeline as data before we ever render.
let st = ed.state_json();
assert_eq!(st["mode"], "Edit");
assert_eq!(st["valid"], true);
assert_eq!(st["flows"][0]["name"], "raw");
assert_eq!(st["flows"][1]["deps"][0], "raw");
let mut ed_ui = ed;
let mut harness = Harness::new_ui(move |ui| {
ed_ui.ui(ui);
});
harness.run();
// Mode toggles, per-flow headings, and the add/remove controls reached the tree
// → the surface rendered non-blank.
assert!(harness.query_by_label("✎ Edit").is_some(), "the Edit toggle painted");
assert!(harness.query_by_label("◈ Graph").is_some(), "the Graph toggle painted");
assert!(harness.query_by_label("raw").is_some(), "the 'raw' flow heading painted");
assert!(harness.query_by_label("clean").is_some(), "the 'clean' flow heading painted");
assert!(harness.query_by_label("+ Add flow").is_some(), "the Add-flow button painted");
// one Remove button per flow (two here) → prove the per-flow rows rendered.
assert_eq!(harness.query_all_by_label("✕ Remove").count(), 2, "a Remove button per flow painted");
}
#[test]
fn graph_mode_renders_the_dag_without_panicking() {
let mut ed = sample();
ed.set_mode(Mode::Graph);
assert_eq!(ed.state_json()["mode"], "Graph");
let mut harness = Harness::new_ui(move |ui| {
ed.ui(ui); // paints nodes + edges via the painter — must not panic
});
harness.run();
}
#[test]
fn state_json_reflects_a_programmatic_sql_edit() {
let mut ed = sample();
ed.set_flow_query(1, "SELECT id, upper(name) AS n FROM raw");
let st = ed.state_json();
assert_eq!(st["flows"][1]["query"], "SELECT id, upper(name) AS n FROM raw");
assert_eq!(st["valid"], true);
// Break a dep → invalid, with an error naming the unknown upstream.
ed.set_flow_deps(1, ["ghost"]);
let bad = ed.state_json();
assert_eq!(bad["valid"], false);
assert!(
bad["errors"].as_array().unwrap().iter().any(|e| e.as_str().unwrap().contains("ghost")),
"errors should name the unknown dep: {:?}",
bad["errors"]
);
}
#[test]
fn empty_editor_renders_cleanly() {
let ed = SdpEditor::new("empty");
assert_eq!(ed.state_json()["valid"], true);
assert!(ed.state_json()["flows"].as_array().unwrap().is_empty());
let mut ed_ui = ed;
let mut harness = Harness::new_ui(move |ui| {
ed_ui.ui(ui);
});
harness.run();
assert!(harness.query_by_label("✎ Edit").is_some());
}