@theia/ai-ide 1.76.0-next.18 → 1.76.0-next.23
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/lib/browser/user-interaction-tool-renderer.d.ts.map +1 -1
- package/lib/browser/user-interaction-tool-renderer.js +19 -6
- package/lib/browser/user-interaction-tool-renderer.js.map +1 -1
- package/lib/browser/user-interaction-tool.d.ts +8 -1
- package/lib/browser/user-interaction-tool.d.ts.map +1 -1
- package/lib/browser/user-interaction-tool.js +23 -15
- package/lib/browser/user-interaction-tool.js.map +1 -1
- package/lib/browser/user-interaction-tool.spec.js +55 -2
- package/lib/browser/user-interaction-tool.spec.js.map +1 -1
- package/lib/common/user-interaction-tool.d.ts +19 -1
- package/lib/common/user-interaction-tool.d.ts.map +1 -1
- package/lib/common/user-interaction-tool.js +100 -29
- package/lib/common/user-interaction-tool.js.map +1 -1
- package/lib/common/user-interaction-tool.spec.js +215 -174
- package/lib/common/user-interaction-tool.spec.js.map +1 -1
- package/package.json +25 -25
- package/src/browser/user-interaction-tool-renderer.tsx +19 -7
- package/src/browser/user-interaction-tool.spec.ts +62 -2
- package/src/browser/user-interaction-tool.ts +25 -15
- package/src/common/user-interaction-tool.spec.ts +217 -161
- package/src/common/user-interaction-tool.ts +127 -32
|
@@ -97,49 +97,80 @@ function parseUserInteractionResult(raw) {
|
|
|
97
97
|
}
|
|
98
98
|
return obj;
|
|
99
99
|
}
|
|
100
|
+
const JSON_ENCODING_HINT = 'Do not JSON-encode nested values - pass real arrays and objects.';
|
|
101
|
+
function describeType(value) {
|
|
102
|
+
// eslint-disable-next-line no-null/no-null
|
|
103
|
+
if (value === null) {
|
|
104
|
+
return 'null';
|
|
105
|
+
}
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
return 'array';
|
|
108
|
+
}
|
|
109
|
+
return typeof value;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Describe the received type, adding the JSON-encoding hint when a string was received where
|
|
113
|
+
* a structured value was expected - by far the most common way agents get these arguments wrong.
|
|
114
|
+
*/
|
|
115
|
+
function describeReceived(value) {
|
|
116
|
+
const type = describeType(value);
|
|
117
|
+
return type === 'string' ? `${type}. ${JSON_ENCODING_HINT}` : `${type}.`;
|
|
118
|
+
}
|
|
100
119
|
function parseUserInteractionArgs(args) {
|
|
101
120
|
if (!args) {
|
|
102
|
-
return
|
|
121
|
+
return { ok: false, error: 'No arguments were provided.' };
|
|
103
122
|
}
|
|
123
|
+
let parsed;
|
|
104
124
|
try {
|
|
105
|
-
|
|
106
|
-
if (!Array.isArray(parsed.interactions)) {
|
|
107
|
-
return undefined;
|
|
108
|
-
}
|
|
109
|
-
const validSteps = parsed.interactions
|
|
110
|
-
.map(parseStep)
|
|
111
|
-
.filter((step) => step !== undefined);
|
|
112
|
-
if (validSteps.length === 0) {
|
|
113
|
-
return undefined;
|
|
114
|
-
}
|
|
115
|
-
return { interactions: validSteps };
|
|
125
|
+
parsed = JSON.parse(args);
|
|
116
126
|
}
|
|
117
127
|
catch {
|
|
118
|
-
return
|
|
128
|
+
return { ok: false, error: 'Arguments are not valid JSON.' };
|
|
129
|
+
}
|
|
130
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
131
|
+
return { ok: false, error: `Arguments must be an object with an "interactions" array, received ${describeReceived(parsed)}` };
|
|
132
|
+
}
|
|
133
|
+
const interactions = parsed.interactions;
|
|
134
|
+
if (!Array.isArray(interactions)) {
|
|
135
|
+
return { ok: false, error: `"interactions" must be an array of step objects, received ${describeReceived(interactions)}` };
|
|
136
|
+
}
|
|
137
|
+
if (interactions.length === 0) {
|
|
138
|
+
return { ok: false, error: '"interactions" must contain at least one step.' };
|
|
119
139
|
}
|
|
140
|
+
const steps = [];
|
|
141
|
+
for (let index = 0; index < interactions.length; index++) {
|
|
142
|
+
const step = parseStep(interactions[index], index + 1);
|
|
143
|
+
if (!step.ok) {
|
|
144
|
+
return step;
|
|
145
|
+
}
|
|
146
|
+
steps.push(step.step);
|
|
147
|
+
}
|
|
148
|
+
return { ok: true, args: { interactions: steps } };
|
|
120
149
|
}
|
|
121
|
-
function parseStep(raw) {
|
|
122
|
-
if (!raw || typeof raw !== 'object') {
|
|
123
|
-
return
|
|
150
|
+
function parseStep(raw, position) {
|
|
151
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
152
|
+
return { ok: false, error: `Step ${position}: expected a step object, received ${describeReceived(raw)}` };
|
|
124
153
|
}
|
|
125
154
|
const obj = raw;
|
|
126
155
|
if (typeof obj.title !== 'string' || typeof obj.message !== 'string') {
|
|
127
|
-
return
|
|
156
|
+
return { ok: false, error: `Step ${position}: "title" and "message" are required strings.` };
|
|
128
157
|
}
|
|
129
|
-
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
&& typeof opt.text === 'string'
|
|
133
|
-
&& typeof opt.value === 'string');
|
|
134
|
-
options = validOptions.length > 0 ? validOptions : undefined;
|
|
158
|
+
const options = parseOptions(obj.options, position);
|
|
159
|
+
if (!options.ok) {
|
|
160
|
+
return options;
|
|
135
161
|
}
|
|
136
162
|
let links;
|
|
137
|
-
|
|
138
|
-
|
|
163
|
+
// eslint-disable-next-line no-null/no-null
|
|
164
|
+
const rawLinks = obj.links === null ? undefined : obj.links;
|
|
165
|
+
if (Array.isArray(rawLinks)) {
|
|
166
|
+
const filtered = rawLinks
|
|
139
167
|
.map(normalizeUserInteractionLink)
|
|
140
168
|
.filter((link) => link !== undefined);
|
|
141
169
|
links = filtered.length > 0 ? filtered : undefined;
|
|
142
170
|
}
|
|
171
|
+
else if (rawLinks !== undefined) {
|
|
172
|
+
return { ok: false, error: `Step ${position}: "links" must be an array of link objects, received ${describeReceived(rawLinks)}` };
|
|
173
|
+
}
|
|
143
174
|
else {
|
|
144
175
|
const link = normalizeUserInteractionLink(obj.link);
|
|
145
176
|
if (link) {
|
|
@@ -147,12 +178,52 @@ function parseStep(raw) {
|
|
|
147
178
|
}
|
|
148
179
|
}
|
|
149
180
|
return {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
181
|
+
ok: true,
|
|
182
|
+
step: {
|
|
183
|
+
title: obj.title,
|
|
184
|
+
message: obj.message,
|
|
185
|
+
options: options.options,
|
|
186
|
+
links
|
|
187
|
+
}
|
|
154
188
|
};
|
|
155
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Validate a step's `options`. An absent or explicitly empty list means the step is
|
|
192
|
+
* informational; anything else must be a fully well-formed array, since a partially
|
|
193
|
+
* dropped option would silently offer the user a different choice than the agent intended.
|
|
194
|
+
*/
|
|
195
|
+
function parseOptions(raw, stepPosition) {
|
|
196
|
+
// eslint-disable-next-line no-null/no-null
|
|
197
|
+
if (raw === undefined || raw === null) {
|
|
198
|
+
return { ok: true, options: undefined };
|
|
199
|
+
}
|
|
200
|
+
if (!Array.isArray(raw)) {
|
|
201
|
+
return {
|
|
202
|
+
ok: false,
|
|
203
|
+
error: `Step ${stepPosition}: "options" must be an array of {text, value} objects, received ${describeReceived(raw)}`
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const options = [];
|
|
207
|
+
for (let index = 0; index < raw.length; index++) {
|
|
208
|
+
const candidate = raw[index];
|
|
209
|
+
const obj = candidate;
|
|
210
|
+
if (!obj || typeof obj !== 'object' || Array.isArray(obj) || typeof obj.text !== 'string' || typeof obj.value !== 'string') {
|
|
211
|
+
return {
|
|
212
|
+
ok: false,
|
|
213
|
+
error: `Step ${stepPosition}, option ${index + 1}: "text" and "value" are required strings, received ${describeReceived(candidate)}`
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const option = { text: obj.text, value: obj.value };
|
|
217
|
+
if (typeof obj.description === 'string') {
|
|
218
|
+
option.description = obj.description;
|
|
219
|
+
}
|
|
220
|
+
if (typeof obj.buttonLabel === 'string') {
|
|
221
|
+
option.buttonLabel = obj.buttonLabel;
|
|
222
|
+
}
|
|
223
|
+
options.push(option);
|
|
224
|
+
}
|
|
225
|
+
return { ok: true, options: options.length > 0 ? options : undefined };
|
|
226
|
+
}
|
|
156
227
|
function normalizeContentRef(ref) {
|
|
157
228
|
if (typeof ref === 'string') {
|
|
158
229
|
return ref.length > 0 ? ref : undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-interaction-tool.js","sourceRoot":"","sources":["../../src/common/user-interaction-tool.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;;AAmBhF,8CAEC;AASD,8CAKC;AAgBD,wCAqBC;AAqCD,8DAaC;AAED,gEAiBC;
|
|
1
|
+
{"version":3,"file":"user-interaction-tool.js","sourceRoot":"","sources":["../../src/common/user-interaction-tool.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;;AAmBhF,8CAEC;AASD,8CAKC;AAgBD,wCAqBC;AAqCD,8DAaC;AAED,gEAiBC;AA+CD,4DA6BC;AA0GD,oEAqBC;AAtVD,oDAAiD;AAEpC,QAAA,4BAA4B,GAAG,iBAAiB,CAAC;AAe9D,SAAgB,iBAAiB,CAAC,GAAe;IAC7C,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC;AAC3E,CAAC;AASD,SAAgB,iBAAiB,CAAC,GAAe;IAC7C,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IACzB,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B,SAAS,gBAAgB;IACrB,OAAO,SAAG,CAAC,QAAQ,CAAC,yCAAyC,EAAE,cAAc,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,UAAU;IACf,OAAO,SAAG,CAAC,QAAQ,CAAC,mCAAmC,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC;AAED,SAAS,SAAS,CAAC,GAAmB;IAClC,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC;AACxF,CAAC;AAED,SAAgB,cAAc,CAC1B,IAAsC,EACtC,KAAuC;IAEvC,MAAM,WAAW,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,YAAY,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC9C,IAAI,WAAW,IAAI,YAAY,EAAE,CAAC;QAC9B,OAAO,GAAG,IAAI,CAAC,KAAK,IAAI,UAAU,EAAE,MAAM,KAAK,CAAC,KAAK,IAAI,UAAU,EAAE,EAAE,CAAC;IAC5E,CAAC;IACD,IAAI,WAAW,EAAE,CAAC;QACd,OAAO,GAAI,KAAwB,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC,KAAuB,CAAC,GAAG,CAAC;IACvH,CAAC;IACD,IAAI,YAAY,EAAE,CAAC;QACf,OAAO,GAAI,IAAuB,CAAC,IAAI,KAAK,SAAS,CAAC,IAAsB,CAAC,MAAM,KAAK,CAAC,KAAK,IAAI,UAAU,EAAE,GAAG,CAAC;IACtH,CAAC;IACD,MAAM,QAAQ,GAAG,IAAsB,CAAC;IACxC,MAAM,SAAS,GAAG,KAAuB,CAAC;IAC1C,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;QACnC,OAAO,GAAG,QAAQ,CAAC,IAAI,KAAK,SAAS,CAAC,QAAQ,CAAC,MAAM,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC;IACjF,CAAC;IACD,OAAO,GAAG,QAAQ,CAAC,IAAI,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;AAClD,CAAC;AAqCD,SAAgB,yBAAyB,CAAC,IAAwB;IAC9D,IAAI,CAAC,IAAI,EAAE,CAAC;QACR,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACvC,CAAC;IACD,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QACnF,MAAM,UAAU,GAAG,OAAO,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3F,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACL,MAAM,KAAK,GAAG,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACrD,CAAC;AACL,CAAC;AAED,SAAgB,0BAA0B,CAAC,GAAY;IACnD,IAAI,SAAS,GAAY,GAAG,CAAC;IAC7B,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC1B,IAAI,CAAC;YACD,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,SAAS,CAAC;QACrB,CAAC;IACL,CAAC;IACD,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC9C,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,MAAM,GAAG,GAAG,SAAoC,CAAC;IACjD,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAClE,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,OAAO,GAAuC,CAAC;AACnD,CAAC;AAyBD,MAAM,kBAAkB,GAAG,kEAAkE,CAAC;AAE9F,SAAS,YAAY,CAAC,KAAc;IAChC,2CAA2C;IAC3C,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACjB,OAAO,MAAM,CAAC;IAClB,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,OAAO,CAAC;IACnB,CAAC;IACD,OAAO,OAAO,KAAK,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,KAAc;IACpC,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACjC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,kBAAkB,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC;AAC7E,CAAC;AAED,SAAgB,wBAAwB,CAAC,IAAwB;IAC7D,IAAI,CAAC,IAAI,EAAE,CAAC;QACR,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,6BAA6B,EAAE,CAAC;IAC/D,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC;IACjE,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACjE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,sEAAsE,gBAAgB,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;IAClI,CAAC;IACD,MAAM,YAAY,GAAI,MAAkC,CAAC,YAAY,CAAC;IACtE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,6DAA6D,gBAAgB,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC;IAC/H,CAAC;IACD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,gDAAgD,EAAE,CAAC;IAClF,CAAC;IACD,MAAM,KAAK,GAA0B,EAAE,CAAC;IACxC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACvD,MAAM,IAAI,GAAG,SAAS,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,CAAC;AACvD,CAAC;AAED,SAAS,SAAS,CAAC,GAAY,EAAE,QAAgB;IAC7C,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACxD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,QAAQ,sCAAsC,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;IAC/G,CAAC;IACD,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACnE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,QAAQ,+CAA+C,EAAE,CAAC;IACjG,CAAC;IACD,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACpD,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QACd,OAAO,OAAO,CAAC;IACnB,CAAC;IACD,IAAI,KAAwC,CAAC;IAC7C,2CAA2C;IAC3C,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,QAAQ;aACpB,GAAG,CAAC,4BAA4B,CAAC;aACjC,MAAM,CAAC,CAAC,IAAqC,EAA+B,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;QACxG,KAAK,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACvD,CAAC;SAAM,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,QAAQ,wDAAwD,gBAAgB,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;IACtI,CAAC;SAAM,CAAC;QACJ,MAAM,IAAI,GAAG,4BAA4B,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpD,IAAI,IAAI,EAAE,CAAC;YACP,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;IACL,CAAC;IACD,OAAO;QACH,EAAE,EAAE,IAAI;QACR,IAAI,EAAE;YACF,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,KAAK;SACR;KACJ,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,GAAY,EAAE,YAAoB;IACpD,2CAA2C;IAC3C,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACpC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IAC5C,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO;YACH,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,QAAQ,YAAY,mEAAmE,gBAAgB,CAAC,GAAG,CAAC,EAAE;SACxH,CAAC;IACN,CAAC;IACD,MAAM,OAAO,GAA4B,EAAE,CAAC;IAC5C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAC9C,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;QAC7B,MAAM,GAAG,GAAG,SAAgD,CAAC;QAC7D,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACzH,OAAO;gBACH,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,QAAQ,YAAY,YAAY,KAAK,GAAG,CAAC,uDAAuD,gBAAgB,CAAC,SAAS,CAAC,EAAE;aACvI,CAAC;QACN,CAAC;QACD,MAAM,MAAM,GAA0B,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;QAC3E,IAAI,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;YACtC,MAAM,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC;QACzC,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;YACtC,MAAM,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC;QACzC,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AAC3E,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAY;IACrC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5C,CAAC;IACD,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,GAA8B,CAAC;QAC3C,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACrB,MAAM,KAAK,GAAoB,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAC/C,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxD,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;YAC5B,CAAC;YACD,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtD,MAAM,OAAO,GAAmB,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;YACnD,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1D,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;YAChC,CAAC;YACD,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBAC5E,OAAO,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;YAC5B,CAAC;YACD,OAAO,OAAO,CAAC;QACnB,CAAC;IACL,CAAC;IACD,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,SAAgB,4BAA4B,CAAC,IAAa;IACtD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,MAAM,GAAG,GAAG,IAA+B,CAAC;IAC5C,MAAM,GAAG,GAAG,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,CAAC,GAAG,EAAE,CAAC;QACP,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,MAAM,UAAU,GAAwB,EAAE,GAAG,EAAE,CAAC;IAChD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnD,IAAI,QAAQ,EAAE,CAAC;QACX,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACnC,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxD,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;IACjC,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACpC,UAAU,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IACvC,CAAC;IACD,OAAO,UAAU,CAAC;AACtB,CAAC"}
|
|
@@ -17,185 +17,222 @@
|
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
const chai_1 = require("chai");
|
|
19
19
|
const user_interaction_tool_1 = require("./user-interaction-tool");
|
|
20
|
+
function expectRejected(input) {
|
|
21
|
+
const result = (0, user_interaction_tool_1.parseUserInteractionArgs)(input);
|
|
22
|
+
(0, chai_1.expect)(result.ok, `expected a rejection but got: ${JSON.stringify(result)}`).to.be.false;
|
|
23
|
+
return result.error;
|
|
24
|
+
}
|
|
25
|
+
function expectAccepted(input) {
|
|
26
|
+
const result = (0, user_interaction_tool_1.parseUserInteractionArgs)(input);
|
|
27
|
+
(0, chai_1.expect)(result.ok, `expected acceptance but got: ${JSON.stringify(result)}`).to.be.true;
|
|
28
|
+
return result.args;
|
|
29
|
+
}
|
|
20
30
|
describe('parseUserInteractionArgs', () => {
|
|
21
|
-
|
|
22
|
-
(
|
|
23
|
-
|
|
24
|
-
it('should return undefined for invalid JSON', () => {
|
|
25
|
-
(0, chai_1.expect)((0, user_interaction_tool_1.parseUserInteractionArgs)('not json')).to.be.undefined;
|
|
26
|
-
});
|
|
27
|
-
it('should return undefined when interactions is missing', () => {
|
|
28
|
-
const input = JSON.stringify({ foo: 'bar' });
|
|
29
|
-
(0, chai_1.expect)((0, user_interaction_tool_1.parseUserInteractionArgs)(input)).to.be.undefined;
|
|
30
|
-
});
|
|
31
|
-
it('should return undefined when interactions is not an array', () => {
|
|
32
|
-
const input = JSON.stringify({ interactions: 'nope' });
|
|
33
|
-
(0, chai_1.expect)((0, user_interaction_tool_1.parseUserInteractionArgs)(input)).to.be.undefined;
|
|
34
|
-
});
|
|
35
|
-
it('should return undefined when no step is valid', () => {
|
|
36
|
-
const input = JSON.stringify({ interactions: [{ foo: 'bar' }, { title: 1 }] });
|
|
37
|
-
(0, chai_1.expect)((0, user_interaction_tool_1.parseUserInteractionArgs)(input)).to.be.undefined;
|
|
38
|
-
});
|
|
39
|
-
it('should drop steps that are missing title or message', () => {
|
|
40
|
-
const input = JSON.stringify({
|
|
41
|
-
interactions: [
|
|
42
|
-
{ title: 'Valid', message: 'Hello' },
|
|
43
|
-
{ title: 'No message' },
|
|
44
|
-
{ message: 'No title' }
|
|
45
|
-
]
|
|
46
|
-
});
|
|
47
|
-
const result = (0, user_interaction_tool_1.parseUserInteractionArgs)(input);
|
|
48
|
-
(0, chai_1.expect)(result).to.not.be.undefined;
|
|
49
|
-
(0, chai_1.expect)(result.interactions).to.have.length(1);
|
|
50
|
-
(0, chai_1.expect)(result.interactions[0].title).to.equal('Valid');
|
|
51
|
-
});
|
|
52
|
-
it('should accept a step without options (informational)', () => {
|
|
53
|
-
const input = JSON.stringify({
|
|
54
|
-
interactions: [{ title: 'Info', message: 'Just so you know' }]
|
|
31
|
+
describe('rejecting malformed arguments', () => {
|
|
32
|
+
it('should reject undefined input', () => {
|
|
33
|
+
(0, chai_1.expect)(expectRejected(undefined)).to.match(/no arguments/i);
|
|
55
34
|
});
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
it('should filter out invalid links from a step', () => {
|
|
106
|
-
const input = JSON.stringify({
|
|
107
|
-
interactions: [{
|
|
108
|
-
title: 'T', message: 'M',
|
|
109
|
-
options: [{ text: 'A', value: 'a' }],
|
|
110
|
-
links: [
|
|
111
|
-
{ ref: 'src/a.ts' },
|
|
112
|
-
{ noRef: true },
|
|
113
|
-
{ ref: '' }
|
|
114
|
-
]
|
|
115
|
-
}]
|
|
116
|
-
});
|
|
117
|
-
const result = (0, user_interaction_tool_1.parseUserInteractionArgs)(input);
|
|
118
|
-
(0, chai_1.expect)(result.interactions[0].links).to.have.length(1);
|
|
119
|
-
});
|
|
120
|
-
it('should ignore invalid rightRef placeholders and keep multi-step file links', () => {
|
|
121
|
-
const emptyRightRefPlaceholder = {
|
|
122
|
-
path: '',
|
|
123
|
-
gitRef: '',
|
|
124
|
-
line: 0,
|
|
125
|
-
empty: false,
|
|
126
|
-
label: ''
|
|
127
|
-
};
|
|
128
|
-
const input = JSON.stringify({
|
|
129
|
-
interactions: ['README.md', 'package.json', 'CONTRIBUTING.md'].map((path, index) => ({
|
|
130
|
-
title: `File link: ${path}`,
|
|
131
|
-
message: 'Open the file link.',
|
|
132
|
-
links: [{
|
|
133
|
-
ref: { path, gitRef: '', line: 1, empty: false, label: '' },
|
|
134
|
-
rightRef: emptyRightRefPlaceholder,
|
|
135
|
-
label: `Open ${path}`,
|
|
136
|
-
autoOpen: index === 0
|
|
35
|
+
it('should reject input that is not valid JSON', () => {
|
|
36
|
+
(0, chai_1.expect)(expectRejected('not json')).to.match(/valid JSON/i);
|
|
37
|
+
});
|
|
38
|
+
it('should reject arguments without an interactions property', () => {
|
|
39
|
+
(0, chai_1.expect)(expectRejected(JSON.stringify({ foo: 'bar' }))).to.match(/"interactions" must be an array of step objects/);
|
|
40
|
+
});
|
|
41
|
+
it('should reject a JSON-encoded argument blob and name the received type', () => {
|
|
42
|
+
const error = expectRejected(JSON.stringify('{"interactions":[{"title":"T","message":"M"}]}'));
|
|
43
|
+
(0, chai_1.expect)(error).to.match(/arguments must be an object with an "interactions" array, received string/i);
|
|
44
|
+
(0, chai_1.expect)(error).to.match(/do not JSON-encode nested values/i);
|
|
45
|
+
});
|
|
46
|
+
it('should reject a top-level array', () => {
|
|
47
|
+
(0, chai_1.expect)(expectRejected('[]')).to.match(/arguments must be an object with an "interactions" array, received array/i);
|
|
48
|
+
});
|
|
49
|
+
it('should reject top-level null', () => {
|
|
50
|
+
(0, chai_1.expect)(expectRejected('null')).to.match(/arguments must be an object with an "interactions" array, received null/i);
|
|
51
|
+
});
|
|
52
|
+
it('should reject a JSON-encoded interactions string and name the received type', () => {
|
|
53
|
+
const error = expectRejected(JSON.stringify({ interactions: '[{"title":"T","message":"M"}]' }));
|
|
54
|
+
(0, chai_1.expect)(error).to.match(/"interactions" must be an array of step objects, received string/);
|
|
55
|
+
(0, chai_1.expect)(error).to.match(/do not JSON-encode nested values/i);
|
|
56
|
+
});
|
|
57
|
+
it('should reject an empty interactions array', () => {
|
|
58
|
+
(0, chai_1.expect)(expectRejected(JSON.stringify({ interactions: [] }))).to.match(/at least one step/);
|
|
59
|
+
});
|
|
60
|
+
it('should reject a step that is missing its message, naming the step position', () => {
|
|
61
|
+
const error = expectRejected(JSON.stringify({
|
|
62
|
+
interactions: [{ title: 'Valid', message: 'Hello' }, { title: 'No message' }]
|
|
63
|
+
}));
|
|
64
|
+
(0, chai_1.expect)(error).to.match(/step 2: "title" and "message" are required strings/i);
|
|
65
|
+
});
|
|
66
|
+
it('should reject a step that is missing its title, naming the step position', () => {
|
|
67
|
+
const error = expectRejected(JSON.stringify({
|
|
68
|
+
interactions: [{ message: 'No title' }]
|
|
69
|
+
}));
|
|
70
|
+
(0, chai_1.expect)(error).to.match(/step 1: "title" and "message" are required strings/i);
|
|
71
|
+
});
|
|
72
|
+
it('should reject a step whose options are a JSON-encoded string', () => {
|
|
73
|
+
const error = expectRejected(JSON.stringify({
|
|
74
|
+
interactions: [{ title: 'Apply fix?', message: 'M', options: '[{"text":"Yes","value":"yes"}]' }]
|
|
75
|
+
}));
|
|
76
|
+
(0, chai_1.expect)(error).to.match(/step 1: "options" must be an array of \{text, value\} objects, received string/i);
|
|
77
|
+
(0, chai_1.expect)(error).to.match(/do not JSON-encode nested values/i);
|
|
78
|
+
});
|
|
79
|
+
it('should reject a step when a single option is malformed, even though others are valid', () => {
|
|
80
|
+
const error = expectRejected(JSON.stringify({
|
|
81
|
+
interactions: [{
|
|
82
|
+
title: 'T', message: 'M',
|
|
83
|
+
options: [{ text: 'A', value: 'a' }, { label: 'B', value: 'b' }, { text: 'C', value: 'c' }]
|
|
137
84
|
}]
|
|
138
|
-
}))
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
it('should reject step links with empty path in object ref', () => {
|
|
171
|
-
const input = JSON.stringify({
|
|
172
|
-
interactions: [{
|
|
173
|
-
title: 'T', message: 'M',
|
|
174
|
-
links: [{ ref: { path: '' } }]
|
|
175
|
-
}]
|
|
176
|
-
});
|
|
177
|
-
const result = (0, user_interaction_tool_1.parseUserInteractionArgs)(input);
|
|
178
|
-
(0, chai_1.expect)(result.interactions[0].links).to.be.undefined;
|
|
85
|
+
}));
|
|
86
|
+
(0, chai_1.expect)(error).to.match(/step 1, option 2: "text" and "value" are required strings/i);
|
|
87
|
+
});
|
|
88
|
+
it('should reject options given as an object rather than an array', () => {
|
|
89
|
+
const error = expectRejected(JSON.stringify({
|
|
90
|
+
interactions: [{ title: 'T', message: 'M', options: { text: 'Yes', value: 'yes' } }]
|
|
91
|
+
}));
|
|
92
|
+
(0, chai_1.expect)(error).to.match(/step 1: "options" must be an array of \{text, value\} objects, received object/i);
|
|
93
|
+
});
|
|
94
|
+
it('should reject plain string options and name the received type', () => {
|
|
95
|
+
const error = expectRejected(JSON.stringify({
|
|
96
|
+
interactions: [{ title: 'T', message: 'M', options: ['Yes', 'No'] }]
|
|
97
|
+
}));
|
|
98
|
+
(0, chai_1.expect)(error).to.match(/step 1, option 1: "text" and "value" are required strings, received string/i);
|
|
99
|
+
(0, chai_1.expect)(error).to.match(/do not JSON-encode nested values/i);
|
|
100
|
+
});
|
|
101
|
+
it('should reject a JSON-encoded links string', () => {
|
|
102
|
+
const error = expectRejected(JSON.stringify({
|
|
103
|
+
interactions: [{ title: 'T', message: 'M', links: '[{"ref":"a.ts"}]' }]
|
|
104
|
+
}));
|
|
105
|
+
(0, chai_1.expect)(error).to.match(/step 1: "links" must be an array of link objects, received string/i);
|
|
106
|
+
(0, chai_1.expect)(error).to.match(/do not JSON-encode nested values/i);
|
|
107
|
+
});
|
|
108
|
+
it('should report the offending option position within the offending step', () => {
|
|
109
|
+
const error = expectRejected(JSON.stringify({
|
|
110
|
+
interactions: [
|
|
111
|
+
{ title: 'Fine', message: 'M', options: [{ text: 'A', value: 'a' }] },
|
|
112
|
+
{ title: 'Broken', message: 'M', options: [{ text: 'A', value: 'a' }, { text: 'B' }] }
|
|
113
|
+
]
|
|
114
|
+
}));
|
|
115
|
+
(0, chai_1.expect)(error).to.match(/step 2, option 2: "text" and "value" are required strings/i);
|
|
116
|
+
});
|
|
179
117
|
});
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
|
|
118
|
+
describe('accepting well-formed arguments', () => {
|
|
119
|
+
it('should accept a step without options (informational)', () => {
|
|
120
|
+
const args = expectAccepted(JSON.stringify({
|
|
121
|
+
interactions: [{ title: 'Info', message: 'Just so you know' }]
|
|
122
|
+
}));
|
|
123
|
+
(0, chai_1.expect)(args.interactions[0].options).to.be.undefined;
|
|
124
|
+
});
|
|
125
|
+
it('should accept an explicitly empty options array as informational', () => {
|
|
126
|
+
const args = expectAccepted(JSON.stringify({
|
|
127
|
+
interactions: [{ title: 'Info', message: 'Just so you know', options: [] }]
|
|
128
|
+
}));
|
|
129
|
+
(0, chai_1.expect)(args.interactions[0].options).to.be.undefined;
|
|
130
|
+
});
|
|
131
|
+
it('should accept multiple steps in order', () => {
|
|
132
|
+
const args = expectAccepted(JSON.stringify({
|
|
133
|
+
interactions: [
|
|
134
|
+
{ title: 'Overview', message: 'PR summary' },
|
|
135
|
+
{ title: 'Area 1', message: 'finding', options: [{ text: 'OK', value: 'approve' }] },
|
|
136
|
+
{ title: 'Area 2', message: 'no findings' }
|
|
137
|
+
]
|
|
138
|
+
}));
|
|
139
|
+
(0, chai_1.expect)(args.interactions).to.have.length(3);
|
|
140
|
+
(0, chai_1.expect)(args.interactions[1].options).to.have.length(1);
|
|
141
|
+
(0, chai_1.expect)(args.interactions[2].options).to.be.undefined;
|
|
142
|
+
});
|
|
143
|
+
it('should preserve buttonLabel and description in options', () => {
|
|
144
|
+
const args = expectAccepted(JSON.stringify({
|
|
145
|
+
interactions: [{
|
|
146
|
+
title: 'T', message: 'M',
|
|
147
|
+
options: [{ text: 'Confirm changes', value: 'confirm', description: 'Applies the patch', buttonLabel: '✅ Confirm' }]
|
|
148
|
+
}]
|
|
149
|
+
}));
|
|
150
|
+
(0, chai_1.expect)(args.interactions[0].options[0].buttonLabel).to.equal('✅ Confirm');
|
|
151
|
+
(0, chai_1.expect)(args.interactions[0].options[0].description).to.equal('Applies the patch');
|
|
152
|
+
});
|
|
189
153
|
});
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
154
|
+
describe('links remain leniently filtered', () => {
|
|
155
|
+
it('should normalize a singular link into a links array on a step', () => {
|
|
156
|
+
const args = expectAccepted(JSON.stringify({
|
|
157
|
+
interactions: [{
|
|
158
|
+
title: 'T', message: 'M',
|
|
159
|
+
options: [{ text: 'A', value: 'a' }],
|
|
160
|
+
link: { ref: 'src/index.ts' }
|
|
161
|
+
}]
|
|
162
|
+
}));
|
|
163
|
+
(0, chai_1.expect)(args.interactions[0].links).to.deep.equal([{ ref: 'src/index.ts' }]);
|
|
164
|
+
});
|
|
165
|
+
it('should accept a links array with multiple entries', () => {
|
|
166
|
+
const args = expectAccepted(JSON.stringify({
|
|
167
|
+
interactions: [{
|
|
168
|
+
title: 'T', message: 'M',
|
|
169
|
+
links: [{ ref: 'a.ts' }, { ref: 'b.ts', label: 'B' }]
|
|
170
|
+
}]
|
|
171
|
+
}));
|
|
172
|
+
(0, chai_1.expect)(args.interactions[0].links).to.have.length(2);
|
|
173
|
+
});
|
|
174
|
+
it('should drop invalid links from a step without rejecting the step', () => {
|
|
175
|
+
const args = expectAccepted(JSON.stringify({
|
|
176
|
+
interactions: [{
|
|
177
|
+
title: 'T', message: 'M',
|
|
178
|
+
links: [{ ref: 'a.ts' }, { nope: true }, { ref: 42 }]
|
|
179
|
+
}]
|
|
180
|
+
}));
|
|
181
|
+
(0, chai_1.expect)(args.interactions[0].links).to.have.length(1);
|
|
182
|
+
});
|
|
183
|
+
it('should ignore invalid rightRef placeholders and keep multi-step file links', () => {
|
|
184
|
+
const emptyRightRefPlaceholder = {
|
|
185
|
+
path: '',
|
|
186
|
+
gitRef: '',
|
|
187
|
+
line: 0,
|
|
188
|
+
empty: false,
|
|
189
|
+
label: ''
|
|
190
|
+
};
|
|
191
|
+
const args = expectAccepted(JSON.stringify({
|
|
192
|
+
interactions: ['README.md', 'package.json', 'CONTRIBUTING.md'].map((path, index) => ({
|
|
193
|
+
title: `File link: ${path}`,
|
|
194
|
+
message: 'Open the file link.',
|
|
195
|
+
links: [{
|
|
196
|
+
ref: { path, gitRef: '', line: 1, empty: false, label: '' },
|
|
197
|
+
rightRef: emptyRightRefPlaceholder,
|
|
198
|
+
label: `Open ${path}`,
|
|
199
|
+
autoOpen: index === 0
|
|
200
|
+
}]
|
|
201
|
+
}))
|
|
202
|
+
}));
|
|
203
|
+
(0, chai_1.expect)(args.interactions.map(step => step.links[0])).to.deep.equal([
|
|
204
|
+
{ ref: { path: 'README.md', line: 1 }, label: 'Open README.md', autoOpen: true },
|
|
205
|
+
{ ref: { path: 'package.json', line: 1 }, label: 'Open package.json', autoOpen: false },
|
|
206
|
+
{ ref: { path: 'CONTRIBUTING.md', line: 1 }, label: 'Open CONTRIBUTING.md', autoOpen: false }
|
|
207
|
+
]);
|
|
208
|
+
});
|
|
209
|
+
it('should drop a link whose object ref has an empty path', () => {
|
|
210
|
+
const args = expectAccepted(JSON.stringify({
|
|
211
|
+
interactions: [{
|
|
212
|
+
title: 'T', message: 'M',
|
|
213
|
+
links: [{ ref: { path: '' } }]
|
|
214
|
+
}]
|
|
215
|
+
}));
|
|
216
|
+
(0, chai_1.expect)(args.interactions[0].links).to.be.undefined;
|
|
217
|
+
});
|
|
218
|
+
it('should accept step links with EmptyContentRef', () => {
|
|
219
|
+
const args = expectAccepted(JSON.stringify({
|
|
220
|
+
interactions: [{
|
|
221
|
+
title: 'T', message: 'M',
|
|
222
|
+
links: [{ ref: { empty: true, label: 'New file' } }]
|
|
223
|
+
}]
|
|
224
|
+
}));
|
|
225
|
+
(0, chai_1.expect)(args.interactions[0].links[0].ref).to.deep.equal({ empty: true, label: 'New file' });
|
|
226
|
+
});
|
|
227
|
+
it('should accept step links with EmptyContentRef as rightRef', () => {
|
|
228
|
+
const args = expectAccepted(JSON.stringify({
|
|
229
|
+
interactions: [{
|
|
230
|
+
title: 'T', message: 'M',
|
|
231
|
+
links: [{ ref: 'src/old.ts', rightRef: { empty: true } }]
|
|
232
|
+
}]
|
|
233
|
+
}));
|
|
234
|
+
(0, chai_1.expect)(args.interactions[0].links[0].rightRef).to.deep.equal({ empty: true });
|
|
235
|
+
});
|
|
199
236
|
});
|
|
200
237
|
});
|
|
201
238
|
describe('parseUserInteractionInput', () => {
|
|
@@ -217,6 +254,10 @@ describe('parseUserInteractionInput', () => {
|
|
|
217
254
|
const input = '{"interactions": [{"title": "Streaming Title", "message": "incom';
|
|
218
255
|
(0, chai_1.expect)((0, user_interaction_tool_1.parseUserInteractionInput)(input).title).to.equal('Streaming Title');
|
|
219
256
|
});
|
|
257
|
+
it('should extract a partial title from JSON truncated mid-title', () => {
|
|
258
|
+
const input = '{"interactions":[{"title":"Streaming ti';
|
|
259
|
+
(0, chai_1.expect)((0, user_interaction_tool_1.parseUserInteractionInput)(input)).to.deep.equal({ title: 'Streaming ti', stepCount: 0 });
|
|
260
|
+
});
|
|
220
261
|
it('should return empty title from incomplete JSON without title field', () => {
|
|
221
262
|
const input = '{"interactions": [{"message": "no title here';
|
|
222
263
|
(0, chai_1.expect)((0, user_interaction_tool_1.parseUserInteractionInput)(input).title).to.equal('');
|