@@ -15,6 +15,7 @@ vi.mock('../utils', () => ({
1515
1616const capturedCallbacks = vi . hoisted ( ( ) => ( {
1717 onCommand : null as ( ( command : string ) => void ) | null ,
18+ onModeChange : null as ( ( mode : string ) => void ) | null ,
1819 onSelect : null as ( ( model : string ) => void ) | null ,
1920 onCancel : null as ( ( ) => void ) | null ,
2021 onToggleMode : null as ( ( ) => void ) | null ,
@@ -29,12 +30,15 @@ vi.mock('./Header', () => ({
2930vi . mock ( './Chat' , ( ) => ( {
3031 Chat : ( {
3132 onCommand,
33+ onModeChange,
3234 } : {
3335 model : string ;
3436 onCommand : ( command : string ) => void ;
35- autoExecute : boolean ;
37+ mode : string ;
38+ onModeChange : ( mode : string ) => void ;
3639 } ) => {
3740 capturedCallbacks . onCommand = onCommand ;
41+ capturedCallbacks . onModeChange = onModeChange ;
3842 return < Text > { '>' } </ Text > ;
3943 } ,
4044} ) ) ;
@@ -56,14 +60,15 @@ vi.mock('./ModelPicker', () => ({
5660
5761vi . mock ( './Footer' , ( ) => ( {
5862 Footer : ( {
59- autoExecute ,
63+ mode ,
6064 onToggleMode,
6165 } : {
62- autoExecute : boolean ;
66+ mode : string ;
6367 onToggleMode : ( ) => void ;
6468 } ) => {
6569 capturedCallbacks . onToggleMode = onToggleMode ;
66- return < Text > Mode: { autoExecute ? 'Auto' : 'Safe' } </ Text > ;
70+ const modeLabel = mode . charAt ( 0 ) . toUpperCase ( ) + mode . slice ( 1 ) ;
71+ return < Text > Mode: { modeLabel } </ Text > ;
6772 } ,
6873} ) ) ;
6974
@@ -72,6 +77,7 @@ import { App } from './App';
7277describe ( 'App' , ( ) => {
7378 beforeEach ( ( ) => {
7479 capturedCallbacks . onCommand = null ;
80+ capturedCallbacks . onModeChange = null ;
7581 capturedCallbacks . onSelect = null ;
7682 capturedCallbacks . onCancel = null ;
7783 capturedCallbacks . onToggleMode = null ;
@@ -127,25 +133,44 @@ describe('App', () => {
127133 expect ( lastFrame ( ) ) . not . toContain ( 'ModelPicker' ) ;
128134 } ) ;
129135
130- it ( 'toggles autoExecute via Footer onToggleMode callback' , async ( ) => {
136+ it ( 'toggles mode via Footer onToggleMode callback (3-state cycle) ' , async ( ) => {
131137 const { lastFrame, rerender } = render ( < App /> ) ;
132138
133- // Initial state
139+ // Initial state: Safe
134140 expect ( lastFrame ( ) ) . toContain ( 'Mode: Safe' ) ;
135141
136- // Call the callback passed to Footer
142+ // Call the callback passed to Footer - cycles to Auto
137143 capturedCallbacks . onToggleMode ?.( ) ;
138144 rerender ( < App /> ) ;
139145 await tick ( ) ;
140-
141- // Should show Auto mode
142146 expect ( lastFrame ( ) ) . toContain ( 'Mode: Auto' ) ;
143147
144- // Call again to toggle back
148+ // Call again - cycles to Plan
149+ capturedCallbacks . onToggleMode ?.( ) ;
150+ rerender ( < App /> ) ;
151+ await tick ( ) ;
152+ expect ( lastFrame ( ) ) . toContain ( 'Mode: Plan' ) ;
153+
154+ // Call again - cycles back to Safe
145155 capturedCallbacks . onToggleMode ?.( ) ;
146156 rerender ( < App /> ) ;
147157 await tick ( ) ;
158+ expect ( lastFrame ( ) ) . toContain ( 'Mode: Safe' ) ;
159+ } ) ;
160+
161+ it ( 'updates footer mode when Chat changes execution mode' , async ( ) => {
162+ const { lastFrame, rerender } = render ( < App /> ) ;
148163
149164 expect ( lastFrame ( ) ) . toContain ( 'Mode: Safe' ) ;
165+
166+ capturedCallbacks . onModeChange ?.( 'auto' ) ;
167+ rerender ( < App /> ) ;
168+ await tick ( ) ;
169+ expect ( lastFrame ( ) ) . toContain ( 'Mode: Auto' ) ;
170+
171+ capturedCallbacks . onModeChange ?.( 'safe' ) ;
172+ rerender ( < App /> ) ;
173+ await tick ( ) ;
174+ expect ( lastFrame ( ) ) . toContain ( 'Mode: Safe' ) ;
150175 } ) ;
151176} ) ;
0 commit comments