fix(desktop): clear shared credential reveal state

This commit is contained in:
Ayush Nangia
2026-09-01 22:27:18 +05:30
committed by brooklyn!
parent 286e3c148c
commit 3213c984f3
2 changed files with 48 additions and 6 deletions

View File

@@ -98,8 +98,8 @@ export function useEnvCredentials(profile?: string): UseEnvCredentials {
setVars(c => (c ? { ...c, [key]: { ...c[key], ...patch } } : c))
}
function clearLocalState(key: string) {
setEdits(c => withoutKey(c, key))
function clearLocalState(key: string, editKey = key) {
setEdits(c => withoutKey(c, editKey))
setRevealed(c => withoutKey(c, key))
}
@@ -115,7 +115,7 @@ export function useEnvCredentials(profile?: string): UseEnvCredentials {
try {
await setEnvVar(key, value, profile)
patchVar(key, { is_set: true, redacted_value: redactedValue(value) })
clearLocalState(editKey)
clearLocalState(key, editKey)
void queryClient.invalidateQueries({ queryKey: ['model-options'] })
notify({ kind: 'success', title: toolsets.savedTitle, message: toolsets.savedMessage(key) })
} catch (err) {
@@ -164,7 +164,7 @@ export function useEnvCredentials(profile?: string): UseEnvCredentials {
try {
await deleteEnvVar(key, profile)
patchVar(key, { is_set: false, redacted_value: null })
clearLocalState(editKey)
clearLocalState(key, editKey)
void queryClient.invalidateQueries({ queryKey: ['model-options'] })
notify({ kind: 'success', title: toolsets.removedTitle, message: toolsets.removedMessage(key) })
} catch (err) {

View File

@@ -9,6 +9,7 @@ import type { EnvVarInfo, OAuthProvider } from '@/types/hermes'
const listOAuthProviders = vi.fn()
const disconnectOAuthProvider = vi.fn()
const getEnvVars = vi.fn()
const revealEnvVar = vi.fn()
const setEnvVar = vi.fn()
const startManualProviderOAuth = vi.fn()
const startManualLocalEndpoint = vi.fn()
@@ -25,10 +26,11 @@ vi.mock('@/store/profile', () => ({
vi.mock('@/hermes', () => ({
setApiRequestProfile: vi.fn(),
getProfiles: async () => ({ profiles: (await import('@/store/profile')).$profiles.get() }),
setEnvVar: (key: string, value: string, profile?: string) => setEnvVar(key, value, profile),
disconnectOAuthProvider: (...args: unknown[]) => disconnectOAuthProvider(...args),
getEnvVars: (...args: unknown[]) => getEnvVars(...args),
listOAuthProviders: (...args: unknown[]) => listOAuthProviders(...args)
listOAuthProviders: (...args: unknown[]) => listOAuthProviders(...args),
revealEnvVar: (key: string, profile?: string) => revealEnvVar(key, profile),
setEnvVar: (key: string, value: string, profile?: string) => setEnvVar(key, value, profile)
}))
vi.mock('@/store/onboarding', () => ({
@@ -81,6 +83,8 @@ beforeEach(() => {
onboarding.set({ manual: false })
getEnvVars.mockResolvedValue({})
disconnectOAuthProvider.mockResolvedValue({ ok: true, provider: 'nous' })
revealEnvVar.mockResolvedValue({ value: 'old-secret' })
setEnvVar.mockResolvedValue({ ok: true })
listOAuthProviders.mockResolvedValue({
providers: [provider('nous', true), provider('minimax-oauth', false)]
})
@@ -279,6 +283,44 @@ describe('ProvidersSettings', () => {
expect((inputs[1] as HTMLInputElement).value).toBe('')
})
it('clears the shared reveal when a namespaced provider-card draft is saved', async () => {
const varKey = 'DASHSCOPE_API_KEY'
const editKey = `Qwen Cloud:${varKey}`
getEnvVars.mockResolvedValue({
[varKey]: keyVar({ is_set: true, redacted_value: '••••••••' })
})
const { useEnvCredentials } = await import('./env-credentials')
const state = { current: null as null | ReturnType<typeof useEnvCredentials> }
function Harness() {
state.current = useEnvCredentials()
return null
}
render(<Harness />)
await waitFor(() => expect(state.current?.vars).not.toBeNull())
await act(async () => {
await Promise.resolve(state.current!.rowProps.onReveal(varKey))
})
expect(state.current!.rowProps.revealed[varKey]).toBe('old-secret')
act(() => {
state.current!.rowProps.setEdits(current => ({ ...current, [editKey]: 'new-secret' }))
})
await waitFor(() => expect(state.current!.rowProps.edits[editKey]).toBe('new-secret'))
await act(async () => {
await Promise.resolve(state.current!.rowProps.onSave(varKey, editKey))
})
expect(setEnvVar).toHaveBeenCalledWith(varKey, 'new-secret', undefined)
expect(state.current!.rowProps.edits[editKey]).toBeUndefined()
expect(state.current!.rowProps.revealed[varKey]).toBeUndefined()
})
it('orders API-key providers by priority then name, and filters them via search', async () => {
// These three providers have no curated PROVIDER_GROUPS priority, so they
// share the default priority and fall back to alphabetical among themselves