Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions webview-ui/src/components/chat/ChatTextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,28 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
const message = event.data

if (message.type === "enhancedPrompt") {
if (message.text) {
setInputValue(message.text)
if (message.text && textAreaRef.current) {
// Use native browser methods to preserve undo stack
const textarea = textAreaRef.current

// Focus the textarea to ensure it's the active element
textarea.focus()

// Select all text first
textarea.select()

// Use execCommand to replace text while preserving undo history
if (document.execCommand) {
document.execCommand("insertText", false, message.text)
} else {
// Fallback for browsers that don't support execCommand
// This approach also preserves undo history in modern browsers
textarea.setRangeText(message.text, 0, textarea.value.length, "select")

// Trigger input event to notify React of the change
const inputEvent = new Event("input", { bubbles: true })
textarea.dispatchEvent(inputEvent)
}
}

setIsEnhancingPrompt(false)
Expand Down
87 changes: 84 additions & 3 deletions webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,70 @@ describe("ChatTextArea", () => {
})

describe("enhanced prompt response", () => {
it("should update input value when receiving enhanced prompt", () => {
it("should update input value using native browser methods when receiving enhanced prompt", () => {
const setInputValue = vi.fn()

render(<ChatTextArea {...defaultProps} setInputValue={setInputValue} />)
// Mock document.execCommand
const mockExecCommand = vi.fn().mockReturnValue(true)
Object.defineProperty(document, "execCommand", {
value: mockExecCommand,
writable: true,
})

const { container } = render(
<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="Original prompt" />,
)

const textarea = container.querySelector("textarea")!

// Mock textarea methods
const mockSelect = vi.fn()
const mockFocus = vi.fn()
textarea.select = mockSelect
textarea.focus = mockFocus

// Simulate receiving enhanced prompt message
window.dispatchEvent(
new MessageEvent("message", {
data: {
type: "enhancedPrompt",
text: "Enhanced test prompt",
},
}),
)

// Verify native browser methods were used
expect(mockFocus).toHaveBeenCalled()
expect(mockSelect).toHaveBeenCalled()
expect(mockExecCommand).toHaveBeenCalledWith("insertText", false, "Enhanced test prompt")
})

it("should fallback to setRangeText when execCommand is not available", () => {
const setInputValue = vi.fn()

// Mock document.execCommand to be undefined (not available)
Object.defineProperty(document, "execCommand", {
value: undefined,
writable: true,
})

const { container } = render(
<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="Original prompt" />,
)

const textarea = container.querySelector("textarea")!

// Mock textarea methods
const mockSelect = vi.fn()
const mockFocus = vi.fn()
const mockSetRangeText = vi.fn()
const mockDispatchEvent = vi.fn()

textarea.select = mockSelect
textarea.focus = mockFocus
textarea.setRangeText = mockSetRangeText
textarea.dispatchEvent = mockDispatchEvent
textarea.value = "Original prompt"

// Simulate receiving enhanced prompt message
window.dispatchEvent(
Expand All @@ -199,7 +259,28 @@ describe("ChatTextArea", () => {
}),
)

expect(setInputValue).toHaveBeenCalledWith("Enhanced test prompt")
// Verify fallback methods were used
expect(mockFocus).toHaveBeenCalled()
expect(mockSetRangeText).toHaveBeenCalledWith("Enhanced test prompt", 0, 15, "select") // 15 is length of "Original prompt"
expect(mockDispatchEvent).toHaveBeenCalledWith(expect.any(Event))
})

it("should not crash when textarea ref is not available", () => {
const setInputValue = vi.fn()

render(<ChatTextArea {...defaultProps} setInputValue={setInputValue} />)

// Simulate receiving enhanced prompt message when textarea ref might not be ready
expect(() => {
window.dispatchEvent(
new MessageEvent("message", {
data: {
type: "enhancedPrompt",
text: "Enhanced test prompt",
},
}),
)
}).not.toThrow()
})
})

Expand Down
6 changes: 6 additions & 0 deletions webview-ui/vitest.setup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import "@testing-library/jest-dom"
import "@testing-library/jest-dom/vitest"

// Force React into development mode for tests
// This is needed to enable act(...) function in React Testing Library
globalThis.process = globalThis.process || {}
globalThis.process.env = globalThis.process.env || {}
globalThis.process.env.NODE_ENV = "development"

class MockResizeObserver {
observe() {}
unobserve() {}
Expand Down