From 3f7a8c6f8ebf3fc13e4500ddd47e8ccfc1581ce7 Mon Sep 17 00:00:00 2001
From: imccyu <276526105+imccyu@users.noreply.github.com>
Date: Tue, 28 Jul 2026 23:04:22 +0800
Subject: [PATCH] feat(client): add the dsh-client-test-runtime package
A jsdom slot test runtime for feature specs: a real Cordis Context, the
production SlotsService and web-react renderer, and typed session/workspace
doubles (TestSessions implements ISessions with FixtureSession sessions;
TestWorkspaces implements IWorkspaces), so the compiler flags fixture drift
when a production face changes. Fixtures feed plain data: list rows,
conversation snapshots, and ISession-typed behavior stubs; provide-bundle
materialization runs the shared SessionProvideChannel.
DOM snapshot support: declare()/renderSlot() mount a single slot inside a
data-slot wrapper for local .snap capture, and a snapshot serializer folds
CSS-module class hashes to their semantic locals and collapses svg internals
to a content fingerprint. The typed provide() constrains declared-service
fakes to Partial of the service's outward face.
---
docs/config-catalog.md | 1 +
docs/module-graph.md | 6 +
packages/client/test-runtime/README.i18n.yaml | 6 +
packages/client/test-runtime/README.md | 24 +
packages/client/test-runtime/README.zh.md | 24 +
packages/client/test-runtime/package.json | 54 ++
packages/client/test-runtime/src/fixtures.ts | 82 +++
packages/client/test-runtime/src/index.ts | 372 ++++++++++++
packages/client/test-runtime/src/invariant.ts | 32 +
packages/client/test-runtime/src/sessions.ts | 390 +++++++++++++
packages/client/test-runtime/src/snapshot.ts | 89 +++
.../client/test-runtime/src/workspaces.ts | 147 +++++
.../tests/__snapshots__/runtime.spec.tsx.snap | 37 ++
.../test-runtime/tests/invariant.spec.ts | 12 +
.../test-runtime/tests/runtime.spec.tsx | 546 ++++++++++++++++++
packages/client/test-runtime/tsconfig.json | 27 +
pnpm-lock.yaml | 76 +++
.../verify-package-readme-model-experience.ts | 1 +
tsconfig.base.json | 1 +
tsconfig.client.json | 1 +
20 files changed, 1928 insertions(+)
create mode 100644 packages/client/test-runtime/README.i18n.yaml
create mode 100644 packages/client/test-runtime/README.md
create mode 100644 packages/client/test-runtime/README.zh.md
create mode 100644 packages/client/test-runtime/package.json
create mode 100644 packages/client/test-runtime/src/fixtures.ts
create mode 100644 packages/client/test-runtime/src/index.ts
create mode 100644 packages/client/test-runtime/src/invariant.ts
create mode 100644 packages/client/test-runtime/src/sessions.ts
create mode 100644 packages/client/test-runtime/src/snapshot.ts
create mode 100644 packages/client/test-runtime/src/workspaces.ts
create mode 100644 packages/client/test-runtime/tests/__snapshots__/runtime.spec.tsx.snap
create mode 100644 packages/client/test-runtime/tests/invariant.spec.ts
create mode 100644 packages/client/test-runtime/tests/runtime.spec.tsx
create mode 100644 packages/client/test-runtime/tsconfig.json
diff --git a/docs/config-catalog.md b/docs/config-catalog.md
index b5e320f7b7..7ff7f0faf2 100644
--- a/docs/config-catalog.md
+++ b/docs/config-catalog.md
@@ -2230,6 +2230,7 @@ Imported as libraries by other packages; a `cordis.yml` cannot load them.
- `@deepseek-ai/dsh-agent-loop-testkit` ([`packages/support/agent-loop-testkit/src/index.ts`](../packages/support/agent-loop-testkit/src/index.ts))
- `@deepseek-ai/dsh-app-boot` ([`packages/ui/app-boot/src/index.ts`](../packages/ui/app-boot/src/index.ts))
- `@deepseek-ai/dsh-brand` ([`packages/util/brand/src/index.ts`](../packages/util/brand/src/index.ts))
+- `@deepseek-ai/dsh-client-test-runtime` ([`packages/client/test-runtime/src/index.ts`](../packages/client/test-runtime/src/index.ts))
- `@deepseek-ai/dsh-client-ui-primitives` ([`packages/client/ui-primitives/src/index.ts`](../packages/client/ui-primitives/src/index.ts))
- `@deepseek-ai/dsh-client-ui-slots` ([`packages/client/ui-slots/src/index.ts`](../packages/client/ui-slots/src/index.ts))
- `@deepseek-ai/dsh-client-web` ([`packages/client/web/src/index.ts`](../packages/client/web/src/index.ts))
diff --git a/docs/module-graph.md b/docs/module-graph.md
index 255202bbae..33e63fc805 100644
--- a/docs/module-graph.md
+++ b/docs/module-graph.md
@@ -141,6 +141,7 @@ flowchart TD
pkg_client_locale["client-locale"]
pkg_client_modules["client-modules"]
pkg_client_runtime["client-runtime"]
+ pkg_client_test_runtime["client-test-runtime"]
pkg_client_ui_command["client-ui-command"]
pkg_client_ui_conversation["client-ui-conversation"]
pkg_client_ui_layout["client-ui-layout"]
@@ -276,6 +277,10 @@ flowchart TD
pkg_client_locale --> pkg_client_ui_primitives
pkg_client_locale --> pkg_client_ui_slots
pkg_client_locale --> pkg_invariants
+ pkg_client_test_runtime --> pkg_client_runtime
+ pkg_client_test_runtime --> pkg_client_ui_slots
+ pkg_client_test_runtime --> pkg_client_web_react
+ pkg_client_test_runtime --> pkg_invariants
pkg_client_ui_models --> pkg_client_runtime
pkg_client_ui_models --> pkg_client_ui_slots
pkg_client_ui_models --> pkg_invariants
@@ -946,6 +951,7 @@ flowchart TD
| [`client-connection`](../packages/client/connection) | `client` | [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) |
| [`client-hmr`](../packages/client/hmr) | `client` | [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) |
| [`client-locale`](../packages/client/locale) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
+| [`client-test-runtime`](../packages/client/test-runtime) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-slots`](../packages/client/ui-slots), [`client-web-react`](../packages/client/web-react), [`invariants`](../packages/support/invariants) |
| [`client-ui-models`](../packages/client/ui-models) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-settings`](../packages/client/ui-settings) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-sidebar`](../packages/client/ui-sidebar) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
diff --git a/packages/client/test-runtime/README.i18n.yaml b/packages/client/test-runtime/README.i18n.yaml
new file mode 100644
index 0000000000..73a4705b1b
--- /dev/null
+++ b/packages/client/test-runtime/README.i18n.yaml
@@ -0,0 +1,6 @@
+# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
+# side as of the last confirmed-consistent state. Both languages carry equal authority;
+# after editing either side, bring the other along and re-record with:
+# pnpm run verify-translation-pairing --write packages/client/test-runtime/README.md
+README.md: 883d71224139dc409229fdfb35362d040e810cc7
+README.zh.md: a3daf112940b03b585d44bc5fd1317e43ebd35fe
diff --git a/packages/client/test-runtime/README.md b/packages/client/test-runtime/README.md
new file mode 100644
index 0000000000..883d712241
--- /dev/null
+++ b/packages/client/test-runtime/README.md
@@ -0,0 +1,24 @@
+# @deepseek-ai/dsh-client-test-runtime
+
+English | [中文](README.zh.md)
+
+jsdom slot test runtime for client feature specs: a real Cordis `Context`, the production `SlotsService` and web-react renderer, assembled around typed session/workspace doubles. Feature suites exercise declaration, registration, scope, store, inject, rendering, updates, and disposal without hand-building the machinery per suite — and without a second implementation of any production logic.
+
+The doubles implement the same outward faces features receive through ctx (`TestSessions implements ISessions`, `TestWorkspaces implements IWorkspaces`; each fixture session is a `FixtureSession implements SessionFace`), so a production face change breaks the bench at compile time instead of silently drifting. Provide-bundle materialization runs the production `SessionProvideChannel` — the one implementation shared with `SessionsService`. Fixtures feed plain data: list rows, conversation snapshots (immer-patched via `updateSnapshot`), projection values, and `ISession`-typed behavior stubs that fail loud when a spec calls an unstubbed verb. The typed `provide()` constrains fakes for declared service names to `Partial` of that service's outward face.
+
+Local DOM snapshots: `declare(children)` registers an auto frame whose per-key `
` wrappers are snapshot roots; `renderSlot(key, owner)` returns the slot-local view (container, scoped Testing Library queries, in-place `update(owner)`); a registered snapshot serializer folds CSS-module class hashes (`_frame_a1b2c3` → `frame`) to keep `.snap` files structural and collapses `