@crazx/dsh-client-test-runtime 0.1.5-alpha.1.zw.3 → 0.1.5-rc.1.zw.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/test-support/client-runtime/README.md
5
- README.md: 616eab2cd5ef03ae4e514ca335b0c60b43d62cf0
6
- README.zh.md: 08effcef7eaacad67b5496507925337e772d4ce9
5
+ README.md: 0b6caf7237520ca1a10fe0bbdf3c12c6d793461f
6
+ README.zh.md: ce78a0ad623247ad769e0ea91895daa1db8e7f1b
package/README.md CHANGED
@@ -29,7 +29,7 @@ This package gives a browser feature spec a real runtime to mount against: creat
29
29
 
30
30
  ### Setting up a feature spec
31
31
 
32
- `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:
32
+ `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, opts?)` returns the slot-local view with scoped queries and in-place updates:
33
33
 
34
34
  ```text
35
35
  const runtime = await SlotTestRuntime.create()
@@ -42,6 +42,8 @@ await runtime.dispose()
42
42
 
43
43
  `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.
44
44
 
45
+ The optional render options select a keyed entry with `entryKey` or a list item with `only`; `view.update(owner)` retains that selection. `runtime.panelInfo` supplies the default `usePanelInfo` source with no global panel selected. Release it with `releasePanelInfoSource()` before mounting the production Layout owner. `dispose()` releases both default Workspace and panel-info root sources; early release is idempotent and does not remove replacement owners.
46
+
45
47
  ### Local DOM snapshots
46
48
 
47
49
  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.
package/README.zh.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "面向浏览器功能测试的 jsdom slot 测试运行时,供测试作者针对生产机制检验 slot、store 与渲染。"
2
+ description: "面向浏览器功能测试的 jsdom slot 测试运行时,供测试作者针对生产机制检验 slot、存储与渲染。"
3
3
  kind: "package-library"
4
4
  ---
5
5
 
@@ -9,7 +9,7 @@ kind: "package-library"
9
9
 
10
10
  ## 概述
11
11
 
12
- `dsh-client-test-runtime` 让浏览器功能测试在 jsdom 中检验生产 slot、store、渲染、更新与销毁行为,而无需重实现 UI 运行时。测试作者可以发布带类型的 Session、Workspace、projection 与 Conversation fixture,查询 slot 局部 DOM 根,并脚本化 Remote 应答或失败。缺失服务、未打桩的会话行为与意外文件上传都会在调用点失败,销毁则保持幂等。仅限仓内、面向浏览器的 Vitest 套件通过 `devDependencies` 使用本包;它不是产品插件或通用 Node 测试框架。
12
+ `dsh-client-test-runtime` 让浏览器功能测试在 jsdom 中检验生产 slot、存储、渲染、更新与销毁行为,而无需重实现 UI 运行时。测试作者可以发布带类型的 Session、Workspace、projection 与 Conversation fixture(测试前置数据),查询 slot 局部 DOM 根,并脚本化 Remote 应答或失败。缺失服务、未打桩的会话行为与意外文件上传都会在调用点失败,销毁则保持幂等。仅限仓内、面向浏览器的 Vitest 套件通过 `devDependencies` 使用本包;它不是产品插件或通用 Node 测试 harness。
13
13
 
14
14
  ## 目录
15
15
 
@@ -29,7 +29,7 @@ kind: "package-library"
29
29
 
30
30
  ### 搭建功能测试
31
31
 
32
- `SlotTestRuntime.create()` 组装运行时,`declare(children)` 注册一个自动 frame,其逐 key 的 `<div data-slot>` 包裹层成为快照根,`mount(plugin)` 在真实 fiber 上运行功能,`renderSlot(key, owner)` 返回带限定查询与原位更新的 slot 局部视图:
32
+ `SlotTestRuntime.create()` 组装运行时,`declare(children)` 注册一个自动 frame,其逐 key 的 `<div data-slot>` 包裹层成为快照根,`mount(plugin)` 在真实 fiber 上运行功能,`renderSlot(key, owner, opts?)` 返回带限定查询与原位更新的 slot 局部视图:
33
33
 
34
34
  ```text
