gentle-shell-bridge 0.3.0 → 0.3.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/LICENSE +202 -0
- package/README.md +91 -722
- package/docs/contract.md +92 -35
- package/package.json +13 -1
- package/src/api.ts +1 -1
- package/.codegraph/.gitignore +0 -5
- package/PRD.md +0 -144
- package/examples/bash-card.ts +0 -1694
- package/gentle-shell-bridge-0.3.0.tgz +0 -0
- package/odd/tasks/palette-sections.md +0 -30
- package/odd/tasks/plugin-keymap.md +0 -32
- package/odd/tasks/plugin-surface-contract.md +0 -55
- package/test/api.test.ts +0 -395
- package/test/apply-on-move.test.ts +0 -477
- package/test/arm.test.ts +0 -220
- package/test/bridge-ready.test.ts +0 -309
- package/test/bus.test.ts +0 -165
- package/test/button-rows.test.ts +0 -409
- package/test/capture.test.ts +0 -356
- package/test/card-style-rows.test.ts +0 -510
- package/test/card-style-transform.test.ts +0 -379
- package/test/card-style-wiring.test.ts +0 -258
- package/test/card-styles-e2e.test.ts +0 -491
- package/test/card-styles.test.ts +0 -324
- package/test/card-transcript-path.test.ts +0 -178
- package/test/config.test.ts +0 -93
- package/test/e2e.test.ts +0 -443
- package/test/example-bash-identity.test.ts +0 -372
- package/test/example-bash-matrix.test.ts +0 -856
- package/test/example-designs.test.ts +0 -734
- package/test/example-neon-transform.test.ts +0 -283
- package/test/example-plain-style.test.ts +0 -1268
- package/test/extension.test.ts +0 -160
- package/test/fake-events.ts +0 -58
- package/test/fake-host.ts +0 -211
- package/test/fake-sidebar.ts +0 -516
- package/test/fake-transcript.ts +0 -486
- package/test/frame-telemetry.test.ts +0 -417
- package/test/hook.test.ts +0 -196
- package/test/manifest.test.ts +0 -105
- package/test/new-category.test.ts +0 -979
- package/test/notices.test.ts +0 -328
- package/test/plugin-integration-lines.test.ts +0 -979
- package/test/plugin-surface.test.ts +0 -152
- package/test/presence.test.ts +0 -459
- package/test/rail-surfaces.test.ts +0 -457
- package/test/rail-width.test.ts +0 -423
- package/test/regression.test.ts +0 -483
- package/test/repaint-api.test.ts +0 -200
- package/test/reserved-categories.test.ts +0 -361
- package/test/row-labels.test.ts +0 -284
- package/test/row-names.test.ts +0 -473
- package/test/row-origin.test.ts +0 -395
- package/test/section-interaction.test.ts +0 -364
- package/test/section-panel.test.ts +0 -403
- package/test/sections-rows.test.ts +0 -291
- package/test/sidebar-backend.test.ts +0 -471
- package/test/sidebar-e2e.test.ts +0 -331
- package/test/sidebar-hover.test.ts +0 -276
- package/test/sidebar-independent.test.ts +0 -349
- package/test/sidebar-render.test.ts +0 -281
- package/test/sidebar-sections.test.ts +0 -245
- package/test/status-card-styles.test.ts +0 -201
- package/test/status-section-frame.test.ts +0 -181
- package/test/status-sections.test.ts +0 -243
- package/test/status.test.ts +0 -209
- package/test/store.test.ts +0 -192
- package/test/surface-repaint-cost.test.ts +0 -520
- package/test/tool-design-chain.test.ts +0 -483
- package/test/tool-design-cost.test.ts +0 -473
- package/test/tool-design-rows.test.ts +0 -359
- package/test/tool-designs.test.ts +0 -745
- package/test/transform-pipeline.test.ts +0 -465
- package/test/transforms-bus.test.ts +0 -233
- package/test/verifier-width-regressions.test.ts +0 -164
- package/tsconfig.json +0 -18
package/test/e2e.test.ts
DELETED
|
@@ -1,443 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import test from "node:test";
|
|
6
|
-
|
|
7
|
-
// The public surface, exactly as a plugin reaches it.
|
|
8
|
-
import * as publicApi from "../src/index.ts";
|
|
9
|
-
import {
|
|
10
|
-
createStore,
|
|
11
|
-
registerRows,
|
|
12
|
-
STORE_SCHEMA_VERSION,
|
|
13
|
-
VERSION,
|
|
14
|
-
type CardFrame,
|
|
15
|
-
type CardFrameBottomContext,
|
|
16
|
-
type CardFrameLineContext,
|
|
17
|
-
type CardFrameTopContext,
|
|
18
|
-
type CardStyleRegistration,
|
|
19
|
-
type CustomizeRow,
|
|
20
|
-
type InlineHelpers,
|
|
21
|
-
type RowChoice,
|
|
22
|
-
type RowContext,
|
|
23
|
-
type RowLabel,
|
|
24
|
-
type RowPreview,
|
|
25
|
-
type RowProvider,
|
|
26
|
-
type SectionContext,
|
|
27
|
-
type SidebarSection,
|
|
28
|
-
type Store,
|
|
29
|
-
type StoreProblem,
|
|
30
|
-
type StoreProblemKind,
|
|
31
|
-
type TransformContext,
|
|
32
|
-
type TranscriptTransform,
|
|
33
|
-
} from "../src/index.ts";
|
|
34
|
-
// The real Pi entry: armed and commanded exactly as Pi loads it.
|
|
35
|
-
import gentleShellBridge, {
|
|
36
|
-
STATUS_COMMAND,
|
|
37
|
-
type BridgeCommandHandler,
|
|
38
|
-
type BridgeExtensionApi,
|
|
39
|
-
type BridgeSessionHandler,
|
|
40
|
-
} from "../src/extension.ts";
|
|
41
|
-
// Teardown-only seams; the API-surface test proves they are not public.
|
|
42
|
-
import { resetBusForTests } from "../src/bus.ts";
|
|
43
|
-
import { resetCaptureStateForTests } from "../src/capture.ts";
|
|
44
|
-
import { resetHookStateForTests } from "../src/hook.ts";
|
|
45
|
-
import { resetRowBuildStateForTests } from "../src/rows.ts";
|
|
46
|
-
import { resetStoreStateForTests } from "../src/store.ts";
|
|
47
|
-
import { createFakeHost, type FakeHost } from "./fake-host.ts";
|
|
48
|
-
|
|
49
|
-
const HOOK_KEY = Symbol.for("gentle-pi.customize-row-providers");
|
|
50
|
-
const hookSlot = (): Record<symbol, unknown> => globalThis as unknown as Record<symbol, unknown>;
|
|
51
|
-
|
|
52
|
-
let configHome: string;
|
|
53
|
-
let previousConfigHome: string | undefined;
|
|
54
|
-
let pristineMap: typeof Array.prototype.map;
|
|
55
|
-
|
|
56
|
-
test.before(() => {
|
|
57
|
-
pristineMap = Array.prototype.map;
|
|
58
|
-
configHome = mkdtempSync(join(tmpdir(), "gsb-e2e-"));
|
|
59
|
-
previousConfigHome = process.env.GENTLE_PI_CONFIG_HOME;
|
|
60
|
-
process.env.GENTLE_PI_CONFIG_HOME = configHome;
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test.after(() => {
|
|
64
|
-
if (previousConfigHome === undefined) delete process.env.GENTLE_PI_CONFIG_HOME;
|
|
65
|
-
else process.env.GENTLE_PI_CONFIG_HOME = previousConfigHome;
|
|
66
|
-
Array.prototype.map = pristineMap;
|
|
67
|
-
rmSync(configHome, { recursive: true, force: true });
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test.beforeEach(() => {
|
|
71
|
-
resetBusForTests();
|
|
72
|
-
resetCaptureStateForTests();
|
|
73
|
-
resetRowBuildStateForTests();
|
|
74
|
-
resetHookStateForTests();
|
|
75
|
-
resetStoreStateForTests();
|
|
76
|
-
delete hookSlot()[HOOK_KEY];
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test.afterEach(() => {
|
|
80
|
-
Array.prototype.map = pristineMap;
|
|
81
|
-
delete hookSlot()[HOOK_KEY];
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
interface Entry {
|
|
85
|
-
sessionHandlers: BridgeSessionHandler[];
|
|
86
|
-
commands: Array<{ name: string; description?: string; handler: BridgeCommandHandler }>;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Load the real extension entry against a recording API, as Pi would. */
|
|
90
|
-
const loadEntry = (): Entry => {
|
|
91
|
-
const sessionHandlers: BridgeSessionHandler[] = [];
|
|
92
|
-
const commands: Entry["commands"] = [];
|
|
93
|
-
const pi: BridgeExtensionApi = {
|
|
94
|
-
// `gentle-pi` is loaded, so the runtime gate is open (S9/T12).
|
|
95
|
-
getCommands: () => [{ name: "gentle:customize" }],
|
|
96
|
-
on(_event, handler) {
|
|
97
|
-
sessionHandlers.push(handler);
|
|
98
|
-
},
|
|
99
|
-
registerCommand(name, command) {
|
|
100
|
-
commands.push({ name, description: command.description, handler: command.handler });
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
gentleShellBridge(pi);
|
|
104
|
-
return { sessionHandlers, commands };
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
/** Pi fires `session_start`; the entry arms the bridge. */
|
|
108
|
-
const armThroughEntry = (ui: unknown): Entry => {
|
|
109
|
-
const entry = loadEntry();
|
|
110
|
-
entry.sessionHandlers[0]({}, { hasUI: true, ui });
|
|
111
|
-
return entry;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
/** Run the registered `status` command and return exactly what it prints. */
|
|
115
|
-
const printStatus = (entry: Entry): string => {
|
|
116
|
-
const printed: string[] = [];
|
|
117
|
-
entry.commands[0].handler("", { ui: { notify: (message: string) => printed.push(message) } });
|
|
118
|
-
return printed.join("\n");
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const labels = (id: string, category: string, values: string[]): RowProvider => ({
|
|
122
|
-
id,
|
|
123
|
-
category,
|
|
124
|
-
rows: () => values.map((label) => ({ label, action: () => {} })),
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const armAndBuild = (host: FakeHost): Entry => {
|
|
128
|
-
const entry = armThroughEntry(host.ui);
|
|
129
|
-
host.buildMenu();
|
|
130
|
-
return entry;
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
test("two providers reach one menu build through registerRows, in subscription order", () => {
|
|
134
|
-
registerRows(labels("first", "Cards", ["First a"]));
|
|
135
|
-
registerRows(labels("second", "Banner", ["Second b"]));
|
|
136
|
-
const host = createFakeHost();
|
|
137
|
-
|
|
138
|
-
armAndBuild(host);
|
|
139
|
-
|
|
140
|
-
assert.deepEqual(host.rendered().slice(4), ["First a", "Second b"]);
|
|
141
|
-
assert.deepEqual(
|
|
142
|
-
host.liveRows().slice(4).map((row) => row.category),
|
|
143
|
-
["Cards", "Banner"],
|
|
144
|
-
);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test("a throwing provider loses only its rows, the menu still renders, and status records it", () => {
|
|
148
|
-
registerRows(labels("before", "Cards", ["Before ok"]));
|
|
149
|
-
registerRows({
|
|
150
|
-
id: "boom",
|
|
151
|
-
category: "Banner",
|
|
152
|
-
rows: () => {
|
|
153
|
-
throw new Error("e2e provider exploded");
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
registerRows(labels("after", "Layout", ["After ok"]));
|
|
157
|
-
const host = createFakeHost();
|
|
158
|
-
|
|
159
|
-
const entry = armAndBuild(host);
|
|
160
|
-
|
|
161
|
-
assert.deepEqual(host.rendered().slice(4), ["Before ok", "After ok"]);
|
|
162
|
-
const status = printStatus(entry);
|
|
163
|
-
assert.match(status, /^last failures: 1$/m);
|
|
164
|
-
assert.match(status, /boom: e2e provider exploded \(rows dropped\)/);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
test("Array.prototype.map identity survives success, a throwing factory and a non-row array", () => {
|
|
168
|
-
registerRows(labels("p1", "Cards", ["Row"]));
|
|
169
|
-
|
|
170
|
-
const okHost = createFakeHost();
|
|
171
|
-
armThroughEntry(okHost.ui);
|
|
172
|
-
const beforeSuccess = Array.prototype.map;
|
|
173
|
-
okHost.buildMenu();
|
|
174
|
-
assert.equal(Array.prototype.map, beforeSuccess);
|
|
175
|
-
|
|
176
|
-
const throwingHost = createFakeHost({ throwInsideFactory: true });
|
|
177
|
-
armThroughEntry(throwingHost.ui);
|
|
178
|
-
const beforeThrow = Array.prototype.map;
|
|
179
|
-
assert.throws(() => throwingHost.buildMenu(), /factory exploded/);
|
|
180
|
-
assert.equal(Array.prototype.map, beforeThrow);
|
|
181
|
-
|
|
182
|
-
const nonRowHost = createFakeHost({ mapOnlyNonRows: true });
|
|
183
|
-
armThroughEntry(nonRowHost.ui);
|
|
184
|
-
const beforeNonRows = Array.prototype.map;
|
|
185
|
-
nonRowHost.buildMenu();
|
|
186
|
-
assert.equal(Array.prototype.map, beforeNonRows);
|
|
187
|
-
assert.deepEqual(
|
|
188
|
-
nonRowHost.rendered().filter((label) => label === "Row"),
|
|
189
|
-
[],
|
|
190
|
-
"a plain array of numbers is never injected into",
|
|
191
|
-
);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
test("two session_start events through the entry arm once: one injection, one invalidate", () => {
|
|
195
|
-
registerRows(labels("p1", "Cards", ["Once"]));
|
|
196
|
-
const host = createFakeHost();
|
|
197
|
-
const entry = loadEntry();
|
|
198
|
-
const context = { hasUI: true, ui: host.ui };
|
|
199
|
-
|
|
200
|
-
entry.sessionHandlers[0]({}, context);
|
|
201
|
-
entry.sessionHandlers[0]({}, context);
|
|
202
|
-
|
|
203
|
-
host.buildMenu();
|
|
204
|
-
assert.deepEqual(
|
|
205
|
-
host.rendered().filter((label) => label === "Once"),
|
|
206
|
-
["Once"],
|
|
207
|
-
"the rows are injected once, not twice",
|
|
208
|
-
);
|
|
209
|
-
assert.equal(host.invalidateCount(), 1, "one invalidate per menu build");
|
|
210
|
-
assert.equal(host.factoryCallCount(), 1, "the host factory ran once");
|
|
211
|
-
assert.match(printStatus(entry), /^backend: capture$/m);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
test("the hook registry is preferred through the entry: nothing wrapped, rows via the registry", () => {
|
|
215
|
-
const registry: Array<{ id: string; rows: (ctx?: unknown) => CustomizeRow[] }> = [];
|
|
216
|
-
hookSlot()[HOOK_KEY] = registry;
|
|
217
|
-
registerRows(labels("hooked", "Cards", ["Hooked row"]));
|
|
218
|
-
const host = createFakeHost();
|
|
219
|
-
const customBefore = host.ui.custom;
|
|
220
|
-
|
|
221
|
-
const entry = armThroughEntry(host.ui);
|
|
222
|
-
|
|
223
|
-
assert.equal(host.ui.custom, customBefore, "capture patches nothing when the hook exists");
|
|
224
|
-
assert.equal(registry.length, 1, "one fan-out provider in the registry");
|
|
225
|
-
assert.deepEqual(
|
|
226
|
-
registry[0].rows({}).map((row) => row.label),
|
|
227
|
-
["Hooked row"],
|
|
228
|
-
"rows land through the registry's own call path",
|
|
229
|
-
);
|
|
230
|
-
assert.match(printStatus(entry), /^backend: hook$/m);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
test("createStore writes atomically under the real config-home override", () => {
|
|
234
|
-
const store = createStore("e2e");
|
|
235
|
-
const bridgeDir = join(configHome, "gentle-shell-bridge");
|
|
236
|
-
|
|
237
|
-
store.set("bannerSource", "output");
|
|
238
|
-
|
|
239
|
-
assert.ok(store.path.startsWith(bridgeDir), "the store path follows GENTLE_PI_CONFIG_HOME");
|
|
240
|
-
assert.equal(store.path, join(bridgeDir, "e2e.json"));
|
|
241
|
-
assert.equal(store.get("bannerSource", "activity"), "output");
|
|
242
|
-
const entries = readdirSync(bridgeDir);
|
|
243
|
-
assert.ok(entries.includes("e2e.json"));
|
|
244
|
-
assert.deepEqual(entries.filter((name) => name.endsWith(".tmp")), [], "no temp file remains");
|
|
245
|
-
const document = JSON.parse(readFileSync(store.path, "utf8"));
|
|
246
|
-
assert.equal(document.schemaVersion, STORE_SCHEMA_VERSION);
|
|
247
|
-
assert.deepEqual(document.values, { bannerSource: "output" });
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
test("a malformed store file falls back, is reported by bridge-status, and is fixed by a later set", () => {
|
|
251
|
-
const bridgeDir = join(configHome, "gentle-shell-bridge");
|
|
252
|
-
mkdirSync(bridgeDir, { recursive: true });
|
|
253
|
-
const path = join(bridgeDir, "broken.json");
|
|
254
|
-
writeFileSync(path, "{ not json", "utf8");
|
|
255
|
-
const store = createStore("broken");
|
|
256
|
-
|
|
257
|
-
assert.equal(store.get("bannerSource", "activity"), "activity");
|
|
258
|
-
const reported = printStatus(loadEntry());
|
|
259
|
-
assert.match(reported, /^store problems: 1$/m);
|
|
260
|
-
assert.match(reported, /broken \(.*broken\.json\): invalid-json/);
|
|
261
|
-
assert.equal(readFileSync(path, "utf8"), "{ not json", "reads never repair the file");
|
|
262
|
-
|
|
263
|
-
store.set("bannerSource", "output");
|
|
264
|
-
|
|
265
|
-
const document = JSON.parse(readFileSync(path, "utf8"));
|
|
266
|
-
assert.equal(document.schemaVersion, 1);
|
|
267
|
-
assert.deepEqual(document.values, { bannerSource: "output" });
|
|
268
|
-
assert.match(printStatus(loadEntry()), /^store problems: none$/m);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
test("S8 end to end: action persists first and the repaint shows the new bullet", async () => {
|
|
272
|
-
const store = createStore("e2e-toggle");
|
|
273
|
-
const toggle = (value: string): CustomizeRow => ({
|
|
274
|
-
category: "Banner",
|
|
275
|
-
label: () => "Loon banner source",
|
|
276
|
-
choice: {
|
|
277
|
-
setting: "Loon banner source",
|
|
278
|
-
value,
|
|
279
|
-
current: () => store.get("bannerSource", "activity") === value,
|
|
280
|
-
},
|
|
281
|
-
action: () => {
|
|
282
|
-
store.set("bannerSource", value); // persist first
|
|
283
|
-
}, // the view repaints second
|
|
284
|
-
});
|
|
285
|
-
registerRows({
|
|
286
|
-
id: "loon.banner-source",
|
|
287
|
-
category: "Banner",
|
|
288
|
-
rows: () => [toggle("activity"), toggle("output")],
|
|
289
|
-
});
|
|
290
|
-
const host = createFakeHost();
|
|
291
|
-
armThroughEntry(host.ui);
|
|
292
|
-
host.buildMenu();
|
|
293
|
-
|
|
294
|
-
assert.deepEqual(host.toggleLines(), ["Loon banner source ● activity ○ output"]);
|
|
295
|
-
|
|
296
|
-
await host.pressRow((row) => row.choice?.value === "output");
|
|
297
|
-
|
|
298
|
-
assert.equal(store.get("bannerSource", "activity"), "output", "action persisted before repaint");
|
|
299
|
-
assert.deepEqual(
|
|
300
|
-
host.toggleLines(),
|
|
301
|
-
// Row order is fixed by the menu; only the rendered bullet moves onto the new value.
|
|
302
|
-
["Loon banner source ○ activity ● output"],
|
|
303
|
-
"the same menu repaints onto the new value, with no second build",
|
|
304
|
-
);
|
|
305
|
-
assert.equal(host.factoryCallCount(), 1, "no second menu build was needed");
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
test("the public entry exports exactly the contract surface and no test seams", () => {
|
|
309
|
-
assert.deepEqual(Object.keys(publicApi).sort(), [
|
|
310
|
-
"BRIDGE_READY_EVENT",
|
|
311
|
-
"KEYMAP_RESOLVER_KEY",
|
|
312
|
-
"PLUGIN_SURFACE_KEY",
|
|
313
|
-
"STORE_SCHEMA_VERSION",
|
|
314
|
-
"VERSION",
|
|
315
|
-
"bridgeApi",
|
|
316
|
-
"createStore",
|
|
317
|
-
"isBridgeApi",
|
|
318
|
-
"onBridgeApi",
|
|
319
|
-
"registerCardStyle",
|
|
320
|
-
"registerCommands",
|
|
321
|
-
"registerKeymap",
|
|
322
|
-
"registerRows",
|
|
323
|
-
"registerSection",
|
|
324
|
-
"registerToolDesign",
|
|
325
|
-
"registerTransform",
|
|
326
|
-
"resolveKeymap",
|
|
327
|
-
]);
|
|
328
|
-
for (const seam of [
|
|
329
|
-
"installCardStyleTransform",
|
|
330
|
-
"disableCardStyleTransform",
|
|
331
|
-
"getCardStyleWiringDiagnostics",
|
|
332
|
-
"resetCardStyleWiringForTests",
|
|
333
|
-
"transformCardLines",
|
|
334
|
-
"getCardStyleTransformFailures",
|
|
335
|
-
"getCardStyleLastOutcome",
|
|
336
|
-
"resetCardStyleTransformForTests",
|
|
337
|
-
"installCardStyleRows",
|
|
338
|
-
"cardStyleRowMode",
|
|
339
|
-
"extendCardStyleLine",
|
|
340
|
-
"setCardStyleSelection",
|
|
341
|
-
"getCardStyleSelection",
|
|
342
|
-
"resolveCardStyleSelection",
|
|
343
|
-
"snapshotCardStyles",
|
|
344
|
-
"resetCardStylesForTests",
|
|
345
|
-
"CARD_STYLE_NATIVE",
|
|
346
|
-
"HOST_CARD_STYLE_SETTING",
|
|
347
|
-
"resetBusForTests",
|
|
348
|
-
"resetCaptureStateForTests",
|
|
349
|
-
"resetHookStateForTests",
|
|
350
|
-
"resetStoreStateForTests",
|
|
351
|
-
"snapshotSections",
|
|
352
|
-
"resetSectionsForTests",
|
|
353
|
-
"snapshotProviders",
|
|
354
|
-
"installSectionRail",
|
|
355
|
-
"installSectionRows",
|
|
356
|
-
"setRailContributor",
|
|
357
|
-
"getSectionFailures",
|
|
358
|
-
"getSidebarDiagnostics",
|
|
359
|
-
"resetBridgeApiForTests",
|
|
360
|
-
"publishBridgeApi",
|
|
361
|
-
"BRIDGE_API_KEY",
|
|
362
|
-
"snapshotTransforms",
|
|
363
|
-
"resetTransformsForTests",
|
|
364
|
-
"applyTransforms",
|
|
365
|
-
"getTransformFrameOutcomes",
|
|
366
|
-
"resetTransformPipelineForTests",
|
|
367
|
-
]) {
|
|
368
|
-
assert.equal(seam in publicApi, false, `${seam} must not be public`);
|
|
369
|
-
}
|
|
370
|
-
assert.equal(typeof STATUS_COMMAND, "string");
|
|
371
|
-
assert.ok(STATUS_COMMAND.length > 0);
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
test("the documented values and types are reachable from the public entry", () => {
|
|
375
|
-
const provider: RowProvider = { id: "api", category: "Cards", rows: (): CustomizeRow[] => [] };
|
|
376
|
-
// The card-style surface: one registration, three functions, the contexts they receive.
|
|
377
|
-
const cardStyle: CardStyleRegistration = {
|
|
378
|
-
id: "api.cute",
|
|
379
|
-
label: "cute",
|
|
380
|
-
order: 1,
|
|
381
|
-
frame: {
|
|
382
|
-
top: (frameCtx: CardFrameTopContext) => `${frameCtx.title ?? ""}${frameCtx.hint ?? ""}`,
|
|
383
|
-
line: (frameCtx: CardFrameLineContext) => `${frameCtx.text}${frameCtx.first}`,
|
|
384
|
-
bottom: (frameCtx: CardFrameBottomContext) => String(frameCtx.width),
|
|
385
|
-
},
|
|
386
|
-
preview: (): RowPreview => ({ title: "Cute", sample: "sample" }),
|
|
387
|
-
};
|
|
388
|
-
const context: RowContext = { theme: undefined, configHome: configHome };
|
|
389
|
-
const choice: RowChoice = { setting: "s", value: "v", current: () => true };
|
|
390
|
-
const label: RowLabel = "text";
|
|
391
|
-
const preview: RowPreview = { title: "t", sample: "s" };
|
|
392
|
-
const helpers: InlineHelpers = {
|
|
393
|
-
input: async () => undefined,
|
|
394
|
-
confirm: async () => true,
|
|
395
|
-
capture: async () => undefined,
|
|
396
|
-
};
|
|
397
|
-
const store: Store = createStore("api-surface");
|
|
398
|
-
const kind: StoreProblemKind = "invalid-json";
|
|
399
|
-
const problem: StoreProblem = { pluginId: "api", path: "/tmp/x", kind, message: "m" };
|
|
400
|
-
// The section surface is public too: a section is written by a plugin and registered with one call.
|
|
401
|
-
const frame: CardFrame = cardStyle.frame;
|
|
402
|
-
assert.equal(typeof frame.bottom, "function");
|
|
403
|
-
const section: SidebarSection = {
|
|
404
|
-
id: "api.section",
|
|
405
|
-
title: "API",
|
|
406
|
-
meta: (sectionCtx: SectionContext) => sectionCtx.configHome,
|
|
407
|
-
lines: (sectionCtx: SectionContext) => [sectionCtx.configHome],
|
|
408
|
-
defaultVisible: true,
|
|
409
|
-
order: 1,
|
|
410
|
-
};
|
|
411
|
-
// The transform surface: one function over the rows, plus the context it receives.
|
|
412
|
-
const transform: TranscriptTransform = {
|
|
413
|
-
id: "api.transform",
|
|
414
|
-
order: 2,
|
|
415
|
-
transform: (lines: readonly string[], transformCtx: TransformContext) => [
|
|
416
|
-
...lines,
|
|
417
|
-
String(transformCtx.width),
|
|
418
|
-
],
|
|
419
|
-
};
|
|
420
|
-
|
|
421
|
-
assert.equal(provider.rows(context).length, 0);
|
|
422
|
-
assert.equal(typeof publicApi.registerCardStyle, "function", "the entry exports the registration");
|
|
423
|
-
assert.equal(
|
|
424
|
-
typeof publicApi.registerTransform,
|
|
425
|
-
"function",
|
|
426
|
-
"and the transcript-transform registration",
|
|
427
|
-
);
|
|
428
|
-
assert.equal(cardStyle.frame.top({ width: 1, title: "t" }), "t");
|
|
429
|
-
assert.equal(cardStyle.frame.line({ text: "x", width: 1, first: true }), "xtrue");
|
|
430
|
-
assert.equal(cardStyle.frame.bottom({ width: 7 }), "7");
|
|
431
|
-
assert.deepEqual(cardStyle.preview?.(), { title: "Cute", sample: "sample" });
|
|
432
|
-
assert.equal(choice.value, "v");
|
|
433
|
-
assert.equal(label, "text");
|
|
434
|
-
assert.deepEqual(preview, { title: "t", sample: "s" });
|
|
435
|
-
assert.equal(typeof helpers.confirm, "function");
|
|
436
|
-
assert.equal(store.pluginId, "api-surface");
|
|
437
|
-
assert.equal(problem.kind, "invalid-json");
|
|
438
|
-
assert.equal(STORE_SCHEMA_VERSION, 1);
|
|
439
|
-
assert.equal(section.title, "API");
|
|
440
|
-
assert.equal(section.lines(context).length, 1);
|
|
441
|
-
assert.deepEqual(transform.transform(["a"], { width: 3 }), ["a", "3"]);
|
|
442
|
-
assert.equal(VERSION, "0.3.0");
|
|
443
|
-
});
|