A test file under packages/client now says which face it covers:
`*.client.spec.{ts,tsx}` and its `*.client.{ts,tsx}` helpers belong to the
Client aggregate, `*.host.spec.ts` to the host aggregate. The carrier's four
node-half specs take the Host suffix.
The two suffixes are mutually exclusive, so each aggregate excludes the
other's and both keep one broad test glob: `exclude` wins over `include`, and
`packages/client/**` no longer has to be excluded wholesale from the host
program with per-file `files` entries carved back out of it. A Host-face spec
that reaches only Host source therefore needs no cross-face project
reference, which the split-project rule rejects.
vitest still discovers every file through `**/*.spec.{ts,tsx}`.
54 lines
2.2 KiB
TypeScript
54 lines
2.2 KiB
TypeScript
// Dynamic-key escape hatches and untouched-key behavior of the terminal core.
|
|
import { describe, expect, it } from 'vitest'
|
|
import type { SlotComponent } from '@deepseek-ai/dsh-client-ui-slots'
|
|
import { SlotCore } from '@deepseek-ai/dsh-client-ui-slots'
|
|
|
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
interface SlotMap {
|
|
'dynamic.a': { kind: 'single'; scope: 'root' }
|
|
'dynamic.b': { kind: 'single'; scope: 'root' }
|
|
'surface.injected': { kind: 'single'; scope: 'root'; inject: { token: string } }
|
|
}
|
|
}
|
|
|
|
const Comp: SlotComponent<object> = () => null
|
|
|
|
describe('dynamic-key escape hatch', () => {
|
|
it('specDynamic reads wide-typed specs for string keys; undefined while undeclared', () => {
|
|
const core = new SlotCore()
|
|
expect(core.specDynamic('dynamic.a')).toBeUndefined()
|
|
core.register({ name: 'root', children: { 'dynamic.a': { kind: 'single', scope: 'root' } } }, Comp as never)
|
|
expect(core.specDynamic('dynamic.a')).toEqual({ kind: 'single', scope: 'root' })
|
|
expect(core.specDynamic('never.declared')).toBeUndefined()
|
|
})
|
|
|
|
it('spec() narrows by SlotMap key', () => {
|
|
const core = new SlotCore()
|
|
core.register({ name: 'root', children: { 'dynamic.a': { kind: 'single', scope: 'root' } } }, Comp as never)
|
|
expect(core.spec('dynamic.a')).toEqual({ kind: 'single', scope: 'root' })
|
|
expect(core.spec('dynamic.b')).toBeUndefined()
|
|
})
|
|
|
|
it('records the parent-declared Slot inject on the runtime spec', () => {
|
|
const core = new SlotCore()
|
|
const inject = { token: 'shared' }
|
|
core.register({
|
|
name: 'root',
|
|
children: { 'surface.injected': { kind: 'single', scope: 'root', inject } },
|
|
}, Comp as never)
|
|
expect(core.spec('surface.injected')?.inject).toBe(inject)
|
|
})
|
|
|
|
it('entries/getVersion on an untouched key return the frozen empty array and 0', () => {
|
|
const core = new SlotCore()
|
|
expect(core.entries('dynamic.b')).toHaveLength(0)
|
|
expect(core.entries('dynamic.b')).toBe(core.entries('dynamic.b'))
|
|
expect(core.getVersion('dynamic.b')).toBe(0)
|
|
})
|
|
|
|
it('isLive is false for entries the core never held', () => {
|
|
const core = new SlotCore()
|
|
expect(core.isLive({ component: Comp, options: {} })).toBe(false)
|
|
})
|
|
})
|