requestty 0.6.3

An easy-to-use collection of interactive cli prompts
Documentation
use rand::prelude::*;
use rand_chacha::ChaCha12Rng;
use ui::{backend::TestBackend, layout::Layout};

use crate::question::{Question, QuestionKind};

use super::*;

const SEED: u64 = 9828123;
const SEP_RATIO: f32 = 0.3;
const DEFAULT_SEP_RATIO: f32 = 0.10;

fn choices_with_default(len: usize) -> impl Iterator<Item = Choice<(String, bool)>> {
    let mut rng = ChaCha12Rng::seed_from_u64(SEED);

    (0..len).map(move |i| {
        let rand: f32 = rng.gen();
        if rand < DEFAULT_SEP_RATIO {
            Choice::DefaultSeparator
        } else if rand < SEP_RATIO {
            Choice::Separator(format!("Separator {}", i))
        } else {
            Choice::Choice((format!("Choice {}", i), rand > 0.7))
        }
    })
}

fn choices(len: usize) -> impl Iterator<Item = Choice<String>> {
    choices_with_default(len).map(|choice| choice.map(|(c, _)| c))
}

fn unwrap_multi_select<'a>(question: impl Into<Question<'a>>) -> MultiSelect<'a> {
    match question.into().kind {
        QuestionKind::MultiSelect(c) => c,
        _ => unreachable!(),
    }
}

macro_rules! test_multi_select {
    ($mod_name:ident { multi_select = $multi_select:expr; height = $height:expr $(;)? }) => {
        test_multi_select!($mod_name {
            multi_select = $multi_select;
            height = $height;
            events = [
                KeyEvent::from(KeyCode::Char('a')),
                KeyCode::Char('a').into(),
                KeyCode::Down.into(),
                KeyCode::Char(' ').into(),
                KeyCode::Char('i').into(),
            ];
            answers = Answers::default()
        });
    };

    ($mod_name:ident { multi_select = $multi_select:expr; height = $height:expr; events = $events:expr $(;)? }) => {
        test_multi_select!($mod_name {
            multi_select = $multi_select;
            height = $height;
            events = $events;
            answers = Answers::default()
        });
    };

    ($mod_name:ident { multi_select = $multi_select:expr; height = $height:expr; answers = $answers:expr $(;)? }) => {
        test_multi_select!($mod_name {
            multi_select = $multi_select;
            height = $height;
            events = [
                KeyEvent::from(KeyCode::Char('a')),
                KeyCode::Char('a').into(),
                KeyCode::Down.into(),
                KeyCode::Char(' ').into(),
                KeyCode::Char('i').into(),
            ];
            answers = $answers
        });
    };

    ($mod_name:ident { multi_select = $multi_select:expr; height = $height:expr; events = $events:expr; answers = $answers:expr $(;)? }) => {
        mod $mod_name {
            use super::*;

            #[test]
            fn test_height() {
                let size = (50, 20).into();
                let base_layout = Layout::new(5, size);
                let answers = $answers;
                let mut multi_select = $multi_select.into_multi_select_prompt("message", &answers);

                let events = $events;

                for &key in events.iter() {
                    let mut layout = base_layout;

                    assert_eq!(multi_select.height(&mut layout), $height);
                    assert_eq!(
                        layout,
                        base_layout.with_offset(0, $height).with_line_offset(0)
                    );

                    assert!(multi_select.handle_key(key))
                }

                let mut layout = base_layout;

                assert_eq!(multi_select.height(&mut layout), $height);
                assert_eq!(
                    layout,
                    base_layout.with_offset(0, $height).with_line_offset(0)
                );
            }

            #[test]
            fn test_render() {
                let size = (50, 20).into();
                let base_layout = Layout::new(5, size);
                let answers = $answers;
                let mut multi_select = $multi_select.into_multi_select_prompt("message", &answers);

                let mut backend = TestBackend::new(size);

                let events = $events;

                for &key in events.iter() {
                    let mut layout = base_layout;
                    backend.reset_with_layout(layout);

                    assert!(multi_select.render(&mut layout, &mut backend).is_ok());
                    assert_eq!(
                        layout,
                        base_layout.with_offset(0, $height).with_line_offset(0)
                    );
                    ui::assert_backend_snapshot!(backend);

                    assert!(multi_select.handle_key(key))
                }

                let mut layout = base_layout;
                backend.reset_with_layout(layout);

                assert!(multi_select.render(&mut layout, &mut backend).is_ok());
                assert_eq!(
                    layout,
                    base_layout.with_offset(0, $height).with_line_offset(0)
                );
                ui::assert_backend_snapshot!(backend);
            }
        }
    };
}

test_multi_select!(basic {
    multi_select = unwrap_multi_select(
            MultiSelectBuilder::new("name".into()).choices(choices(10)),
        );
    height = 12;
});

test_multi_select!(pagination {
    multi_select = unwrap_multi_select(
            MultiSelectBuilder::new("name".into()).choices(choices(20)),
        );
    height = 17;
});

test_multi_select!(with_default {
    multi_select = unwrap_multi_select(
            MultiSelectBuilder::new("name".into()).choices_with_default(choices_with_default(10)),
        );
    height = 12;
    events = [
        KeyEvent::from(KeyCode::Char('i')),
        KeyCode::Down.into(),
        KeyCode::Char(' ').into(),
        KeyCode::Char('a').into(),
        KeyCode::Char('a').into(),
    ]
});