@@ -641,6 +641,364 @@ describe("ButtonSetManager", () => {
641641 } ) ;
642642 } ) ;
643643
644+ describe ( "createButtonSet" , ( ) => {
645+ it ( "should create new button set with provided buttons" , async ( ) => {
646+ const mockConfig = createMockConfig ( ) ;
647+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . WORKSPACE ) ;
648+ mockConfig . inspect . mockReturnValue ( { workspaceValue : [ ] } ) ;
649+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
650+ mockConfig as unknown as vscode . WorkspaceConfiguration
651+ ) ;
652+
653+ const configManager = createMockConfigManager ( ) ;
654+ const configReader = createMockConfigReader ( ) ;
655+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
656+
657+ const manager = ButtonSetManager . create ( { buttonSetWriter, configManager, configReader } ) ;
658+ const buttons = [ { command : "echo test" , id : "btn-1" , name : "Test Button" } ] ;
659+ const result = await manager . createButtonSet ( "New Set" , buttons ) ;
660+
661+ expect ( result . success ) . toBe ( true ) ;
662+ expect ( buttonSetWriter . writeButtonSets ) . toHaveBeenCalled ( ) ;
663+ const writtenSets = buttonSetWriter . writeButtonSets . mock . calls [ 0 ] [ 0 ] ;
664+ expect ( writtenSets ) . toHaveLength ( 1 ) ;
665+ expect ( writtenSets [ 0 ] . name ) . toBe ( "New Set" ) ;
666+ expect ( writtenSets [ 0 ] . buttons ) . toHaveLength ( 1 ) ;
667+ } ) ;
668+
669+ it ( "should create empty button set when no buttons provided" , async ( ) => {
670+ const mockConfig = createMockConfig ( ) ;
671+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . WORKSPACE ) ;
672+ mockConfig . inspect . mockReturnValue ( { workspaceValue : [ ] } ) ;
673+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
674+ mockConfig as unknown as vscode . WorkspaceConfiguration
675+ ) ;
676+
677+ const configManager = createMockConfigManager ( ) ;
678+ const configReader = createMockConfigReader ( ) ;
679+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
680+
681+ const manager = ButtonSetManager . create ( { buttonSetWriter, configManager, configReader } ) ;
682+ const result = await manager . createButtonSet ( "Empty Set" ) ;
683+
684+ expect ( result . success ) . toBe ( true ) ;
685+ const writtenSets = buttonSetWriter . writeButtonSets . mock . calls [ 0 ] [ 0 ] ;
686+ expect ( writtenSets [ 0 ] . buttons ) . toHaveLength ( 0 ) ;
687+ } ) ;
688+
689+ it ( "should return error for empty name" , async ( ) => {
690+ const mockConfig = createMockConfig ( ) ;
691+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . WORKSPACE ) ;
692+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
693+ mockConfig as unknown as vscode . WorkspaceConfiguration
694+ ) ;
695+
696+ const configManager = createMockConfigManager ( ) ;
697+ const configReader = createMockConfigReader ( ) ;
698+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
699+
700+ const manager = ButtonSetManager . create ( { buttonSetWriter, configManager, configReader } ) ;
701+ const result = await manager . createButtonSet ( " " ) ;
702+
703+ expect ( result . success ) . toBe ( false ) ;
704+ if ( ! result . success ) {
705+ expect ( result . error ) . toBe ( "setNameRequired" ) ;
706+ }
707+ } ) ;
708+
709+ it ( "should return error for duplicate name" , async ( ) => {
710+ const existingSets = createTestButtonSets ( ) ;
711+ const mockConfig = createMockConfig ( ) ;
712+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . WORKSPACE ) ;
713+ mockConfig . inspect . mockReturnValue ( {
714+ workspaceValue : existingSets . map ( ( s ) => ( { buttons : s . buttons , name : s . name } ) ) ,
715+ } ) ;
716+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
717+ mockConfig as unknown as vscode . WorkspaceConfiguration
718+ ) ;
719+
720+ const configManager = createMockConfigManager ( ) ;
721+ const configReader = createMockConfigReader ( ) ;
722+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
723+
724+ const manager = ButtonSetManager . create ( { buttonSetWriter, configManager, configReader } ) ;
725+ const result = await manager . createButtonSet ( "Frontend" ) ;
726+
727+ expect ( result . success ) . toBe ( false ) ;
728+ if ( ! result . success ) {
729+ expect ( result . error ) . toBe ( "duplicateSetName" ) ;
730+ }
731+ } ) ;
732+
733+ it ( "should copy buttons from source set when sourceSetId provided" , async ( ) => {
734+ const existingSets = createTestButtonSets ( ) ;
735+ const mockConfig = createMockConfig ( ) ;
736+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . WORKSPACE ) ;
737+ // Include id in workspaceValue so ensureSetIdsInArray can match
738+ mockConfig . inspect . mockReturnValue ( {
739+ workspaceValue : existingSets ,
740+ } ) ;
741+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
742+ mockConfig as unknown as vscode . WorkspaceConfiguration
743+ ) ;
744+
745+ const configManager = createMockConfigManager ( ) ;
746+ const configReader = createMockConfigReader ( ) ;
747+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
748+
749+ const manager = ButtonSetManager . create ( { buttonSetWriter, configManager, configReader } ) ;
750+ const result = await manager . createButtonSet ( "Copied Set" , [ ] , existingSets [ 0 ] . id ) ;
751+
752+ expect ( result . success ) . toBe ( true ) ;
753+ const writtenSets = buttonSetWriter . writeButtonSets . mock . calls [ 0 ] [ 0 ] ;
754+ const newSet = writtenSets . find ( ( s : { name : string } ) => s . name === "Copied Set" ) ;
755+ expect ( newSet . buttons ) . toEqual ( existingSets [ 0 ] . buttons ) ;
756+ } ) ;
757+
758+ it ( "should use provided buttons when sourceSetId not found" , async ( ) => {
759+ const mockConfig = createMockConfig ( ) ;
760+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . WORKSPACE ) ;
761+ mockConfig . inspect . mockReturnValue ( { workspaceValue : [ ] } ) ;
762+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
763+ mockConfig as unknown as vscode . WorkspaceConfiguration
764+ ) ;
765+
766+ const configManager = createMockConfigManager ( ) ;
767+ const configReader = createMockConfigReader ( ) ;
768+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
769+
770+ const manager = ButtonSetManager . create ( { buttonSetWriter, configManager, configReader } ) ;
771+ const buttons = [ { command : "echo fallback" , id : "fb-1" , name : "Fallback" } ] ;
772+ const result = await manager . createButtonSet ( "New Set" , buttons , "non-existent-id" ) ;
773+
774+ expect ( result . success ) . toBe ( true ) ;
775+ const writtenSets = buttonSetWriter . writeButtonSets . mock . calls [ 0 ] [ 0 ] ;
776+ expect ( writtenSets [ 0 ] . buttons ) . toEqual ( buttons ) ;
777+ } ) ;
778+
779+ it ( "should write to local storage when local scope" , async ( ) => {
780+ const mockConfig = createMockConfig ( ) ;
781+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . LOCAL ) ;
782+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
783+ mockConfig as unknown as vscode . WorkspaceConfiguration
784+ ) ;
785+
786+ const configManager = createMockConfigManager ( ) ;
787+ const configReader = createMockConfigReader ( ) ;
788+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
789+ const buttonSetLocalStorage = createMockButtonSetLocalStorage ( ) ;
790+
791+ const manager = ButtonSetManager . create ( {
792+ buttonSetLocalStorage,
793+ buttonSetWriter,
794+ configManager,
795+ configReader,
796+ } ) ;
797+ const result = await manager . createButtonSet ( "Local Set" ) ;
798+
799+ expect ( result . success ) . toBe ( true ) ;
800+ expect ( buttonSetLocalStorage . setButtonSets ) . toHaveBeenCalled ( ) ;
801+ expect ( buttonSetWriter . writeButtonSets ) . not . toHaveBeenCalled ( ) ;
802+ } ) ;
803+ } ) ;
804+
805+ describe ( "updateActiveSetButtons" , ( ) => {
806+ it ( "should return false when no active set" , async ( ) => {
807+ const mockConfig = createMockConfig ( ) ;
808+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . WORKSPACE ) ;
809+ mockConfig . inspect . mockReturnValue ( { workspaceValue : null } ) ;
810+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
811+ mockConfig as unknown as vscode . WorkspaceConfiguration
812+ ) ;
813+
814+ const configManager = createMockConfigManager ( ) ;
815+ const configReader = createMockConfigReader ( ) ;
816+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
817+
818+ const manager = ButtonSetManager . create ( { buttonSetWriter, configManager, configReader } ) ;
819+ const result = await manager . updateActiveSetButtons ( [ ] ) ;
820+
821+ expect ( result ) . toBe ( false ) ;
822+ } ) ;
823+
824+ it ( "should return false when active set not found" , async ( ) => {
825+ const mockConfig = createMockConfig ( ) ;
826+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . WORKSPACE ) ;
827+ mockConfig . inspect . mockImplementation ( ( key : string ) => {
828+ if ( key === "activeSet" ) {
829+ return { workspaceValue : "NonExistent" } ;
830+ }
831+ if ( key === "buttonSets" ) {
832+ return { workspaceValue : [ ] } ;
833+ }
834+ return { } ;
835+ } ) ;
836+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
837+ mockConfig as unknown as vscode . WorkspaceConfiguration
838+ ) ;
839+
840+ const configManager = createMockConfigManager ( ) ;
841+ const configReader = createMockConfigReader ( ) ;
842+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
843+
844+ const manager = ButtonSetManager . create ( { buttonSetWriter, configManager, configReader } ) ;
845+ const result = await manager . updateActiveSetButtons ( [ ] ) ;
846+
847+ expect ( result ) . toBe ( false ) ;
848+ } ) ;
849+
850+ it ( "should update buttons in active set" , async ( ) => {
851+ const existingSets = createTestButtonSets ( ) ;
852+ const mockConfig = createMockConfig ( ) ;
853+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . WORKSPACE ) ;
854+ mockConfig . inspect . mockImplementation ( ( key : string ) => {
855+ if ( key === "activeSet" ) {
856+ return { workspaceValue : "Frontend" } ;
857+ }
858+ if ( key === "buttonSets" ) {
859+ return {
860+ workspaceValue : existingSets . map ( ( s ) => ( { buttons : s . buttons , name : s . name } ) ) ,
861+ } ;
862+ }
863+ return { } ;
864+ } ) ;
865+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
866+ mockConfig as unknown as vscode . WorkspaceConfiguration
867+ ) ;
868+
869+ const configManager = createMockConfigManager ( ) ;
870+ const configReader = createMockConfigReader ( ) ;
871+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
872+
873+ const manager = ButtonSetManager . create ( { buttonSetWriter, configManager, configReader } ) ;
874+ const newButtons = [
875+ { command : "echo updated1" , name : "Updated 1" } ,
876+ { command : "echo updated2" , name : "Updated 2" } ,
877+ ] ;
878+ const result = await manager . updateActiveSetButtons ( newButtons ) ;
879+
880+ expect ( result ) . toBe ( true ) ;
881+ expect ( buttonSetWriter . writeButtonSets ) . toHaveBeenCalled ( ) ;
882+ const writtenSets = buttonSetWriter . writeButtonSets . mock . calls [ 0 ] [ 0 ] ;
883+ const frontendSet = writtenSets . find ( ( s : { name : string } ) => s . name === "Frontend" ) ;
884+ expect ( frontendSet . buttons ) . toHaveLength ( 2 ) ;
885+ expect ( frontendSet . buttons [ 0 ] . command ) . toBe ( "echo updated1" ) ;
886+ } ) ;
887+
888+ it ( "should ensure IDs for buttons without IDs" , async ( ) => {
889+ const existingSets = [ { buttons : [ ] , id : "set-1" , name : "TestSet" } ] ;
890+ const mockConfig = createMockConfig ( ) ;
891+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . WORKSPACE ) ;
892+ mockConfig . inspect . mockImplementation ( ( key : string ) => {
893+ if ( key === "activeSet" ) {
894+ return { workspaceValue : "TestSet" } ;
895+ }
896+ if ( key === "buttonSets" ) {
897+ return { workspaceValue : existingSets } ;
898+ }
899+ return { } ;
900+ } ) ;
901+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
902+ mockConfig as unknown as vscode . WorkspaceConfiguration
903+ ) ;
904+
905+ const configManager = createMockConfigManager ( ) ;
906+ const configReader = createMockConfigReader ( ) ;
907+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
908+
909+ const manager = ButtonSetManager . create ( { buttonSetWriter, configManager, configReader } ) ;
910+ const newButtons = [ { command : "echo no-id" , name : "No ID Button" } ] ;
911+ const result = await manager . updateActiveSetButtons ( newButtons ) ;
912+
913+ expect ( result ) . toBe ( true ) ;
914+ const writtenSets = buttonSetWriter . writeButtonSets . mock . calls [ 0 ] [ 0 ] ;
915+ expect ( writtenSets [ 0 ] . buttons [ 0 ] . id ) . toBeDefined ( ) ;
916+ } ) ;
917+ } ) ;
918+
919+ describe ( "setActiveSet with global scope" , ( ) => {
920+ it ( "should set active set to global scope" , async ( ) => {
921+ const mockConfig = createMockConfig ( ) ;
922+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . GLOBAL ) ;
923+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
924+ mockConfig as unknown as vscode . WorkspaceConfiguration
925+ ) ;
926+
927+ const configManager = createMockConfigManager ( ) ;
928+ const configReader = createMockConfigReader ( ) ;
929+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
930+
931+ const manager = ButtonSetManager . create ( { buttonSetWriter, configManager, configReader } ) ;
932+ await manager . setActiveSet ( "GlobalSet" ) ;
933+
934+ expect ( buttonSetWriter . writeActiveSet ) . toHaveBeenCalledWith (
935+ "GlobalSet" ,
936+ vscode . ConfigurationTarget . Global
937+ ) ;
938+ } ) ;
939+ } ) ;
940+
941+ describe ( "deleteButtonSet with case insensitivity" , ( ) => {
942+ it ( "should delete button set case-insensitively" , async ( ) => {
943+ const existingSets = createTestButtonSets ( ) ;
944+ const mockConfig = createMockConfig ( ) ;
945+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . WORKSPACE ) ;
946+ mockConfig . inspect . mockImplementation ( ( key : string ) => {
947+ if ( key === "buttonSets" ) {
948+ return {
949+ workspaceValue : existingSets . map ( ( s ) => ( { buttons : s . buttons , name : s . name } ) ) ,
950+ } ;
951+ }
952+ if ( key === "activeSet" ) {
953+ return { workspaceValue : null } ;
954+ }
955+ return { } ;
956+ } ) ;
957+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
958+ mockConfig as unknown as vscode . WorkspaceConfiguration
959+ ) ;
960+
961+ const configManager = createMockConfigManager ( ) ;
962+ const configReader = createMockConfigReader ( ) ;
963+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
964+
965+ const manager = ButtonSetManager . create ( { buttonSetWriter, configManager, configReader } ) ;
966+ await manager . deleteButtonSet ( "FRONTEND" ) ;
967+
968+ expect ( buttonSetWriter . writeButtonSets ) . toHaveBeenCalled ( ) ;
969+ const writtenSets = buttonSetWriter . writeButtonSets . mock . calls [ 0 ] [ 0 ] ;
970+ expect ( writtenSets ) . toHaveLength ( 1 ) ;
971+ expect ( writtenSets [ 0 ] . name ) . toBe ( "Backend" ) ;
972+ } ) ;
973+
974+ it ( "should delete from local storage when local scope" , async ( ) => {
975+ const localSets = createTestButtonSets ( ) ;
976+ const mockConfig = createMockConfig ( ) ;
977+ mockConfig . get . mockReturnValue ( CONFIGURATION_TARGETS . LOCAL ) ;
978+ vi . spyOn ( vscode . workspace , "getConfiguration" ) . mockReturnValue (
979+ mockConfig as unknown as vscode . WorkspaceConfiguration
980+ ) ;
981+
982+ const configManager = createMockConfigManager ( ) ;
983+ const configReader = createMockConfigReader ( ) ;
984+ const buttonSetWriter = createMockButtonSetWriter ( ) ;
985+ const buttonSetLocalStorage = createMockButtonSetLocalStorage ( ) ;
986+ buttonSetLocalStorage . getButtonSets . mockReturnValue ( localSets ) ;
987+ buttonSetLocalStorage . getActiveSet . mockReturnValue ( null ) ;
988+
989+ const manager = ButtonSetManager . create ( {
990+ buttonSetLocalStorage,
991+ buttonSetWriter,
992+ configManager,
993+ configReader,
994+ } ) ;
995+ await manager . deleteButtonSet ( "Frontend" ) ;
996+
997+ expect ( buttonSetLocalStorage . setButtonSets ) . toHaveBeenCalled ( ) ;
998+ expect ( buttonSetWriter . writeButtonSets ) . not . toHaveBeenCalled ( ) ;
999+ } ) ;
1000+ } ) ;
1001+
6441002 describe ( "renameButtonSet" , ( ) => {
6451003 it ( "should return setNotFound error when renaming non-existent set" , async ( ) => {
6461004 const existingSets = [ { buttons : [ ] , id : "set-1" , name : "SetA" } ] ;
0 commit comments