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

nordisk/facett

facett-gantt/tests/robot.rs

//! **Robot render proof for facett-gantt.** The `Gantt::ui()` render path — the
//! time-axis, the dependency arrows, the task-bar geometry, the milestone
//! diamond, the today marker and the click-select interaction — is never touched
//! by the unit tests (they only exercise `critical_path` / `effective_range` /
//! `state_json`). This drives a *real* egui frame through `egui_kittest` so an
//! index/geometry panic in the painter would be caught here, and asserts the
//! heading node reaches the AccessKit tree so the widget is addressable.

use chrono::NaiveDate;
use egui_kittest::Harness;
use egui_kittest::kittest::Queryable;

use facett_gantt::{Gantt, Task};

fn d(y: i32, m: u32, day: u32) -> NaiveDate {
    NaiveDate::from_ymd_opt(y, m, day).unwrap()
}

/// A board that exercises every render branch: a plain bar with progress, a
/// dependency arrow, a milestone diamond, and a `today` marker inside the range.
/// CONSUMED from the crate's own `Gantt::local()` — one fixture, not one per test
/// file (L5); `tests/matrix.rs` probes the SAME board.
fn board() -> Gantt {
    Gantt::local()
}

#[test]
fn gantt_renders_and_is_addressable() {
    let mut g = board();
    let mut harness = Harness::new_ui(move |ui| {
        g.ui(ui);
    });
    // A real frame: the axis ticks, dependency arrows, bars, progress fills, the
    // milestone diamond and the today line all paint. A panic here fails the test.
    harness.run();

    // The heading text rode into the AccessKit tree — the widget is queryable by a
    // robot driver / screen reader.
    let _heading = harness.get_by_label("Bygglov");
}

#[test]
fn gantt_empty_and_milestone_only_render_without_panic() {
    // Empty board: effective_range falls back to the 2026 window; ui() must not
    // divide-by-zero or index out of bounds.
    let mut empty = Gantt::new("Empty", vec![]);
    let mut h1 = Harness::new_ui(move |ui| {
        empty.ui(ui);
    });
    h1.run();

    // Milestone-only board: the diamond path (no bar rect / no interact) renders.
    let mut ms = Task::new("go", "Go-live", d(2026, 6, 1), d(2026, 6, 1));
    ms.milestone = true;
    let mut only = Gantt::new("Milestones", vec![ms]);
    let mut h2 = Harness::new_ui(move |ui| {
        only.ui(ui);
    });
    h2.run();
    let _ = h2.get_by_label("Milestones");
}

#[test]
fn gantt_state_survives_a_render() {
    // Rendering must not mutate the domain state (no selection without a click).
    let g = board();
    let before = g.state_json();
    let mut harness = Harness::new_ui({
        let mut g2 = board();
        move |ui| {
            g2.ui(ui);
        }
    });
    harness.run();
    assert_eq!(g.state_json(), before, "ui() render is side-effect free without input");
    assert_eq!(before["task_count"], 4);
    assert!(before["critical_path"].as_array().unwrap().contains(&serde_json::json!("beslut")));
}