@@ -43,10 +43,14 @@ vi.mock("vscode", () => ({
4343 window : {
4444 showInformationMessage : vi . fn ( ) ,
4545 showErrorMessage : vi . fn ( ) ,
46+ showOpenDialog : vi . fn ( ) ,
4647 } ,
4748 workspace : {
4849 workspaceFolders : [ { uri : { fsPath : "/mock/workspace" } } ] ,
4950 } ,
51+ Uri : {
52+ file : vi . fn ( ( path : string ) => ( { fsPath : path } ) ) ,
53+ } ,
5054} ) )
5155
5256vi . mock ( "../../../i18n" , ( ) => ( {
@@ -70,14 +74,17 @@ vi.mock("../../../i18n", () => ({
7074vi . mock ( "fs/promises" , ( ) => {
7175 const mockRm = vi . fn ( ) . mockResolvedValue ( undefined )
7276 const mockMkdir = vi . fn ( ) . mockResolvedValue ( undefined )
77+ const mockReadFile = vi . fn ( ) . mockResolvedValue ( "" )
7378
7479 return {
7580 default : {
7681 rm : mockRm ,
7782 mkdir : mockMkdir ,
83+ readFile : mockReadFile ,
7884 } ,
7985 rm : mockRm ,
8086 mkdir : mockMkdir ,
87+ readFile : mockReadFile ,
8188 }
8289} )
8390
@@ -374,6 +381,167 @@ describe("webviewMessageHandler - requestRouterModels", () => {
374381 } )
375382} )
376383
384+ describe ( "webviewMessageHandler - importMode" , ( ) => {
385+ beforeEach ( ( ) => {
386+ vi . clearAllMocks ( )
387+ // Add handleModeSwitch to the mock
388+ mockClineProvider . handleModeSwitch = vi . fn ( )
389+ // Cast to any to add mock methods
390+ ; ( mockClineProvider . customModesManager as any ) . importModeWithRules = vi . fn ( )
391+ } )
392+
393+ it ( "should switch to imported mode after successful import" , async ( ) => {
394+ const mockImportResult = {
395+ success : true ,
396+ importedModes : [ "imported-mode" ] ,
397+ }
398+
399+ vi . mocked ( mockClineProvider . customModesManager . importModeWithRules ) . mockResolvedValue ( mockImportResult )
400+ // Mock getCustomModes to return the imported mode so validation passes
401+ vi . mocked ( mockClineProvider . customModesManager . getCustomModes ) . mockResolvedValue ( [
402+ {
403+ name : "Imported Mode" ,
404+ slug : "imported-mode" ,
405+ roleDefinition : "Test Role" ,
406+ groups : [ ] ,
407+ source : "project" ,
408+ } as ModeConfig ,
409+ ] )
410+
411+ // Mock vscode.window.showOpenDialog
412+ const vscode = await import ( "vscode" )
413+ vi . mocked ( vscode . window . showOpenDialog ) . mockResolvedValue ( [ { fsPath : "/path/to/mode.yaml" } ] as any )
414+
415+ // Mock fs.readFile
416+ const fs = await import ( "fs/promises" )
417+ vi . mocked ( fs . readFile ) . mockResolvedValue ( "yaml content" )
418+
419+ await webviewMessageHandler ( mockClineProvider , {
420+ type : "importMode" ,
421+ source : "project" ,
422+ } )
423+
424+ expect ( mockClineProvider . handleModeSwitch ) . toHaveBeenCalledWith ( "imported-mode" )
425+ expect ( mockClineProvider . postMessageToWebview ) . toHaveBeenCalledWith ( {
426+ type : "importModeResult" ,
427+ success : true ,
428+ importedModes : [ "imported-mode" ] ,
429+ } )
430+ } )
431+
432+ it ( "should not switch mode if import fails" , async ( ) => {
433+ const mockImportResult = {
434+ success : false ,
435+ error : "Import failed" ,
436+ }
437+
438+ vi . mocked ( mockClineProvider . customModesManager . importModeWithRules ) . mockResolvedValue ( mockImportResult )
439+
440+ // Mock vscode.window.showOpenDialog
441+ const vscode = await import ( "vscode" )
442+ vi . mocked ( vscode . window . showOpenDialog ) . mockResolvedValue ( [ { fsPath : "/path/to/mode.yaml" } ] as any )
443+
444+ // Mock fs.readFile
445+ const fs = await import ( "fs/promises" )
446+ vi . mocked ( fs . readFile ) . mockResolvedValue ( "yaml content" )
447+
448+ await webviewMessageHandler ( mockClineProvider , {
449+ type : "importMode" ,
450+ source : "project" ,
451+ } )
452+
453+ expect ( mockClineProvider . handleModeSwitch ) . not . toHaveBeenCalled ( )
454+ expect ( mockClineProvider . postMessageToWebview ) . toHaveBeenCalledWith ( {
455+ type : "importModeResult" ,
456+ success : false ,
457+ error : "Import failed" ,
458+ } )
459+ } )
460+
461+ it ( "should switch to first mode when multiple modes are imported" , async ( ) => {
462+ const mockImportResult = {
463+ success : true ,
464+ importedModes : [ "mode-one" , "mode-two" , "mode-three" ] ,
465+ }
466+
467+ vi . mocked ( mockClineProvider . customModesManager . importModeWithRules ) . mockResolvedValue ( mockImportResult )
468+ // Mock getCustomModes to return the imported modes so validation passes
469+ vi . mocked ( mockClineProvider . customModesManager . getCustomModes ) . mockResolvedValue ( [
470+ {
471+ name : "Mode One" ,
472+ slug : "mode-one" ,
473+ roleDefinition : "Test Role" ,
474+ groups : [ ] ,
475+ source : "project" ,
476+ } as ModeConfig ,
477+ {
478+ name : "Mode Two" ,
479+ slug : "mode-two" ,
480+ roleDefinition : "Test Role" ,
481+ groups : [ ] ,
482+ source : "project" ,
483+ } as ModeConfig ,
484+ {
485+ name : "Mode Three" ,
486+ slug : "mode-three" ,
487+ roleDefinition : "Test Role" ,
488+ groups : [ ] ,
489+ source : "project" ,
490+ } as ModeConfig ,
491+ ] )
492+
493+ // Mock vscode.window.showOpenDialog
494+ const vscode = await import ( "vscode" )
495+ vi . mocked ( vscode . window . showOpenDialog ) . mockResolvedValue ( [ { fsPath : "/path/to/modes.yaml" } ] as any )
496+
497+ // Mock fs.readFile
498+ const fs = await import ( "fs/promises" )
499+ vi . mocked ( fs . readFile ) . mockResolvedValue ( "yaml content with multiple modes" )
500+
501+ await webviewMessageHandler ( mockClineProvider , {
502+ type : "importMode" ,
503+ source : "project" ,
504+ } )
505+
506+ // Should switch to the first mode only
507+ expect ( mockClineProvider . handleModeSwitch ) . toHaveBeenCalledWith ( "mode-one" )
508+ expect ( mockClineProvider . handleModeSwitch ) . toHaveBeenCalledTimes ( 1 )
509+ } )
510+
511+ it ( "should handle invalid mode slug gracefully" , async ( ) => {
512+ const mockImportResult = {
513+ success : true ,
514+ importedModes : [ "invalid-mode-that-does-not-exist" ] ,
515+ }
516+
517+ vi . mocked ( mockClineProvider . customModesManager . importModeWithRules ) . mockResolvedValue ( mockImportResult )
518+ vi . mocked ( mockClineProvider . customModesManager . getCustomModes ) . mockResolvedValue ( [ ] )
519+
520+ // Mock vscode.window.showOpenDialog
521+ const vscode = await import ( "vscode" )
522+ vi . mocked ( vscode . window . showOpenDialog ) . mockResolvedValue ( [ { fsPath : "/path/to/mode.yaml" } ] as any )
523+
524+ // Mock fs.readFile
525+ const fs = await import ( "fs/promises" )
526+ vi . mocked ( fs . readFile ) . mockResolvedValue ( "yaml content" )
527+
528+ await webviewMessageHandler ( mockClineProvider , {
529+ type : "importMode" ,
530+ source : "project" ,
531+ } )
532+
533+ // Should not call handleModeSwitch with invalid mode
534+ expect ( mockClineProvider . handleModeSwitch ) . not . toHaveBeenCalled ( )
535+
536+ // Should still send success message
537+ expect ( mockClineProvider . postMessageToWebview ) . toHaveBeenCalledWith ( {
538+ type : "importModeResult" ,
539+ success : true ,
540+ importedModes : [ "invalid-mode-that-does-not-exist" ] ,
541+ } )
542+ } )
543+ } )
544+
377545describe ( "webviewMessageHandler - deleteCustomMode" , ( ) => {
378546 beforeEach ( ( ) => {
379547 vi . clearAllMocks ( )
0 commit comments