@@ -64,6 +64,17 @@ fn header() -> OptionSelectorHeader {
6464
6565type CapturedEvents = Rc < RefCell < Vec < TuiOptionSelectorEvent > > > ;
6666
67+ /// The captured events with `LayoutChanged` filtered out, for tests that
68+ /// assert on the primary confirmation flow.
69+ fn primary_events ( events : & CapturedEvents ) -> Vec < TuiOptionSelectorEvent > {
70+ events
71+ . borrow ( )
72+ . iter ( )
73+ . filter ( |event| * * event != TuiOptionSelectorEvent :: LayoutChanged )
74+ . cloned ( )
75+ . collect ( )
76+ }
77+
6778/// Adds a selector in a fresh TUI window and captures its emitted events.
6879fn add_selector ( app : & mut App ) -> ( ViewHandle < TuiOptionSelector > , CapturedEvents ) {
6980 app. add_singleton_model ( |_| Appearance :: mock ( ) ) ;
@@ -285,7 +296,7 @@ fn digits_are_viewport_relative_in_scrolled_lists() {
285296 TuiOptionSelectorAction :: SelectNumberedOption ( 1 ) ,
286297 ) ;
287298 assert_eq ! (
288- events . borrow ( ) . as_slice ( ) ,
299+ primary_events ( & events ) ,
289300 [ TuiOptionSelectorEvent :: Confirmed {
290301 id: "row-2" . to_string( )
291302 } ] ,
@@ -438,7 +449,7 @@ fn custom_text_editor_trims_validates_and_submits() {
438449 TuiOptionSelectorAction :: InsertChar ( ' ' ) ,
439450 ) ;
440451 confirm ( & mut app, & selector) ;
441- assert ! ( events . borrow ( ) . is_empty( ) ) ;
452+ assert ! ( primary_events ( & events ) . is_empty( ) ) ;
442453 assert ! ( render_lines( & app, & selector, 60 )
443454 . iter( )
444455 . any( |line| line. contains( "Enter a value to continue." ) ) ) ;
@@ -450,7 +461,7 @@ fn custom_text_editor_trims_validates_and_submits() {
450461 act ( & mut app, & selector, TuiOptionSelectorAction :: Backspace ) ;
451462 confirm ( & mut app, & selector) ;
452463 assert_eq ! (
453- events . borrow ( ) . as_slice ( ) ,
464+ primary_events ( & events ) ,
454465 [ TuiOptionSelectorEvent :: CustomTextSubmitted {
455466 value: "my-host" . to_string( )
456467 } ] ,
@@ -503,6 +514,69 @@ fn create_new_auth_secret_footer_is_ignored() {
503514 } ) ;
504515}
505516
517+ #[ test]
518+ fn layout_changed_is_emitted_only_when_overflow_markers_toggle ( ) {
519+ App :: test ( ( ) , |mut app| async move {
520+ let ( selector, events) = add_selector ( & mut app) ;
521+ set_page (
522+ & mut app,
523+ & selector,
524+ snapshot ( & [ "a" , "b" , "c" , "d" , "e" , "f" ] , Some ( "a" ) ) ,
525+ ) ;
526+ events. borrow_mut ( ) . clear ( ) ;
527+
528+ // Moves within the viewport do not scroll, so nothing is emitted.
529+ for _ in 0 ..3 {
530+ act ( & mut app, & selector, TuiOptionSelectorAction :: MoveDown ) ;
531+ }
532+ assert ! ( !events
533+ . borrow( )
534+ . contains( & TuiOptionSelectorEvent :: LayoutChanged ) ) ;
535+
536+ // Scrolling past the viewport reveals the `↑` marker: one event.
537+ act ( & mut app, & selector, TuiOptionSelectorAction :: MoveDown ) ;
538+ assert_eq ! (
539+ events
540+ . borrow( )
541+ . iter( )
542+ . filter( |event| * * event == TuiOptionSelectorEvent :: LayoutChanged )
543+ . count( ) ,
544+ 1 ,
545+ ) ;
546+ } ) ;
547+ }
548+
549+ #[ test]
550+ fn layout_changed_is_emitted_when_the_custom_text_error_row_toggles ( ) {
551+ App :: test ( ( ) , |mut app| async move {
552+ let ( selector, events) = add_selector ( & mut app) ;
553+ let mut with_footer = snapshot ( & [ "warp" ] , Some ( "warp" ) ) ;
554+ with_footer. footer = Some ( OptionFooter :: CustomText {
555+ label : "Custom host…" . to_string ( ) ,
556+ } ) ;
557+ set_page ( & mut app, & selector, with_footer) ;
558+ act ( & mut app, & selector, TuiOptionSelectorAction :: SelectItem ( 1 ) ) ;
559+ events. borrow_mut ( ) . clear ( ) ;
560+
561+ // An empty submit adds the validation-error row.
562+ confirm ( & mut app, & selector) ;
563+ assert ! ( events
564+ . borrow( )
565+ . contains( & TuiOptionSelectorEvent :: LayoutChanged ) ) ;
566+ events. borrow_mut ( ) . clear ( ) ;
567+
568+ // Typing clears the error row.
569+ act (
570+ & mut app,
571+ & selector,
572+ TuiOptionSelectorAction :: InsertChar ( 'x' ) ,
573+ ) ;
574+ assert ! ( events
575+ . borrow( )
576+ . contains( & TuiOptionSelectorEvent :: LayoutChanged ) ) ;
577+ } ) ;
578+ }
579+
506580#[ test]
507581fn snapshot_refresh_preserves_the_highlighted_row ( ) {
508582 App :: test ( ( ) , |mut app| async move {
@@ -517,7 +591,7 @@ fn snapshot_refresh_preserves_the_highlighted_row() {
517591 } ) ;
518592 confirm ( & mut app, & selector) ;
519593 assert_eq ! (
520- events . borrow ( ) . as_slice ( ) ,
594+ primary_events ( & events ) ,
521595 [ TuiOptionSelectorEvent :: Confirmed {
522596 id: "c" . to_string( )
523597 } ] ,
@@ -539,7 +613,7 @@ fn snapshot_refresh_falls_back_to_the_selected_value_when_the_highlight_vanishes
539613 } ) ;
540614 confirm ( & mut app, & selector) ;
541615 assert_eq ! (
542- events . borrow ( ) . as_slice ( ) ,
616+ primary_events ( & events ) ,
543617 [ TuiOptionSelectorEvent :: Confirmed {
544618 id: "a" . to_string( )
545619 } ] ,
0 commit comments