@deepseek-ai/dsh-client-test-runtime 0.0.1-rc.1
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/LICENSE +28 -0
- package/README.i18n.yaml +6 -0
- package/README.md +24 -0
- package/README.zh.md +24 -0
- package/lib/index.js +1259 -0
- package/lib/invariant.js +25 -0
- package/lib/types/fixtures.d.ts +46 -0
- package/lib/types/index.d.ts +194 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/locale-env.d.ts +9 -0
- package/lib/types/sessions.d.ts +263 -0
- package/lib/types/settings-scope.d.ts +25 -0
- package/lib/types/snapshot.d.ts +14 -0
- package/lib/types/translate.d.ts +16 -0
- package/lib/types/workspaces.d.ts +109 -0
- package/package.json +61 -0
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region lib/types/invariant.js
|
|
2
|
+
/**
|
|
3
|
+
* Package-owned invariant companion for `@deepseek-ai/dsh-client-test-runtime`.
|
|
4
|
+
* @module @deepseek-ai/dsh-client-test-runtime/invariant
|
|
5
|
+
*/
|
|
6
|
+
const PACKAGE_NAME = "@deepseek-ai/dsh-client-test-runtime";
|
|
7
|
+
/** Cordis companion plugin name. */
|
|
8
|
+
const name = "client-test-runtime-invariant";
|
|
9
|
+
/** Service required before the companion can reserve package ownership. */
|
|
10
|
+
const inject = ["invariants"];
|
|
11
|
+
/**
|
|
12
|
+
* No runtime invariant: this test-support package owns no production event
|
|
13
|
+
* stream or mutable data — it assembles the runtime SlotsService and renderer
|
|
14
|
+
* (whose packages own their invariants) around test doubles; its own behavior
|
|
15
|
+
* is exercised by its package tests.
|
|
16
|
+
*/
|
|
17
|
+
const install = () => {};
|
|
18
|
+
/**
|
|
19
|
+
* Register this package's invariant companion.
|
|
20
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
21
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
22
|
+
*/
|
|
23
|
+
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
24
|
+
//#endregion
|
|
25
|
+
export { apply, inject, name };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Session/workspace fixture shapes and snapshot defaults for the test runtime. */
|
|
2
|
+
import type { ConversationSnapshot, ISession, SessionId, SessionSummary, WorkspaceListState } from '@deepseek-ai/dsh-client-runtime/client';
|
|
3
|
+
/**
|
|
4
|
+
* Fixture overrides for the session behavior face: any subset of the
|
|
5
|
+
* production ISession verbs (typed against it, so a face change surfaces
|
|
6
|
+
* here at compile time), plus extra members feature-specific casts consume.
|
|
7
|
+
* The open Record tail means a misnamed EXTRA member is not caught by the
|
|
8
|
+
* compiler (it grafts as dead weight); the ISession verbs stay safe — a
|
|
9
|
+
* misnamed verb leaves the fail-loud stub in place, which names itself at
|
|
10
|
+
* the first call.
|
|
11
|
+
*/
|
|
12
|
+
export type SessionBehaviorOverrides = Partial<ISession> & Record<string, unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* act-wrapped mutation runner shared by every runtime object: public mutators
|
|
15
|
+
* funnel through it so tests never handle SlotCore microtask batching or
|
|
16
|
+
* React act themselves.
|
|
17
|
+
*/
|
|
18
|
+
export type Stabilizer = (fn: () => void | Promise<void>) => Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Session fixture accepted by {@link TestSessions.add}: identity plus optional
|
|
21
|
+
* snapshot/list-row overrides and the session behavior face the feature under
|
|
22
|
+
* test actually calls (kept open — the runtime never fakes methods a test did
|
|
23
|
+
* not supply, so an unstubbed call fails loud at the call site).
|
|
24
|
+
*/
|
|
25
|
+
export interface SessionFixture {
|
|
26
|
+
id: string;
|
|
27
|
+
/** Overrides merged over {@link conversationSnapshot} (sessionId comes from `id`). */
|
|
28
|
+
snapshot?: Partial<Omit<ConversationSnapshot, 'sessionId'>>;
|
|
29
|
+
/** List-row overrides merged over the defaults derived from `id`. */
|
|
30
|
+
summary?: Partial<Omit<SessionSummary, 'id'>>;
|
|
31
|
+
/** Session behavior face: exactly the methods the feature under test calls (ISession subset + extras). */
|
|
32
|
+
session?: SessionBehaviorOverrides;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A complete quiescent conversation snapshot (open window, no traffic).
|
|
36
|
+
* @param sessionId - owning session id.
|
|
37
|
+
* @returns the snapshot; spread fixture overrides on top.
|
|
38
|
+
*/
|
|
39
|
+
export declare function conversationSnapshot(sessionId: SessionId): ConversationSnapshot;
|
|
40
|
+
/**
|
|
41
|
+
* A ready workspace list with no workspaces (the shape WorkspacesService
|
|
42
|
+
* projects after both baselines land).
|
|
43
|
+
* @returns the initial state of the test workspaces store.
|
|
44
|
+
*/
|
|
45
|
+
export declare function workspaceListState(): WorkspaceListState;
|
|
46
|
+
//# sourceMappingURL=fixtures.d.ts.map
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* jsdom slot test runtime: a real small runtime — Cordis `Context`, the
|
|
3
|
+
* runtime `SlotsService`, and the web-react renderer — assembled around
|
|
4
|
+
* test-owned session/workspace doubles, so feature specs exercise
|
|
5
|
+
* declaration, registration, scope, store, inject, rendering, updates, and
|
|
6
|
+
* disposal without hand-building the machinery per suite.
|
|
7
|
+
*
|
|
8
|
+
* Not part of the product plugin graph (no `dsh.client`); feature packages
|
|
9
|
+
* depend on it in devDependencies only. It copies no SlotCore/renderer/store
|
|
10
|
+
* machinery — everything mounts the production implementations.
|
|
11
|
+
* @module @deepseek-ai/dsh-client-test-runtime
|
|
12
|
+
*/
|
|
13
|
+
import { Context } from '@deepseek-ai/cordis';
|
|
14
|
+
import type { Fiber, Plugin } from '@deepseek-ai/cordis';
|
|
15
|
+
import type { RenderResult } from '@testing-library/react';
|
|
16
|
+
import type { queries } from '@testing-library/dom';
|
|
17
|
+
import type { BoundFunctions } from '@testing-library/dom';
|
|
18
|
+
import { SlotsService } from '@deepseek-ai/dsh-client-runtime/client';
|
|
19
|
+
import type { ChildrenDecl, ComposedProps, OwnerOf, SlotComponent, SlotMap, StoreInstanceLike } from '@deepseek-ai/dsh-client-ui-slots';
|
|
20
|
+
import { TestSessions } from './sessions.ts';
|
|
21
|
+
import { TestWorkspaces } from './workspaces.ts';
|
|
22
|
+
import type { Stabilizer } from './fixtures.ts';
|
|
23
|
+
export { domSnapshotSerializer, registerDomSnapshotSerializer } from './snapshot.ts';
|
|
24
|
+
export { FixtureSession, TestSessions } from './sessions.ts';
|
|
25
|
+
export { stubSettingsScope } from './settings-scope.ts';
|
|
26
|
+
export type { StubSettingsScope } from './settings-scope.ts';
|
|
27
|
+
export { TestWorkspaces } from './workspaces.ts';
|
|
28
|
+
export { conversationSnapshot, workspaceListState } from './fixtures.ts';
|
|
29
|
+
export type { SessionBehaviorOverrides, SessionFixture, Stabilizer } from './fixtures.ts';
|
|
30
|
+
export { makeTranslate } from './translate.ts';
|
|
31
|
+
export { usePinnedBrowserLanguages } from './locale-env.ts';
|
|
32
|
+
/**
|
|
33
|
+
* One rendered slot's local view, from {@link SlotTestRuntime.renderSlot}:
|
|
34
|
+
* the `data-slot` wrapper is the snapshot root (`expect(view.container)
|
|
35
|
+
* .toMatchSnapshot()` captures exactly this slot's output), Testing Library
|
|
36
|
+
* queries are bound inside it, and `update` re-renders with new owner props.
|
|
37
|
+
*/
|
|
38
|
+
export interface SlotView<K extends keyof SlotMap & string> {
|
|
39
|
+
/** The `<div data-slot="<key>">` wrapper around the slot's rendered output. */
|
|
40
|
+
readonly container: HTMLElement;
|
|
41
|
+
/** Testing Library queries scoped to {@link SlotView.container}. */
|
|
42
|
+
readonly view: BoundFunctions<typeof queries>;
|
|
43
|
+
/**
|
|
44
|
+
* Replace the owner props and flush the re-render (the render-site update:
|
|
45
|
+
* in production the owner recomputes the share and React re-renders).
|
|
46
|
+
* @param owner - the next owner props share.
|
|
47
|
+
*/
|
|
48
|
+
update(owner: OwnerOf<K>): void;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Mounted feature plugin handle: the live fiber plus an act-wrapped,
|
|
52
|
+
* idempotent dispose (unload cascade: entries, declared child slots, store
|
|
53
|
+
* instances, and provided services all fall together).
|
|
54
|
+
*/
|
|
55
|
+
export interface FeatureHandle {
|
|
56
|
+
/** The plugin's live Cordis fiber (state assertions, escape hatch). */
|
|
57
|
+
readonly fiber: Fiber;
|
|
58
|
+
/**
|
|
59
|
+
* Dispose the plugin fiber inside React act; repeated calls no-op.
|
|
60
|
+
* @returns completion of the unload cascade.
|
|
61
|
+
*/
|
|
62
|
+
dispose(): Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The test-owned 'root' occupant: declares the child slots a suite needs
|
|
66
|
+
* through the REAL `slots.register`, with a caller-supplied minimal frame —
|
|
67
|
+
* the runtime never guesses a feature's page structure.
|
|
68
|
+
*/
|
|
69
|
+
export declare class TestRoot {
|
|
70
|
+
private readonly slots;
|
|
71
|
+
private readonly stabilize;
|
|
72
|
+
private disposeEntry;
|
|
73
|
+
/**
|
|
74
|
+
* @param slots - the runtime SlotsService.
|
|
75
|
+
* @param stabilize - the owning runtime's act wrapper.
|
|
76
|
+
*/
|
|
77
|
+
constructor(slots: SlotsService, stabilize: Stabilizer);
|
|
78
|
+
/**
|
|
79
|
+
* Register the root frame, declaring (and thereby claiming) the child
|
|
80
|
+
* slots. One declaration per runtime — a second call fails loud in the
|
|
81
|
+
* core ('root' is a single slot).
|
|
82
|
+
* @param children - child-slot declaration table (declaration + render authorization + runtime spec).
|
|
83
|
+
* @param frame - minimal frame component; its props derive from the declared keys (composed-props contract).
|
|
84
|
+
* @returns completion of the act-wrapped registration.
|
|
85
|
+
*/
|
|
86
|
+
declare<const D extends ChildrenDecl>(children: D, frame: SlotComponent<ComposedProps<'root', never, keyof NoInfer<D> & keyof SlotMap & string, undefined, object>>): Promise<void>;
|
|
87
|
+
/** Remove the root registration and collapse its declarations (runtime dispose path). */
|
|
88
|
+
release(): void;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The assembled test runtime. Obtain via {@link SlotTestRuntime.create};
|
|
92
|
+
* dispose with {@link SlotTestRuntime.dispose} (afterEach). Public mutators
|
|
93
|
+
* are act-wrapped throughout — tests never handle SlotCore microtask
|
|
94
|
+
* batching or React act themselves.
|
|
95
|
+
*/
|
|
96
|
+
export declare class SlotTestRuntime {
|
|
97
|
+
/** The runtime's Cordis root (escape hatch: extra services via `ctx.provide`, raw `ctx.plugin` mounts). */
|
|
98
|
+
readonly ctx: Context;
|
|
99
|
+
/** The production SlotsService mounted on {@link SlotTestRuntime.ctx}. */
|
|
100
|
+
readonly slots: SlotsService;
|
|
101
|
+
/** The test-owned 'root' occupant. */
|
|
102
|
+
readonly root: TestRoot;
|
|
103
|
+
/** Sessions double (list/current observable, cells, scopes, behavior faces). */
|
|
104
|
+
readonly sessions: TestSessions;
|
|
105
|
+
/** Workspaces double (list observable, recorded intent actions). */
|
|
106
|
+
readonly workspaces: TestWorkspaces;
|
|
107
|
+
private readonly stabilizer;
|
|
108
|
+
private host;
|
|
109
|
+
private readonly views;
|
|
110
|
+
private readonly handles;
|
|
111
|
+
private disposed;
|
|
112
|
+
/** Auto-frame state ({@link SlotTestRuntime.declare} / {@link SlotTestRuntime.renderSlot}). */
|
|
113
|
+
private readonly ownerCell;
|
|
114
|
+
private readonly autoDeclared;
|
|
115
|
+
private autoRootView;
|
|
116
|
+
private constructor();
|
|
117
|
+
/**
|
|
118
|
+
* Assemble a runtime: real Context, mounted SlotsService, installed
|
|
119
|
+
* renderer, and the session/workspace doubles provided as services.
|
|
120
|
+
* @returns the ready runtime.
|
|
121
|
+
*/
|
|
122
|
+
static create(): Promise<SlotTestRuntime>;
|
|
123
|
+
/**
|
|
124
|
+
* Provide an extra service the feature under test injects (e.g. a layout
|
|
125
|
+
* fake). Sugar over `ctx.provide`, typed against the Context declaration
|
|
126
|
+
* merge: for a declared service name the fake must be a subset of that
|
|
127
|
+
* service's outward face (Partial — supply only what the feature calls),
|
|
128
|
+
* so a production face change breaks the fake at compile time. Undeclared
|
|
129
|
+
* names stay unchecked (ad-hoc test services).
|
|
130
|
+
* @param name - service name.
|
|
131
|
+
* @param value - service implementation (test double).
|
|
132
|
+
*/
|
|
133
|
+
provide<K extends string>(name: K, value: K extends keyof Context ? Partial<Context[K]> : unknown): void;
|
|
134
|
+
/**
|
|
135
|
+
* Mount a feature plugin on a real fiber. Required services are prechecked
|
|
136
|
+
* so a missing provider fails loud instead of suspending the fiber forever
|
|
137
|
+
* (deliberate load-order suspension tests use `ctx.plugin` directly).
|
|
138
|
+
* @param plugin - plugin value (function, class, or `{ inject, apply }` object).
|
|
139
|
+
* @returns handle owning the fiber's explicit disposal.
|
|
140
|
+
*/
|
|
141
|
+
mount(plugin: Plugin): Promise<FeatureHandle>;
|
|
142
|
+
/**
|
|
143
|
+
* Render the root slot tree through the ctx-level entry (the shell's own
|
|
144
|
+
* entry point): `ctx.slots.renderSlot('root', {})` under Testing Library.
|
|
145
|
+
* @returns the Testing Library view.
|
|
146
|
+
*/
|
|
147
|
+
renderRoot(): RenderResult;
|
|
148
|
+
/**
|
|
149
|
+
* Declare child slots under an auto-generated root frame — the single-slot
|
|
150
|
+
* mounting path for local DOM snapshots. Each key later supplied through
|
|
151
|
+
* {@link SlotTestRuntime.renderSlot} renders inside its own
|
|
152
|
+
* `<div data-slot="<key>">` wrapper (the snapshot root). Mutually exclusive
|
|
153
|
+
* with {@link TestRoot.declare} ('root' is a single slot); one call per
|
|
154
|
+
* runtime.
|
|
155
|
+
* @param children - child-slot declaration table (same contract as TestRoot.declare).
|
|
156
|
+
* @returns completion of the act-wrapped registration.
|
|
157
|
+
*/
|
|
158
|
+
declare(children: ChildrenDecl): Promise<void>;
|
|
159
|
+
/**
|
|
160
|
+
* Render one declared slot with its owner props and return the local view.
|
|
161
|
+
* The whole root tree mounts through the production assembly path
|
|
162
|
+
* (renderer, scope providers, store axis); only this key's output lands in
|
|
163
|
+
* the returned container. Call again with another key to view a sibling
|
|
164
|
+
* slot of the same tree.
|
|
165
|
+
* @param key - a key declared through {@link SlotTestRuntime.declare}.
|
|
166
|
+
* @param owner - owner props share for the render site.
|
|
167
|
+
* @returns the slot-local view (snapshot container, scoped queries, owner updates).
|
|
168
|
+
*/
|
|
169
|
+
renderSlot<K extends keyof SlotMap & string>(key: K, owner: OwnerOf<K>): SlotView<K>;
|
|
170
|
+
/**
|
|
171
|
+
* Resolve the store instance the renderer would hand a slot's component
|
|
172
|
+
* (identity assertions, action-driven writes). Requires a prior
|
|
173
|
+
* {@link SlotTestRuntime.renderRoot} — the host face exists only inside the
|
|
174
|
+
* installed renderer, exactly as in production.
|
|
175
|
+
* @param key - slot key whose first entry declares the store.
|
|
176
|
+
* @param scopeKey - session id for session-scope slots; omit for root scope.
|
|
177
|
+
* @returns the live store instance.
|
|
178
|
+
*/
|
|
179
|
+
storeOf(key: keyof SlotMap & string, scopeKey?: string): StoreInstanceLike;
|
|
180
|
+
/**
|
|
181
|
+
* Flush pending ledger/store notifications inside act — for mutations made
|
|
182
|
+
* outside the runtime's own methods (e.g. a direct `slots.register`).
|
|
183
|
+
* @returns completion of the act pass.
|
|
184
|
+
*/
|
|
185
|
+
flush(): Promise<void>;
|
|
186
|
+
/**
|
|
187
|
+
* Tear down: unmount React trees first, then dispose feature fibers, the
|
|
188
|
+
* root registration, minted session scopes, and persisted test state.
|
|
189
|
+
* Idempotent.
|
|
190
|
+
* @returns completion of the teardown.
|
|
191
|
+
*/
|
|
192
|
+
dispose(): Promise<void>;
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `@deepseek-ai/dsh-client-test-runtime`.
|
|
3
|
+
* @module @deepseek-ai/dsh-client-test-runtime/invariant
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export declare const name = "client-test-runtime-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register this package's invariant companion.
|
|
12
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
+
*/
|
|
15
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
+
//# sourceMappingURL=invariant.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pin `navigator.languages`/`navigator.language` for every test in the
|
|
3
|
+
* calling file (or describe block), restoring the environment's own values
|
|
4
|
+
* afterwards. Call at suite level, like the other vitest hooks.
|
|
5
|
+
* @param primary - most preferred BCP 47 tag; also becomes `navigator.language`.
|
|
6
|
+
* @param rest - further tags in preference order.
|
|
7
|
+
*/
|
|
8
|
+
export declare function usePinnedBrowserLanguages(primary: string, ...rest: string[]): void;
|
|
9
|
+
//# sourceMappingURL=locale-env.d.ts.map
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/** Test-owned sessions face: the SlotsService host contract over declarative fixtures. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { AttachmentIdType } from '@deepseek-ai/dsh-attachment';
|
|
4
|
+
import type { AgentContext, ConversationSnapshot, ISessions, ProjectionsFace, SessionFace, SessionId, SessionListState, SessionProvideDescriptor, SessionSearchResultItem, SessionSummary, SnapshotStore, SubagentAddress } from '@deepseek-ai/dsh-client-runtime/client';
|
|
5
|
+
import type { HostObservable, SessionMaybeProvideInfo, SessionProvideInfo } from '@deepseek-ai/dsh-client-ui-slots';
|
|
6
|
+
import type { SessionFixture, Stabilizer } from './fixtures.ts';
|
|
7
|
+
/**
|
|
8
|
+
* The fixture-backed session face: conversation reads delegate to the
|
|
9
|
+
* fixture's snapshot store; ISession verbs are fail-loud stubs unless the
|
|
10
|
+
* fixture supplies them (the runtime never fakes behavior a test did not
|
|
11
|
+
* declare — an unstubbed call names itself instead of half-working). Extra
|
|
12
|
+
* fixture methods are grafted verbatim for feature-side casts.
|
|
13
|
+
*/
|
|
14
|
+
export declare class FixtureSession implements SessionFace {
|
|
15
|
+
readonly sessionId: SessionId;
|
|
16
|
+
private readonly store;
|
|
17
|
+
/**
|
|
18
|
+
* The useProjection seat: identity-stable per-key faces over the fixture's
|
|
19
|
+
* projection values (set via {@link TestSessions.setProjection}).
|
|
20
|
+
*/
|
|
21
|
+
readonly projections: ProjectionsFace & {
|
|
22
|
+
set(key: string, value: unknown): void;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* @param sessionId - host identity (branded view of the fixture id).
|
|
26
|
+
* @param store - conversation snapshot store (updateSnapshot writes it).
|
|
27
|
+
* @param overrides - fixture-declared behavior face, grafted over the stubs.
|
|
28
|
+
*/
|
|
29
|
+
constructor(sessionId: SessionId, store: SnapshotStore<ConversationSnapshot>, overrides: Record<string, unknown>);
|
|
30
|
+
/** @returns the fixture conversation snapshot (useSession read side). */
|
|
31
|
+
getSnapshot(): ConversationSnapshot;
|
|
32
|
+
/**
|
|
33
|
+
* Subscribe to fixture snapshot changes.
|
|
34
|
+
* @param fn - change callback.
|
|
35
|
+
* @returns unsubscribe.
|
|
36
|
+
*/
|
|
37
|
+
subscribe(fn: () => void): () => void;
|
|
38
|
+
/**
|
|
39
|
+
* Fail-loud stub; supply `prompt` on the fixture's session face to exercise it.
|
|
40
|
+
* @returns never — always throws.
|
|
41
|
+
*/
|
|
42
|
+
prompt(): never;
|
|
43
|
+
/**
|
|
44
|
+
* Fail-loud stub; supply `readAttachment` on the fixture's session face to exercise it.
|
|
45
|
+
* @param _attachmentId - opaque durable attachment id.
|
|
46
|
+
* @returns never — always throws.
|
|
47
|
+
*/
|
|
48
|
+
readAttachment(_attachmentId: AttachmentIdType): never;
|
|
49
|
+
/**
|
|
50
|
+
* Fail-loud stub; supply `updateQueue` on the fixture's session face to exercise it.
|
|
51
|
+
* @returns never — always throws.
|
|
52
|
+
*/
|
|
53
|
+
updateQueue(): never;
|
|
54
|
+
/**
|
|
55
|
+
* Fail-loud stub; supply `cancel` on the fixture's session face to exercise it.
|
|
56
|
+
* @returns never — always throws.
|
|
57
|
+
*/
|
|
58
|
+
cancel(): never;
|
|
59
|
+
/**
|
|
60
|
+
* Fail-loud stub; supply `command` on the fixture's session face to exercise it.
|
|
61
|
+
* @returns never — always throws.
|
|
62
|
+
*/
|
|
63
|
+
command(): never;
|
|
64
|
+
/**
|
|
65
|
+
* Fail-loud stub; supply `loadOlder` on the fixture's session face to exercise it.
|
|
66
|
+
* @returns never — always throws.
|
|
67
|
+
*/
|
|
68
|
+
loadOlder(): never;
|
|
69
|
+
/**
|
|
70
|
+
* Fail-loud stub; supply `rename` on the fixture's session face to exercise it.
|
|
71
|
+
* @returns never — always throws.
|
|
72
|
+
*/
|
|
73
|
+
rename(): never;
|
|
74
|
+
}
|
|
75
|
+
/** Test binding shape handed to provider resolvers and feature injects (a SessionBinding whose session is the fixture face). */
|
|
76
|
+
export interface TestSessionBinding {
|
|
77
|
+
readonly sessionId: SessionId;
|
|
78
|
+
readonly session: FixtureSession;
|
|
79
|
+
readonly ctx: AgentContext;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Sessions test double behind the renderer host and feature injects: owns the
|
|
83
|
+
* list/current observable, the standard-props provide channel (the runtime's
|
|
84
|
+
* `useSession` contribution included), scope minting through the production
|
|
85
|
+
* `createScope`, and the session behavior face supplied per fixture.
|
|
86
|
+
*
|
|
87
|
+
* Implements the same ISessions face features receive as `ctx.sessions`, so
|
|
88
|
+
* a production face change breaks this double at compile time; the extra
|
|
89
|
+
* members (add/updateSnapshot/setCurrent/remove/behavior/calls/stubSearch and
|
|
90
|
+
* the legacy provideInfo/maybeProvideInfo lookups) are bench-only surface.
|
|
91
|
+
*/
|
|
92
|
+
export declare class TestSessions implements ISessions {
|
|
93
|
+
private readonly stabilize;
|
|
94
|
+
private readonly rootCtx;
|
|
95
|
+
/** The useSessions standard feed (list rows + current selection). */
|
|
96
|
+
readonly list: SnapshotStore<SessionListState>;
|
|
97
|
+
/**
|
|
98
|
+
* Atomic current-session provide projection (production SessionsService
|
|
99
|
+
* mirror): selection changes and provider-roster changes publish through
|
|
100
|
+
* this one source — the member the SlotsService host face hands the
|
|
101
|
+
* renderer's SessionProvider.
|
|
102
|
+
*/
|
|
103
|
+
readonly currentProvideInfo: HostObservable<SessionMaybeProvideInfo>;
|
|
104
|
+
private readonly records;
|
|
105
|
+
/** The production provide channel (roster, materialization rules, current projection) — no test-side mirror. */
|
|
106
|
+
private readonly channel;
|
|
107
|
+
/** Calls observed on the service-level face, newest last. */
|
|
108
|
+
readonly calls: {
|
|
109
|
+
method: 'open' | 'openSubagent' | 'setSubagentCatalogOpen' | 'refreshSubagents' | 'clear' | 'search' | 'fork';
|
|
110
|
+
args: unknown[];
|
|
111
|
+
}[];
|
|
112
|
+
/** The wire schema's `session.search` result bound (production parity). */
|
|
113
|
+
readonly searchResultLimit = 20;
|
|
114
|
+
/** Replaceable search behavior (see {@link TestSessions.stubSearch}). */
|
|
115
|
+
private searchStub;
|
|
116
|
+
/**
|
|
117
|
+
* @param stabilize - the owning runtime's act wrapper.
|
|
118
|
+
* @param rootCtx - the runtime's Cordis root; scope fibers mount under it.
|
|
119
|
+
*/
|
|
120
|
+
constructor(stabilize: Stabilizer, rootCtx: Context);
|
|
121
|
+
/**
|
|
122
|
+
* Add a session from a fixture and (by default) make it current.
|
|
123
|
+
* @param fixture - identity + snapshot/summary overrides + behavior face.
|
|
124
|
+
* @param opts - pass `current: false` to add without selecting.
|
|
125
|
+
* @returns the stable session id (branded view of `fixture.id`).
|
|
126
|
+
*/
|
|
127
|
+
add(fixture: SessionFixture, opts?: {
|
|
128
|
+
current?: boolean;
|
|
129
|
+
}): Promise<SessionId>;
|
|
130
|
+
/**
|
|
131
|
+
* Update a session's conversation snapshot through an immer draft (the
|
|
132
|
+
* live-stream stand-in: components subscribed via useSession re-render).
|
|
133
|
+
* @param id - session id.
|
|
134
|
+
* @param mutate - draft mutator.
|
|
135
|
+
*/
|
|
136
|
+
updateSnapshot(id: string, mutate: (draft: ConversationSnapshot) => void): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Update a session's list row (the wire-echo stand-in: title settles,
|
|
139
|
+
* running flips — components subscribed via useSessions re-render).
|
|
140
|
+
* @param id - session id.
|
|
141
|
+
* @param patch - summary fields to merge over the row.
|
|
142
|
+
*/
|
|
143
|
+
updateSummary(id: string, patch: Partial<Omit<SessionSummary, 'id'>>): Promise<void>;
|
|
144
|
+
/**
|
|
145
|
+
* Switch the current selection (undefined = the no-session empty state).
|
|
146
|
+
* @param id - session id to select, or undefined to clear.
|
|
147
|
+
*/
|
|
148
|
+
setCurrent(id: string | undefined): Promise<void>;
|
|
149
|
+
/**
|
|
150
|
+
* Remove a session: list row, scope fiber, and per-session store instances
|
|
151
|
+
* (with persisted state) die together — the same single lifecycle axis the
|
|
152
|
+
* production SessionsService drives on session death, minus staging.
|
|
153
|
+
* @param id - session id.
|
|
154
|
+
*/
|
|
155
|
+
remove(id: string): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* Register a per-session standard-props provider (production `provide`
|
|
158
|
+
* contract: hooks become `use<Name>` selector hooks on the render side,
|
|
159
|
+
* props spread verbatim; duplicate names fail loud at materialization).
|
|
160
|
+
* @param descriptor - static member roster plus per-session resolver.
|
|
161
|
+
* @returns disposer removing the provider.
|
|
162
|
+
*/
|
|
163
|
+
provide(descriptor: SessionProvideDescriptor): () => void;
|
|
164
|
+
/**
|
|
165
|
+
* Resolve the definite per-session standard-props bundle (host face member).
|
|
166
|
+
* @param id - session id.
|
|
167
|
+
* @returns the identity-stable bundle, or undefined for unknown sessions.
|
|
168
|
+
*/
|
|
169
|
+
provideInfo(id: string): SessionProvideInfo | undefined;
|
|
170
|
+
/**
|
|
171
|
+
* Resolve the current-session-optional standard kit (host face member):
|
|
172
|
+
* unknown or absent ids return the static no-session projection.
|
|
173
|
+
* @param id - current session id, when selected.
|
|
174
|
+
* @returns a definite or no-session provide bundle.
|
|
175
|
+
*/
|
|
176
|
+
maybeProvideInfo(id: string | undefined): SessionMaybeProvideInfo;
|
|
177
|
+
/**
|
|
178
|
+
* Resolve (mint on first touch) the session-scoped Cordis context through
|
|
179
|
+
* the production `createScope`, so real `scopeOf`/scope-addressed services
|
|
180
|
+
* resolve it.
|
|
181
|
+
* @param id - session id.
|
|
182
|
+
* @returns the scoped context, or undefined for unknown sessions.
|
|
183
|
+
*/
|
|
184
|
+
scope(id: string): AgentContext | undefined;
|
|
185
|
+
/**
|
|
186
|
+
* Session assembly binding (inject factories and provide resolvers receive it).
|
|
187
|
+
* @param id - session id.
|
|
188
|
+
* @returns sessionId + behavior face + scoped ctx, or undefined when unknown.
|
|
189
|
+
*/
|
|
190
|
+
binding(id: string): TestSessionBinding | undefined;
|
|
191
|
+
/**
|
|
192
|
+
* Read the session scope tag off a context (service-method boundary mirror).
|
|
193
|
+
* @param ctx - any client context.
|
|
194
|
+
* @returns the session id, or undefined on root contexts.
|
|
195
|
+
*/
|
|
196
|
+
scopeOf(ctx: Context): SessionId | undefined;
|
|
197
|
+
/**
|
|
198
|
+
* Resolve the scoped session face off a context (production `sessionOf`
|
|
199
|
+
* mirror).
|
|
200
|
+
* @param ctx - any client context.
|
|
201
|
+
* @returns the fixture session face, or undefined off-scope.
|
|
202
|
+
*/
|
|
203
|
+
sessionOf(ctx: Context): SessionFace | undefined;
|
|
204
|
+
/**
|
|
205
|
+
* Service-level selection call (recorded, then applied to the list store
|
|
206
|
+
* synchronously — inject callbacks call this outside any act window; the
|
|
207
|
+
* store notify is microtask-batched so the next stabilized step observes it).
|
|
208
|
+
* @param id - session id.
|
|
209
|
+
*/
|
|
210
|
+
open(id: SessionId): void;
|
|
211
|
+
/** Open an existing fixture through its catalog address. */
|
|
212
|
+
openSubagent(address: SubagentAddress): void;
|
|
213
|
+
/** Resolve the current fixture's retained catalog address. */
|
|
214
|
+
subagentAddress(id: SessionId): SubagentAddress | undefined;
|
|
215
|
+
/** Record catalog consumption; fixture callers drive snapshots explicitly. */
|
|
216
|
+
setSubagentCatalogOpen(parentSessionId: SessionId, open: boolean): void;
|
|
217
|
+
/** Record a catalog refresh; fixture callers drive snapshots explicitly. */
|
|
218
|
+
refreshSubagents(parentSessionId: SessionId): Promise<void>;
|
|
219
|
+
/** Apply a confirmed preset switch into the fixture list, as production does. */
|
|
220
|
+
noteAgentPreset(sessionId: SessionId, agentPreset: string): void;
|
|
221
|
+
/** Clear the current selection (recorded; the production no-session flow). */
|
|
222
|
+
clear(): void;
|
|
223
|
+
/**
|
|
224
|
+
* Replace the sidebar-search result page (the call is still recorded).
|
|
225
|
+
* @param impl - hits for a query, as the Host would rank them.
|
|
226
|
+
*/
|
|
227
|
+
stubSearch(impl: (query: string, signal: AbortSignal) => {
|
|
228
|
+
items: SessionSearchResultItem[];
|
|
229
|
+
hasMore: boolean;
|
|
230
|
+
}): void;
|
|
231
|
+
/**
|
|
232
|
+
* Content search over the fixture corpus (recorded). The default answers an
|
|
233
|
+
* empty page: content ranking is Host behavior, so a scenario that asserts
|
|
234
|
+
* hits declares them through {@link TestSessions.stubSearch}.
|
|
235
|
+
* @param query - non-blank literal phrase.
|
|
236
|
+
* @param signal - cancellation for a superseded search (recorded and forwarded).
|
|
237
|
+
* @returns the stubbed or empty result page.
|
|
238
|
+
*/
|
|
239
|
+
search(query: string, signal: AbortSignal): ReturnType<ISessions['search']>;
|
|
240
|
+
/**
|
|
241
|
+
* Recorded fork stub: no child materializes (benches asserting the full
|
|
242
|
+
* fork flow drive the production service; this face only proves the call).
|
|
243
|
+
* @param opts - source session id, optional cut anchor, and client title policy.
|
|
244
|
+
* @returns the source id (no child record is created).
|
|
245
|
+
*/
|
|
246
|
+
fork(opts: {
|
|
247
|
+
sessionId: SessionId;
|
|
248
|
+
atSeq?: number;
|
|
249
|
+
increaseTitle?: boolean;
|
|
250
|
+
}): Promise<SessionId>;
|
|
251
|
+
/**
|
|
252
|
+
* The session face of a fixture (typed view for assertions; fixture
|
|
253
|
+
* behavior methods are grafted onto it).
|
|
254
|
+
* @param id - session id.
|
|
255
|
+
* @returns the FixtureSession the binding and provide channel carry.
|
|
256
|
+
*/
|
|
257
|
+
behavior(id: string): FixtureSession;
|
|
258
|
+
/** Dispose minted scope fibers (runtime dispose path). */
|
|
259
|
+
disposeScopes(): Promise<void>;
|
|
260
|
+
private bindingOf;
|
|
261
|
+
private require;
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=sessions.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Test double for the client settings-scope seam. */
|
|
2
|
+
import { vi } from 'vitest';
|
|
3
|
+
import type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-runtime/client';
|
|
4
|
+
/** Handle over one stubbed scope: the scope, its write spy, and publication controls. */
|
|
5
|
+
export interface StubSettingsScope<T> {
|
|
6
|
+
/** The scope face handed to the service under test. */
|
|
7
|
+
scope: SettingsScope<T>;
|
|
8
|
+
/** Spy behind `scope.set`; resolves immediately. */
|
|
9
|
+
set: ReturnType<typeof vi.fn>;
|
|
10
|
+
/** @returns how many listeners are currently subscribed (disposal assertions). */
|
|
11
|
+
listenerCount(): number;
|
|
12
|
+
/**
|
|
13
|
+
* Replace part of the snapshot and notify subscribers, as a Host
|
|
14
|
+
* acceptance would.
|
|
15
|
+
* @param next - snapshot fields to replace.
|
|
16
|
+
*/
|
|
17
|
+
publish(next: Partial<SettingsScopeSnapshot<T>>): void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Build an in-memory settings scope for service specs: starts in the host
|
|
21
|
+
* loading state, records writes, and lets the test publish Host acceptances.
|
|
22
|
+
* @returns the stub handle.
|
|
23
|
+
*/
|
|
24
|
+
export declare function stubSettingsScope<T>(): StubSettingsScope<T>;
|
|
25
|
+
//# sourceMappingURL=settings-scope.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SnapshotSerializer } from 'vitest';
|
|
2
|
+
/**
|
|
3
|
+
* The serializer plugin. Matches DOM elements whose subtree carries a scoped
|
|
4
|
+
* class or svg internals; serializes a normalized clone, which no longer
|
|
5
|
+
* matches, so printing falls through to the built-in DOM element serializer.
|
|
6
|
+
*/
|
|
7
|
+
export declare const domSnapshotSerializer: SnapshotSerializer;
|
|
8
|
+
/**
|
|
9
|
+
* Register {@link domSnapshotSerializer} with vitest's expect (idempotent).
|
|
10
|
+
* SlotTestRuntime.create() calls this; specs that snapshot DOM outside the
|
|
11
|
+
* runtime import and call it themselves.
|
|
12
|
+
*/
|
|
13
|
+
export declare function registerDomSnapshotSerializer(): void;
|
|
14
|
+
//# sourceMappingURL=snapshot.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test double of the locale lookup chain: a translate stub over plain
|
|
3
|
+
* dictionaries, mirroring LocaleService's resolution order (first dictionary
|
|
4
|
+
* that owns the key wins, then the key itself stays visible) and its
|
|
5
|
+
* `{name}` template interpolation. Specs stub the framework-injected `t`
|
|
6
|
+
* seat with `makeTranslate(zh, commonZh)` instead of re-implementing the
|
|
7
|
+
* chain per suite.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Build a translate stub resolving through `dicts` in order (namespace
|
|
11
|
+
* first, then the shared common vocabulary), falling back to the key.
|
|
12
|
+
* @param dicts - dictionaries consulted in order.
|
|
13
|
+
* @returns the translate function (assignable to any `XxxProps['t']` seat).
|
|
14
|
+
*/
|
|
15
|
+
export declare function makeTranslate(...dicts: readonly Record<string, string>[]): (key: string, params?: Record<string, unknown>) => string;
|
|
16
|
+
//# sourceMappingURL=translate.d.ts.map
|