mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
94 lines
3.8 KiB
TypeScript
94 lines
3.8 KiB
TypeScript
// @vitest-environment jsdom
|
|
import { Context, Service } from '@deepseek-ai/cordis'
|
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { cleanup } from '@testing-library/react'
|
|
import { LocaleService } from '@deepseek-ai/dsh-client-locale/client'
|
|
import { SlotsService } from '@deepseek-ai/dsh-client-runtime/client'
|
|
import { resolveSlotLabel } from '@deepseek-ai/dsh-client-ui-slots'
|
|
import { usePinnedBrowserLanguages } from '@deepseek-ai/dsh-client-test-runtime'
|
|
import { apply, inject, NS } from '../src/client/index.ts'
|
|
import { PluginSettingsSection } from '../src/client/PluginSettingsSection.tsx'
|
|
import type { PluginSettingsSectionInjected } from '../src/client/PluginSettingsSection.tsx'
|
|
|
|
usePinnedBrowserLanguages('zh-CN')
|
|
afterEach(cleanup)
|
|
|
|
const EMPTY = { entries: [] }
|
|
type ListResult =
|
|
| { readonly ok: true; readonly value: typeof EMPTY }
|
|
| { readonly ok: false; readonly error: { readonly code: string; readonly message: string } }
|
|
|
|
async function bench() {
|
|
const ctx = new Context()
|
|
await ctx.plugin(SlotsService).await()
|
|
const locale = new LocaleService(ctx)
|
|
ctx.provide('locale', locale)
|
|
class RemoteService extends Service {
|
|
constructor(serviceCtx: Context) {
|
|
super(serviceCtx, 'remote')
|
|
}
|
|
}
|
|
new RemoteService(ctx)
|
|
const list = vi.fn<() => Promise<ListResult>>()
|
|
.mockResolvedValue({ ok: true, value: EMPTY })
|
|
ctx.provide('remote.pluginInventory', { list })
|
|
return { ctx, slots: ctx.get('slots') as SlotsService, locale, list }
|
|
}
|
|
|
|
function declare(slots: SlotsService): () => void {
|
|
return slots.register({
|
|
name: 'root',
|
|
children: { 'settings.section': { kind: 'list', scope: 'root' } },
|
|
} as never, () => null)
|
|
}
|
|
|
|
describe('ui-plugins browser plugin', () => {
|
|
it('declares only the services used by the Settings Remote contribution', () => {
|
|
expect(inject).toEqual(['slots', 'locale', 'remote', 'remote.pluginInventory'])
|
|
})
|
|
|
|
it('registers a localized section without reading the Remote eagerly', async () => {
|
|
const b = await bench()
|
|
declare(b.slots)
|
|
await b.ctx.plugin({ inject: [...inject], apply }).await()
|
|
|
|
const entry = b.slots.entries('settings.section')[0]!
|
|
expect(entry.component).toBe(PluginSettingsSection)
|
|
expect(entry.options).toMatchObject({ id: 'plugin-inventory', order: 15 })
|
|
expect(entry.locale).toBe(NS)
|
|
expect(resolveSlotLabel(entry.options.label)).toBe('插件')
|
|
expect(b.list).not.toHaveBeenCalled()
|
|
|
|
const injected = (entry.inject as unknown as () => PluginSettingsSectionInjected)()
|
|
await expect(injected.list()).resolves.toEqual(EMPTY)
|
|
expect(b.list).toHaveBeenCalledOnce()
|
|
b.list.mockResolvedValueOnce({ ok: false, error: { code: 'REMOTE_ERROR', message: 'unavailable' } })
|
|
await expect(injected.list()).rejects.toThrow('pluginInventory.list failed: REMOTE_ERROR: unavailable')
|
|
await b.ctx.fiber.dispose()
|
|
})
|
|
|
|
it('follows locale and recovers across late declaration and declarer reload', async () => {
|
|
const b = await bench()
|
|
const fiber = b.ctx.plugin({ inject: [...inject], apply })
|
|
await fiber.await()
|
|
expect(b.slots.entries('settings.section')).toHaveLength(0)
|
|
|
|
const stop = declare(b.slots)
|
|
await vi.waitFor(() => { expect(b.slots.entries('settings.section')).toHaveLength(1) })
|
|
b.locale.setLocale('en')
|
|
expect(resolveSlotLabel(b.slots.entries('settings.section')[0]!.options.label)).toBe('Plugins')
|
|
|
|
stop()
|
|
expect(b.slots.entries('settings.section')).toHaveLength(0)
|
|
declare(b.slots)
|
|
await vi.waitFor(() => {
|
|
expect(b.slots.entries('settings.section')[0]?.component).toBe(PluginSettingsSection)
|
|
})
|
|
|
|
await fiber.dispose()
|
|
expect(b.slots.entries('settings.section')).toHaveLength(0)
|
|
expect(() => b.locale.register(NS, 'zh', {})).not.toThrow()
|
|
await b.ctx.fiber.dispose()
|
|
})
|
|
})
|