Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-brokkr/tests/render.rs

//! **CPU render-proof for facett-brokkr.** The inline tests cover TOML
//! round-trip, validation and `state_json`; the `PipelineBuilder::ui()` render
//! path — the TEXT editor, the CLICK builder's stage/step/trigger rows with their
//! Add/Remove buttons, and the DAG painter — is never executed there. This drives
//! real `egui_kittest` frames in **all three** modes so the painter runs and the
//! controls ride into the AccessKit tree — asserting the surface renders non-blank
//! (LAW #4) AND that `state_json` reflects a programmatic edit.

use egui_kittest::kittest::Queryable;
use egui_kittest::Harness;

use facett_brokkr::{Mode, PipelineBuilder, RunKind};

#[test]
fn build_mode_paints_toggles_stages_steps_and_add_buttons() {
    let b = PipelineBuilder::demo("ci");
    // state_json exposes the pipeline as data before we ever render.
    let st = b.state_json();
    assert_eq!(st["mode"], "Build");
    assert_eq!(st["valid"], true);
    assert_eq!(st["stage_count"], 3);
    assert_eq!(st["step_count"], 4);

    let mut b_ui = b;
    let mut harness = Harness::builder().with_size(egui::vec2(720.0, 640.0)).build_ui(move |ui| {
        b_ui.ui(ui);
    });
    harness.run();
    // Mode toggles + the structural add controls reached the tree → non-blank.
    assert!(harness.query_by_label("✎ Text").is_some(), "the Text toggle painted");
    assert!(harness.query_by_label("⚙ Build").is_some(), "the Build toggle painted");
    assert!(harness.query_by_label("◈ Graph").is_some(), "the Graph toggle painted");
    assert!(harness.query_by_label("+ Add stage").is_some(), "the Add-stage button painted");
    assert!(harness.query_by_label("+ Add trigger").is_some(), "the Add-trigger button painted");
    // One "+ Add step" per stage (three here) → the per-stage rows rendered.
    assert_eq!(harness.query_all_by_label("+ Add step").count(), 3, "an Add-step button per stage painted");
}

#[test]
fn text_mode_paints_the_toml_editor() {
    let mut b = PipelineBuilder::demo("ci");
    b.set_mode(Mode::Text);
    assert_eq!(b.state_json()["mode"], "Text");
    let mut harness = Harness::builder().with_size(egui::vec2(640.0, 520.0)).build_ui(move |ui| {
        b.ui(ui);
    });
    harness.run();
    assert!(harness.query_by_label("brokkr.toml").is_some(), "the TEXT editor heading painted");
}

#[test]
fn graph_mode_renders_the_dag_without_panicking() {
    let mut b = PipelineBuilder::demo("ci");
    b.set_mode(Mode::Graph);
    assert_eq!(b.state_json()["mode"], "Graph");
    let mut harness = Harness::builder().with_size(egui::vec2(760.0, 520.0)).build_ui(move |ui| {
        b.ui(ui); // paints nodes + dependency edges via the painter — must not panic
    });
    harness.run();
}

#[test]
fn state_json_reflects_a_programmatic_click_edit() {
    let mut b = PipelineBuilder::new("ci");
    b.set_name("my-ci");
    let s0 = b.add_stage("build");
    b.add_step(s0, "compile", RunKind::Variant("server".into()));
    let s1 = b.add_stage("test");
    b.add_step(s1, "unit", RunKind::Container(facett_brokkr::ContainerRun {
        name: "rust:1.85".into(),
        cmd: vec!["cargo".into(), "test".into()],
    }));
    b.set_step_needs(s1, 0, ["compile"]);

    let st = b.state_json();
    assert_eq!(st["name"], "my-ci");
    assert_eq!(st["stage_count"], 2);
    assert_eq!(st["step_count"], 2);
    assert_eq!(st["valid"], true);
    assert!(st["toml"].as_str().unwrap().contains("needs = [\"compile\"]"));

    // Break the dep → invalid, with an error naming the unknown upstream.
    b.set_step_needs(s1, 0, ["ghost"]);
    let bad = b.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 need: {:?}",
        bad["errors"]
    );
}

#[test]
fn empty_builder_renders_cleanly() {
    let b = PipelineBuilder::new("empty");
    // An empty pipeline is stepless (invalid) but must still render its toggles.
    let mut b_ui = b;
    let mut harness = Harness::new_ui(move |ui| {
        b_ui.ui(ui);
    });
    harness.run();
    assert!(harness.query_by_label("⚙ Build").is_some());
    assert!(harness.query_by_label("+ Add stage").is_some());
}