35
35
  const runtime = await SlotTestRuntime.create()
@@ -40,11 +40,13 @@ expect(view.container).toMatchSnapshot()
40
40
  await runtime.dispose()
41
41
  ```
42
42
 
43
- `mount` 会预检必需服务,缺失时自明报错——先用 `provide(name, value)` 提供额外服务。运行时会提供不可用的 `fileUpload` 替身,使装配可以挂载;测试上传行为时,需要在挂载前替换 `runtime.fileUpload.upload`。`storeOf(key, scopeKey)` 返回渲染器交给 slot 组件的实时 store 实例,用于身份与动作驱动写入断言。
43
+ `mount` 会预检必需服务,缺失时自明报错——先用 `provide(name, value)` 提供额外服务。运行时会提供不可用的 `fileUpload` 替身,使装配可以挂载;测试上传行为时,需要在挂载前替换 `runtime.fileUpload.upload`。`storeOf(key, scopeKey)` 返回渲染器交给 slot 组件的实时存储实例,用于身份与动作驱动写入断言。
44
+
45
+ 可选渲染参数通过 `entryKey` 选择 keyed 条目,或通过 `only` 选择 list 条目;`view.update(owner)` 保留该选择。`runtime.panelInfo` 提供默认的 `usePanelInfo` 数据源,初始不选中全局面板。挂载生产 Layout 所有者之前,先调用 `releasePanelInfoSource()` 释放该数据源。`dispose()` 同时释放默认的工作区与面板信息根数据源;提前释放是幂等的,不会移除替代它们的所有者。
44
46
 
45
47
  ### 局部 DOM 快照
46
48
 
47
- 注册的快照序列化器把 CSS-module 哈希类名折回语义名(`_frame_a1b2c3` → `frame`),使 `.snap` 文件只含结构,并把 `<svg>` 内部折叠为 `data-content` 指纹。需要自定义页面 frame 的套件改用 `root.declare(children, Frame)` 而非自动 frame;`dispose()` 沿单一轴拆除视图、feature fiber、已铸 scope 与持久化 store 状态,且幂等。
49
+ 注册的快照序列化器把 CSS-module 哈希类名折回语义名(`_frame_a1b2c3` → `frame`),使 `.snap` 文件只含结构,并把 `<svg>` 内部折叠为 `data-content` 指纹。需要自定义页面 frame 的套件改用 `root.declare(children, Frame)` 而非自动 frame;`dispose()` 沿单一轴拆除视图、功能 fiber、已铸 scope 与持久化存储状态,且幂等。
48
50
 
49
51
  ### 脚本化 Remote 应答与失败
50
52
 
@@ -64,7 +66,7 @@ expect(view.getByRole('alert')).toHaveTextContent('goal/not-found')
64
66
 
65
67
  ### 何时使用
66
68
 
67
- 当功能套件要在真实运行时下检验 slot、store、渲染与销毁时使用本测试台——生产 `SlotRegistry`、渲染器与 provide bundle 物化都会被挂载,绝不重实现。它是浏览器侧测试基础设施:永远不触及模型请求,feature 包仅以 `devDependencies` 依赖之。
69
+ 当功能套件要在真实运行时下检验 slot、存储、渲染与销毁时使用本测试台——生产 `SlotRegistry`、渲染器与 provide bundle 物化都会被挂载,绝不重实现。它是浏览器侧测试基础设施:永远不触及模型请求,功能包仅以 `devDependencies` 依赖之。
68
70
 
69
71
  ### 可能出什么问题
70
72
 
@@ -84,7 +86,7 @@ expect(view.getByRole('alert')).toHaveTextContent('goal/not-found')
84
86
 
85
87
  ### 设计
86
88
 
87
- 测试台不复制生产逻辑:它挂载生产 `SlotRegistry`、生产渲染器与 `UiSession` 适配器。`TestSessions` 与 `TestWorkspaces` 实现功能通过 Cordis 消费的 owner 接口,每个 fixture Session 实现 `SessionFace`,`stubSettingsScope` 实现 `SettingsScope`。`UiSession` 从这些 Controller binding 派生标准渲染器 source。未 stub 的 `ISession` 行为会携缺失方法名失败。
89
+ 测试台不复制生产逻辑:它挂载生产 `SlotRegistry`、生产渲染器与 `UiSession` 适配器。`TestSessions` 与 `TestWorkspaces` 实现功能通过 Cordis 消费的 owner 接口,每个 fixture Session 实现 `SessionFace`,`stubSettingsScope` 实现 `SettingsScope`。`UiSession` 从这些控制器绑定派生标准渲染器数据源。未 stub 的 `ISession` 行为会携缺失方法名失败。
88
90
 
89
91
  ### 源码地图
90
92
 
@@ -97,11 +99,11 @@ expect(view.getByRole('alert')).toHaveTextContent('goal/not-found')
97
99
  | [`src/remote.ts`](src/remote.ts) | 用于 host RPC 的 `TestRemote` 替身、`RemoteError` 值转出 |
98
100
  | [`src/translate.ts`](src/translate.ts) + [`src/locale-env.ts`](src/locale-env.ts) | 翻译与固定浏览器语言测试辅助 |
99
101
  | [`src/settings-scope.ts`](src/settings-scope.ts) | 带测试驱动发布与写入 spy 的 `stubSettingsScope` |
100
- | — | 不发布运行时不变式伴生入口;所挂载的生产包拥有各自的不变式。 |
102
+ | — | 不发布运行时不变式伴生入口;本测试支持包不拥有任何生产事件流或可变数据;它围绕测试替身组装运行时 SlotRegistry 与渲染器,其所属包拥有各自的不变式;本包自身行为由本包测试检验。 |
101
103
 
102
104
  ### 生命周期
103
105
 
104
- `create()` 构建全新上下文,挂载 slot 与会话注册表,安装渲染器,并提供 session/workspace 替身和明确失败的文件上传替身。`mount` 在启动 fiber 前对照上下文检查每个已声明注入,使缺失提供方自明报错而非永久挂起。`dispose()` 先卸载 React 树,再 dispose feature fiber、释放根注册、dispose 已铸 session scope 并清除持久化 store 状态;每个公共修改器都包裹在 act 中,因此测试无需自行处理 SlotCore 微任务批处理或 React `act`。
106
+ `create()` 构建全新上下文,挂载 slot 与会话注册表,安装渲染器,并提供 session/workspace 替身和明确失败的文件上传替身。`mount` 在启动 fiber 前对照上下文检查每个已声明注入,使缺失提供方自明报错而非永久挂起。`dispose()` 先卸载 React 树,再 dispose 功能 fiber、释放根注册、dispose 已铸 session scope 并清除持久化存储状态;每个公共修改器都包裹在 act 中,因此测试无需自行处理 SlotCore 微任务批处理或 React `act`。
105
107
 
106
108
  </details>
107
109
 
@@ -112,7 +114,7 @@ expect(view.getByRole('alert')).toHaveTextContent('goal/not-found')
112
114
 
113
115
  当包级约定不够用时阅读以下页面。它们从测试台逐步进入它所挂载的生产机制以及使用它的测试。
114
116
 
115
- - [ui-session](../../client/ui-session/README.zh.md)——从 Controller 替身派生标准 Slot source 的生产适配器。
117
+ - [ui-session](../../client/ui-session/README.zh.md)——从控制器替身派生标准 Slot 数据源的生产适配器。
116
118
  - [UI slots 包](../../client/ui-slots/README.zh.md)——测试台挂载的 `SlotRegistry` 约定。
117
119
  - [UI renderer 包](../../client/ui-renderer/README.zh.md)——测试台安装的渲染器。
118
120
  - [测试策略](../../../docs/testing.zh.md)——覆盖层级与浏览器快照流水线。
@@ -123,7 +125,7 @@ expect(view.getByRole('alert')).toHaveTextContent('goal/not-found')
123
125
  <a id="model-experience"></a>
124
126
  ## 模型体验
125
127
 
126
- 无;本包是浏览器侧测试基础设施,无一物到达模型请求。
128
+ 无;本包是浏览器侧测试基础设施,不会发起任何模型请求。
127
129
 
128
130
  #### KV Cache 影响
129
131
 
@@ -136,8 +138,8 @@ expect(view.getByRole('alert')).toHaveTextContent('goal/not-found')
136
138
 
137
139
  这些限制说明本测试台如何被消费。它们是当前包约束,不是任务积压。
138
140
 
139
- - **仅限 Vitest 与 jsdom**——所有消费方都是仓内、面向浏览器的 Vitest 套件。本包不是产品插件,也不是通用 Node 测试框架。
140
- - **Session、Conversation 与 Chat fixture 保持分离**——`sessionSnapshot` 只包含 Session Controller 状态,`conversationSnapshot` 包含 target-neutral Conversation 状态,`chatSnapshot` 包含 Chat target 状态。组装测试提供 Session event entry,而不是向 `SessionSnapshot` 添加 Conversation 或 Chat 字段。
141
+ - **仅限 Vitest 与 jsdom**——所有消费方都是仓内、面向浏览器的 Vitest 套件。本包不是产品插件,也不是通用 Node 测试 harness。
142
+ - **Session、Conversation 与 Chat fixture 保持分离**——`sessionSnapshot` 只包含 Session 控制器状态,`conversationSnapshot` 包含与目标无关的 Conversation 状态,`chatSnapshot` 包含 Chat 目标状态。组装测试提供 Session 事件条目,而不是向 `SessionSnapshot` 添加 Conversation 或 Chat 字段。
141
143
 
142
144
  <a id="dev-note"></a>
143
145
  ### 开发备注
package/lib/index.js CHANGED
@@ -5,9 +5,9 @@ import { SlotRegistry } from "@deepseek-ai/dsh-client-ui-renderer/client";
5
5
  import { bindSnapshotSelector as bindSnapshotSelector$1 } from "@deepseek-ai/dsh-client-ui-renderer/src/client/bind.ts";
6
6
  import { createSlotRenderer as createSlotRenderer$1 } from "@deepseek-ai/dsh-client-ui-renderer/src/client/scoped-slots.tsx";
7
7
  import { apply, inject } from "@deepseek-ai/dsh-client-ui-session/client";
8
+ import { createSnapshotStore } from "@deepseek-ai/dsh-client-store";
8
9
  import { afterEach, beforeEach, expect, vi } from "vitest";
9
10
  import { MutableSessionEventSource, SESSION_SEARCH_RESULT_LIMIT, createScope, scopeOf } from "@deepseek-ai/dsh-api-session-controller/client";
10
- import { createSnapshotStore } from "@deepseek-ai/dsh-client-store";
11
11
  import { EMPTY_CONVERSATION_SNAPSHOT } from "@deepseek-ai/dsh-client-ui-conversation/client";
12
12
  import { EMPTY_CHAT_SNAPSHOT } from "@deepseek-ai/dsh-client-ui-chat/client";
13
13
  import { RemoteError } from "@deepseek-ai/dsh-typert-protocol";
@@ -584,15 +584,6 @@ var TestSessions = class {
584
584
  draft.currentAddress = void 0;
585
585
  });
586
586
  }
587
- /** History stubs: the test runtime has no selection history — both bounds report empty. */
588
- canBack() {
589
- return false;
590
- }
591
- canForward() {
592
- return false;
593
- }
594
- back() {}
595
- forward() {}
596
587
  /** Record a list refresh; fixture callers publish list state explicitly. */
597
588
  refresh() {
598
589
  this.calls.push({
@@ -1124,9 +1115,13 @@ var OwnerPropsCell = class {
1124
1115
  * caller wraps in act).
1125
1116
  * @param key - slot key.
1126
1117
  * @param owner - owner props share.
1118
+ * @param opts - explicit keyed or list selection for the render site.
1127
1119
  */
1128
- set(key, owner) {
1129
- this.owners.set(key, owner);
1120
+ set(key, owner, opts) {
1121
+ this.owners.set(key, {
1122
+ owner,
1123
+ opts
1124
+ });
1130
1125
  this.version += 1;
1131
1126
  for (const fn of [...this.listeners]) fn();
1132
1127
  }
@@ -1191,6 +1186,10 @@ var SlotTestRuntime = class SlotTestRuntime {
1191
1186
  sessions;
1192
1187
  /** Workspaces double (list observable, recorded intent actions). */
1193
1188
  workspaces;
1189
+ /** Test-owned panel selection used by the framework's usePanelInfo hook. */
1190
+ panelInfo = createSnapshotStore({ activePanelId: null });
1191
+ /** Test-owned toolbar portal hosts used by the framework's useToolbarHosts hook (null = no toolbar). */
1192
+ toolbarHosts = createSnapshotStore(null);
1194
1193
  /** Mutable file-upload stub; replace `upload` in suites that exercise the capability. */
1195
1194
  fileUpload;
1196
1195
  stabilizer = async (fn) => {
@@ -1207,6 +1206,8 @@ var SlotTestRuntime = class SlotTestRuntime {
1207
1206
  autoDeclared = /* @__PURE__ */ new Set();
1208
1207
  autoRootView;
1209
1208
  disposeWorkspaceSource;
1209
+ disposePanelInfoSource;
1210
+ disposeToolbarHostsSource;
1210
1211
  constructor(ctx, slots) {
1211
1212
  this.ctx = ctx;
1212
1213
  this.slots = slots;
@@ -1221,6 +1222,8 @@ var SlotTestRuntime = class SlotTestRuntime {
1221
1222
  ctx.provide("workspaces", this.workspaces);
1222
1223
  ctx.provide("fileUpload", this.fileUpload);
1223
1224
  this.disposeWorkspaceSource = slots.provideRoot({ hooks: { workspaces: this.workspaces.list } });
1225
+ this.disposePanelInfoSource = slots.provideRoot({ hooks: { panelInfo: this.panelInfo } });
1226
+ this.disposeToolbarHostsSource = slots.provideRoot({ hooks: { toolbarHosts: this.toolbarHosts } });
1224
1227
  const renderer = createSlotRenderer();
1225
1228
  slots.install({ renderRoot: (host, ownerProps) => {
1226
1229
  this.host = host;
@@ -1273,6 +1276,11 @@ var SlotTestRuntime = class SlotTestRuntime {
1273
1276
  releaseWorkspaceSource() {
1274
1277
  this.disposeWorkspaceSource();
1275
1278
  }
1279
+ /** Release the default panel hook before mounting the production Layout owner. */
1280
+ releasePanelInfoSource() {
1281
+ this.disposePanelInfoSource();
1282
+ this.disposeToolbarHostsSource();
1283
+ }
1276
1284
  /**
1277
1285
  * Render the root slot tree through the ctx-level entry (the shell's own
1278
1286
  * entry point): `ctx.slots.renderSlot('root', {})` under Testing Library.
@@ -1298,7 +1306,7 @@ var SlotTestRuntime = class SlotTestRuntime {
1298
1306
  const cell = this.ownerCell;
1299
1307
  const AutoFrame = (props) => {
1300
1308
  useSyncExternalStore(cell.subscribe, cell.getVersion);
1301
- return createElement(Fragment, null, cell.entries().map(([key, owner]) => createElement(Fragment, { key }, props.renderSlot(key, owner))));
1309
+ return createElement(Fragment, null, cell.entries().map(([key, { owner, opts }]) => createElement(Fragment, { key }, props.renderSlot(key, owner, opts))));
1302
1310
  };
1303
1311
  await this.root.declare(children, AutoFrame);
1304
1312
  }
@@ -1310,13 +1318,14 @@ var SlotTestRuntime = class SlotTestRuntime {
1310
1318
  * slot of the same tree.
1311
1319
  * @param key - a key declared through {@link SlotTestRuntime.declare}.
1312
1320
  * @param owner - owner props share for the render site.
1321
+ * @param opts - explicit keyed or list selection; retained by view updates.
1313
1322
  * @returns the slot-local view (snapshot container, scoped queries, owner updates).
1314
1323
  */
1315
- renderSlot(key, owner) {
1324
+ renderSlot(key, owner, opts) {
1316
1325
  if (!this.autoDeclared.has(key)) throw new Error(`renderSlot('${key}') without declare() — declare the key first (or use root.declare for a custom frame)`);
1317
1326
  const install = (next) => {
1318
1327
  act(() => {
1319
- this.ownerCell.set(key, next);
1328
+ this.ownerCell.set(key, next, opts);
1320
1329
  });
1321
1330
  };
1322
1331
  install(owner);
@@ -1358,7 +1367,7 @@ var SlotTestRuntime = class SlotTestRuntime {
1358
1367
  }
1359
1368
  /**
1360
1369
  * Tear down: unmount React trees first, then dispose feature fibers, the
1361
- * root registration, minted session scopes, and persisted test state.
1370
+ * root registration and standard sources, minted session scopes, and persisted test state.
1362
1371
  * Idempotent.
1363
1372
  * @returns completion of the teardown.
1364
1373
  */
@@ -1369,6 +1378,9 @@ var SlotTestRuntime = class SlotTestRuntime {
1369
1378
  for (const view of this.views.splice(0)) view.unmount();
1370
1379
  for (const handle of this.handles.splice(0)) await handle.dispose();
1371
1380
  this.root.release();
1381
+ this.disposeWorkspaceSource();
1382
+ this.disposePanelInfoSource();
1383
+ this.disposeToolbarHostsSource();
1372
1384
  await this.sessions.disposeScopes();
1373
1385
  localStorage.clear();
1374
1386
  }
@@ -17,7 +17,8 @@ import type { queries } from '@testing-library/dom';
17
17
  import type { BoundFunctions } from '@testing-library/dom';
18
18
  import { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client';
19
19
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
20
- import type { ChildrenDecl, ComposedProps, HostObservable, OwnerOf, SlotComponent, SlotMap, SlotRenderer, SnapshotSelectorHook, StoreInstanceLike } from '@deepseek-ai/dsh-client-ui-slots';
20
+ import type { PanelInfo, ToolbarHosts } from '@deepseek-ai/dsh-client-ui-layout/client';
21
+ import type { ChildrenDecl, ComposedProps, HostObservable, OwnerOf, RenderOpts, SlotComponent, SlotMap, SlotRenderer, SnapshotSelectorHook, StoreInstanceLike } from '@deepseek-ai/dsh-client-ui-slots';
21
22
  import { TestSessions } from './sessions.ts';
22
23
  import { TestWorkspaces } from './workspaces.ts';
23
24
  import type { Stabilizer } from './fixtures.ts';
@@ -128,6 +129,10 @@ export declare class SlotTestRuntime {
128
129
  readonly sessions: TestSessions;
129
130
  /** Workspaces double (list observable, recorded intent actions). */
130
131
  readonly workspaces: TestWorkspaces;
132
+ /** Test-owned panel selection used by the framework's usePanelInfo hook. */
133
+ readonly panelInfo: import("@deepseek-ai/dsh-client-store").SnapshotStore<PanelInfo>;
134
+ /** Test-owned toolbar portal hosts used by the framework's useToolbarHosts hook (null = no toolbar). */
135
+ readonly toolbarHosts: import("@deepseek-ai/dsh-client-store").SnapshotStore<ToolbarHosts | null>;
131
136
  /** Mutable file-upload stub; replace `upload` in suites that exercise the capability. */
132
137
  readonly fileUpload: TestFileUpload;
133
138
  private readonly stabilizer;
@@ -140,6 +145,8 @@ export declare class SlotTestRuntime {
140
145
  private readonly autoDeclared;
141
146
  private autoRootView;
142
147
  private readonly disposeWorkspaceSource;
148
+ private readonly disposePanelInfoSource;
149
+ private readonly disposeToolbarHostsSource;
143
150
  private constructor();
144
151
  /**
145
152
  * Assemble a runtime: real Context, mounted SlotRegistry, installed
@@ -157,6 +164,8 @@ export declare class SlotTestRuntime {
157
164
  mount(plugin: Plugin): Promise<FeatureHandle>;
158
165
  /** Release the default Workspace hook before mounting its production owner. */
159
166
  releaseWorkspaceSource(): void;
167
+ /** Release the default panel hook before mounting the production Layout owner. */
168
+ releasePanelInfoSource(): void;
160
169
  /**
161
170
  * Render the root slot tree through the ctx-level entry (the shell's own
162
171
  * entry point): `ctx.slots.renderSlot('root', {})` under Testing Library.
@@ -182,9 +191,10 @@ export declare class SlotTestRuntime {
182
191
  * slot of the same tree.
183
192
  * @param key - a key declared through {@link SlotTestRuntime.declare}.
184
193
  * @param owner - owner props share for the render site.
194
+ * @param opts - explicit keyed or list selection; retained by view updates.
185
195
  * @returns the slot-local view (snapshot container, scoped queries, owner updates).
186
196
  */
187
- renderSlot<K extends keyof SlotMap & string>(key: K, owner: OwnerOf<K>): SlotView<K>;
197
+ renderSlot<K extends keyof SlotMap & string>(key: K, owner: OwnerOf<K>, opts?: RenderOpts): SlotView<K>;
188
198
  /**
189
199
  * Resolve the store instance the renderer would hand a slot's component
190
200
  * (identity assertions, action-driven writes). Requires a prior
@@ -203,7 +213,7 @@ export declare class SlotTestRuntime {
203
213
  flush(): Promise<void>;
204
214
  /**
205
215
  * Tear down: unmount React trees first, then dispose feature fibers, the
206
- * root registration, minted session scopes, and persisted test state.
216
+ * root registration and standard sources, minted session scopes, and persisted test state.
207
217
  * Idempotent.
208
218
  * @returns completion of the teardown.
209
219
  */
@@ -226,11 +226,6 @@ export declare class TestSessions implements ISessions {
226
226
  refreshSubagents(parentSessionId: SessionId): Promise<void>;
227
227
  /** Clear the current selection (recorded; the production no-session flow). */
228
228
  clear(): void;
229
- /** History stubs: the test runtime has no selection history — both bounds report empty. */
230
- canBack(): boolean;
231
- canForward(): boolean;
232
- back(): void;
233
- forward(): void;
234
229
  /** Record a list refresh; fixture callers publish list state explicitly. */
235
230
  refresh(): Promise<void>;
236
231
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@crazx/dsh-client-test-runtime",
3
3
  "description": "jsdom slot test runtime: real Cordis Context + SlotRegistry + UI renderer with test-owned session/workspace doubles for feature specs",
4
- "version": "0.1.5-alpha.1.zw.3",
4
+ "version": "0.1.5-rc.1.zw.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -28,39 +28,40 @@
28
28
  "vitest": "^4.1.8"
29
29
  },
30
30
  "peerDependencies": {
31
- "@deepseek-ai/dsh-api-session-controller": "npm:@crazx/dsh-api-session-controller@0.1.5-alpha.1.zw.3",
32
- "@deepseek-ai/dsh-api-workspace-controller": "^0.1.5-alpha.1",
33
- "@deepseek-ai/dsh-attachment": "^0.1.5-alpha.1",
34
- "@deepseek-ai/dsh-client-connection": "^0.1.5-alpha.1",
35
- "@deepseek-ai/dsh-client-store": "^0.1.5-alpha.1",
36
- "@deepseek-ai/dsh-client-ui-chat": "^0.1.5-alpha.1",
37
- "@deepseek-ai/dsh-client-ui-conversation": "npm:@crazx/dsh-client-ui-conversation@0.1.5-alpha.1.zw.3",
38
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-alpha.1",
39
- "@deepseek-ai/dsh-client-ui-session": "^0.1.5-alpha.1",
40
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-alpha.1",
41
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-alpha.1",
42
- "@deepseek-ai/dsh-session": "^0.1.5-alpha.1",
43
- "@deepseek-ai/dsh-subagent": "^0.1.5-alpha.1",
44
- "@deepseek-ai/dsh-typert-protocol": "^0.1.5-alpha.1",
31
+ "@deepseek-ai/dsh-api-session-controller": "0.1.5-rc.1.zw.2",
32
+ "@deepseek-ai/dsh-api-workspace-controller": "^0.1.5-rc.1",
33
+ "@deepseek-ai/dsh-attachment": "^0.1.5-rc.1",
34
+ "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.1",
35
+ "@deepseek-ai/dsh-client-store": "^0.1.5-rc.1",
36
+ "@deepseek-ai/dsh-client-ui-chat": "^0.1.5-rc.1",
37
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.5-rc.1.zw.2",
38
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.1",
39
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.5-rc.1",
40
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.1",
41
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.1",
42
+ "@deepseek-ai/dsh-session": "^0.1.5-rc.1",
43
+ "@deepseek-ai/dsh-subagent": "^0.1.5-rc.1",
44
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.5-rc.1",
45
45
  "@deepseek-ai/cordis": "^4.0.2",
46
46
  "react": "^18.2.0",
47
47
  "react-dom": "^18.2.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@deepseek-ai/dsh-api-session-controller": "npm:@crazx/dsh-api-session-controller@0.1.5-alpha.1.zw.3",
51
- "@deepseek-ai/dsh-api-workspace-controller": "^0.1.5-alpha.1",
52
- "@deepseek-ai/dsh-attachment": "^0.1.5-alpha.1",
53
- "@deepseek-ai/dsh-client-connection": "^0.1.5-alpha.1",
54
- "@deepseek-ai/dsh-client-store": "^0.1.5-alpha.1",
55
- "@deepseek-ai/dsh-client-ui-chat": "^0.1.5-alpha.1",
56
- "@deepseek-ai/dsh-client-ui-conversation": "npm:@crazx/dsh-client-ui-conversation@0.1.5-alpha.1.zw.3",
57
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-alpha.1",
58
- "@deepseek-ai/dsh-client-ui-session": "^0.1.5-alpha.1",
59
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-alpha.1",
60
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-alpha.1",
61
- "@deepseek-ai/dsh-session": "^0.1.5-alpha.1",
62
- "@deepseek-ai/dsh-subagent": "^0.1.5-alpha.1",
63
- "@deepseek-ai/dsh-typert-protocol": "^0.1.5-alpha.1",
50
+ "@deepseek-ai/dsh-api-session-controller": "npm:@crazx/dsh-api-session-controller@0.1.5-rc.1.zw.2",
51
+ "@deepseek-ai/dsh-api-workspace-controller": "^0.1.5-rc.1",
52
+ "@deepseek-ai/dsh-attachment": "^0.1.5-rc.1",
53
+ "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.1",
54
+ "@deepseek-ai/dsh-client-store": "^0.1.5-rc.1",
55
+ "@deepseek-ai/dsh-client-ui-chat": "^0.1.5-rc.1",
56
+ "@deepseek-ai/dsh-client-ui-conversation": "npm:@crazx/dsh-client-ui-conversation@0.1.5-rc.1.zw.2",
57
+ "@deepseek-ai/dsh-client-ui-layout": "npm:@crazx/dsh-client-ui-layout@0.1.5-rc.1.zw.2",
58
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.1",
59
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.5-rc.1",
60
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.1",
61
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.1",
62
+ "@deepseek-ai/dsh-session": "^0.1.5-rc.1",
63
+ "@deepseek-ai/dsh-subagent": "^0.1.5-rc.1",
64
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.5-rc.1",
64
65
  "@types/react": "~18.3.1",
65
66
  "@types/react-dom": "~18.3.0",
66
67
  "@deepseek-ai/cordis": "^4.0.2",