@@ -10,8 +10,9 @@ use instant::Instant;
1010use tempfile:: TempDir ;
1111use warp:: appearance:: Appearance ;
1212use warp:: settings:: {
13- AISettings , TuiStatuslineConfig , TuiStatuslineItem , TuiTheme , TuiThemeSettings ,
14- TuiUsageDisplayMode , TuiVoiceInputHoldKey , TuiVoiceSettings , TuiZeroStateObject ,
13+ AISettings , SettingsFileError , TuiStatuslineConfig , TuiStatuslineItem , TuiTheme ,
14+ TuiThemeSettings , TuiUsageDisplayMode , TuiVoiceInputHoldKey , TuiVoiceSettings ,
15+ TuiZeroStateObject ,
1516} ;
1617use warp:: terminal:: model:: ansi:: { Handler , InputBufferValue , Mode } ;
1718use warp:: tui_export:: {
@@ -21,8 +22,9 @@ use warp::tui_export::{
2122 Harness , InputTypeAutoDetectionSource , LLMPreferences , LinkedWorkflowData ,
2223 LongRunningCommandControlState , PtyIntent , PtyIntentEvent , SizeInfo , SizeUpdate ,
2324 SlashCommandDataSource as _, SlashCommandKind , TaskId , TranscriptScope , TuiMcpAction ,
24- TuiMcpServerId , TuiUpArrowHistoryItemKind , UserTakeOverReason , export_conversation_markdown,
25- light_theme, register_tui_session_view_test_singletons, slash_commands,
25+ TuiMcpServerId , TuiUpArrowHistoryItemKind , UserTakeOverReason , WarpConfig ,
26+ WarpConfigUpdateEvent , export_conversation_markdown, light_theme,
27+ register_tui_session_view_test_singletons, slash_commands,
2628} ;
2729use warp_core:: channel:: Channel ;
2830use warp_core:: features:: FeatureFlag ;
@@ -92,7 +94,10 @@ use crate::statusline_config_view::TuiStatuslineConfigEvent;
9294use crate :: terminal_background:: TuiHostTerminalBackground ;
9395use crate :: terminal_block:: { block_content_rows, should_render_terminal_block} ;
9496use crate :: terminal_use:: TuiInputTarget ;
95- use crate :: test_fixtures:: { add_test_semantic_selection, add_test_terminal_session} ;
97+ use crate :: test_fixtures:: {
98+ add_test_semantic_selection, add_test_terminal_session,
99+ add_test_terminal_session_with_settings_file_error,
100+ } ;
96101use crate :: transcript_view:: TRANSCRIPT_BLOCK_SPACING ;
97102use crate :: transient_hint:: TransientHintTone ;
98103use crate :: tui_builder:: TuiUiBuilder ;
@@ -976,6 +981,42 @@ fn zero_state_reload_failure_renders_as_an_error_footer_hint() {
976981 } ) ;
977982}
978983
984+ #[ test]
985+ fn settings_reload_failure_renders_as_an_error_footer_hint ( ) {
986+ App :: test ( ( ) , |mut app| async move {
987+ let fixture = focus_test_fixture ( & mut app) ;
988+ let ( view, _) = add_focus_test_session ( & mut app, & fixture, true ) ;
989+
990+ app. update ( |ctx| {
991+ WarpConfig :: handle ( ctx) . update ( ctx, |_, ctx| {
992+ ctx. emit ( WarpConfigUpdateEvent :: SettingsErrors (
993+ SettingsFileError :: InvalidSettings ( vec ! [ "Theme" . to_owned( ) ] ) ,
994+ ) ) ;
995+ } ) ;
996+ } ) ;
997+
998+ assert_eq ! (
999+ view. read( & app, |view, _| {
1000+ view. transient_hint
1001+ . current( )
1002+ . map( |( text, tone) | ( text. to_owned( ) , tone) )
1003+ } ) ,
1004+ Some ( (
1005+ super :: SETTINGS_INVALID_VALUES_HINT . to_owned( ) ,
1006+ TransientHintTone :: Error
1007+ ) )
1008+ ) ;
1009+
1010+ app. read ( |ctx| {
1011+ let footer = view. as_ref ( ctx) . render_footer ( ctx) . finish ( ) ;
1012+ assert_eq ! (
1013+ render_element( footer, ctx, 120 ) . to_lines( ) ,
1014+ vec![ super :: SETTINGS_INVALID_VALUES_HINT . to_owned( ) ]
1015+ ) ;
1016+ } ) ;
1017+ } ) ;
1018+ }
1019+
9791020#[ test]
9801021fn theme_slash_command_accepts_direct_selection_and_rejects_invalid_values ( ) {
9811022 App :: test ( ( ) , |mut app| async move {
@@ -1100,6 +1141,38 @@ fn zero_state_initial_load_failure_shows_an_error_footer_hint() {
11001141 } ) ;
11011142}
11021143
1144+ #[ test]
1145+ fn startup_settings_parse_failure_renders_as_an_error_footer_hint ( ) {
1146+ App :: test ( ( ) , |mut app| async move {
1147+ let fixture = focus_test_fixture ( & mut app) ;
1148+ let view = add_focus_test_session_with_settings_file_error (
1149+ & mut app,
1150+ & fixture,
1151+ SettingsFileError :: FileParseFailed ( "expected a value" . to_owned ( ) ) ,
1152+ ) ;
1153+
1154+ assert_eq ! (
1155+ view. read( & app, |view, _| {
1156+ view. transient_hint
1157+ . current( )
1158+ . map( |( text, tone) | ( text. to_owned( ) , tone) )
1159+ } ) ,
1160+ Some ( (
1161+ super :: SETTINGS_PARSE_FAILED_HINT . to_owned( ) ,
1162+ TransientHintTone :: Error
1163+ ) )
1164+ ) ;
1165+
1166+ app. read ( |ctx| {
1167+ let footer = view. as_ref ( ctx) . render_footer ( ctx) . finish ( ) ;
1168+ assert_eq ! (
1169+ render_element( footer, ctx, 120 ) . to_lines( ) ,
1170+ vec![ super :: SETTINGS_PARSE_FAILED_HINT . to_owned( ) ]
1171+ ) ;
1172+ } ) ;
1173+ } ) ;
1174+ }
1175+
11031176#[ test]
11041177fn listening_voice_input_animates_the_input_border ( ) {
11051178 App :: test ( ( ) , |mut app| async move {
@@ -2133,6 +2206,19 @@ fn add_focus_test_session(
21332206 ( view, session_id)
21342207}
21352208
2209+ fn add_focus_test_session_with_settings_file_error (
2210+ app : & mut App ,
2211+ fixture : & FocusTestFixture ,
2212+ error : SettingsFileError ,
2213+ ) -> ViewHandle < super :: TuiTerminalSessionView > {
2214+ let ( view, manager) =
2215+ add_test_terminal_session_with_settings_file_error ( app, fixture. window_id , Some ( error) ) ;
2216+ app. update ( |ctx| {
2217+ TuiSessions :: register_session ( & fixture. sessions , view. clone ( ) , manager, true , ctx) ;
2218+ } ) ;
2219+ view
2220+ }
2221+
21362222fn render_element ( element : Box < dyn TuiElement > , ctx : & AppContext , width : u16 ) -> TuiBuffer {
21372223 render_element_with_size ( element, ctx, width, 1 )
21382224}
0 commit comments