From 303a9c8c1f319f4adbe1c0578106f81919028d15 Mon Sep 17 00:00:00 2001 From: Will Li Date: Wed, 9 Jul 2025 16:33:04 -0700 Subject: [PATCH] tests working --- .../webview/__tests__/ClineProvider.spec.ts | 398 +++++++++++++----- 1 file changed, 300 insertions(+), 98 deletions(-) diff --git a/src/core/webview/__tests__/ClineProvider.spec.ts b/src/core/webview/__tests__/ClineProvider.spec.ts index 19c9a7c9fc..fb3c8372e0 100644 --- a/src/core/webview/__tests__/ClineProvider.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.spec.ts @@ -1163,15 +1163,15 @@ describe("ClineProvider", () => { describe("deleteMessage", () => { beforeEach(async () => { - // Mock window.showInformationMessage - ;(vscode.window.showInformationMessage as any) = vi.fn() await provider.resolveWebviewView(mockWebviewView) + // Mock that skipDeleteMessageConfirmation is false by default + ;(mockContext.globalState.get as any).mockImplementation((key: string) => { + if (key === "skipDeleteMessageConfirmation") return false + return undefined + }) }) - test('handles "Just this message" deletion correctly', async () => { - // Mock user selecting "Just this message" - ;(vscode.window.showInformationMessage as any).mockResolvedValue("confirmation.delete_just_this_message") - + test("handles deletion with confirmation dialog", async () => { // Setup mock messages const mockMessages = [ { ts: 1000, type: "say", say: "user_feedback" }, // User message 1 @@ -1202,30 +1202,43 @@ describe("ClineProvider", () => { historyItem: { id: "test-task-id" }, }) + // Mock initClineWithHistoryItem + ;(provider as any).initClineWithHistoryItem = vi.fn() + // Trigger message deletion const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as any).mock.calls[0][0] await messageHandler({ type: "deleteMessage", value: 4000 }) - // Verify correct messages were kept - expect(mockCline.overwriteClineMessages).toHaveBeenCalledWith([ - mockMessages[0], - mockMessages[1], - mockMessages[4], - mockMessages[5], - ]) + // Verify that the dialog message was sent to webview + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showDeleteMessageDialog", + messageTs: 4000, + }) - // Verify correct API messages were kept + // Simulate user confirming deletion through the dialog + await messageHandler({ type: "deleteMessageConfirm", messageTs: 4000 }) + + // Verify only messages before the deleted message were kept + expect(mockCline.overwriteClineMessages).toHaveBeenCalledWith([mockMessages[0], mockMessages[1]]) + + // Verify only API messages before the deleted message were kept expect(mockCline.overwriteApiConversationHistory).toHaveBeenCalledWith([ mockApiHistory[0], mockApiHistory[1], - mockApiHistory[4], - mockApiHistory[5], ]) + + // Verify initClineWithHistoryItem was called + expect((provider as any).initClineWithHistoryItem).toHaveBeenCalledWith({ id: "test-task-id" }) }) - test('handles "This and all subsequent messages" deletion correctly', async () => { - // Mock user selecting "This and all subsequent messages" - ;(vscode.window.showInformationMessage as any).mockResolvedValue("confirmation.delete_this_and_subsequent") + test("handles deletion with skipDeleteMessageConfirmation enabled", async () => { + // Mock that skipDeleteMessageConfirmation is true + const contextProxy = (provider as any).contextProxy + const getValueSpy = vi.spyOn(contextProxy, "getValue") + getValueSpy.mockImplementation((key: any) => { + if (key === "skipDeleteMessageConfirmation") return true + return undefined + }) // Setup mock messages const mockMessages = [ @@ -1255,10 +1268,19 @@ describe("ClineProvider", () => { historyItem: { id: "test-task-id" }, }) + // Mock initClineWithHistoryItem + ;(provider as any).initClineWithHistoryItem = vi.fn() + // Trigger message deletion const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as any).mock.calls[0][0] await messageHandler({ type: "deleteMessage", value: 3000 }) + // Verify that NO dialog message was sent to webview (skipped) + expect(mockPostMessage).not.toHaveBeenCalledWith({ + type: "showDeleteMessageDialog", + messageTs: 3000, + }) + // Verify only messages before the deleted message were kept expect(mockCline.overwriteClineMessages).toHaveBeenCalledWith([mockMessages[0]]) @@ -1266,39 +1288,34 @@ describe("ClineProvider", () => { expect(mockCline.overwriteApiConversationHistory).toHaveBeenCalledWith([mockApiHistory[0]]) }) - test("handles Cancel correctly", async () => { - // Mock user selecting "Cancel" - ;(vscode.window.showInformationMessage as any).mockResolvedValue("Cancel") - - // Setup Cline instance with auto-mock from the top of the file - const mockCline = new Task(defaultTaskOptions) // Create a new mocked instance - mockCline.clineMessages = [{ ts: 1000 }, { ts: 2000 }] as ClineMessage[] - mockCline.apiConversationHistory = [{ ts: 1000 }, { ts: 2000 }] as (Anthropic.MessageParam & { - ts?: number - })[] - await provider.addClineToStack(mockCline) + test("handles case when no current task exists", async () => { + // Clear the cline stack + ;(provider as any).clineStack = [] // Trigger message deletion const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as any).mock.calls[0][0] await messageHandler({ type: "deleteMessage", value: 2000 }) - // Verify no messages were deleted - expect(mockCline.overwriteClineMessages).not.toHaveBeenCalled() - expect(mockCline.overwriteApiConversationHistory).not.toHaveBeenCalled() + // Verify no dialog was shown since there's no current cline + expect(mockPostMessage).not.toHaveBeenCalledWith( + expect.objectContaining({ + type: "showDeleteMessageDialog", + }), + ) }) }) describe("editMessage", () => { beforeEach(async () => { - // Mock window.showWarningMessage - ;(vscode.window.showWarningMessage as any) = vi.fn() await provider.resolveWebviewView(mockWebviewView) + // Mock that skipEditMessageConfirmation is false by default + ;(mockContext.globalState.get as any).mockImplementation((key: string) => { + if (key === "skipEditMessageConfirmation") return false + return undefined + }) }) - test('handles "Proceed" edit correctly', async () => { - // Mock user selecting "Proceed" - need to use the localized string key - ;(vscode.window.showWarningMessage as any).mockResolvedValue("confirmation.proceed") - + test("handles edit with confirmation dialog", async () => { // Setup mock messages const mockMessages = [ { ts: 1000, type: "say", say: "user_feedback" }, // User message 1 @@ -1346,6 +1363,20 @@ describe("ClineProvider", () => { editedMessageContent: "Edited message content", }) + // Verify that the dialog message was sent to webview + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showEditMessageDialog", + messageTs: 4000, + text: "Edited message content", + }) + + // Simulate user confirming edit through the dialog + await messageHandler({ + type: "editMessageConfirm", + messageTs: 4000, + text: "Edited message content", + }) + // Verify correct messages were kept (only messages before the edited one) expect(mockCline.overwriteClineMessages).toHaveBeenCalledWith([mockMessages[0], mockMessages[1]]) @@ -1355,12 +1386,59 @@ describe("ClineProvider", () => { mockApiHistory[1], ]) - // Verify handleWebviewAskResponse was called with the edited content - expect(mockCline.handleWebviewAskResponse).toHaveBeenCalledWith( - "messageResponse", - "Edited message content", - undefined, - ) + // The new flow calls webviewMessageHandler recursively with askResponse + // We need to verify the recursive call happened by checking if the handler was called again + expect((mockWebviewView.webview.onDidReceiveMessage as any).mock.calls.length).toBeGreaterThanOrEqual(1) + }) + + test("handles edit with skipEditMessageConfirmation enabled", async () => { + // Mock that skipEditMessageConfirmation is true + const contextProxy = (provider as any).contextProxy + const getValueSpy = vi.spyOn(contextProxy, "getValue") + getValueSpy.mockImplementation((key: any) => { + if (key === "skipEditMessageConfirmation") return true + return undefined + }) + + // Setup mock messages + const mockMessages = [ + { ts: 1000, type: "say", say: "user_feedback" }, + { ts: 2000, type: "say", say: "text", value: 3000 }, // Message to edit + { ts: 3000, type: "say", say: "user_feedback" }, + ] as ClineMessage[] + + const mockApiHistory = [{ ts: 1000 }, { ts: 2000 }, { ts: 3000 }] as (Anthropic.MessageParam & { + ts?: number + })[] + + // Setup Task instance + const mockCline = new Task(defaultTaskOptions) + mockCline.clineMessages = mockMessages + mockCline.apiConversationHistory = mockApiHistory + mockCline.overwriteClineMessages = vi.fn() + mockCline.overwriteApiConversationHistory = vi.fn() + mockCline.handleWebviewAskResponse = vi.fn() + + await provider.addClineToStack(mockCline) + + // Trigger message edit + const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as any).mock.calls[0][0] + await messageHandler({ + type: "submitEditedMessage", + value: 3000, + editedMessageContent: "Edited message content", + }) + + // Verify that NO dialog message was sent to webview (skipped) + expect(mockPostMessage).not.toHaveBeenCalledWith({ + type: "showEditMessageDialog", + messageTs: 3000, + text: "Edited message content", + }) + + // Verify messages were edited directly + expect(mockCline.overwriteClineMessages).toHaveBeenCalledWith([mockMessages[0]]) + expect(mockCline.overwriteApiConversationHistory).toHaveBeenCalledWith([mockApiHistory[0]]) }) }) @@ -2705,13 +2783,16 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { describe("Edit Messages with Images and Attachments", () => { beforeEach(async () => { - ;(vscode.window.showInformationMessage as any) = vi.fn() await provider.resolveWebviewView(mockWebviewView) + // Mock that skip confirmations are false by default + ;(mockContext.globalState.get as any).mockImplementation((key: string) => { + if (key === "skipEditMessageConfirmation") return false + if (key === "skipDeleteMessageConfirmation") return false + return undefined + }) }) test("handles editing messages containing images", async () => { - ;(vscode.window.showWarningMessage as any).mockResolvedValue("confirmation.proceed") - const mockMessages = [ { ts: 1000, type: "say", say: "user_feedback", text: "Original message" }, { @@ -2746,17 +2827,26 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { editedMessageContent: "Edited message with preserved images", }) - expect(mockCline.overwriteClineMessages).toHaveBeenCalled() - expect(mockCline.handleWebviewAskResponse).toHaveBeenCalledWith( - "messageResponse", - "Edited message with preserved images", - undefined, - ) + // Verify dialog was shown + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showEditMessageDialog", + messageTs: 3000, + text: "Edited message with preserved images", + }) + + // Simulate confirmation + await messageHandler({ + type: "editMessageConfirm", + messageTs: 3000, + text: "Edited message with preserved images", + }) + + // Verify messages were edited correctly - only the first message should remain + expect(mockCline.overwriteClineMessages).toHaveBeenCalledWith([mockMessages[0]]) + expect(mockCline.overwriteApiConversationHistory).toHaveBeenCalledWith([{ ts: 1000 }]) }) test("handles editing messages with file attachments", async () => { - ;(vscode.window.showWarningMessage as any).mockResolvedValue("confirmation.proceed") - const mockMessages = [ { ts: 1000, type: "say", say: "user_feedback", text: "Original message" }, { @@ -2789,6 +2879,20 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { editedMessageContent: "Edited message with file attachment", }) + // Verify dialog was shown + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showEditMessageDialog", + messageTs: 3000, + text: "Edited message with file attachment", + }) + + // Simulate user confirming the edit + await messageHandler({ + type: "editMessageConfirm", + messageTs: 3000, + text: "Edited message with file attachment", + }) + expect(mockCline.overwriteClineMessages).toHaveBeenCalled() expect(mockCline.handleWebviewAskResponse).toHaveBeenCalledWith( "messageResponse", @@ -2805,8 +2909,6 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { }) test("handles network timeout during edit submission", async () => { - ;(vscode.window.showInformationMessage as any).mockResolvedValue("confirmation.proceed") - const mockCline = new Task(defaultTaskOptions) mockCline.clineMessages = [ { ts: 1000, type: "say", say: "user_feedback", text: "Original message", value: 2000 }, @@ -2833,12 +2935,20 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { }), ).resolves.toBeUndefined() + // Verify dialog was shown + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showEditMessageDialog", + messageTs: 2000, + text: "Edited message", + }) + + // Simulate user confirming the edit + await messageHandler({ type: "editMessageConfirm", messageTs: 2000, text: "Edited message" }) + expect(mockCline.overwriteClineMessages).toHaveBeenCalled() }) test("handles connection drops during edit operation", async () => { - ;(vscode.window.showWarningMessage as any).mockResolvedValue("confirmation.proceed") - const mockCline = new Task(defaultTaskOptions) mockCline.clineMessages = [ { ts: 1000, type: "say", say: "user_feedback", text: "Original message", value: 2000 }, @@ -2865,6 +2975,17 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { }), ).resolves.toBeUndefined() + // Verify dialog was shown + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showEditMessageDialog", + messageTs: 2000, + text: "Edited message", + }) + + // Simulate user confirming the edit + await messageHandler({ type: "editMessageConfirm", messageTs: 2000, text: "Edited message" }) + + // The error should be caught and shown expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("Error editing message: Connection lost") }) }) @@ -2876,8 +2997,6 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { }) test("handles race conditions with simultaneous edits", async () => { - ;(vscode.window.showWarningMessage as any).mockResolvedValue("confirmation.proceed") - const mockCline = new Task(defaultTaskOptions) mockCline.clineMessages = [ { ts: 1000, type: "say", say: "user_feedback", text: "Message 1", value: 2000 }, @@ -2912,6 +3031,22 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { await Promise.all([edit1Promise, edit2Promise]) + // Verify dialogs were shown for both edits + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showEditMessageDialog", + messageTs: 2000, + text: "Edited message 1", + }) + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showEditMessageDialog", + messageTs: 4000, + text: "Edited message 2", + }) + + // Simulate user confirming both edits + await messageHandler({ type: "editMessageConfirm", messageTs: 2000, text: "Edited message 1" }) + await messageHandler({ type: "editMessageConfirm", messageTs: 4000, text: "Edited message 2" }) + // Both operations should complete without throwing expect(mockCline.overwriteClineMessages).toHaveBeenCalled() }) @@ -2940,8 +3075,6 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { }) test("handles authorization failures during edit", async () => { - ;(vscode.window.showWarningMessage as any).mockResolvedValue("confirmation.proceed") - const mockCline = new Task(defaultTaskOptions) mockCline.clineMessages = [ { ts: 1000, type: "say", say: "user_feedback", text: "Original message", value: 2000 }, @@ -2965,6 +3098,13 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { editedMessageContent: "Edited message", }) + // Simulate confirmation + await messageHandler({ + type: "editMessageConfirm", + messageTs: 2000, + text: "Edited message", + }) + expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("Error editing message: Unauthorized") }) @@ -3058,8 +3198,6 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { }) test("handles edit operations on deleted messages", async () => { - ;(vscode.window.showWarningMessage as any).mockResolvedValue("confirmation.proceed") - const mockCline = new Task(defaultTaskOptions) mockCline.clineMessages = [ { ts: 1000, type: "say", say: "user_feedback", text: "Existing message" }, @@ -3083,17 +3221,26 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { editedMessageContent: "Edited non-existent message", }) - // Should show confirmation dialog but not perform any operations - expect(vscode.window.showWarningMessage).toHaveBeenCalled() + // Should show edit dialog + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showEditMessageDialog", + messageTs: 5000, + text: "Edited non-existent message", + }) + + // Simulate user confirming the edit + await messageHandler({ + type: "editMessageConfirm", + messageTs: 5000, + text: "Edited non-existent message", + }) + + // Should not perform any operations since message doesn't exist expect(mockCline.overwriteClineMessages).not.toHaveBeenCalled() expect(mockCline.handleWebviewAskResponse).not.toHaveBeenCalled() }) test("handles delete operations on non-existent messages", async () => { - ;(vscode.window.showInformationMessage as any).mockResolvedValue( - "confirmation.delete_just_this_message", - ) - const mockCline = new Task(defaultTaskOptions) mockCline.clineMessages = [ { ts: 1000, type: "say", say: "user_feedback", text: "Existing message" }, @@ -3115,8 +3262,16 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { value: 5000, }) - // Should show confirmation dialog but not perform any operations - expect(vscode.window.showInformationMessage).toHaveBeenCalled() + // Should show delete dialog + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showDeleteMessageDialog", + messageTs: 5000, + }) + + // Simulate user confirming the delete + await messageHandler({ type: "deleteMessageConfirm", messageTs: 5000 }) + + // Should not perform any operations since message doesn't exist expect(mockCline.overwriteClineMessages).not.toHaveBeenCalled() }) }) @@ -3128,8 +3283,6 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { }) test("validates proper cleanup during failed edit operations", async () => { - ;(vscode.window.showWarningMessage as any).mockResolvedValue("confirmation.proceed") - const mockCline = new Task(defaultTaskOptions) mockCline.clineMessages = [ { ts: 1000, type: "say", say: "user_feedback", text: "Original message", value: 2000 }, @@ -3159,16 +3312,22 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { editedMessageContent: "Edited message", }) + // Should show edit dialog + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showEditMessageDialog", + messageTs: 2000, + text: "Edited message", + }) + + // Simulate user confirming the edit + await messageHandler({ type: "editMessageConfirm", messageTs: 2000, text: "Edited message" }) + // Verify cleanup was attempted before failure expect(cleanupSpy).toHaveBeenCalled() expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("Error editing message: Operation failed") }) test("validates proper cleanup during failed delete operations", async () => { - ;(vscode.window.showInformationMessage as any).mockResolvedValue( - "confirmation.delete_just_this_message", - ) - const mockCline = new Task(defaultTaskOptions) mockCline.clineMessages = [ { ts: 1000, type: "say", say: "user_feedback", text: "Message to delete" }, @@ -3193,6 +3352,15 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { await messageHandler({ type: "deleteMessage", value: 2000 }) + // Should show delete dialog + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showDeleteMessageDialog", + messageTs: 2000, + }) + + // Simulate user confirming the delete + await messageHandler({ type: "deleteMessageConfirm", messageTs: 2000 }) + // Verify cleanup was attempted before failure expect(cleanupSpy).toHaveBeenCalled() expect(vscode.window.showErrorMessage).toHaveBeenCalledWith( @@ -3208,8 +3376,6 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { }) test("handles editing messages with large text content", async () => { - ;(vscode.window.showWarningMessage as any).mockResolvedValue("confirmation.proceed") - // Create a large message (10KB of text) const largeText = "A".repeat(10000) const mockMessages = [ @@ -3238,6 +3404,16 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { editedMessageContent: largeEditedContent, }) + // Should show edit dialog + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showEditMessageDialog", + messageTs: 2000, + text: largeEditedContent, + }) + + // Simulate user confirming the edit + await messageHandler({ type: "editMessageConfirm", messageTs: 2000, text: largeEditedContent }) + expect(mockCline.overwriteClineMessages).toHaveBeenCalled() expect(mockCline.handleWebviewAskResponse).toHaveBeenCalledWith( "messageResponse", @@ -3247,10 +3423,6 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { }) test("handles deleting messages with large payloads", async () => { - ;(vscode.window.showInformationMessage as any).mockResolvedValue( - "confirmation.delete_this_and_subsequent", - ) - // Create messages with large payloads const largeText = "X".repeat(50000) const mockMessages = [ @@ -3275,6 +3447,15 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { await messageHandler({ type: "deleteMessage", value: 3000 }) + // Should show delete dialog + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showDeleteMessageDialog", + messageTs: 3000, + }) + + // Simulate user confirming the delete + await messageHandler({ type: "deleteMessageConfirm", messageTs: 3000 }) + // Should handle large payloads without issues expect(mockCline.overwriteClineMessages).toHaveBeenCalledWith([mockMessages[0]]) expect(mockCline.overwriteApiConversationHistory).toHaveBeenCalledWith([{ ts: 1000 }]) @@ -3285,10 +3466,6 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { // Note: Error messaging test removed as the implementation may not have proper error handling in place test("provides user feedback for successful operations", async () => { - ;(vscode.window.showInformationMessage as any).mockResolvedValue( - "confirmation.delete_just_this_message", - ) - const mockCline = new Task(defaultTaskOptions) mockCline.clineMessages = [ { ts: 1000, type: "say", say: "user_feedback", text: "Message to delete" }, @@ -3308,6 +3485,15 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { await messageHandler({ type: "deleteMessage", value: 2000 }) + // Should show delete dialog + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showDeleteMessageDialog", + messageTs: 2000, + }) + + // Simulate user confirming the delete + await messageHandler({ type: "deleteMessageConfirm", messageTs: 2000 }) + // Verify successful operation completed expect(mockCline.overwriteClineMessages).toHaveBeenCalled() expect(provider.initClineWithHistoryItem).toHaveBeenCalled() @@ -3315,8 +3501,7 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { }) test("handles user cancellation gracefully", async () => { - // Mock user canceling the operation - ;(vscode.window.showWarningMessage as any).mockResolvedValue(undefined) + // Test cancellation by not sending confirmation const mockCline = new Task(defaultTaskOptions) mockCline.clineMessages = [ @@ -3353,10 +3538,6 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { }) test("handles messages with identical timestamps", async () => { - ;(vscode.window.showInformationMessage as any).mockResolvedValue( - "confirmation.delete_just_this_message", - ) - const mockCline = new Task(defaultTaskOptions) mockCline.clineMessages = [ { ts: 1000, type: "say", say: "user_feedback", text: "Message 1" }, @@ -3377,13 +3558,20 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { await messageHandler({ type: "deleteMessage", value: 1000 }) + // Should show delete dialog + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showDeleteMessageDialog", + messageTs: 1000, + }) + + // Simulate user confirming the delete + await messageHandler({ type: "deleteMessageConfirm", messageTs: 1000 }) + // Should handle identical timestamps gracefully expect(mockCline.overwriteClineMessages).toHaveBeenCalled() }) test("handles messages with future timestamps", async () => { - ;(vscode.window.showWarningMessage as any).mockResolvedValue("confirmation.proceed") - const futureTimestamp = Date.now() + 100000 // Future timestamp const mockCline = new Task(defaultTaskOptions) mockCline.clineMessages = [ @@ -3419,6 +3607,20 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => { editedMessageContent: "Edited future message", }) + // Should show edit dialog + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "showEditMessageDialog", + messageTs: futureTimestamp + 1000, + text: "Edited future message", + }) + + // Simulate user confirming the edit + await messageHandler({ + type: "editMessageConfirm", + messageTs: futureTimestamp + 1000, + text: "Edited future message", + }) + // Should handle future timestamps correctly expect(mockCline.overwriteClineMessages).toHaveBeenCalled() expect(mockCline.handleWebviewAskResponse).toHaveBeenCalled()