Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions webview-ui/src/components/settings/SettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
const contentRef = useRef<HTMLDivElement | null>(null)

const prevApiConfigName = useRef(currentApiConfigName)
const handledSettingsImportedAt = useRef<number | undefined>(undefined)
const confirmDialogHandler = useRef<() => void>()

const [cachedState, setCachedState] = useState(() => extensionState)
Expand Down Expand Up @@ -233,10 +234,13 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t

// Bust the cache when settings are imported.
useEffect(() => {
if (settingsImportedAt) {
setCachedState((prevCachedState) => ({ ...prevCachedState, ...extensionState }))
setChangeDetected(false)
if (!settingsImportedAt || handledSettingsImportedAt.current === settingsImportedAt) {
return
}

handledSettingsImportedAt.current = settingsImportedAt
setCachedState((prevCachedState) => ({ ...prevCachedState, ...extensionState }))
setChangeDetected(false)
}, [settingsImportedAt, extensionState])

const setCachedStateField: SetCachedStateField<keyof ExtensionStateContextType> = useCallback((field, value) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import React from "react"

// Mock vscode API
const mockPostMessage = vi.fn()
const mockPostMessage = vi.hoisted(() => vi.fn())
const mockVscode = {
postMessage: mockPostMessage,
}
;(global as any).acquireVsCodeApi = () => mockVscode

vi.mock("@src/utils/vscode", () => ({
vscode: {
postMessage: mockPostMessage,
},
}))

// Import the actual component
import SettingsView from "../SettingsView"
import { useExtensionState } from "@src/context/ExtensionStateContext"
Expand Down Expand Up @@ -187,7 +193,24 @@ vi.mock("../ApiConfigManager", () => ({
}))

vi.mock("../ApiOptions", () => ({
default: () => null,
default: ({ apiConfiguration, setApiConfigurationField }: any) => (
<div>
<span data-testid="provider-value">{apiConfiguration.apiProvider}</span>
<input
data-testid="baseten-api-key"
value={apiConfiguration.basetenApiKey ?? ""}
onChange={(event) => setApiConfigurationField("basetenApiKey", event.target.value)}
/>
{["openrouter", "baseten", "deepseek"].map((provider) => (
<button
key={provider}
data-testid={`set-provider-${provider}`}
onClick={() => setApiConfigurationField("apiProvider", provider)}>
{provider}
</button>
))}
</div>
),
}))

vi.mock("../AutoApproveSettings", () => ({
Expand Down Expand Up @@ -369,4 +392,74 @@ describe("SettingsView - Change Detection Fix", () => {

expect(true).toBe(true) // Placeholder - the real test is the running system
})

it("preserves a DeepSeek provider edit after saving Baseten when the same import timestamp replays", async () => {
const onDone = vi.fn()
let extensionState = createExtensionState({
settingsImportedAt: 123,
apiConfiguration: {
apiProvider: "openai",
apiModelId: "gpt-4.1",
},
})

;(useExtensionState as any).mockImplementation(() => extensionState)

const { rerender } = render(
<QueryClientProvider client={queryClient}>
<SettingsView onDone={onDone} />
</QueryClientProvider>,
)

await waitFor(() => {
expect(screen.getByTestId("provider-value")).toHaveTextContent("openai")
})

fireEvent.click(screen.getByTestId("set-provider-baseten"))
fireEvent.change(screen.getByTestId("baseten-api-key"), { target: { value: "test-baseten-key" } })
expect(screen.getByTestId("provider-value")).toHaveTextContent("baseten")

mockPostMessage.mockClear()
fireEvent.click(screen.getByTestId("save-button"))
expect(mockPostMessage).toHaveBeenCalledWith({
type: "upsertApiConfiguration",
text: "default",
apiConfiguration: expect.objectContaining({
apiProvider: "baseten",
basetenApiKey: "test-baseten-key",
}),
})

fireEvent.click(screen.getByTestId("set-provider-deepseek"))
expect(screen.getByTestId("provider-value")).toHaveTextContent("deepseek")

extensionState = createExtensionState({
settingsImportedAt: 123,
soundEnabled: true,
apiConfiguration: {
apiProvider: "baseten",
apiModelId: "zai-org/GLM-4.6",
basetenApiKey: "test-baseten-key",
},
})

rerender(
<QueryClientProvider client={queryClient}>
<SettingsView onDone={onDone} />
</QueryClientProvider>,
)

expect(screen.getByTestId("provider-value")).toHaveTextContent("deepseek")

mockPostMessage.mockClear()
fireEvent.click(screen.getByTestId("save-button"))

expect(mockPostMessage).toHaveBeenCalledWith({
type: "upsertApiConfiguration",
text: "default",
apiConfiguration: expect.objectContaining({
apiProvider: "deepseek",
}),
})
})
})