@@ -422,10 +422,10 @@ describe("ChatTextArea", () => {
422422 } )
423423
424424 describe ( "prompt history navigation" , ( ) => {
425- const mockTaskHistory = [
426- { task : "First prompt" , workspace : "/test/workspace" } ,
427- { task : "Second prompt" , workspace : "/test/workspace" } ,
428- { task : "Third prompt" , workspace : "/test/workspace" } ,
425+ const mockClineMessages = [
426+ { type : "say" , say : "user_feedback" , text : "First prompt" , ts : 1000 } ,
427+ { type : "say" , say : "user_feedback" , text : "Second prompt" , ts : 2000 } ,
428+ { type : "say" , say : "user_feedback" , text : "Third prompt" , ts : 3000 } ,
429429 ]
430430
431431 beforeEach ( ( ) => {
@@ -435,7 +435,8 @@ describe("ChatTextArea", () => {
435435 apiConfiguration : {
436436 apiProvider : "anthropic" ,
437437 } ,
438- taskHistory : mockTaskHistory ,
438+ taskHistory : [ ] ,
439+ clineMessages : mockClineMessages ,
439440 cwd : "/test/workspace" ,
440441 } )
441442 } )
@@ -451,8 +452,8 @@ describe("ChatTextArea", () => {
451452 // Simulate arrow up key press
452453 fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
453454
454- // Should set the most recent prompt (last in array)
455- expect ( setInputValue ) . toHaveBeenCalledWith ( "First prompt" )
455+ // Should set the newest conversation message (first in reversed array)
456+ expect ( setInputValue ) . toHaveBeenCalledWith ( "Third prompt" )
456457 } )
457458
458459 it ( "should navigate through history with multiple arrow up presses" , ( ) => {
@@ -463,14 +464,14 @@ describe("ChatTextArea", () => {
463464
464465 const textarea = container . querySelector ( "textarea" ) !
465466
466- // First arrow up - most recent prompt
467+ // First arrow up - newest conversation message
467468 fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
468- expect ( setInputValue ) . toHaveBeenCalledWith ( "First prompt" )
469+ expect ( setInputValue ) . toHaveBeenCalledWith ( "Third prompt" )
469470
470471 // Update input value to simulate the state change
471472 setInputValue . mockClear ( )
472473
473- // Second arrow up - previous prompt
474+ // Second arrow up - previous conversation message
474475 fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
475476 expect ( setInputValue ) . toHaveBeenCalledWith ( "Second prompt" )
476477 } )
@@ -483,14 +484,14 @@ describe("ChatTextArea", () => {
483484
484485 const textarea = container . querySelector ( "textarea" ) !
485486
486- // Go back in history first (index 0 -> "First prompt", then index 1 -> "Second prompt")
487+ // Go back in history first (index 0 -> "Third prompt", then index 1 -> "Second prompt")
487488 fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
488489 fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
489490 setInputValue . mockClear ( )
490491
491492 // Navigate forward (from index 1 back to index 0)
492493 fireEvent . keyDown ( textarea , { key : "ArrowDown" } )
493- expect ( setInputValue ) . toHaveBeenCalledWith ( "First prompt" )
494+ expect ( setInputValue ) . toHaveBeenCalledWith ( "Third prompt" )
494495 } )
495496
496497 it ( "should preserve current input when starting navigation" , ( ) => {
@@ -503,13 +504,12 @@ describe("ChatTextArea", () => {
503504
504505 // Navigate to history
505506 fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
506- expect ( setInputValue ) . toHaveBeenCalledWith ( "First prompt" )
507+ expect ( setInputValue ) . toHaveBeenCalledWith ( "Third prompt" )
507508
508509 setInputValue . mockClear ( )
509510
510511 // Navigate back to current input
511512 fireEvent . keyDown ( textarea , { key : "ArrowDown" } )
512- fireEvent . keyDown ( textarea , { key : "ArrowDown" } )
513513 expect ( setInputValue ) . toHaveBeenCalledWith ( "Current input" )
514514 } )
515515
@@ -570,14 +570,14 @@ describe("ChatTextArea", () => {
570570 // With empty input, cursor is at first line by default
571571 // Arrow up should navigate history
572572 fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
573- expect ( setInputValue ) . toHaveBeenCalledWith ( "First prompt" )
573+ expect ( setInputValue ) . toHaveBeenCalledWith ( "Third prompt" )
574574 } )
575575
576576 it ( "should filter history by current workspace" , ( ) => {
577- const mixedTaskHistory = [
578- { task : "Workspace 1 prompt" , workspace : "/test/workspace" } ,
579- { task : "Other workspace prompt" , workspace : "/other/ workspace" } ,
580- { task : "Workspace 1 prompt 2" , workspace : "/test/workspace" } ,
577+ const mixedClineMessages = [
578+ { type : "say" , say : "user_feedback" , text : " Workspace 1 prompt", ts : 1000 } ,
579+ { type : "say" , say : "user_feedback" , text : "Other workspace prompt" , ts : 2000 } ,
580+ { type : "say" , say : "user_feedback" , text : " Workspace 1 prompt 2", ts : 3000 } ,
581581 ]
582582
583583 ; ( useExtensionState as jest . Mock ) . mockReturnValue ( {
@@ -586,7 +586,8 @@ describe("ChatTextArea", () => {
586586 apiConfiguration : {
587587 apiProvider : "anthropic" ,
588588 } ,
589- taskHistory : mixedTaskHistory ,
589+ taskHistory : [ ] ,
590+ clineMessages : mixedClineMessages ,
590591 cwd : "/test/workspace" ,
591592 } )
592593
@@ -597,23 +598,24 @@ describe("ChatTextArea", () => {
597598
598599 const textarea = container . querySelector ( "textarea" ) !
599600
600- // Should only show prompts from current workspace
601+ // Should show conversation messages newest first (after reverse)
601602 fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
602- expect ( setInputValue ) . toHaveBeenCalledWith ( "Workspace 1 prompt" )
603+ expect ( setInputValue ) . toHaveBeenCalledWith ( "Workspace 1 prompt 2 " )
603604
604605 setInputValue . mockClear ( )
605606 fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
606- expect ( setInputValue ) . toHaveBeenCalledWith ( "Workspace 1 prompt 2 " )
607+ expect ( setInputValue ) . toHaveBeenCalledWith ( "Other workspace prompt" )
607608 } )
608609
609- it ( "should handle empty task history gracefully" , ( ) => {
610+ it ( "should handle empty conversation history gracefully" , ( ) => {
610611 ; ( useExtensionState as jest . Mock ) . mockReturnValue ( {
611612 filePaths : [ ] ,
612613 openedTabs : [ ] ,
613614 apiConfiguration : {
614615 apiProvider : "anthropic" ,
615616 } ,
616617 taskHistory : [ ] ,
618+ clineMessages : [ ] ,
617619 cwd : "/test/workspace" ,
618620 } )
619621
@@ -629,12 +631,12 @@ describe("ChatTextArea", () => {
629631 expect ( setInputValue ) . not . toHaveBeenCalled ( )
630632 } )
631633
632- it ( "should ignore empty or whitespace-only tasks " , ( ) => {
633- const taskHistoryWithEmpty = [
634- { task : "Valid prompt" , workspace : "/test/workspace" } ,
635- { task : "" , workspace : "/test/workspace" } ,
636- { task : " " , workspace : "/test/workspace" } ,
637- { task : "Another valid prompt" , workspace : "/test/workspace" } ,
634+ it ( "should ignore empty or whitespace-only messages " , ( ) => {
635+ const clineMessagesWithEmpty = [
636+ { type : "say" , say : "user_feedback" , text : "Valid prompt" , ts : 1000 } ,
637+ { type : "say " , say : "user_feedback" , text : "" , ts : 2000 } ,
638+ { type : "say" , say : "user_feedback" , text : " " , ts : 3000 } ,
639+ { type : "say" , say : "user_feedback" , text : " Another valid prompt", ts : 4000 } ,
638640 ]
639641
640642 ; ( useExtensionState as jest . Mock ) . mockReturnValue ( {
@@ -643,7 +645,8 @@ describe("ChatTextArea", () => {
643645 apiConfiguration : {
644646 apiProvider : "anthropic" ,
645647 } ,
646- taskHistory : taskHistoryWithEmpty ,
648+ taskHistory : [ ] ,
649+ clineMessages : clineMessagesWithEmpty ,
647650 cwd : "/test/workspace" ,
648651 } )
649652
@@ -654,13 +657,99 @@ describe("ChatTextArea", () => {
654657
655658 const textarea = container . querySelector ( "textarea" ) !
656659
657- // Should skip empty tasks
660+ // Should skip empty messages, newest first for conversation
661+ fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
662+ expect ( setInputValue ) . toHaveBeenCalledWith ( "Another valid prompt" )
663+
664+ setInputValue . mockClear ( )
658665 fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
659666 expect ( setInputValue ) . toHaveBeenCalledWith ( "Valid prompt" )
667+ } )
668+
669+ it ( "should use task history (oldest first) when no conversation messages exist" , ( ) => {
670+ const mockTaskHistory = [
671+ { task : "First task" , workspace : "/test/workspace" } ,
672+ { task : "Second task" , workspace : "/test/workspace" } ,
673+ { task : "Third task" , workspace : "/test/workspace" } ,
674+ ]
675+
676+ ; ( useExtensionState as jest . Mock ) . mockReturnValue ( {
677+ filePaths : [ ] ,
678+ openedTabs : [ ] ,
679+ apiConfiguration : {
680+ apiProvider : "anthropic" ,
681+ } ,
682+ taskHistory : mockTaskHistory ,
683+ clineMessages : [ ] , // No conversation messages
684+ cwd : "/test/workspace" ,
685+ } )
686+
687+ const setInputValue = jest . fn ( )
688+ const { container } = render (
689+ < ChatTextArea { ...defaultProps } setInputValue = { setInputValue } inputValue = "" /> ,
690+ )
691+
692+ const textarea = container . querySelector ( "textarea" ) !
693+
694+ // Should show task history oldest first (chronological order)
695+ fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
696+ expect ( setInputValue ) . toHaveBeenCalledWith ( "First task" )
660697
661698 setInputValue . mockClear ( )
662699 fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
663- expect ( setInputValue ) . toHaveBeenCalledWith ( "Another valid prompt" )
700+ expect ( setInputValue ) . toHaveBeenCalledWith ( "Second task" )
701+ } )
702+
703+ it ( "should reset navigation position when switching between history sources" , ( ) => {
704+ const setInputValue = jest . fn ( )
705+ const { rerender } = render (
706+ < ChatTextArea { ...defaultProps } setInputValue = { setInputValue } inputValue = "" /> ,
707+ )
708+
709+ // Start with task history
710+ ; ( useExtensionState as jest . Mock ) . mockReturnValue ( {
711+ filePaths : [ ] ,
712+ openedTabs : [ ] ,
713+ apiConfiguration : {
714+ apiProvider : "anthropic" ,
715+ } ,
716+ taskHistory : [
717+ { task : "Task 1" , workspace : "/test/workspace" } ,
718+ { task : "Task 2" , workspace : "/test/workspace" } ,
719+ ] ,
720+ clineMessages : [ ] ,
721+ cwd : "/test/workspace" ,
722+ } )
723+
724+ rerender ( < ChatTextArea { ...defaultProps } setInputValue = { setInputValue } inputValue = "" /> )
725+
726+ const textarea = document . querySelector ( "textarea" ) !
727+
728+ // Navigate in task history
729+ fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
730+ expect ( setInputValue ) . toHaveBeenCalledWith ( "Task 1" )
731+
732+ // Switch to conversation messages
733+ ; ( useExtensionState as jest . Mock ) . mockReturnValue ( {
734+ filePaths : [ ] ,
735+ openedTabs : [ ] ,
736+ apiConfiguration : {
737+ apiProvider : "anthropic" ,
738+ } ,
739+ taskHistory : [ ] ,
740+ clineMessages : [
741+ { type : "say" , say : "user_feedback" , text : "Message 1" , ts : 1000 } ,
742+ { type : "say" , say : "user_feedback" , text : "Message 2" , ts : 2000 } ,
743+ ] ,
744+ cwd : "/test/workspace" ,
745+ } )
746+
747+ setInputValue . mockClear ( )
748+ rerender ( < ChatTextArea { ...defaultProps } setInputValue = { setInputValue } inputValue = "" /> )
749+
750+ // Should start from beginning of conversation history (newest first)
751+ fireEvent . keyDown ( textarea , { key : "ArrowUp" } )
752+ expect ( setInputValue ) . toHaveBeenCalledWith ( "Message 2" )
664753 } )
665754 } )
666755 } )
0 commit comments