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

nordisk/facett

facett-kanban/tests/robot.rs

//! **Robot render proof for facett-kanban.** The `Board::ui()` drag-and-drop
//! render path — the swimlane headers, the per-column WIP-breach highlight, the
//! drop zones, the draggable card frames and the card-selection interaction — is
//! never executed by the unit tests (which only cover `state_json` / `count_in` /
//! `take_moves`). This drives a real `egui_kittest` frame so the painter /
//! dnd-source / dnd-drop-zone code runs, asserts the column titles, the WIP-breach
//! banner and the card nodes are addressable, and confirms `state_json` still
//! reflects the board after a render.

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

use facett_kanban::{Board, Card, Column};

/// A board with a WIP-breaching column (`active` has 3 cards over a WIP of 2) and
/// two swimlanes, so both the breach banner and the lane headers paint. CONSUMED
/// from the crate's own `Board::local()` — one fixture, not one per test file
/// (L5); `tests/matrix.rs` probes the SAME board.
fn board() -> Board {
    Board::local()
}

#[test]
fn board_renders_columns_cards_and_wip_breach() {
    let mut b = board();
    let mut harness = Harness::new_ui(move |ui| {
        b.ui(ui);
    });
    // Real frame: swimlane headers, column frames, drop zones, draggable cards.
    harness.run();

    // Column titles reached the AccessKit tree (each lane repeats them, so query
    // the "all" form and assert at least one per column).
    assert!(harness.get_all_by_label("Triage").count() >= 1);
    assert!(harness.get_all_by_label("In Progress").count() >= 1);

    // The WIP-breach banner painted (board-wide `active` count 3 > WIP 2). It only
    // appears when a lane's column is over its limit.
    assert!(
        harness.get_all_by_label("⚠ over WIP limit").count() >= 1,
        "the over-WIP banner must render when a column breaches its limit"
    );

    // A card title rode in as an addressable node.
    let _card = harness.get_by_label("Login loop");
}

#[test]
fn board_without_swimlanes_renders_single_lane() {
    let mut b = Board::new("Board", vec![Column::new("todo", "To do"), Column::new("done", "Done")]);
    b.set_cards(vec![Card::new("T-1", "First task", "todo")]);
    let mut harness = Harness::new_ui(move |ui| {
        b.ui(ui);
    });
    harness.run();
    let _title = harness.get_by_label("First task");
    let _col = harness.get_by_label("To do");
}

#[test]
fn state_json_still_reflects_board_after_render() {
    // Rendering without a real drag must not fabricate moves or change counts.
    let before = board().state_json();
    let mut b = board();
    let mut harness = Harness::new_ui(move |ui| {
        b.ui(ui);
    });
    harness.run();

    assert_eq!(before["card_count"], 4);
    let cols = before["columns"].as_array().unwrap();
    let active = cols.iter().find(|c| c["id"] == "active").unwrap();
    assert_eq!(active["count"], 3);
    assert_eq!(active["wip_breach"], true);
    assert_eq!(before["pending_moves"].as_array().unwrap().len(), 0, "no drag → no moves");
    // A freshly rendered clone reports the same observable state.
    assert_eq!(board().state_json(), before);
}