33import { render , screen , fireEvent , within } from "@/utils/test-utils"
44import { QueryClient , QueryClientProvider } from "@tanstack/react-query"
55
6- import { type ModelInfo , type ProviderSettings , openAiModelInfoSaneDefaults } from "@roo-code/types"
7- import { openAiCodexDefaultModelId } from "@roo-code/types"
6+ import {
7+ type ModelInfo ,
8+ type ProviderSettings ,
9+ aetherapiDefaultModelId ,
10+ openAiCodexDefaultModelId ,
11+ openAiModelInfoSaneDefaults ,
12+ } from "@roo-code/types"
813
914import * as ExtensionStateContext from "@src/context/ExtensionStateContext"
1015const { ExtensionStateContextProvider } = ExtensionStateContext
@@ -13,10 +18,18 @@ import ApiOptions, { ApiOptionsProps } from "../ApiOptions"
1318
1419// Mock VSCode components
1520vi . mock ( "@vscode/webview-ui-toolkit/react" , ( ) => ( {
16- VSCodeTextField : ( { children, value, onBlur } : any ) => (
21+ VSCodeTextField : ( { children, value, onBlur, onInput , type , placeholder } : any ) => (
1722 < div >
1823 { children }
19- < input type = "text" value = { value } onChange = { onBlur } />
24+ < input
25+ type = { type ?? "text" }
26+ value = { value }
27+ placeholder = { placeholder }
28+ onChange = { ( event ) => {
29+ onInput ?.( event )
30+ onBlur ?.( event )
31+ } }
32+ />
2033 </ div >
2134 ) ,
2235 VSCodeLink : ( { children, href } : any ) => < a href = { href } > { children } </ a > ,
@@ -68,8 +81,8 @@ vi.mock("@/components/ui", () => ({
6881 </ option >
6982 ) ,
7083 SelectSeparator : ( { children } : any ) => < div className = "select-separator-mock" > { children } </ div > ,
71- Button : ( { children, onClick, _variant, role, className } : any ) => (
72- < button onClick = { onClick } className = { `button-mock ${ className || "" } ` } role = { role } >
84+ Button : ( { children, onClick, _variant, role, className, ... props } : any ) => (
85+ < button onClick = { onClick } className = { `button-mock ${ className || "" } ` } role = { role } { ... props } >
7386 { children }
7487 </ button >
7588 ) ,
@@ -86,8 +99,8 @@ vi.mock("@/components/ui", () => ({
8699 className = { className }
87100 />
88101 ) ,
89- CommandItem : ( { children, value, onSelect } : any ) => (
90- < div className = "command-item-mock" onClick = { ( ) => onSelect && onSelect ( value ) } >
102+ CommandItem : ( { children, value, onSelect, ... props } : any ) => (
103+ < div className = "command-item-mock" onClick = { ( ) => onSelect && onSelect ( value ) } { ... props } >
91104 { children }
92105 </ div >
93106 ) ,
@@ -242,13 +255,15 @@ vi.mock("@src/components/ui/hooks/useSelectedModel", () => ({
242255
243256 return {
244257 provider : apiConfiguration . apiProvider ,
258+ id : apiConfiguration . apiModelId ,
245259 info,
246260 }
247261 } else {
248262 const info : ModelInfo = { contextWindow : 4000 , supportsPromptCache : true }
249263
250264 return {
251265 provider : apiConfiguration . apiProvider ,
266+ id : apiConfiguration . apiModelId ,
252267 info,
253268 }
254269 }
@@ -300,6 +315,48 @@ describe("ApiOptions", () => {
300315 expect ( mockSetApiConfigurationField ) . toHaveBeenCalledWith ( "apiModelId" , openAiCodexDefaultModelId , false )
301316 } )
302317
318+ it ( "wires AetherAPI provider settings and model default" , ( ) => {
319+ const mockSetApiConfigurationField = vi . fn ( )
320+
321+ renderApiOptions ( {
322+ apiConfiguration : {
323+ apiProvider : "aetherapi" ,
324+ apiModelId : aetherapiDefaultModelId ,
325+ aetherapiApiKey : "" ,
326+ } ,
327+ setApiConfigurationField : mockSetApiConfigurationField ,
328+ } )
329+
330+ expect ( screen . getByText ( "settings:providers.aetherapiApiKey" ) ) . toBeInTheDocument ( )
331+ expect ( screen . getByText ( "settings:providers.getAetherapiApiKey" ) ) . toBeInTheDocument ( )
332+ expect ( screen . getByTestId ( "model-picker-button" ) ) . toHaveTextContent ( aetherapiDefaultModelId )
333+
334+ const apiKeyInput = screen . getByPlaceholderText ( "settings:placeholders.apiKey" )
335+ fireEvent . change ( apiKeyInput , { target : { value : "aether-key" } } )
336+
337+ expect ( mockSetApiConfigurationField ) . toHaveBeenCalledWith ( "aetherapiApiKey" , "aether-key" )
338+ } )
339+
340+ it ( "resets model to AetherAPI default when switching providers" , ( ) => {
341+ const mockSetApiConfigurationField = vi . fn ( )
342+
343+ renderApiOptions ( {
344+ apiConfiguration : {
345+ apiProvider : "anthropic" ,
346+ apiModelId : "claude-3-5-sonnet-20241022" ,
347+ } ,
348+ setApiConfigurationField : mockSetApiConfigurationField ,
349+ } )
350+
351+ const providerSelectContainer = screen . getByTestId ( "provider-select" )
352+ const providerSelect = providerSelectContainer . querySelector ( "select" ) as HTMLSelectElement
353+
354+ fireEvent . change ( providerSelect , { target : { value : "aetherapi" } } )
355+
356+ expect ( mockSetApiConfigurationField ) . toHaveBeenCalledWith ( "apiProvider" , "aetherapi" )
357+ expect ( mockSetApiConfigurationField ) . toHaveBeenCalledWith ( "apiModelId" , aetherapiDefaultModelId , false )
358+ } )
359+
303360 it ( "shows temperature and rate limit controls by default" , ( ) => {
304361 renderApiOptions ( {
305362 apiConfiguration : { } ,
0 commit comments