@robota-sdk/agent-transport 3.0.0-beta.75 → 3.0.0-beta.76
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.md +10 -10
- package/dist/node/headless/index.cjs +1 -1
- package/dist/node/{headless-CT2ibQnr.cjs → headless-OnpVk4-k.cjs} +7 -7
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.d.ts +1 -6
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +1 -1
- package/dist/node/index.js.map +1 -1
- package/package.json +7 -75
- package/src/index.ts +1 -5
- package/src/transport-registry.ts +0 -9
- package/dist/node/http/index.cjs +0 -1
- package/dist/node/http/index.d.ts +0 -2
- package/dist/node/http/index.js +0 -1
- package/dist/node/http-2Jiuflc1.js +0 -2
- package/dist/node/http-2Jiuflc1.js.map +0 -1
- package/dist/node/http-CBAvefLw.cjs +0 -1
- package/dist/node/index-BNccqSpv.d.ts +0 -86
- package/dist/node/index-BNccqSpv.d.ts.map +0 -1
- package/dist/node/index-BUhHIf7X.d.ts +0 -86
- package/dist/node/index-BUhHIf7X.d.ts.map +0 -1
- package/dist/node/index-BnAGE-u9.d.ts +0 -33
- package/dist/node/index-BnAGE-u9.d.ts.map +0 -1
- package/dist/node/index-BrQ4gGw0.d.ts +0 -213
- package/dist/node/index-BrQ4gGw0.d.ts.map +0 -1
- package/dist/node/index-CoeBF21y.d.ts +0 -213
- package/dist/node/index-CoeBF21y.d.ts.map +0 -1
- package/dist/node/index-DHt-2VQ-.d.ts +0 -46
- package/dist/node/index-DHt-2VQ-.d.ts.map +0 -1
- package/dist/node/index-DMwKN5Le.d.ts +0 -33
- package/dist/node/index-DMwKN5Le.d.ts.map +0 -1
- package/dist/node/index-c0M42fsA.d.ts +0 -46
- package/dist/node/index-c0M42fsA.d.ts.map +0 -1
- package/dist/node/mcp/index.cjs +0 -1
- package/dist/node/mcp/index.d.ts +0 -2
- package/dist/node/mcp/index.js +0 -1
- package/dist/node/mcp-BOglBJNy.cjs +0 -1
- package/dist/node/mcp-D3BBVK7C.js +0 -2
- package/dist/node/mcp-D3BBVK7C.js.map +0 -1
- package/dist/node/rolldown-runtime-CMqjfN_6.cjs +0 -1
- package/dist/node/tui/index.cjs +0 -1
- package/dist/node/tui/index.d.ts +0 -2
- package/dist/node/tui/index.js +0 -1
- package/dist/node/tui-CcH5EsQh.js +0 -25
- package/dist/node/tui-CcH5EsQh.js.map +0 -1
- package/dist/node/tui-DznRbcku.cjs +0 -24
- package/dist/node/ws/index.cjs +0 -1
- package/dist/node/ws/index.d.ts +0 -2
- package/dist/node/ws/index.js +0 -1
- package/dist/node/ws-Dc2RUwVs.js +0 -2
- package/dist/node/ws-Dc2RUwVs.js.map +0 -1
- package/dist/node/ws-QNMQn5kg.cjs +0 -1
- package/src/http/__tests__/http-transport.test.ts +0 -55
- package/src/http/__tests__/routes.test.ts +0 -168
- package/src/http/http-transport.ts +0 -41
- package/src/http/index.ts +0 -4
- package/src/http/routes.ts +0 -152
- package/src/mcp/__tests__/mcp-server.test.ts +0 -66
- package/src/mcp/__tests__/mcp-transport.test.ts +0 -46
- package/src/mcp/index.ts +0 -4
- package/src/mcp/mcp-server.ts +0 -163
- package/src/mcp/mcp-transport.ts +0 -48
- package/src/tui/App.tsx +0 -491
- package/src/tui/BackgroundTaskPanel.tsx +0 -36
- package/src/tui/CjkTextInput.tsx +0 -199
- package/src/tui/ConfirmPrompt.tsx +0 -70
- package/src/tui/ContextWarningBanner.tsx +0 -34
- package/src/tui/ExecutionWorkspaceDetailPane.tsx +0 -64
- package/src/tui/ExecutionWorkspaceSwitcher.tsx +0 -187
- package/src/tui/InputArea.tsx +0 -310
- package/src/tui/InteractivePrompt.tsx +0 -59
- package/src/tui/ListPicker.tsx +0 -95
- package/src/tui/MenuSelect.tsx +0 -104
- package/src/tui/MessageList.tsx +0 -284
- package/src/tui/PermissionPrompt.tsx +0 -86
- package/src/tui/PluginTUI.tsx +0 -258
- package/src/tui/SessionPicker.tsx +0 -68
- package/src/tui/SessionStatusBar.tsx +0 -73
- package/src/tui/SlashAutocomplete.tsx +0 -110
- package/src/tui/StatusBar.tsx +0 -236
- package/src/tui/StreamingIndicator.tsx +0 -93
- package/src/tui/TextPrompt.tsx +0 -81
- package/src/tui/ToolCommandOutput.tsx +0 -39
- package/src/tui/ToolDiffBlock.tsx +0 -32
- package/src/tui/TransportTUI.tsx +0 -117
- package/src/tui/TuiInteractionChannel.ts +0 -495
- package/src/tui/UpdateNotice.tsx +0 -14
- package/src/tui/UsageSummaryEntry.tsx +0 -39
- package/src/tui/WaveText.tsx +0 -44
- package/src/tui/__tests__/InteractivePrompt.test.tsx +0 -82
- package/src/tui/__tests__/ListPicker.test.tsx +0 -159
- package/src/tui/__tests__/MenuSelect.test.tsx +0 -103
- package/src/tui/__tests__/PluginTUI.test.tsx +0 -167
- package/src/tui/__tests__/SlashAutocomplete.test.tsx +0 -140
- package/src/tui/__tests__/TextPrompt.test.tsx +0 -98
- package/src/tui/__tests__/TuiInteractionChannel.display-contract.test.ts +0 -239
- package/src/tui/__tests__/TuiInteractionChannel.lifecycle.test.ts +0 -297
- package/src/tui/__tests__/TuiInteractionChannel.requestAction.test.ts +0 -124
- package/src/tui/__tests__/UpdateNotice.test.tsx +0 -15
- package/src/tui/__tests__/abort-after-permission.test.tsx +0 -169
- package/src/tui/__tests__/abort-streaming-e2e.test.tsx +0 -183
- package/src/tui/__tests__/background-task-panel.test.tsx +0 -53
- package/src/tui/__tests__/background-task-row-format.test.ts +0 -59
- package/src/tui/__tests__/channel-factory-integration.test.ts +0 -138
- package/src/tui/__tests__/cjk-text-input-flow.test.ts +0 -109
- package/src/tui/__tests__/cjk-text-input.test.ts +0 -191
- package/src/tui/__tests__/command-effect-handler.test.ts +0 -127
- package/src/tui/__tests__/command-output-summary.test.ts +0 -95
- package/src/tui/__tests__/compact-event-bridge.test.ts +0 -20
- package/src/tui/__tests__/confirm-permission-flow.test.ts +0 -130
- package/src/tui/__tests__/confirm-prompt.test.tsx +0 -87
- package/src/tui/__tests__/execution-workspace-switcher.test.tsx +0 -110
- package/src/tui/__tests__/execution-workspace-view-model.test.ts +0 -93
- package/src/tui/__tests__/fixtures/provider-setup-prompt-driver.tsx +0 -125
- package/src/tui/__tests__/input-area-flow.test.ts +0 -164
- package/src/tui/__tests__/message-list-rendering.test.tsx +0 -353
- package/src/tui/__tests__/prompt-queue.test.tsx +0 -255
- package/src/tui/__tests__/provider-setup-pty-e2e.test.ts +0 -233
- package/src/tui/__tests__/pty/pty-driver.ts +0 -135
- package/src/tui/__tests__/pty/tui-pty.ptytest.ts +0 -61
- package/src/tui/__tests__/render-channel-options.test.ts +0 -32
- package/src/tui/__tests__/render-markdown.test.ts +0 -72
- package/src/tui/__tests__/selection-flow.test.ts +0 -61
- package/src/tui/__tests__/session-init-poller.test.ts +0 -102
- package/src/tui/__tests__/session-naming.test.ts +0 -64
- package/src/tui/__tests__/session-switch-channel.test.tsx +0 -307
- package/src/tui/__tests__/slash-routing-effects.test.ts +0 -228
- package/src/tui/__tests__/status-activity.test.ts +0 -71
- package/src/tui/__tests__/status-bar.test.tsx +0 -177
- package/src/tui/__tests__/streaming-indicator.test.tsx +0 -137
- package/src/tui/__tests__/text-prompt-flow.test.ts +0 -77
- package/src/tui/__tests__/tui-channel-init-failure.test.ts +0 -57
- package/src/tui/__tests__/tui-state-manager.test.ts +0 -401
- package/src/tui/background-task-row-format.ts +0 -53
- package/src/tui/command-interaction.ts +0 -9
- package/src/tui/command-output-summary.ts +0 -122
- package/src/tui/create-default-tui-cli-adapter.ts +0 -41
- package/src/tui/execution-workspace-view-model.ts +0 -123
- package/src/tui/flows/cjk-text-input-flow.ts +0 -285
- package/src/tui/flows/confirm-prompt-flow.ts +0 -45
- package/src/tui/flows/input-area-flow.ts +0 -189
- package/src/tui/flows/permission-prompt-flow.ts +0 -85
- package/src/tui/flows/selection-flow.ts +0 -126
- package/src/tui/flows/session-init-poller.ts +0 -77
- package/src/tui/flows/text-prompt-flow.ts +0 -98
- package/src/tui/hooks/command-effect-handler.ts +0 -97
- package/src/tui/hooks/command-effect-queue.ts +0 -39
- package/src/tui/hooks/side-effects-types.ts +0 -35
- package/src/tui/hooks/useAutocomplete.ts +0 -87
- package/src/tui/hooks/usePluginCallbacks.ts +0 -31
- package/src/tui/hooks/usePluginScreenData.ts +0 -85
- package/src/tui/hooks/useSideEffects.ts +0 -175
- package/src/tui/hooks/useSlashRouting.ts +0 -118
- package/src/tui/hooks/useStatusLineSettings.ts +0 -37
- package/src/tui/hooks/useTuiChannel.ts +0 -95
- package/src/tui/index.ts +0 -14
- package/src/tui/interactions/CommandConfirm.tsx +0 -36
- package/src/tui/interactions/CommandPicker.tsx +0 -77
- package/src/tui/interactions/__tests__/CommandConfirm.test.tsx +0 -124
- package/src/tui/interactions/__tests__/CommandPicker.test.tsx +0 -138
- package/src/tui/plugin-tui-handlers.ts +0 -163
- package/src/tui/render-markdown.ts +0 -130
- package/src/tui/render.tsx +0 -129
- package/src/tui/session-naming.ts +0 -33
- package/src/tui/status-activity.ts +0 -63
- package/src/tui/tui-cli-adapter-context.tsx +0 -13
- package/src/tui/tui-cli-adapter.ts +0 -25
- package/src/tui/tui-state-manager.ts +0 -226
- package/src/tui/tui-transport.ts +0 -35
- package/src/tui/types.ts +0 -15
- package/src/tui/utils/__tests__/edit-diff.test.ts +0 -426
- package/src/tui/utils/__tests__/paste-detection.test.ts +0 -116
- package/src/tui/utils/__tests__/paste-labels.test.ts +0 -46
- package/src/tui/utils/__tests__/tool-call-extractor.test.ts +0 -227
- package/src/tui/utils/__tests__/tool-diff-summary.test.ts +0 -104
- package/src/tui/utils/edit-diff.ts +0 -153
- package/src/tui/utils/paste-labels.ts +0 -9
- package/src/tui/utils/tool-call-extractor.ts +0 -92
- package/src/tui/utils/tool-diff-summary.ts +0 -75
- package/src/ws/__tests__/ws-handler.test.ts +0 -409
- package/src/ws/__tests__/ws-transport.test.ts +0 -53
- package/src/ws/index.ts +0 -13
- package/src/ws/ws-background-messages.ts +0 -170
- package/src/ws/ws-handler.ts +0 -280
- package/src/ws/ws-protocol.ts +0 -78
- package/src/ws/ws-transport-configurable.ts +0 -128
- package/src/ws/ws-transport.ts +0 -42
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import { homedir } from 'node:os';
|
|
2
|
-
import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import { describe, expect, it } from 'vitest';
|
|
6
|
-
import { afterEach, vi } from 'vitest';
|
|
7
|
-
import {
|
|
8
|
-
CommandRegistry,
|
|
9
|
-
BundlePluginLoader,
|
|
10
|
-
PluginCommandSource,
|
|
11
|
-
} from '@robota-sdk/agent-framework';
|
|
12
|
-
import type {
|
|
13
|
-
ICommandInteraction,
|
|
14
|
-
IInteractiveSession,
|
|
15
|
-
} from '@robota-sdk/agent-interface-transport';
|
|
16
|
-
import { TuiStateManager } from '../tui-state-manager.js';
|
|
17
|
-
import { applySystemCommandResult } from '../hooks/useSlashRouting.js';
|
|
18
|
-
import { CommandEffectQueue } from '../hooks/command-effect-queue.js';
|
|
19
|
-
|
|
20
|
-
const PLUGIN_SOURCE_NAME = 'plugin';
|
|
21
|
-
|
|
22
|
-
function reloadPluginCommandSource(registry: CommandRegistry): void {
|
|
23
|
-
const pluginsDir = join(process.env.HOME ?? homedir(), '.robota', 'plugins');
|
|
24
|
-
const loader = new BundlePluginLoader(pluginsDir);
|
|
25
|
-
try {
|
|
26
|
-
// allow-fallback: test helper — empty registry on load error is safe
|
|
27
|
-
const plugins = loader.loadPluginsSync();
|
|
28
|
-
if (plugins.length === 0) {
|
|
29
|
-
registry.replaceSource(PLUGIN_SOURCE_NAME);
|
|
30
|
-
} else {
|
|
31
|
-
registry.replaceSource(PLUGIN_SOURCE_NAME, new PluginCommandSource(plugins));
|
|
32
|
-
}
|
|
33
|
-
} catch {
|
|
34
|
-
// allow-fallback: test helper — empty registry on load error is safe
|
|
35
|
-
registry.replaceSource(PLUGIN_SOURCE_NAME);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
describe('applySystemCommandResult', () => {
|
|
40
|
-
afterEach(() => {
|
|
41
|
-
vi.unstubAllEnvs();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
function createRegistry(): CommandRegistry {
|
|
45
|
-
return new CommandRegistry();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function legacyCommandField(suffix: 'Interaction' | 'Effects'): string {
|
|
49
|
-
return `_pendingCommand${suffix}`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
it('stores statusline settings patch as a CLI side effect', () => {
|
|
53
|
-
const session = {
|
|
54
|
-
getContextState: () => ({ usedPercentage: 0, usedTokens: 0, maxTokens: 0 }),
|
|
55
|
-
} as unknown as IInteractiveSession;
|
|
56
|
-
const manager = new TuiStateManager();
|
|
57
|
-
const queue = new CommandEffectQueue();
|
|
58
|
-
|
|
59
|
-
applySystemCommandResult(
|
|
60
|
-
{
|
|
61
|
-
success: true,
|
|
62
|
-
message: 'Status line disabled.',
|
|
63
|
-
effects: [{ type: 'statusline-settings-patch', patch: { enabled: false } }],
|
|
64
|
-
},
|
|
65
|
-
session,
|
|
66
|
-
createRegistry(),
|
|
67
|
-
manager,
|
|
68
|
-
queue,
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
expect(queue.drain()).toEqual({
|
|
72
|
-
type: 'effects',
|
|
73
|
-
effects: [{ type: 'statusline-settings-patch', patch: { enabled: false } }],
|
|
74
|
-
});
|
|
75
|
-
expect(Object.hasOwn(session, legacyCommandField('Effects'))).toBe(false);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('stores generic command interactions without interpreting command-specific data', () => {
|
|
79
|
-
const session = {
|
|
80
|
-
getContextState: () => ({ usedPercentage: 0, usedTokens: 0, maxTokens: 0 }),
|
|
81
|
-
} as unknown as IInteractiveSession;
|
|
82
|
-
const manager = new TuiStateManager();
|
|
83
|
-
const queue = new CommandEffectQueue();
|
|
84
|
-
const interaction: ICommandInteraction = {
|
|
85
|
-
prompt: {
|
|
86
|
-
kind: 'choice',
|
|
87
|
-
title: 'Change provider?',
|
|
88
|
-
options: [
|
|
89
|
-
{ value: 'yes', label: 'Yes' },
|
|
90
|
-
{ value: 'no', label: 'No' },
|
|
91
|
-
],
|
|
92
|
-
},
|
|
93
|
-
submit: () => ({ success: true, message: 'done' }),
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
applySystemCommandResult(
|
|
97
|
-
{
|
|
98
|
-
success: true,
|
|
99
|
-
message: 'Switch provider?',
|
|
100
|
-
interaction,
|
|
101
|
-
},
|
|
102
|
-
session,
|
|
103
|
-
createRegistry(),
|
|
104
|
-
manager,
|
|
105
|
-
queue,
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
expect(queue.drain()).toEqual({ type: 'interaction', interaction });
|
|
109
|
-
expect(Object.hasOwn(session, legacyCommandField('Interaction'))).toBe(false);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('stores host command side effects', () => {
|
|
113
|
-
const session = {
|
|
114
|
-
getContextState: () => ({ usedPercentage: 0, usedTokens: 0, maxTokens: 0 }),
|
|
115
|
-
} as unknown as IInteractiveSession;
|
|
116
|
-
const manager = new TuiStateManager();
|
|
117
|
-
const queue = new CommandEffectQueue();
|
|
118
|
-
|
|
119
|
-
applySystemCommandResult(
|
|
120
|
-
{
|
|
121
|
-
success: true,
|
|
122
|
-
message: 'Opening plugin manager...',
|
|
123
|
-
effects: [{ type: 'plugin-tui-requested' }],
|
|
124
|
-
},
|
|
125
|
-
session,
|
|
126
|
-
createRegistry(),
|
|
127
|
-
manager,
|
|
128
|
-
queue,
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
expect(queue.drain()).toEqual({
|
|
132
|
-
type: 'effects',
|
|
133
|
-
effects: [{ type: 'plugin-tui-requested' }],
|
|
134
|
-
});
|
|
135
|
-
expect(Object.hasOwn(session, legacyCommandField('Effects'))).toBe(false);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('applies conversation history clearing immediately before adding the command result', () => {
|
|
139
|
-
const session = {
|
|
140
|
-
getContextState: () => ({ usedPercentage: 0, usedTokens: 0, maxTokens: 0 }),
|
|
141
|
-
} as unknown as IInteractiveSession;
|
|
142
|
-
const manager = new TuiStateManager();
|
|
143
|
-
const queue = new CommandEffectQueue();
|
|
144
|
-
manager.addEntry({
|
|
145
|
-
id: 'old',
|
|
146
|
-
timestamp: new Date('2026-05-03T00:00:00.000Z'),
|
|
147
|
-
category: 'chat',
|
|
148
|
-
type: 'user',
|
|
149
|
-
data: { role: 'user', content: 'old message' },
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
applySystemCommandResult(
|
|
153
|
-
{
|
|
154
|
-
success: true,
|
|
155
|
-
message: 'Conversation cleared.',
|
|
156
|
-
effects: [{ type: 'conversation-history-cleared' }],
|
|
157
|
-
},
|
|
158
|
-
session,
|
|
159
|
-
createRegistry(),
|
|
160
|
-
manager,
|
|
161
|
-
queue,
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
expect(manager.history).toHaveLength(1);
|
|
165
|
-
expect(manager.history[0]?.type).toBe('system');
|
|
166
|
-
expect(manager.history[0]?.data).toMatchObject({ content: 'Conversation cleared.' });
|
|
167
|
-
expect(queue.drain()).toBeUndefined();
|
|
168
|
-
expect(Object.hasOwn(session, legacyCommandField('Effects'))).toBe(false);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('reloads plugin command source immediately when requested', () => {
|
|
172
|
-
const home = mkdtempSync(join(tmpdir(), 'robota-plugin-reload-'));
|
|
173
|
-
const pluginDir = join(
|
|
174
|
-
home,
|
|
175
|
-
'.robota',
|
|
176
|
-
'plugins',
|
|
177
|
-
'cache',
|
|
178
|
-
'community',
|
|
179
|
-
'fresh-plugin',
|
|
180
|
-
'1.0.0',
|
|
181
|
-
);
|
|
182
|
-
mkdirSync(join(pluginDir, '.claude-plugin'), { recursive: true });
|
|
183
|
-
mkdirSync(join(pluginDir, 'skills', 'fresh-skill'), { recursive: true });
|
|
184
|
-
writeFileSync(
|
|
185
|
-
join(pluginDir, '.claude-plugin', 'plugin.json'),
|
|
186
|
-
JSON.stringify({
|
|
187
|
-
name: 'fresh-plugin',
|
|
188
|
-
version: '1.0.0',
|
|
189
|
-
description: 'Fresh plugin',
|
|
190
|
-
features: { skills: true },
|
|
191
|
-
}),
|
|
192
|
-
'utf8',
|
|
193
|
-
);
|
|
194
|
-
writeFileSync(
|
|
195
|
-
join(pluginDir, 'skills', 'fresh-skill', 'SKILL.md'),
|
|
196
|
-
'---\ndescription: Fresh skill\n---\n# Fresh Skill\n',
|
|
197
|
-
'utf8',
|
|
198
|
-
);
|
|
199
|
-
vi.stubEnv('HOME', home);
|
|
200
|
-
const session = {
|
|
201
|
-
getContextState: () => ({ usedPercentage: 0, usedTokens: 0, maxTokens: 0 }),
|
|
202
|
-
} as unknown as IInteractiveSession;
|
|
203
|
-
const registry = createRegistry();
|
|
204
|
-
const queue = new CommandEffectQueue();
|
|
205
|
-
registry.addSource({
|
|
206
|
-
name: 'plugin',
|
|
207
|
-
getCommands: () => [{ name: 'stale-skill', description: 'Stale', source: 'plugin' }],
|
|
208
|
-
});
|
|
209
|
-
const manager = new TuiStateManager();
|
|
210
|
-
|
|
211
|
-
applySystemCommandResult(
|
|
212
|
-
{
|
|
213
|
-
success: true,
|
|
214
|
-
message: 'Reloaded 1 plugin resource.',
|
|
215
|
-
effects: [{ type: 'plugin-registry-reload-requested' }],
|
|
216
|
-
},
|
|
217
|
-
session,
|
|
218
|
-
registry,
|
|
219
|
-
manager,
|
|
220
|
-
queue,
|
|
221
|
-
reloadPluginCommandSource,
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
expect(registry.getCommands().map((command) => command.name)).toEqual(['fresh-skill']);
|
|
225
|
-
expect(queue.drain()).toBeUndefined();
|
|
226
|
-
expect(Object.hasOwn(session, legacyCommandField('Effects'))).toBe(false);
|
|
227
|
-
});
|
|
228
|
-
});
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { formatStatusActivity } from '../status-activity.js';
|
|
3
|
-
|
|
4
|
-
describe('formatStatusActivity', () => {
|
|
5
|
-
it('prioritizes running tools over thinking, background work, and queued prompts', () => {
|
|
6
|
-
const activity = formatStatusActivity({
|
|
7
|
-
isThinking: true,
|
|
8
|
-
activeToolCount: 2,
|
|
9
|
-
activeBackgroundTaskCount: 3,
|
|
10
|
-
hasPendingPrompt: true,
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
expect(activity.kind).toBe('tools');
|
|
14
|
-
expect(activity.label).toBe('Tools (2)');
|
|
15
|
-
expect(activity.color).toBe('cyan');
|
|
16
|
-
expect(activity.segments).toEqual(['queued']);
|
|
17
|
-
expect(activity.text).toBe('Tools (2) · queued');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('shows thinking as the primary model waiting state', () => {
|
|
21
|
-
const activity = formatStatusActivity({
|
|
22
|
-
isThinking: true,
|
|
23
|
-
activeToolCount: 0,
|
|
24
|
-
activeBackgroundTaskCount: 0,
|
|
25
|
-
hasPendingPrompt: false,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
expect(activity.kind).toBe('thinking');
|
|
29
|
-
expect(activity.label).toBe('Thinking');
|
|
30
|
-
expect(activity.segments).toEqual([]);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('shows background activity when foreground work is idle', () => {
|
|
34
|
-
const activity = formatStatusActivity({
|
|
35
|
-
isThinking: false,
|
|
36
|
-
activeToolCount: 0,
|
|
37
|
-
activeBackgroundTaskCount: 1,
|
|
38
|
-
hasPendingPrompt: false,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
expect(activity.kind).toBe('background');
|
|
42
|
-
expect(activity.label).toBe('Background (1)');
|
|
43
|
-
expect(activity.color).toBe('cyan');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('shows queued prompt before idle when no work is active', () => {
|
|
47
|
-
const activity = formatStatusActivity({
|
|
48
|
-
isThinking: false,
|
|
49
|
-
activeToolCount: 0,
|
|
50
|
-
activeBackgroundTaskCount: 0,
|
|
51
|
-
hasPendingPrompt: true,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
expect(activity.kind).toBe('queued');
|
|
55
|
-
expect(activity.label).toBe('Queued');
|
|
56
|
-
expect(activity.color).toBe('yellow');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('keeps idle compact and dim', () => {
|
|
60
|
-
const activity = formatStatusActivity({
|
|
61
|
-
isThinking: false,
|
|
62
|
-
activeToolCount: 0,
|
|
63
|
-
activeBackgroundTaskCount: 0,
|
|
64
|
-
hasPendingPrompt: false,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
expect(activity.kind).toBe('idle');
|
|
68
|
-
expect(activity.text).toBe('Idle');
|
|
69
|
-
expect(activity.color).toBe('gray');
|
|
70
|
-
});
|
|
71
|
-
});
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render } from 'ink-testing-library';
|
|
3
|
-
import { describe, it, expect } from 'vitest';
|
|
4
|
-
import StatusBar from '../StatusBar.js';
|
|
5
|
-
|
|
6
|
-
describe('StatusBar', () => {
|
|
7
|
-
const baseProps = {
|
|
8
|
-
permissionMode: 'default' as const,
|
|
9
|
-
modelName: 'test-model',
|
|
10
|
-
sessionId: 'sess-1',
|
|
11
|
-
isThinking: false,
|
|
12
|
-
activeToolCount: 0,
|
|
13
|
-
activeBackgroundTaskCount: 0,
|
|
14
|
-
hasPendingPrompt: false,
|
|
15
|
-
contextPercentage: 10,
|
|
16
|
-
contextUsedTokens: 1000,
|
|
17
|
-
contextMaxTokens: 200000,
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
it('renders without session name', () => {
|
|
21
|
-
const { lastFrame } = render(<StatusBar {...baseProps} />);
|
|
22
|
-
const frame = lastFrame()!;
|
|
23
|
-
expect(frame).toContain('test-model');
|
|
24
|
-
expect(frame).not.toContain('Mode: default');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('hides default permission mode', () => {
|
|
28
|
-
const { lastFrame } = render(<StatusBar {...baseProps} permissionMode="default" />);
|
|
29
|
-
const frame = lastFrame()!;
|
|
30
|
-
expect(frame).not.toContain('Mode:');
|
|
31
|
-
expect(frame).not.toContain('default');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('shows non-default permission modes', () => {
|
|
35
|
-
for (const permissionMode of ['plan', 'acceptEdits', 'bypassPermissions'] as const) {
|
|
36
|
-
const { lastFrame, unmount } = render(
|
|
37
|
-
<StatusBar {...baseProps} permissionMode={permissionMode} />,
|
|
38
|
-
);
|
|
39
|
-
const frame = lastFrame()!;
|
|
40
|
-
expect(frame).toContain('Mode:');
|
|
41
|
-
expect(frame).toContain(permissionMode);
|
|
42
|
-
unmount();
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('renders session name when provided', () => {
|
|
47
|
-
const { lastFrame } = render(<StatusBar {...baseProps} sessionName="my-feature" />);
|
|
48
|
-
const frame = lastFrame()!;
|
|
49
|
-
expect(frame).toContain('my-feature');
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('does not show session name when undefined', () => {
|
|
53
|
-
const { lastFrame } = render(<StatusBar {...baseProps} sessionName={undefined} />);
|
|
54
|
-
const frame = lastFrame()!;
|
|
55
|
-
// Should not have extra separator for missing name
|
|
56
|
-
expect(frame).not.toContain('my-feature');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('renders model name', () => {
|
|
60
|
-
const { lastFrame } = render(<StatusBar {...baseProps} />);
|
|
61
|
-
const frame = lastFrame()!;
|
|
62
|
-
expect(frame).toContain('test-model');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('renders provider display name and model when provided', () => {
|
|
66
|
-
const { lastFrame } = render(<StatusBar {...baseProps} providerDisplayName="Anthropic" />);
|
|
67
|
-
const frame = lastFrame()!;
|
|
68
|
-
expect(frame).toContain('Anthropic');
|
|
69
|
-
expect(frame).toContain('test-model');
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('does not render message count in the status bar', () => {
|
|
73
|
-
const { lastFrame } = render(<StatusBar {...baseProps} />);
|
|
74
|
-
const frame = lastFrame()!;
|
|
75
|
-
expect(frame).not.toContain('msgs:');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('shows thinking indicator when isThinking is true', () => {
|
|
79
|
-
const { lastFrame } = render(<StatusBar {...baseProps} isThinking={true} />);
|
|
80
|
-
const frame = lastFrame()!;
|
|
81
|
-
expect(frame).not.toContain('Activity:');
|
|
82
|
-
expect(frame).toContain('Thinking');
|
|
83
|
-
expect(frame.indexOf('Thinking')).toBeLessThan(frame.indexOf('test-model'));
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('does not duplicate thinking state in secondary status text', () => {
|
|
87
|
-
const { lastFrame } = render(<StatusBar {...baseProps} isThinking={true} />);
|
|
88
|
-
const frame = lastFrame()!;
|
|
89
|
-
expect(frame).toContain('Thinking');
|
|
90
|
-
expect(frame).not.toContain('thinking...');
|
|
91
|
-
expect(frame).not.toContain('msgs:');
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('hides the lower-right prompt-processing indicator while idle', () => {
|
|
95
|
-
const { lastFrame } = render(<StatusBar {...baseProps} isThinking={false} />);
|
|
96
|
-
const frame = lastFrame()!;
|
|
97
|
-
expect(frame).not.toContain('thinking...');
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('prioritizes tool activity in the primary scan path', () => {
|
|
101
|
-
const { lastFrame } = render(
|
|
102
|
-
<StatusBar
|
|
103
|
-
{...baseProps}
|
|
104
|
-
isThinking={true}
|
|
105
|
-
activeToolCount={2}
|
|
106
|
-
activeBackgroundTaskCount={1}
|
|
107
|
-
hasPendingPrompt={true}
|
|
108
|
-
/>,
|
|
109
|
-
);
|
|
110
|
-
const frame = lastFrame()!;
|
|
111
|
-
expect(frame).not.toContain('Activity:');
|
|
112
|
-
expect(frame).toContain('Tools (2)');
|
|
113
|
-
expect(frame).not.toContain('Tools x2');
|
|
114
|
-
expect(frame).toContain('queued');
|
|
115
|
-
expect(frame).not.toContain('thinking...');
|
|
116
|
-
expect(frame.indexOf('Tools (2)')).toBeLessThan(frame.indexOf('test-model'));
|
|
117
|
-
expect(frame).not.toContain('Thinking...');
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('shows background activity when no foreground execution is active', () => {
|
|
121
|
-
const { lastFrame } = render(<StatusBar {...baseProps} activeBackgroundTaskCount={3} />);
|
|
122
|
-
const frame = lastFrame()!;
|
|
123
|
-
expect(frame).toContain('Background (3)');
|
|
124
|
-
expect(frame.indexOf('Background (3)')).toBeLessThan(frame.indexOf('test-model'));
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('keeps the activity segment compact for narrow terminals', () => {
|
|
128
|
-
const { lastFrame } = render(
|
|
129
|
-
<StatusBar
|
|
130
|
-
{...baseProps}
|
|
131
|
-
isThinking={true}
|
|
132
|
-
activeToolCount={12}
|
|
133
|
-
activeBackgroundTaskCount={9}
|
|
134
|
-
hasPendingPrompt={true}
|
|
135
|
-
/>,
|
|
136
|
-
);
|
|
137
|
-
const frame = lastFrame()!;
|
|
138
|
-
const firstLine = frame.split('\n')[0] ?? '';
|
|
139
|
-
const activityEnd = firstLine.indexOf('test-model');
|
|
140
|
-
const activitySegment = firstLine.slice(0, activityEnd);
|
|
141
|
-
expect(activitySegment).toContain('Tools (12)');
|
|
142
|
-
expect(activitySegment.length).toBeLessThanOrEqual(40);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it('renders git branch when provided', () => {
|
|
146
|
-
const { lastFrame } = render(<StatusBar {...baseProps} gitBranch="feat/status-line" />);
|
|
147
|
-
const frame = lastFrame()!;
|
|
148
|
-
expect(frame).toContain('feat/status-line');
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('does not render git branch when visibility is disabled', () => {
|
|
152
|
-
const { lastFrame } = render(
|
|
153
|
-
<StatusBar {...baseProps} gitBranch="feat/status-line" showGitBranch={false} />,
|
|
154
|
-
);
|
|
155
|
-
const frame = lastFrame()!;
|
|
156
|
-
expect(frame).not.toContain('feat/status-line');
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('TC-03: shows the active preset id when set and non-default', () => {
|
|
160
|
-
const { lastFrame } = render(<StatusBar {...baseProps} activePresetId="autonomous-builder" />);
|
|
161
|
-
const frame = lastFrame()!;
|
|
162
|
-
expect(frame).toContain('Preset:');
|
|
163
|
-
expect(frame).toContain('autonomous-builder');
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('TC-03: hides the default active preset', () => {
|
|
167
|
-
const { lastFrame } = render(<StatusBar {...baseProps} activePresetId="default" />);
|
|
168
|
-
const frame = lastFrame()!;
|
|
169
|
-
expect(frame).not.toContain('Preset:');
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('TC-03: hides the preset label when no active preset is provided', () => {
|
|
173
|
-
const { lastFrame } = render(<StatusBar {...baseProps} activePresetId={undefined} />);
|
|
174
|
-
const frame = lastFrame()!;
|
|
175
|
-
expect(frame).not.toContain('Preset:');
|
|
176
|
-
});
|
|
177
|
-
});
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { describe, it, expect } from 'vitest';
|
|
3
|
-
import { render } from 'ink-testing-library';
|
|
4
|
-
import StreamingIndicator from '../StreamingIndicator.js';
|
|
5
|
-
|
|
6
|
-
describe('StreamingIndicator', () => {
|
|
7
|
-
it('renders empty when no tools and no text', () => {
|
|
8
|
-
const { lastFrame } = render(<StreamingIndicator text="" activeTools={[]} />);
|
|
9
|
-
expect(lastFrame()).not.toContain('Thinking');
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('shows a generic thinking state when foreground work has no text or tools yet', () => {
|
|
13
|
-
const { lastFrame } = render(<StreamingIndicator text="" activeTools={[]} isThinking={true} />);
|
|
14
|
-
|
|
15
|
-
expect(lastFrame()).toContain('Thinking...');
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('shows Tools: section with running tool', () => {
|
|
19
|
-
const { lastFrame } = render(
|
|
20
|
-
<StreamingIndicator
|
|
21
|
-
text=""
|
|
22
|
-
activeTools={[{ toolName: 'Bash', firstArg: 'ls -la', isRunning: true }]}
|
|
23
|
-
/>,
|
|
24
|
-
);
|
|
25
|
-
const frame = lastFrame()!;
|
|
26
|
-
expect(frame).toContain('Tools:');
|
|
27
|
-
expect(frame).toContain('Bash(ls -la)');
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('shows ⟳ for running and ✓ for completed tools', () => {
|
|
31
|
-
const { lastFrame } = render(
|
|
32
|
-
<StreamingIndicator
|
|
33
|
-
text=""
|
|
34
|
-
activeTools={[
|
|
35
|
-
{ toolName: 'Read', firstArg: '/src/index.ts', isRunning: false },
|
|
36
|
-
{ toolName: 'Bash', firstArg: 'ls', isRunning: true },
|
|
37
|
-
]}
|
|
38
|
-
/>,
|
|
39
|
-
);
|
|
40
|
-
const frame = lastFrame()!;
|
|
41
|
-
expect(frame).toContain('✓ Read(/src/index.ts)');
|
|
42
|
-
expect(frame).toContain('⟳ Bash(ls)');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('shows Robota: section with streaming text', () => {
|
|
46
|
-
const { lastFrame } = render(<StreamingIndicator text="Hello world" activeTools={[]} />);
|
|
47
|
-
const frame = lastFrame()!;
|
|
48
|
-
expect(frame).toContain('Robota:');
|
|
49
|
-
expect(frame).toContain('Hello world');
|
|
50
|
-
expect(frame).not.toContain('Tools:');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('preserves CJK and emoji text in streaming output', () => {
|
|
54
|
-
const text = '긴 한국어 응답과 emoji 🎉 를 스트리밍합니다';
|
|
55
|
-
const { lastFrame } = render(<StreamingIndicator text={text} activeTools={[]} />);
|
|
56
|
-
|
|
57
|
-
expect(lastFrame()).toContain(text);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('shows Tools: before Robota: when both present', () => {
|
|
61
|
-
const { lastFrame } = render(
|
|
62
|
-
<StreamingIndicator
|
|
63
|
-
text="Analyzing..."
|
|
64
|
-
activeTools={[{ toolName: 'Read', firstArg: 'file.ts', isRunning: false }]}
|
|
65
|
-
/>,
|
|
66
|
-
);
|
|
67
|
-
const frame = lastFrame()!;
|
|
68
|
-
const toolsIndex = frame.indexOf('Tools:');
|
|
69
|
-
const robotaIndex = frame.indexOf('Robota:');
|
|
70
|
-
expect(toolsIndex).toBeGreaterThanOrEqual(0);
|
|
71
|
-
expect(robotaIndex).toBeGreaterThanOrEqual(0);
|
|
72
|
-
expect(toolsIndex).toBeLessThan(robotaIndex);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('does not show Thinking... when tools are active', () => {
|
|
76
|
-
const { lastFrame } = render(
|
|
77
|
-
<StreamingIndicator
|
|
78
|
-
text=""
|
|
79
|
-
activeTools={[{ toolName: 'Glob', firstArg: '**/*.md', isRunning: true }]}
|
|
80
|
-
/>,
|
|
81
|
-
);
|
|
82
|
-
expect(lastFrame()).not.toContain('Thinking...');
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('does not show Thinking... when text is present', () => {
|
|
86
|
-
const { lastFrame } = render(
|
|
87
|
-
<StreamingIndicator text="Some response" activeTools={[]} isThinking={true} />,
|
|
88
|
-
);
|
|
89
|
-
expect(lastFrame()).not.toContain('Thinking...');
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('shows multiple tools in order', () => {
|
|
93
|
-
const { lastFrame } = render(
|
|
94
|
-
<StreamingIndicator
|
|
95
|
-
text=""
|
|
96
|
-
activeTools={[
|
|
97
|
-
{ toolName: 'Read', firstArg: 'a.ts', isRunning: false },
|
|
98
|
-
{ toolName: 'Bash', firstArg: 'echo hi', isRunning: false },
|
|
99
|
-
{ toolName: 'Glob', firstArg: '**/*.md', isRunning: true },
|
|
100
|
-
]}
|
|
101
|
-
/>,
|
|
102
|
-
);
|
|
103
|
-
const frame = lastFrame()!;
|
|
104
|
-
const readIdx = frame.indexOf('Read(a.ts)');
|
|
105
|
-
const bashIdx = frame.indexOf('Bash(echo hi)');
|
|
106
|
-
const globIdx = frame.indexOf('Glob(**/*.md)');
|
|
107
|
-
expect(readIdx).toBeLessThan(bashIdx);
|
|
108
|
-
expect(bashIdx).toBeLessThan(globIdx);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('renders tool diffs through markdown diff body format', () => {
|
|
112
|
-
const { lastFrame } = render(
|
|
113
|
-
<StreamingIndicator
|
|
114
|
-
text=""
|
|
115
|
-
activeTools={[
|
|
116
|
-
{
|
|
117
|
-
toolName: 'Edit',
|
|
118
|
-
firstArg: '/src/index.ts',
|
|
119
|
-
isRunning: false,
|
|
120
|
-
result: 'success',
|
|
121
|
-
diffFile: '/src/index.ts',
|
|
122
|
-
diffLines: [
|
|
123
|
-
{ type: 'remove', lineNumber: 1, text: 'const oldValue = true;' },
|
|
124
|
-
{ type: 'add', lineNumber: 1, text: 'const newValue = true;' },
|
|
125
|
-
],
|
|
126
|
-
},
|
|
127
|
-
]}
|
|
128
|
-
/>,
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
const frame = lastFrame() ?? '';
|
|
132
|
-
expect(frame).toContain('/src/index.ts');
|
|
133
|
-
expect(frame).toContain('- 1 | const oldValue = true;');
|
|
134
|
-
expect(frame).toContain('+ 1 | const newValue = true;');
|
|
135
|
-
expect(frame).not.toContain('│ 1 - const oldValue = true;');
|
|
136
|
-
});
|
|
137
|
-
});
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
applyTextPromptInput,
|
|
4
|
-
createTextPromptFlowState,
|
|
5
|
-
getTextPromptInputAction,
|
|
6
|
-
type ITextPromptFlowState,
|
|
7
|
-
} from '../flows/text-prompt-flow.js';
|
|
8
|
-
|
|
9
|
-
describe('text prompt flow', () => {
|
|
10
|
-
it('Given typed text When submit is applied Then it emits trimmed submit value', () => {
|
|
11
|
-
const typed = applyTextPromptInput(
|
|
12
|
-
createTextPromptFlowState(),
|
|
13
|
-
{ type: 'insert', value: ' hello ' },
|
|
14
|
-
{ allowEmpty: false },
|
|
15
|
-
).state;
|
|
16
|
-
|
|
17
|
-
const result = applyTextPromptInput(typed, { type: 'submit' }, { allowEmpty: false });
|
|
18
|
-
|
|
19
|
-
expect(result.effect).toEqual({ type: 'submit', value: 'hello' });
|
|
20
|
-
expect(result.state.resolved).toBe(true);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('Given empty required text When submit is applied Then validation error stays in state', () => {
|
|
24
|
-
const result = applyTextPromptInput(
|
|
25
|
-
createTextPromptFlowState(),
|
|
26
|
-
{ type: 'submit' },
|
|
27
|
-
{ allowEmpty: false, validate: (value) => (value.length === 0 ? 'Required' : undefined) },
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
expect(result.effect).toEqual({ type: 'none' });
|
|
31
|
-
expect(result.state.error).toBe('Required');
|
|
32
|
-
expect(result.state.resolved).toBe(false);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('Given defaultable empty text When submit is applied Then empty submit is allowed', () => {
|
|
36
|
-
const result = applyTextPromptInput(
|
|
37
|
-
createTextPromptFlowState(),
|
|
38
|
-
{ type: 'submit' },
|
|
39
|
-
{ allowEmpty: true },
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
expect(result.effect).toEqual({ type: 'submit', value: '' });
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('Given text with an error When delete or insert is applied Then the error is cleared', () => {
|
|
46
|
-
const state: ITextPromptFlowState = { value: 'ab', error: 'Invalid', resolved: false };
|
|
47
|
-
|
|
48
|
-
const deleted = applyTextPromptInput(state, { type: 'delete' }, { allowEmpty: false });
|
|
49
|
-
const inserted = applyTextPromptInput(
|
|
50
|
-
state,
|
|
51
|
-
{ type: 'insert', value: 'c' },
|
|
52
|
-
{ allowEmpty: false },
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
expect(deleted.state).toMatchObject({ value: 'a', error: undefined });
|
|
56
|
-
expect(inserted.state).toMatchObject({ value: 'abc', error: undefined });
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('Given prompt is already resolved When input is applied Then no second effect is emitted', () => {
|
|
60
|
-
const state: ITextPromptFlowState = { value: 'done', resolved: true };
|
|
61
|
-
|
|
62
|
-
const result = applyTextPromptInput(state, { type: 'submit' }, { allowEmpty: false });
|
|
63
|
-
|
|
64
|
-
expect(result.effect).toEqual({ type: 'none' });
|
|
65
|
-
expect(result.state).toBe(state);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('Given raw Ink key info When mapped Then terminal details become prompt actions', () => {
|
|
69
|
-
expect(getTextPromptInputAction('', { escape: true })).toEqual({ type: 'cancel' });
|
|
70
|
-
expect(getTextPromptInputAction('', { return: true })).toEqual({ type: 'submit' });
|
|
71
|
-
expect(getTextPromptInputAction('', { backspace: true })).toEqual({ type: 'delete' });
|
|
72
|
-
expect(getTextPromptInputAction('x', { ctrl: false, meta: false })).toEqual({
|
|
73
|
-
type: 'insert',
|
|
74
|
-
value: 'x',
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
});
|