fix(web): satisfy provider model gates
This commit is contained in:
@@ -18,7 +18,7 @@ import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-web-react'
|
|||||||
import { CustomProviderCard } from './CustomProviderCard.tsx'
|
import { CustomProviderCard } from './CustomProviderCard.tsx'
|
||||||
import { deriveKeyRef, messageOf, protocolChoices } from './store.ts'
|
import { deriveKeyRef, messageOf, protocolChoices } from './store.ts'
|
||||||
import type { ModelsSettingsState, ModelsSettingsStore, ProviderRow } from './store.ts'
|
import type { ModelsSettingsState, ModelsSettingsStore, ProviderRow } from './store.ts'
|
||||||
import { ProviderEditor } from './ProviderEditor.tsx'
|
import { ProviderEditor, type ProviderEditorProps } from './ProviderEditor.tsx'
|
||||||
import type { en } from './locales.ts'
|
import type { en } from './locales.ts'
|
||||||
import styles from './ModelsSection.module.css'
|
import styles from './ModelsSection.module.css'
|
||||||
|
|
||||||
@@ -56,6 +56,26 @@ interface EditorTarget extends ProviderIdentity {
|
|||||||
credentialRef?: string
|
credentialRef?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Values that vary around the shared provider-editor rendering. */
|
||||||
|
interface ProviderEditorRenderProps extends Pick<
|
||||||
|
ProviderEditorProps,
|
||||||
|
'namespace' | 'api' | 't' | 'readOnly' | 'onClose'
|
||||||
|
> {
|
||||||
|
target: EditorTarget
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Render an editor for either the setup posture or an expanded provider row. */
|
||||||
|
function renderProviderEditor({ target, ...props }: ProviderEditorRenderProps): ReactNode {
|
||||||
|
return (
|
||||||
|
<ProviderEditor
|
||||||
|
provider={target.provider}
|
||||||
|
displayName={target.displayName}
|
||||||
|
settingsPath={target.settingsPath}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove one user-added provider and its page-managed credential. Credential
|
* Remove one user-added provider and its page-managed credential. Credential
|
||||||
* removal comes first so a second-step failure leaves the provider row visible
|
* removal comes first so a second-step failure leaves the provider row visible
|
||||||
@@ -232,16 +252,14 @@ function Loaded({ injected }: { injected: ModelsSectionInjected }): ReactNode {
|
|||||||
// setup card IS its presence on the page.
|
// setup card IS its presence on the page.
|
||||||
return (
|
return (
|
||||||
<li key={row.entry.provider} className={styles['setupCard']}>
|
<li key={row.entry.provider} className={styles['setupCard']}>
|
||||||
<ProviderEditor
|
{renderProviderEditor({
|
||||||
provider={target.provider}
|
target,
|
||||||
displayName={target.displayName}
|
namespace,
|
||||||
namespace={namespace}
|
api,
|
||||||
settingsPath={target.settingsPath}
|
t,
|
||||||
api={api}
|
readOnly: !state.writable,
|
||||||
t={t}
|
onClose: (changed) => { closeEditor(changed, target) },
|
||||||
readOnly={!state.writable}
|
})}
|
||||||
onClose={(changed) => { closeEditor(changed, target) }}
|
|
||||||
/>
|
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -312,18 +330,14 @@ function Loaded({ injected }: { injected: ModelsSectionInjected }): ReactNode {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{open
|
{open
|
||||||
? (
|
? renderProviderEditor({
|
||||||
<ProviderEditor
|
target,
|
||||||
provider={target.provider}
|
namespace,
|
||||||
displayName={target.displayName}
|
api,
|
||||||
namespace={namespace}
|
t,
|
||||||
settingsPath={target.settingsPath}
|
readOnly: !state.writable,
|
||||||
api={api}
|
onClose: (changed) => { closeEditor(changed, target) },
|
||||||
t={t}
|
})
|
||||||
readOnly={!state.writable}
|
|
||||||
onClose={(changed) => { closeEditor(changed, target) }}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
: null}
|
: null}
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ async function mountSection(options: Parameters<typeof scriptedFace>[0] = {}) {
|
|||||||
t,
|
t,
|
||||||
}
|
}
|
||||||
render(<ModelsSection {...injected} />)
|
render(<ModelsSection {...injected} />)
|
||||||
return scripted
|
return { ...scripted, controller }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Open the editor of one configured row and expand its customized fold. */
|
/** Open the editor of one configured row and expand its customized fold. */
|
||||||
@@ -862,4 +862,20 @@ describe('hand-declared providers', () => {
|
|||||||
await waitFor(() => { expect(screen.queryByText(en.customTitle)).toBeNull() })
|
await waitFor(() => { expect(screen.queryByText(en.customTitle)).toBeNull() })
|
||||||
expect(screen.getByRole('button', { name: en.customAdd })).toBeTruthy()
|
expect(screen.getByRole('button', { name: en.customAdd })).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('reloads the section after creating a hand-declared provider', async () => {
|
||||||
|
const { controller, mutate } = await mountSection()
|
||||||
|
const load = vi.spyOn(controller, 'load')
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: en.customAdd }))
|
||||||
|
fireEvent.change(screen.getByLabelText(en.customRoute), { target: { value: 'acme' } })
|
||||||
|
fireEvent.change(screen.getByLabelText(en.baseUrl), { target: { value: 'https://acme.test/v1' } })
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: en.addModel }))
|
||||||
|
fireEvent.change(screen.getByLabelText(`${en.modelId} 1`), { target: { value: 'm' } })
|
||||||
|
fireEvent.click(screen.getByText(en.create))
|
||||||
|
|
||||||
|
await waitFor(() => { expect(mutate).toHaveBeenCalledOnce() })
|
||||||
|
await waitFor(() => { expect(load).toHaveBeenCalledOnce() })
|
||||||
|
expect(screen.queryByText(en.customTitle)).toBeNull()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user