description: "jsdom slot test runtime for browser feature specs, for test authors exercising slots, stores, and rendering against production machinery."
English | 中文
dsh-client-test-runtime gives a browser feature spec a real jsdom test bench: it assembles a Cordis context, the renderer-owned slot registry, and the production UiSession adapter around typed Session and Workspace Controller doubles. A default file-upload stub satisfies features that declare the service and rejects if a test starts an upload without replacing it. Feature suites exercise declaration, registration, scoping, stores, injection, rendering, updates, and disposal without copying production renderer or adapter logic. Suites publish Session lifecycle state, Workspace state, projection values, and Conversation events through typed fixtures, then use local DOM snapshot roots, scoped Testing Library queries, and fail-loud service checks. It is not part of the product plugin graph (no dsh.client); feature packages depend on it in devDependencies only.
This package gives a browser feature spec a real runtime to mount against: create the bench, declare the slots your feature occupies, mount the feature plugin, render a slot, assert on the local view, and dispose — with no second implementation of production logic.
SlotTestRuntime.create() assembles the runtime, declare(children) registers an auto frame whose per-key <div data-slot> wrappers become snapshot roots, mount(plugin) runs the feature on a real fiber, and renderSlot(key, owner) returns the slot-local view with scoped queries and in-place updates:
const runtime = await SlotTestRuntime.create()
await runtime.declare({ 'feature-slot': {} })
const handle = await runtime.mount(FeaturePlugin)
const view = runtime.renderSlot('feature-slot', { owner: props })
expect(view.container).toMatchSnapshot()
await runtime.dispose()
mount prechecks required services and fails loud when one is missing — provide(name, value) supplies an extra service first. The runtime provides an unavailable fileUpload stub so assemblies can mount; replace runtime.fileUpload.upload before mounting when a test exercises upload behavior. storeOf(key, scopeKey) returns the live store instance the renderer hands a slot's component for identity and action-driven-write assertions.
A registered snapshot serializer folds CSS-module class hashes (_frame_a1b2c3 → frame) so .snap files stay structural, and collapses <svg> internals to a data-content fingerprint. Suites needing a custom page frame use root.declare(children, Frame) instead of the auto frame; dispose() tears down views, feature fibers, minted scopes, and persisted store state on one axis and is idempotent.
TestRemote is the double for the ctx.remote face: it registers itself plus one service per scripted namespace so a plugin injecting remote.<name> unparks, drives $on subscriptions from an explicit test event driver, and exposes $host as a plain mutable field a spec assigns to script a homed or non-loopback Host. This package is also where a UI spec takes the RemoteError constructor as a value — the dsh-api-remotes facade cannot carry it, because a value import from a spec would pull that assembly's unbuilt /remote artifact chain.
Script a failure by the code the Host would answer with, and assert the same way production code discriminates — on code, never on the class:
import { RemoteError } from '@deepseek-ai/dsh-client-test-runtime'
remote.goals.create.mockResolvedValue({
ok: false,
error: new RemoteError('goal/not-found', 'goal "g1" does not exist', { goalId: 'g1' }),
})
expect(view.getByRole('alert')).toHaveTextContent('goal/not-found')
Use the bench for feature suites that exercise slots, stores, rendering, and disposal under a real runtime — the production SlotRegistry, renderer, and provide-bundle materialization are mounted, never reimplemented. It is browser-side test infrastructure: it never reaches a model request, and feature packages depend on it in devDependencies only.
mount fails loud with the missing names; provide() them first.declare — renderSlot fails loud; declare the key first.Read these pages when the package-level contract is not enough. They move from the bench to the production machinery it mounts and the tests that use it.
SlotRegistry contract the bench mounts.None, as this package is browser-side test infrastructure; nothing here reaches a model request.
None; this package neither assembles nor sends a provider request.
These limits define how the bench is consumed. They are current package constraints, not a task backlog.
sessionSnapshot contains only Session Controller state, conversationSnapshot contains target-neutral Conversation state, and chatSnapshot contains Chat target state. Assembly tests provide Session event entries instead of adding Conversation or Chat fields to SessionSnapshot.