feat(web): unify onboarding dialogs
This commit is contained in:
@@ -8,6 +8,7 @@ import { TestRemote, usePinnedBrowserLanguages } from '@deepseek-ai/dsh-client-t
|
||||
import { apply, inject, refreshIfLoaded } from '@deepseek-ai/dsh-client-ui-settings-models/client'
|
||||
import { ModelsSection } from '../src/client/ModelsSection.tsx'
|
||||
import { DeepSeekOnboardingDialog } from '../src/client/DeepSeekOnboardingDialog.tsx'
|
||||
import { WelcomeNotice } from '../src/client/WelcomeNotice.tsx'
|
||||
|
||||
// The service reads its initial locale from the browser; these specs assert
|
||||
// the shipped Chinese copy, so they state the browser they assume.
|
||||
@@ -23,7 +24,7 @@ async function bench() {
|
||||
new TestRemote(ctx)
|
||||
// The apply path only captures the wire face; no call leaves this fake
|
||||
// until a section actually loads.
|
||||
ctx.provide('connection', { api: {} } as never)
|
||||
ctx.provide('connection', { api: {}, isLoopback: true } as never)
|
||||
return { ctx, slots: ctx.get('slots') as SlotRegistry, locale }
|
||||
}
|
||||
|
||||
@@ -60,9 +61,20 @@ describe('ui-settings-models apply', () => {
|
||||
expect(typeof injected.controller.load).toBe('function')
|
||||
expect(typeof injected.useSnapshot).toBe('function')
|
||||
expect(injected.api).toBeDefined()
|
||||
const onboarding = before.slots.entries('settings.onboarding')[0]!
|
||||
expect(onboarding.component).toBe(DeepSeekOnboardingDialog)
|
||||
expect(onboarding.options).toMatchObject({ id: 'deepseek-official', order: 0 })
|
||||
const onboarding = before.slots.entries('settings.onboarding')
|
||||
expect(onboarding).toHaveLength(2)
|
||||
expect(onboarding.find(entry => entry.options.id === 'welcome-notice')).toMatchObject({
|
||||
component: WelcomeNotice,
|
||||
options: { id: 'welcome-notice', order: -100 },
|
||||
})
|
||||
const deepSeek = onboarding.find(entry => entry.options.id === 'deepseek-official')!
|
||||
expect(deepSeek.component).toBe(DeepSeekOnboardingDialog)
|
||||
expect(deepSeek.options).toMatchObject({ id: 'deepseek-official', order: 0 })
|
||||
const deepSeekInjected = (
|
||||
deepSeek.inject as unknown as () => import('../src/client/DeepSeekOnboardingDialog.tsx').DeepSeekOnboardingInjected
|
||||
)()
|
||||
expect(deepSeekInjected.hooks.models).toBe(injected.controller.store)
|
||||
expect(deepSeekInjected.api).toBeDefined()
|
||||
|
||||
const after = await bench()
|
||||
await after.ctx.plugin({ inject: [...inject], apply }).await()
|
||||
@@ -71,7 +83,7 @@ describe('ui-settings-models apply', () => {
|
||||
declare(after.slots)
|
||||
await Promise.resolve()
|
||||
expect(after.slots.entries('settings.section')[0]!.component).toBe(ModelsSection)
|
||||
expect(after.slots.entries('settings.onboarding')[0]!.component).toBe(DeepSeekOnboardingDialog)
|
||||
expect(after.slots.entries('settings.onboarding')).toHaveLength(2)
|
||||
// The self-inflicted ledger notifications hit the duplicate guard.
|
||||
expect(after.slots.entries('settings.section')).toHaveLength(1)
|
||||
})
|
||||
@@ -110,7 +122,7 @@ describe('ui-settings-models apply', () => {
|
||||
declare(b.slots)
|
||||
await Promise.resolve()
|
||||
expect(b.slots.entries('settings.section')[0]!.component).toBe(ModelsSection)
|
||||
expect(b.slots.entries('settings.onboarding')[0]!.component).toBe(DeepSeekOnboardingDialog)
|
||||
expect(b.slots.entries('settings.onboarding')).toHaveLength(2)
|
||||
// The locale path also recovers through the same ledger re-check.
|
||||
b.locale.setLocale('en')
|
||||
expect(resolveSlotLabel(b.slots.entries('settings.section')[0]!.options.label)).toBe('Models')
|
||||
@@ -164,8 +176,10 @@ describe('pushed invalidations', () => {
|
||||
const b = await bench()
|
||||
declare(b.slots)
|
||||
await b.ctx.plugin({ inject: [...inject], apply }).await()
|
||||
const entry = b.slots.entries('settings.onboarding')
|
||||
.find(candidate => candidate.options.id === 'deepseek-official')!
|
||||
const injected = (
|
||||
b.slots.entries('settings.onboarding')[0]!.inject as unknown as
|
||||
entry.inject as unknown as
|
||||
() => import('../src/client/DeepSeekOnboardingDialog.tsx').DeepSeekOnboardingInjected
|
||||
)()
|
||||
injected.controller.store.update((state) => { state.status = 'ready' })
|
||||
@@ -173,4 +187,25 @@ describe('pushed invalidations', () => {
|
||||
b.ctx.remote.$dispatch('credentials/updated', ['DEEPSEEK_API_KEY'])
|
||||
expect(load).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('routes only the onboarding namespace invalidation into welcome state', async () => {
|
||||
const b = await bench()
|
||||
declare(b.slots)
|
||||
await b.ctx.plugin({ inject: [...inject], apply }).await()
|
||||
const entry = b.slots.entries('settings.onboarding')
|
||||
.find(candidate => candidate.options.id === 'welcome-notice')!
|
||||
const injected = (
|
||||
entry.inject as unknown as
|
||||
() => import('../src/client/WelcomeNotice.tsx').WelcomeNoticeInjected
|
||||
)()
|
||||
injected.hooks.welcome.update((state) => { state.status = 'ready' })
|
||||
const load = vi.spyOn(injected.controller, 'load').mockResolvedValue()
|
||||
|
||||
b.ctx.remote.$dispatch('settings/document-updated', ['llm-deepseek', 1])
|
||||
expect(load).not.toHaveBeenCalled()
|
||||
b.ctx.remote.$dispatch('settings/document-updated', ['ui-onboarding', 2])
|
||||
expect(load).toHaveBeenCalledOnce()
|
||||
b.ctx.emit('connection/reset')
|
||||
expect(load).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user