mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
93 lines
3.6 KiB
TypeScript
93 lines
3.6 KiB
TypeScript
// @vitest-environment jsdom
|
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { act, cleanup } from '@testing-library/react'
|
|
import { Context } from '@deepseek-ai/cordis'
|
|
import { SlotRegistry } from '@deepseek-ai/dsh-client-runtime/client'
|
|
import { TestSessions, TestWorkspaces } from '@deepseek-ai/dsh-client-test-runtime'
|
|
import type { Stabilizer } from '@deepseek-ai/dsh-client-test-runtime'
|
|
import { apply as nodeApply } from '@deepseek-ai/dsh-client-ui-renderer'
|
|
import * as UiRenderer from '../src/client/index.ts'
|
|
import { locale } from './locale.client.ts'
|
|
|
|
const mounted: (() => void)[] = []
|
|
|
|
afterEach(() => {
|
|
act(() => { for (const unmount of mounted.splice(0)) unmount() })
|
|
vi.restoreAllMocks()
|
|
cleanup()
|
|
document.body.innerHTML = ''
|
|
})
|
|
|
|
const stabilize: Stabilizer = async (fn) => { await act(async () => { await fn() }) }
|
|
|
|
async function bench() {
|
|
const ctx = new Context()
|
|
await ctx.plugin(SlotRegistry).await()
|
|
const slots = ctx.get('slots') as SlotRegistry
|
|
ctx.provide('sessions', new TestSessions(stabilize, ctx))
|
|
ctx.provide('workspaces', new TestWorkspaces(stabilize))
|
|
ctx.provide('locale' as never, locale as never)
|
|
const fiber = ctx.plugin({ inject: [...UiRenderer.inject], apply: UiRenderer.apply })
|
|
await fiber.await()
|
|
return { ctx, slots, fiber }
|
|
}
|
|
|
|
function container(): HTMLElement {
|
|
const el = document.createElement('div')
|
|
document.body.append(el)
|
|
return el
|
|
}
|
|
|
|
describe('UI renderer plugin', () => {
|
|
it('provides no host-side behavior', () => {
|
|
expect(() => { nodeApply() }).not.toThrow()
|
|
})
|
|
|
|
it('installs the renderer and mounts the assembled application', async () => {
|
|
const { ctx, slots } = await bench()
|
|
slots.register({ name: 'root' }, () => <div data-testid="root-probe" />)
|
|
const renderer = ctx.get('uiRenderer')
|
|
expect(renderer).toBeDefined()
|
|
const el = container()
|
|
act(() => { mounted.push(renderer!.mount(el)) })
|
|
expect(el.querySelector('[data-testid="root-probe"]')).toBeTruthy()
|
|
})
|
|
|
|
it('hydrates the boot page before switching to the assembled application', async () => {
|
|
const { ctx, slots } = await bench()
|
|
slots.register({ name: 'root' }, () => <div data-testid="root-probe" />)
|
|
const el = container()
|
|
el.innerHTML = '<div class="boot" data-dsh-boot=""><div><div class="spinner" data-dsh-boot-spinner="" style="--dsh-boot-arc: 180deg"></div><div>Loading plugins…</div></div></div>'
|
|
const boot = el.firstElementChild
|
|
const observer = new MutationObserver(() => {})
|
|
observer.observe(el, { childList: true, subtree: true })
|
|
const error = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
|
|
act(() => { mounted.push(ctx.get('uiRenderer')!.mount(el)) })
|
|
|
|
const records = observer.takeRecords()
|
|
observer.disconnect()
|
|
expect(error).not.toHaveBeenCalled()
|
|
error.mockRestore()
|
|
expect(el.querySelector('[data-testid="root-probe"]')).toBeTruthy()
|
|
expect(records.some(record => record.target === boot)).toBe(false)
|
|
})
|
|
|
|
it('returns an unmount disposer', async () => {
|
|
const { ctx, slots } = await bench()
|
|
slots.register({ name: 'root' }, () => <div data-testid="root-probe" />)
|
|
const el = container()
|
|
let unmount: () => void = () => {}
|
|
act(() => { unmount = ctx.get('uiRenderer')!.mount(el) })
|
|
act(() => { unmount() })
|
|
expect(el.querySelector('[data-testid="root-probe"]')).toBeNull()
|
|
})
|
|
|
|
it('retracts the service and renderer with its fiber', async () => {
|
|
const { ctx, slots, fiber } = await bench()
|
|
await stabilize(() => fiber.dispose())
|
|
expect(ctx.get('uiRenderer')).toBeUndefined()
|
|
expect(() => slots.renderSlot('root', {})).toThrow('not installed')
|
|
})
|
|
})
|