@@ -1022,6 +1022,132 @@ describe("ClineProvider - Parallel Mode Support", () => {
10221022 } )
10231023 } )
10241024
1025+ describe ( "parallel mode writes" , ( ) => {
1026+ it ( "should persist mode switches to the stable view-local state key" , async ( ) => {
1027+ const provider = new ClineProvider ( mockContext , mockOutputChannel , "sidebar" , new ContextProxy ( mockContext ) )
1028+ await ( provider as any ) . setViewStateId ( "stable-sidebar-mode" )
1029+ const contextProxySpy = vi . spyOn ( provider . contextProxy , "setValue" )
1030+
1031+ await provider . handleModeSwitch ( "architect" )
1032+
1033+ expect ( contextProxySpy ) . toHaveBeenCalledWith ( "__view_state_stable-sidebar-mode_mode" , "architect" )
1034+ const state = await provider . getState ( )
1035+ expect ( state . mode ) . toBe ( "architect" )
1036+
1037+ await provider . dispose ( )
1038+ } )
1039+
1040+ it ( "should keep mode switches isolated between parallel provider instances" , async ( ) => {
1041+ const provider1 = new ClineProvider (
1042+ mockContext ,
1043+ mockOutputChannel ,
1044+ "sidebar" ,
1045+ new ContextProxy ( mockContext ) ,
1046+ )
1047+ const provider2 = new ClineProvider ( mockContext , mockOutputChannel , "editor" , new ContextProxy ( mockContext ) )
1048+ await ( provider1 as any ) . setViewStateId ( "stable-sidebar-mode-a" )
1049+ await ( provider2 as any ) . setViewStateId ( "stable-editor-mode-b" )
1050+
1051+ await provider1 . handleModeSwitch ( "architect" )
1052+ await provider2 . handleModeSwitch ( "debugger" as any )
1053+
1054+ const state1 = await provider1 . getState ( )
1055+ const state2 = await provider2 . getState ( )
1056+ expect ( state1 . mode ) . toBe ( "architect" )
1057+ expect ( state2 . mode ) . toBe ( "debugger" )
1058+ expect ( provider1 . contextProxy . getValue ( "__view_state_stable-sidebar-mode-a_mode" as any ) ) . toBe ( "architect" )
1059+ expect ( provider2 . contextProxy . getValue ( "__view_state_stable-editor-mode-b_mode" as any ) ) . toBe ( "debugger" )
1060+
1061+ await provider1 . dispose ( )
1062+ await provider2 . dispose ( )
1063+ } )
1064+
1065+ it ( "should update view-local profile state when activating a provider profile" , async ( ) => {
1066+ const provider = new ClineProvider ( mockContext , mockOutputChannel , "sidebar" , new ContextProxy ( mockContext ) )
1067+ await ( provider as any ) . setViewStateId ( "stable-sidebar-profile" )
1068+ const providerSettings = {
1069+ apiProvider : "openrouter" as const ,
1070+ openRouterModelId : "openrouter/anthropic/claude-sonnet-4" ,
1071+ }
1072+ vi . spyOn ( provider . providerSettingsManager , "activateProfile" ) . mockResolvedValue ( {
1073+ name : "profile-a" ,
1074+ id : "profile-a-id" ,
1075+ ...providerSettings ,
1076+ } as any )
1077+ vi . spyOn ( provider . providerSettingsManager , "listConfig" ) . mockResolvedValue ( [
1078+ { name : "profile-a" , id : "profile-a-id" , apiProvider : "openrouter" } ,
1079+ ] )
1080+
1081+ await provider . activateProviderProfile ( { name : "profile-a" } )
1082+
1083+ const state = await provider . getState ( )
1084+ expect ( state . currentApiConfigName ) . toBe ( "profile-a" )
1085+ expect ( state . apiConfiguration ) . toMatchObject ( providerSettings )
1086+ expect (
1087+ provider . contextProxy . getValue ( "__view_state_stable-sidebar-profile_currentApiConfigName" as any ) ,
1088+ ) . toBe ( "profile-a" )
1089+ expect (
1090+ provider . contextProxy . getValue ( "__view_state_stable-sidebar-profile_apiConfiguration" as any ) ,
1091+ ) . toMatchObject ( providerSettings )
1092+
1093+ await provider . dispose ( )
1094+ } )
1095+
1096+ it ( "should update view-local profile state when upserting and activating a provider profile" , async ( ) => {
1097+ const provider = new ClineProvider ( mockContext , mockOutputChannel , "sidebar" , new ContextProxy ( mockContext ) )
1098+ await ( provider as any ) . setViewStateId ( "stable-sidebar-upsert" )
1099+ const providerSettings = {
1100+ apiProvider : "openrouter" as const ,
1101+ openRouterModelId : "openrouter/anthropic/claude-sonnet-4" ,
1102+ }
1103+ vi . spyOn ( provider . providerSettingsManager , "saveConfig" ) . mockResolvedValue ( "profile-b-id" )
1104+ vi . spyOn ( provider . providerSettingsManager , "listConfig" ) . mockResolvedValue ( [
1105+ { name : "profile-b" , id : "profile-b-id" , apiProvider : "openrouter" } ,
1106+ ] )
1107+
1108+ await provider . upsertProviderProfile ( "profile-b" , providerSettings , true )
1109+
1110+ const state = await provider . getState ( )
1111+ expect ( state . currentApiConfigName ) . toBe ( "profile-b" )
1112+ expect ( state . apiConfiguration ) . toMatchObject ( providerSettings )
1113+ expect (
1114+ provider . contextProxy . getValue ( "__view_state_stable-sidebar-upsert_currentApiConfigName" as any ) ,
1115+ ) . toBe ( "profile-b" )
1116+ expect (
1117+ provider . contextProxy . getValue ( "__view_state_stable-sidebar-upsert_apiConfiguration" as any ) ,
1118+ ) . toMatchObject ( providerSettings )
1119+
1120+ await provider . dispose ( )
1121+ } )
1122+ } )
1123+
1124+ describe ( "deleteProviderProfile" , ( ) => {
1125+ it ( "should use merged view-local state when choosing the profile to keep" , async ( ) => {
1126+ const provider = new ClineProvider ( mockContext , mockOutputChannel , "sidebar" , new ContextProxy ( mockContext ) )
1127+ await ( provider as any ) . setViewStateId ( "stable-delete-profile" )
1128+ await ( provider as any ) . saveViewState ( "currentApiConfigName" , "profile-b" )
1129+
1130+ await provider . contextProxy . setValues ( {
1131+ currentApiConfigName : "profile-a" ,
1132+ listApiConfigMeta : [
1133+ { id : "a-id" , name : "profile-a" , apiProvider : "anthropic" } ,
1134+ { id : "b-id" , name : "profile-b" , apiProvider : "openrouter" } ,
1135+ { id : "del-id" , name : "profile-to-delete" , apiProvider : "anthropic" } ,
1136+ ] ,
1137+ } )
1138+
1139+ await provider . deleteProviderProfile ( { id : "del-id" , name : "profile-to-delete" , apiProvider : "anthropic" } )
1140+
1141+ const state = await provider . getState ( )
1142+ expect ( state . currentApiConfigName ) . toBe ( "profile-b" )
1143+ expect (
1144+ provider . contextProxy . getValue ( "__view_state_stable-delete-profile_currentApiConfigName" as any ) ,
1145+ ) . toBe ( "profile-b" )
1146+
1147+ await provider . dispose ( )
1148+ } )
1149+ } )
1150+
10251151 describe ( "_clearViewLocalState" , ( ) => {
10261152 it ( "should clear all view-local state values" , async ( ) => {
10271153 const provider = new ClineProvider ( mockContext , mockOutputChannel , "sidebar" , new ContextProxy ( mockContext ) )
0 commit comments