audit combobox
This commit is contained in:
@@ -2,8 +2,8 @@ use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
AppContext as _, Context, Entity, IntoElement, ParentElement as _, Render, SharedString,
|
||||
Styled as _, TestAppContext, VisualTestContext, Window, div,
|
||||
AppContext as _, Context, Entity, IntoElement, MouseButton, ParentElement as _, Render,
|
||||
SharedString, Styled as _, TestAppContext, VisualTestContext, Window, div, point, px,
|
||||
};
|
||||
use gpui_native_core::combobox::{Combobox, ComboboxOption, ComboboxState};
|
||||
use gpui_native_core::init;
|
||||
@@ -99,6 +99,31 @@ fn draw(visual: &mut VisualTestContext) {
|
||||
});
|
||||
}
|
||||
|
||||
fn click(visual: &mut VisualTestContext, position: gpui::Point<gpui::Pixels>) {
|
||||
visual.simulate_mouse_down(position, MouseButton::Left, gpui::Modifiers::default());
|
||||
visual.simulate_mouse_up(position, MouseButton::Left, gpui::Modifiers::default());
|
||||
}
|
||||
|
||||
/// Clicking into a valueless combobox field opens the options right away:
|
||||
/// no keystroke is needed to see the choices.
|
||||
#[test]
|
||||
fn clicking_the_empty_field_opens_the_popup() {
|
||||
let mut host = combo_host(false, false);
|
||||
|
||||
// Inside the field, which sits at the top of the padded host.
|
||||
click(&mut host.visual, point(px(100.), px(27.)));
|
||||
draw(&mut host.visual);
|
||||
|
||||
assert!(
|
||||
host.state.read_with(&host.cx, |state, _| state.is_open()),
|
||||
"clicking the empty field should show the options"
|
||||
);
|
||||
assert!(
|
||||
host.visual.debug_bounds("test-combo-panel").is_some(),
|
||||
"the options panel should be rendered"
|
||||
);
|
||||
}
|
||||
|
||||
fn focus(host: &mut ComboHost) {
|
||||
let state = host.state.clone();
|
||||
host.visual.update(|window, cx| {
|
||||
|
||||
@@ -38,8 +38,11 @@ impl Render for FieldHost {
|
||||
}
|
||||
}
|
||||
|
||||
/// A field page in a real window, with the url input focused.
|
||||
fn field_host() -> (
|
||||
/// A field page in a real window; the url input is focused unless
|
||||
/// `focus` is false, which leaves the page truly idle.
|
||||
fn field_host(
|
||||
focus: bool,
|
||||
) -> (
|
||||
TestAppContext,
|
||||
Entity<InputState>,
|
||||
Rc<RefCell<usize>>,
|
||||
@@ -64,8 +67,10 @@ fn field_host() -> (
|
||||
});
|
||||
let mut visual = VisualTestContext::from_window(window.into(), &cx);
|
||||
visual.update(|window, cx| {
|
||||
let focus = url.read(cx).focus_handle();
|
||||
window.focus(&focus, cx);
|
||||
if focus {
|
||||
let focus = url.read(cx).focus_handle();
|
||||
window.focus(&focus, cx);
|
||||
}
|
||||
window.draw(cx).clear(cx);
|
||||
});
|
||||
(cx, url, renders, visual)
|
||||
@@ -94,17 +99,27 @@ fn type_and_time(visual: &mut VisualTestContext, text: &str) -> std::time::Durat
|
||||
|
||||
#[test]
|
||||
fn an_idle_field_page_schedules_no_frames() {
|
||||
let (_cx, _url, _renders, mut visual) = field_host();
|
||||
let (_cx, _url, _renders, mut visual) = field_host(false);
|
||||
let frames = visual.update(|window, cx| window.simulate_next_frame(cx));
|
||||
assert_eq!(
|
||||
frames, 0,
|
||||
"an idle field page must not request animation frames"
|
||||
"an idle unfocused field page must not request animation frames"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_focused_field_page_schedules_frames_for_its_blink() {
|
||||
let (_cx, _url, _renders, mut visual) = field_host(true);
|
||||
let frames = visual.update(|window, cx| window.simulate_next_frame(cx));
|
||||
assert!(
|
||||
frames > 0,
|
||||
"the focused caret's blink needs frames to redraw at the blink cadence"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn field_typing_frame_cost() {
|
||||
let (_cx, _url, _renders, mut visual) = field_host();
|
||||
let (_cx, _url, _renders, mut visual) = field_host(true);
|
||||
let elapsed = type_and_time(&mut visual, "dsdsadsadadadada");
|
||||
assert!(
|
||||
elapsed.as_millis() < 1000,
|
||||
|
||||
+27
-24
@@ -269,42 +269,45 @@ fn read_only_refuses_edits_but_keeps_selection_and_copy() {
|
||||
assert_eq!(clipboard, "lo");
|
||||
|
||||
// The caret is suppressed entirely while read-only.
|
||||
let caret = cx.update(|cx| state.update(cx, |state, cx| state.caret_visible(true, cx)));
|
||||
let caret = cx.update(|cx| state.update(cx, |state, _| state.caret_visible(true)));
|
||||
assert!(!caret);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn caret_blinks_while_focused_and_holds_solid_while_typing() {
|
||||
fn the_caret_visibility_is_pure_time_math_against_the_epoch() {
|
||||
let (cx, state, mut visual) = input_window("", false);
|
||||
focus(&mut visual, &state);
|
||||
visual.update(|window, cx| {
|
||||
window.draw(cx).clear(cx);
|
||||
});
|
||||
visual.run_until_parked();
|
||||
let caret = || cx.update(|cx| state.update(cx, |state, _| state.caret_visible(true)));
|
||||
|
||||
let caret = || cx.update(|cx| state.update(cx, |state, cx| state.caret_visible(true, cx)));
|
||||
|
||||
// The caret shows on focus, hides after one interval, and reappears
|
||||
// after another.
|
||||
assert!(caret());
|
||||
cx.executor().advance_clock(Duration::from_millis(500));
|
||||
visual.run_until_parked();
|
||||
assert!(!caret());
|
||||
cx.executor().advance_clock(Duration::from_millis(500));
|
||||
visual.run_until_parked();
|
||||
// Fresh focus starts a new epoch: the caret shows solid — the elapsed
|
||||
// time since the epoch began is microseconds, far under one interval.
|
||||
assert!(caret());
|
||||
|
||||
// Typing holds the caret solid and restarts the cycle.
|
||||
// Typing restarts the epoch, so the caret stays solid through edits.
|
||||
visual.simulate_keystrokes("a");
|
||||
assert!(caret());
|
||||
cx.executor().advance_clock(Duration::from_millis(500));
|
||||
visual.run_until_parked();
|
||||
assert!(!caret());
|
||||
|
||||
// An unfocused field reports no caret.
|
||||
let caret_unfocused =
|
||||
cx.update(|cx| state.update(cx, |state, cx| state.caret_visible(false, cx)));
|
||||
assert!(!caret_unfocused);
|
||||
// The blink cadence itself: hidden in the second half of the cycle.
|
||||
let hidden = cx.update(|cx| {
|
||||
state.update(cx, |state, _| {
|
||||
state.caret_blink_visible_at(true, Duration::from_millis(700))
|
||||
})
|
||||
});
|
||||
assert!(!hidden);
|
||||
let visible = cx.update(|cx| {
|
||||
state.update(cx, |state, _| {
|
||||
state.caret_blink_visible_at(true, Duration::from_millis(300))
|
||||
})
|
||||
});
|
||||
assert!(visible);
|
||||
|
||||
// An unfocused field reports no caret, at any point in the cycle.
|
||||
let unfocused = cx.update(|cx| {
|
||||
state.update(cx, |state, _| {
|
||||
state.caret_blink_visible_at(false, Duration::from_millis(100))
|
||||
})
|
||||
});
|
||||
assert!(!unfocused);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user