From 78bf5917a5a5634ef16b42bc0637b2bce911728d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sun, 4 Oct 2026 17:59:36 -0400 Subject: [PATCH] =?UTF-8?q?Tests=20=C2=B7=20NumberInput's=20commit=20test?= =?UTF-8?q?=20selects=20with=20the=20platform's=20secondary=20key;=20Tabs?= =?UTF-8?q?=20tests=20move=20to=20navigation/tests/tabs.rs,=20under=20500?= =?UTF-8?q?=20lines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/forms/tests/mod.rs | 2 +- src/navigation/{tests.rs => tests/mod.rs} | 165 +--------------------- src/navigation/tests/tabs.rs | 165 ++++++++++++++++++++++ 3 files changed, 171 insertions(+), 161 deletions(-) rename src/navigation/{tests.rs => tests/mod.rs} (66%) create mode 100644 src/navigation/tests/tabs.rs diff --git a/src/forms/tests/mod.rs b/src/forms/tests/mod.rs index dea7b05..f7d9fcf 100644 --- a/src/forms/tests/mod.rs +++ b/src/forms/tests/mod.rs @@ -237,7 +237,7 @@ fn typing_commits_only_on_enter_and_steps(cx: &mut TestAppContext) { setup(cx); let (view, cx) = cx.add_window_view(|_, _| Committed { values: Vec::new() }); cx.update(|window, cx| window.focus_next(cx)); - cx.simulate_keystrokes("cmd-a"); + cx.simulate_keystrokes("secondary-a"); cx.simulate_input("12"); assert!( view.read_with(cx, |view, _| view.values.is_empty()), diff --git a/src/navigation/tests.rs b/src/navigation/tests/mod.rs similarity index 66% rename from src/navigation/tests.rs rename to src/navigation/tests/mod.rs index bb3a4d5..f192cf1 100644 --- a/src/navigation/tests.rs +++ b/src/navigation/tests/mod.rs @@ -1,12 +1,12 @@ +mod tabs; + use gpui::{ - AppContext as _, Context, IntoElement, KeyUpEvent, Keystroke, Modifiers, ParentElement, Render, - ScrollDelta, ScrollWheelEvent, SharedString, Styled, TestAppContext, TouchPhase, - VisualTestContext, Window, div, point, px, + AppContext as _, Context, IntoElement, KeyUpEvent, Keystroke, Render, SharedString, + TestAppContext, VisualTestContext, Window, px, }; use super::{ - BackForwardNavigation, Breadcrumb, Crumb, EditorTab, EditorTabs, GoToLine, LoadMore, - NavigationMenu, Steps, Tabs, Wizard, + BackForwardNavigation, Breadcrumb, Crumb, GoToLine, LoadMore, NavigationMenu, Steps, Wizard, }; use crate::{ forms::{Choice, TextInput}, @@ -28,38 +28,6 @@ fn press(key: &str, cx: &mut VisualTestContext) { }); } -struct Strip { - chosen: SharedString, -} - -impl Render for Strip { - fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { - let view = cx.entity(); - let tabs = [ - Choice::new("a", "A"), - Choice::new("b", "B").disabled(), - Choice::new("c", "C"), - ]; - Tabs::new("tabs", tabs, self.chosen.clone()).on_change(move |value, _, cx| { - view.update(cx, |view, cx| { - view.chosen = value.clone(); - cx.notify(); - }) - }) - } -} - -#[gpui::test] -fn tab_arrows_skip_disabled_tabs_and_wrap(cx: &mut TestAppContext) { - setup(cx); - let (view, cx) = cx.add_window_view(|_, _| Strip { chosen: "a".into() }); - cx.update(|window, cx| window.focus_next(cx)); - cx.simulate_keystrokes("right"); - assert_eq!(view.read_with(cx, |view, _| view.chosen.clone()), "c"); - cx.simulate_keystrokes("right"); - assert_eq!(view.read_with(cx, |view, _| view.chosen.clone()), "a"); -} - struct Path { picked: Option<(usize, SharedString)>, } @@ -174,91 +142,6 @@ fn a_picked_step_hands_focus_to_next(cx: &mut TestAppContext) { assert_eq!(view.read_with(cx, |view, _| view.step), 1); } -struct Docs { - chosen: SharedString, - clicked: Option, - closed: Option, -} - -impl Render for Docs { - fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { - let (picker, closer) = (cx.entity(), cx.entity()); - let tabs = (0..10).map(|ix| EditorTab::new(format!("t{ix}"), format!("document-{ix}.rs"))); - div().w(px(200.0)).child( - EditorTabs::new("docs", tabs) - .selected(self.chosen.clone()) - .on_select(move |id, _, cx| { - let id = id.clone(); - picker.update(cx, |view, _| view.clicked = Some(id)); - }) - .on_close(move |id, _, cx| { - let id = id.clone(); - closer.update(cx, |view, _| view.closed = Some(id)); - }), - ) - } -} - -/// The test platform never runs next-frame callbacks; a refresh stands in for the display link. -fn settle(cx: &mut VisualTestContext) { - cx.run_until_parked(); - cx.update(|window, _| window.refresh()); - cx.run_until_parked(); -} - -fn click_at(x: f32, cx: &mut VisualTestContext) { - let at = point(px(x), px(16.0)); - cx.simulate_mouse_move(at, None, Modifiers::none()); - cx.simulate_click(at, Modifiers::none()); -} - -#[gpui::test] -fn the_chosen_tab_scrolls_in_once_and_a_manual_scroll_stays(cx: &mut TestAppContext) { - setup(cx); - let (view, cx) = cx.add_window_view(|_, _| Docs { - chosen: "t9".into(), - clicked: None, - closed: None, - }); - settle(cx); - let strip_end = cx - .update(|window, _| window.viewport_size().width) - .min(px(200.0)) - - cx.update(|_, cx| { - crate::theme::ActiveTheme::theme(cx) - .control_height(crate::theme::ControlSize::Sm) - .to_pixels(px(16.0)) - }) - - px(8.0); - click_at(f32::from(strip_end) - 16.0, cx); - let closed = view.read_with(cx, |view, _| view.closed.clone()); - assert_eq!( - closed, - Some(SharedString::from("t9")), - "the chosen tab's close button sits at the strip's end" - ); - view.update(cx, |view, cx| { - view.chosen = "t8".into(); - cx.notify(); - }); - settle(cx); - cx.simulate_event(ScrollWheelEvent { - position: point(px(60.0), px(16.0)), - delta: ScrollDelta::Pixels(point(px(5000.0), px(0.0))), - modifiers: Modifiers::none(), - touch_phase: TouchPhase::Moved, - }); - view.update(cx, |_, cx| cx.notify()); - settle(cx); - click_at(6.0, cx); - let clicked = view.read_with(cx, |view, _| view.clicked.clone()); - assert_eq!( - clicked, - Some(SharedString::from("t0")), - "a manual scroll stays put" - ); -} - struct Site { entries: Vec<(&'static str, Vec)>, went: Option, @@ -470,41 +353,3 @@ fn steps_fill_a_column_their_block_measures_by_content(cx: &mut TestAppContext) "the steps span the card inside its padding" ); } - -struct Context_ { - chosen: SharedString, - asked: Option, -} - -impl Render for Context_ { - fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { - let view = cx.entity(); - let tabs = [Choice::new("a", "A"), Choice::new("c", "C")]; - Tabs::new("tabs", tabs, self.chosen.clone()).on_context(move |value, _, _, cx| { - view.update(cx, |view, cx| { - view.asked = Some(value.clone()); - cx.notify(); - }) - }) - } -} - -#[gpui::test] -fn a_right_click_names_its_tab_and_keeps_the_choice(cx: &mut TestAppContext) { - setup(cx); - let (view, cx) = cx.add_window_view(|_, _| Context_ { - chosen: "c".into(), - asked: None, - }); - settle(cx); - let at = point(px(12.0), px(16.0)); - cx.simulate_mouse_move(at, None, Modifiers::none()); - cx.simulate_mouse_down(at, gpui::MouseButton::Right, Modifiers::none()); - let (chosen, asked) = view.read_with(cx, |view, _| (view.chosen.clone(), view.asked.clone())); - assert_eq!( - asked.as_deref(), - Some("a"), - "the clicked tab, not the chosen one" - ); - assert_eq!(chosen, "c"); -} diff --git a/src/navigation/tests/tabs.rs b/src/navigation/tests/tabs.rs new file mode 100644 index 0000000..dff53c5 --- /dev/null +++ b/src/navigation/tests/tabs.rs @@ -0,0 +1,165 @@ +use gpui::{ + Context, IntoElement, Modifiers, ParentElement, Render, ScrollDelta, ScrollWheelEvent, + SharedString, Styled, TestAppContext, TouchPhase, VisualTestContext, Window, div, point, px, +}; + +use super::setup; +use crate::{ + forms::Choice, + navigation::{EditorTab, EditorTabs, Tabs}, +}; + +struct Strip { + chosen: SharedString, +} + +impl Render for Strip { + fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + let view = cx.entity(); + let tabs = [ + Choice::new("a", "A"), + Choice::new("b", "B").disabled(), + Choice::new("c", "C"), + ]; + Tabs::new("tabs", tabs, self.chosen.clone()).on_change(move |value, _, cx| { + view.update(cx, |view, cx| { + view.chosen = value.clone(); + cx.notify(); + }) + }) + } +} + +#[gpui::test] +fn tab_arrows_skip_disabled_tabs_and_wrap(cx: &mut TestAppContext) { + setup(cx); + let (view, cx) = cx.add_window_view(|_, _| Strip { chosen: "a".into() }); + cx.update(|window, cx| window.focus_next(cx)); + cx.simulate_keystrokes("right"); + assert_eq!(view.read_with(cx, |view, _| view.chosen.clone()), "c"); + cx.simulate_keystrokes("right"); + assert_eq!(view.read_with(cx, |view, _| view.chosen.clone()), "a"); +} + +struct Docs { + chosen: SharedString, + clicked: Option, + closed: Option, +} + +impl Render for Docs { + fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + let (picker, closer) = (cx.entity(), cx.entity()); + let tabs = (0..10).map(|ix| EditorTab::new(format!("t{ix}"), format!("document-{ix}.rs"))); + div().w(px(200.0)).child( + EditorTabs::new("docs", tabs) + .selected(self.chosen.clone()) + .on_select(move |id, _, cx| { + let id = id.clone(); + picker.update(cx, |view, _| view.clicked = Some(id)); + }) + .on_close(move |id, _, cx| { + let id = id.clone(); + closer.update(cx, |view, _| view.closed = Some(id)); + }), + ) + } +} + +/// The test platform never runs next-frame callbacks; a refresh stands in for the display link. +fn settle(cx: &mut VisualTestContext) { + cx.run_until_parked(); + cx.update(|window, _| window.refresh()); + cx.run_until_parked(); +} + +fn click_at(x: f32, cx: &mut VisualTestContext) { + let at = point(px(x), px(16.0)); + cx.simulate_mouse_move(at, None, Modifiers::none()); + cx.simulate_click(at, Modifiers::none()); +} + +#[gpui::test] +fn the_chosen_tab_scrolls_in_once_and_a_manual_scroll_stays(cx: &mut TestAppContext) { + setup(cx); + let (view, cx) = cx.add_window_view(|_, _| Docs { + chosen: "t9".into(), + clicked: None, + closed: None, + }); + settle(cx); + let strip_end = cx + .update(|window, _| window.viewport_size().width) + .min(px(200.0)) + - cx.update(|_, cx| { + crate::theme::ActiveTheme::theme(cx) + .control_height(crate::theme::ControlSize::Sm) + .to_pixels(px(16.0)) + }) + - px(8.0); + click_at(f32::from(strip_end) - 16.0, cx); + let closed = view.read_with(cx, |view, _| view.closed.clone()); + assert_eq!( + closed, + Some(SharedString::from("t9")), + "the chosen tab's close button sits at the strip's end" + ); + view.update(cx, |view, cx| { + view.chosen = "t8".into(); + cx.notify(); + }); + settle(cx); + cx.simulate_event(ScrollWheelEvent { + position: point(px(60.0), px(16.0)), + delta: ScrollDelta::Pixels(point(px(5000.0), px(0.0))), + modifiers: Modifiers::none(), + touch_phase: TouchPhase::Moved, + }); + view.update(cx, |_, cx| cx.notify()); + settle(cx); + click_at(6.0, cx); + let clicked = view.read_with(cx, |view, _| view.clicked.clone()); + assert_eq!( + clicked, + Some(SharedString::from("t0")), + "a manual scroll stays put" + ); +} + +struct Context_ { + chosen: SharedString, + asked: Option, +} + +impl Render for Context_ { + fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + let view = cx.entity(); + let tabs = [Choice::new("a", "A"), Choice::new("c", "C")]; + Tabs::new("tabs", tabs, self.chosen.clone()).on_context(move |value, _, _, cx| { + view.update(cx, |view, cx| { + view.asked = Some(value.clone()); + cx.notify(); + }) + }) + } +} + +#[gpui::test] +fn a_right_click_names_its_tab_and_keeps_the_choice(cx: &mut TestAppContext) { + setup(cx); + let (view, cx) = cx.add_window_view(|_, _| Context_ { + chosen: "c".into(), + asked: None, + }); + settle(cx); + let at = point(px(12.0), px(16.0)); + cx.simulate_mouse_move(at, None, Modifiers::none()); + cx.simulate_mouse_down(at, gpui::MouseButton::Right, Modifiers::none()); + let (chosen, asked) = view.read_with(cx, |view, _| (view.chosen.clone(), view.asked.clone())); + assert_eq!( + asked.as_deref(), + Some("a"), + "the clicked tab, not the chosen one" + ); + assert_eq!(chosen, "c"); +}