1+ import { render , screen , fireEvent } from '@testing-library/react' ;
2+ import '@testing-library/jest-dom' ;
3+ import ApiConfigManager from '../ApiConfigManager' ;
4+
5+ // Mock VSCode components
6+ jest . mock ( '@vscode/webview-ui-toolkit/react' , ( ) => ( {
7+ VSCodeButton : ( { children, onClick, title, disabled } : any ) => (
8+ < button onClick = { onClick } title = { title } disabled = { disabled } >
9+ { children }
10+ </ button >
11+ ) ,
12+ VSCodeTextField : ( { value, onInput, placeholder } : any ) => (
13+ < input
14+ value = { value }
15+ onChange = { e => onInput ( e ) }
16+ placeholder = { placeholder }
17+ ref = { undefined } // Explicitly set ref to undefined to avoid warning
18+ />
19+ ) ,
20+ } ) ) ;
21+
22+ describe ( 'ApiConfigManager' , ( ) => {
23+ const mockOnSelectConfig = jest . fn ( ) ;
24+ const mockOnDeleteConfig = jest . fn ( ) ;
25+ const mockOnRenameConfig = jest . fn ( ) ;
26+ const mockOnUpsertConfig = jest . fn ( ) ;
27+
28+ const defaultProps = {
29+ currentApiConfigName : 'Default Config' ,
30+ listApiConfigMeta : [
31+ { name : 'Default Config' } ,
32+ { name : 'Another Config' }
33+ ] ,
34+ onSelectConfig : mockOnSelectConfig ,
35+ onDeleteConfig : mockOnDeleteConfig ,
36+ onRenameConfig : mockOnRenameConfig ,
37+ onUpsertConfig : mockOnUpsertConfig ,
38+ } ;
39+
40+ beforeEach ( ( ) => {
41+ jest . clearAllMocks ( ) ;
42+ } ) ;
43+
44+ it ( 'immediately creates a copy when clicking add button' , ( ) => {
45+ render ( < ApiConfigManager { ...defaultProps } /> ) ;
46+
47+ // Find and click the add button
48+ const addButton = screen . getByTitle ( 'Add profile' ) ;
49+ fireEvent . click ( addButton ) ;
50+
51+ // Verify that onUpsertConfig was called with the correct name
52+ expect ( mockOnUpsertConfig ) . toHaveBeenCalledTimes ( 1 ) ;
53+ expect ( mockOnUpsertConfig ) . toHaveBeenCalledWith ( 'Default Config (copy)' ) ;
54+ } ) ;
55+
56+ it ( 'creates copy with correct name when current config has spaces' , ( ) => {
57+ render (
58+ < ApiConfigManager
59+ { ...defaultProps }
60+ currentApiConfigName = "My Test Config"
61+ />
62+ ) ;
63+
64+ const addButton = screen . getByTitle ( 'Add profile' ) ;
65+ fireEvent . click ( addButton ) ;
66+
67+ expect ( mockOnUpsertConfig ) . toHaveBeenCalledWith ( 'My Test Config (copy)' ) ;
68+ } ) ;
69+
70+ it ( 'handles empty current config name gracefully' , ( ) => {
71+ render (
72+ < ApiConfigManager
73+ { ...defaultProps }
74+ currentApiConfigName = ""
75+ />
76+ ) ;
77+
78+ const addButton = screen . getByTitle ( 'Add profile' ) ;
79+ fireEvent . click ( addButton ) ;
80+
81+ expect ( mockOnUpsertConfig ) . toHaveBeenCalledWith ( ' (copy)' ) ;
82+ } ) ;
83+
84+ it ( 'allows renaming the current config' , ( ) => {
85+ render ( < ApiConfigManager { ...defaultProps } /> ) ;
86+
87+ // Start rename
88+ const renameButton = screen . getByTitle ( 'Rename profile' ) ;
89+ fireEvent . click ( renameButton ) ;
90+
91+ // Find input and enter new name
92+ const input = screen . getByDisplayValue ( 'Default Config' ) ;
93+ fireEvent . input ( input , { target : { value : 'New Name' } } ) ;
94+
95+ // Save
96+ const saveButton = screen . getByTitle ( 'Save' ) ;
97+ fireEvent . click ( saveButton ) ;
98+
99+ expect ( mockOnRenameConfig ) . toHaveBeenCalledWith ( 'Default Config' , 'New Name' ) ;
100+ } ) ;
101+
102+ it ( 'allows selecting a different config' , ( ) => {
103+ render ( < ApiConfigManager { ...defaultProps } /> ) ;
104+
105+ const select = screen . getByRole ( 'combobox' ) ;
106+ fireEvent . change ( select , { target : { value : 'Another Config' } } ) ;
107+
108+ expect ( mockOnSelectConfig ) . toHaveBeenCalledWith ( 'Another Config' ) ;
109+ } ) ;
110+
111+ it ( 'allows deleting the current config when not the only one' , ( ) => {
112+ render ( < ApiConfigManager { ...defaultProps } /> ) ;
113+
114+ const deleteButton = screen . getByTitle ( 'Delete profile' ) ;
115+ expect ( deleteButton ) . not . toBeDisabled ( ) ;
116+
117+ fireEvent . click ( deleteButton ) ;
118+ expect ( mockOnDeleteConfig ) . toHaveBeenCalledWith ( 'Default Config' ) ;
119+ } ) ;
120+
121+ it ( 'disables delete button when only one config exists' , ( ) => {
122+ render (
123+ < ApiConfigManager
124+ { ...defaultProps }
125+ listApiConfigMeta = { [ { name : 'Default Config' } ] }
126+ />
127+ ) ;
128+
129+ const deleteButton = screen . getByTitle ( 'Cannot delete the only profile' ) ;
130+ expect ( deleteButton ) . toHaveAttribute ( 'disabled' ) ;
131+ } ) ;
132+
133+ it ( 'cancels rename operation when clicking cancel' , ( ) => {
134+ render ( < ApiConfigManager { ...defaultProps } /> ) ;
135+
136+ // Start rename
137+ const renameButton = screen . getByTitle ( 'Rename profile' ) ;
138+ fireEvent . click ( renameButton ) ;
139+
140+ // Find input and enter new name
141+ const input = screen . getByDisplayValue ( 'Default Config' ) ;
142+ fireEvent . input ( input , { target : { value : 'New Name' } } ) ;
143+
144+ // Cancel
145+ const cancelButton = screen . getByTitle ( 'Cancel' ) ;
146+ fireEvent . click ( cancelButton ) ;
147+
148+ // Verify rename was not called
149+ expect ( mockOnRenameConfig ) . not . toHaveBeenCalled ( ) ;
150+
151+ // Verify we're back to normal view
152+ expect ( screen . queryByDisplayValue ( 'New Name' ) ) . not . toBeInTheDocument ( ) ;
153+ } ) ;
154+ } ) ;
0 commit comments