arf-console 0.7.0-alpha.1

A cross-platform R console written in Rust
use super::*;
use crate::pager::help::browser::tests::topic;
use crate::pager::help::search::SearchWorker;
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
use std::sync::Arc;

#[test]
fn candidate_movement_keeps_pending_search_and_only_moves_ready_candidates() {
    let topics: Arc<[arf_harp::help::HelpTopic]> = vec![
        topic("base", "mean", &[], "Mean"),
        topic("stats", "median", &[], "Median"),
    ]
    .into();
    let mut browser = HelpBrowser::new(Arc::clone(&topics), Vec::new(), "m");
    browser.query_generation = 3;
    browser.pending_generation = Some(12);
    browser.remember_pending_open();
    browser.move_selection(1, 5);
    assert_eq!(browser.pending_generation, Some(12));
    assert_eq!(browser.pending_open, None);
    assert_eq!(browser.selected, 0);
    browser.accept_search_result(crate::pager::help::search::SearchResult {
        generation: 12,
        matches: vec![(0, 8), (1, 4)],
    });
    browser.move_selection(1, 5);
    assert_eq!(browser.selected, 1);
}

#[test]
fn ready_enter_stops_event_drain_before_later_browser_keys() {
    use std::collections::VecDeque;
    let topics: Arc<[arf_harp::help::HelpTopic]> = vec![topic("base", "mean", &[], "Mean")].into();
    let mut browser = HelpBrowser::new(Arc::clone(&topics), Vec::new(), "");
    let mut worker = SearchWorker::spawn(topics).unwrap();
    let mut events = VecDeque::from([
        Event::Key(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)),
        Event::Key(KeyEvent::new(KeyCode::Char('q'), KeyModifiers::NONE)),
    ]);
    let drained = drain_help_events(
        || Ok(events.pop_front()),
        |event| browser.handle_event(event, false, &worker),
    )
    .unwrap();
    assert_eq!(drained.action, BrowserAction::Open(0));
    assert_eq!(drained.events_read, 1);
    assert_eq!(events.len(), 1, "the pager must receive the unread q key");
    worker.shutdown_and_join().unwrap();
}

#[test]
fn thirty_third_exit_cancels_pending_open_before_results_apply() {
    use std::collections::VecDeque;
    let topics: Arc<[arf_harp::help::HelpTopic]> = vec![topic("base", "mean", &[], "Mean")].into();
    let mut browser = HelpBrowser::new(Arc::clone(&topics), Vec::new(), "mean");
    browser.query_generation = 5;
    browser.pending_generation = Some(11);
    let mut worker = SearchWorker::spawn(topics).unwrap();
    let mut events = VecDeque::new();
    events.push_back(Event::Key(KeyEvent::new(
        KeyCode::Enter,
        KeyModifiers::NONE,
    )));
    events.extend((0..31).map(|_| Event::Resize(80, 24)));
    events.push_back(Event::Key(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE)));
    let first = drain_help_events(
        || Ok(events.pop_front()),
        |event| browser.handle_event(event, false, &worker),
    )
    .unwrap();
    assert_eq!(first.action, BrowserAction::Continue);
    assert_eq!(first.events_read, 32);
    assert_eq!(browser.pending_open, Some(5));
    let mut applied = false;
    let backlog = poll_backlog_before_results(
        first.action,
        || Ok(!events.is_empty()),
        || {
            applied = true;
            Ok(())
        },
    )
    .unwrap();
    assert!(backlog);
    assert!(!applied);
    let second = drain_help_events(
        || Ok(events.pop_front()),
        |event| browser.handle_event(event, false, &worker),
    )
    .unwrap();
    assert_eq!(second.action, BrowserAction::Exit);
    assert_eq!(browser.pending_open, None);
    worker.shutdown_and_join().unwrap();
}

#[test]
fn input_arriving_at_end_of_redraw_prevents_result_application() {
    use std::collections::VecDeque;
    let mut events = VecDeque::new();
    let mut applied = false;
    let backlog = poll_backlog_before_results(
        BrowserAction::Continue,
        || {
            events.push_back(Event::Key(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE)));
            Ok(!events.is_empty())
        },
        || {
            applied = true;
            Ok(())
        },
    )
    .unwrap();
    assert!(backlog);
    assert!(!applied);
    assert_eq!(events.len(), 1);
}

#[test]
fn batched_edits_submit_only_final_query_for_deferred_enter() {
    use std::collections::VecDeque;
    let topics: Arc<[arf_harp::help::HelpTopic]> = vec![topic("base", "mean", &[], "Mean")].into();
    let mut browser = HelpBrowser::new(Arc::clone(&topics), Vec::new(), "");
    let mut worker = SearchWorker::spawn(topics).unwrap();
    let mut events = VecDeque::from([
        Event::Key(KeyEvent::new(KeyCode::Char('m'), KeyModifiers::NONE)),
        Event::Key(KeyEvent::new(KeyCode::Char('e'), KeyModifiers::NONE)),
        Event::Key(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)),
    ]);
    let drained = drain_help_events(
        || Ok(events.pop_front()),
        |event| browser.handle_event(event, false, &worker),
    )
    .unwrap();
    assert_eq!(drained.action, BrowserAction::Continue);
    assert_eq!(browser.query, "me");
    assert_eq!(browser.pending_open, Some(browser.query_generation));
    browser.dispatch_search(&worker).unwrap();
    let generation = browser.pending_generation.unwrap();
    assert_eq!(generation, 1, "one batch submits one final query");
    let result = worker.wait_for_result(generation).unwrap();
    assert_eq!(result.matches.first().map(|(index, _)| *index), Some(0));
    assert_eq!(browser.accept_search_result(result), Some(0));
    worker.shutdown_and_join().unwrap();
}