@@ -82,7 +82,7 @@ vi.mock('./tasks', () => ({
8282 } ) ,
8383} ) ) ;
8484
85- import { navigateColumn , navigateRow } from './focus' ;
85+ import { navigateColumn , navigateRow , navigateTask } from './focus' ;
8686
8787function setTask ( id : string , overrides : Record < string , unknown > = { } ) : void {
8888 mockStore . tasks [ id ] = {
@@ -223,3 +223,92 @@ describe('focus navigation neighbor map', () => {
223223 expect ( mockStore . focusedPanel [ 'task-1' ] ) . toBe ( 'shell:0' ) ;
224224 } ) ;
225225} ) ;
226+
227+ describe ( 'navigateTask' , ( ) => {
228+ it ( 'preserves the focused panel name when switching to the next task' , ( ) => {
229+ setTask ( 'task-1' ) ;
230+ setTask ( 'task-2' ) ;
231+ mockStore . taskOrder = [ 'task-1' , 'task-2' ] ;
232+ mockStore . focusedPanel [ 'task-1' ] = 'changed-files' ;
233+
234+ navigateTask ( 'right' ) ;
235+
236+ expect ( mockStore . activeTaskId ) . toBe ( 'task-2' ) ;
237+ expect ( mockStore . focusedPanel [ 'task-2' ] ) . toBe ( 'changed-files' ) ;
238+ } ) ;
239+
240+ it ( 'preserves the focused panel name when switching to the previous task' , ( ) => {
241+ setTask ( 'task-1' ) ;
242+ setTask ( 'task-2' ) ;
243+ mockStore . taskOrder = [ 'task-1' , 'task-2' ] ;
244+ mockStore . activeTaskId = 'task-2' ;
245+ mockStore . focusedPanel [ 'task-2' ] = 'notes' ;
246+
247+ navigateTask ( 'left' ) ;
248+
249+ expect ( mockStore . activeTaskId ) . toBe ( 'task-1' ) ;
250+ expect ( mockStore . focusedPanel [ 'task-1' ] ) . toBe ( 'notes' ) ;
251+ } ) ;
252+
253+ it ( 'falls back to the default panel when the current panel does not exist in the target' , ( ) => {
254+ setTask ( 'task-1' , { stepsEnabled : true , stepsContent : [ { id : 'step-1' } ] } ) ;
255+ setTask ( 'task-2' ) ;
256+ mockStore . taskOrder = [ 'task-1' , 'task-2' ] ;
257+ mockStore . focusedPanel [ 'task-1' ] = 'steps' ;
258+
259+ navigateTask ( 'right' ) ;
260+
261+ expect ( mockStore . activeTaskId ) . toBe ( 'task-2' ) ;
262+ expect ( mockStore . focusedPanel [ 'task-2' ] ) . toBe ( 'ai-terminal' ) ;
263+ } ) ;
264+
265+ it ( 'is a no-op at the leftmost task' , ( ) => {
266+ setTask ( 'task-1' ) ;
267+ setTask ( 'task-2' ) ;
268+ mockStore . taskOrder = [ 'task-1' , 'task-2' ] ;
269+ mockStore . activeTaskId = 'task-1' ;
270+ mockStore . focusedPanel [ 'task-1' ] = 'changed-files' ;
271+
272+ navigateTask ( 'left' ) ;
273+
274+ expect ( mockStore . activeTaskId ) . toBe ( 'task-1' ) ;
275+ expect ( mockStore . focusedPanel [ 'task-1' ] ) . toBe ( 'changed-files' ) ;
276+ expect ( mockStore . sidebarFocused ) . toBe ( false ) ;
277+ } ) ;
278+
279+ it ( 'is a no-op at the rightmost task' , ( ) => {
280+ setTask ( 'task-1' ) ;
281+ setTask ( 'task-2' ) ;
282+ mockStore . taskOrder = [ 'task-1' , 'task-2' ] ;
283+ mockStore . activeTaskId = 'task-2' ;
284+ mockStore . focusedPanel [ 'task-2' ] = 'notes' ;
285+
286+ navigateTask ( 'right' ) ;
287+
288+ expect ( mockStore . activeTaskId ) . toBe ( 'task-2' ) ;
289+ expect ( mockStore . focusedPanel [ 'task-2' ] ) . toBe ( 'notes' ) ;
290+ expect ( mockStore . placeholderFocused ) . toBe ( false ) ;
291+ } ) ;
292+
293+ it ( 'is a no-op when the active id is not in taskOrder (e.g. terminal)' , ( ) => {
294+ setTask ( 'task-1' ) ;
295+ mockStore . taskOrder = [ 'task-1' ] ;
296+ mockStore . activeTaskId = 'terminal-1' ;
297+
298+ navigateTask ( 'right' ) ;
299+
300+ expect ( mockStore . activeTaskId ) . toBe ( 'terminal-1' ) ;
301+ } ) ;
302+
303+ it ( 'is a no-op while a dialog is open' , ( ) => {
304+ setTask ( 'task-1' ) ;
305+ setTask ( 'task-2' ) ;
306+ mockStore . taskOrder = [ 'task-1' , 'task-2' ] ;
307+ mockStore . focusedPanel [ 'task-1' ] = 'changed-files' ;
308+ mockStore . showHelpDialog = true ;
309+
310+ navigateTask ( 'right' ) ;
311+
312+ expect ( mockStore . activeTaskId ) . toBe ( 'task-1' ) ;
313+ } ) ;
314+ } ) ;
0 commit comments