bezel-ui 0.2.13

SwiftUI-flavored components for gpui — popovers, menus, buttons, toggles, and loaders
Documentation
use std::{cell::RefCell, rc::Rc};

use gpui::{Entity, SharedString, TestAppContext, VisualTestContext, px, size};
use ui::{
    input,
    multi_select::{self, Check, Choice, MultiSelect, MultiSelectEvent},
};

type Events = Rc<RefCell<Vec<MultiSelectEvent>>>;

fn choice(name: &str, check: Check) -> Choice {
    Choice {
        name: name.into(),
        check,
        count: None,
    }
}

fn normalize(text: &str) -> Option<SharedString> {
    let name = text
        .split_whitespace()
        .collect::<Vec<_>>()
        .join("-")
        .to_lowercase();
    (!name.is_empty()).then(|| name.into())
}

fn picker(
    cx: &mut TestAppContext,
    choices: Vec<Choice>,
) -> (Entity<MultiSelect>, Events, VisualTestContext) {
    cx.update(|cx| {
        theme::Theme::install(theme::Appearance::Dark, cx);
        input::init(cx);
        multi_select::init(cx);
    });
    let window = cx.add_window(|_, cx| MultiSelect::new(choices, cx).with_create(normalize));
    let picker = window.root(cx).unwrap();
    let events = Events::default();
    cx.update(|cx| {
        let events = events.clone();
        cx.subscribe(&picker, move |_, event: &MultiSelectEvent, _| {
            events.borrow_mut().push(event.clone())
        })
        .detach();
    });
    let mut visual = VisualTestContext::from_window(window.into(), cx);
    visual.simulate_resize(size(px(400.0), px(400.0)));
    visual.update(|window, cx| picker.update(cx, |picker, cx| picker.reset(window, cx)));
    visual.run_until_parked();
    (picker, events, visual)
}

#[gpui::test]
fn enter_on_a_new_name_creates_it_normalised(cx: &mut TestAppContext) {
    let (_, events, mut cx) = picker(cx, vec![choice("design", Check::Off)]);
    cx.simulate_input("Q3 Plan");
    cx.simulate_keystrokes("enter");
    assert_eq!(
        *events.borrow(),
        [MultiSelectEvent::Created("q3-plan".into())]
    );
}

#[gpui::test]
fn a_name_already_offered_is_toggled_not_created(cx: &mut TestAppContext) {
    let (_, events, mut cx) = picker(cx, vec![choice("design", Check::Off)]);
    cx.simulate_input("Design");
    cx.simulate_keystrokes("enter");
    assert_eq!(
        *events.borrow(),
        [MultiSelectEvent::Toggled {
            name: "design".into(),
            on: true
        }]
    );
}

#[gpui::test]
fn a_mixed_choice_turns_on(cx: &mut TestAppContext) {
    let (_, events, mut cx) = picker(cx, vec![choice("design", Check::Mixed)]);
    cx.simulate_keystrokes("enter");
    assert_eq!(
        *events.borrow(),
        [MultiSelectEvent::Toggled {
            name: "design".into(),
            on: true
        }]
    );
}

#[gpui::test]
fn backspace_in_an_empty_query_turns_off_the_last_choice_on(cx: &mut TestAppContext) {
    let (_, events, mut cx) = picker(
        cx,
        vec![
            choice("design", Check::On),
            choice("q3", Check::On),
            choice("urgent", Check::Mixed),
        ],
    );
    cx.simulate_input("x");
    cx.simulate_keystrokes("backspace");
    assert!(
        events.borrow().is_empty(),
        "the first backspace edits the query"
    );
    cx.simulate_keystrokes("backspace");
    assert_eq!(
        *events.borrow(),
        [MultiSelectEvent::Toggled {
            name: "q3".into(),
            on: false
        }]
    );
}

#[gpui::test]
fn escape_asks_to_be_dismissed(cx: &mut TestAppContext) {
    let (_, events, mut cx) = picker(cx, vec![choice("design", Check::Off)]);
    cx.simulate_keystrokes("escape");
    assert_eq!(*events.borrow(), [MultiSelectEvent::Dismissed]);
}