Skip to content

Commit c38c43b

Browse files
committed
test: Fix Windows unit test failures for prompt history navigation
- Add missing taskHistory and cwd properties to all useExtensionState mocks - Add comprehensive test coverage for prompt history navigation feature - Ensure all 25 tests pass including new prompt history functionality Fixes failing Windows CI test in PR #4450
1 parent d1f15ba commit c38c43b

File tree

1 file changed

+255
-0
lines changed

1 file changed

+255
-0
lines changed

webview-ui/src/components/chat/__tests__/ChatTextArea.test.tsx

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ describe("ChatTextArea", () => {
6868
apiConfiguration: {
6969
apiProvider: "anthropic",
7070
},
71+
taskHistory: [],
72+
cwd: "/test/workspace",
7173
})
7274
})
7375

@@ -76,6 +78,8 @@ describe("ChatTextArea", () => {
7678
;(useExtensionState as jest.Mock).mockReturnValue({
7779
filePaths: [],
7880
openedTabs: [],
81+
taskHistory: [],
82+
cwd: "/test/workspace",
7983
})
8084
render(<ChatTextArea {...defaultProps} sendingDisabled={true} />)
8185
const enhanceButton = getEnhancePromptButton()
@@ -94,6 +98,8 @@ describe("ChatTextArea", () => {
9498
filePaths: [],
9599
openedTabs: [],
96100
apiConfiguration,
101+
taskHistory: [],
102+
cwd: "/test/workspace",
97103
})
98104

99105
render(<ChatTextArea {...defaultProps} inputValue="Test prompt" />)
@@ -114,6 +120,8 @@ describe("ChatTextArea", () => {
114120
apiConfiguration: {
115121
apiProvider: "openrouter",
116122
},
123+
taskHistory: [],
124+
cwd: "/test/workspace",
117125
})
118126

119127
render(<ChatTextArea {...defaultProps} inputValue="" />)
@@ -131,6 +139,8 @@ describe("ChatTextArea", () => {
131139
apiConfiguration: {
132140
apiProvider: "openrouter",
133141
},
142+
taskHistory: [],
143+
cwd: "/test/workspace",
134144
})
135145

136146
render(<ChatTextArea {...defaultProps} inputValue="Test prompt" />)
@@ -155,6 +165,8 @@ describe("ChatTextArea", () => {
155165
apiProvider: "openrouter",
156166
newSetting: "test",
157167
},
168+
taskHistory: [],
169+
cwd: "/test/workspace",
158170
})
159171

160172
rerender(<ChatTextArea {...defaultProps} />)
@@ -408,6 +420,249 @@ describe("ChatTextArea", () => {
408420
// Verify setInputValue was not called
409421
expect(setInputValue).not.toHaveBeenCalled()
410422
})
423+
424+
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" },
429+
]
430+
431+
beforeEach(() => {
432+
;(useExtensionState as jest.Mock).mockReturnValue({
433+
filePaths: [],
434+
openedTabs: [],
435+
apiConfiguration: {
436+
apiProvider: "anthropic",
437+
},
438+
taskHistory: mockTaskHistory,
439+
cwd: "/test/workspace",
440+
})
441+
})
442+
443+
it("should navigate to previous prompt on arrow up", () => {
444+
const setInputValue = jest.fn()
445+
const { container } = render(
446+
<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="" />,
447+
)
448+
449+
const textarea = container.querySelector("textarea")!
450+
451+
// Simulate arrow up key press
452+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
453+
454+
// Should set the most recent prompt (last in array)
455+
expect(setInputValue).toHaveBeenCalledWith("First prompt")
456+
})
457+
458+
it("should navigate through history with multiple arrow up presses", () => {
459+
const setInputValue = jest.fn()
460+
const { container } = render(
461+
<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="" />,
462+
)
463+
464+
const textarea = container.querySelector("textarea")!
465+
466+
// First arrow up - most recent prompt
467+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
468+
expect(setInputValue).toHaveBeenCalledWith("First prompt")
469+
470+
// Update input value to simulate the state change
471+
setInputValue.mockClear()
472+
473+
// Second arrow up - previous prompt
474+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
475+
expect(setInputValue).toHaveBeenCalledWith("Second prompt")
476+
})
477+
478+
it("should navigate forward with arrow down", () => {
479+
const setInputValue = jest.fn()
480+
const { container } = render(
481+
<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="" />,
482+
)
483+
484+
const textarea = container.querySelector("textarea")!
485+
486+
// Go back in history first (index 0 -> "First prompt", then index 1 -> "Second prompt")
487+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
488+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
489+
setInputValue.mockClear()
490+
491+
// Navigate forward (from index 1 back to index 0)
492+
fireEvent.keyDown(textarea, { key: "ArrowDown" })
493+
expect(setInputValue).toHaveBeenCalledWith("First prompt")
494+
})
495+
496+
it("should preserve current input when starting navigation", () => {
497+
const setInputValue = jest.fn()
498+
const { container } = render(
499+
<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="Current input" />,
500+
)
501+
502+
const textarea = container.querySelector("textarea")!
503+
504+
// Navigate to history
505+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
506+
expect(setInputValue).toHaveBeenCalledWith("First prompt")
507+
508+
setInputValue.mockClear()
509+
510+
// Navigate back to current input
511+
fireEvent.keyDown(textarea, { key: "ArrowDown" })
512+
fireEvent.keyDown(textarea, { key: "ArrowDown" })
513+
expect(setInputValue).toHaveBeenCalledWith("Current input")
514+
})
515+
516+
it("should reset history navigation when user types", () => {
517+
const setInputValue = jest.fn()
518+
const { container } = render(
519+
<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="" />,
520+
)
521+
522+
const textarea = container.querySelector("textarea")!
523+
524+
// Navigate to history
525+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
526+
setInputValue.mockClear()
527+
528+
// Type something
529+
fireEvent.change(textarea, { target: { value: "New input", selectionStart: 9 } })
530+
531+
// Should reset history navigation
532+
expect(setInputValue).toHaveBeenCalledWith("New input")
533+
})
534+
535+
it("should reset history navigation when sending message", () => {
536+
const onSend = jest.fn()
537+
const setInputValue = jest.fn()
538+
const { container } = render(
539+
<ChatTextArea
540+
{...defaultProps}
541+
onSend={onSend}
542+
setInputValue={setInputValue}
543+
inputValue="Test message"
544+
/>,
545+
)
546+
547+
const textarea = container.querySelector("textarea")!
548+
549+
// Navigate to history first
550+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
551+
setInputValue.mockClear()
552+
553+
// Send message
554+
fireEvent.keyDown(textarea, { key: "Enter" })
555+
556+
expect(onSend).toHaveBeenCalled()
557+
})
558+
559+
it("should navigate history when cursor is at first line", () => {
560+
const setInputValue = jest.fn()
561+
const { container } = render(
562+
<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="" />,
563+
)
564+
565+
const textarea = container.querySelector("textarea")!
566+
567+
// Clear any calls from initial render
568+
setInputValue.mockClear()
569+
570+
// With empty input, cursor is at first line by default
571+
// Arrow up should navigate history
572+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
573+
expect(setInputValue).toHaveBeenCalledWith("First prompt")
574+
})
575+
576+
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" },
581+
]
582+
583+
;(useExtensionState as jest.Mock).mockReturnValue({
584+
filePaths: [],
585+
openedTabs: [],
586+
apiConfiguration: {
587+
apiProvider: "anthropic",
588+
},
589+
taskHistory: mixedTaskHistory,
590+
cwd: "/test/workspace",
591+
})
592+
593+
const setInputValue = jest.fn()
594+
const { container } = render(
595+
<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="" />,
596+
)
597+
598+
const textarea = container.querySelector("textarea")!
599+
600+
// Should only show prompts from current workspace
601+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
602+
expect(setInputValue).toHaveBeenCalledWith("Workspace 1 prompt")
603+
604+
setInputValue.mockClear()
605+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
606+
expect(setInputValue).toHaveBeenCalledWith("Workspace 1 prompt 2")
607+
})
608+
609+
it("should handle empty task history gracefully", () => {
610+
;(useExtensionState as jest.Mock).mockReturnValue({
611+
filePaths: [],
612+
openedTabs: [],
613+
apiConfiguration: {
614+
apiProvider: "anthropic",
615+
},
616+
taskHistory: [],
617+
cwd: "/test/workspace",
618+
})
619+
620+
const setInputValue = jest.fn()
621+
const { container } = render(
622+
<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="" />,
623+
)
624+
625+
const textarea = container.querySelector("textarea")!
626+
627+
// Should not crash or call setInputValue
628+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
629+
expect(setInputValue).not.toHaveBeenCalled()
630+
})
631+
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" },
638+
]
639+
640+
;(useExtensionState as jest.Mock).mockReturnValue({
641+
filePaths: [],
642+
openedTabs: [],
643+
apiConfiguration: {
644+
apiProvider: "anthropic",
645+
},
646+
taskHistory: taskHistoryWithEmpty,
647+
cwd: "/test/workspace",
648+
})
649+
650+
const setInputValue = jest.fn()
651+
const { container } = render(
652+
<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="" />,
653+
)
654+
655+
const textarea = container.querySelector("textarea")!
656+
657+
// Should skip empty tasks
658+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
659+
expect(setInputValue).toHaveBeenCalledWith("Valid prompt")
660+
661+
setInputValue.mockClear()
662+
fireEvent.keyDown(textarea, { key: "ArrowUp" })
663+
expect(setInputValue).toHaveBeenCalledWith("Another valid prompt")
664+
})
665+
})
411666
})
412667

413668
describe("selectApiConfig", () => {

0 commit comments

Comments
 (0)