schemaui 0.11.0

A Rust library for generating TUI and Web UIs from JSON Schemas for configuration management.
Documentation
use crate::tui::app::{
    input::{InputRouter, KeyAction},
    keymap::{self, KeymapContext},
};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};

fn key(code: KeyCode, modifiers: KeyModifiers) -> KeyEvent {
    KeyEvent::new(code, modifiers)
}

fn router() -> InputRouter {
    InputRouter::new(keymap::default_store())
}

#[test]
fn ctrl_jl_cycle_root_sections() {
    let router = router();
    let prev = router.classify(&key(KeyCode::Char('j'), KeyModifiers::CONTROL));
    let next = router.classify(&key(KeyCode::Char('l'), KeyModifiers::CONTROL));
    assert!(matches!(prev, KeyAction::RootStep(-1)));
    assert!(matches!(next, KeyAction::RootStep(1)));
}

#[test]
fn ctrl_tab_maps_to_section_steps() {
    let router = router();
    let next = router.classify(&key(KeyCode::Tab, KeyModifiers::CONTROL));
    let prev = router.classify(&key(
        KeyCode::Tab,
        KeyModifiers::CONTROL | KeyModifiers::SHIFT,
    ));
    assert!(matches!(next, KeyAction::SectionStep(1)));
    assert!(matches!(prev, KeyAction::SectionStep(-1)));
}

#[test]
fn shift_tab_triggers_previous_field() {
    let router = router();
    let action = router.classify(&key(KeyCode::BackTab, KeyModifiers::SHIFT));
    assert!(matches!(action, KeyAction::FieldStep(-1)));
}

#[test]
fn help_only_text_bindings_do_not_override_raw_input() {
    let router = router();

    let left = router.classify(&key(KeyCode::Left, KeyModifiers::NONE));
    let backspace = router.classify(&key(KeyCode::Backspace, KeyModifiers::NONE));
    let ctrl_w = router.classify(&key(KeyCode::Char('w'), KeyModifiers::CONTROL));

    assert!(matches!(left, KeyAction::Input(event) if event.code == KeyCode::Left));
    assert!(matches!(backspace, KeyAction::Input(event) if event.code == KeyCode::Backspace));
    assert!(matches!(ctrl_w, KeyAction::Input(event)
        if event.code == KeyCode::Char('w') && event.modifiers == KeyModifiers::CONTROL));
}

#[test]
fn default_keymap_exposes_text_and_numeric_help_contexts() {
    let store = keymap::default_store();

    let text_help = store
        .help_text(KeymapContext::TextInput)
        .expect("text input help");
    assert!(text_help.contains("Left -> Move cursor left"));
    assert!(text_help.contains("Right -> Move cursor right"));
    assert!(text_help.contains("Ctrl+W -> Delete previous word"));
    assert!(text_help.contains("Ctrl+Z -> Undo text edit"));
    assert!(text_help.contains("Ctrl+Y -> Redo text edit"));

    let numeric_help = store
        .help_text(KeymapContext::NumericInput)
        .expect("numeric input help");
    assert!(numeric_help.contains("Left -> Step value down"));
    assert!(numeric_help.contains("Right -> Step value up"));
    assert!(numeric_help.contains("Shift+Left -> Fast step value down"));
    assert!(numeric_help.contains("Shift+Right -> Fast step value up"));
    assert!(numeric_help.contains("Ctrl+Z -> Undo numeric edit"));
    assert!(numeric_help.contains("Ctrl+Y -> Redo numeric edit"));
}

#[test]
fn help_context_bindings_are_only_classified_when_requested() {
    let store = keymap::default_store();
    let esc = key(KeyCode::Esc, KeyModifiers::NONE);

    assert!(!matches!(store.classify(&esc), Some(KeyAction::HelpClose)));
    assert!(matches!(
        store.classify_for_contexts(&esc, &[KeymapContext::Help]),
        Some(KeyAction::HelpClose)
    ));
}