Skip to content

Commit 11612ca

Browse files
committed
Just make a copy when adding a new profile
1 parent 1649556 commit 11612ca

File tree

2 files changed

+158
-4
lines changed

2 files changed

+158
-4
lines changed

webview-ui/src/components/settings/ApiConfigManager.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ const ApiConfigManager = ({
3636
setInputValue("");
3737
}, [currentApiConfigName]);
3838

39-
const handleStartNew = () => {
40-
setEditState('new');
41-
setInputValue("");
39+
const handleAdd = () => {
40+
const newConfigName = currentApiConfigName + " (copy)";
41+
onUpsertConfig(newConfigName);
4242
};
4343

4444
const handleStartRename = () => {
@@ -162,7 +162,7 @@ const ApiConfigManager = ({
162162
</select>
163163
<VSCodeButton
164164
appearance="icon"
165-
onClick={handleStartNew}
165+
onClick={handleAdd}
166166
title="Add profile"
167167
style={{
168168
padding: 0,
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
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

Comments
 (0)