@@ -2,9 +2,20 @@ import { webviewMessageHandler } from "../webviewMessageHandler"
22import { ClineProvider } from "../ClineProvider"
33import { getModels } from "../../../api/providers/fetchers/modelCache"
44import { ModelRecord } from "../../../shared/api"
5+ import type { ClineMessage } from "@roo-code/types"
6+ import * as vscode from "vscode"
57
68// Mock dependencies
79jest . mock ( "../../../api/providers/fetchers/modelCache" )
10+ jest . mock ( "vscode" , ( ) => ( {
11+ window : {
12+ showWarningMessage : jest . fn ( ) ,
13+ } ,
14+ } ) )
15+ jest . mock ( "../../checkpoints" , ( ) => ( {
16+ checkpointRestore : jest . fn ( ) ,
17+ } ) )
18+
819const mockGetModels = getModels as jest . MockedFunction < typeof getModels >
920
1021// Mock ClineProvider
@@ -272,3 +283,93 @@ describe("webviewMessageHandler - requestRouterModels", () => {
272283 } )
273284 } )
274285} )
286+
287+ describe ( "webviewMessageHandler - editMessage" , ( ) => {
288+ let mockCline : any
289+
290+ beforeEach ( ( ) => {
291+ jest . clearAllMocks ( )
292+
293+ // Mock Cline instance
294+ mockCline = {
295+ taskId : "test-task-id" ,
296+ clineMessages : [
297+ { ts : 1000 , type : "say" , say : "user_feedback" , text : "First message" } ,
298+ { ts : 2000 , type : "say" , say : "user_feedback" , text : "Second message" } ,
299+ { ts : 3000 , type : "say" , say : "checkpoint_saved" , text : "Checkpoint saved" } ,
300+ { ts : 4000 , type : "say" , say : "user_feedback" , text : "Third message" } ,
301+ ] as ClineMessage [ ] ,
302+ apiConversationHistory : [
303+ { ts : 1000 , role : "user" , content : "First message" } ,
304+ { ts : 2000 , role : "user" , content : "Second message" } ,
305+ { ts : 4000 , role : "user" , content : "Third message" } ,
306+ ] ,
307+ overwriteClineMessages : jest . fn ( ) ,
308+ overwriteApiConversationHistory : jest . fn ( ) ,
309+ }
310+
311+ mockClineProvider . getCurrentCline = jest . fn ( ) . mockReturnValue ( mockCline )
312+ mockClineProvider . getState = jest . fn ( ) . mockResolvedValue ( { enableCheckpoints : true } )
313+ mockClineProvider . getTaskWithId = jest . fn ( ) . mockResolvedValue ( {
314+ historyItem : { clineMessages : mockCline . clineMessages } ,
315+ } )
316+ mockClineProvider . postStateToWebview = jest . fn ( )
317+ mockClineProvider . initClineWithHistoryItem = jest . fn ( )
318+ } )
319+
320+ it ( "handles basic message editing without confirmation" , async ( ) => {
321+ // Mock no subsequent messages and no checkpoints
322+ mockCline . clineMessages = [ { ts : 1000 , type : "say" , say : "user_feedback" , text : "Only message" } ]
323+ mockClineProvider . getState = jest . fn ( ) . mockResolvedValue ( { enableCheckpoints : false } )
324+
325+ await webviewMessageHandler ( mockClineProvider , {
326+ type : "editMessage" ,
327+ value : 1000 ,
328+ text : "Edited message" ,
329+ } )
330+
331+ expect ( mockClineProvider . initClineWithHistoryItem ) . toHaveBeenCalled ( )
332+ } )
333+
334+ it ( "shows confirmation dialog when editing affects subsequent messages" , async ( ) => {
335+ const mockShowWarning = vscode . window . showWarningMessage as jest . Mock
336+ mockShowWarning . mockResolvedValue ( "Edit Message" )
337+
338+ await webviewMessageHandler ( mockClineProvider , {
339+ type : "editMessage" ,
340+ value : 2000 , // Edit second message, affecting third message
341+ text : "Edited second message" ,
342+ } )
343+
344+ expect ( mockShowWarning ) . toHaveBeenCalledWith (
345+ "Edit and delete subsequent messages?\n\n• 1 checkpoint(s) will be removed" ,
346+ { modal : true } ,
347+ "Edit Message" ,
348+ )
349+ expect ( mockClineProvider . initClineWithHistoryItem ) . toHaveBeenCalled ( )
350+ } )
351+
352+ it ( "cancels edit when user declines confirmation" , async ( ) => {
353+ const mockShowWarning = vscode . window . showWarningMessage as jest . Mock
354+ mockShowWarning . mockResolvedValue ( undefined ) // User cancelled
355+
356+ await webviewMessageHandler ( mockClineProvider , {
357+ type : "editMessage" ,
358+ value : 2000 ,
359+ text : "This edit should be cancelled" ,
360+ } )
361+
362+ expect ( mockClineProvider . postStateToWebview ) . toHaveBeenCalled ( )
363+ expect ( mockClineProvider . initClineWithHistoryItem ) . not . toHaveBeenCalled ( )
364+ } )
365+
366+ it ( "handles invalid message parameters gracefully" , async ( ) => {
367+ await webviewMessageHandler ( mockClineProvider , {
368+ type : "editMessage" ,
369+ value : undefined , // Invalid value
370+ text : "Should not process" ,
371+ } )
372+
373+ expect ( mockClineProvider . initClineWithHistoryItem ) . not . toHaveBeenCalled ( )
374+ } )
375+ } )
0 commit comments